diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index aa12f36d..00000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -**/node_modules/* -dist -json -@types -.github -__tests__ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index e1cb6f11..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "env": { - "es6": true - }, - "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2017 - }, - "extends": "plugin:@typescript-eslint/eslint-recommended", - "parser": "@typescript-eslint/parser", - "rules": { - "no-var": "error", - "semi": ["error", "always", { "omitLastInOneLineBlock": true }], - "block-spacing": "error", - "indent": ["error", 4, { "SwitchCase": 1 }], - "no-mixed-spaces-and-tabs": "error", - "no-multiple-empty-lines": ["error" , { "max": 1 }], - "no-trailing-spaces": "error", - "space-infix-ops": "error", - "dot-notation": "error", - "eqeqeq": "error", - "quotes": ["error", "double"], - "no-else-return": "error", - "no-loop-func": "error", - "arrow-parens": "error", - "arrow-spacing": "error", - "no-undef": "off", - "comma-dangle": "warn", - "no-unused-vars": ["error", { "vars": "local", "args": "all" }], - "no-use-before-define": "error", - "no-const-assign": "error", - "space-before-blocks": "error", - "no-unexpected-multiline": "error", - "object-curly-spacing": ["error", "always"], - "quote-props": ["error", "always"], - "max-len": ["error", { - "code": 200, - "ignoreStrings": true, - "ignoreComments": true, - "ignoreTemplateLiterals": true - }], - "no-debugger": "error", - "no-dupe-keys": "error", - "no-duplicate-case": "error", - "no-empty": "error", - "no-extra-parens": "error", - "no-func-assign": "error", - "no-irregular-whitespace": "error", - "no-sparse-arrays": "error", - "no-unreachable": "error", - "no-unsafe-negation": "error", - "use-isnan": "error", - "block-scoped-var": "error", - "no-caller": "error", - "curly": "error", - "no-case-declarations": "error", - "no-floating-decimal": "error", - "no-eq-null": "error", - "no-empty-function": "error", - "no-empty-pattern": "error", - "no-extend-native": "error", - "dot-location": ["error", "property"], - "no-global-assign": "error", - "no-implicit-globals": "error", - "no-invalid-this": "error", - "no-lone-blocks": "error", - "no-iterator": "error", - "no-new": "error", - "no-proto": "error", - "no-return-assign": "error", - "no-self-assign": "error", - "no-self-compare": "error", - "no-useless-concat": "error", - "no-useless-call": "error", - "no-useless-return": "error", - "no-unused-expressions": "error", - "no-class-assign": "error", - "no-sequences": "error", - "no-dupe-args": "error", - "no-extra-boolean-cast": "error", - "no-obj-calls": "error", - "no-console": "off", - "no-extra-semi": "warn" - } -} \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6285f6b5..c92f097b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 19aa8db6..64a862fa 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -14,19 +14,15 @@ jobs: macos-browser-test: runs-on: macos-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm install - - run: npm run clean - - run: npm run build - run: npm run test windows-browser-test: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm install - - run: npm run clean - - run: npm run build - run: npm run test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 67401768..4346c728 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,15 +14,15 @@ jobs: macos-browser-test: runs-on: macos-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm install - run: npm run lint windows-browser-test: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm install - run: npm run lint \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0b215b67..62db533d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,24 +3,25 @@ name: Publish Package on: push: branches: - - publish + - main jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22.x" registry-url: "https://registry.npmjs.org" - run: npm install - - run: npm run build - - run: npm run clean - run: npm run publish:dist - run: cd ~/work/player/player/dist/src && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} + - run: cd ~/work/player/player/dist/packages/cache && npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - run: cd ~/work/player/player/dist/packages/core && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} @@ -36,25 +37,25 @@ jobs: - run: cd ~/work/player/player/dist/packages/geom && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - run: cd ~/work/player/player/dist/packages/interface && npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - run: cd ~/work/player/player/dist/packages/media && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - run: cd ~/work/player/player/dist/packages/net && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - run: cd ~/work/player/player/dist/packages/share && npm publish --access public + - run: cd ~/work/player/player/dist/packages/render-queue && npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} + - run: cd ~/work/player/player/dist/packages/renderer && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - run: cd ~/work/player/player/dist/packages/text && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - run: cd ~/work/player/player/dist/packages/ui && npm publish --access public + - run: cd ~/work/player/player/dist/packages/texture-packer && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - run: cd ~/work/player/player/dist/packages/util && npm publish --access public + - run: cd ~/work/player/player/dist/packages/ui && npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - run: cd ~/work/player/player/dist/packages/webgl && npm publish --access public diff --git a/.gitignore b/.gitignore index 880317ba..c5695e03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ node_modules json dist +build coverage -*.html -package-lock.json .DS_Store .idea Thumbs.db \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 927fc5e6..00000000 --- a/.npmignore +++ /dev/null @@ -1,18 +0,0 @@ -.eslintignore -.eslintrc.json -.gitattributes -.gitignore -.github -__tests__ -json -node_modules -src -DOCS.md -DEVELOP.md -jest.config.js -jest.setup.js -webpack.config.js -babel.config.js -jsdoc.conf.js -tsconfig.eslint.json -next2d.js \ No newline at end of file diff --git a/@types/window.d.ts b/@types/window.d.ts index ad8e3041..2ecffa15 100644 --- a/@types/window.d.ts +++ b/@types/window.d.ts @@ -1,79 +1,10 @@ -import { Next2D } from "../src/Next2D"; -import { IndexRangeImpl } from "@next2d/interface"; +import type { Next2D } from "@next2d/core"; declare global { - // eslint-disable-next-line no-unused-vars const next2d: Next2D; - // eslint-disable-next-line no-unused-vars - interface Location { - search: string; - origin: string; - } - - // eslint-disable-next-line no-unused-vars interface Window { - performance: Performance; - navigator: Navigator; - setTimeout: setTimeout; - Map: Map; - Number: Number; - Array: Array; - document: Document; - location: Location; - isNaN: isNaN; - Math: Math; - Event: Event; next2d?: Next2D; } - - // eslint-disable-next-line no-unused-vars - interface WebGLTexture { - width: number; - height: number; - area: number; - dirty: boolean; - smoothing: boolean; - filterState: boolean; - matrix: string; - offsetX: number; - offsetY: number; - } - - // eslint-disable-next-line no-unused-vars - interface WebGLProgram { - id: number; - } - - // eslint-disable-next-line no-unused-vars - interface WebGLRenderbuffer { - stencil: WebGLRenderbuffer; - samples: number; - width: number; - height: number; - area: number; - dirty: boolean; - } - - // eslint-disable-next-line no-unused-vars - interface WebGLBuffer { - length: number; - } - - // eslint-disable-next-line no-unused-vars - interface WebGLVertexArrayObject { - vertexBuffer: WebGLBuffer; - vertexLength: number; - indexBuffer: WebGLBuffer; - indexLength: number; - indexRanges: IndexRangeImpl[]; - indexCount: number; - } - - // eslint-disable-next-line no-unused-vars - interface AudioBufferSourceNode { - _$gainNode: GainNode | null; - _$volume: number; - } } \ No newline at end of file diff --git a/DEVELOP.md b/DEVELOP.md index a48c09f4..119b1fa7 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -3,12 +3,12 @@ ## Version Middleware required for development and supported versions ``` -node >= v17.x +node >= v22.x ``` ## Initial Settings ``` -git clone -b develop git@github.com:Next2D/player.git +git clone git@github.com:Next2D/player.git cd player npm install ``` @@ -28,10 +28,33 @@ npm test npm run lint ``` -## Export minify +## concept +各 `class` の `method` は `usecase` もしくは `service` で実装しています。但し、`service` から `service` をコールするのは禁止しています。`method` が簡素な場合は、`service` を直接コールし、複雑な場合や、複数の `service` を呼び出したい場合は `usecase` を実装しています。ロジックは `usecase` もしくは `service` に責務を置き、 `method` の役割は、 `private` や `protected` など、`class` 変数への値のセットまでとしています。 + +The `method` of each `class` is implemented by `usecase` or `service`. However, calling `service` from `service` is prohibited. If the `method` is simple, call `service` directly. If the `method` is complex or you want to call multiple `service`, implement `usecase`. The logic places the responsibility on the `usecase` or `service`, and the role of the `method` is limited to setting values in `class` variables, such as `private` or `protected`. + +### dependency diagram + +#### case1 +``` +class => method => service ``` -npm run build + +#### case2 +``` +class => method => usecase => service ``` +## packages +`packages` ディレクトリの依存関係で注意する点は以下の通りです。 +- `@next2d/core` は他の `packages` からの参照を禁止しています。 +- `@next2d/events`, `@next2d/cache`, `@next2d/filters`, `@next2d/geom`, `@next2d/texture-packer`, `@next2d/render-queue` は疎結合で設計されている為、他の `packages` の `import` を禁止しています。 +- `@next2d/renderer` はOffscreenCanvasがworkerで処理されるため、 `@next2d/webgl` のみ `import` を許可しています。 + +The dependencies to note in the `packages` directory are as follows +- `@next2d/core` does not allow references from other `packages`. +- `@next2d/events`, `@next2d/cache`, `@next2d/filters`, `@next2d/geom`, `@next2d/texture-packer` and `@next2d/render-queue` are designed to be loosely coupled, so `import` of other `packages` is prohibited. +- `@next2d/renderer` allows `import` only for `@next2d/webgl`, because OffscreenCanvas is processed by the worker. + ## License This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/README.md b/README.md index 4f8caf9a..3bfdf7d3 100644 --- a/README.md +++ b/README.md @@ -46,49 +46,55 @@ If Next2D is useful to you, we hope you will support our project. ## Related Sites * [Website](https://next2d.app) -* [Player API Documentation](https://next2d.app/ja/docs/player) -* [NoCode Tool](https://tool.next2d.app) +* [Animation Tool](https://tool.next2d.app) * [Framework](https://github.com/Next2D/framework) ## Examples ### Use Simple Sample ```javascript -next2d.load("Path to JSON output from NoCode Tool"); +next2d.load("Path to JSON output from Animation Tool"); ``` -[CodePen](https://codepen.io/next2d/pen/rNGMrZG) -### Use Program Sample +### Use Program Sample For JavaScript ```javascript const { Loader } = next2d.display; const { URLRequest } = next2d.net; const { Event } = next2d.events; // create root MovieClip -const start = async () => +const start = async () => { - const root = await next2d.createRootMovieClip(); - const request = new URLRequest("JSON path"); - const loader = new Loader(request); - - loader - .contentLoaderInfo - .addEventListener(Event.COMPLETE, (event) => - { - root.addChild(event.currentTarget.content); - }); - - loader.load(request); + const loader = new Loader(); + await loader.load(request); + + const root = await next2d.createRootMovieClip(); + root.addChild(loader.contentLoaderInfo.content); }; start(); ``` -### Use Program Sample +### Use Program Sample For TypeScript +```typescript +import { Loader } from "@next2d/display"; +import { URLRequest } from "@next2d/net"; +import { Event } from "@next2d/events"; -[CodePen](https://codepen.io/next2d/pen/VwMKGEv)\ -@see [API Documentation](https://next2d.app/en/docs/player) +// create root MovieClip +const start = async (): Promise => +{ + const request = new URLRequest("JSON path"); + const loader = new Loader(); + await loader.load(request); + + const root = await next2d.createRootMovieClip(); + root.addChild(loader.content); +}; + +start(); +``` ## Option settings @@ -96,7 +102,6 @@ start(); | プロパティ名 | 型 | デフォルト値 | 説明 | |--------------|---------|---------------|-----------------------------------------------------------------------| -| `base` | string | empty | 相対パスでJSONを取得する場合、ここで設定したURLがrootとして適用されます。絶対パスの場合はここで設定したURLは適用されません。 | | `fullScreen` | boolean | false | Stageクラスで設定した幅と高さを超えて画面全体に描画されます。 | | `tagId` | string | empty | IDを指定すると、指定したIDのエレメント内で描画を行います。 | | `bgColor` | string | "transparent" | 背景色を16進数で指定できます。デフォルトは無色透明です。 | @@ -105,7 +110,6 @@ start(); | name | type | default | description | |----------------|---------|---------------|-------------------------------------------------------------------------------------------------------------------------------------| -| `base` | string | empty | When JSON is acquired by a relative path, the URL set here is applied as root. For absolute paths, the URL set here is not applied. | | `fullScreen` | boolean | false | The entire screen is drawn beyond the width and height set in the Stage class. | | `tagId` | string | empty | When an ID is specified, drawing is performed within the element of the specified ID. | | `bgColor` | string | "transparent" | You can specify a background color in hexadecimal. The default is colorless. | @@ -114,7 +118,6 @@ start(); | 名称 | 值类型 | 默认值 | 说明 | |--------------|---------|---------------|---------------------------------------------------| -| `base` | string | empty | 当JSON是由相对路径获得的,这里设置的URL被应用为根。对于绝对路径,这里设置的URL不被应用。 | | `fullScreen` | boolean | false | 整个屏幕的绘制超出了Stage类中设置的宽度和高度。 | | `tagId` | string | empty | 当一个ID被指定时,在指定ID的元素内进行绘图。 | | `bgColor` | string | "transparent" | 你可以指定一个十六进制的背景颜色。默认为无色。 | diff --git a/__tests__/next2d/display/DisplayObjectContainerTest.ts b/__tests__/next2d/display/DisplayObjectContainerTest.ts deleted file mode 100644 index 5b5c519f..00000000 --- a/__tests__/next2d/display/DisplayObjectContainerTest.ts +++ /dev/null @@ -1,563 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { Event } from "../../../packages/events/src/Event"; -import { - DisplayObjectContainer, - Sprite, - MovieClip -} from "@next2d/display"; - -describe("DisplayObjectContainer.js property test", function() -{ - // mouseChildren - it("mouseChildren test success", function() - { - expect($PREFIX).toBe("__next2d__"); - - let doc = new DisplayObjectContainer(); - - // reset - expect(doc.mouseChildren).toBe(true); - - // start - doc.mouseChildren = false; - expect(doc.mouseChildren).toBe(false); - }); - - it("mouseChildren test valid case1", function() - { - let doc = new DisplayObjectContainer(); - - // reset - expect(doc.mouseChildren).toBe(true); - - // @ts-ignore - doc.mouseChildren = 0; - expect(doc.mouseChildren).toBe(false); - - // @ts-ignore - doc.mouseChildren = 1; - expect(doc.mouseChildren).toBe(true); - }); - - // numChildren - it("numChildren test success", function() - { - let doc = new DisplayObjectContainer(); - - expect(doc.numChildren).toBe(0); - - // start - try { - // @ts-ignore - doc.numChildren = 10; - } catch (e) {} - - expect(doc.numChildren).toBe(0); - }); - -}); - -describe("DisplayObjectContainer.js addChild test", function() -{ - - it("addChild success case", function() - { - - let container1 = new Sprite(); - let container2 = new Sprite(); - - let circle1 = new Sprite(); - let circle2 = new Sprite(); - - container2.addChild(container1); - container1.addChild(circle1); - container1.addChild(circle2); - - expect(container1.numChildren).toBe(2); - expect(container2.numChildren).toBe(1); - expect(circle1.numChildren).toBe(0); - expect(circle2.numChildren).toBe(0); - }); - - it("addChild duplicate case", function() - { - let sprite = new Sprite(); - - let str = ""; - sprite.addEventListener(Event.REMOVED, function () - { - str = Event.REMOVED; - }); - - let parent1 = new MovieClip(); - let parent2 = new MovieClip(); - - let a = parent1.addChild(sprite); - let b = parent2.addChild(sprite); - - expect(a === b).toBe(true); - expect(str).toBe(Event.REMOVED); - expect(parent1.numChildren).toBe(0); - expect(parent2.numChildren).toBe(1); - }); - -}); - -describe("DisplayObjectContainer.js addChildAt test", function() -{ - - it("addChildAt success case", function() - { - let container = new Sprite(); - - let circle1 = new Sprite(); - let circle2 = new Sprite(); - - container.addChild(circle1); - container.addChildAt(circle2, 0); - - expect(container.getChildAt(0)._$instanceId === circle2._$instanceId).toBe(true); - expect(container.getChildAt(1)._$instanceId === circle1._$instanceId).toBe(true); - - }); - -}); - -describe("DisplayObjectContainer.js contains test", function() -{ - - it("contains success case", function() - { - let sprite1 = new Sprite(); - let sprite2 = new Sprite(); - let sprite3 = new Sprite(); - let sprite4 = new Sprite(); - - sprite1.addChild(sprite2); - sprite2.addChild(sprite3); - - expect(sprite1.contains(sprite1)).toBe(true); - expect(sprite1.contains(sprite2)).toBe(true); - expect(sprite1.contains(sprite3)).toBe(true); - expect(sprite1.contains(sprite4)).toBe(false); - }); - -}); - -describe("DisplayObjectContainer.js getChildAt test", function() -{ - it("getChildAt success case", function() - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - let sprite2 = new Sprite(); - let sprite3 = new Sprite(); - - container.addChild(sprite1); // 0 => 1 - container.addChild(sprite2); // 1 => 2 - container.addChildAt(sprite3, 0); - - expect(container.getChildAt(0)._$instanceId === sprite3._$instanceId).toBe(true); - expect(container.getChildAt(1)._$instanceId === sprite1._$instanceId).toBe(true); - expect(container.getChildAt(2)._$instanceId === sprite2._$instanceId).toBe(true); - }); - -}); - -describe("DisplayObjectContainer.js getChildIndex test", function() -{ - - it("getChildIndex success case1", function() - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - - container.addChild(sprite1); - container.addChild(sprite2); - - let target = container.getChildByName("sprite1"); - expect(container.getChildIndex(target)).toBe(0); - - }); - -}); - -describe("DisplayObjectContainer.js getChildByName test", function() -{ - - it("addChild name path test case1", function() - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "test"; - sprite1.x = 1; - - let sprite2 = new Sprite(); - sprite2.name = "test"; - sprite2.x = 2; - - container.addChild(sprite2); - container.addChild(sprite1); - - expect(container.getChildByName("test").x).toBe(2); - }); - - it("addChild name path test case2", function() - { - let mc = new MovieClip(); - - let sprite1 = new Sprite(); - sprite1.name = "test"; - sprite1.x = 1; - - let sprite2 = new Sprite(); - sprite2.name = "test"; - sprite2.x = 2; - - mc.addChild(sprite1); - mc.addChild(sprite2); - - expect(mc.getChildByName("test").x).toBe(1); - }); - -}); - -describe("DisplayObjectContainer.js removeChild test", function() -{ - it("removeChild success", function() - { - - let container = new Sprite(); - - let circle1 = new Sprite(); - let circle2 = new Sprite(); - - container.addChild(circle1); - container.addChild(circle2); - expect(container.numChildren).toBe(2); - - container.removeChild(circle1); - expect(container.numChildren).toBe(1); - expect(container.getChildAt(0)._$instanceId).toBe(circle2._$instanceId); - - }); - -}); - -describe("DisplayObjectContainer.js removeChildAt test", function() -{ - - it("removeChildAt success", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - - container.addChild(sprite1); - container.addChild(sprite2); - - expect(container.numChildren).toBe(2); - container.removeChildAt(0); - expect(container.numChildren).toBe(1); - expect(container.getChildAt(0).name).toBe("sprite2"); - }); - -}); - -describe("DisplayObjectContainer.js removeChildren test", function() -{ - - it("removeChildren success case1", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); - container.addChild(sprite2); - container.addChild(sprite3); - - expect(container.numChildren).toBe(3); - container.removeChildren(0, 1); - expect(container.numChildren).toBe(1); - }); - - it("removeChildren success case2", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); - container.addChild(sprite2); - container.addChild(sprite3); - - expect(container.numChildren).toBe(3); - container.removeChildren(1, 1); - expect(container.numChildren).toBe(2); - }); - - it("removeChildren success case3", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); - container.addChild(sprite2); - container.addChild(sprite3); - - expect(container.numChildren).toBe(3); - container.removeChildren(); - expect(container.numChildren).toBe(0); - }); - -}); - -describe("DisplayObjectContainer.js setChildIndex test", function() -{ - - it("setChildIndex success case1", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); // 0 - container.addChild(sprite2); // 1 - container.addChild(sprite3); // 2 - - var children = container._$getChildren(); - - expect(children[0] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[2] === sprite3).toBe(true); - - container.setChildIndex(sprite3, 0); - - var children = container._$getChildren(); - expect(children[0] === sprite3).toBe(true); - expect(children[1] === sprite1).toBe(true); - expect(children[2] === sprite2).toBe(true); - }); - - it("setChildIndex success case2", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); // 0 - container.addChild(sprite2); // 1 - container.addChild(sprite3); // 2 - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[2] === sprite3).toBe(true); - - container.setChildIndex(sprite1, 2); - - var children = container._$getChildren(); - expect(children[0] === sprite2).toBe(true); - expect(children[1] === sprite3).toBe(true); - expect(children[2] === sprite1).toBe(true); - }); - - it("setChildIndex success case3", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - let sprite4 = new Sprite(); - sprite4.name = "sprite4"; - let sprite5 = new Sprite(); - sprite5.name = "sprite5"; - - container.addChild(sprite1); // 0 - container.addChild(sprite2); // 1 - container.addChild(sprite3); // 2 - container.addChild(sprite4); // 3 - container.addChild(sprite5); // 4 - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[2] === sprite3).toBe(true); - expect(children[3] === sprite4).toBe(true); - expect(children[4] === sprite5).toBe(true); - - container.setChildIndex(sprite4, 1); - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[2] === sprite2).toBe(true); - expect(children[3] === sprite3).toBe(true); - expect(children[1] === sprite4).toBe(true); - expect(children[4] === sprite5).toBe(true); - }); - - it("setChildIndex success case4", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - let sprite4 = new Sprite(); - sprite4.name = "sprite4"; - let sprite5 = new Sprite(); - sprite5.name = "sprite5"; - - container.addChild(sprite1); // 0 - container.addChild(sprite2); // 1 - container.addChild(sprite3); // 2 - container.addChild(sprite4); // 3 - container.addChild(sprite5); // 4 - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[2] === sprite3).toBe(true); - expect(children[3] === sprite4).toBe(true); - expect(children[4] === sprite5).toBe(true); - - container.setChildIndex(sprite2, 3); - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[3] === sprite2).toBe(true); - expect(children[1] === sprite3).toBe(true); - expect(children[2] === sprite4).toBe(true); - expect(children[4] === sprite5).toBe(true); - }); - -}); - -describe("DisplayObjectContainer.js swapChildren test", function() -{ - - it("swapChildren success case1", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); - container.addChild(sprite2); - container.addChild(sprite3); - - var children = container._$getChildren(); - expect(children[0] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[2] === sprite3).toBe(true); - - container.swapChildren(sprite1, sprite3); - - var children = container._$getChildren(); - expect(children[2] === sprite1).toBe(true); - expect(children[1] === sprite2).toBe(true); - expect(children[0] === sprite3).toBe(true); - }); - - it("swapChildren success case2", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - - container.addChild(sprite1); - container.addChild(sprite2); - - expect(container.getChildAt(0).name).toBe("sprite1"); - expect(container.getChildAt(1).name).toBe("sprite2"); - - container.swapChildren(sprite1, sprite2); - container._$getChildren(); - expect(container.getChildAt(0).name).toBe("sprite2"); - expect(container.getChildAt(1).name).toBe("sprite1"); - }); - -}); - -describe("DisplayObjectContainer.js swapChildrenAt test", function() -{ - - it("swapChildrenAt success case1", function () - { - let container = new Sprite(); - - let sprite1 = new Sprite(); - sprite1.name = "sprite1"; - let sprite2 = new Sprite(); - sprite2.name = "sprite2"; - let sprite3 = new Sprite(); - sprite3.name = "sprite3"; - - container.addChild(sprite1); - container.addChild(sprite2); - container.addChild(sprite3); - - expect(container.getChildAt(0).name).toBe("sprite1"); - expect(container.getChildAt(1).name).toBe("sprite2"); - expect(container.getChildAt(2).name).toBe("sprite3"); - - container.swapChildrenAt(0, 2); - container._$getChildren(); - expect(container.getChildAt(0).name).toBe("sprite3"); - expect(container.getChildAt(1).name).toBe("sprite2"); - expect(container.getChildAt(2).name).toBe("sprite1"); - }); - -}); diff --git a/__tests__/next2d/display/DisplayObjectTest.ts b/__tests__/next2d/display/DisplayObjectTest.ts deleted file mode 100644 index d7600614..00000000 --- a/__tests__/next2d/display/DisplayObjectTest.ts +++ /dev/null @@ -1,1711 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { DisplayObject } from "../../../packages/display/src/DisplayObject"; -import { Stage } from "../../../packages/display/src/Stage"; -import { Shape } from "../../../packages/display/src/Shape"; -import { MovieClip } from "../../../packages/display/src/MovieClip"; -import { - $clamp, - $Infinity, - $Math, - $Rad2Deg -} from "../../../packages/share/src/RenderUtil"; -import { Sprite } from "../../../packages/display/src/Sprite"; -import { BlurFilter } from "../../../packages/filters/src/BlurFilter"; -import { Point } from "../../../packages/geom/src/Point"; -import type { DisplayObjectImpl } from "../../../packages/interface/src/DisplayObjectImpl"; -import type { ParentImpl } from "../../../packages/interface/src/ParentImpl"; - -describe("DisplayObject.js property test", () => -{ - it("stage test success case1", () => - { - expect($PREFIX).toBe("__next2d__"); - }); - - // stage - it("stage test success case1", () => - { - const obj = new DisplayObject(); - expect(obj.stage).toBe(null); - }); - - it("stage test success case2", () => - { - const stage = new Stage(); - const obj = stage.addChild(new Shape()); - expect(stage).toEqual(obj.stage); // reset - }); - - // parent - it("parent test success case1", () => { - const obj = new DisplayObject(); - expect(obj.parent).toBe(null); - }); - - it("parent test success case2", () => { - const stage = new Stage(); - const obj = stage.addChild(new Shape()); - expect(obj.parent._$instanceId).toBe(stage._$instanceId); - }); - - // name - it("name test success case1", () => { - const obj = new DisplayObject(); - obj.name = "abc"; - expect(obj.name).toBe("abc"); - }); - - it("name test success case2", () => { - const obj = new DisplayObject(); - // @ts-ignore - obj.name = 1; - expect(obj.name).toBe("1"); - }); - - // alpha - it("alpha test success case1", () => { - const obj = new DisplayObject(); - obj.alpha = 0.5; - expect(obj.alpha).toBe(0.5); - }); - - it("alpha test success case2", () => { - const obj = new DisplayObject(); - // @ts-ignore - obj.alpha = "0.2"; - expect(obj.alpha).toBe(0.20000000298023224); - }); - - it("alpha test success case3", () => { - const obj = new DisplayObject(); - obj.alpha = 2; - expect(obj.alpha).toBe(1); - }); - - it("alpha test success case4", () => { - const obj = new DisplayObject(); - obj.alpha = -1; - expect(obj.alpha).toBe(0); - }); - - // rotation - it("rotation test success case1", () => { - const obj = new DisplayObject(); - expect(obj.rotation).toBe(0); - }); - - it("rotation test success case2", () => { - const obj = new DisplayObject(); - - for (let i = 0; i <= 360; i++) { - obj.rotation = i; - expect(obj.rotation).toBe($clamp(i % 360, 0 - 360, 360, 0)); - } - }); - - it("rotation test success case3", () => { - const obj = new DisplayObject(); - - for (let i = 0; i <= 360; i++) { - const value = i * -1; - - obj.rotation = value; - expect(obj.rotation).toBe($clamp(value % 360, 0 - 360, 360, 0)); - } - }); - - // scaleX - it("scaleX test success case1", () => { - const obj = new DisplayObject(); - expect(obj.scaleX).toBe(1); - }); - - it("scaleX test success case2", () => { - const obj = new DisplayObject(); - obj.scaleX = 15; - expect(obj.scaleX).toBe(15); - }); - - it("scaleX test success case3", () => { - const obj = new DisplayObject(); - obj.scaleX = -11; - expect(obj.scaleX).toBe(-11); - }); - - // scaleY - it("scaleY test success case1", () => { - const obj = new DisplayObject(); - expect(obj.scaleY).toBe(1); - }); - - it("scaleY test success case2", () => { - const obj = new DisplayObject(); - obj.scaleY = 15; - expect(obj.scaleY).toBe(15); - }); - - it("scaleY test success case3", () => { - const obj = new DisplayObject(); - obj.scaleY = -11; - expect(obj.scaleY).toBe(-11); - }); - - it("scaleY test success case4", () => { - const obj = new DisplayObject(); - obj.scaleY = -1; - expect(obj.scaleY).toBe(-1); - }); - - it("scaleY test success case5", () => { - const obj = new DisplayObject(); - obj.scaleY = 0.5449999999999999; - expect(obj.scaleY).toBe(0.545); - }); - - it("scaleY test success case6", () => { - const obj = new DisplayObject(); - - obj.scaleY = -0.6717931453290831; - expect(obj.scaleY).toBe(-0.6718); - obj.scaleY = -0.9990366556971153; - expect(obj.scaleY).toBe(-0.999); - }); - - it("scaleY test success case7", () => { - const obj = new DisplayObject(); - - obj.scaleY = -0.9758; - expect(obj.scaleY).toBe(-0.9758); - obj.scaleY = 0.1858000000000002; - expect(obj.scaleY).toBe(0.1858); - }); - - it("scaleY test success case8", () => { - const obj = new DisplayObject(); - - obj.scaleY = -1; - expect(obj.scaleY).toBe(-1); - obj.scaleY = -0.7822; - expect(obj.scaleY).toBe(-0.7822); - }); - - // scaleX and scaleY - it("scaleX and scaleY test success case1", () => { - const obj = new DisplayObject(); - obj.scaleX = 2; - obj.scaleY = 5; - expect(obj.scaleX).toBe(2); - expect(obj.scaleY).toBe(5); - }); - - // height - it("height test success case1", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.height).toBe(85); - }); - - it("height test success case2", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - mc.height = 100; - - expect(mc.height).toBe(100); - }); - - it("height test success case3", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - // @ts-ignore - mc.height = "123"; - - expect(mc.height).toBe(123); - }); - - it("height test success case4", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.height).toBe(85); - - mc.height = 0; - expect(mc.height).toBe(0); - - mc.height = 85; - expect(mc.height).toBe(85); - }); - - it("height test valid case1", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.height).toBe(85); - }); - - it("Height test set infinity case1", () => { - - const shape = new Shape(); - shape.graphics.drawRect(0, 0, 50, 40).endFill(); - - shape.height = $Infinity; - - expect(shape.height).toBe(1310680); - - expect(shape.transform.matrix.d).toBe(32767); - - /** - * TODO Flash側のバグ? - * @see https://blog.gskinner.com/archives/2007/08/annoying_as3_bu.html - */ - shape.width = 100; - expect(shape.scaleY).toBe(32767); - }); - - it("Height test set case1", () => { - - const shape = new Shape(); - shape.graphics.drawRect(0, 0, 50, 40).endFill(); - - shape.rotation = 90; - - shape.height = 30; - expect(shape.width).toBe(24); - expect(shape.height).toBe(50); - - /** - * TODO Flash側のバグ? - * @see https://blog.gskinner.com/archives/2007/08/annoying_as3_bu.html - */ - shape.height = 30; - expect(shape.width).toBe(24); - expect(shape.height).toBe(50); - }); - - // width - it("width test success case1", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.width).toBe(75); - }); - - it("width test success case2", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - mc.width = 100; - - expect(mc.width).toBe(100); - }); - - it("width test success case3", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.width).toBe(75); - - // @ts-ignore - mc.width = "312"; - expect(mc.width).toBe(312); - }); - - it("width test success case4", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.width).toBe(75); - - mc.width = 0; - expect(mc.width).toBe(0); - - mc.width = 75; - expect(mc.width).toBe(75); - }); - - it("width test valid case1", () => { - - const mc = new MovieClip(); - - const shape1 = new Shape(); - shape1 - .graphics - .beginFill("red") - .drawRect(0, 0, 50, 40) - .endFill(); - - const shape2 = new Shape(); - shape2 - .graphics - .beginFill("red") - .drawRect(25, 25, 50, 60) - .endFill(); - - mc.addChild(shape1); - mc.addChild(shape2); - - expect(mc.width).toBe(75); - }); - - it("width test set infinity case1", () => { - - const shape = new Shape(); - shape.graphics.drawRect(0, 0, 50, 40).endFill(); - - shape.width = $Infinity; - - expect(shape.width).toBe(1638350); - - expect(shape.transform.matrix.a).toBe(32767); - - shape.height = 100; - expect(Math.round(shape.scaleX)).toBe(32767); - }); - - it("width test set case1", () => { - - const shape = new Shape(); - shape.graphics.drawRect(0, 0, 50, 40).endFill(); - - shape.rotation = 90; - - shape.width = 30; - expect(shape.width).toBe(40); - expect(shape.height).toBe(37.5); - - /** - * TODO Flash側のバグ? - * @see https://blog.gskinner.com/archives/2007/08/annoying_as3_bu.html - */ - shape.width = 30; - expect(shape.width).toBe(40); - expect(shape.height).toBe(37.5); - }); - - // x - it("x test success case1", () => { - const obj = new DisplayObject(); - expect(obj.x).toBe(0); - }); - - it("x test success case2", () => { - const obj = new DisplayObject(); - - obj.x = 100; - expect(obj.x).toBe(100); - }); - - it("x test success case2", () => { - const obj = new DisplayObject(); - - // @ts-ignore - obj.x = "158"; - expect(obj.x).toBe(158); - }); - - // y - it("y test success case1", () => { - const obj = new DisplayObject(); - expect(obj.y).toBe(0); - }); - - it("y test success case2", () => { - const obj = new DisplayObject(); - - obj.y = 100; - expect(obj.y).toBe(100); - }); - - it("y test success case2", () => { - const obj = new DisplayObject(); - - // @ts-ignore - obj.y = "158"; - expect(obj.y).toBe(158); - }); - - // visible - it("visible test success case1", () => { - const obj = new DisplayObject(); - expect(obj.visible).toBe(true); - }); - - it("visible test success case2", () => { - const obj = new DisplayObject(); - obj.visible = false; - expect(obj.visible).toBe(false); - }); - - it("visible test valid case1", () => { - const obj = new DisplayObject(); - // @ts-ignore - obj.visible = "1"; - expect(obj.visible).toBe(true); - }); - -}); - -describe("DisplayObject.js getBounds test", () => -{ - - it("getBounds test success case1", () => { - - const sprite = new Sprite(); - - const container = new Sprite(); - container.x = 100; - container.y = 100; - sprite.addChild(container); - - const contents = new Shape(); - contents.graphics.drawCircle(0, 0, 100); - container.addChild(contents); - - const bounds1 = contents.getBounds(container); - const bounds2 = contents.getBounds(sprite); - - expect(bounds1.toString()).toBe("(x=-100, y=-100, w=200, h=200)"); - expect(bounds2.toString()).toBe("(x=0, y=0, w=200, h=200)"); - }); - - it("line point zero test case 0", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(10, 0); - - expect(shape.getBounds(shape).toString()).toBe("(x=-10, y=-10, w=30, h=20)"); - - }); - - it("line point zero test case 45", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(10, 10); - - expect(shape.getBounds(shape).toString()) - .toBe("(x=-14.142135623730951, y=-14.142135623730951, w=38.2842712474619, h=38.2842712474619)"); - - }); - - it("line point zero test case 90", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(0, 10); - - expect(shape.getBounds(shape).toString()) - .toBe("(x=-10, y=-20, w=20, h=50)"); - - }); - - it("line point zero test case 135", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(-10, 10); - - expect(shape.getBounds(shape).toString()) - .toBe("(x=-24.14213562373095, y=-14.142135623730951, w=38.2842712474619, h=38.2842712474619)"); - - }); - - it("line point zero test case 180", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(-10, 0); - - expect(shape.getBounds(shape).toString()).toBe("(x=-20, y=-10, w=30, h=20)"); - - }); - - it("line point zero test case -45", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(10, -10); - - expect(shape.getBounds(shape).toString()) - .toBe("(x=-14.142135623730951, y=-24.14213562373095, w=38.2842712474619, h=38.2842712474619)"); - - }); - - it("line point zero test case -90", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(0, -10); - - expect(shape.getBounds(shape).toString()).toBe("(x=-10, y=-30, w=20, h=50)"); - - }); - - it("rect test case1", () => - { - const shape = new Shape(); - - shape - .graphics - .beginFill(0xff0000) - .drawRect(50, 50, 100, 100) - .endFill(); - - expect(shape.getBounds(shape).toString()).toBe("(x=50, y=50, w=100, h=100)"); - - }); - - it("line point zero test case -135", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(-10, -10); - - expect(shape.getBounds(shape).toString()) - .toBe("(x=-24.14213562373095, y=-24.14213562373095, w=38.2842712474619, h=38.2842712474619)"); - - }); - - // it("line MITER test case1", () => - // { - // const shape = new Shape(); - // - // shape - // .graphics - // .lineStyle(20, 0x000000, 1.0, LineScaleMode.NORMAL, "square", JointStyle.MITER, 10) - // .moveTo(0, 0) - // .lineTo(0, 100) - // .lineTo(100, 100) - // .lineTo(0, 0); - // - // - // const bounds = shape.getBounds(shape); - // - // expect(bounds.x|0).toBe(-10); - // expect(bounds.y|0).toBe(-24); - // expect(bounds.width|0).toBe(134); - // expect(bounds.height|0).toBe(134); - // - // }); - - // it("line MITER test case2", () => - // { - // const shape = new Shape(); - // - // shape - // .graphics - // .lineStyle(20, 0x000000, 1.0, LineScaleMode.NORMAL, "square", JointStyle.MITER, 10) - // .moveTo(0, 0) - // .lineTo(0, 100) - // .lineTo(100, 100) - // .lineTo(0, 0); - // - // shape.x = 60; - // shape.y = 60; - // - // const bounds = shape.getBounds(shape); - // - // expect(bounds.x|0).toBe(-10); - // expect(bounds.y|0).toBe(-24); - // expect(bounds.width|0).toBe(134); - // expect(bounds.height|0).toBe(134); - // - // }); - - // it("line MITER test case3", () => - // { - // const shape = new Shape(); - // - // shape - // .graphics - // .lineStyle(20, 0x000000, 1.0, LineScaleMode.NORMAL, "square", JointStyle.MITER, 10) - // .moveTo(0, 0) - // .lineTo(0, 100) - // .lineTo(100, 100) - // .lineTo(0, 0); - // - // shape.x = 60; - // shape.y = 60; - // - // const bounds = shape.getBounds(new Shape()); - // - // expect(bounds.x|0).toBe(50); - // expect(bounds.y|0).toBe(35); - // expect(bounds.width|0).toBe(134); - // expect(bounds.height|0).toBe(134); - // - // }); - - it("line BEVEL test case1", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .lineTo(100, 50) - .lineTo(0, 100) - .lineTo(0, 0); - - const bounds = shape.getBounds(shape); - - expect(bounds.toString()) - .toBe("(x=-13.41640786499874, y=-20, w=126.83281572999749, h=140)"); - - }); - - it("line BEVEL test in curveTo case1", () => - { - const shape = new Shape(); - - shape - .graphics - .lineStyle(20, 0x000000, 1.0, "square", "bevel", 10) - .moveTo(0, 0) - .curveTo(10, 50, 100, 100) - .lineTo(-10, 100) - .lineTo(0, 0); - - shape.x = 60; - shape.y = 60; - - const bounds = shape.getBounds(new Shape()); - - expect(bounds.toString()).toBe("(x=39.05459090769012, y=45.85786437626905, w=135.08754471604084, h=128.2842712474619)"); - - }); - -}); - -describe("DisplayObject.js hitTestObject test", () => -{ - - function leaf(s: ParentImpl) { - while (s.numChildren) { - s = s.getChildAt(0); - } - return s; - } - - function createContainer(x: number, y: number, w: number, h: number) { - const sp = new Sprite(); - - // const shape = new Shape(); - // shape.graphics.lineStyle(0, 1); - // shape.graphics.drawRect(0, 0, w, h); - // sp.addChild(shape); - - sp.x = x; - sp.y = y; - return sp; - } - - function constructAndAssert( - a: ParentImpl, b: ParentImpl, - expectedResultOutOfStage: ParentImpl, - expectedResultOnStage: ParentImpl - ) { - const stage = new Stage(); - const x = 0; - const y = 0; - const s = 100; - - expect(leaf(a).hitTestObject(leaf(b))).toBe(expectedResultOutOfStage); - - const container = createContainer(x, y, s, s); - container.addChild(a); - container.addChild(b); - - stage.addChild(container); - - expect(leaf(a).hitTestObject(leaf(b))).toBe(expectedResultOnStage); - } - - function wrap(x: number, y: number, w: number, h: number, o:DisplayObjectImpl) { - const container = createContainer(x, y, w, h); - container.addChild(o); - return container; - } - - function createBound(x: number, y: number, w: number, h: number) { - const sp = new Shape(); - sp.graphics.beginFill(0, 0.5); - sp.graphics.drawRect(-w * 0.5, - h * 0.5, w, h); - sp.x = x; - sp.y = y; - return sp; - } - - it("hitTestObject case1", () => - { - const a = createBound(32, 32, 32,32); - const b = createBound(65, 65, 32, 32); - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case2", () => - { - const a = createBound(32, 32, 32,32); - const b = createBound(64, 64, 32, 32); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, true, true); - }); - - it("hitTestObject case3", () => - { - const a = createBound(36, 36, 32,32); - const b = createBound(60, 60, 32, 32); - constructAndAssert(a, b, true, true); - }); - - it("hitTestObject case4", () => - { - const a = createBound(26, 50, 32,32); - const b = createBound(74, 50, 32, 32); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case5", () => - { - const a = createBound(28, 50, 32,32); - const b = createBound(72, 50, 32, 32); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, true, true); - }); - - it("hitTestObject case6", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(65, 65, 32, 32); - const c = wrap(32, 32, 64, 64, a); - constructAndAssert(c, b, false, false); - }); - - it("hitTestObject case7", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(64, 64, 32, 32); - const c = wrap(32, 32, 64, 64, a); - constructAndAssert(c, b, true, true); - }); - - it("hitTestObject case8", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(60, 60, 32, 32); - const c = wrap(36, 36, 64, 64, a); - constructAndAssert(c, b, true, true); - }); - - it("hitTestObject case9", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(74, 50, 32, 32); - const c = wrap(26, 50, 64, 64, a); - a.rotation = b.rotation = 45; - constructAndAssert(c, b, false, false); - }); - - it("hitTestObject case10", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(72, 50, 32, 32); - const c = wrap(28, 50, 64, 64, a); - a.rotation = b.rotation = 45; - constructAndAssert(c, b, true, true); - }); - - it("hitTestObject case11", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(65, 65, 32, 32); - const c = wrap(32, 32, 64, 64, a); - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case12", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(64, 64, 32, 32); - const c = wrap(32, 32, 64, 64, a); - constructAndAssert(a, b, true, false); - }); - - it("hitTestObject case13", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(60, 60, 32, 32); - const c = wrap(36, 36, 64, 64, a); - constructAndAssert(a, b, true, false); - }); - - it("hitTestObject case14", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(74, 50, 32, 32); - const c = wrap(26, 50, 64, 64, a); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case15", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(72, 50, 32, 32); - const c = wrap(28, 50, 64, 64, a); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, true, false); - }); - - it("hitTestObject case16", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(65, 65, 32, 32); - const c = wrap(0, 32, 64, 64, a); - const d = wrap(32, 0, 64, 64, c); - constructAndAssert(d, b, false, false); - }); - - it("hitTestObject case17", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(64, 64, 32, 32); - const c = wrap(0, 32, 64, 64, a); - const d = wrap(32, 0, 64, 64, c); - constructAndAssert(d, b, true, true); - }); - - it("hitTestObject case18", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(60, 60, 32, 32); - const c = wrap(0, 36, 64, 64, a); - const d = wrap(36, 0, 64, 64, c); - constructAndAssert(d, b, true, true); - }); - - it("hitTestObject case19", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(74, 50, 32, 32); - const c = wrap(0, 50, 64, 64, a); - const d = wrap(26, 0, 64, 64, c); - a.rotation = b.rotation = 45; - constructAndAssert(d, b, false, false); - }); - - it("hitTestObject case20", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(72, 50, 32, 32); - const c = wrap(0, 50, 64, 64, a); - const d = wrap(28, 0, 64, 64, c); - a.rotation = b.rotation = 45; - constructAndAssert(d, b, true, true); - }); - - it("hitTestObject case21", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(65, 65, 32, 32); - const c = wrap(0, 32, 64, 64, a); - const d = wrap(32, 0, 64, 64, c); - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case22", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(64, 64, 32, 32); - const c = wrap(0, 32, 64, 64, a); - const d = wrap(32, 0, 64, 64, c); - constructAndAssert(a, b, true, false); - }); - - it("hitTestObject case23", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(60, 60, 32, 32); - const c = wrap(0, 36, 64, 64, a); - const d = wrap(36, 0, 64, 64, c); - constructAndAssert(a, c, true, false); - }); - - it("hitTestObject case24", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(74, 50, 32, 32); - const c = wrap(0, 50, 64, 64, a); - const d = wrap(26, 0, 64, 64, c); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, false, false); - }); - - it("hitTestObject case25", () => - { - const a = createBound(0, 0, 32,32); - const b = createBound(72, 50, 32, 32); - const c = wrap(0, 50, 64, 64, a); - const d = wrap(28, 0, 64, 64, c); - a.rotation = b.rotation = 45; - constructAndAssert(a, b, true, false); - }); - -}); - -describe("DisplayObject.js localToGlobal test", () => -{ - - it("localToGlobal test success case1", () => - { - const root = new Sprite(); - - const sprite1 = new Sprite(); - sprite1.x = 400; - sprite1.y = 400; - - const sprite2 = new Sprite(); - sprite2.x = -200; - sprite2.y = -200; - - sprite1.addChild(sprite2); - root.addChild(sprite1); - - const point = sprite1.localToGlobal(new Point(100, 100)); - expect(point.toString()).toBe("(x=500, y=500)"); - - }); - -}); - -describe("DisplayObject.js globalToLocal test", () => -{ - - it("globalToLocal test success case1", () => - { - const root = new Sprite(); - - const sprite1 = new Sprite(); - sprite1.x = 400; - sprite1.y = 400; - - const sprite2 = new Sprite(); - sprite2.x = -200; - sprite2.y = -200; - - sprite1.addChild(sprite2); - root.addChild(sprite1); - - const point = sprite1.globalToLocal(new Point(100, 100)); - expect(point.toString()).toBe("(x=-300, y=-300)"); - - }); - -}); - -describe("DisplayObject.js filters test", () => -{ - - it("filters test success case1", () => - { - const sprite = new Sprite(); - sprite.x = 10; - sprite.y = 10; - - const blurFilter = new BlurFilter(); - sprite.filters = [blurFilter]; - - expect(sprite.filters.length).toBe(1); - expect(sprite.filters[0].toString()).toBe(blurFilter.toString()); - - sprite.filters = null; - - const filters = sprite.filters; - if (!filters) { - throw new Error("filter error."); - } - - expect(filters.length).toBe(0); - - }); - -}); - -describe("DisplayObject.js visible test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.visible).toBe(true); - }); - - it("default test case4", () => - { - const obj = new DisplayObject(); - obj.visible = true; - expect(obj.visible).toBe(true); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - // @ts-ignore - obj.visible = 0; - expect(obj.visible).toBe(false); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - // @ts-ignore - obj.visible = 1; - expect(obj.visible).toBe(true); - }); - -}); - -describe("DisplayObject.js alpha test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.alpha).toBe(1); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.alpha = 0; - expect(obj.alpha).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.alpha = 1; - expect(obj.alpha).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.alpha = 500; - expect(obj.alpha).toBe(1); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.alpha = -1; - expect(obj.alpha).toBe(0); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.alpha = -500; - expect(obj.alpha).toBe(0); - }); - - it("default test case21", () => - { - const obj = new DisplayObject(); - obj.alpha = $Math.PI; - expect(obj.alpha).toBe(1); - }); - - it("default test case22", () => - { - const obj = new DisplayObject(); - obj.alpha = 1.11111111; - expect(obj.alpha).toBe(1); - }); - - it("default test case23", () => - { - const obj = new DisplayObject(); - obj.alpha = -1.11111111; - expect(obj.alpha).toBe(0); - }); - - it("default test case24", () => - { - const obj = new DisplayObject(); - obj.alpha = 10000000000000000; - expect(obj.alpha).toBe(1); - }); - - it("default test case25", () => - { - const obj = new DisplayObject(); - obj.alpha = -10000000000000000; - expect(obj.alpha).toBe(0); - }); - -}); - -describe("DisplayObject.js height test", () => -{ - - it("default test case1", () => - { - const obj = new MovieClip(); - expect(obj.height).toBe(0); - }); - - it("default test case7", () => - { - const obj = new MovieClip(); - obj.height = 0; - expect(obj.height).toBe(0); - }); - - it("default test case8", () => - { - const obj = new MovieClip(); - obj.height = 1; - expect(obj.height).toBe(0); - }); - - it("default test case9", () => - { - const obj = new MovieClip(); - obj.height = 500; - expect(obj.height).toBe(0); - }); - - it("default test case10", () => - { - const obj = new MovieClip(); - obj.height = -1; - expect(obj.height).toBe(0); - }); - - it("default test case11", () => - { - const obj = new MovieClip(); - obj.height = -500; - expect(obj.height).toBe(0); - }); - -}); - -describe("DisplayObject.js rotation test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.rotation).toBe(0); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.rotation = 0; - expect(obj.rotation).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.rotation = 1; - - const matrix = obj.transform._$rawMatrix(); - expect($Math.atan2(matrix[1], matrix[0]) * $Rad2Deg).toBe(0.9999999465488009); - expect(obj.rotation).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.rotation = 500; - - const matrix = obj.transform._$rawMatrix(); - expect($Math.atan2(matrix[1], matrix[0]) * $Rad2Deg).toBe(139.99999868188684); - expect(obj.rotation).toBe($clamp(500 % 360, 0 - 360, 360, 0)); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.rotation = -1; - - const matrix = obj.transform._$rawMatrix(); - expect($Math.atan2(matrix[1], matrix[0]) * $Rad2Deg).toBe(-0.9999999465488009); - expect(obj.rotation).toBe(-1); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.rotation = -500; - - const matrix = obj.transform._$rawMatrix(); - expect($Math.atan2(matrix[1], matrix[0]) * $Rad2Deg).toBe(-139.99999868188684); - expect(obj.rotation).toBe($clamp(-500 % 360, 0 - 360, 360, 0)); - }); - -}); - -describe("DisplayObject.js scaleX test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.scaleX).toBe(1); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.scaleX = 0; - expect(obj.scaleX).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.scaleX = 1; - expect(obj.scaleX).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.scaleX = 500; - expect(obj.scaleX).toBe(500); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.scaleX = -1; - expect(obj.scaleX).toBe(-1); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.scaleX = -500; - expect(obj.scaleX).toBe(-500); - }); - -}); - -describe("DisplayObject.js scaleY test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.scaleY).toBe(1); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.scaleY = 0; - expect(obj.scaleY).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.scaleY = 1; - expect(obj.scaleY).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.scaleY = 500; - expect(obj.scaleY).toBe(500); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.scaleY = -1; - expect(obj.scaleY).toBe(-1); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.scaleY = -500; - expect(obj.scaleY).toBe(-500); - }); - -}); - -describe("DisplayObject.js width test", () => -{ - - it("default test case1", () => - { - const obj = new MovieClip(); - expect(obj.width).toBe(0); - }); - - it("default test case7", () => - { - const obj = new MovieClip(); - obj.width = 0; - expect(obj.width).toBe(0); - }); - - it("default test case8", () => - { - const obj = new MovieClip(); - obj.width = 1; - expect(obj.width).toBe(0); - }); - - it("default test case9", () => - { - const obj = new MovieClip(); - obj.width = 500; - expect(obj.width).toBe(0); - }); - - it("default test case10", () => - { - const obj = new MovieClip(); - obj.width = -1; - expect(obj.width).toBe(0); - }); - - it("default test case11", () => - { - const obj = new MovieClip(); - obj.width = -500; - expect(obj.width).toBe(0); - }); - -}); - -describe("DisplayObject.js x test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.x).toBe(0); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.x = 0; - expect(obj.x).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.x = 1; - expect(obj.x).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.x = 500; - expect(obj.x).toBe(500); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.x = -1; - expect(obj.x).toBe(-1); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.x = -500; - expect(obj.x).toBe(-500); - }); - -}); - -describe("DisplayObject.js y test", () => -{ - - it("default test case1", () => - { - const obj = new DisplayObject(); - expect(obj.y).toBe(0); - }); - - it("default test case7", () => - { - const obj = new DisplayObject(); - obj.y = 0; - expect(obj.y).toBe(0); - }); - - it("default test case8", () => - { - const obj = new DisplayObject(); - obj.y = 1; - expect(obj.y).toBe(1); - }); - - it("default test case9", () => - { - const obj = new DisplayObject(); - obj.y = 500; - expect(obj.y).toBe(500); - }); - - it("default test case10", () => - { - const obj = new DisplayObject(); - obj.y = -1; - expect(obj.y).toBe(-1); - }); - - it("default test case11", () => - { - const obj = new DisplayObject(); - obj.y = -500; - expect(obj.y).toBe(-500); - }); - -}); - -describe("DisplayObject.js LocalVariable test", () => -{ - it("default test case1", () => - { - const mc1 = new MovieClip(); - const mc2 = new MovieClip(); - - mc1.setLocalVariable("test", 10); - mc2.setLocalVariable("test", 20); - - expect(mc1.getLocalVariable("test")).toBe(10); - expect(mc2.getLocalVariable("test")).toBe(20); - - mc1.deleteLocalVariable("test"); - - expect(mc1.hasLocalVariable("test")).toBe(false); - expect(mc2.hasLocalVariable("test")).toBe(true); - }); -}); - -describe("DisplayObject.js GlobalVariable test", () => -{ - it("default test case1", () => - { - const mc1 = new MovieClip(); - const mc2 = new MovieClip(); - - mc1.setGlobalVariable("test", 10); - - expect(mc1.getGlobalVariable("test")).toBe(10); - expect(mc2.getGlobalVariable("test")).toBe(10); - - mc1.deleteGlobalVariable("test"); - - expect(mc1.hasGlobalVariable("test")).toBe(false); - expect(mc2.hasGlobalVariable("test")).toBe(false); - }); -}); diff --git a/__tests__/next2d/display/FrameLabelTest.ts b/__tests__/next2d/display/FrameLabelTest.ts deleted file mode 100644 index 18b75db8..00000000 --- a/__tests__/next2d/display/FrameLabelTest.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { FrameLabel } from "../../../packages/display/src/FrameLabel"; - -describe("FrameLabel.js toString test", function() -{ - - // toString - it("toString test success", function () - { - const frameLabel = new FrameLabel("test", 10); - expect($PREFIX).toBe("__next2d__"); - expect(frameLabel.toString()).toBe("[object FrameLabel]"); - }); - -}); - -describe("FrameLabel.js static toString test", function() -{ - - it("static toString test", function() - { - expect(FrameLabel.toString()).toBe("[class FrameLabel]"); - }); - -}); - -describe("FrameLabel.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new FrameLabel("", 1); - expect(object.namespace).toBe("next2d.display.FrameLabel"); - }); - - it("namespace test static", function() - { - expect(FrameLabel.namespace).toBe("next2d.display.FrameLabel"); - }); - -}); - -describe("FrameLabel.js property test", function() -{ - - // property - it("property test", function() - { - const frameLabel = new FrameLabel("test", 10); - - expect(frameLabel.name).toBe("test"); - expect(frameLabel.frame).toBe(10); - }); - - // readonly - it("property readonly test", function() - { - const frameLabel = new FrameLabel("testA", 10); - - let name = frameLabel.name; - try { - // @ts-ignore - frameLabel.name = "test2"; - name = frameLabel.name; - } catch (e) {} - - let frame = frameLabel.frame; - try { - // @ts-ignore - frameLabel.frame = 1; - frame = frameLabel.frame; - } catch (e) {} - - expect(name).toBe("testA"); - expect(frame).toBe(10); - }); - -}); diff --git a/__tests__/next2d/display/GraphicsTest.ts b/__tests__/next2d/display/GraphicsTest.ts deleted file mode 100644 index 39abe33a..00000000 --- a/__tests__/next2d/display/GraphicsTest.ts +++ /dev/null @@ -1,1181 +0,0 @@ -import { Graphics } from "../../../packages/display/src/Graphics"; - -describe("Graphics.js toString test", () => -{ - - it("toString test", function () - { - let g = new Graphics(); - expect(g.toString()).toBe("[object Graphics]"); - }); - -}); - -describe("Graphics.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Graphics.toString()).toBe("[class Graphics]"); - }); - -}); - -describe("Graphics.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Graphics(); - expect(object.namespace).toBe("next2d.display.Graphics"); - }); - - it("namespace test static", () => - { - expect(Graphics.namespace).toBe("next2d.display.Graphics"); - }); - -}); - -describe("Graphics.js beginFill test", () => -{ - - it("beginFill test case1", function () - { - let g = new Graphics(); - - g - .beginFill(0x990000, 1); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fillType).toBe(Graphics.FILL_STYLE); - - }); - - it("beginFill test case2", function () - { - let g = new Graphics(); - g.beginFill("red", 0.2); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fillType).toBe(Graphics.FILL_STYLE); - - }); - - it("beginFill test valid case1", function () - { - let g = new Graphics(); - g.beginFill("red", 10); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // fill style - // @ts-ignore - expect(g._$fillType).toBe(Graphics.FILL_STYLE); - - }); - - it("beginFill test valid case2", function () - { - let g = new Graphics(); - g - .beginFill("red", 10) - .beginFill("green", 10); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(1 in g._$fills).toBe(false); - - }); - -}); - -describe("Graphics.js moveTo test", () => -{ - - it("moveTo test success", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - .moveTo(100, 60); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(60); - - }); - - it("moveTo test valid case1", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - // @ts-ignore - .moveTo("100", "60"); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(60); - - }); - - it("moveTo test valid case1", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - // @ts-ignore - .moveTo("a", "b"); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(0); - // @ts-ignore - expect(g._$fills[3]).toBe(0); - - }); - -}); - -describe("Graphics.js lineTo test", () => -{ - - it("lineTo test success", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - .lineTo(100, 60); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(60); - }); - - it("lineTo test valid case1", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - // @ts-ignore - .lineTo("100", "60"); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(60); - - }); - - it("lineTo test valid case2", function () - { - let g = new Graphics(); - g - .beginFill(0x990000, 1) - .moveTo(100, 100) - // @ts-ignore - .lineTo("a", "b"); - - // beginPath - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(100); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(0); - }); - -}); - -describe("Graphics.js endFill test", () => -{ - - it("endFill test success case1", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - .endFill(); - - // fill style - // @ts-ignore - expect(g._$recode).toBe(null); - // @ts-ignore - expect(g._$fills).toBe(null); - - }); - - it("endFill test success case2", function () - { - let g = new Graphics(); - g - .endFill(); - - // @ts-ignore - expect(g._$fills).toBe(null); - // @ts-ignore - expect(g._$recode).toBe(null); - }); - - it("drawRect test valid case3", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - .drawRect(10, 10, 100, 100) - .endFill() - .beginFill(0x00ff00, 1) - .drawRect(120, 120, 100, 100) - .endFill(); - - if (!g._$recode) { - throw new Error("Graphics non recode."); - } - - // fill style - expect(g._$recode[0]).toBe(Graphics.BEGIN_PATH); - expect(g._$recode[1]).toBe(Graphics.MOVE_TO); - expect(g._$recode[2]).toBe(10); - expect(g._$recode[3]).toBe(10); - expect(g._$recode[4]).toBe(Graphics.LINE_TO); - expect(g._$recode[5]).toBe(10); - expect(g._$recode[6]).toBe(110); - expect(g._$recode[7]).toBe(Graphics.LINE_TO); - expect(g._$recode[8]).toBe(110); - expect(g._$recode[9]).toBe(110); - expect(g._$recode[10]).toBe(Graphics.LINE_TO); - expect(g._$recode[11]).toBe(110); - expect(g._$recode[12]).toBe(10); - expect(g._$recode[13]).toBe(Graphics.LINE_TO); - expect(g._$recode[14]).toBe(10); - expect(g._$recode[15]).toBe(10); - expect(g._$recode[16]).toBe(Graphics.FILL_STYLE); - expect(g._$recode[17]).toBe(255); - expect(g._$recode[18]).toBe(0); - expect(g._$recode[19]).toBe(0); - expect(g._$recode[20]).toBe(255); - expect(g._$recode[21]).toBe(Graphics.END_FILL); - - expect(g._$recode[22]).toBe(Graphics.BEGIN_PATH); - expect(g._$recode[23]).toBe(Graphics.MOVE_TO); - expect(g._$recode[24]).toBe(120); - expect(g._$recode[25]).toBe(120); - expect(g._$recode[26]).toBe(Graphics.LINE_TO); - expect(g._$recode[27]).toBe(120); - expect(g._$recode[28]).toBe(220); - expect(g._$recode[29]).toBe(Graphics.LINE_TO); - expect(g._$recode[30]).toBe(220); - expect(g._$recode[31]).toBe(220); - expect(g._$recode[32]).toBe(Graphics.LINE_TO); - expect(g._$recode[33]).toBe(220); - expect(g._$recode[34]).toBe(120); - expect(g._$recode[35]).toBe(Graphics.LINE_TO); - expect(g._$recode[36]).toBe(120); - expect(g._$recode[37]).toBe(120); - expect(g._$recode[38]).toBe(Graphics.FILL_STYLE); - expect(g._$recode[39]).toBe(0); - expect(g._$recode[40]).toBe(255); - expect(g._$recode[41]).toBe(0); - expect(g._$recode[42]).toBe(255); - expect(g._$recode[43]).toBe(Graphics.END_FILL); - - }); - -}); - -describe("Graphics.js drawRect test", () => -{ - - it("drawRect test success", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - .drawRect(0, 1, 200, 300); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(0); - // @ts-ignore - expect(g._$fills[3]).toBe(1); - - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(301); - - // @ts-ignore - expect(g._$fills[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[8]).toBe(200); - // @ts-ignore - expect(g._$fills[9]).toBe(301); - - // @ts-ignore - expect(g._$fills[10]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[11]).toBe(200); - // @ts-ignore - expect(g._$fills[12]).toBe(1); - - // @ts-ignore - expect(g._$fills[13]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[14]).toBe(0); - // @ts-ignore - expect(g._$fills[15]).toBe(1); - }); - - it("drawRect test valid case1", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - // @ts-ignore - .drawRect("0", "1", "200", "300"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(0); - // @ts-ignore - expect(g._$fills[3]).toBe(1); - - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(301); - - // @ts-ignore - expect(g._$fills[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[8]).toBe(200); - // @ts-ignore - expect(g._$fills[9]).toBe(301); - - // @ts-ignore - expect(g._$fills[10]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[11]).toBe(200); - // @ts-ignore - expect(g._$fills[12]).toBe(1); - - // @ts-ignore - expect(g._$fills[13]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[14]).toBe(0); - // @ts-ignore - expect(g._$fills[15]).toBe(1); - }); - - it("drawRect test valid case2", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - // @ts-ignore - .drawRect("a", "b", "c", "d"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(0); - // @ts-ignore - expect(g._$fills[3]).toBe(0); - }); - -}); - -describe("Graphics.js clear test", () => -{ - - it("clear test success", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - .drawRect(0, 1, 200, 300) - .endFill(); - - g.clear(); - - // @ts-ignore - expect(g._$fills).toBe(null); - // @ts-ignore - expect(g._$lines).toBe(null); - // @ts-ignore - expect(g._$doFill).toBe(false); - // @ts-ignore - expect(g._$doLine).toBe(false); - - }); - -}); - -describe("Graphics.js copyFrom test", () => -{ - - it("copyFrom test success", function () - { - let g = new Graphics(); - g - .beginFill(0xff0000, 1) - .drawRect(0, 1, 200, 300); - - let copy = new Graphics(); - copy.copyFrom(g); - - g.clear(); - // @ts-ignore - expect(g._$fills).toBe(null); - - // @ts-ignore - expect(copy._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(copy._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(copy._$fills[2]).toBe(0); - // @ts-ignore - expect(copy._$fills[3]).toBe(1); - - // @ts-ignore - expect(copy._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(copy._$fills[5]).toBe(0); - // @ts-ignore - expect(copy._$fills[6]).toBe(301); - - // @ts-ignore - expect(copy._$fills[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(copy._$fills[8]).toBe(200); - // @ts-ignore - expect(copy._$fills[9]).toBe(301); - - // @ts-ignore - expect(copy._$fills[10]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(copy._$fills[11]).toBe(200); - // @ts-ignore - expect(copy._$fills[12]).toBe(1); - - // @ts-ignore - expect(copy._$fills[13]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(copy._$fills[14]).toBe(0); - // @ts-ignore - expect(copy._$fills[15]).toBe(1); - - }); - -}); - -describe("Graphics.js cubicCurveTo test", () => -{ - - it("cubicCurveTo test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .cubicCurveTo(275, 0, 300, 25, 300, 50); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[2]).toBe(275); - // @ts-ignore - expect(g._$fills[3]).toBe(0); - // @ts-ignore - expect(g._$fills[4]).toBe(300); - // @ts-ignore - expect(g._$fills[5]).toBe(25); - // @ts-ignore - expect(g._$fills[6]).toBe(300); - // @ts-ignore - expect(g._$fills[7]).toBe(50); - - }); - - it("cubicCurveTo test valid case1", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - // @ts-ignore - .cubicCurveTo("275", "0", "300", "25", "300", "50"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[2]).toBe(275); - // @ts-ignore - expect(g._$fills[3]).toBe(0); - // @ts-ignore - expect(g._$fills[4]).toBe(300); - // @ts-ignore - expect(g._$fills[5]).toBe(25); - // @ts-ignore - expect(g._$fills[6]).toBe(300); - // @ts-ignore - expect(g._$fills[7]).toBe(50); - - }); - - it("cubicCurveTo test valid case2", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .moveTo(100, 100) - // @ts-ignore - .cubicCurveTo("a", "b", "c", "d", "e", "f"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(100); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(0); - // @ts-ignore - expect(g._$fills[7]).toBe(0); - // @ts-ignore - expect(g._$fills[8]).toBe(0); - // @ts-ignore - expect(g._$fills[9]).toBe(0); - // @ts-ignore - expect(g._$fills[10]).toBe(0); - - }); - -}); - -describe("Graphics.js curveTo test", () => -{ - - it("curveTo test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .curveTo(300, 100, 250, 100); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.CURVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(300); - // @ts-ignore - expect(g._$fills[3]).toBe(100); - // @ts-ignore - expect(g._$fills[4]).toBe(250); - // @ts-ignore - expect(g._$fills[5]).toBe(100); - - }); - - it("curveTo test valid case1", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - // @ts-ignore - .curveTo("300", "100", "250", "100"); - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.CURVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(300); - // @ts-ignore - expect(g._$fills[3]).toBe(100); - // @ts-ignore - expect(g._$fills[4]).toBe(250); - // @ts-ignore - expect(g._$fills[5]).toBe(100); - - }); - - it("curveTo test valid case1", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .moveTo(100, 100) - // @ts-ignore - .curveTo("a", "b", "c", "d"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(100); - // @ts-ignore - expect(g._$fills[3]).toBe(100); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.CURVE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(0); - // @ts-ignore - expect(g._$fills[7]).toBe(0); - // @ts-ignore - expect(g._$fills[8]).toBe(0); - - }); - -}); - -describe("Graphics.js drawEllipse test", () => -{ - - it("drawEllipse test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .drawEllipse(10, 10, 150, 200); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(85); - // @ts-ignore - expect(g._$fills[3]).toBe(10); - - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[5] | 0).toBe(126); - // @ts-ignore - expect(g._$fills[6]).toBe(10); - // @ts-ignore - expect(g._$fills[7]).toBe(160); - // @ts-ignore - expect(g._$fills[8] | 0).toBe(54); - // @ts-ignore - expect(g._$fills[9]).toBe(160); - // @ts-ignore - expect(g._$fills[10]).toBe(110); - - // @ts-ignore - expect(g._$fills[11]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[12]).toBe(160); - // @ts-ignore - expect(g._$fills[13] | 0).toBe(165); - // @ts-ignore - expect(g._$fills[14] | 0).toBe(126); - // @ts-ignore - expect(g._$fills[15]).toBe(210); - // @ts-ignore - expect(g._$fills[16]).toBe(85); - // @ts-ignore - expect(g._$fills[17]).toBe(210); - - // @ts-ignore - expect(g._$fills[18]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[19] | 0).toBe(43); - // @ts-ignore - expect(g._$fills[20]).toBe(210); - // @ts-ignore - expect(g._$fills[21]).toBe(10); - // @ts-ignore - expect(g._$fills[22] | 0).toBe(165); - // @ts-ignore - expect(g._$fills[23]).toBe(10); - // @ts-ignore - expect(g._$fills[24]).toBe(110); - - // @ts-ignore - expect(g._$fills[25]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[26]).toBe(10); - // @ts-ignore - expect(g._$fills[27] | 0).toBe(54); - // @ts-ignore - expect(g._$fills[28] | 0).toBe(43); - // @ts-ignore - expect(g._$fills[29]).toBe(10); - // @ts-ignore - expect(g._$fills[30]).toBe(85); - // @ts-ignore - expect(g._$fills[31]).toBe(10); - }); - -}); - -describe("Graphics.js drawCircle test", () => -{ - - it("drawCircle test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .drawCircle(120, 120, 50); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(170); - // @ts-ignore - expect(g._$fills[3]).toBe(120); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.ARC); - // @ts-ignore - expect(g._$fills[5]).toBe(120); - // @ts-ignore - expect(g._$fills[6]).toBe(120); - // @ts-ignore - expect(g._$fills[7]).toBe(50); - - }); - - it("drawCircle test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - // @ts-ignore - .drawCircle("120", "120", "50"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(170); - // @ts-ignore - expect(g._$fills[3]).toBe(120); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.ARC); - // @ts-ignore - expect(g._$fills[5]).toBe(120); - // @ts-ignore - expect(g._$fills[6]).toBe(120); - // @ts-ignore - expect(g._$fills[7]).toBe(50); - - }); - - it("drawCircle test success", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - // @ts-ignore - .drawCircle("a", "b", "c"); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(0); - // @ts-ignore - expect(g._$fills[3]).toBe(0); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.ARC); - // @ts-ignore - expect(g._$fills[5]).toBe(0); - // @ts-ignore - expect(g._$fills[6]).toBe(0); - // @ts-ignore - expect(g._$fills[7]).toBe(0); - - }); - -}); - -describe("Graphics.js drawRoundRect test", () => -{ - - it("drawRoundRect test success case1", function () - { - let g = new Graphics(); - - g - .beginFill(0x0000FF, 1) - .drawRoundRect(10, 10, 100, 100, 50, 50); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(35); - // @ts-ignore - expect(g._$fills[3]).toBe(10); - - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(85); - // @ts-ignore - expect(g._$fills[6]).toBe(10); - - // @ts-ignore - expect(g._$fills[7]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[8] | 0).toBe(98); - // @ts-ignore - expect(g._$fills[9]).toBe(10); - // @ts-ignore - expect(g._$fills[10]).toBe(110); - // @ts-ignore - expect(g._$fills[11] | 0).toBe(21); - // @ts-ignore - expect(g._$fills[12]).toBe(110); - // @ts-ignore - expect(g._$fills[13]).toBe(35); - - // @ts-ignore - expect(g._$fills[14]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[15]).toBe(110); - // @ts-ignore - expect(g._$fills[16]).toBe(85); - - // @ts-ignore - expect(g._$fills[17]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[18]).toBe(110); - // @ts-ignore - expect(g._$fills[19] | 0).toBe(98); - // @ts-ignore - expect(g._$fills[20] | 0).toBe(98); - // @ts-ignore - expect(g._$fills[21]).toBe(110); - // @ts-ignore - expect(g._$fills[22]).toBe(85); - // @ts-ignore - expect(g._$fills[23]).toBe(110); - - // @ts-ignore - expect(g._$fills[24]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[25]).toBe(35); - // @ts-ignore - expect(g._$fills[26]).toBe(110); - - // @ts-ignore - expect(g._$fills[27]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[28] | 0).toBe(21); - // @ts-ignore - expect(g._$fills[29]).toBe(110); - // @ts-ignore - expect(g._$fills[30]).toBe(10); - // @ts-ignore - expect(g._$fills[31] | 0).toBe(98); - // @ts-ignore - expect(g._$fills[32]).toBe(10); - // @ts-ignore - expect(g._$fills[33]).toBe(85); - - // @ts-ignore - expect(g._$fills[34]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[35]).toBe(10); - // @ts-ignore - expect(g._$fills[36]).toBe(35); - - // @ts-ignore - expect(g._$fills[37]).toBe(Graphics.CUBIC); - // @ts-ignore - expect(g._$fills[38]).toBe(10); - // @ts-ignore - expect(g._$fills[39] | 0).toBe(21); - // @ts-ignore - expect(g._$fills[40] | 0).toBe(21); - // @ts-ignore - expect(g._$fills[41]).toBe(10); - // @ts-ignore - expect(g._$fills[42]).toBe(35); - // @ts-ignore - expect(g._$fills[43]).toBe(10); - }); - -}); - -describe("Graphics.js lineStyle test", () => -{ - - it("lineStyle test success case1", function () - { - let g = new Graphics(); - - g - .beginFill(0xff0000, 1.0) - .moveTo(20.0 , 20.0) - .lineTo(120.0 , 20.0) - .lineStyle(10, 0x00ff00, 1.0, "none", "miter", 1) - .lineTo(120.0 , 120.0 ); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(20); - // @ts-ignore - expect(g._$fills[3]).toBe(20); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(120); - // @ts-ignore - expect(g._$fills[6]).toBe(20); - // @ts-ignore - expect(g._$fills[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[8]).toBe(120); - // @ts-ignore - expect(g._$fills[9]).toBe(120); - - // @ts-ignore - expect(g._$lineType).toBe(Graphics.STROKE_STYLE); - // @ts-ignore - expect(g._$lineWidth).toBe(10); - // @ts-ignore - expect(g._$caps).toBe("none"); - // @ts-ignore - expect(g._$joints).toBe("miter"); - // @ts-ignore - expect(g._$miterLimit).toBe(1); - - // @ts-ignore - expect(g._$lines[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$lines[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$lines[2]).toBe(120); - // @ts-ignore - expect(g._$lines[3]).toBe(20); - // @ts-ignore - expect(g._$lines[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$lines[5]).toBe(120); - // @ts-ignore - expect(g._$lines[6]).toBe(120); - - g - .endLine() - .lineTo(20.0 , 120.0 ); - - // @ts-ignore - expect(g._$lineWidth).toBe(0); - // @ts-ignore - expect(g._$lines).toBe(null); - - g - // @ts-ignore - .lineStyle(20, 0x0000ff, 0.5, false, "normal", "none", 255) - .lineTo(20.0 , 20.0 ) - .endFill(); - - // lines - // @ts-ignore - expect(g._$lineWidth).toBe(20); - // @ts-ignore - expect(g._$lines[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$lines[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$lines[2]).toBe(20); - // @ts-ignore - expect(g._$lines[3]).toBe(120); - // @ts-ignore - expect(g._$lines[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$lines[5]).toBe(20); - // @ts-ignore - expect(g._$lines[6]).toBe(20); - - }); - - it("lineStyle test success case2", function () - { - let g = new Graphics(); - - g - .beginFill(0xff0000, 1.0) - .moveTo(20.0 , 20.0) - .lineTo(120.0 , 20.0 ) - .lineStyle(10, 0x00ff00, 1.0, "none", "miter", 1) - .lineTo(120.0 , 120.0 ); - - // @ts-ignore - expect(g._$fills[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$fills[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$fills[2]).toBe(20); - // @ts-ignore - expect(g._$fills[3]).toBe(20); - // @ts-ignore - expect(g._$fills[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[5]).toBe(120); - // @ts-ignore - expect(g._$fills[6]).toBe(20); - // @ts-ignore - expect(g._$fills[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$fills[8]).toBe(120); - // @ts-ignore - expect(g._$fills[9]).toBe(120); - - // @ts-ignore - expect(g._$lineType).toBe(Graphics.STROKE_STYLE); - // @ts-ignore - expect(g._$lineWidth).toBe(10); - // @ts-ignore - expect(g._$caps).toBe("none"); - // @ts-ignore - expect(g._$joints).toBe("miter"); - // @ts-ignore - expect(g._$miterLimit).toBe(1); - - // @ts-ignore - expect(g._$lines[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$lines[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$lines[2]).toBe(120); - // @ts-ignore - expect(g._$lines[3]).toBe(20); - // @ts-ignore - expect(g._$lines[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$lines[5]).toBe(120); - // @ts-ignore - expect(g._$lines[6]).toBe(120); - - g - .lineStyle(20, 0x0000ff, 0.5, "none", "bevel", 255) - .lineTo(20.0 , 120.0 ) - .lineTo(20.0 , 20.0 ) // <= logic test - .endFill(); - - // line width - // @ts-ignore - expect(g._$lineWidth).toBe(20); - - // @ts-ignore - expect(g._$lines[0]).toBe(Graphics.BEGIN_PATH); - // @ts-ignore - expect(g._$lines[1]).toBe(Graphics.MOVE_TO); - // @ts-ignore - expect(g._$lines[2]).toBe(120); - // @ts-ignore - expect(g._$lines[3]).toBe(120); - // @ts-ignore - expect(g._$lines[4]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$lines[5]).toBe(20); - // @ts-ignore - expect(g._$lines[6]).toBe(120); - // @ts-ignore - expect(g._$lines[7]).toBe(Graphics.LINE_TO); - // @ts-ignore - expect(g._$lines[8]).toBe(20); - // @ts-ignore - expect(g._$lines[9]).toBe(20); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/display/InteractiveObjectTest.ts b/__tests__/next2d/display/InteractiveObjectTest.ts deleted file mode 100644 index 5ccc7adf..00000000 --- a/__tests__/next2d/display/InteractiveObjectTest.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { InteractiveObject } from "../../../packages/display/src/InteractiveObject"; - -describe("InteractiveObject.js mouseEnabled test", function() -{ - - it("default test case1", function() - { - let io = new InteractiveObject(); - expect($PREFIX).toBe("__next2d__"); - expect(io.mouseEnabled).toBe(true); - }); - - it("default test case4", function() - { - let io = new InteractiveObject(); - io.mouseEnabled = true; - expect(io.mouseEnabled).toBe(true); - }); - - it("default test case7", function() - { - let io = new InteractiveObject(); - // @ts-ignore - io.mouseEnabled = 0; - expect(io.mouseEnabled).toBe(false); - }); - - it("default test case8", function() - { - let io = new InteractiveObject(); - // @ts-ignore - io.mouseEnabled = 1; - expect(io.mouseEnabled).toBe(true); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/display/LoopConfigTest.ts b/__tests__/next2d/display/LoopConfigTest.ts deleted file mode 100644 index 7e985766..00000000 --- a/__tests__/next2d/display/LoopConfigTest.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { LoopConfig } from "../../../packages/display/src/LoopConfig"; - -describe("LoopConfig.js toString test", function() -{ - - // toString - it("toString test success", function () - { - expect(new LoopConfig().toString()).toBe("[object LoopConfig]"); - }); - -}); - -describe("LoopConfig.js static toString test", function() -{ - - it("static toString test", function() - { - expect(LoopConfig.toString()).toBe("[class LoopConfig]"); - }); - -}); - -describe("LoopConfig.js namespace test", function() -{ - - it("namespace test public", function() - { - expect(new LoopConfig().namespace).toBe("next2d.display.LoopConfig"); - }); - - it("namespace test static", function() - { - expect(LoopConfig.namespace).toBe("next2d.display.LoopConfig"); - }); - -}); - -describe("LoopConfig.js property test", function() -{ - // default - it("default test success", function() - { - let loopConfig = new LoopConfig(); - expect(loopConfig.type).toBe(0); - expect(loopConfig.start).toBe(1); - expect(loopConfig.end).toBe(0); - expect(loopConfig.frame).toBe(1); - }); - - // type - it("type test success case1", function() - { - for (let idx = 0; idx < 10; ++idx) { - expect(new LoopConfig(idx).type).toBe(Math.min(4, idx)); - expect(new LoopConfig(idx + 0.5).type).toBe(Math.min(4, idx)); - expect(new LoopConfig(idx * -1).type).toBe(0); - } - }); - - // start - it("start test success case1", function() - { - for (let idx = 0; idx < 10; ++idx) { - expect(new LoopConfig(0, idx + 1).start).toBe(idx + 1); - expect(new LoopConfig(0, idx + 1.5).start).toBe(idx + 1); - expect(new LoopConfig(0, idx * -1).start).toBe(1); - } - }); - - // end - it("end test success case1", function() - { - for (let idx = 0; idx < 10; ++idx) { - expect(new LoopConfig(0, 1, idx).end).toBe(idx); - expect(new LoopConfig(0, 1, idx + 0.5).end).toBe(idx); - expect(new LoopConfig(0, 1, idx * -1).end).toBe(0); - } - }); -}); diff --git a/__tests__/next2d/display/MovieClipTest.ts b/__tests__/next2d/display/MovieClipTest.ts deleted file mode 100644 index 488aacdf..00000000 --- a/__tests__/next2d/display/MovieClipTest.ts +++ /dev/null @@ -1,476 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { MovieClip } from "../../../packages/display/src/MovieClip"; -import { FrameLabel } from "../../../packages/display/src/FrameLabel"; - -describe("MovieClip.js toString test", () => -{ - - // toString - it("toString test success", () => - { - expect($PREFIX).toBe("__next2d__"); - expect(new MovieClip().toString()).toBe("[object MovieClip]"); - }); - -}); - -describe("MovieClip.js static toString test", () => -{ - - it("static toString test", () => - { - expect(MovieClip.toString()).toBe("[class MovieClip]"); - }); - -}); - -describe("MovieClip.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new MovieClip(); - expect(object.namespace).toBe("next2d.display.MovieClip"); - }); - - it("namespace test static", () => - { - expect(MovieClip.namespace).toBe("next2d.display.MovieClip"); - }); - -}); - -describe("MovieClip.js property test", () => -{ - // currentFrame - it("currentFrame test success", () => - { - let mc = new MovieClip(); - expect(mc.currentFrame).toBe(1); - }); - - it("currentFrame test readonly", () => - { - let mc = new MovieClip(); - try { - // @ts-ignore - mc.currentFrame = 10; - } catch (e) {} - - expect(mc.currentFrame).toBe(1); - }); - - // currentFrameLabel - it("currentFrameLabel test success case null", () => - { - let mc = new MovieClip(); - expect(mc.currentFrameLabel).toBe(null); - }); - - it("currentFrameLabel test success case string", () => - { - let mc = new MovieClip(); - - mc.addFrameLabel(new FrameLabel("aaa", 1)); - mc.addFrameLabel(new FrameLabel("bbb", 1)); - - // @ts-ignore - expect(mc.currentFrameLabel.name).toBe("bbb"); - }); - - it("currentFrameLabel test readonly", () => - { - let mc = new MovieClip(); - try { - // @ts-ignore - mc.currentFrameLabel = "aaa"; - } catch (e) {} - - // @ts-ignore - expect(mc.currentFrameLabel).toBe(null); - }); - - it("currentFrameLabel test success case2", () => - { - let mc = new MovieClip(); - - mc._$currentFrame = 2; - mc.addFrameLabel(new FrameLabel("bbb", 3)); - mc.addFrameLabel(new FrameLabel("aaa", 1)); - - // @ts-ignore - expect(mc.currentFrameLabel).toBe(null); - }); - - // currentLabels - it("currentLabels test success case1", () => - { - let mc = new MovieClip(); - // @ts-ignore - expect(mc.currentLabels).toBe(null); - }); - - it("currentLabels test success case2", () => - { - let mc = new MovieClip(); - - mc.addFrameLabel(new FrameLabel("aaa", 1)); - mc.addFrameLabel(new FrameLabel("bbb", 2)); - - // @ts-ignore - expect(mc.currentLabels.length).toBe(2); - - // @ts-ignore - let labels = mc.currentLabels; - // @ts-ignore - for (let i = 0; i < labels.length; i++) { - - // @ts-ignore - let label = labels[i]; - switch (i) { - case 0: - expect(label.name).toBe("aaa"); - break; - case 1: - expect(label.name).toBe("bbb"); - break; - } - } - }); - - // isPlaying - it("isPlaying test success", () => - { - let mc = new MovieClip(); - expect(mc.isPlaying).toBe(false); - }); - - it("isPlaying test readonly", () => - { - let mc = new MovieClip(); - try { - // @ts-ignore - mc.isPlaying = true; - } catch (e) {} - expect(mc.isPlaying).toBe(false); - }); - - it("isPlaying test case1", () => - { - let mc = new MovieClip(); - mc.play(); - expect(mc.isPlaying).toBe(true); - }); - - it("isPlaying test case2", () => - { - let mc = new MovieClip(); - mc.gotoAndPlay(1); - expect(mc.isPlaying).toBe(true); - }); - - it("isPlaying test case3", () => - { - let mc = new MovieClip(); - mc.gotoAndPlay(1); - mc.stop(); - expect(mc.isPlaying).toBe(false); - }); - - // totalFrames - it("totalFrames test success", () => - { - let mc = new MovieClip(); - expect(mc.totalFrames).toBe(1); - }); - - it("totalFrames test readonly", () => - { - let mc = new MovieClip(); - try { - // @ts-ignore - mc.totalFrames = 10; - } catch (e) {} - expect(mc.totalFrames).toBe(1); - }); - -}); - -describe("MovieClip.js addFrameLabel test", () => -{ - - it("addFrameLabel test success", () => - { - let mc = new MovieClip(); - mc.addFrameLabel(new FrameLabel("test", 1)); - - // @ts-ignore - let labels = mc.currentLabels; - // @ts-ignore - expect(labels.length).toBe(1); - // @ts-ignore - expect(labels[0] instanceof FrameLabel).toBe(true); - }); - -}); - -describe("MovieClip.js play test", () => -{ - - it("play test success", () => - { - let mc = new MovieClip(); - mc._$stopFlag = true; - mc.play(); - expect(mc._$stopFlag).toBe(false); - }); - -}); - -describe("MovieClip.js stop test", () => -{ - - it("stop test success", () => - { - let mc = new MovieClip(); - mc._$stopFlag = false; - mc.stop(); - expect(mc._$stopFlag).toBe(true); - }); - -}); - -describe("MovieClip.js gotoAndPlay test", () => -{ - - it("gotoAndPlay test success case number", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.stop(); - expect(mc.currentFrame).toBe(1); - - mc.gotoAndPlay(2); - expect(mc.currentFrame).toBe(2); - - expect(mc._$stopFlag).toBe(false); - }); - - it("gotoAndPlay test success case string", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.stop(); - expect(mc.currentFrame).toBe(1); - - mc.addFrameLabel(new FrameLabel("f1", 1)); - mc.addFrameLabel(new FrameLabel("f2", 2)); - mc.addFrameLabel(new FrameLabel("f3", 3)); - - mc.gotoAndPlay("f2"); - expect(mc.currentFrame).toBe(2); - - expect(mc._$stopFlag).toBe(false); - }); - - it("gotoAndPlay test valid case1", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.stop(); - - mc.gotoAndPlay(0); - expect(mc.currentFrame).toBe(1); - - expect(mc._$stopFlag).toBe(false); - }); - - it("gotoAndPlay test valid case2", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.stop(); - - mc.gotoAndPlay(4); - expect(mc.currentFrame).toBe(3); - - expect(mc._$stopFlag).toBe(false); - }); - - it("gotoAndPlay test valid case3", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.stop(); - - mc.gotoAndPlay(2); - mc.gotoAndPlay(-1); - expect(mc.currentFrame).toBe(1); - - expect(mc._$stopFlag).toBe(false); - }); -}); - -describe("MovieClip.js gotoAndStop test", () => -{ - - it("gotoAndStop test success case number", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - expect(mc.currentFrame).toBe(1); - - mc.gotoAndStop(2); - expect(mc.currentFrame).toBe(2); - - expect(mc._$stopFlag).toBe(true); - }); - - it("gotoAndStop test success case string", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.play(); - expect(mc.currentFrame).toBe(1); - - mc.addFrameLabel(new FrameLabel("f1", 1)); - mc.addFrameLabel(new FrameLabel("f2", 2)); - mc.addFrameLabel(new FrameLabel("f3", 3)); - - mc.gotoAndStop("f2"); - expect(mc.currentFrame).toBe(2); - - expect(mc._$stopFlag).toBe(true); - }); - - it("gotoAndStop test valid case1", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.play(); - - mc.gotoAndStop(0); - expect(mc.currentFrame).toBe(1); - - expect(mc._$stopFlag).toBe(true); - }); - - it("gotoAndStop test valid case2", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.play(); - - mc.gotoAndStop(4); - expect(mc.currentFrame).toBe(3); - - expect(mc._$stopFlag).toBe(true); - }); - - it("gotoAndStop test valid case3", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.play(); - - mc.gotoAndStop(2); - mc.gotoAndStop(-1); - expect(mc.currentFrame).toBe(1); - - expect(mc._$stopFlag).toBe(true); - }); - -}); - -describe("MovieClip.js nextFrame test", () => -{ - it("nextFrame test success", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc.play(); - - expect(mc._$stopFlag).toBe(false); - expect(mc.currentFrame).toBe(1); - - mc.nextFrame(); - expect(mc.currentFrame).toBe(2); - expect(mc._$stopFlag).toBe(true); - - mc.nextFrame(); - expect(mc.currentFrame).toBe(3); - expect(mc._$stopFlag).toBe(true); - - mc.nextFrame(); - expect(mc.currentFrame).toBe(3); - expect(mc._$stopFlag).toBe(true); - - }); -}); - -describe("MovieClip.js prevFrame test", () => -{ - it("prevFrame test success", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc._$currentFrame = 3; - mc.play(); - - expect(mc._$stopFlag).toBe(false); - expect(mc.currentFrame).toBe(3); - - mc.prevFrame(); - expect(mc.currentFrame).toBe(2); - expect(mc._$stopFlag).toBe(true); - - mc.prevFrame(); - expect(mc.currentFrame).toBe(1); - expect(mc._$stopFlag).toBe(true); - - mc.prevFrame(); - expect(mc.currentFrame).toBe(1); - expect(mc._$stopFlag).toBe(true); - }); - - it("prevFrame test success case2", () => - { - let mc = new MovieClip(); - mc._$totalFrames = 3; - mc._$currentFrame = 1; - mc.play(); - - expect(mc._$stopFlag).toBe(false); - expect(mc.currentFrame).toBe(1); - - mc.prevFrame(); - expect(mc.currentFrame).toBe(1); - expect(mc._$stopFlag).toBe(false); - - mc._$currentFrame = 2; - mc.prevFrame(); - expect(mc.currentFrame).toBe(1); - expect(mc._$stopFlag).toBe(true); - - }); - -}); - -describe("MovieClip.js isPlaying test", () => -{ - - it("default test case1", () => - { - let mc = new MovieClip(); - expect(mc.isPlaying).toBe(false); - }); - - it("default test case1", () => - { - let mc = new MovieClip(); - mc.play(); - expect(mc.isPlaying).toBe(true); - mc.stop(); - expect(mc.isPlaying).toBe(false); - }); -}); diff --git a/__tests__/next2d/display/ShapeTest.ts b/__tests__/next2d/display/ShapeTest.ts deleted file mode 100644 index 98605696..00000000 --- a/__tests__/next2d/display/ShapeTest.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { Shape } from "../../../packages/display/src/Shape"; -import { Graphics } from "../../../packages/display/src/Graphics"; - -describe("Shape.js toString test", function() -{ - - // toString - it("toString test success", function () - { - let shape = new Shape(); - expect($PREFIX).toBe("__next2d__"); - expect(shape.toString()).toBe("[object Shape]"); - }); - -}); - -describe("Shape.js static toString test", function() -{ - - it("static toString test", function() - { - expect(Shape.toString()).toBe("[class Shape]"); - }); - -}); - -describe("Shape.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new Shape(); - expect(object.namespace).toBe("next2d.display.Shape"); - }); - - it("namespace test static", function() - { - expect(Shape.namespace).toBe("next2d.display.Shape"); - }); - -}); - -describe("Shape.js graphics test", function() -{ - - it("graphics test case1", function() - { - // @ts-ignore - expect(new Shape()._$graphics).toBe(null); - }); - - it("graphics test case2", function() - { - expect(new Shape().graphics instanceof Graphics).toBe(true); - }); - -}); - diff --git a/__tests__/next2d/display/SpriteTest.ts b/__tests__/next2d/display/SpriteTest.ts deleted file mode 100644 index 7d58f24a..00000000 --- a/__tests__/next2d/display/SpriteTest.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {$currentPlayer, $PREFIX} from "../../../packages/util/src/Util"; -import { Sprite } from "../../../packages/display/src/Sprite"; -import {MovieClip} from "../../../packages/display/src/MovieClip"; - -describe("Sprite.js toString test", function() -{ - - // toString - it("toString test success", function () - { - let sprite = new Sprite(); - expect($PREFIX).toBe("__next2d__"); - expect(sprite.toString()).toBe("[object Sprite]"); - }); - -}); - -describe("Sprite.js static toString test", function() -{ - - it("static toString test", function() - { - expect(Sprite.toString()).toBe("[class Sprite]"); - }); - -}); - -describe("Sprite.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new Sprite(); - expect(object.namespace).toBe("next2d.display.Sprite"); - }); - - it("namespace test static", function() - { - expect(Sprite.namespace).toBe("next2d.display.Sprite"); - }); - -}); - -describe("Sprite.js property test", function() -{ - - it("buttonMode test case1", function () - { - let s = new Sprite(); - expect(s.buttonMode).toBe(false); - }); - - it("buttonMode test case2", function () - { - let s = new Sprite(); - s.buttonMode = true; - expect(s.buttonMode).toBe(true); - }); - -}); - -describe("Sprite.js buttonMode test", function() -{ - - it("default test case1", function() - { - let sp = new Sprite(); - expect(sp.buttonMode).toBe(false); - }); - - it("default test case4", function() - { - let sp = new Sprite(); - sp.buttonMode = true; - expect(sp.buttonMode).toBe(true); - }); - - it("default test case7", function() - { - let sp = new Sprite(); - // @ts-ignore - sp.buttonMode = 0; - expect(sp.buttonMode).toBe(false); - }); - - it("default test case8", function() - { - let sp = new Sprite(); - // @ts-ignore - sp.buttonMode = 1; - expect(sp.buttonMode).toBe(true); - }); - -}); diff --git a/__tests__/next2d/events/EventDispatcherTest.ts b/__tests__/next2d/events/EventDispatcherTest.ts deleted file mode 100644 index 2187c18b..00000000 --- a/__tests__/next2d/events/EventDispatcherTest.ts +++ /dev/null @@ -1,746 +0,0 @@ -import { $currentPlayer } from "../../../packages/util/src/Util"; -import { Event } from "../../../packages/events/src/Event"; -import { EventDispatcher } from "../../../packages/events/src/EventDispatcher"; -import { MovieClip } from "../../../packages/display/src/MovieClip"; -import { Sprite } from "../../../packages/display/src/Sprite"; -import { Stage } from "../../../packages/display/src/Stage"; -import { EventPhase } from "../../../packages/events/src/EventPhase"; - -describe("EventDispatcher.js toString test", function() -{ - - // toString - it("toString test success", () => - { - const object = new EventDispatcher(); - expect(object.toString()).toBe("[object EventDispatcher]"); - }); - -}); - -describe("EventDispatcher.js static toString test", function() -{ - - it("static toString test", function() - { - expect(EventDispatcher.toString()).toBe("[class EventDispatcher]"); - }); - -}); - -describe("EventDispatcher.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new EventDispatcher(); - expect(object.namespace).toBe("next2d.events.EventDispatcher"); - }); - - it("namespace test static", function() - { - expect(EventDispatcher.namespace).toBe("next2d.events.EventDispatcher"); - }); - -}); - -describe("EventDispatcher.js addEventListener test", function() -{ - // addEventListener - it("addEventListener test success case1", () => - { - const di = new EventDispatcher(); - - di.addEventListener("test", () => { return "OK" }); - di.addEventListener("test", () => { return "NG" }); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - expect(events[0].listener()).toBe("OK"); - expect(events[1].listener()).toBe("NG"); - }); - - it("addEventListener test success case2", () => - { - const di = new EventDispatcher(); - - di.addEventListener("test", () => { return "NG" }, false, 50); - di.addEventListener("test", () => { return "OK" }, false, 100); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - expect(events[0].listener()).toBe("OK"); - expect(events[1].listener()).toBe("NG"); - }); - - it("addEventListener test success case3", () => - { - const di = new EventDispatcher(); - - di.addEventListener("123", () => { return "NG" }, false, 50); - di.addEventListener("123", () => { return "OK" }, false, 100); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("123"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - expect(events[0].listener()).toBe("OK"); - expect(events[1].listener()).toBe("NG"); - }); - - it("addEventListener test valid case1", () => - { - const di = new EventDispatcher(); - - // @ts-ignore - di.addEventListener("test", {}); - // @ts-ignore - di.addEventListener("test", []); - di.addEventListener("test", () => { return "OK" }); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(3); - expect(events[2].listener()).toBe("OK"); - }); - - it("addEventListener test duplicate case1", () => - { - const di = new EventDispatcher(); - - const a = () => { return "OK" }; - - di.addEventListener("test", a); - di.addEventListener("test", a); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(1); - expect(events[0].listener()).toBe("OK"); - }); - - it("addEventListener test duplicate case2", () => - { - const di = new EventDispatcher(); - - let name = null - di.addEventListener("test", () => { name = "ok" }); - di.addEventListener("test", () => { name = "ng" }); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - di.dispatchEvent(new Event("test")); - expect(name).toBe("ng"); - }); - - it("addEventListener test duplicate case4", () => - { - const mc1 = new MovieClip(); - mc1.name = "mc1"; - - const mc2 = new MovieClip(); - mc2.name = "mc2"; - - const player = $currentPlayer(); - player.broadcastEvents.clear(); - - let name = null - const a = (e: any) => { name = e.currentTarget.name }; - - mc1.addEventListener(Event.ENTER_FRAME, a); - mc2.addEventListener(Event.ENTER_FRAME, a); - - const events = player.broadcastEvents.get(Event.ENTER_FRAME); - if (!events) { - throw new Error("events none"); - } - - expect(events.length).toBe(2); - - mc1.dispatchEvent(new Event(Event.ENTER_FRAME)) - expect(name).toBe("mc1"); - - mc2.dispatchEvent(new Event(Event.ENTER_FRAME)) - expect(name).toBe("mc2"); - - // end - mc1.removeEventListener(Event.ENTER_FRAME, a); - mc2.removeEventListener(Event.ENTER_FRAME, a); - - }); - -}); - -describe("EventDispatcher.js hasEventListener test", function() -{ - - // hasEventListener - it("hasEventListener test success", () => - { - const di = new EventDispatcher(); - - di.addEventListener("test1", () => { return "OK" }); - di.addEventListener("test3", () => { return "NG" }); - - expect(di.hasEventListener("test1")).toBe(true); - expect(di.hasEventListener("test2")).toBe(false); - expect(di.hasEventListener("test3")).toBe(true); - expect(di.hasEventListener("test4")).toBe(false); - - }); - - // メソッドが所属する EventDispatcher インスタンスについてのみリスナーが登録されているか - it("hasEventListener test success case2", () => - { - const doc1 = new MovieClip(); - const doc2 = new MovieClip(); - const doc3 = new MovieClip(); - doc1.addChild(doc2); - doc2.addChild(doc3); - - doc2.addEventListener(Event.ENTER_FRAME, () => { return "OK" }); - - expect(doc1.hasEventListener(Event.ENTER_FRAME)).toBe(false); - expect(doc2.hasEventListener(Event.ENTER_FRAME)).toBe(true); - expect(doc3.hasEventListener(Event.ENTER_FRAME)).toBe(false); - - }); -}); - -describe("EventDispatcher.js removeEventListener test", function() -{ - - // removeEventListener - it("removeEventListener test success case1", () => - { - const di = new EventDispatcher(); - - const test1 = () => { return "OK1" }; - const test2 = () => { return "OK2" }; - const test3 = () => { return "OK3" }; - - di.addEventListener("test", test1, false, 10); - di.addEventListener("test", test2, false, 20); - di.addEventListener("test", test3, false, 30); - - di.removeEventListener("test", test2); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - expect(events[0].listener()).toBe("OK3"); - expect(events[1].listener()).toBe("OK1"); - }); - - it("removeEventListener test success case2", () => - { - const di = new EventDispatcher(); - - const a = () => { return "ok" }; - const b = () => { return "no" }; - - di.addEventListener("test", a); - di.addEventListener("test", b); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - - di.removeEventListener("test", a); - - expect(di._$events.has("test")).toBe(true); - expect(events.length).toBe(1); - }); - - it("removeEventListener test success case3", () => - { - const di = new EventDispatcher(); - - const a = () => { return "yes" }; - const b = () => { return "no" }; - - di.addEventListener("test", a, true); - di.addEventListener("test", b, false); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - - di.removeEventListener("test", b, true); - di.removeEventListener("test", a, true); - - expect(events.length).toBe(1); - expect(events[0].listener().toString()).toBe("no"); - }); - - it("removeEventListener test success case4", () => - { - const di = new EventDispatcher(); - - const a = () => { return "ok" }; - const b = () => { return "no" }; - - di.addEventListener("test", a, true); - di.addEventListener("test", b, false); - - if (!di._$events) { - throw new Error("addEventListener test success case1"); - } - - const events = di._$events.get("test"); - if (!events) { - throw new Error("the events is none."); - } - - expect(events.length).toBe(2); - - di.removeEventListener("test", a, false); - di.removeEventListener("test", b, true); - - expect(events.length).toBe(2); - expect(events[0].listener().toString()).toBe("ok"); - expect(events[1].listener().toString()).toBe("no"); - }); - - it("removeEventListener test success case5", () => - { - const player = $currentPlayer(); - player.broadcastEvents.clear(); - - const mc1 = new MovieClip(); - const mc2 = new MovieClip(); - - const a = () => { return undefined }; - - mc1.addEventListener(Event.ENTER_FRAME, a); - mc2.addEventListener(Event.ENTER_FRAME, a); - - const events = player.broadcastEvents.get(Event.ENTER_FRAME); - if (!events) { - throw new Error("events none"); - } - - expect(events.length).toBe(2); - - mc1.removeEventListener(Event.ENTER_FRAME, a); - expect(events.length).toBe(1); - - mc2.removeEventListener(Event.ENTER_FRAME, a); - expect(player.broadcastEvents.has(Event.ENTER_FRAME)).toBe(false); - }); - -}); - -describe("EventDispatcher.js dispatchEvent test", function() -{ - - // dispatchEvent - it("dispatchEvent test success case1", () => - { - const di = new EventDispatcher(); - - let s = ""; - const test1 = () => { s += "O" }; - const test2 = () => { s += "K" }; - const test3 = () => { s += "!" }; - - di.addEventListener("test", test1); - di.addEventListener("test", test2); - di.addEventListener("test", test3); - - di.dispatchEvent(new Event("test")); - - expect(s).toBe("OK!"); - }); - - it("dispatchEvent test success case2", () => - { - - const di = new EventDispatcher(); - - let s = ""; - const test1 = () => { return s += "!" }; - const test2 = () => { return s += "K" }; - const test3 = () => { return s += "O" }; - - di.addEventListener("test", test2, false, 20); - di.addEventListener("test", test1, false, 10); - di.addEventListener("test", test3, false, 30); - - di.dispatchEvent(new Event("test")); - - expect(s).toBe("OK!"); - }); - - it("dispatchEvent test success capture case1", () => - { - const mc = new MovieClip(); - - let s = ""; - const test = () => { return s = "capture" }; - - mc.addEventListener("test", test, true); - expect(s).toBe(""); - - const sprite = new Sprite(); - mc.addChild(sprite); - - sprite.dispatchEvent(new Event("test")); - expect(s).toBe("capture"); - }); - - it("dispatchEvent test success capture case2", () => - { - const mc = new MovieClip(); - - let s = ""; - const test = () => { return s = "capture" }; - - mc.addEventListener("test", test); - - const sprite = new Sprite(); - mc.addChild(sprite); - - sprite.dispatchEvent(new Event("test")); - expect(s).toBe(""); - }); - - it("dispatchEvent test success capture case2", () => - { - const mc = new MovieClip(); - - let s = ""; - const test1 = () => { return s += "cap" }; - const test2 = () => { return s += "ture" }; - - mc.addEventListener("test", test1, true); - - const sprite = new Sprite(); - sprite.addEventListener("test", test2); - mc.addChild(sprite); - - sprite.dispatchEvent(new Event("test")); - expect(s).toBe("capture"); - }); - - it("dispatchEvent test success capture and bubble case1", () => - { - const mc = new MovieClip(); - - let s = ""; - const test1 = () => { return s += "cap" }; - const test2 = () => { return s += "ture" }; - const test3 = () => { return s += " and bubble" }; - - mc.addEventListener("test", test1, true); - mc.addEventListener("test", test3); - - const sprite = new Sprite(); - sprite.addEventListener("test", test2); - mc.addChild(sprite); - - sprite.dispatchEvent(new Event("test", true)); - - expect(s).toBe("capture and bubble"); - }); - - // stopImmediatePropagation - it("dispatchEvent stopImmediatePropagation test case1", () => - { - const stage = new Stage(); - stage.name = "stage"; - - const root = new MovieClip(); - stage.addChild(root); - - const sprite_a = new Sprite(); - sprite_a.name = "A"; - stage.addChild(sprite_a); - - const sprite_b = new Sprite(); - sprite_b.name = "B"; - sprite_a.addChild(sprite_b); - - const sprite_c = new Sprite(); - sprite_c.name = "C"; - sprite_b.addChild(sprite_c); - - let str = ""; - const EventRemovedFunc = (e: any) => - { - str += e.currentTarget.name; - - // ターゲットフェーズに到達した - if (e.eventPhase === EventPhase.AT_TARGET) { - e.stopImmediatePropagation(); - } - } - - // event - stage.addEventListener(Event.REMOVED, EventRemovedFunc, true); - stage.addEventListener(Event.REMOVED, EventRemovedFunc, false); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFunc, true); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFunc, false); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFunc, true); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFunc, false); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFunc, true); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFunc, false); - - sprite_c.parent.removeChild(sprite_c); - - expect(str).toBe("stageABC"); - }); - - // stopPropagation - it("dispatchEvent stopImmediatePropagation test case2", () => - { - const stage = new Stage(); - stage.name = "stage1"; - - const root = new MovieClip(); - stage.addChild(root); - - const sprite_a = new Sprite(); - sprite_a.name = "A"; - stage.addChild(sprite_a); - - const sprite_b = new Sprite(); - sprite_b.name = "B"; - sprite_a.addChild(sprite_b); - - const sprite_c = new Sprite(); - sprite_c.name = "C"; - sprite_b.addChild(sprite_c); - - let strA = ""; - const EventRemovedFuncA = (e: any) => - { - - strA += e.currentTarget.name; - - // ターゲットフェーズに到達した - if (e.eventPhase === EventPhase.AT_TARGET) { - // イベント通知の伝達を終了する - e.stopPropagation(); - } - } - - let strB = ""; - const EventRemovedFuncB = (e: any) => - { - strB += e.currentTarget.name; - } - - // A - stage.addEventListener(Event.REMOVED, EventRemovedFuncA, true); - stage.addEventListener(Event.REMOVED, EventRemovedFuncA, false); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncA, true); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncA, false); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncA, true); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncA, false); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncA, true); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncA, false); - - // B - stage.addEventListener(Event.REMOVED, EventRemovedFuncB, true); - stage.addEventListener(Event.REMOVED, EventRemovedFuncB, false); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncB, true); - sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncB, false); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncB, true); - sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncB, false); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncB, true); - sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncB, false); - - // execute - sprite_c.parent.removeChild(sprite_c); - - expect(strA).toBe("stage1ABC"); - expect(strB).toBe("stage1ABC"); - - }); - - // stopPropagation - it("dispatchEvent stopImmediatePropagation test case3", () => - { - - let strA = ""; - const sprite1 = new Sprite(); - sprite1.addEventListener(Event.ADDED_TO_STAGE, () => - { - strA = "ADDED_TO_STAGE"; - }, true); - - let strB = ""; - sprite1.addEventListener(Event.REMOVED_FROM_STAGE, () => - { - strB = "REMOVED_FROM_STAGE"; - }, true); - - const sprite2 = new Sprite(); - sprite1.addChild(sprite2); - sprite1.removeChild(sprite2); - - expect(strA).toBe(""); - expect(strB).toBe(""); - - const stage = new Stage(); - const root = new MovieClip(); - stage.addChild(root); - - root.addChild(sprite1); - sprite1.addChild(sprite2); - sprite1.removeChild(sprite2); - - expect(strA).toBe("ADDED_TO_STAGE"); - expect(strB).toBe("REMOVED_FROM_STAGE"); - - }); - - it("dispatchEvent test single dispatchEvent", () => - { - const player = $currentPlayer(); - player.broadcastEvents.clear(); - - const stage = new Stage(); - - const mc = new MovieClip(); - stage.addChild(mc); - - let log = ""; - mc.addEventListener(Event.ENTER_FRAME, () => - { - log += "MovieClip"; - }); - - stage.addEventListener(Event.ENTER_FRAME, () => - { - log += "Stage"; - }); - - mc.dispatchEvent(new Event(Event.ENTER_FRAME)); - expect(log).toBe("MovieClip"); - }); - -}); - -describe("EventDispatcher.js willTrigger test", function() -{ - - // hasEventListener - it("willTrigger test success case1", () => - { - const container = new Sprite(); - - const s1 = container.addChild(new Sprite()); - const s2 = container.addChild(new Sprite()); - const s3 = s2.addChild(new Sprite()); - - s2.addEventListener("test", () => {}); - - expect(s1.willTrigger("test")).toBe(false); - expect(s2.willTrigger("test")).toBe(true); - expect(s3.willTrigger("test")).toBe(true); - - }); - - // hasEventListener - it("willTrigger test success case2", () => - { - const container = new Sprite(); - - const s1 = container.addChild(new Sprite()); - const s2 = s1.addChild(new Sprite()); - const s3 = s2.addChild(new Sprite()); - - s1.addEventListener("test", () => {}); - - expect(s1.willTrigger("test")).toBe(true); - expect(s2.willTrigger("test")).toBe(true); - expect(s3.willTrigger("test")).toBe(true); - - }); - -}); - -describe("EventDispatcher.js removeAllEventListener test", function() -{ - - // hasEventListener - it("removeAllEventListener test success case1", () => - { - const sprite = new Sprite(); - sprite.addEventListener("test", () => {}); - sprite.addEventListener("test", () => {}); - sprite.addEventListener("test", () => {}); - - expect(sprite.hasEventListener("test")).toBe(true); - sprite.removeAllEventListener("test"); - expect(sprite.hasEventListener("test")).toBe(false); - }); - -}); diff --git a/__tests__/next2d/events/EventPhaseTest.ts b/__tests__/next2d/events/EventPhaseTest.ts deleted file mode 100644 index 7d9538c5..00000000 --- a/__tests__/next2d/events/EventPhaseTest.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { EventPhase } from "../../../packages/events/src/EventPhase"; - -describe("EventPhase.js toString test", () => -{ - it("toString test success", () => - { - let object = new EventPhase(); - expect(object.toString()).toBe("[object EventPhase]"); - }); - -}); - -describe("EventPhase.js static toString test", () => -{ - - it("static toString test", () => - { - expect(EventPhase.toString()).toBe("[class EventPhase]"); - }); - -}); - -describe("EventPhase.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new EventPhase(); - expect(object.namespace).toBe("next2d.events.EventPhase"); - }); - - it("namespace test static", () => - { - expect(EventPhase.namespace).toBe("next2d.events.EventPhase"); - }); - -}); - -describe("EventPhase.js property test", () => -{ - - it("CAPTURING_PHASE test", () => - { - expect(EventPhase.CAPTURING_PHASE).toBe(1); - }); - - it("AT_TARGET test", () => - { - expect(EventPhase.AT_TARGET).toBe(2); - }); - - it("BUBBLING_PHASE test", () => - { - expect(EventPhase.BUBBLING_PHASE).toBe(3); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/events/EventTest.ts b/__tests__/next2d/events/EventTest.ts deleted file mode 100644 index 92c140f6..00000000 --- a/__tests__/next2d/events/EventTest.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Event } from "../../../packages/events/src/Event"; - -describe("Event.js toString test", function() -{ - it("toString test success", function() - { - const object = new Event("test"); - expect(object.toString()) - .toBe("[Event type=\"test\" bubbles=false cancelable=false eventPhase=2]"); - }); - -}); - -describe("Event.js static toString test", function() -{ - - it("static toString test", function() - { - expect(Event.toString()).toBe("[class Event]"); - }); - -}); - -describe("Event.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new Event("test"); - expect(object.namespace).toBe("next2d.events.Event"); - }); - - it("namespace test static", function() - { - expect(Event.namespace).toBe("next2d.events.Event"); - }); - -}); - -describe("Event.js property test", function() -{ - - it("ACTIVATE test", () => - { - expect(Event.ACTIVATE).toBe("activate"); - }); - - it("ADDED test", () => - { - expect(Event.ADDED).toBe("added"); - }); - - it("ADDED_TO_STAGE test", () => - { - expect(Event.ADDED_TO_STAGE).toBe("addedToStage"); - }); - - it("COMPLETE test", () => - { - expect(Event.COMPLETE).toBe("complete"); - }); - - it("DEACTIVATE test", () => - { - expect(Event.DEACTIVATE).toBe("deactivate"); - }); - - it("ENTER_FRAME test", () => - { - expect(Event.ENTER_FRAME).toBe("enterFrame"); - }); - - it("EXIT_FRAME test", () => - { - expect(Event.EXIT_FRAME).toBe("exitFrame"); - }); - - it("FRAME_CONSTRUCTED test", () => - { - expect(Event.FRAME_CONSTRUCTED).toBe("frameConstructed"); - }); - - it("INIT test", () => - { - expect(Event.INIT).toBe("init"); - }); - - it("MOUSE_LEAVE test", () => - { - expect(Event.MOUSE_LEAVE).toBe("mouseLeave"); - }); - - it("REMOVED test", () => - { - expect(Event.REMOVED).toBe("removed"); - }); - - it("REMOVED_FROM_STAGE test", () => - { - expect(Event.REMOVED_FROM_STAGE).toBe("removedFromStage"); - }); - - it("RENDER test", () => - { - expect(Event.RENDER).toBe("render"); - }); - - it("SOUND_COMPLETE test", () => - { - expect(Event.SOUND_COMPLETE).toBe("soundComplete"); - }); - - it("FRAME_CONSTRUCTED test", () => - { - expect(Event.FRAME_CONSTRUCTED).toBe("frameConstructed"); - }); - - it("FRAME_LABEL test", () => - { - expect(Event.FRAME_LABEL).toBe("frameLabel"); - }); -}); \ No newline at end of file diff --git a/__tests__/next2d/events/FocusEventTest.ts b/__tests__/next2d/events/FocusEventTest.ts deleted file mode 100644 index 8df1fb61..00000000 --- a/__tests__/next2d/events/FocusEventTest.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { FocusEvent } from "../../../packages/events/src/FocusEvent"; - -describe("FocusEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(FocusEvent.toString()).toBe("[class FocusEvent]"); - }); - -}); - -describe("FocusEvent.js toString test", () => -{ - // toString - it("toString test success", () => - { - let event = new FocusEvent("focusIn"); - expect(event.toString()).toBe("[FocusEvent type=\"focusIn\" bubbles=true cancelable=false eventPhase=2]"); - }); - -}); - -describe("FocusEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new FocusEvent("test"); - expect(object.namespace).toBe("next2d.events.FocusEvent"); - }); - - it("namespace test static", () => - { - expect(FocusEvent.namespace).toBe("next2d.events.FocusEvent"); - }); - -}); - -describe("FocusEvent.js property test", () => -{ - - it("FOCUS_IN test", () => - { - expect(FocusEvent.FOCUS_IN).toBe("focusIn"); - }); - - it("FOCUS_OUT test", () => - { - expect(FocusEvent.FOCUS_OUT).toBe("focusOut"); - }); - -}); - diff --git a/__tests__/next2d/events/HTTPStatusEventTest.ts b/__tests__/next2d/events/HTTPStatusEventTest.ts deleted file mode 100644 index 900afa99..00000000 --- a/__tests__/next2d/events/HTTPStatusEventTest.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { HTTPStatusEvent } from "../../../packages/events/src/HTTPStatusEvent"; - - -describe("HTTPStatusEvent.js toString test", () => -{ - it("toString test1 success", () => - { - let event = new HTTPStatusEvent(""); - expect(event.toString()).toBe("[HTTPStatusEvent type=\"\" bubbles=false cancelable=false eventPhase=2 status=0 responseURL=\"\"]"); - }); - - it("toString test2 success", () => - { - let event = new HTTPStatusEvent("type", true, false, 200, "url"); - expect(event.toString()).toBe("[HTTPStatusEvent type=\"type\" bubbles=true cancelable=false eventPhase=2 status=200 responseURL=\"url\"]"); - }); - -}); - -describe("HTTPStatusEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(HTTPStatusEvent.toString()).toBe("[class HTTPStatusEvent]"); - }); - -}); - -describe("HTTPStatusEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new HTTPStatusEvent("test"); - expect(object.namespace).toBe("next2d.events.HTTPStatusEvent"); - }); - - it("namespace test static", () => - { - expect(HTTPStatusEvent.namespace).toBe("next2d.events.HTTPStatusEvent"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/events/IOErrorEventTest.ts b/__tests__/next2d/events/IOErrorEventTest.ts deleted file mode 100644 index aa67d9f5..00000000 --- a/__tests__/next2d/events/IOErrorEventTest.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { IOErrorEvent } from "../../../packages/events/src/IOErrorEvent"; - -describe("IOErrorEvent.js toString test", () => -{ - // toString - it("toString test1 success", () => - { - let event = new IOErrorEvent(""); - expect(event.toString()).toBe("[IOErrorEvent type=\"\" bubbles=false cancelable=false eventPhase=2 text=\"\"]"); - }); - - it("toString test2 success", () => - { - let event = new IOErrorEvent("ioError", false, false, "IOErrorEvent"); - expect(event.toString()).toBe("[IOErrorEvent type=\"ioError\" bubbles=false cancelable=false eventPhase=2 text=\"IOErrorEvent\"]"); - }); - -}); - -describe("IOErrorEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(IOErrorEvent.toString()).toBe("[class IOErrorEvent]"); - }); - -}); - -describe("IOErrorEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new IOErrorEvent("test"); - expect(object.namespace).toBe("next2d.events.IOErrorEvent"); - }); - - it("namespace test static", () => - { - expect(IOErrorEvent.namespace).toBe("next2d.events.IOErrorEvent"); - }); - -}); - -describe("IOErrorEvent.js property test", () => -{ - - it("IO_ERROR test", function () { - expect(IOErrorEvent.IO_ERROR).toBe("ioError"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/events/MouseEventTest.ts b/__tests__/next2d/events/MouseEventTest.ts deleted file mode 100644 index 5cf8db8c..00000000 --- a/__tests__/next2d/events/MouseEventTest.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { MouseEvent } from "../../../packages/events/src/MouseEvent"; - -describe("MouseEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(MouseEvent.toString()).toBe("[class MouseEvent]"); - }); - -}); - -describe("MouseEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new MouseEvent("test"); - expect(object.namespace).toBe("next2d.events.MouseEvent"); - }); - - it("namespace test static", () => - { - expect(MouseEvent.namespace).toBe("next2d.events.MouseEvent"); - }); - -}); - -describe("MouseEvent.js property test", () => -{ - - it("CLICK test", () => - { - expect(MouseEvent.CLICK).toBe("click"); - }); - - it("DOUBLE_CLICK test", () => - { - expect(MouseEvent.DOUBLE_CLICK).toBe("dblclick"); - }); - - it("MOUSE_DOWN test", () => - { - expect(MouseEvent.MOUSE_DOWN).toBe("mouseDown"); - }); - - it("MOUSE_MOVE test", () => - { - expect(MouseEvent.MOUSE_MOVE).toBe("mouseMove"); - }); - - it("MOUSE_OUT test", () => - { - expect(MouseEvent.MOUSE_OUT).toBe("mouseOut"); - }); - - it("MOUSE_OVER test", () => - { - expect(MouseEvent.MOUSE_OVER).toBe("mouseOver"); - }); - - it("MOUSE_UP test", () => - { - expect(MouseEvent.MOUSE_UP).toBe("mouseUp"); - }); - - it("MOUSE_WHEEL test", () =>{ - expect(MouseEvent.MOUSE_WHEEL).toBe("mouseWheel"); - }); - - it("ROLL_OUT test", () => - { - expect(MouseEvent.ROLL_OUT).toBe("rollOut"); - }); - - it("ROLL_OVER test", () => - { - expect(MouseEvent.ROLL_OVER).toBe("rollOver"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/events/ProgressEventTest.ts b/__tests__/next2d/events/ProgressEventTest.ts deleted file mode 100644 index d3c6df29..00000000 --- a/__tests__/next2d/events/ProgressEventTest.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ProgressEvent } from "../../../packages/events/src/ProgressEvent"; - -describe("ProgressEvent.js toString test", () => -{ - it("toString test success", () => - { - let event = new ProgressEvent(""); - expect(event.toString()).toBe("[ProgressEvent type=\"\" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=0]"); - }); - -}); - -describe("ProgressEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(ProgressEvent.toString()).toBe("[class ProgressEvent]"); - }); - -}); - -describe("ProgressEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new ProgressEvent("test"); - expect(object.namespace).toBe("next2d.events.ProgressEvent"); - }); - - it("namespace test static", () => - { - expect(ProgressEvent.namespace).toBe("next2d.events.ProgressEvent"); - }); - -}); - -describe("ProgressEvent.js property test", () => -{ - - it("PROGRESS test", () => { - expect(ProgressEvent.PROGRESS).toBe("progress"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/events/VideoEventTest.ts b/__tests__/next2d/events/VideoEventTest.ts deleted file mode 100644 index 327b37c3..00000000 --- a/__tests__/next2d/events/VideoEventTest.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { VideoEvent } from "../../../packages/events/src/VideoEvent"; - -describe("VideoEvent.js toString test", () => -{ - it("toString test success", () => - { - let event = new VideoEvent(""); - expect(event.toString()).toBe("[VideoEvent type=\"\" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=0]"); - }); - -}); - -describe("VideoEvent.js static toString test", () => -{ - - it("static toString test", () => - { - expect(VideoEvent.toString()).toBe("[class VideoEvent]"); - }); - -}); - -describe("VideoEvent.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new VideoEvent("test"); - expect(object.namespace).toBe("next2d.events.VideoEvent"); - }); - - it("namespace test static", () => - { - expect(VideoEvent.namespace).toBe("next2d.events.VideoEvent"); - }); - -}); - -describe("VideoEvent.js property test", () => -{ - - it("PLAY_START test", () => - { - expect(VideoEvent.PLAY_START).toBe("playStart"); - }); - - it("PLAY test", () => - { - expect(VideoEvent.PLAY).toBe("play"); - }); - - it("PROGRESS test", () => - { - expect(VideoEvent.PROGRESS).toBe("progress"); - }); - - it("PLAY_END test", () => - { - expect(VideoEvent.PLAY_END).toBe("playEnd"); - }); - - it("PAUSE test", () => - { - expect(VideoEvent.PAUSE).toBe("pause"); - }); - - it("SEEK test", () => - { - expect(VideoEvent.SEEK).toBe("seek"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/BevelFilterTest.ts b/__tests__/next2d/filters/BevelFilterTest.ts deleted file mode 100644 index 2e18668d..00000000 --- a/__tests__/next2d/filters/BevelFilterTest.ts +++ /dev/null @@ -1,2244 +0,0 @@ -import { BevelFilter } from "../../../packages/filters/src/BevelFilter"; - -describe("BevelFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new BevelFilter(); - expect(filter.toString()).toBe("[object BevelFilter]"); - }); - -}); - -describe("BevelFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(BevelFilter.toString()).toBe("[class BevelFilter]"); - }); - -}); - -describe("BevelFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new BevelFilter(); - expect(object.namespace).toBe("next2d.filters.BevelFilter"); - }); - - it("namespace test static", () => - { - expect(BevelFilter.namespace).toBe("next2d.filters.BevelFilter"); - }); - -}); - -describe("BevelFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new BevelFilter(); - expect(filter.angle).toBe(45); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.distance).toBe(4); - expect(filter.highlightAlpha).toBe(1); - expect(filter.highlightColor).toBe(0xffffff); - expect(filter.knockout).toBe(false); - expect(filter.quality).toBe(1); - expect(filter.shadowAlpha).toBe(1); - expect(filter.shadowColor).toBe(0x000000); - expect(filter.strength).toBe(1); - expect(filter.type).toBe("inner"); - }); - - // distance - it("distance test success case1", () => - { - let filter = new BevelFilter(10.5); - expect(filter.distance).toBe(10.5); - }); - - it("distance test success case2", () => - { - let filter = new BevelFilter(10.5); - filter.distance = -12.6; - expect(filter.distance).toBe(-12.6); - }); - - it("distance test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter("23"); - expect(filter.distance).toBe(23); - }); - - it("distance test valid case2", () => - { - let filter = new BevelFilter(1000); - expect(filter.distance).toBe(255); - }); - - it("distance test valid case3", () => - { - let filter = new BevelFilter(-1000); - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case4", () => - { - let filter = new BevelFilter(10); - // @ts-ignore - filter.distance = "-56"; - expect(filter.distance).toBe(-56); - }); - - it("distance test valid case5", () => - { - let filter = new BevelFilter(10); - filter.distance = 400; - expect(filter.distance).toBe(255); - }); - - it("distance test valid case6", () => - { - let filter = new BevelFilter(10); - filter.distance = -400; - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter("test"); - expect(filter.distance).toBe(4); - }); - - it("distance test valid case8", () => - { - let filter = new BevelFilter(10); - // @ts-ignore - filter.distance = "abc"; - expect(filter.distance).toBe(4); - }); - - // angle - it("angle test success case1", () => - { - let filter = new BevelFilter(10.5, 90); - expect(filter.angle).toBe(90); - }); - - it("angle test success case2", () => - { - let filter = new BevelFilter(10.5, 90); - filter.angle = 180; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, "20"); - expect(filter.angle).toBe(20); - }); - - it("angle test valid case2", () => - { - let filter = new BevelFilter(10.5, 20); - // @ts-ignore - filter.angle = "180"; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case3", () => - { - let filter = new BevelFilter(10.5, 20); - filter.angle = 4500; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case4", () => - { - let filter = new BevelFilter(10.5, 20); - filter.angle = -4500; - expect(filter.angle).toBe(-180); - }); - - it("angle test valid case5", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, "test"); - expect(filter.angle).toBe(45); - }); - - it("angle test valid case6", () => - { - let filter = new BevelFilter(10.5, 20); - // @ts-ignore - filter.angle = "abc"; - expect(filter.angle).toBe(45); - }); - - // highlightColor - it("highlightColor test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0x000000); - expect(filter.highlightColor).toBe(0x000000); - }); - - it("highlightColor test success case2", () => - { - let filter = new BevelFilter(10.5, 90, 0xff0000); - filter.highlightColor = 0x00ff00; - expect(filter.highlightColor).toBe(0x00ff00); - }); - - it("highlightColor test valid case3", () => - { - let filter = new BevelFilter(10.5, 90, -10); - expect(filter.highlightColor).toBe(0); - }); - - it("highlightColor test valid case4", () => - { - let filter = new BevelFilter(10.5, 90, 16777220); - expect(filter.highlightColor).toBe(0xffffff); - }); - - // highlightAlpha - it("highlightAlpha test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 0); - expect(filter.highlightAlpha).toBe(0); - }); - - it("highlightAlpha test success case2", () => - { - let filter = new BevelFilter(10.5, 90, 0xff0000, 0.5); - filter.highlightAlpha = 0.75; - expect(filter.highlightAlpha).toBe(0.75); - }); - - it("highlightAlpha test valid case2", () => - { - let filter = new BevelFilter(10.5, 90, 0xff0000, -10); - expect(filter.highlightAlpha).toBe(0); - }); - - it("highlightAlpha test valid case3", () => - { - let filter = new BevelFilter(10.5, 90, 0xff0000, 10); - expect(filter.highlightAlpha).toBe(1); - }); - - // shadowColor - it("shadowColor test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0); - expect(filter.shadowColor).toBe(0); - }); - - it("shadowColor test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xff0000); - filter.shadowColor = 0x00ff00; - expect(filter.shadowColor).toBe(0x00ff00); - }); - - it("shadowColor test valid case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, -10); - expect(filter.shadowColor).toBe(0); - }); - - it("shadowColor test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 16777220); - expect(filter.shadowColor).toBe(0xffffff); - }); - - // shadowAlpha - it("shadowAlpha test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 0); - expect(filter.shadowAlpha).toBe(0); - }); - - it("shadowAlpha test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 0.5); - filter.shadowAlpha = 0.75; - expect(filter.shadowAlpha).toBe(0.75); - }); - - it("shadowAlpha test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, "0.25"); - expect(filter.shadowAlpha).toBe(0.25); - }); - - it("shadowAlpha test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, -10); - expect(filter.shadowAlpha).toBe(0); - }); - - it("shadowAlpha test valid case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 10); - expect(filter.shadowAlpha).toBe(1); - }); - - it("shadowAlpha test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1); - - // @ts-ignore - filter.shadowAlpha = "0.75"; - expect(filter.shadowAlpha).toBe(0.75); - - filter.shadowAlpha = -10; - expect(filter.shadowAlpha).toBe(0); - - filter.shadowAlpha = 16777220; - expect(filter.shadowAlpha).toBe(1); - }); - - // blurX - it("blurX test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 0); - expect(filter.blurX).toBe(0); - }); - - it("blurX test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 100); - filter.blurX = 50; - expect(filter.blurX).toBe(50); - }); - - it("blurX test valid case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 1000); - expect(filter.blurX).toBe(255); - }); - - it("blurX test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10); - filter.blurX = 1000; - expect(filter.blurX).toBe(255); - }); - - it("blurX test valid case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, -10); - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10); - filter.blurX = -1; - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case5", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, "100"); - expect(filter.blurX).toBe(100); - }); - - it("blurX test valid case6", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10); - // @ts-ignore - filter.blurX = "123"; - expect(filter.blurX).toBe(123); - }); - - it("blurX test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, "test"); - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case8", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10); - // @ts-ignore - filter.blurX = "123a"; - expect(filter.blurX).toBe(0); - }); - - // blurY - it("blurY test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 0); - expect(filter.blurY).toBe(0); - }); - - it("blurY test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 100); - filter.blurY = 50; - expect(filter.blurY).toBe(50); - }); - - it("blurY test valid case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 1000); - expect(filter.blurY).toBe(255); - }); - - it("blurY test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10); - filter.blurY = 1000; - expect(filter.blurY).toBe(255); - }); - - it("blurY test valid case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, -10); - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10); - filter.blurY = -1; - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case5", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, "100"); - expect(filter.blurY).toBe(100); - }); - - it("blurY test valid case6", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10); - // @ts-ignore - filter.blurY = "123"; - expect(filter.blurY).toBe(123); - }); - - it("blurY test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, "test"); - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case8", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10); - // @ts-ignore - filter.blurY = "123a"; - expect(filter.blurY).toBe(0); - }); - - // strength - it("strength test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 0); - expect(filter.strength).toBe(0); - }); - - it("strength test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 10); - filter.strength = 2; - expect(filter.strength).toBe(2); - }); - - it("strength test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, "9"); - expect(filter.strength).toBe(9); - }); - - it("strength test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 2); - // @ts-ignore - filter.strength = "9"; - expect(filter.strength).toBe(9); - }); - - it("strength test valid case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, -1); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 2); - filter.strength = -10; - expect(filter.strength).toBe(0); - }); - - it("strength test valid case5", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 1000); - expect(filter.strength).toBe(255); - }); - - it("strength test valid case6", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 2); - filter.strength = 1000; - expect(filter.strength).toBe(255); - }); - - it("strength test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, "test"); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case8", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3); - // @ts-ignore - filter.strength = "abc"; - expect(filter.strength).toBe(0); - }); - - // quality - it("quality test success case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 0); - expect(filter.quality).toBe(0); - }); - - it("quality test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 5); - filter.quality = 2; - expect(filter.quality).toBe(2); - }); - - it("quality test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 1000); - expect(filter.quality).toBe(15); - }); - - it("quality test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 2); - // @ts-ignore - filter.quality = 1000; - expect(filter.quality).toBe(15); - }); - - it("quality test valid case3", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, -1); - expect(filter.quality).toBe(0); - }); - - it("quality test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 5); - // @ts-ignore - filter.quality = -1; - expect(filter.quality).toBe(0); - }); - - it("quality test valid case5", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, "12"); - expect(filter.quality).toBe(12); - }); - - it("quality test valid case6", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10); - // @ts-ignore - filter.quality = "12"; - expect(filter.quality).toBe(12); - }); - - it("quality test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, "test"); - expect(filter.quality).toBe(0); - }); - - it("quality test valid case8", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10); - // @ts-ignore - filter.quality = "123a"; - expect(filter.quality).toBe(0); - }); - - // type - it("type test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner"); - expect(filter.type).toBe("inner"); - }); - - it("type test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "outer"); - expect(filter.type).toBe("outer"); - }); - - it("type test success case3", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "full"); - expect(filter.type).toBe("full"); - }); - - it("type test success case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner"); - filter.type = "outer"; - expect(filter.type).toBe("outer"); - }); - - it("type test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "outer"); - expect(filter.type).toBe("outer"); - }); - - it("type test valid case2", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "outer"); - filter.type = "full"; - expect(filter.type).toBe("full"); - }); - - // knockout - it("knockout test success case1", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", true); - expect(filter.knockout).toBe(true); - }); - - it("knockout test success case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", true); - filter.knockout = false; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case1", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", 1); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case2", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", false); - // @ts-ignore - filter.knockout = 1; - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case3", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", 0); - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case4", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", true); - // @ts-ignore - filter.knockout = 0; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case5", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", "test"); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case6", () => - { - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", true); - // @ts-ignore - filter.knockout = ""; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case7", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", {}); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case8", () => - { - // @ts-ignore - let filter = new BevelFilter(10.5, 20, 0xff0000, 1, 0xffffff, 1, 10, 10, 3, 10, "inner", []); - expect(filter.knockout).toBe(true); - }); - -}); - -describe("BevelFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new BevelFilter( - 1, 25, 0xff0000, 0.8, 0x00ff00, - 0.2, 120, 100, 9, 2, - "outer", true - - ); - let clone = filter.clone(); - - // clone check - expect(clone.angle).toBe(25); - expect(clone.blurX).toBe(120); - expect(clone.blurY).toBe(100); - expect(clone.distance).toBe(1); - expect(clone.highlightAlpha).toBe(0.8); - expect(clone.highlightColor).toBe(0xff0000); - expect(clone.knockout).toBe(true); - expect(clone.quality).toBe(2); - expect(clone.shadowAlpha).toBe(0.2); - expect(clone.shadowColor).toBe(0x00ff00); - expect(clone.strength).toBe(9); - expect(clone.type).toBe("outer"); - - // edit param - clone.angle = 90; - clone.blurX = 10; - clone.blurY = 10; - clone.distance = 10; - clone.highlightAlpha = 0.5; - clone.highlightColor = 0xff0000; - clone.knockout = false; - clone.quality = 3; - clone.shadowAlpha = 0.1; - clone.shadowColor = 0xffffff; - clone.strength = 10; - clone.type = "full"; - - // origin - expect(filter.angle).toBe(25); - expect(filter.blurX).toBe(120); - expect(filter.blurY).toBe(100); - expect(filter.distance).toBe(1); - expect(filter.highlightAlpha).toBe(0.8); - expect(filter.highlightColor).toBe(0xff0000); - expect(filter.knockout).toBe(true); - expect(filter.quality).toBe(2); - expect(filter.shadowAlpha).toBe(0.2); - expect(filter.shadowColor).toBe(0x00ff00); - expect(filter.strength).toBe(9); - expect(filter.type).toBe("outer"); - - // clone - expect(clone.angle).toBe(90); - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(10); - expect(clone.distance).toBe(10); - expect(clone.highlightAlpha).toBe(0.5); - expect(clone.highlightColor).toBe(0xff0000); - expect(clone.knockout).toBe(false); - expect(clone.quality).toBe(3); - expect(clone.shadowAlpha).toBe(0.1); - expect(clone.shadowColor).toBe(0xffffff); - expect(clone.strength).toBe(10); - expect(clone.type).toBe("full"); - - }); - -}); - -describe("BevelFilter.js knockout test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.knockout).toBe(false); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = null; - expect(bf.knockout).toBe(false); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = undefined; - expect(bf.knockout).toBe(false); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - bf.knockout = true; - expect(bf.knockout).toBe(true); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = ""; - expect(bf.knockout).toBe(false); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = "abc"; - expect(bf.knockout).toBe(true); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = 0; - expect(bf.knockout).toBe(false); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = 1; - expect(bf.knockout).toBe(true); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = 500; - expect(bf.knockout).toBe(true); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = 50000000000000000; - expect(bf.knockout).toBe(true); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = -1; - expect(bf.knockout).toBe(true); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = -500; - expect(bf.knockout).toBe(true); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = -50000000000000000; - expect(bf.knockout).toBe(true); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = { "a":0 }; - expect(bf.knockout).toBe(true); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = function a() {}; - expect(bf.knockout).toBe(true); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = [1]; - expect(bf.knockout).toBe(true); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = [1,2]; - expect(bf.knockout).toBe(true); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = {}; - expect(bf.knockout).toBe(true); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = { "toString":() => { return 1 } }; - expect(bf.knockout).toBe(true); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = { "toString":() => { return "1" } }; - expect(bf.knockout).toBe(true); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.knockout = { "toString":() => { return "1a" } }; - expect(bf.knockout).toBe(true); - }); - -}); - -describe("BevelFilter.js angle test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.angle).toBe(45); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = null; - expect(bf.angle).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = undefined; - expect(bf.angle).toBe(45); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = true; - expect(bf.angle).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = ""; - expect(bf.angle).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = "abc"; - expect(bf.angle).toBe(45); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.angle = 0; - expect(bf.angle).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.angle = 1; - expect(bf.angle).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.angle = 500; - expect(bf.angle).toBe(140); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.angle = 50000000000000000; - expect(bf.angle).toBe(320); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.angle = -1; - expect(bf.angle).toBe(-1); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.angle = -500; - expect(bf.angle).toBe(-140); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.angle = -50000000000000000; - expect(bf.angle).toBe(-320); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = { "a":0 }; - expect(bf.angle).toBe(45); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = function a() {}; - expect(bf.angle).toBe(45); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = [1]; - expect(bf.angle).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = [1,2]; - expect(bf.angle).toBe(45); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = {}; - expect(bf.angle).toBe(45); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = { "toString":() => { return 1 } }; - expect(bf.angle).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = { "toString":() => { return "1" } }; - expect(bf.angle).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.angle = { "toString":() => { return "1a" } }; - expect(bf.angle).toBe(45); - }); - -}); - -describe("BevelFilter.js blurX test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.blurX).toBe(4); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = null; - expect(bf.blurX).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = undefined; - expect(bf.blurX).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = true; - expect(bf.blurX).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = ""; - expect(bf.blurX).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = "abc"; - expect(bf.blurX).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.blurX = 0; - expect(bf.blurX).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.blurX = 1; - expect(bf.blurX).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.blurX = 500; - expect(bf.blurX).toBe(255); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.blurX = 50000000000000000; - expect(bf.blurX).toBe(255); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.blurX = -1; - expect(bf.blurX).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.blurX = -500; - expect(bf.blurX).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.blurX = -50000000000000000; - expect(bf.blurX).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = { "a":0 }; - expect(bf.blurX).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = function a() {}; - expect(bf.blurX).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = [1]; - expect(bf.blurX).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = [1,2]; - expect(bf.blurX).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = {}; - expect(bf.blurX).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return 1 } }; - expect(bf.blurX).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return "1" } }; - expect(bf.blurX).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return "1a" } }; - expect(bf.blurX).toBe(0); - }); - -}); - -describe("BevelFilter.js distance test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.distance).toBe(4); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = null; - expect(bf.distance).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = undefined; - expect(bf.distance).toBe(4); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = true; - expect(bf.distance).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = ""; - expect(bf.distance).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = "abc"; - expect(bf.distance).toBe(4); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.distance = 0; - expect(bf.distance).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.distance = 1; - expect(bf.distance).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.distance = 500; - expect(bf.distance).toBe(255); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.distance = 50000000000000000; - expect(bf.distance).toBe(255); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.distance = -1; - expect(bf.distance).toBe(-1); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.distance = -500; - expect(bf.distance).toBe(-255); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.distance = -50000000000000000; - expect(bf.distance).toBe(-255); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = { "a":0 }; - expect(bf.distance).toBe(4); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = function a() {}; - expect(bf.distance).toBe(4); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = [1]; - expect(bf.distance).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = [1,2]; - expect(bf.distance).toBe(4); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = {}; - expect(bf.distance).toBe(4); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = { "toString":() => { return 1 } }; - expect(bf.distance).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = { "toString":() => { return "1" } }; - expect(bf.distance).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.distance = { "toString":() => { return "1a" } }; - expect(bf.distance).toBe(4); - }); - -}); - -describe("BevelFilter.js highlightAlpha test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = null; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = undefined; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = true; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = ""; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = "abc"; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = 0; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = 1; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = 500; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = 50000000000000000; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = -1; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = -500; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.highlightAlpha = -50000000000000000; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = { "a":0 }; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = function a() {}; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = [1]; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = [1,2]; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = {}; - expect(bf.highlightAlpha).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = { "toString":() => { return 1 } }; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = { "toString":() => { return "1" } }; - expect(bf.highlightAlpha).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.highlightAlpha = { "toString":() => { return "1a" } }; - expect(bf.highlightAlpha).toBe(0); - }); - -}); - -describe("BevelFilter.js highlightColor test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.highlightColor).toBe(16777215); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.highlightColor = 0; - expect(bf.highlightColor).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.highlightColor = 1; - expect(bf.highlightColor).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.highlightColor = 500; - expect(bf.highlightColor).toBe(500); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.highlightColor = 50000000000000000; - expect(bf.highlightColor).toBe(0xffffff); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.highlightColor = -1; - expect(bf.highlightColor).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.highlightColor = -500; - expect(bf.highlightColor).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.highlightColor = -50000000000000000; - expect(bf.highlightColor).toBe(0); - }); -}); - -describe("BevelFilter.js quality test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.quality).toBe(1); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = null; - expect(bf.quality).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = undefined; - expect(bf.quality).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = true; - expect(bf.quality).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = ""; - expect(bf.quality).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = "abc"; - expect(bf.quality).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.quality = 0; - expect(bf.quality).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.quality = 1; - expect(bf.quality).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = 500; - expect(bf.quality).toBe(15); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = 50000000000000000; - expect(bf.quality).toBe(15); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = -1; - expect(bf.quality).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = -500; - expect(bf.quality).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = -50000000000000000; - expect(bf.quality).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = { "a":0 }; - expect(bf.quality).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = function a() {}; - expect(bf.quality).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = [1]; - expect(bf.quality).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = [1,2]; - expect(bf.quality).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = {}; - expect(bf.quality).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return 1 } }; - expect(bf.quality).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return "1" } }; - expect(bf.quality).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return "1a" } }; - expect(bf.quality).toBe(0); - }); - -}); - -describe("BevelFilter.js shadowAlpha test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = null; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = undefined; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = true; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = ""; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = "abc"; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = 0; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = 1; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = 500; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = 50000000000000000; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = -1; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = -500; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.shadowAlpha = -50000000000000000; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = { "a":0 }; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = function a() {}; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = [1]; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = [1,2]; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = {}; - expect(bf.shadowAlpha).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = { "toString":() => { return 1 } }; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = { "toString":() => { return "1" } }; - expect(bf.shadowAlpha).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.shadowAlpha = { "toString":() => { return "1a" } }; - expect(bf.shadowAlpha).toBe(0); - }); - -}); - -describe("BevelFilter.js shadowColor test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.shadowColor).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.shadowColor = 0; - expect(bf.shadowColor).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.shadowColor = 1; - expect(bf.shadowColor).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.shadowColor = 500; - expect(bf.shadowColor).toBe(500); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.shadowColor = 50000000000000000; - expect(bf.shadowColor).toBe(0xffffff); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.shadowColor = -1; - expect(bf.shadowColor).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.shadowColor = -500; - expect(bf.shadowColor).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.shadowColor = -50000000000000000; - expect(bf.shadowColor).toBe(0); - }); -}); - -describe("BevelFilter.js strength test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.strength).toBe(1); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = null; - expect(bf.strength).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = undefined; - expect(bf.strength).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = true; - expect(bf.strength).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = ""; - expect(bf.strength).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = "abc"; - expect(bf.strength).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BevelFilter(); - bf.strength = 0; - expect(bf.strength).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BevelFilter(); - bf.strength = 1; - expect(bf.strength).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BevelFilter(); - bf.strength = 500; - expect(bf.strength).toBe(255); - }); - - it("default test case10", () => - { - let bf = new BevelFilter(); - bf.strength = 50000000000000000; - expect(bf.strength).toBe(255); - }); - - it("default test case11", () => - { - let bf = new BevelFilter(); - bf.strength = -1; - expect(bf.strength).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BevelFilter(); - bf.strength = -500; - expect(bf.strength).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BevelFilter(); - bf.strength = -50000000000000000; - expect(bf.strength).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = { "a":0 }; - expect(bf.strength).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = function a() {}; - expect(bf.strength).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = [1]; - expect(bf.strength).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = [1,2]; - expect(bf.strength).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = {}; - expect(bf.strength).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = { "toString":() => { return 1 } }; - expect(bf.strength).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = { "toString":() => { return "1" } }; - expect(bf.strength).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BevelFilter(); - // @ts-ignore - bf.strength = { "toString":() => { return "1a" } }; - expect(bf.strength).toBe(0); - }); - -}); - -describe("BevelFilter.js type test", () => -{ - - it("default test case1", () => - { - let bf = new BevelFilter(); - expect(bf.type).toBe("inner"); - }); - - it("default test case2", () => - { - let bf = new BevelFilter(); - bf.type = "full"; - expect(bf.type).toBe("full"); - }); -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/BitmapFilterTest.ts b/__tests__/next2d/filters/BitmapFilterTest.ts deleted file mode 100644 index 48874654..00000000 --- a/__tests__/next2d/filters/BitmapFilterTest.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BitmapFilter } from "../../../packages/filters/src/BitmapFilter"; - -describe("BitmapFilter.js toString test", function() -{ - - // toString - it("toString test success", function() - { - let bitmapFilter = new BitmapFilter(); - expect(bitmapFilter.toString()).toBe("[object BitmapFilter]"); - }); - -}); - -describe("BitmapFilter.js static toString test", function() -{ - - it("static toString test", function() - { - expect(BitmapFilter.toString()).toBe("[class BitmapFilter]"); - }); - -}); - -describe("BitmapFilter.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new BitmapFilter(); - expect(object.namespace).toBe("next2d.filters.BitmapFilter"); - }); - - it("namespace test static", function() - { - expect(BitmapFilter.namespace).toBe("next2d.filters.BitmapFilter"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/BlurFilterTest.ts b/__tests__/next2d/filters/BlurFilterTest.ts deleted file mode 100644 index 36048ed9..00000000 --- a/__tests__/next2d/filters/BlurFilterTest.ts +++ /dev/null @@ -1,779 +0,0 @@ -import { BlurFilter } from "../../../packages/filters/src/BlurFilter"; - -describe("BlurFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new BlurFilter(); - expect(object.namespace).toBe("next2d.filters.BlurFilter"); - }); - - it("namespace test static", () => - { - expect(BlurFilter.namespace).toBe("next2d.filters.BlurFilter"); - }); - -}); - -describe("BlurFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new BlurFilter(); - expect(filter.toString()).toBe("[object BlurFilter]"); - }); - -}); - -describe("BlurFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(BlurFilter.toString()).toBe("[class BlurFilter]"); - }); - -}); - -describe("BlurFilter.js property test", () => -{ - // default - it("default test success", () => - { - let filter = new BlurFilter(); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.quality).toBe(1); - }); - - // blurX - it("blurX test success case1", () => - { - let filter = new BlurFilter(0); - expect(filter.blurX).toBe(0); - }); - - it("blurX test success case2", () => - { - let filter = new BlurFilter(100); - filter.blurX = 50; - expect(filter.blurX).toBe(50); - }); - - it("blurX test valid case1", () => - { - let filter = new BlurFilter(1000); - expect(filter.blurX).toBe(255); - }); - - it("blurX test valid case2", () => - { - let filter = new BlurFilter(10); - filter.blurX = 1000; - expect(filter.blurX).toBe(255); - }); - - it("blurX test valid case3", () => - { - let filter = new BlurFilter(-10); - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case4", () => - { - let filter = new BlurFilter(10); - filter.blurX = -1; - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case5", () => - { - // @ts-ignore - let filter = new BlurFilter("100"); - expect(filter.blurX).toBe(100); - }); - - it("blurX test valid case6", () => - { - let filter = new BlurFilter(10); - // @ts-ignore - filter.blurX = "123"; - expect(filter.blurX).toBe(123); - }); - - it("blurX test valid case7", () => - { - // @ts-ignore - let filter = new BlurFilter("test"); - expect(filter.blurX).toBe(0); - }); - - it("blurX test valid case8", () => - { - let filter = new BlurFilter(10); - // @ts-ignore - filter.blurX = "123a"; - expect(filter.blurX).toBe(0); - }); - - // blurY - it("blurY test success case1", () => - { - let filter = new BlurFilter(10, 0); - expect(filter.blurY).toBe(0); - }); - - it("blurY test success case2", () => - { - let filter = new BlurFilter(10, 100); - filter.blurY = 50; - expect(filter.blurY).toBe(50); - }); - - it("blurY test valid case1", () => - { - let filter = new BlurFilter(10, 1000); - expect(filter.blurY).toBe(255); - }); - - it("blurY test valid case2", () => - { - let filter = new BlurFilter(10, 10); - filter.blurY = 1000; - expect(filter.blurY).toBe(255); - }); - - it("blurY test valid case3", () => - { - let filter = new BlurFilter(10, -10); - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case4", () => - { - let filter = new BlurFilter(10, 10); - filter.blurY = -1; - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case5", () => - { - // @ts-ignore - let filter = new BlurFilter(10, "100"); - expect(filter.blurY).toBe(100); - }); - - it("blurY test valid case6", () => - { - let filter = new BlurFilter(10, 10); - // @ts-ignore - filter.blurY = "123"; - expect(filter.blurY).toBe(123); - }); - - it("blurY test valid case7", () => - { - // @ts-ignore - let filter = new BlurFilter(10, "test"); - expect(filter.blurY).toBe(0); - }); - - it("blurY test valid case8", () => - { - let filter = new BlurFilter(10, 10); - // @ts-ignore - filter.blurY = "123a"; - expect(filter.blurY).toBe(0); - }); - - // quality - it("quality test success case1", () => - { - let filter = new BlurFilter(10, 100, 0); - expect(filter.quality).toBe(0); - }); - - it("quality test success case2", () => - { - let filter = new BlurFilter(10, 100, 5); - filter.quality = 2; - expect(filter.quality).toBe(2); - }); - - it("quality test valid case1", () => - { - // @ts-ignore - let filter = new BlurFilter(10, 100, 1000); - expect(filter.quality).toBe(15); - }); - - it("quality test valid case2", () => - { - let filter = new BlurFilter(10, 100, 2); - // @ts-ignore - filter.quality = 1000; - expect(filter.quality).toBe(15); - }); - - it("quality test valid case3", () => - { - // @ts-ignore - let filter = new BlurFilter(10, 100, -1); - expect(filter.quality).toBe(0); - }); - - it("quality test valid case4", () => - { - let filter = new BlurFilter(10, 100, 5); - // @ts-ignore - filter.quality = -1; - expect(filter.quality).toBe(0); - }); - - it("quality test valid case5", () => - { - // @ts-ignore - let filter = new BlurFilter(10, 100, "12"); - expect(filter.quality).toBe(12); - }); - - it("quality test valid case6", () => - { - let filter = new BlurFilter(10, 100, 10); - // @ts-ignore - filter.quality = "12"; - expect(filter.quality).toBe(12); - }); - - it("quality test valid case7", () => - { - // @ts-ignore - let filter = new BlurFilter(10, 100, "test"); - expect(filter.quality).toBe(0); - }); - - it("quality test valid case8", () => - { - let filter = new BlurFilter(10, 100, 10); - // @ts-ignore - filter.quality = "123a"; - expect(filter.quality).toBe(0); - }); - -}); - -describe("BlurFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new BlurFilter(20, 40, 5); - let clone = filter.clone(); - - // clone check - expect(clone.blurX).toBe(20); - expect(clone.blurY).toBe(40); - expect(clone.quality).toBe(5); - - // edit param - clone.blurX = 10; - clone.blurY = 50; - clone.quality = 9; - - // origin - expect(filter.blurX).toBe(20); - expect(filter.blurY).toBe(40); - expect(filter.quality).toBe(5); - - // clone - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(50); - expect(clone.quality).toBe(9); - - }); - -}); - -describe("BlurFilter.js blurX test", () => -{ - - it("default test case1", () => - { - let bf = new BlurFilter(); - expect(bf.blurX).toBe(4); - }); - - it("default test case2", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = null; - expect(bf.blurX).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = undefined; - expect(bf.blurX).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = true; - expect(bf.blurX).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = ""; - expect(bf.blurX).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = "abc"; - expect(bf.blurX).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BlurFilter(); - bf.blurX = 0; - expect(bf.blurX).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BlurFilter(); - bf.blurX = 1; - expect(bf.blurX).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BlurFilter(); - bf.blurX = 500; - expect(bf.blurX).toBe(255); - }); - - it("default test case10", () => - { - let bf = new BlurFilter(); - bf.blurX = 50000000000000000; - expect(bf.blurX).toBe(255); - }); - - it("default test case11", () => - { - let bf = new BlurFilter(); - bf.blurX = -1; - expect(bf.blurX).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BlurFilter(); - bf.blurX = -500; - expect(bf.blurX).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BlurFilter(); - bf.blurX = -50000000000000000; - expect(bf.blurX).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = { "a":0 }; - expect(bf.blurX).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = function a() {}; - expect(bf.blurX).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = [1]; - expect(bf.blurX).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = [1,2]; - expect(bf.blurX).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = {}; - expect(bf.blurX).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return 1 } }; - expect(bf.blurX).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return "1" } }; - expect(bf.blurX).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurX = { "toString":() => { return "1a" } }; - expect(bf.blurX).toBe(0); - }); - -}); - -describe("BlurFilter.js blurY test", () => -{ - - it("default test case1", () => - { - let bf = new BlurFilter(); - expect(bf.blurY).toBe(4); - }); - - it("default test case2", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = null; - expect(bf.blurY).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = undefined; - expect(bf.blurY).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = true; - expect(bf.blurY).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = ""; - expect(bf.blurY).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = "abc"; - expect(bf.blurY).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BlurFilter(); - bf.blurY = 0; - expect(bf.blurY).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BlurFilter(); - bf.blurY = 1; - expect(bf.blurY).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BlurFilter(); - bf.blurY = -1; - expect(bf.blurY).toBe(0); - }); - - it("default test case10", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = { "a":0 }; - expect(bf.blurY).toBe(0); - }); - - it("default test case11", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = function a() {}; - expect(bf.blurY).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = [1]; - expect(bf.blurY).toBe(1); - }); - - it("default test case13", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = [1,2]; - expect(bf.blurY).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = {}; - expect(bf.blurY).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = { "toString":() => { return 1 } }; - expect(bf.blurY).toBe(1); - }); - - it("default test case16", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = { "toString":() => { return "1" } }; - expect(bf.blurY).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.blurY = { "toString":() => { return "1a" } }; - expect(bf.blurY).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BlurFilter(); - bf.blurY = 500; - expect(bf.blurY).toBe(255); - }); - - it("default test case20", () => - { - let bf = new BlurFilter(); - bf.blurY = -500; - expect(bf.blurY).toBe(0); - }); - -}); - -describe("BlurFilter.js quality test", () => -{ - - it("default test case1", () => - { - let bf = new BlurFilter(); - expect(bf.quality).toBe(1); - }); - - it("default test case2", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = null; - expect(bf.quality).toBe(0); - }); - - it("default test case3", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = undefined; - expect(bf.quality).toBe(0); - }); - - it("default test case4", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = true; - expect(bf.quality).toBe(1); - }); - - it("default test case5", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = ""; - expect(bf.quality).toBe(0); - }); - - it("default test case6", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = "abc"; - expect(bf.quality).toBe(0); - }); - - it("default test case7", () => - { - let bf = new BlurFilter(); - bf.quality = 0; - expect(bf.quality).toBe(0); - }); - - it("default test case8", () => - { - let bf = new BlurFilter(); - bf.quality = 1; - expect(bf.quality).toBe(1); - }); - - it("default test case9", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = 500; - expect(bf.quality).toBe(15); - }); - - it("default test case10", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = 50000000000000000; - expect(bf.quality).toBe(15); - }); - - it("default test case11", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = -1; - expect(bf.quality).toBe(0); - }); - - it("default test case12", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = -500; - expect(bf.quality).toBe(0); - }); - - it("default test case13", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = -50000000000000000; - expect(bf.quality).toBe(0); - }); - - it("default test case14", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = { "a":0 }; - expect(bf.quality).toBe(0); - }); - - it("default test case15", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = function a() {}; - expect(bf.quality).toBe(0); - }); - - it("default test case16", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = [1]; - expect(bf.quality).toBe(1); - }); - - it("default test case17", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = [1,2]; - expect(bf.quality).toBe(0); - }); - - it("default test case18", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = {}; - expect(bf.quality).toBe(0); - }); - - it("default test case19", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return 1 } }; - expect(bf.quality).toBe(1); - }); - - it("default test case20", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return "1" } }; - expect(bf.quality).toBe(1); - }); - - it("default test case21", () => - { - let bf = new BlurFilter(); - // @ts-ignore - bf.quality = { "toString":() => { return "1a" } }; - expect(bf.quality).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/ColorMatrixFilterTest.ts b/__tests__/next2d/filters/ColorMatrixFilterTest.ts deleted file mode 100644 index f39b10d3..00000000 --- a/__tests__/next2d/filters/ColorMatrixFilterTest.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { ColorMatrixFilter } from "../../../packages/filters/src/ColorMatrixFilter"; - -describe("ColorMatrixFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new ColorMatrixFilter(); - expect(object.namespace).toBe("next2d.filters.ColorMatrixFilter"); - }); - - it("namespace test static", () => - { - expect(ColorMatrixFilter.namespace).toBe("next2d.filters.ColorMatrixFilter"); - }); - -}); - -describe("ColorMatrixFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new ColorMatrixFilter(); - expect(filter.toString()).toBe("[object ColorMatrixFilter]"); - }); - -}); - -describe("ColorMatrixFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(ColorMatrixFilter.toString()).toBe("[class ColorMatrixFilter]"); - }); - -}); - -describe("ColorMatrixFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let matrix = [ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0 - ]; - - let filter = new ColorMatrixFilter(); - for (let i = 0; i < matrix.length; i++) { - expect(filter.matrix[i]).toBe(matrix[i]); - } - }); - - // matrix - it("matrix test success case1", () => - { - let filter = new ColorMatrixFilter([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]); - expect(filter.matrix.length).toBe(20); - }); - - it("matrix test success case2", () => - { - let filter = new ColorMatrixFilter(); - filter.matrix = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; - expect(filter.matrix.length).toBe(20); - }); - - it("matrix test valid case1", () => - { - let matrix = [ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0 - ]; - - let filter = new ColorMatrixFilter([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]); - for (let i = 0; i < matrix.length; i++) { - expect(filter.matrix[i]).toBe(matrix[i]); - } - }); - - it("matrix test valid case2", () => - { - let matrix = [ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0 - ]; - - let filter = new ColorMatrixFilter(); - filter.matrix = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]; - for (let i = 0; i < matrix.length; i++) { - expect(filter.matrix[i]).toBe(matrix[i]); - } - }); - -}); - -describe("ColorMatrixFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new ColorMatrixFilter([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]); - let clone = filter.clone(); - - // clone check - expect(clone.matrix[0]).toBe(1); - expect(clone.matrix[1]).toBe(2); - expect(clone.matrix[2]).toBe(3); - expect(clone.matrix[3]).toBe(4); - expect(clone.matrix[4]).toBe(5); - expect(clone.matrix[5]).toBe(6); - expect(clone.matrix[6]).toBe(7); - expect(clone.matrix[7]).toBe(8); - expect(clone.matrix[8]).toBe(9); - expect(clone.matrix[9]).toBe(10); - expect(clone.matrix[10]).toBe(11); - expect(clone.matrix[11]).toBe(12); - expect(clone.matrix[12]).toBe(13); - expect(clone.matrix[13]).toBe(14); - expect(clone.matrix[14]).toBe(15); - expect(clone.matrix[15]).toBe(16); - expect(clone.matrix[16]).toBe(17); - expect(clone.matrix[17]).toBe(18); - expect(clone.matrix[18]).toBe(19); - expect(clone.matrix[19]).toBe(20); - - // edit param - clone.matrix = [20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]; - - // origin - expect(filter.matrix[0]).toBe(1); - expect(filter.matrix[1]).toBe(2); - expect(filter.matrix[2]).toBe(3); - expect(filter.matrix[3]).toBe(4); - expect(filter.matrix[4]).toBe(5); - expect(filter.matrix[5]).toBe(6); - expect(filter.matrix[6]).toBe(7); - expect(filter.matrix[7]).toBe(8); - expect(filter.matrix[8]).toBe(9); - expect(filter.matrix[9]).toBe(10); - expect(filter.matrix[10]).toBe(11); - expect(filter.matrix[11]).toBe(12); - expect(filter.matrix[12]).toBe(13); - expect(filter.matrix[13]).toBe(14); - expect(filter.matrix[14]).toBe(15); - expect(filter.matrix[15]).toBe(16); - expect(filter.matrix[16]).toBe(17); - expect(filter.matrix[17]).toBe(18); - expect(filter.matrix[18]).toBe(19); - expect(filter.matrix[19]).toBe(20); - - // clone - expect(clone.matrix[0]).toBe(20); - expect(clone.matrix[1]).toBe(19); - expect(clone.matrix[2]).toBe(18); - expect(clone.matrix[3]).toBe(17); - expect(clone.matrix[4]).toBe(16); - expect(clone.matrix[5]).toBe(15); - expect(clone.matrix[6]).toBe(14); - expect(clone.matrix[7]).toBe(13); - expect(clone.matrix[8]).toBe(12); - expect(clone.matrix[9]).toBe(11); - expect(clone.matrix[10]).toBe(10); - expect(clone.matrix[11]).toBe(9); - expect(clone.matrix[12]).toBe(8); - expect(clone.matrix[13]).toBe(7); - expect(clone.matrix[14]).toBe(6); - expect(clone.matrix[15]).toBe(5); - expect(clone.matrix[16]).toBe(4); - expect(clone.matrix[17]).toBe(3); - expect(clone.matrix[18]).toBe(2); - expect(clone.matrix[19]).toBe(1); - - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/ConvolutionFilterTest.ts b/__tests__/next2d/filters/ConvolutionFilterTest.ts deleted file mode 100644 index 8be353d0..00000000 --- a/__tests__/next2d/filters/ConvolutionFilterTest.ts +++ /dev/null @@ -1,1345 +0,0 @@ -import { ConvolutionFilter } from "../../../packages/filters/src/ConvolutionFilter"; - -describe("ConvolutionFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new ConvolutionFilter(); - expect(object.namespace).toBe("next2d.filters.ConvolutionFilter"); - }); - - it("namespace test static", () => - { - expect(ConvolutionFilter.namespace).toBe("next2d.filters.ConvolutionFilter"); - }); - -}); - -describe("ConvolutionFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new ConvolutionFilter(); - expect(filter.toString()).toBe("[object ConvolutionFilter]"); - }); - -}); - -describe("ConvolutionFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(ConvolutionFilter.toString()).toBe("[class ConvolutionFilter]"); - }); - -}); - -describe("ConvolutionFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new ConvolutionFilter(); - expect(filter.alpha).toBe(0); - expect(filter.bias).toBe(0); - expect(filter.clamp).toBe(true); - expect(filter.color).toBe(0); - expect(filter.divisor).toBe(1); - expect(filter.matrix).toBe(null); - expect(filter.matrixX).toBe(0); - expect(filter.matrixY).toBe(0); - expect(filter.preserveAlpha).toBe(true); - }); - - // matrixX - it("matrixX test success case1", () => - { - let filter = new ConvolutionFilter(0); - expect(filter.matrixX).toBe(0); - }); - - it("matrixX test success case2", () => - { - let filter = new ConvolutionFilter(0); - filter.matrixX = 10; - expect(filter.matrixX).toBe(10); - }); - - it("matrixX test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter("10"); - expect(filter.matrixX).toBe(10); - }); - - it("matrixX test valid case2", () => - { - let filter = new ConvolutionFilter(1); - // @ts-ignore - filter.matrixX = "10"; - expect(filter.matrixX).toBe(10); - }); - - it("matrixX test valid case3", () => - { - // @ts-ignore - let filter = new ConvolutionFilter("test"); - expect(filter.matrixX).toBe(0); - }); - - it("matrixX test valid case4", () => - { - let filter = new ConvolutionFilter(1); - // @ts-ignore - filter.matrixX = "abc"; - expect(filter.matrixX).toBe(0); - }); - - // matrixY - it("matrixY test success case1", () => - { - let filter = new ConvolutionFilter(3, 0); - expect(filter.matrixY).toBe(0); - }); - - it("matrixY test success case2", () => - { - let filter = new ConvolutionFilter(3, 0); - filter.matrixY = 10; - expect(filter.matrixY).toBe(10); - }); - - it("matrixY test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(2, "10"); - expect(filter.matrixY).toBe(10); - }); - - it("matrixY test valid case2", () => - { - let filter = new ConvolutionFilter(3, 1); - // @ts-ignore - filter.matrixY = "10"; - expect(filter.matrixY).toBe(10); - }); - - it("matrixY test valid case3", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, "test"); - expect(filter.matrixY).toBe(0); - }); - - it("matrixY test valid case4", () => - { - let filter = new ConvolutionFilter(3, 1); - // @ts-ignore - filter.matrixY = "abc"; - expect(filter.matrixY).toBe(0); - }); - - // matrix - it("matrix test success case1", () => - { - let filter = new ConvolutionFilter(3, 1, [1,2,3,4,5,6,7,8,9]); - // @ts-ignore - expect(filter.matrix.length).toBe(9); - }); - - it("matrix test success case2", () => - { - let filter = new ConvolutionFilter(3, 1, null); - filter.matrix = [1,2,3,4,5,6,7,8,9]; - // @ts-ignore - expect(filter.matrix.length).toBe(9); - }); - - it("matrix test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 1, {}); - expect(filter.matrix).toBe(null); - }); - - // divisor - it("divisor test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 2); - expect(filter.divisor).toBe(2); - }); - - it("divisor test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 2); - filter.divisor = 4.5; - expect(filter.divisor).toBe(4); - }); - - it("divisor test valid case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0); - expect(filter.divisor).toBe(0); - }); - - it("divisor test valid case2", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], "4"); - expect(filter.divisor).toBe(4); - }); - - it("divisor test valid case3", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 2); - // @ts-ignore - filter.divisor = "3"; - expect(filter.divisor).toBe(3); - }); - - // bias - it("bias test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 2); - expect(filter.bias).toBe(2); - }); - - it("bias test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 2); - filter.bias = 4.5; - expect(filter.bias).toBe(4); - }); - - it("bias test valid case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 0); - expect(filter.bias).toBe(0); - }); - - it("bias test valid case2", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, "4"); - expect(filter.bias).toBe(4); - }); - - it("bias test valid case3", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 2); - // @ts-ignore - filter.bias = "3"; - expect(filter.bias).toBe(3); - }); - - // preserveAlpha - it("preserveAlpha test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 2, false); - expect(filter.preserveAlpha).toBe(false); - }); - - it("preserveAlpha test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 2, true); - filter.preserveAlpha = false; - expect(filter.preserveAlpha).toBe(false); - }); - - it("preserveAlpha test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, ""); - expect(filter.preserveAlpha).toBe(false); - }); - - it("preserveAlpha test valid case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true); - // @ts-ignore - filter.preserveAlpha = ""; - expect(filter.preserveAlpha).toBe(false); - }); - - it("preserveAlpha test valid case3", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, "abc"); - expect(filter.preserveAlpha).toBe(true); - }); - - it("preserveAlpha test valid case4", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, []); - expect(filter.preserveAlpha).toBe(true); - }); - - it("preserveAlpha test valid case5", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, {}); - expect(filter.preserveAlpha).toBe(true); - }); - - // clamp - it("clamp test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true); - expect(filter.clamp).toBe(true); - }); - - it("clamp test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true); - filter.clamp = false; - expect(filter.clamp).toBe(false); - }); - - it("clamp test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, 1); - expect(filter.clamp).toBe(true); - }); - - it("clamp test valid case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, false); - // @ts-ignore - filter.clamp = 1; - expect(filter.clamp).toBe(true); - }); - - it("clamp test valid case3", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, 0); - expect(filter.clamp).toBe(false); - }); - - it("clamp test valid case4", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true); - // @ts-ignore - filter.clamp = 0; - expect(filter.clamp).toBe(false); - }); - - it("clamp test valid case5", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, "test"); - expect(filter.clamp).toBe(true); - }); - - it("clamp test valid case6", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true); - // @ts-ignore - filter.clamp = ""; - expect(filter.clamp).toBe(false); - }); - - it("clamp test valid case7", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, {}); - expect(filter.clamp).toBe(true); - }); - - it("clamp test valid case8", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, []); - expect(filter.clamp).toBe(true); - }); - - // color - it("color test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0); - expect(filter.color).toBe(0); - }); - - it("color test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xff0000); - filter.color = 0x00ff00; - expect(filter.color).toBe(0x00ff00); - }); - - it("color test valid case3", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, -10); - expect(filter.color).toBe(0); - }); - - it("color test valid case4", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 16777220); - expect(filter.color).toBe(0xffffff); - }); - - // alpha - it("alpha test success case1", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xffffff, 0); - expect(filter.alpha).toBe(0); - }); - - it("alpha test success case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xffffff, 0.5); - filter.alpha = 0.75; - expect(filter.alpha).toBe(0.75); - }); - - it("alpha test valid case1", () => - { - // @ts-ignore - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xffffff, "0.25"); - expect(filter.alpha).toBe(0.25); - }); - - it("alpha test valid case2", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xffffff, -10); - expect(filter.alpha).toBe(0); - }); - - it("alpha test valid case3", () => - { - let filter = new ConvolutionFilter(3, 3, [1,2,3,4,5,6,7,8,9], 0, 10, true, true, 0xffffff, 10); - expect(filter.alpha).toBe(1); - }); -}); - -describe("ConvolutionFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new ConvolutionFilter( - 2, 2, [1,2,3,4], 2, 1, - false, false, 0xff0000, 1 - ); - let clone = filter.clone(); - - // clone check - expect(clone.matrixX).toBe(2); - expect(clone.matrixY).toBe(2); - // @ts-ignore - expect(clone.matrix[0]).toBe(1); - // @ts-ignore - expect(clone.matrix[1]).toBe(2); - // @ts-ignore - expect(clone.matrix[2]).toBe(3); - // @ts-ignore - expect(clone.matrix[3]).toBe(4); - expect(clone.divisor).toBe(2); - expect(clone.bias).toBe(1); - expect(clone.preserveAlpha).toBe(false); - expect(clone.clamp).toBe(false); - expect(clone.color).toBe(0xff0000); - expect(clone.alpha).toBe(1); - - // edit param - clone.matrixX = 3; - clone.matrixY = 3; - clone.matrix = [1,2,3,4,5,6,7,8,9]; - clone.divisor = 1; - clone.bias = 2; - clone.preserveAlpha = true; - clone.clamp = true; - clone.color = 0xffffff; - clone.alpha = 0.5; - - // origin - expect(filter.matrixX).toBe(2); - expect(filter.matrixY).toBe(2); - // @ts-ignore - expect(filter.matrix[0]).toBe(1); - // @ts-ignore - expect(filter.matrix[1]).toBe(2); - // @ts-ignore - expect(filter.matrix[2]).toBe(3); - // @ts-ignore - expect(filter.matrix[3]).toBe(4); - expect(filter.divisor).toBe(2); - expect(filter.bias).toBe(1); - expect(filter.preserveAlpha).toBe(false); - expect(filter.clamp).toBe(false); - expect(filter.color).toBe(0xff0000); - expect(filter.alpha).toBe(1); - - // clone - expect(clone.matrixX).toBe(3); - expect(clone.matrixY).toBe(3); - // @ts-ignore - expect(clone.matrix[0]).toBe(1); - // @ts-ignore - expect(clone.matrix[1]).toBe(2); - // @ts-ignore - expect(clone.matrix[2]).toBe(3); - // @ts-ignore - expect(clone.matrix[3]).toBe(4); - // @ts-ignore - expect(clone.matrix[4]).toBe(5); - // @ts-ignore - expect(clone.matrix[5]).toBe(6); - // @ts-ignore - expect(clone.matrix[6]).toBe(7); - // @ts-ignore - expect(clone.matrix[7]).toBe(8); - // @ts-ignore - expect(clone.matrix[8]).toBe(9); - expect(clone.divisor).toBe(1); - expect(clone.bias).toBe(2); - expect(clone.preserveAlpha).toBe(true); - expect(clone.clamp).toBe(true); - expect(clone.color).toBe(0xffffff); - expect(clone.alpha).toBe(0.5); - - }); - -}); - -describe("ConvolutionFilter.js clamp test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.clamp).toBe(true); - }); - - it("default test case2", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = null; - expect(cf.clamp).toBe(false); - }); - - it("default test case3", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = undefined; - expect(cf.clamp).toBe(false); - }); - - it("default test case4", () => - { - let cf = new ConvolutionFilter(); - cf.clamp = true; - expect(cf.clamp).toBe(true); - }); - - it("default test case5", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = ""; - expect(cf.clamp).toBe(false); - }); - - it("default test case6", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = "abc"; - expect(cf.clamp).toBe(true); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = 0; - expect(cf.clamp).toBe(false); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = 1; - expect(cf.clamp).toBe(true); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = 500; - expect(cf.clamp).toBe(true); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = 50000000000000000; - expect(cf.clamp).toBe(true); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = -1; - expect(cf.clamp).toBe(true); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = -500; - expect(cf.clamp).toBe(true); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = -50000000000000000; - expect(cf.clamp).toBe(true); - }); - - it("default test case14", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = { "a":0 }; - expect(cf.clamp).toBe(true); - }); - - it("default test case15", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = function a() {}; - expect(cf.clamp).toBe(true); - }); - - it("default test case16", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = [1]; - expect(cf.clamp).toBe(true); - }); - - it("default test case17", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = [1,2]; - expect(cf.clamp).toBe(true); - }); - - it("default test case18", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = {}; - expect(cf.clamp).toBe(true); - }); - - it("default test case19", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = { "toString":() => { return 1 } }; - expect(cf.clamp).toBe(true); - }); - - it("default test case20", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = { "toString":() => { return "1" } }; - expect(cf.clamp).toBe(true); - }); - - it("default test case21", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.clamp = { "toString":() => { return "1a" } }; - expect(cf.clamp).toBe(true); - }); - -}); - -describe("ConvolutionFilter.js preserveAlpha test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case2", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = null; - expect(cf.preserveAlpha).toBe(false); - }); - - it("default test case3", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = undefined; - expect(cf.preserveAlpha).toBe(false); - }); - - it("default test case4", () => - { - let cf = new ConvolutionFilter(); - cf.preserveAlpha = true; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case5", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = ""; - expect(cf.preserveAlpha).toBe(false); - }); - - it("default test case6", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = "abc"; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = 0; - expect(cf.preserveAlpha).toBe(false); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = 1; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = 500; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = 50000000000000000; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = -1; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = -500; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = -50000000000000000; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case14", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = { "a":0 }; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case15", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = function a() {}; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case16", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = [1]; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case17", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = [1,2]; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case18", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = {}; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case19", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = { "toString":() => { return 1 } }; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case20", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = { "toString":() => { return "1" } }; - expect(cf.preserveAlpha).toBe(true); - }); - - it("default test case21", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.preserveAlpha = { "toString":() => { return "1a" } }; - expect(cf.preserveAlpha).toBe(true); - }); - -}); - -describe("ConvolutionFilter.js alpha test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.alpha).toBe(0); - }); - - it("default test case2", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = null; - expect(cf.alpha).toBe(0); - }); - - it("default test case3", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = undefined; - expect(cf.alpha).toBe(0); - }); - - it("default test case4", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = true; - expect(cf.alpha).toBe(1); - }); - - it("default test case5", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = ""; - expect(cf.alpha).toBe(0); - }); - - it("default test case6", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = "abc"; - expect(cf.alpha).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = 0; - expect(cf.alpha).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = 1; - expect(cf.alpha).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = 500; - expect(cf.alpha).toBe(1); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = 50000000000000000; - expect(cf.alpha).toBe(1); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = -1; - expect(cf.alpha).toBe(0); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = -500; - expect(cf.alpha).toBe(0); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - cf.alpha = -50000000000000000; - expect(cf.alpha).toBe(0); - }); - - it("default test case14", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = { "a":0 }; - expect(cf.alpha).toBe(0); - }); - - it("default test case15", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = function a() {}; - expect(cf.alpha).toBe(0); - }); - - it("default test case16", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = [1]; - expect(cf.alpha).toBe(1); - }); - - it("default test case17", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = [1,2]; - expect(cf.alpha).toBe(0); - }); - - it("default test case18", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = {}; - expect(cf.alpha).toBe(0); - }); - - it("default test case19", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = { "toString":() => { return 1 } }; - expect(cf.alpha).toBe(1); - }); - - it("default test case20", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = { "toString":() => { return "1" } }; - expect(cf.alpha).toBe(1); - }); - - it("default test case21", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.alpha = { "toString":() => { return "1a" } }; - expect(cf.alpha).toBe(0); - }); - -}); - -describe("ConvolutionFilter.js bias test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.bias).toBe(0); - }); - - it("default test case4", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.bias = true; - expect(cf.bias).toBe(1); - }); - - it("default test case5", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.bias = ""; - expect(cf.bias).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.bias = 0; - expect(cf.bias).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.bias = 1; - expect(cf.bias).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.bias = 500; - expect(cf.bias).toBe(500); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.bias = -1; - expect(cf.bias).toBe(-1); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.bias = -500; - expect(cf.bias).toBe(-500); - }); - -}); - -describe("ConvolutionFilter.js color test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.color).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.color = 0; - expect(cf.color).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.color = 1; - expect(cf.color).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.color = 500; - expect(cf.color).toBe(500); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - cf.color = 50000000000000000; - expect(cf.color).toBe(0xffffff); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.color = -1; - expect(cf.color).toBe(0); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.color = -500; - expect(cf.color).toBe(0); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - cf.color = -50000000000000000; - expect(cf.color).toBe(0); - }); -}); - -describe("ConvolutionFilter.js divisor test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.divisor).toBe(1); - }); - - it("default test case2", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.divisor = null; - expect(cf.divisor).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.divisor = 0; - expect(cf.divisor).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.divisor = 1; - expect(cf.divisor).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.divisor = 500; - expect(cf.divisor).toBe(500); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.divisor = -1; - expect(cf.divisor).toBe(-1); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.divisor = -500; - expect(cf.divisor).toBe(-500); - }); -}); - -describe("ConvolutionFilter.js matrixX test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.matrixX).toBe(0); - }); - - it("default test case4", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.matrixX = true; - expect(cf.matrixX).toBe(1); - }); - - it("default test case5", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.matrixX = ""; - expect(cf.matrixX).toBe(0); - }); - - it("default test case6", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.matrixX = "abc"; - expect(cf.matrixX).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = 0; - expect(cf.matrixX).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = 1; - expect(cf.matrixX).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = 500; - expect(cf.matrixX).toBe(15); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = 50000000000000000; - expect(cf.matrixX).toBe(15); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = -1; - expect(cf.matrixX).toBe(0); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = -500; - expect(cf.matrixX).toBe(0); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - cf.matrixX = -50000000000000000; - expect(cf.matrixX).toBe(0); - }); -}); - -describe("ConvolutionFilter.js matrixY test", () => -{ - - it("default test case1", () => - { - let cf = new ConvolutionFilter(); - expect(cf.matrixY).toBe(0); - }); - - it("default test case2", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.matrixY = null; - expect(cf.matrixY).toBe(0); - }); - - it("default test case3", () => - { - let cf = new ConvolutionFilter(); - // @ts-ignore - cf.matrixY = undefined; - expect(cf.matrixY).toBe(0); - }); - - it("default test case7", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = 0; - expect(cf.matrixY).toBe(0); - }); - - it("default test case8", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = 1; - expect(cf.matrixY).toBe(1); - }); - - it("default test case9", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = 500; - expect(cf.matrixY).toBe(15); - }); - - it("default test case10", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = 50000000000000000; - expect(cf.matrixY).toBe(15); - }); - - it("default test case11", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = -1; - expect(cf.matrixY).toBe(0); - }); - - it("default test case12", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = -500; - expect(cf.matrixY).toBe(0); - }); - - it("default test case13", () => - { - let cf = new ConvolutionFilter(); - cf.matrixY = -50000000000000000; - expect(cf.matrixY).toBe(0); - }); -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/DisplacementMapFilterTest.ts b/__tests__/next2d/filters/DisplacementMapFilterTest.ts deleted file mode 100644 index 866d6f59..00000000 --- a/__tests__/next2d/filters/DisplacementMapFilterTest.ts +++ /dev/null @@ -1,793 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { DisplacementMapFilter } from "../../../packages/filters/src/DisplacementMapFilter"; -import { Point } from "../../../packages/geom/src/Point"; - -describe("DisplacementMapFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new DisplacementMapFilter(); - expect($PREFIX).toBe("__next2d__"); - expect(object.namespace).toBe("next2d.filters.DisplacementMapFilter"); - }); - - it("namespace test static", () => - { - expect(DisplacementMapFilter.namespace).toBe("next2d.filters.DisplacementMapFilter"); - }); - -}); - -describe("DisplacementMapFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new DisplacementMapFilter(); - expect(filter.toString()).toBe("[object DisplacementMapFilter]"); - }); - -}); - -describe("DisplacementMapFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(DisplacementMapFilter.toString()).toBe("[class DisplacementMapFilter]"); - }); - -}); - -// describe("DisplacementMapFilter.js property test", () => -// { -// -// // default -// it("default test success", () => -// { -// let filter = new DisplacementMapFilter(); -// expect(filter.mapBitmap).toBe(null); -// expect(filter.mapPoint).toBe(null); -// expect(filter.componentX).toBe(0); -// expect(filter.componentY).toBe(0); -// expect(filter.scaleX).toBe(0); -// expect(filter.scaleY).toBe(0); -// expect(filter.mode).toBe("wrap"); -// expect(filter.color).toBe(0); -// expect(filter.alpha).toBe(0); -// }); -// -// // mapBitmap -// it("mapBitmap test success case1", () => -// { -// let filter = new DisplacementMapFilter(new Image(100, 100)); -// expect(filter.mapBitmap instanceof Image).toBe(true); -// }); -// -// it("mapBitmap test success case2", () => -// { -// let filter = new DisplacementMapFilter(new Image()); -// filter.mapBitmap = null; -// expect(filter.mapBitmap).toBe(null); -// }); -// -// // mapPoint -// it("mapPoint test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0) -// ); -// expect(filter.mapPoint instanceof Point).toBe(true); -// }); -// -// it("mapPoint test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0) -// ); -// filter.mapPoint = null; -// expect(filter.mapPoint).toBe(null); -// }); -// -// // componentX -// it("componentX test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1 -// ); -// expect(filter.componentX).toBe(1); -// }); -// -// it("componentX test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1 -// ); -// filter.componentX = 8; -// expect(filter.componentX).toBe(8); -// }); -// -// it("componentX test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1 -// ); -// filter.componentX = 2; -// expect(filter.componentX).toBe(2); -// }); -// -// // componentY -// it("componentY test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2 -// ); -// expect(filter.componentY).toBe(2); -// }); -// -// it("componentY test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2 -// ); -// filter.componentY = 8; -// expect(filter.componentY).toBe(8); -// }); -// -// it("componentY test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2 -// ); -// filter.componentY = 4; -// expect(filter.componentY).toBe(4); -// }); -// -// // scaleX -// it("scaleX test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10 -// ); -// expect(filter.scaleX).toBe(10); -// }); -// -// it("scaleX test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10 -// ); -// filter.scaleX = -20; -// expect(filter.scaleX).toBe(-20); -// }); -// -// it("scaleX test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 100 -// ); -// // @ts-ignore -// filter.scaleX = "abc"; -// expect(filter.scaleX).toBe(0); -// }); -// -// // scaleY -// it("scaleY test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20 -// ); -// expect(filter.scaleY).toBe(20); -// }); -// -// it("scaleY test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 30 -// ); -// filter.scaleY = -40; -// expect(filter.scaleY).toBe(-40); -// }); -// -// it("scaleY test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 100, -// 50 -// ); -// // @ts-ignore -// filter.scaleY = "abc"; -// expect(filter.scaleY).toBe(0); -// }); -// -// // mode -// it("mode test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp" -// ); -// expect(filter.mode).toBe("clamp"); -// }); -// -// it("mode test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 30, -// "clamp" -// ); -// filter.mode = "ignore"; -// expect(filter.mode).toBe("ignore"); -// }); -// -// it("mode test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 30, -// "clamp" -// ); -// expect(filter.mode).toBe("clamp"); -// filter.mode = "ignore"; -// expect(filter.mode).toBe("ignore"); -// }); -// -// // color -// it("color test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0 -// ); -// expect(filter.color).toBe(0); -// }); -// -// it("color test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xff0000 -// ); -// filter.color = 0x00ff00; -// expect(filter.color).toBe(0x00ff00); -// }); -// -// it("color test valid case3", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// -10 -// ); -// expect(filter.color).toBe(0); -// }); -// -// it("color test valid case4", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 16777220 -// ); -// expect(filter.color).toBe(0xffffff); -// }); -// -// it("color test valid case5", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff -// ); -// -// filter.color = -10; -// expect(filter.color).toBe(0); -// -// filter.color = 16777220; -// expect(filter.color).toBe(0xffffff); -// }); -// -// // alpha -// it("alpha test success case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// 0 -// ); -// expect(filter.alpha).toBe(0); -// }); -// -// it("alpha test success case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// 0.5 -// ); -// filter.alpha = 0.75; -// expect(filter.alpha).toBe(0.75); -// }); -// -// it("alpha test valid case1", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// // @ts-ignore -// "0.25" -// ); -// expect(filter.alpha).toBe(0.25); -// }); -// -// it("alpha test valid case2", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// -10 -// ); -// expect(filter.alpha).toBe(0); -// }); -// -// it("alpha test valid case3", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// 10 -// ); -// expect(filter.alpha).toBe(1); -// }); -// -// it("alpha test valid case4", () => -// { -// let filter = new DisplacementMapFilter( -// new Image(100, 100), -// new Point(0,0), -// 1, -// 2, -// 10, -// 20, -// "clamp", -// 0xffffff, -// 1 -// ); -// -// // @ts-ignore -// filter.alpha = "0.75"; -// expect(filter.alpha).toBe(0.75); -// -// filter.alpha = -10; -// expect(filter.alpha).toBe(0); -// -// filter.alpha = 16777220; -// expect(filter.alpha).toBe(1); -// }); -// }); -// -// describe("DisplacementMapFilter.js clone test", () => -// { -// -// it("clone test", () => -// { -// let bitmapData = new Image(100, 100); -// let point = new Point(); -// let filter = new DisplacementMapFilter( -// bitmapData, -// point, -// 1, -// 2, -// 1.0, 2.0, -// "clamp", -// 20, -// 0.5 -// ); -// -// let clone = filter.clone(); -// -// // clone check -// expect(clone.mapBitmap instanceof Image).toBe(true); -// expect(clone.mapPoint instanceof Point).toBe(true); -// expect(clone.componentX).toBe(1); -// expect(clone.componentY).toBe(2); -// expect(clone.scaleX).toBe(1); -// expect(clone.scaleY).toBe(2); -// expect(clone.mode).toBe("clamp"); -// expect(clone.color).toBe(20); -// expect(clone.alpha).toBe(0.5); -// -// // edit param -// clone.mapBitmap = null; -// clone.mapPoint = null; -// clone.componentX = 4; -// clone.componentY = 8; -// clone.scaleX = 3; -// clone.scaleY = 4; -// clone.mode = "color"; -// clone.color = 100; -// clone.alpha = 1; -// -// // origin -// expect(filter.mapBitmap instanceof Image).toBe(true); -// expect(filter.mapPoint instanceof Point).toBe(true); -// expect(filter.componentX).toBe(1); -// expect(filter.componentY).toBe(2); -// expect(filter.scaleX).toBe(1); -// expect(filter.scaleY).toBe(2); -// expect(filter.mode).toBe("clamp"); -// expect(filter.color).toBe(20); -// expect(filter.alpha).toBe(0.5); -// -// // clone -// expect(clone.mapBitmap).toBe(null); -// expect(clone.mapPoint).toBe(null); -// expect(clone.componentX).toBe(4); -// expect(clone.componentY).toBe(8); -// expect(clone.scaleX).toBe(3); -// expect(clone.scaleY).toBe(4); -// expect(clone.mode).toBe("color"); -// expect(clone.color).toBe(100); -// expect(clone.alpha).toBe(1); -// -// }); -// -// }); - -describe("DisplacementMapFilter.js alpha test", () => -{ - - it("default test case1", () => - { - let dm = new DisplacementMapFilter(); - expect(dm.alpha).toBe(0); - }); - - it("default test case7", () => - { - let dm = new DisplacementMapFilter(); - dm.alpha = 0; - expect(dm.alpha).toBe(0); - }); - - it("default test case8", () => - { - let dm = new DisplacementMapFilter(); - dm.alpha = 1; - expect(dm.alpha).toBe(1); - }); - - it("default test case9", () => - { - let dm = new DisplacementMapFilter(); - dm.alpha = -1; - expect(dm.alpha).toBe(0); - }); - - it("default test case19", () => - { - - let ds = new DisplacementMapFilter(); - ds.alpha = 500; - expect(ds.alpha).toBe(1); - }); - - it("default test case20", () => - { - - let ds = new DisplacementMapFilter(); - ds.alpha = -500; - expect(ds.alpha).toBe(0); - }); - -}); - -describe("DisplacementMapFilter.js color test", () => -{ - - it("default test case1", () => - { - let dm = new DisplacementMapFilter(); - expect(dm.color).toBe(0); - }); - - it("default test case7", () => - { - let dm = new DisplacementMapFilter(); - dm.color = 0; - expect(dm.color).toBe(0); - }); - - it("default test case8", () => - { - let dm = new DisplacementMapFilter(); - dm.color = 1; - expect(dm.color).toBe(1); - }); - - it("default test case9", () => - { - let dm = new DisplacementMapFilter(); - dm.color = -1; - expect(dm.color).toBe(0); - }); - - it("default test case19", () => - { - let dm = new DisplacementMapFilter(); - dm.color = 500; - expect(dm.color).toBe(500); - }); - - it("default test case20", () => - { - let dm = new DisplacementMapFilter(); - dm.color = -500; - expect(dm.color).toBe(0); - }); - -}); - -describe("DisplacementMapFilter.js componentX test", () => -{ - - it("default test case1", () => - { - let dm = new DisplacementMapFilter(); - expect(dm.componentX).toBe(0); - }); - - it("default test case7", () => - { - let dm = new DisplacementMapFilter(); - dm.componentX = 0; - expect(dm.componentX).toBe(0); - }); - - it("default test case8", () => - { - let dm = new DisplacementMapFilter(); - dm.componentX = 1; - expect(dm.componentX).toBe(1); - }); -}); - -describe("DisplacementMapFilter.js componentY test", () => -{ - - it("default test case1", () => - { - - let dm = new DisplacementMapFilter(); - expect(dm.componentY).toBe(0); - }); - - it("default test case7", () => - { - - let dm = new DisplacementMapFilter(); - dm.componentY = 0; - expect(dm.componentY).toBe(0); - }); - - it("default test case8", () => - { - - let dm = new DisplacementMapFilter(); - dm.componentY = 1; - expect(dm.componentY).toBe(1); - }); -}); - -describe("DisplacementMapFilter.js scaleX test", () => -{ - - it("default test case1", () => - { - - let dm = new DisplacementMapFilter(); - expect(dm.scaleX).toBe(0); - }); - - it("default test case7", () => - { - - let dm = new DisplacementMapFilter(); - dm.scaleX = 0; - expect(dm.scaleX).toBe(0); - }); - - it("default test case8", () => - { - - let dm = new DisplacementMapFilter(); - dm.scaleX = 1; - expect(dm.scaleX).toBe(1); - }); - - it("default test case9", () => - { - - let dm = new DisplacementMapFilter(); - dm.scaleX = -1; - expect(dm.scaleX).toBe(-1); - }); - - it("default test case19", () => - { - - let dm = new DisplacementMapFilter(); - dm.scaleX = 500; - expect(dm.scaleX).toBe(500); - }); - - it("default test case20", () => - { - - let dm = new DisplacementMapFilter(); - dm.scaleX = -500; - expect(dm.scaleX).toBe(-500); - }); - -}); - -describe("DisplacementMapFilter.js scaleY test", () => -{ - - it("default test case1", () => - { - - let dm = new DisplacementMapFilter(); - expect(dm.scaleY).toBe(0); - }); - - it("default test case7", () => - { - let dm = new DisplacementMapFilter(); - dm.scaleY = 0; - expect(dm.scaleY).toBe(0); - }); - - it("default test case8", () => - { - let dm = new DisplacementMapFilter(); - dm.scaleY = 1; - expect(dm.scaleY).toBe(1); - }); - - it("default test case9", () => - { - let dm = new DisplacementMapFilter(); - dm.scaleY = -1; - expect(dm.scaleY).toBe(-1); - }); - - it("default test case19", () => - { - let dm = new DisplacementMapFilter(); - dm.scaleY = 500; - expect(dm.scaleY).toBe(500); - }); - - it("default test case20", () => - { - let dm = new DisplacementMapFilter(); - dm.scaleY = -500; - expect(dm.scaleY).toBe(-500); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/DropShadowFilterTest.ts b/__tests__/next2d/filters/DropShadowFilterTest.ts deleted file mode 100644 index d0f890b2..00000000 --- a/__tests__/next2d/filters/DropShadowFilterTest.ts +++ /dev/null @@ -1,1196 +0,0 @@ -import { DropShadowFilter } from "../../../packages/filters/src/DropShadowFilter"; - -describe("DropShadowFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new DropShadowFilter(); - expect(object.namespace).toBe("next2d.filters.DropShadowFilter"); - }); - - it("namespace test static", () => - { - expect(DropShadowFilter.namespace).toBe("next2d.filters.DropShadowFilter"); - }); - -}); - -describe("DropShadowFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new DropShadowFilter(); - expect(filter.toString()).toBe("[object DropShadowFilter]"); - }); - -}); - -describe("DropShadowFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(DropShadowFilter.toString()).toBe("[class DropShadowFilter]"); - }); - -}); - -describe("DropShadowFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new DropShadowFilter(); - expect(filter.distance).toBe(4); - expect(filter.angle).toBe(45); - expect(filter.color).toBe(0); - expect(filter.alpha).toBe(1); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.strength).toBe(1); - expect(filter.quality).toBe(1); - expect(filter.inner).toBe(false); - expect(filter.knockout).toBe(false); - expect(filter.hideObject).toBe(false); - }); - - // distance - it("distance test success case1", () => - { - let filter = new DropShadowFilter(0); - expect(filter.distance).toBe(0); - }); - - it("distance test success case2", () => - { - let filter = new DropShadowFilter(10.5); - filter.distance = -12.6; - expect(filter.distance).toBe(-12.6); - }); - - it("distance test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter("23"); - expect(filter.distance).toBe(23); - }); - - it("distance test valid case2", () => - { - let filter = new DropShadowFilter(1000); - expect(filter.distance).toBe(255); - }); - - it("distance test valid case3", () => - { - let filter = new DropShadowFilter(-1000); - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case4", () => - { - let filter = new DropShadowFilter(10); - // @ts-ignore - filter.distance = "-56"; - expect(filter.distance).toBe(-56); - }); - - it("distance test valid case5", () => - { - let filter = new DropShadowFilter(10); - filter.distance = 400; - expect(filter.distance).toBe(255); - }); - - it("distance test valid case6", () => - { - let filter = new DropShadowFilter(10); - filter.distance = -400; - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case7", () => - { - // @ts-ignore - let filter = new DropShadowFilter("test"); - expect(filter.distance).toBe(4); - }); - - it("distance test valid case8", () => - { - let filter = new DropShadowFilter(10); - // @ts-ignore - filter.distance = "abc"; - expect(filter.distance).toBe(4); - }); - - // angle - it("angle test success case1", () => - { - let filter = new DropShadowFilter(10.5, 0); - expect(filter.angle).toBe(0); - }); - - it("angle test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90); - filter.angle = 180; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, "20"); - expect(filter.angle).toBe(20); - }); - - it("angle test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 20); - // @ts-ignore - filter.angle = "180"; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case3", () => - { - let filter = new DropShadowFilter(10.5, 20); - filter.angle = 4500; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 20); - filter.angle = -4500; - expect(filter.angle).toBe(-180); - }); - - it("angle test valid case5", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, "test"); - expect(filter.angle).toBe(45); - }); - - it("angle test valid case6", () => - { - let filter = new DropShadowFilter(10.5, 20); - // @ts-ignore - filter.angle = "abc"; - expect(filter.angle).toBe(45); - }); - - // color - it("color test success case1", () => - { - let filter = new DropShadowFilter(10.5, 20, 0); - expect(filter.color).toBe(0); - }); - - it("color test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xff0000); - filter.color = 0x00ff00; - expect(filter.color).toBe(0x00ff00); - }); - - it("color test valid case3", () => - { - let filter = new DropShadowFilter(10.5, 90, -10); - expect(filter.color).toBe(0); - }); - - it("color test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 16777220); - expect(filter.color).toBe(0xffffff); - }); - - it("color test valid case5", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff); - - filter.color = -10; - expect(filter.color).toBe(0); - - filter.color = 16777220; - expect(filter.color).toBe(0xffffff); - }); - - // alpha - it("alpha test success case1", () => - { - let filter = new DropShadowFilter(10.5, 20, 0xff0000, 0); - expect(filter.alpha).toBe(0); - }); - - it("alpha test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xff0000, 0.5); - filter.alpha = 0.75; - expect(filter.alpha).toBe(0.75); - }); - - it("alpha test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xff0000, "0.25"); - expect(filter.alpha).toBe(0.25); - }); - - it("alpha test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xff0000, -10); - expect(filter.alpha).toBe(0); - }); - - it("alpha test valid case3", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xff0000, 10); - expect(filter.alpha).toBe(1); - }); - - it("alpha test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1); - - filter.alpha = -10; - expect(filter.alpha).toBe(0); - - filter.alpha = 16777220; - expect(filter.alpha).toBe(1); - }); - - // strength - it("strength test success case1", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 100, 0); - expect(filter.strength).toBe(0); - }); - - it("strength test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 100, 10); - filter.strength = 2; - expect(filter.strength).toBe(2); - }); - - it("strength test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 100, "9"); - expect(filter.strength).toBe(9); - }); - - it("strength test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 2); - // @ts-ignore - filter.strength = "9"; - expect(filter.strength).toBe(9); - }); - - it("strength test valid case3", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, -1); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 2); - filter.strength = -10; - expect(filter.strength).toBe(0); - }); - - it("strength test valid case5", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 1000); - expect(filter.strength).toBe(255); - }); - - it("strength test valid case6", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 2); - filter.strength = 1000; - expect(filter.strength).toBe(255); - }); - - it("strength test valid case7", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, "test"); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case8", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3); - // @ts-ignore - filter.strength = "abc"; - expect(filter.strength).toBe(0); - }); - - // inner - it("inner test success case1", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, true); - expect(filter.inner).toBe(true); - }); - - it("inner test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, true); - filter.inner = false; - expect(filter.inner).toBe(false); - }); - - it("inner test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, 1); - expect(filter.inner).toBe(true); - }); - - it("inner test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false); - // @ts-ignore - filter.inner = 1; - expect(filter.inner).toBe(true); - }); - - it("inner test valid case3", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, 0); - expect(filter.inner).toBe(false); - }); - - it("inner test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, true); - // @ts-ignore - filter.inner = 0; - expect(filter.inner).toBe(false); - }); - - it("inner test valid case5", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, "test"); - expect(filter.inner).toBe(true); - }); - - it("inner test valid case6", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, true); - // @ts-ignore - filter.inner = ""; - expect(filter.inner).toBe(false); - }); - - // knockout - it("knockout test success case1", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, true); - expect(filter.knockout).toBe(true); - }); - - it("knockout test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, true); - filter.knockout = false; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, 1); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false); - // @ts-ignore - filter.knockout = 1; - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case3", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, 0); - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, true); - // @ts-ignore - filter.knockout = 0; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case5", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, "test"); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case6", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, true); - // @ts-ignore - filter.knockout = ""; - expect(filter.knockout).toBe(false); - }); - - // hideObject - it("hideObject test success case1", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, true); - expect(filter.hideObject).toBe(true); - }); - - it("hideObject test success case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, true); - filter.hideObject = false; - expect(filter.hideObject).toBe(false); - }); - - it("hideObject test valid case1", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, 1); - expect(filter.hideObject).toBe(true); - }); - - it("hideObject test valid case2", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, false); - // @ts-ignore - filter.hideObject = 1; - expect(filter.hideObject).toBe(true); - }); - - it("hideObject test valid case3", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, 0); - expect(filter.hideObject).toBe(false); - }); - - it("hideObject test valid case4", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, true); - // @ts-ignore - filter.hideObject = 0; - expect(filter.hideObject).toBe(false); - }); - - it("hideObject test valid case5", () => - { - // @ts-ignore - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, "test"); - expect(filter.hideObject).toBe(true); - }); - - it("hideObject test valid case6", () => - { - let filter = new DropShadowFilter(10.5, 90, 0xffffff, 1, 10, 10, 3, 5, false, false, true); - // @ts-ignore - filter.hideObject = ""; - expect(filter.hideObject).toBe(false); - }); -}); - -describe("DropShadowFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new DropShadowFilter( - 1, 90, 0xff0000, 0.3, - 10, 20, 2, 3, - true, true, true - ); - let clone = filter.clone(); - - // clone check - expect(clone.distance).toBe(1); - expect(clone.angle).toBe(90); - expect(clone.color).toBe(0xff0000); - expect(clone.alpha).toBe(0.3); - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(20); - expect(clone.strength).toBe(2); - expect(clone.quality).toBe(3); - expect(clone.inner).toBe(true); - expect(clone.knockout).toBe(true); - expect(clone.hideObject).toBe(true); - - // edit param - clone.distance = 3; - clone.angle = 20; - clone.color = 0xffffff; - clone.alpha = 1; - clone.blurX = 40; - clone.blurY = 30; - clone.strength = 4; - clone.quality = 2; - clone.inner = false; - clone.knockout = false; - clone.hideObject = false; - - // origin - expect(filter.distance).toBe(1); - expect(filter.angle).toBe(90); - expect(filter.color).toBe(0xff0000); - expect(filter.alpha).toBe(0.3); - expect(filter.blurX).toBe(10); - expect(filter.blurY).toBe(20); - expect(filter.strength).toBe(2); - expect(filter.quality).toBe(3); - expect(filter.inner).toBe(true); - expect(filter.knockout).toBe(true); - expect(filter.hideObject).toBe(true); - - // clone - expect(clone.distance).toBe(3); - expect(clone.angle).toBe(20); - expect(clone.color).toBe(0xffffff); - expect(clone.alpha).toBe(1); - expect(clone.blurX).toBe(40); - expect(clone.blurY).toBe(30); - expect(clone.strength).toBe(4); - expect(clone.quality).toBe(2); - expect(clone.inner).toBe(false); - expect(clone.knockout).toBe(false); - expect(clone.hideObject).toBe(false); - - }); - -}); - -describe("DropShadowFilter.js hideObject test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.hideObject).toBe(false); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - dsf.hideObject = true; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = ""; - expect(dsf.hideObject).toBe(false); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = "abc"; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = 0; - expect(dsf.hideObject).toBe(false); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = 1; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = 500; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = 50000000000000000; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = -1; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = -500; - expect(dsf.hideObject).toBe(true); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.hideObject = -50000000000000000; - expect(dsf.hideObject).toBe(true); - }); -}); - -describe("DropShadowFilter.js inner test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.inner).toBe(false); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - dsf.inner = true; - expect(dsf.inner).toBe(true); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = ""; - expect(dsf.inner).toBe(false); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = "abc"; - expect(dsf.inner).toBe(true); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = 0; - expect(dsf.inner).toBe(false); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = 1; - expect(dsf.inner).toBe(true); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = 500; - expect(dsf.inner).toBe(true); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = 50000000000000000; - expect(dsf.inner).toBe(true); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = -1; - expect(dsf.inner).toBe(true); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = -500; - expect(dsf.inner).toBe(true); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.inner = -50000000000000000; - expect(dsf.inner).toBe(true); - }); -}); - -describe("DropShadowFilter.js knockout test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.knockout).toBe(false); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - dsf.knockout = true; - expect(dsf.knockout).toBe(true); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.knockout = ""; - expect(dsf.knockout).toBe(false); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.knockout = "abc"; - expect(dsf.knockout).toBe(true); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.knockout = 0; - expect(dsf.knockout).toBe(false); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.knockout = 1; - expect(dsf.knockout).toBe(true); - }); -}); - -describe("DropShadowFilter.js alpha test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.alpha).toBe(1); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.alpha = true; - expect(dsf.alpha).toBe(1); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.alpha = ""; - expect(dsf.alpha).toBe(0); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.alpha = "abc"; - expect(dsf.alpha).toBe(0); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = 0; - expect(dsf.alpha).toBe(0); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = 1; - expect(dsf.alpha).toBe(1); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = 500; - expect(dsf.alpha).toBe(1); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = 50000000000000000; - expect(dsf.alpha).toBe(1); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = -1; - expect(dsf.alpha).toBe(0); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = -500; - expect(dsf.alpha).toBe(0); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - dsf.alpha = -50000000000000000; - expect(dsf.alpha).toBe(0); - }); - -}); - -describe("DropShadowFilter.js angle test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.angle).toBe(45); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.angle = true; - expect(dsf.angle).toBe(1); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.angle = ""; - expect(dsf.angle).toBe(0); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.angle = "abc"; - expect(dsf.angle).toBe(45); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = 0; - expect(dsf.angle).toBe(0); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = 1; - expect(dsf.angle).toBe(1); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = 500; - expect(dsf.angle).toBe(140); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = 50000000000000000; - expect(dsf.angle).toBe(320); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = -1; - expect(dsf.angle).toBe(-1); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = -500; - expect(dsf.angle).toBe(-140); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - dsf.angle = -50000000000000000; - expect(dsf.angle).toBe(-320); - }); - -}); - -describe("DropShadowFilter.js color test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.color).toBe(0); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.color = true; - expect(dsf.color).toBe(1); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.color = ""; - expect(dsf.color).toBe(0); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - dsf.color = 0; - expect(dsf.color).toBe(0); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - dsf.color = 1; - expect(dsf.color).toBe(1); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - dsf.color = 500; - expect(dsf.color).toBe(500); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - dsf.color = 50000000000000000; - expect(dsf.color).toBe(0xffffff); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - dsf.color = -1; - expect(dsf.color).toBe(0); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - dsf.color = -500; - expect(dsf.color).toBe(0); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - dsf.color = -50000000000000000; - expect(dsf.color).toBe(0); - }); - -}); - -describe("DropShadowFilter.js distance test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.distance).toBe(4); - }); - - it("default test case4", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.distance = true; - expect(dsf.distance).toBe(1); - }); - - it("default test case5", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.distance = ""; - expect(dsf.distance).toBe(0); - }); - - it("default test case6", () => - { - let dsf = new DropShadowFilter(); - // @ts-ignore - dsf.distance = "abc"; - expect(dsf.distance).toBe(4); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = 0; - expect(dsf.distance).toBe(0); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = 1; - expect(dsf.distance).toBe(1); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = 500; - expect(dsf.distance).toBe(255); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = 50000000000000000; - expect(dsf.distance).toBe(255); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = -1; - expect(dsf.distance).toBe(-1); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = -500; - expect(dsf.distance).toBe(-255); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - dsf.distance = -50000000000000000; - expect(dsf.distance).toBe(-255); - }); -}); - -describe("DropShadowFilter.js strength test", () => -{ - - it("default test case1", () => - { - let dsf = new DropShadowFilter(); - expect(dsf.strength).toBe(1); - }); - - it("default test case7", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = 0; - expect(dsf.strength).toBe(0); - }); - - it("default test case8", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = 1; - expect(dsf.strength).toBe(1); - }); - - it("default test case9", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = 500; - expect(dsf.strength).toBe(255); - }); - - it("default test case10", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = 50000000000000000; - expect(dsf.strength).toBe(255); - }); - - it("default test case11", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = -1; - expect(dsf.strength).toBe(0); - }); - - it("default test case12", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = -500; - expect(dsf.strength).toBe(0); - }); - - it("default test case13", () => - { - let dsf = new DropShadowFilter(); - dsf.strength = -50000000000000000; - expect(dsf.strength).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/GlowFilterTest.ts b/__tests__/next2d/filters/GlowFilterTest.ts deleted file mode 100644 index 09b651f4..00000000 --- a/__tests__/next2d/filters/GlowFilterTest.ts +++ /dev/null @@ -1,630 +0,0 @@ -import { GlowFilter } from "../../../packages/filters/src/GlowFilter"; - -describe("GlowFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new GlowFilter(); - expect(object.namespace).toBe("next2d.filters.GlowFilter"); - }); - - it("namespace test static", () => - { - expect(GlowFilter.namespace).toBe("next2d.filters.GlowFilter"); - }); - -}); - -describe("GlowFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new GlowFilter(); - expect(filter.toString()).toBe("[object GlowFilter]"); - }); - -}); - -describe("GlowFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(GlowFilter.toString()).toBe("[class GlowFilter]"); - }); - -}); - -describe("GlowFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new GlowFilter(); - expect(filter.color).toBe(0); - expect(filter.alpha).toBe(1); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.strength).toBe(1); - expect(filter.quality).toBe(1); - expect(filter.inner).toBe(false); - expect(filter.knockout).toBe(false); - }); - - // color - it("color test success case1", () => - { - let filter = new GlowFilter(0x000000); - expect(filter.color).toBe(0x000000); - }); - - it("color test success case2", () => - { - let filter = new GlowFilter(0xff0000); - filter.color = 0x00ff00; - expect(filter.color).toBe(0x00ff00); - }); - - it("color test valid case3", () => - { - let filter = new GlowFilter(-10); - expect(filter.color).toBe(0); - }); - - it("color test valid case4", () => - { - let filter = new GlowFilter(16777220); - expect(filter.color).toBe(0xffffff); - }); - - it("color test valid case5", () => - { - let filter = new GlowFilter(0xffffff); - - filter.color = -10; - expect(filter.color).toBe(0); - - filter.color = 16777220; - expect(filter.color).toBe(0xffffff); - }); - - // alpha - it("alpha test success case1", () => - { - let filter = new GlowFilter(0xff0000, 0); - expect(filter.alpha).toBe(0); - }); - - it("alpha test success case2", () => - { - let filter = new GlowFilter(0xff0000, 0.5); - filter.alpha = 0.75; - expect(filter.alpha).toBe(0.75); - }); - - it("alpha test valid case1", () => - { - // @ts-ignore - let filter = new GlowFilter(0xff0000, "0.25"); - expect(filter.alpha).toBe(0.25); - }); - - it("alpha test valid case2", () => - { - let filter = new GlowFilter(0xff0000, -10); - expect(filter.alpha).toBe(0); - }); - - it("alpha test valid case3", () => - { - let filter = new GlowFilter(0xff0000, 10); - expect(filter.alpha).toBe(1); - }); - - it("alpha test valid case4", () => - { - let filter = new GlowFilter(0xffffff, 1); - - filter.alpha = -10; - expect(filter.alpha).toBe(0); - - filter.alpha = 16777220; - expect(filter.alpha).toBe(1); - }); - - // strength - it("strength test success case1", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 100, 0); - expect(filter.strength).toBe(0); - }); - - it("strength test success case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 100, 10); - filter.strength = 2; - expect(filter.strength).toBe(2); - }); - - it("strength test valid case1", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 100, "9"); - expect(filter.strength).toBe(9); - }); - - it("strength test valid case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 2); - // @ts-ignore - filter.strength = "9"; - expect(filter.strength).toBe(9); - }); - - it("strength test valid case3", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, -1); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case4", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 2); - filter.strength = -10; - expect(filter.strength).toBe(0); - }); - - it("strength test valid case5", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 1000); - expect(filter.strength).toBe(255); - }); - - it("strength test valid case6", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 2); - filter.strength = 1000; - expect(filter.strength).toBe(255); - }); - - it("strength test valid case7", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 10, "test"); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case8", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3); - // @ts-ignore - filter.strength = "abc"; - expect(filter.strength).toBe(0); - }); - - // inner - it("inner test success case1", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, true); - expect(filter.inner).toBe(true); - }); - - it("inner test success case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, true); - filter.inner = false; - expect(filter.inner).toBe(false); - }); - - it("inner test valid case1", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, 1); - expect(filter.inner).toBe(true); - }); - - it("inner test valid case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false); - // @ts-ignore - filter.inner = 1; - expect(filter.inner).toBe(true); - }); - - it("inner test valid case3", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, 0); - expect(filter.inner).toBe(false); - }); - - it("inner test valid case4", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, true); - // @ts-ignore - filter.inner = 0; - expect(filter.inner).toBe(false); - }); - - // knockout - it("knockout test success case1", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, true); - expect(filter.knockout).toBe(true); - }); - - it("knockout test success case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, true); - filter.knockout = false; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case1", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, 1); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case2", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, false); - // @ts-ignore - filter.knockout = 1; - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case3", () => - { - // @ts-ignore - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, 0); - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case4", () => - { - let filter = new GlowFilter(0xffffff, 1, 10, 10, 3, 5, false, true); - // @ts-ignore - filter.knockout = 0; - expect(filter.knockout).toBe(false); - }); - -}); - -describe("GlowFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new GlowFilter( - 0x000000, 0.2, 10, 20, 6, - 2, true, true - ); - let clone = filter.clone(); - - // clone check - expect(clone.color).toBe(0x000000); - expect(clone.alpha).toBe(0.2); - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(20); - expect(clone.strength).toBe(6); - expect(clone.quality).toBe(2); - expect(clone.inner).toBe(true); - expect(clone.knockout).toBe(true); - - // edit param - clone.color = 0xffffff; - clone.alpha = 1; - clone.blurX = 9; - clone.blurY = 19; - clone.strength = 4; - clone.quality = 3; - clone.inner = false; - clone.knockout = false; - - // origin - expect(filter.color).toBe(0x000000); - expect(filter.alpha).toBe(0.2); - expect(filter.blurX).toBe(10); - expect(filter.blurY).toBe(20); - expect(filter.strength).toBe(6); - expect(filter.quality).toBe(2); - expect(filter.inner).toBe(true); - expect(filter.knockout).toBe(true); - - // clone - expect(clone.color).toBe(0xffffff); - expect(clone.alpha).toBe(1); - expect(clone.blurX).toBe(9); - expect(clone.blurY).toBe(19); - expect(clone.strength).toBe(4); - expect(clone.quality).toBe(3); - expect(clone.inner).toBe(false); - expect(clone.knockout).toBe(false); - - }); - -}); - -describe("GlowFilter.js alpha test", () => -{ - - it("default test case1", () => - { - let gf = new GlowFilter(); - expect(gf.alpha).toBe(1); - }); - - it("default test case4", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.alpha = true; - expect(gf.alpha).toBe(1); - }); - - it("default test case5", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.alpha = ""; - expect(gf.alpha).toBe(0); - }); - - it("default test case7", () => - { - let gf = new GlowFilter(); - gf.alpha = 0; - expect(gf.alpha).toBe(0); - }); - - it("default test case8", () => - { - let gf = new GlowFilter(); - gf.alpha = 1; - expect(gf.alpha).toBe(1); - }); - - it("default test case9", () => - { - let gf = new GlowFilter(); - gf.alpha = 500; - expect(gf.alpha).toBe(1); - }); - - it("default test case10", () => - { - let gf = new GlowFilter(); - gf.alpha = 50000000000000000; - expect(gf.alpha).toBe(1); - }); - - it("default test case11", () => - { - let gf = new GlowFilter(); - gf.alpha = -1; - expect(gf.alpha).toBe(0); - }); - - it("default test case12", () => - { - let gf = new GlowFilter(); - gf.alpha = -500; - expect(gf.alpha).toBe(0); - }); - - it("default test case13", () => - { - let gf = new GlowFilter(); - gf.alpha = -50000000000000000; - expect(gf.alpha).toBe(0); - }); - -}); - -describe("GlowFilter.js color test", () => -{ - - it("default test case1", () => - { - let gf = new GlowFilter(); - expect(gf.color).toBe(0); - }); - - it("default test case7", () => - { - let gf = new GlowFilter(); - gf.color = 0; - expect(gf.color).toBe(0); - }); - - it("default test case8", () => - { - let gf = new GlowFilter(); - gf.color = 1; - expect(gf.color).toBe(1); - }); - - it("default test case9", () => - { - let gf = new GlowFilter(); - gf.color = 500; - expect(gf.color).toBe(500); - }); - - it("default test case10", () => - { - let gf = new GlowFilter(); - gf.color = 50000000000000000; - expect(gf.color).toBe(0xffffff); - }); - - it("default test case11", () => - { - let gf = new GlowFilter(); - gf.color = -1; - expect(gf.color).toBe(0); - }); - - it("default test case12", () => - { - let gf = new GlowFilter(); - gf.color = -500; - expect(gf.color).toBe(0); - }); - - it("default test case13", () => - { - let gf = new GlowFilter(); - gf.color = -50000000000000000; - expect(gf.color).toBe(0); - }); - -}); - -describe("GlowFilter.js inner test", () => -{ - - it("default test case1", () => - { - let gf = new GlowFilter(); - expect(gf.inner).toBe(false); - }); - - it("default test case4", () => - { - let gf = new GlowFilter(); - gf.inner = true; - expect(gf.inner).toBe(true); - }); - - it("default test case5", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.inner = ""; - expect(gf.inner).toBe(false); - }); - - it("default test case7", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.inner = 0; - expect(gf.inner).toBe(false); - }); - - it("default test case8", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.inner = 1; - expect(gf.inner).toBe(true); - }); - -}); - -describe("GlowFilter.js knockout test", () => -{ - - it("default test case1", () => - { - let gf = new GlowFilter(); - expect(gf.knockout).toBe(false); - }); - - it("default test case4", () => - { - let gf = new GlowFilter(); - gf.knockout = true; - expect(gf.knockout).toBe(true); - }); - - it("default test case5", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.knockout = ""; - expect(gf.knockout).toBe(false); - }); - - it("default test case7", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.knockout = 0; - expect(gf.knockout).toBe(false); - }); - - it("default test case8", () => - { - let gf = new GlowFilter(); - // @ts-ignore - gf.knockout = 1; - expect(gf.knockout).toBe(true); - }); - -}); - -describe("GlowFilter.js strength test", () => -{ - - it("default test case1", () => - { - let gf = new GlowFilter(); - expect(gf.strength).toBe(1); - }); - - it("default test case7", () => - { - let gf = new GlowFilter(); - gf.strength = 0; - expect(gf.strength).toBe(0); - }); - - it("default test case8", () => - { - let gf = new GlowFilter(); - gf.strength = 1; - expect(gf.strength).toBe(1); - }); - - it("default test case9", () => - { - let gf = new GlowFilter(); - gf.strength = 500; - expect(gf.strength).toBe(255); - }); - - it("default test case10", () => - { - let gf = new GlowFilter(); - gf.strength = 50000000000000000; - expect(gf.strength).toBe(255); - }); - - it("default test case11", () => - { - let gf = new GlowFilter(); - gf.strength = -1; - expect(gf.strength).toBe(0); - }); - - it("default test case12", () => - { - let gf = new GlowFilter(); - gf.strength = -500; - expect(gf.strength).toBe(0); - }); - - it("default test case13", () => - { - let gf = new GlowFilter(); - gf.strength = -50000000000000000; - expect(gf.strength).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/GradientBevelFilterTest.ts b/__tests__/next2d/filters/GradientBevelFilterTest.ts deleted file mode 100644 index ab1685cf..00000000 --- a/__tests__/next2d/filters/GradientBevelFilterTest.ts +++ /dev/null @@ -1,599 +0,0 @@ -import { GradientBevelFilter } from "../../../packages/filters/src/GradientBevelFilter"; - -describe("GradientBevelFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new GradientBevelFilter(); - expect(object.namespace).toBe("next2d.filters.GradientBevelFilter"); - }); - - it("namespace test static", () => - { - expect(GradientBevelFilter.namespace).toBe("next2d.filters.GradientBevelFilter"); - }); - -}); - -describe("GradientBevelFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new GradientBevelFilter(); - expect(filter.toString()).toBe("[object GradientBevelFilter]"); - }); - -}); - -describe("GradientBevelFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(GradientBevelFilter.toString()).toBe("[class GradientBevelFilter]"); - }); - -}); - -describe("GradientBevelFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new GradientBevelFilter(); - expect(filter.distance).toBe(4); - expect(filter.angle).toBe(45); - expect(filter.colors).toBe(null); - expect(filter.alphas).toBe(null); - expect(filter.ratios).toBe(null); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.strength).toBe(1); - expect(filter.quality).toBe(1); - expect(filter.type).toBe("inner"); - expect(filter.knockout).toBe(false); - }); - - // distance - it("distance test success case1", () => - { - let filter = new GradientBevelFilter(0); - expect(filter.distance).toBe(0); - }); - - it("distance test success case2", () => - { - let filter = new GradientBevelFilter(10.5); - filter.distance = -12.6; - expect(filter.distance).toBe(-12.6); - }); - - it("distance test valid case1", () => - { - // @ts-ignore - let filter = new GradientBevelFilter("23"); - expect(filter.distance).toBe(23); - }); - - it("distance test valid case2", () => - { - let filter = new GradientBevelFilter(1000); - expect(filter.distance).toBe(255); - }); - - it("distance test valid case3", () => - { - let filter = new GradientBevelFilter(-1000); - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case4", () => - { - let filter = new GradientBevelFilter(10); - // @ts-ignore - filter.distance = "-56"; - expect(filter.distance).toBe(-56); - }); - - it("distance test valid case5", () => - { - let filter = new GradientBevelFilter(10); - filter.distance = 400; - expect(filter.distance).toBe(255); - }); - - it("distance test valid case6", () => - { - let filter = new GradientBevelFilter(10); - filter.distance = -400; - expect(filter.distance).toBe(-255); - }); - - // angle - it("angle test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0); - expect(filter.angle).toBe(0); - }); - - it("angle test success case2", () => - { - let filter = new GradientBevelFilter(10.5, 90); - filter.angle = 180; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case1", () => - { - // @ts-ignore - let filter = new GradientBevelFilter(10.5, "20"); - expect(filter.angle).toBe(20); - }); - - it("angle test valid case2", () => - { - let filter = new GradientBevelFilter(10.5, 20); - // @ts-ignore - filter.angle = "180"; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case3", () => - { - let filter = new GradientBevelFilter(10.5, 20); - filter.angle = 4500; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case4", () => - { - let filter = new GradientBevelFilter(10.5, 20); - filter.angle = -4500; - expect(filter.angle).toBe(-180); - }); - - // colors - it("colors test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255]); - if (!filter.colors) { - throw new Error("colors"); - } - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0xff00ff); - }); - - it("colors test valid case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [16777216, -1], [1, 1], [0, 255]); - if (!filter.colors) { - throw new Error("colors"); - } - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0x000000); - }); - - // alphas - it("alphas test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 0.5], [0, 255]); - if (!filter.alphas) { - throw new Error("alphas"); - } - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0.5); - }); - - it("alphas test valid case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [2, -1], [0, 255]); - if (!filter.alphas) { - throw new Error("ratios"); - } - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0); - }); - - // ratios - it("ratios test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255]); - if (!filter.ratios) { - throw new Error("ratios"); - } - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - }); - - it("ratios test valid case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [-1, 256]); - if (!filter.ratios) { - throw new Error("ratios"); - } - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - }); - - // strength - it("strength test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 0); - expect(filter.strength).toBe(0); - }); - - it("strength test success case2", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 10); - filter.strength = 2; - expect(filter.strength).toBe(2); - }); - - it("strength test valid case1", () => - { - // @ts-ignore - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, "9"); - expect(filter.strength).toBe(9); - }); - - it("strength test valid case2", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - // @ts-ignore - filter.strength = "9"; - expect(filter.strength).toBe(9); - }); - - it("strength test valid case3", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, -1); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case4", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - filter.strength = -10; - expect(filter.strength).toBe(0); - }); - - it("strength test valid case5", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 1000); - expect(filter.strength).toBe(255); - }); - - it("strength test valid case6", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - filter.strength = 1000; - expect(filter.strength).toBe(255); - }); - - // type - it("type test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "inner"); - expect(filter.type).toBe("inner"); - }); - - it("type test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "outer"); - expect(filter.type).toBe("outer"); - }); - - it("type test success case3", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full"); - expect(filter.type).toBe("full"); - }); - - it("type test success case4", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "inner"); - filter.type = "outer"; - expect(filter.type).toBe("outer"); - }); - - it("type test valid case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full"); - expect(filter.type).toBe("full"); - }); - - it("type test valid case2", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full"); - filter.type = "inner"; - expect(filter.type).toBe("inner"); - }); - - // knockout - it("knockout test success case1", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - expect(filter.knockout).toBe(true); - }); - - it("knockout test success case2", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - filter.knockout = false; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case1", () => - { - // @ts-ignore - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", 1); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case2", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", false); - // @ts-ignore - filter.knockout = 1; - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case3", () => - { - // @ts-ignore - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", 0); - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case4", () => - { - let filter = new GradientBevelFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - // @ts-ignore - filter.knockout = 0; - expect(filter.knockout).toBe(false); - }); - -}); - -describe("GradientBevelFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new GradientBevelFilter( - 10, 90, [0xffffff, 0xff00ff], [1, 0.2], [0, 255], - 10, 20, 3, 2, "full", true - ); - let clone = filter.clone(); - - // clone check - expect(clone.distance).toBe(10); - expect(clone.angle).toBe(90); - if (clone.colors) { - expect(clone.colors[0]).toBe(0xffffff); - expect(clone.colors[1]).toBe(0xff00ff); - } - if (clone.alphas) { - expect(clone.alphas[0]).toBe(1); - expect(clone.alphas[1]).toBe(0.2); - } - if (clone.ratios) { - expect(clone.ratios[0]).toBe(0); - expect(clone.ratios[1]).toBe(255); - } - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(20); - expect(clone.strength).toBe(3); - expect(clone.quality).toBe(2); - expect(clone.type).toBe("full"); - expect(clone.knockout).toBe(true); - - // edit param - clone.distance = 20; - clone.angle = 30; - clone.colors = [0, 100]; - clone.alphas = [0.3, 0.6]; - clone.ratios = [100, 180]; - clone.blurX = 5; - clone.blurY = 6; - clone.strength = 2; - clone.quality = 1; - clone.type = "inner"; - clone.knockout = false; - - // origin - expect(filter.distance).toBe(10); - expect(filter.angle).toBe(90); - if (filter.colors) { - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0xff00ff); - } - if (filter.alphas) { - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0.2); - } - if (filter.ratios) { - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - } - expect(filter.blurX).toBe(10); - expect(filter.blurY).toBe(20); - expect(filter.strength).toBe(3); - expect(filter.quality).toBe(2); - expect(filter.type).toBe("full"); - expect(filter.knockout).toBe(true); - - // clone check - expect(clone.distance).toBe(20); - expect(clone.angle).toBe(30); - expect(clone.colors[0]).toBe(0); - expect(clone.colors[1]).toBe(100); - expect(clone.alphas[0]).toBe(0.3); - expect(clone.alphas[1]).toBe(0.6); - expect(clone.ratios[0]).toBe(100); - expect(clone.ratios[1]).toBe(180); - expect(clone.blurX).toBe(5); - expect(clone.blurY).toBe(6); - expect(clone.strength).toBe(2); - expect(clone.quality).toBe(1); - expect(clone.type).toBe("inner"); - expect(clone.knockout).toBe(false); - - }); - -}); - -describe("GradientBevelFilter.js knockout test", () => -{ - - it("default test case1", () => - { - let gbf = new GradientBevelFilter(); - expect(gbf.knockout).toBe(false); - }); - - it("default test case4", () => - { - let gbf = new GradientBevelFilter(); - gbf.knockout = true; - expect(gbf.knockout).toBe(true); - }); - - it("default test case7", () => - { - let gbf = new GradientBevelFilter(); - // @ts-ignore - gbf.knockout = 0; - expect(gbf.knockout).toBe(false); - }); - - it("default test case8", () => - { - let gbf = new GradientBevelFilter(); - // @ts-ignore - gbf.knockout = 1; - expect(gbf.knockout).toBe(true); - }); -}); - -describe("GradientBevelFilter.js angle test", () => -{ - - it("default test case1", () => - { - let gbf = new GradientBevelFilter(); - expect(gbf.angle).toBe(45); - }); - - it("default test case7", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = 0; - expect(gbf.angle).toBe(0); - }); - - it("default test case8", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = 1; - expect(gbf.angle).toBe(1); - }); - - it("default test case9", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = 500; - expect(gbf.angle).toBe(140); - }); - - it("default test case10", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = 50000000000000000; - expect(gbf.angle).toBe(320); - }); - - it("default test case11", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = -1; - expect(gbf.angle).toBe(-1); - }); - - it("default test case12", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = -500; - expect(gbf.angle).toBe(-140); - }); - - it("default test case13", () => - { - let gbf = new GradientBevelFilter(); - gbf.angle = -50000000000000000; - expect(gbf.angle).toBe(-320); - }); - -}); - -describe("GradientBevelFilter.js distance test", () => -{ - - it("default test case1", () => - { - let gbf = new GradientBevelFilter(); - expect(gbf.distance).toBe(4); - }); - - it("default test case7", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = 0; - expect(gbf.distance).toBe(0); - }); - - it("default test case8", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = 1; - expect(gbf.distance).toBe(1); - }); - - it("default test case9", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = 500; - expect(gbf.distance).toBe(255); - }); - - it("default test case10", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = 50000000000000000; - expect(gbf.distance).toBe(255); - }); - - it("default test case11", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = -1; - expect(gbf.distance).toBe(-1); - }); - - it("default test case12", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = -500; - expect(gbf.distance).toBe(-255); - }); - - it("default test case13", () => - { - let gbf = new GradientBevelFilter(); - gbf.distance = -50000000000000000; - expect(gbf.distance).toBe(-255); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/filters/GradientGlowFilterTest.ts b/__tests__/next2d/filters/GradientGlowFilterTest.ts deleted file mode 100644 index e860f005..00000000 --- a/__tests__/next2d/filters/GradientGlowFilterTest.ts +++ /dev/null @@ -1,662 +0,0 @@ -import { GradientGlowFilter } from "../../../packages/filters/src/GradientGlowFilter"; - -describe("GradientGlowFilter.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new GradientGlowFilter(); - expect(object.namespace).toBe("next2d.filters.GradientGlowFilter"); - }); - - it("namespace test static", () => - { - expect(GradientGlowFilter.namespace).toBe("next2d.filters.GradientGlowFilter"); - }); - -}); - -describe("GradientGlowFilter.js toString test", () => -{ - it("toString test success", () => - { - let filter = new GradientGlowFilter(); - expect(filter.toString()).toBe("[object GradientGlowFilter]"); - }); - -}); - -describe("GradientGlowFilter.js static toString test", () => -{ - - it("static toString test", () => - { - expect(GradientGlowFilter.toString()).toBe("[class GradientGlowFilter]"); - }); - -}); - -describe("GradientGlowFilter.js property test", () => -{ - - // default - it("default test success", () => - { - let filter = new GradientGlowFilter(); - expect(filter.distance).toBe(4); - expect(filter.angle).toBe(45); - expect(filter.colors).toBe(null); - expect(filter.alphas).toBe(null); - expect(filter.ratios).toBe(null); - expect(filter.blurX).toBe(4); - expect(filter.blurY).toBe(4); - expect(filter.strength).toBe(1); - expect(filter.quality).toBe(1); - expect(filter.type).toBe("inner"); - expect(filter.knockout).toBe(false); - }); - - // distance - it("distance test success case1", () => - { - let filter = new GradientGlowFilter(0); - expect(filter.distance).toBe(0); - }); - - it("distance test success case2", () => - { - let filter = new GradientGlowFilter(10.5); - filter.distance = -12.6; - expect(filter.distance).toBe(-12.6); - }); - - it("distance test valid case1", () => - { - // @ts-ignore - let filter = new GradientGlowFilter("23"); - expect(filter.distance).toBe(23); - }); - - it("distance test valid case2", () => - { - let filter = new GradientGlowFilter(1000); - expect(filter.distance).toBe(255); - }); - - it("distance test valid case3", () => - { - let filter = new GradientGlowFilter(-1000); - expect(filter.distance).toBe(-255); - }); - - it("distance test valid case4", () => - { - let filter = new GradientGlowFilter(10); - // @ts-ignore - filter.distance = "-56"; - expect(filter.distance).toBe(-56); - }); - - it("distance test valid case5", () => - { - let filter = new GradientGlowFilter(10); - filter.distance = 400; - expect(filter.distance).toBe(255); - }); - - it("distance test valid case6", () => - { - let filter = new GradientGlowFilter(10); - filter.distance = -400; - expect(filter.distance).toBe(-255); - }); - - // angle - it("angle test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0); - expect(filter.angle).toBe(0); - }); - - it("angle test success case2", () => - { - let filter = new GradientGlowFilter(10.5, 90); - filter.angle = 180; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case1", () => - { - // @ts-ignore - let filter = new GradientGlowFilter(10.5, "20"); - expect(filter.angle).toBe(20); - }); - - it("angle test valid case2", () => - { - let filter = new GradientGlowFilter(10.5, 20); - // @ts-ignore - filter.angle = "180"; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case3", () => - { - let filter = new GradientGlowFilter(10.5, 20); - filter.angle = 4500; - expect(filter.angle).toBe(180); - }); - - it("angle test valid case4", () => - { - let filter = new GradientGlowFilter(10.5, 20); - filter.angle = -4500; - expect(filter.angle).toBe(-180); - }); - - // colors - it("colors test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255]); - if (!filter.colors) { - throw new Error("colors"); - } - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0xff00ff); - }); - - it("colors test valid case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [16777216, -1], [1, 1], [0, 255]); - if (!filter.colors) { - throw new Error("colors"); - } - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0); - }); - - // alphas - it("alphas test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 0.5], [0, 255]); - if (!filter.alphas) { - throw new Error("alphas"); - } - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0.5); - }); - - it("alphas test valid case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [2, -1], [0, 255]); - if (!filter.alphas) { - throw new Error("alphas"); - } - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0); - }); - - // ratios - it("ratios test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255]); - if (!filter.ratios) { - throw new Error("ratios"); - } - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - }); - - it("ratios test valid case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [-1, 256]); - if (!filter.ratios) { - throw new Error("ratios"); - } - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - }); - - // strength - it("strength test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 0); - expect(filter.strength).toBe(0); - }); - - it("strength test success case2", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 10); - filter.strength = 2; - expect(filter.strength).toBe(2); - }); - - it("strength test valid case1", () => - { - // @ts-ignore - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, "9"); - expect(filter.strength).toBe(9); - }); - - it("strength test valid case2", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - // @ts-ignore - filter.strength = "9"; - expect(filter.strength).toBe(9); - }); - - it("strength test valid case3", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, -1); - expect(filter.strength).toBe(0); - }); - - it("strength test valid case4", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - filter.strength = -10; - expect(filter.strength).toBe(0); - }); - - it("strength test valid case5", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 1000); - expect(filter.strength).toBe(255); - }); - - it("strength test valid case6", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 2); - filter.strength = 1000; - expect(filter.strength).toBe(255); - }); - - // type - it("type test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "inner"); - expect(filter.type).toBe("inner"); - }); - - it("type test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "outer"); - expect(filter.type).toBe("outer"); - }); - - it("type test success case3", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full"); - expect(filter.type).toBe("full"); - }); - - it("type test success case4", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "inner"); - filter.type = "outer"; - expect(filter.type).toBe("outer"); - }); - - // knockout - it("knockout test success case1", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - expect(filter.knockout).toBe(true); - }); - - it("knockout test success case2", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - filter.knockout = false; - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case1", () => - { - // @ts-ignore - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", 1); - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case2", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", false); - // @ts-ignore - filter.knockout = 1; - expect(filter.knockout).toBe(true); - }); - - it("knockout test valid case3", () => - { - // @ts-ignore - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", 0); - expect(filter.knockout).toBe(false); - }); - - it("knockout test valid case4", () => - { - let filter = new GradientGlowFilter(10.5, 0, [0xffffff, 0xff00ff], [1, 1], [0, 255], 10, 10, 3, 10, "full", true); - // @ts-ignore - filter.knockout = 0; - expect(filter.knockout).toBe(false); - }); - -}); - -describe("GradientGlowFilter.js clone test", () => -{ - - it("clone test", () => - { - let filter = new GradientGlowFilter( - 10, 90, [0xffffff, 0xff00ff], [1, 0.2], [0, 255], - 10, 20, 3, 2, "full", true - ); - let clone = filter.clone(); - - // clone check - expect(clone.distance).toBe(10); - expect(clone.angle).toBe(90); - if (!clone.colors) { - throw new Error("colors"); - } - expect(clone.colors[0]).toBe(0xffffff); - expect(clone.colors[1]).toBe(0xff00ff); - if (!clone.alphas) { - throw new Error("alphas"); - } - expect(clone.alphas[0]).toBe(1); - expect(clone.alphas[1]).toBe(0.2); - if (!clone.ratios) { - throw new Error("ratios"); - } - expect(clone.ratios[0]).toBe(0); - expect(clone.ratios[1]).toBe(255); - expect(clone.blurX).toBe(10); - expect(clone.blurY).toBe(20); - expect(clone.strength).toBe(3); - expect(clone.quality).toBe(2); - expect(clone.type).toBe("full"); - expect(clone.knockout).toBe(true); - - // edit param - clone.distance = 20; - clone.angle = 30; - clone.colors = [0, 100]; - clone.alphas = [0.3, 0.6]; - clone.ratios = [100, 180]; - clone.blurX = 5; - clone.blurY = 6; - clone.strength = 2; - clone.quality = 1; - clone.type = "inner"; - clone.knockout = false; - - // origin - expect(filter.distance).toBe(10); - expect(filter.angle).toBe(90); - if (!filter.colors) { - throw new Error("ratios"); - } - expect(filter.colors[0]).toBe(0xffffff); - expect(filter.colors[1]).toBe(0xff00ff); - if (!filter.alphas) { - throw new Error("ratios"); - } - expect(filter.alphas[0]).toBe(1); - expect(filter.alphas[1]).toBe(0.2); - if (!filter.ratios) { - throw new Error("ratios"); - } - expect(filter.ratios[0]).toBe(0); - expect(filter.ratios[1]).toBe(255); - expect(filter.blurX).toBe(10); - expect(filter.blurY).toBe(20); - expect(filter.strength).toBe(3); - expect(filter.quality).toBe(2); - expect(filter.type).toBe("full"); - expect(filter.knockout).toBe(true); - - // clone check - expect(clone.distance).toBe(20); - expect(clone.angle).toBe(30); - if (!clone.colors) { - throw new Error("colors"); - } - expect(clone.colors[0]).toBe(0); - expect(clone.colors[1]).toBe(100); - if (!clone.alphas) { - throw new Error("alphas"); - } - expect(clone.alphas[0]).toBe(0.3); - expect(clone.alphas[1]).toBe(0.6); - if (!clone.ratios) { - throw new Error("ratios"); - } - expect(clone.ratios[0]).toBe(100); - expect(clone.ratios[1]).toBe(180); - expect(clone.blurX).toBe(5); - expect(clone.blurY).toBe(6); - expect(clone.strength).toBe(2); - expect(clone.quality).toBe(1); - expect(clone.type).toBe("inner"); - expect(clone.knockout).toBe(false); - - }); - -}); - -describe("GradientGlowFilter.js knockout test", () => -{ - - it("default test case1", () => - { - let gg = new GradientGlowFilter(); - expect(gg.knockout).toBe(false); - }); - - it("default test case4", () => - { - let gg = new GradientGlowFilter(); - gg.knockout = true; - expect(gg.knockout).toBe(true); - }); - - it("default test case7", () => - { - let gg = new GradientGlowFilter(); - // @ts-ignore - gg.knockout = 0; - expect(gg.knockout).toBe(false); - }); - - it("default test case8", () => - { - let gg = new GradientGlowFilter(); - // @ts-ignore - gg.knockout = 1; - expect(gg.knockout).toBe(true); - }); - -}); - -describe("GradientGlowFilter.js angle test", () => -{ - - it("default test case1", () => - { - let ggf = new GradientGlowFilter(); - expect(ggf.angle).toBe(45); - }); - - it("default test case7", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = 0; - expect(ggf.angle).toBe(0); - }); - - it("default test case8", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = 1; - expect(ggf.angle).toBe(1); - }); - - it("default test case9", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = 500; - expect(ggf.angle).toBe(140); - }); - - it("default test case10", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = 50000000000000000; - expect(ggf.angle).toBe(320); - }); - - it("default test case11", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = -1; - expect(ggf.angle).toBe(-1); - }); - - it("default test case12", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = -500; - expect(ggf.angle).toBe(-140); - }); - - it("default test case13", () => - { - let ggf = new GradientGlowFilter(); - ggf.angle = -50000000000000000; - expect(ggf.angle).toBe(-320); - }); - -}); - -describe("GradientGlowFilter.js distance test", () => -{ - - it("default test case1", () => - { - let ggf = new GradientGlowFilter(); - expect(ggf.distance).toBe(4); - }); - - it("default test case7", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = 0; - expect(ggf.distance).toBe(0); - }); - - it("default test case8", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = 1; - expect(ggf.distance).toBe(1); - }); - - it("default test case9", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = 500; - expect(ggf.distance).toBe(255); - }); - - it("default test case10", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = 50000000000000000; - expect(ggf.distance).toBe(255); - }); - - it("default test case11", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = -1; - expect(ggf.distance).toBe(-1); - }); - - it("default test case12", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = -500; - expect(ggf.distance).toBe(-255); - }); - - it("default test case13", () => - { - let ggf = new GradientGlowFilter(); - ggf.distance = -50000000000000000; - expect(ggf.distance).toBe(-255); - }); - -}); - -describe("GradientGlowFilter.js strength test", () => -{ - - it("default test case1", () => - { - let ggf = new GradientGlowFilter(); - expect(ggf.strength).toBe(1); - }); - - it("default test case7", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = 0; - expect(ggf.strength).toBe(0); - }); - - it("default test case8", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = 1; - expect(ggf.strength).toBe(1); - }); - - it("default test case9", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = 500; - expect(ggf.strength).toBe(255); - }); - - it("default test case10", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = 50000000000000000; - expect(ggf.strength).toBe(255); - }); - - it("default test case11", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = -1; - expect(ggf.strength).toBe(0); - }); - - it("default test case12", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = -500; - expect(ggf.strength).toBe(0); - }); - - it("default test case13", () => - { - let ggf = new GradientGlowFilter(); - ggf.strength = -50000000000000000; - expect(ggf.strength).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/geom/ColorTransformTest.ts b/__tests__/next2d/geom/ColorTransformTest.ts deleted file mode 100644 index a49107e6..00000000 --- a/__tests__/next2d/geom/ColorTransformTest.ts +++ /dev/null @@ -1,1848 +0,0 @@ -import { ColorTransform } from "../../../packages/geom/src/ColorTransform"; - -describe("ColorTransform.js toString test", () => -{ - it("toString test1 success", () => - { - const object = new ColorTransform(); - expect(object.toString()).toBe("(redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=0, greenOffset=0, blueOffset=0, alphaOffset=0)"); - }); - - it("toString test2 success", () => - { - const object = new ColorTransform(2, 3, 4, 5, 6, 7, 8, 9); - expect(object.toString()).toBe("(redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=6, greenOffset=7, blueOffset=8, alphaOffset=9)"); - }); - -}); - -describe("ColorTransform.js static toString test", () => -{ - - it("static toString test", () => - { - expect(ColorTransform.toString()).toBe("[class ColorTransform]"); - }); - -}); - -describe("ColorTransform.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new ColorTransform(); - expect(object.namespace).toBe("next2d.geom.ColorTransform"); - }); - - it("namespace test static", () => - { - expect(ColorTransform.namespace).toBe("next2d.geom.ColorTransform"); - }); - -}); - -describe("ColorTransform.js property test", () => -{ - - it("test success", () => { - - const ct = new ColorTransform(0.1, 0.2, 0.3, 0.4, 1, 2, 3, 4); - - expect(ct.redMultiplier).toBe(0.10000000149011612); - expect(ct.greenMultiplier).toBe(0.20000000298023224); - expect(ct.blueMultiplier).toBe(0.30000001192092896); - expect(ct.alphaMultiplier).toBe(0.4000000059604645); - expect(ct.redOffset).toBe(1); - expect(ct.greenOffset).toBe(2); - expect(ct.blueOffset).toBe(3); - expect(ct.alphaOffset).toBe(4); - - }); - - it("clone test success", () => { - - const ct = new ColorTransform(); - const clone = ct._$clone(); - - // ct - ct.redMultiplier = 0.1; - ct.greenMultiplier = 0.2; - ct.blueMultiplier = 0.3; - ct.alphaMultiplier = 0.4; - ct.redOffset = 1; - ct.greenOffset = 2; - ct.blueOffset = 3; - ct.alphaOffset = 4; - - expect(ct.redMultiplier).toBe(0.10000000149011612); - expect(ct.greenMultiplier).toBe(0.20000000298023224); - expect(ct.blueMultiplier).toBe(0.30000001192092896); - expect(ct.alphaMultiplier).toBe(0.4000000059604645); - expect(ct.redOffset).toBe(1); - expect(ct.greenOffset).toBe(2); - expect(ct.blueOffset).toBe(3); - expect(ct.alphaOffset).toBe(4); - - expect(clone.redMultiplier).toBe(1); - expect(clone.greenMultiplier).toBe(1); - expect(clone.blueMultiplier).toBe(1); - expect(clone.alphaMultiplier).toBe(1); - expect(clone.redOffset).toBe(0); - expect(clone.greenOffset).toBe(0); - expect(clone.blueOffset).toBe(0); - expect(clone.alphaOffset).toBe(0); - - }); - - it("valid case3", () => { - - const ct = new ColorTransform( - 1.1, 1.1, 1.1, 1.1, - 256, 256, 256, 256 - ); - - expect(ct.redMultiplier).toBe(1); - expect(ct.greenMultiplier).toBe(1); - expect(ct.blueMultiplier).toBe(1); - expect(ct.alphaMultiplier).toBe(1); - expect(ct.redOffset).toBe(255); - expect(ct.greenOffset).toBe(255); - expect(ct.blueOffset).toBe(255); - expect(ct.alphaOffset).toBe(255); - - }); - - it("valid case4", () => { - - const ct = new ColorTransform( - -1.1, -1.1, -1.1, -1.1, - -256, -256, -256, -256 - ); - - expect(ct.redMultiplier).toBe(0); - expect(ct.greenMultiplier).toBe(0); - expect(ct.blueMultiplier).toBe(0); - expect(ct.alphaMultiplier).toBe(0); - expect(ct.redOffset).toBe(-255); - expect(ct.greenOffset).toBe(-255); - expect(ct.blueOffset).toBe(-255); - expect(ct.alphaOffset).toBe(-255); - - }); - - it("valid case5", () => { - - const ct = new ColorTransform( - 10, 10, 10, 10, - 1.1, 1.1, 1.1, 1.1 - ); - - expect(ct.redMultiplier).toBe(1); - expect(ct.greenMultiplier).toBe(1); - expect(ct.blueMultiplier).toBe(1); - expect(ct.alphaMultiplier).toBe(1); - expect(ct.redOffset).toBe(1); - expect(ct.greenOffset).toBe(1); - expect(ct.blueOffset).toBe(1); - expect(ct.alphaOffset).toBe(1); - - }); - -}); - -describe("ColorTransform.js concat test", () => -{ - it("concat test1", () => - { - - const ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - const ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat test2", () => - { - - const ct1 = new ColorTransform(100, 0.2, 0.3, 0.5, 50, 100, 150, 200); - const ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.8999999761581421, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=-205, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat test3", () => - { - - const ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 5000, 100, 150, 200); - const ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=229, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat test4", () => - { - - const ct1 = new ColorTransform(0, -9, 0.3, 0.5, 50, 100, 150, 200); - const ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0, greenMultiplier=0, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=50, greenOffset=100, blueOffset=105, alphaOffset=150)" - ); - - }); - -}); - -// properties -describe("ColorTransform.js alphaMultiplier test", () => -{ - - it("default test case1", () => - { - const ct = new ColorTransform(); - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case2", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = null; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case3", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = undefined; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case4", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = true; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case5", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = ""; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case6", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = "abc"; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case7", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = 0; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case8", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = 1; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case9", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = 500; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case10", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = 50000000000000000; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case11", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = -1; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case12", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = -500; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case13", () => - { - const ct = new ColorTransform(); - ct.alphaMultiplier = -50000000000000000; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case14", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = { "a":0 }; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case15", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = function a() {}; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case16", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = [1]; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case17", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = [1,2]; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case18", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = {}; - expect(ct.alphaMultiplier).toBe(0); - }); - - it("default test case19", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = { "toString":function () { return 1 } }; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case20", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = { "toString":function () { return "1" } }; - expect(ct.alphaMultiplier).toBe(1); - }); - - it("default test case21", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaMultiplier = { "toString":function () { return "1a" } }; - expect(ct.alphaMultiplier).toBe(0); - }); - -}); - -describe("ColorTransform.js alphaOffset test", () => -{ - - it("default test case1", () => - { - const ct = new ColorTransform(); - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case2", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = null; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case3", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = undefined; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case4", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = true; - expect(ct.alphaOffset).toBe(1); - }); - - it("default test case5", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = ""; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case6", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = "abc"; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case7", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = 0; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case8", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = 1; - expect(ct.alphaOffset).toBe(1); - }); - - it("default test case9", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = 500; - expect(ct.alphaOffset).toBe(255); - }); - - it("default test case10", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = 50000000000000000; - expect(ct.alphaOffset).toBe(255); - }); - - it("default test case11", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = -1; - expect(ct.alphaOffset).toBe(-1); - }); - - it("default test case12", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = -500; - expect(ct.alphaOffset).toBe(-255); - }); - - it("default test case13", () => - { - const ct = new ColorTransform(); - ct.alphaOffset = -50000000000000000; - expect(ct.alphaOffset).toBe(-255); - }); - - it("default test case14", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = { "a":0 }; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = function a() {}; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case16", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = [1]; - expect(ct.alphaOffset).toBe(1); - }); - - it("default test case17", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = [1,2]; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case18", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = {}; - expect(ct.alphaOffset).toBe(0); - }); - - it("default test case19", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = { "toString":function () { return 1 } }; - expect(ct.alphaOffset).toBe(1); - }); - - it("default test case20", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = { "toString":function () { return "1" } }; - expect(ct.alphaOffset).toBe(1); - }); - - it("default test case21", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.alphaOffset = { "toString":function () { return "1a" } }; - expect(ct.alphaOffset).toBe(0); - }); - -}); - -describe("ColorTransform.js blueMultiplier test", () => -{ - - it("default test case1", () => - { - const ct = new ColorTransform(); - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case2", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = null; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case3", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = undefined; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case4", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = true; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case5", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = ""; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case6", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = "abc"; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case7", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = 0; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case8", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = 1; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case9", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = 500; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case10", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = 50000000000000000; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case11", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = -1; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case12", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = -500; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case13", () => - { - const ct = new ColorTransform(); - ct.blueMultiplier = -50000000000000000; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case14", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = { "a":0 }; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case15", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = function a() {}; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case16", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = [1]; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case17", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = [1,2]; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case18", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = {}; - expect(ct.blueMultiplier).toBe(0); - }); - - it("default test case19", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = { "toString":function () { return 1 } }; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case20", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = { "toString":function () { return "1" } }; - expect(ct.blueMultiplier).toBe(1); - }); - - it("default test case21", () => - { - const ct = new ColorTransform(); - // @ts-ignore - ct.blueMultiplier = { "toString":function () { return "1a" } }; - expect(ct.blueMultiplier).toBe(0); - }); - -}); - -describe("ColorTransform.js blueOffset test", () => -{ - - it("default test case1", () => - { - let ct = new ColorTransform(); - expect(ct.blueOffset).toBe(0); - }); - - it("default test case2", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = null; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case3", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = undefined; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case4", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = true; - expect(ct.blueOffset).toBe(1); - }); - - it("default test case5", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = ""; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case6", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = "abc"; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case7", () => - { - let ct = new ColorTransform(); - ct.blueOffset = 0; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case8", () => - { - let ct = new ColorTransform(); - ct.blueOffset = 1; - expect(ct.blueOffset).toBe(1); - }); - - it("default test case9", () => - { - let ct = new ColorTransform(); - ct.blueOffset = 500; - expect(ct.blueOffset).toBe(255); - }); - - it("default test case10", () => - { - let ct = new ColorTransform(); - ct.blueOffset = 50000000000000000; - expect(ct.blueOffset).toBe(255); - }); - - it("default test case11", () => - { - let ct = new ColorTransform(); - ct.blueOffset = -1; - expect(ct.blueOffset).toBe(-1); - }); - - it("default test case12", () => - { - let ct = new ColorTransform(); - ct.blueOffset = -500; - expect(ct.blueOffset).toBe(-255); - }); - - it("default test case13", () => - { - let ct = new ColorTransform(); - ct.blueOffset = -50000000000000000; - expect(ct.blueOffset).toBe(-255); - }); - - it("default test case14", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = { "a":0 }; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = function a() {}; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case16", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = [1]; - expect(ct.blueOffset).toBe(1); - }); - - it("default test case17", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = [1,2]; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case18", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = {}; - expect(ct.blueOffset).toBe(0); - }); - - it("default test case19", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = { "toString":function () { return 1 } }; - expect(ct.blueOffset).toBe(1); - }); - - it("default test case20", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = { "toString":function () { return "1" } }; - expect(ct.blueOffset).toBe(1); - }); - - it("default test case21", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.blueOffset = { "toString":function () { return "1a" } }; - expect(ct.blueOffset).toBe(0); - }); - -}); - -describe("ColorTransform.js greenMultiplier test", () => -{ - - it("default test case1", () => - { - let ct = new ColorTransform(); - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case2", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = null; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case3", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = undefined; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case4", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = true; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case5", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = ""; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case6", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = "abc"; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case7", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = 0; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case8", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = 1; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case9", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = 500; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case10", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = 50000000000000000; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case11", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = -1; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case12", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = -500; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case13", () => - { - let ct = new ColorTransform(); - ct.greenMultiplier = -50000000000000000; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case14", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = { "a":0 }; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = function a() {}; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case16", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = [1]; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case17", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = [1,2]; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case18", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = {}; - expect(ct.greenMultiplier).toBe(0); - }); - - it("default test case19", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = { "toString":function () { return 1 } }; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case20", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = { "toString":function () { return "1" } }; - expect(ct.greenMultiplier).toBe(1); - }); - - it("default test case21", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenMultiplier = { "toString":function () { return "1a" } }; - expect(ct.greenMultiplier).toBe(0); - }); - -}); - -describe("ColorTransform.js greenOffset test", () => -{ - - it("default test case1", () => - { - let ct = new ColorTransform(); - expect(ct.greenOffset).toBe(0); - }); - - it("default test case2", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = null; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case3", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = undefined; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case4", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = true; - expect(ct.greenOffset).toBe(1); - }); - - it("default test case5", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = ""; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case6", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = "abc"; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case7", () => - { - let ct = new ColorTransform(); - ct.greenOffset = 0; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case8", () => - { - let ct = new ColorTransform(); - ct.greenOffset = 1; - expect(ct.greenOffset).toBe(1); - }); - - it("default test case9", () => - { - let ct = new ColorTransform(); - ct.greenOffset = 500; - expect(ct.greenOffset).toBe(255); - }); - - it("default test case10", () => - { - let ct = new ColorTransform(); - ct.greenOffset = 50000000000000000; - expect(ct.greenOffset).toBe(255); - }); - - it("default test case11", () => - { - let ct = new ColorTransform(); - ct.greenOffset = -1; - expect(ct.greenOffset).toBe(-1); - }); - - it("default test case12", () => - { - let ct = new ColorTransform(); - ct.greenOffset = -500; - expect(ct.greenOffset).toBe(-255); - }); - - it("default test case13", () => - { - let ct = new ColorTransform(); - ct.greenOffset = -50000000000000000; - expect(ct.greenOffset).toBe(-255); - }); - - it("default test case14", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = { "a":0 }; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = function a() {}; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case16", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = [1]; - expect(ct.greenOffset).toBe(1); - }); - - it("default test case17", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = [1,2]; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case18", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = {}; - expect(ct.greenOffset).toBe(0); - }); - - it("default test case19", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = { "toString":function () { return 1 } }; - expect(ct.greenOffset).toBe(1); - }); - - it("default test case20", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = { "toString":function () { return "1" } }; - expect(ct.greenOffset).toBe(1); - }); - - it("default test case21", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.greenOffset = { "toString":function () { return "1a" } }; - expect(ct.greenOffset).toBe(0); - }); - -}); - -describe("ColorTransform.js redMultiplier test", () => -{ - - it("default test case1", () => - { - let ct = new ColorTransform(); - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case2", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = null; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case3", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = undefined; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case4", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = true; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case5", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = ""; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case6", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = "abc"; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case7", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = 0; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case8", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = 1; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case9", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = 500; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case10", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = 50000000000000000; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case11", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = -1; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case12", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = -500; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case13", () => - { - let ct = new ColorTransform(); - ct.redMultiplier = -50000000000000000; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case14", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = { "a":0 }; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = function a() {}; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case16", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = [1]; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case17", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = [1,2]; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case18", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = {}; - expect(ct.redMultiplier).toBe(0); - }); - - it("default test case19", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = { "toString":function () { return 1 } }; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case20", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = { "toString":function () { return "1" } }; - expect(ct.redMultiplier).toBe(1); - }); - - it("default test case21", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redMultiplier = { "toString":function () { return "1a" } }; - expect(ct.redMultiplier).toBe(0); - }); - -}); - -describe("ColorTransform.js redOffset test", () => -{ - - it("default test case1", () => - { - let ct = new ColorTransform(); - expect(ct.redOffset).toBe(0); - }); - - it("default test case2", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = null; - expect(ct.redOffset).toBe(0); - }); - - it("default test case3", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = undefined; - expect(ct.redOffset).toBe(0); - }); - - it("default test case4", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = true; - expect(ct.redOffset).toBe(1); - }); - - it("default test case5", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = ""; - expect(ct.redOffset).toBe(0); - }); - - it("default test case6", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = "abc"; - expect(ct.redOffset).toBe(0); - }); - - it("default test case7", () => - { - let ct = new ColorTransform(); - ct.redOffset = 0; - expect(ct.redOffset).toBe(0); - }); - - it("default test case8", () => - { - let ct = new ColorTransform(); - ct.redOffset = 1; - expect(ct.redOffset).toBe(1); - }); - - it("default test case9", () => - { - let ct = new ColorTransform(); - ct.redOffset = 500; - expect(ct.redOffset).toBe(255); - }); - - it("default test case10", () => - { - let ct = new ColorTransform(); - ct.redOffset = 50000000000000000; - expect(ct.redOffset).toBe(255); - }); - - it("default test case11", () => - { - let ct = new ColorTransform(); - ct.redOffset = -1; - expect(ct.redOffset).toBe(-1); - }); - - it("default test case12", () => - { - let ct = new ColorTransform(); - ct.redOffset = -500; - expect(ct.redOffset).toBe(-255); - }); - - it("default test case13", () => - { - let ct = new ColorTransform(); - ct.redOffset = -50000000000000000; - expect(ct.redOffset).toBe(-255); - }); - - it("default test case14", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = { "a":0 }; - expect(ct.redOffset).toBe(0); - }); - - it("default test case15", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = function a() {}; - expect(ct.redOffset).toBe(0); - }); - - it("default test case16", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = [1]; - expect(ct.redOffset).toBe(1); - }); - - it("default test case17", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = [1,2]; - expect(ct.redOffset).toBe(0); - }); - - it("default test case18", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = {}; - expect(ct.redOffset).toBe(0); - }); - - it("default test case19", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = { "toString":function () { return 1 } }; - expect(ct.redOffset).toBe(1); - }); - - it("default test case20", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = { "toString":function () { return "1" } }; - expect(ct.redOffset).toBe(1); - }); - - it("default test case21", () => - { - let ct = new ColorTransform(); - // @ts-ignore - ct.redOffset = { "toString":function () { return "1a" } }; - expect(ct.redOffset).toBe(0); - }); - -}); - -describe("ColorTransform.js alphaMultiplier test", () => -{ - it("alphaMultiplier valid test1", () => - { - - let ct = new ColorTransform(1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000); - - expect(ct.redMultiplier).toBe(1); - expect(ct.greenMultiplier).toBe(1); - expect(ct.blueMultiplier).toBe(1); - expect(ct.alphaMultiplier).toBe(1); - expect(ct.redOffset).toBe(255); - expect(ct.greenOffset).toBe(255); - expect(ct.blueOffset).toBe(255); - expect(ct.alphaOffset).toBe(255); - - }); - - it("alphaMultiplier valid test2", () => - { - - let ct = new ColorTransform(-1000000, -1000000, -1000000, -1000000, -1000000, -1000000, -1000000, -1000000); - - expect(ct.redMultiplier).toBe(0); - expect(ct.greenMultiplier).toBe(0); - expect(ct.blueMultiplier).toBe(0); - expect(ct.alphaMultiplier).toBe(0); - expect(ct.redOffset).toBe(-255); - expect(ct.greenOffset).toBe(-255); - expect(ct.blueOffset).toBe(-255); - expect(ct.alphaOffset).toBe(-255); - - }); - - it("alphaMultiplier valid test3", () => - { - - let ct = new ColorTransform(0, 0, 0, 0, 0, 0, 0, 0); - - expect(ct.redMultiplier).toBe(0); - expect(ct.greenMultiplier).toBe(0); - expect(ct.blueMultiplier).toBe(0); - expect(ct.alphaMultiplier).toBe(0); - expect(ct.redOffset).toBe(0); - expect(ct.greenOffset).toBe(0); - expect(ct.blueOffset).toBe(0); - expect(ct.alphaOffset).toBe(0); - - }); - - it("alphaMultiplier valid test4", () => - { - - let ct = new ColorTransform(-1, -1, -1, -1, -1, -1, -1, -1); - - expect(ct.redMultiplier).toBe(0); - expect(ct.greenMultiplier).toBe(0); - expect(ct.blueMultiplier).toBe(0); - expect(ct.alphaMultiplier).toBe(0); - expect(ct.redOffset).toBe(-1); - expect(ct.greenOffset).toBe(-1); - expect(ct.blueOffset).toBe(-1); - expect(ct.alphaOffset).toBe(-1); - - }); - - it("alphaMultiplier valid test4", () => - { - - let ct = new ColorTransform(-1.1111, -1.1111, -1.1111, -1.1111, -1.1111, -1.1111, -1.1111, -1.1111); - - expect(ct.redMultiplier).toBe(0); - expect(ct.greenMultiplier).toBe(0); - expect(ct.blueMultiplier).toBe(0); - expect(ct.alphaMultiplier).toBe(0); - expect(ct.redOffset).toBe(-1); - expect(ct.greenOffset).toBe(-1); - expect(ct.blueOffset).toBe(-1); - expect(ct.alphaOffset).toBe(-1); - - }); -}); - -describe("ColorTransform.js concat test", () => -{ - - it("concat valid test1", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.redMultiplier = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=50, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test2", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.redOffset = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=-25, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test3", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.greenMultiplier = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=100, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test4", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.greenOffset = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=-40, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test5", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.blueMultiplier = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=150, alphaOffset=150)" - ); - - }); - - it("concat valid test6", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.blueOffset = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=-45, alphaOffset=150)" - ); - - }); - - it("concat valid test7", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.alphaMultiplier = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0, redOffset=24, greenOffset=60, blueOffset=105, alphaOffset=200)" - ); - - }); - - it("concat valid test8", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.alphaOffset = "a"; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=105, alphaOffset=-50)" - ); - - }); - - it("concat valid test9", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.redMultiplier = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=50, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test10", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.redOffset = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=-25, greenOffset=60, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test11", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.greenMultiplier = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=100, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test12", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.greenOffset = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=-40, blueOffset=105, alphaOffset=150)" - ); - - }); - - it("concat valid test13", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.blueMultiplier = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=150, alphaOffset=150)" - ); - - }); - - it("concat valid test14", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.blueOffset = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=-45, alphaOffset=150)" - ); - - }); - - it("concat valid test15", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.alphaMultiplier = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0, redOffset=24, greenOffset=60, blueOffset=105, alphaOffset=200)" - ); - - }); - - it("concat valid test16", () => - { - - let ct1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); - let ct2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); - // @ts-ignore - ct1.alphaOffset = false; - ct1.concat(ct2); - - expect(ct1.toString()).toBe( - "(redMultiplier=0.08999999612569809, greenMultiplier=0.1600000113248825, blueMultiplier=0.21000000834465027, alphaMultiplier=0.30000001192092896, redOffset=24, greenOffset=60, blueOffset=105, alphaOffset=-50)" - ); - - }); -}); diff --git a/__tests__/next2d/geom/MatrixTest.ts b/__tests__/next2d/geom/MatrixTest.ts deleted file mode 100644 index 613a2133..00000000 --- a/__tests__/next2d/geom/MatrixTest.ts +++ /dev/null @@ -1,2953 +0,0 @@ -import { Matrix } from "../../../packages/geom/src/Matrix"; -import { Point } from "../../../packages/geom/src/Point"; -import { - $SHORT_INT_MAX, - $SHORT_INT_MIN -} from "../../../packages/share/src/RenderUtil"; - -describe("Matrix.js toString test", () => -{ - it("toString test1 success", () => - { - const object = new Matrix(); - expect(object.toString()).toBe("(a=1, b=0, c=0, d=1, tx=0, ty=0)"); - }); - - it("toString test2 success", () => - { - const object = new Matrix(2, 3, 4, 5, 6, 7); - expect(object.toString()).toBe("(a=2, b=3, c=4, d=5, tx=6, ty=7)"); - }); -}); - -describe("Matrix.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Matrix.toString()).toBe("[class Matrix]"); - }); - -}); - -describe("Matrix.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Matrix(); - expect(object.namespace).toBe("next2d.geom.Matrix"); - }); - - it("namespace test static", () => - { - expect(Matrix.namespace).toBe("next2d.geom.Matrix"); - }); - -}); - -describe("Matrix.js property valid test and clone test", () => -{ - - it("property success case1", () => - { - let m = new Matrix(); - m.a = 1.2; - m.b = 0.765; - m.c = -0.872; - m.d = -1.5; - m.tx = 10; - m.ty = -10; - - expect(m.a).toBe(1.2000000476837158); - expect(m.b).toBe(0.7649999856948853); - expect(m.c).toBe(-0.871999979019165); - expect(m.d).toBe(-1.5); - expect(m.tx).toBe(10); - expect(m.ty).toBe(-10); - }); - - it("property success case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = "1.2"; - // @ts-ignore - m.b = "0.765"; - // @ts-ignore - m.c = "-0.872"; - // @ts-ignore - m.d = "-1.5"; - // @ts-ignore - m.tx = "10"; - // @ts-ignore - m.ty = "-10"; - - expect(m.a).toBe(1.2000000476837158); - expect(m.b).toBe(0.7649999856948853); - expect(m.c).toBe(-0.871999979019165); - expect(m.d).toBe(-1.5); - expect(m.tx).toBe(10); - expect(m.ty).toBe(-10); - }); - - it("valid and clone test", () => - { - // valid - // @ts-ignore - let m1 = new Matrix("a", "b", "c", "d", "tx", "ty"); - // @ts-ignore - m1.a = "a"; - // @ts-ignore - m1.b = "b"; - // @ts-ignore - m1.c = "c"; - // @ts-ignore - m1.d = "d"; - // @ts-ignore - m1.tx = "tx"; - // @ts-ignore - m1.ty = "ty"; - - // clone matrix - let m2 = m1._$clone(); - m2.a = 1.2; - m2.b = 0.765; - m2.c = -0.872; - m2.d = -1.5; - m2.tx = 10; - m2.ty = -10; - - // origin - expect(m1.a).toBe(0); - expect(m1.b).toBe(0); - expect(m1.c).toBe(0); - expect(m1.d).toBe(0); - expect(m1.tx).toBe(0); - expect(m1.ty).toBe(0); - - // clone - expect(m2.a).toBe(1.2000000476837158); - expect(m2.b).toBe(0.7649999856948853); - expect(m2.c).toBe(-0.871999979019165); - expect(m2.d).toBe(-1.5); - expect(m2.tx).toBe(10); - expect(m2.ty).toBe(-10); - }); -}); - -describe("Matrix.js concat test", () => -{ - it("concat test1", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=2.5999999046325684, b=0, c=-1.2999999523162842, d=-2.25, tx=10, ty=-17.5)" - ); - }); - - it("concat test2", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(0, 0.75, 0, -1.5, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=0, b=0, c=0, d=-2.25, tx=10, ty=-17.5)" - ); - }); - - it("concat test3", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(1.3, 0, 0, -1.5, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=2.5999999046325684, b=-1.5, c=-1.2999999523162842, d=-1.5, tx=10, ty=-17.5)" - ); - }); - - it("concat test4", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(1.3, 0.75, 0, 0, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=2.5999999046325684, b=1.5, c=-1.2999999523162842, d=-0.75, tx=10, ty=-10)" - ); - }); - - it("concat test5", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(1.3, 0.75, 0, -1.5, 0, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=2.5999999046325684, b=0, c=-1.2999999523162842, d=-2.25, tx=0, ty=-17.5)" - ); - }); - - it("concat test6", () => - { - let m1 = new Matrix(2, 1, -1, 1, 0, 5); - let m2 = new Matrix(1.3, 0.75, 0, -1.5, 10, 0); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=2.5999999046325684, b=0, c=-1.2999999523162842, d=-2.25, tx=10, ty=-7.5)" - ); - }); - - it("concat test7", () => - { - let m1 = new Matrix(1,0,0,1,0,0); - let m2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=1.2999999523162842, b=0.75, c=0, d=-1.5, tx=10, ty=-10)" - ); - }); - - it("concat test8", () => - { - let m1 = new Matrix(1,0,0,1,10,10); - let m2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); - m1.concat(m2); - expect(m1.toString()).toBe( - "(a=1.2999999523162842, b=0.75, c=0, d=-1.5, tx=23, ty=-17.5)" - ); - }); -}); - -describe("Matrix.js rotate test", () => -{ - it("rotate test1", () => - { - let m = new Matrix(1, 0, 0, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=0.7071067690849304, b=0.7071067690849304, c=-0.7071067690849304, d=0.7071067690849304, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test2", () => - { - let m = new Matrix(1, 0, 0, -1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=0.7071067690849304, b=0.7071067690849304, c=0.7071067690849304, d=-0.7071067690849304, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test3", () => - { - let m = new Matrix(-1, 0, 0, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-0.7071067690849304, b=-0.7071067690849304, c=-0.7071067690849304, d=0.7071067690849304, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test4", () => - { - let m = new Matrix(-1, 0, 0, -1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-0.7071067690849304, b=-0.7071067690849304, c=0.7071067690849304, d=-0.7071067690849304, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test5", () => - { - let m = new Matrix(1, 10, 10, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-6.363961219787598, b=7.77817440032959, c=6.363961219787598, d=7.77817440032959, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test6", () => - { - let m = new Matrix(1, -10, 10, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=7.77817440032959, b=-6.363961219787598, c=6.363961219787598, d=7.77817440032959, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test7", () => - { - let m = new Matrix(1, 10, -10, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-6.363961219787598, b=7.77817440032959, c=-7.77817440032959, d=-6.363961219787598, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test8", () => - { - let m = new Matrix(1, 10, 10, 1, -100, 110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-6.363961219787598, b=7.77817440032959, c=6.363961219787598, d=7.77817440032959, tx=-148.492431640625, ty=7.071067810058594)" - ); - }); - - it("rotate test9", () => - { - let m = new Matrix(1, 10, 10, 1, 100, -110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-6.363961219787598, b=7.77817440032959, c=6.363961219787598, d=7.77817440032959, tx=148.492431640625, ty=-7.071067810058594)" - ); - }); - - it("rotate test10", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=6.363961219787598, b=-7.77817440032959, c=-6.363961219787598, d=-7.77817440032959, tx=7.071067810058594, ty=-148.492431640625)" - ); - }); - - it("rotate test11", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(0.5); - expect(m.toString()).toBe( - "(a=3.916672706604004, b=-9.255250930786133, c=-8.29640007019043, d=-5.67183780670166, tx=-35.021446228027344, ty=-144.4766387939453)" - ); - }); - - it("rotate test12", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(-0.5); - expect(m.toString()).toBe( - "(a=-5.67183780670166, b=-8.29640007019043, c=-9.255250930786133, d=3.916672706604004, tx=-140.4950714111328, ty=-48.591529846191406)" - ); - }); - - it("rotate test13", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(90 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=10, b=-1, c=1, d=-10, tx=110, ty=-100)" - ); - }); - - it("rotate test14", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(135 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=7.77817440032959, b=6.363961219787598, c=7.77817440032959, d=-6.363961219787598, tx=148.492431640625, ty=7.071067810058594)" - ); - }); - - it("rotate test15", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(Math.PI); - expect(m.toString()).toBe( - "(a=1, b=10, c=10, d=1, tx=100, ty=110)" - ); - }); - - it("rotate test16", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(-45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-7.77817440032959, b=-6.363961219787598, c=-7.77817440032959, d=6.363961219787598, tx=-148.492431640625, ty=-7.071067810058594)" - ); - }); - - it("rotate test17", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(-90 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-10, b=1, c=-1, d=10, tx=-110, ty=100)" - ); - }); - - it("rotate test18", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(-135 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=-6.363961219787598, b=7.77817440032959, c=6.363961219787598, d=7.77817440032959, tx=-7.071067810058594, ty=148.492431640625)" - ); - }); - - it("rotate test19", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - m.rotate(-1 * Math.PI); - expect(m.toString()).toBe( - "(a=1, b=10, c=10, d=1, tx=100, ty=110)" - ); - }); - - it("rotate test20", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - // @ts-ignore - m.rotate("a"); - expect(m.toString()).toBe( - "(a=0, b=0, c=0, d=0, tx=0, ty=0)" - ); - }); - - it("rotate test21", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - // @ts-ignore - m.a = "a"; - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=7.071067810058594, b=-7.071067810058594, c=-6.363961219787598, d=-7.77817440032959, tx=7.071067810058594, ty=-148.492431640625)" - ); - }); - - it("rotate test22", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - // @ts-ignore - m.c = "a"; - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=6.363961219787598, b=-7.77817440032959, c=0.7071067690849304, d=-0.7071067690849304, tx=7.071067810058594, ty=-148.492431640625)" - ); - }); - - it("rotate test23", () => - { - let m = new Matrix(-1, -10, -10, -1, -100, -110); - // @ts-ignore - m.tx = "a"; - m.rotate(45 / 180 * Math.PI); - expect(m.toString()).toBe( - "(a=6.363961219787598, b=-7.77817440032959, c=-6.363961219787598, d=-7.77817440032959, tx=77.78174591064453, ty=-77.78174591064453)" - ); - }); -}); - -describe("Matrix.js createGradientBox test", () => -{ - it("createGradientBox test1", () => - { - let m = new Matrix(); - m.createGradientBox(1, 0, 9, 0, 0); - expect(m.toString()).toBe( - "(a=-0.0005561097641475499, b=0, c=-0.0002515371597837657, d=0, tx=0.5, ty=0)" - ); - }); - - it("createGradientBox test2", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 9, 0, 0); - expect(m.toString()).toBe( - "(a=-0.0005561097641475499, b=0.0002515371597837657, c=-0.0002515371597837657, d=-0.0005561097641475499, tx=0.5, ty=0.5)" - ); - }); - - it("createGradientBox test3", () => - { - let m = new Matrix(); - m.createGradientBox(1, 0, 9, 1, 0); - expect(m.toString()).toBe( - "(a=-0.0005561097641475499, b=0, c=-0.0002515371597837657, d=0, tx=1.5, ty=0)" - ); - }); - - it("createGradientBox test4", () => - { - let m = new Matrix(); - m.createGradientBox(1, 0, 9, 0, 1); - expect(m.toString()).toBe( - "(a=-0.0005561097641475499, b=0, c=-0.0002515371597837657, d=0, tx=0.5, ty=1)" - ); - }); - - it("createGradientBox test5", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 9, 1, 1); - expect(m.toString()).toBe( - "(a=-0.0005561097641475499, b=0.0002515371597837657, c=-0.0002515371597837657, d=-0.0005561097641475499, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test6", () => - { - let m = new Matrix(); - m.createGradientBox(-1, -1, -9, -1, -1); - expect(m.toString()).toBe( - "(a=0.0005561097641475499, b=0.0002515371597837657, c=-0.0002515371597837657, d=0.0005561097641475499, tx=-1.5, ty=-1.5)" - ); - }); - - it("createGradientBox test7", () => - { - let m = new Matrix(); - // @ts-ignore - m.createGradientBox("a", -1, -9, -1, -1); - expect(m.toString()).toBe( - "(a=0, b=0.0002515371597837657, c=0, d=0.0005561097641475499, tx=0, ty=-1.5)" - ); - }); - - it("createGradientBox test8", () => - { - let m = new Matrix(); - // @ts-ignore - m.createGradientBox(-1, "a", -9, -1, -1); - expect(m.toString()).toBe( - "(a=0.0005561097641475499, b=0, c=-0.0002515371597837657, d=0, tx=-1.5, ty=0)" - ); - }); - - it("createGradientBox test9", () => - { - let m = new Matrix(); - // @ts-ignore - m.createGradientBox(-1, -1, "a", -1, -1); - expect(m.toString()).toBe( - "(a=0, b=0, c=0, d=0, tx=-1.5, ty=-1.5)" - ); - }); - - it("createGradientBox test10", () => - { - let m = new Matrix(); - // @ts-ignore - m.createGradientBox(-1, -1, -9, "a", -1); - expect(m.toString()).toBe( - "(a=0.0005561097641475499, b=0.0002515371597837657, c=-0.0002515371597837657, d=0.0005561097641475499, tx=0, ty=-1.5)" - ); - }); - - it("createGradientBox test11", () => - { - let m = new Matrix(); - // @ts-ignore - m.createGradientBox(-1, -1, -9, -1, "a"); - expect(m.toString()).toBe( - "(a=0.0005561097641475499, b=0.0002515371597837657, c=-0.0002515371597837657, d=0.0005561097641475499, tx=-1.5, ty=0)" - ); - }); - - it("createGradientBox test12", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 45 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=0.0004315837286412716, b=0.0004315837286412716, c=-0.0004315837286412716, d=0.0004315837286412716, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test13", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 90 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=3.7373254383716084e-20, b=0.0006103515625, c=-0.0006103515625, d=3.7373254383716084e-20, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test14", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 135 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=-0.0004315837286412716, b=0.0004315837286412716, c=-0.0004315837286412716, d=-0.0004315837286412716, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test15", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, 180 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=-0.0006103515625, b=7.474650876743217e-20, c=-7.474650876743217e-20, d=-0.0006103515625, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test16", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, -45 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=0.0004315837286412716, b=-0.0004315837286412716, c=0.0004315837286412716, d=0.0004315837286412716, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test17", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, -90 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=3.7373254383716084e-20, b=-0.0006103515625, c=0.0006103515625, d=3.7373254383716084e-20, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test18", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, -135 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=-0.0004315837286412716, b=-0.0004315837286412716, c=0.0004315837286412716, d=-0.0004315837286412716, tx=1.5, ty=1.5)" - ); - }); - - it("createGradientBox test19", () => - { - let m = new Matrix(); - m.createGradientBox(1, 1, -180 / 180 * Math.PI, 1, 1); - expect(m.toString()).toBe( - "(a=-0.0006103515625, b=-7.474650876743217e-20, c=7.474650876743217e-20, d=-0.0006103515625, tx=1.5, ty=1.5)" - ); - }); -}); - -describe("Matrix.js createBox test", () => -{ - it("createBox test1", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = 2.0; - let sy = 3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = 10; - let ty = 20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=10, ty=20)" - ); - }); - - it("createBox test2", () => - { - let m = new Matrix(-1, 0.5, -0.2); - let sx = 2.0; - let sy = 3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = 10; - let ty = 20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=10, ty=20)" - ); - }); - - it("createBox test3", () => - { - let m = new Matrix(-1, -0.5, -0.2); - let sx = 2.0; - let sy = 3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = 10; - let ty = 20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=10, ty=20)" - ); - }); - - it("createBox test4", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = 2.0; - let sy = 3.0; - let r = -2 * Math.PI * (45 / 360); - let tx = 10; - let ty = 20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=2.1213202476501465, tx=10, ty=20)" - ); - }); - - it("createBox test5", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = 10; - let ty = 20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=10, ty=20)" - ); - }); - - it("createBox test6", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = 2.0; - let sy = 3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test7", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test8", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (90 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.2246468525851679e-16, b=3, c=-2, d=-1.8369702788777518e-16, tx=-10, ty=-20)" - ); - }); - - it("createBox test9", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (135 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test10", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (180 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=2, b=3.6739405577555036e-16, c=-2.4492937051703357e-16, d=3, tx=-10, ty=-20)" - ); - }); - - it("createBox test11", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (225 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test12", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (270 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=3.6739402930577075e-16, b=-3, c=2, d=5.510910704284357e-16, tx=-10, ty=-20)" - ); - }); - - it("createBox test13", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (315 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - - expect(m.a).toBe(-1.4142135381698608); - expect(m.b).toBe(-2.1213202476501465); - expect(m.c).toBe(1.4142135381698608); - expect(m.d).toBe(-2.1213202476501465); - expect(m.tx).toBe(-10); - expect(m.ty).toBe(-20); - }); - - it("createBox test14", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = -2 * Math.PI * (360 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-2, b=-7.347881115511007e-16, c=4.898587410340671e-16, d=-3, tx=-10, ty=-20)" - ); - }); - - it("createBox test15", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = "a"; - let tx = -10; - let ty = -20; - // @ts-ignore - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=0, b=0, c=0, d=0, tx=-10, ty=-20)" - ); - }); - - it("createBox test16", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = "a"; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - // @ts-ignore - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=0, b=-2.1213202476501465, c=0, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test17", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = "a"; - let ty = -20; - // @ts-ignore - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=0, ty=-20)" - ); - }); - - it("createBox test18", () => - { - let m = new Matrix(1, 0.5, -0.2); - // @ts-ignore - m.a = "a"; - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test19", () => - { - let m = new Matrix(1, 0.5, -0.2); - // @ts-ignore - m.c = "a"; - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test20", () => - { - let m = new Matrix(1, 0.5, -0.2); - // @ts-ignore - m.tx = "a"; - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test21", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 0; - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-2, b=0, c=0, d=-3, tx=-10, ty=-20)" - ); - }); - - it("createBox test22", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test23", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (90 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.2246468525851679e-16, b=-3, c=2, d=-1.8369702788777518e-16, tx=-10, ty=-20)" - ); - }); - - it("createBox test24", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (135 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test25", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (180 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=2, b=-3.6739405577555036e-16, c=2.4492937051703357e-16, d=3, tx=-10, ty=-20)" - ); - }); - - it("createBox test26", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (225 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=2.1213202476501465, c=-1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test27", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (270 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=3.6739402930577075e-16, b=3, c=-2, d=5.510910704284357e-16, tx=-10, ty=-20)" - ); - }); - - it("createBox test28", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (315 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - - expect(m.a).toBe(-1.4142135381698608); - expect(m.b).toBe(2.1213202476501465); - expect(m.c).toBe(-1.4142135381698608); - expect(m.d).toBe(-2.1213202476501465); - expect(m.tx).toBe(-10); - expect(m.ty).toBe(-20); - }); - - it("createBox test29", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = 2 * Math.PI * (360 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-2, b=7.347881115511007e-16, c=-4.898587410340671e-16, d=-3, tx=-10, ty=-20)" - ); - }); - - it("createBox test30", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (45 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.8477590084075928, b=-1.148050308227539, c=0.7653668522834778, d=-2.7716383934020996, tx=-10, ty=-20)" - ); - }); - - it("createBox test31", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (90 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=-2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test32", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (135 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-0.7653668522834778, b=-2.7716383934020996, c=1.8477590084075928, d=-1.148050308227539, tx=-10, ty=-20)" - ); - }); - - it("createBox test33", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (180 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=-1.2246468525851679e-16, b=-3, c=2, d=-1.8369702788777518e-16, tx=-10, ty=-20)" - ); - }); - - it("createBox test34", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (225 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=0.7653668522834778, b=-2.7716383934020996, c=1.8477590084075928, d=1.148050308227539, tx=-10, ty=-20)" - ); - }); - - it("createBox test35", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (270 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=1.4142135381698608, b=-2.1213202476501465, c=1.4142135381698608, d=2.1213202476501465, tx=-10, ty=-20)" - ); - }); - - it("createBox test36", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (315 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - - expect(m.a).toBe(1.8477590084075928); - expect(m.b).toBe(-1.148050308227539); - expect(m.c).toBe(0.7653668522834778); - expect(m.d).toBe(2.7716383934020996); - expect(m.tx).toBe(-10); - expect(m.ty).toBe(-20); - }); - - it("createBox test37", () => - { - let m = new Matrix(1, 0.5, -0.2); - let sx = -2.0; - let sy = -3.0; - let r = Math.PI * (360 / 360); - let tx = -10; - let ty = -20; - m.createBox(sx, sy, r, tx, ty); - expect(m.toString()).toBe( - "(a=2, b=-3.6739405577555036e-16, c=2.4492937051703357e-16, d=3, tx=-10, ty=-20)" - ); - }); -}); - -describe("Matrix.js invert test", () => -{ - it("invert test1", () => - { - let m = new Matrix(2, 1, 1, 2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=0.6666666865348816, b=-0.3333333432674408, c=-0.3333333432674408, d=0.6666666865348816, tx=66.66667175292969, ty=66.66667175292969)" - ); - }); - - it("invert test2", () => - { - let m = new Matrix(2, 1, 1, 2, -200, -200); - m.invert(); - m.invert(); - expect(m.toString()).toBe( - "(a=2, b=1, c=1, d=2, tx=-200.00001525878906, ty=-200.00001525878906)" - ); - }); - - it("invert test3", () => - { - let m = new Matrix(-2, 1, 1, 2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=-0.4000000059604645, b=0.20000000298023224, c=0.20000000298023224, d=0.4000000059604645, tx=-40, ty=120)" - ); - }); - - it("invert test4", () => - { - let m = new Matrix(2, -1, 1, 2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=0.4000000059604645, b=0.20000000298023224, c=-0.20000000298023224, d=0.4000000059604645, tx=40, ty=120)" - ); - }); - - it("invert test5", () => - { - let m = new Matrix(2, 1, -1, 2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=0.4000000059604645, b=-0.20000000298023224, c=0.20000000298023224, d=0.4000000059604645, tx=120, ty=40)" - ); - }); - - it("invert test6", () => - { - let m = new Matrix(2, 1, 1, -2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=0.4000000059604645, b=0.20000000298023224, c=0.20000000298023224, d=-0.4000000059604645, tx=120, ty=-40)" - ); - }); - - it("invert test7", () => - { - let m = new Matrix(-2, -1, -1, -2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=-0.6666666865348816, b=0.3333333432674408, c=0.3333333432674408, d=-0.6666666865348816, tx=-66.66667175292969, ty=-66.66667175292969)" - ); - }); - - it("invert test8", () => - { - // @ts-ignore - let m = new Matrix("a", -1, -1, -2, -200, -200); - m.invert(); - expect(m.toString()).toBe( - "(a=2, b=-1, c=-1, d=0, tx=200, ty=-200)" - ); - }); -}); - -describe("Matrix.js transformPoint test", () => -{ - it("transformPoint test1", () => - { - let m = new Matrix(1, 0, 0, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-19.79898965358734, y=164.04878056049347)" - ); - }); - - it("transformPoint test2", () => - { - let m = new Matrix(1, 1, 0, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-21.213203191757202, y=165.46299409866333)" - ); - }); - - it("transformPoint test3", () => - { - let m = new Matrix(1, 0, 1, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-5); - expect(p2.y | 0).toBe(178); - - }); - - it("transformPoint test4", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-7); - expect(p2.y | 0).toBe(179); - }); - - it("transformPoint test5", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-7); - expect(p2.y | 0).toBe(117); - }); - - it("transformPoint test6", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - // @ts-ignore - m.rotate("a"); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=0, y=0)" - ); - }); - - it("transformPoint test7", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - // @ts-ignore - m.a = "a"; - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-5); - expect(p2.y | 0).toBe(118); - }); - - it("transformPoint test8", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - // @ts-ignore - m.c = "a"; - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=7.071067571640015, y=131.52186918258667)" - ); - }); - - it("transformPoint test9", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - // @ts-ignore - m.tx = "a"; - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-77.78174591064453, y=46.66904807090759)" - ); - }); - - it("transformPoint test10", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - // @ts-ignore - p1.x = "a"; - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-7); - expect(p2.y | 0).toBe(120); - }); - - it("transformPoint test11", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(-7); - expect(p2.y | 0).toBe(117); - }); - - it("transformPoint test12", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(90 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-88, y=78)" - ); - }); - - it("transformPoint test13", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(135 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-117.37973380088806, y=-7.071067810058596)" - ); - }); - - it("transformPoint test14", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(180 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-78, y=-88)" - ); - }); - - it("transformPoint test15", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(-45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.x | 0).toBe(117); - expect(p2.y | 0).toBe(7); - - }); - - it("transformPoint test16", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(-90 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=88, y=-78)" - ); - }); - - it("transformPoint test17", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(-135 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=7.071067810058591, y=-117.37973380088806)" - ); - }); - - it("transformPoint test18", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.rotate(-180 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.transformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-78, y=-88)" - ); - }); -}); - -describe("Matrix.js deltaTransformPoint test", () => -{ - it("deltaTransformPoint test1", () => - { - let m = new Matrix(1, 0, 0, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-12.727921843528748, y=15.55634891986847)" - ); - }); - - it("deltaTransformPoint test2", () => - { - let m = new Matrix(10, 0, 0, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=2.384185791015625e-7, y=28.284271001815796)" - ); - }); - - it("deltaTransformPoint test3", () => - { - let m = new Matrix(1, 10, 0, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(-26); - expect(p2.y | 0).toBe(29); - }); - - it("deltaTransformPoint test4", () => - { - let m = new Matrix(1, 0, 10, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=128.6934379339218, y=156.97770154476166)" - ); - }); - - it("deltaTransformPoint test5", () => - { - let m = new Matrix(1, 0, 0, 10, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-140.007142663002, y=142.83556973934174)" - ); - }); - - it("deltaTransformPoint test6", () => - { - let m = new Matrix(-1, 0, 0, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-15.55634891986847, y=12.727921843528748)" - ); - }); - - it("deltaTransformPoint test7", () => - { - let m = new Matrix(1, 0, 0, -1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=15.55634891986847, y=-12.727921843528748)" - ); - }); - - it("deltaTransformPoint test8", () => - { - let m = new Matrix(-1, 0, 0, -1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=12.727921843528748, y=-15.55634891986847)" - ); - }); - - it("deltaTransformPoint test9", () => - { - let m = new Matrix(-1, -1, 0, -1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=14.142135381698608, y=-16.97056245803833)" - ); - }); - - it("deltaTransformPoint test10", () => - { - let m = new Matrix(-1, 0, -1, -1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(-1); - expect(p2.y | 0).toBe(-29); - }); - - it("deltaTransformPoint test11", () => - { - let m = new Matrix(-1, -1, -1, -1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(-31); - }); - - it("deltaTransformPoint test12", () => - { - // @ts-ignore - let m = new Matrix("a", 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(-1); - expect(p2.y | 0).toBe(29); - - }); - - it("deltaTransformPoint test13", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - // @ts-ignore - m.translate("a", 0); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(31); - }); - - it("deltaTransformPoint test14", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - // @ts-ignore - m.translate(10, "a"); - m.rotate(45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(31); - }); - - it("deltaTransformPoint test15", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - // @ts-ignore - let p1 = new Point("a", 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(28); - }); - - it("deltaTransformPoint test16", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(45 / 180 * Math.PI); - - // @ts-ignore - let p1 = new Point(2, "a"); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(2); - }); - - it("deltaTransformPoint test17", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - - // @ts-ignore - m.rotate("a"); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=0, y=0)" - ); - }); - - it("deltaTransformPoint test18", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(90 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-22, y=22)" - ); - }); - - it("deltaTransformPoint test19", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(135 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-31.11269783973694, y=2.4424906541753444e-15)" - ); - }); - - it("deltaTransformPoint test20", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(180 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-22, y=-22)" - ); - }); - - it("deltaTransformPoint test21", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(-45 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(31); - expect(p2.y | 0).toBe(0); - }); - - it("deltaTransformPoint test22", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(-90 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=22, y=-22)" - ); - }); - - it("deltaTransformPoint test23", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(-135 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.x | 0).toBe(0); - expect(p2.y | 0).toBe(-31); - - }); - - it("deltaTransformPoint test24", () => - { - let m = new Matrix(1, 1, 1, 1, 100, 110); - m.translate(10, 0); - m.rotate(-180 / 180 * Math.PI); - - let p1 = new Point(2, 20); - let p2 = m.deltaTransformPoint(p1); - - expect(p2.toString()).toBe( - "(x=-22, y=-22)" - ); - }); -}); - -describe("Matrix.js pattern test", () => -{ - - it("pattern test case1", () => - { - let matrix = new Matrix(); - - // 単位行列化 - matrix.identity(); - // 拡大縮小成分を乗算 - matrix.scale( 256 / 1638.4 , 256 / 1638.4 ); - // 角度成分を乗算 - matrix.rotate(Math.PI / 180); - // 移動成分を乗算 - matrix.translate( 128.0 , 128.0 ); - - expect(matrix.toString()).toBe("(a=0.15622620284557343, b=0.0027269385755062103, c=-0.0027269385755062103, d=0.15622620284557343, tx=128, ty=128)"); - }); - -}); - -describe("Matrix.js scale test", () => -{ - - it("scale test case1", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); - matrix.scale(2, 2); - expect(matrix.toString()).toBe("(a=2, b=1, c=-1, d=2, tx=0, ty=0)"); - }); - - it("scale test case2", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); - matrix.scale(2, 3); - expect(matrix.toString()).toBe("(a=2, b=1.5, c=-1, d=3, tx=0, ty=0)"); - }); - - it("scale test case3", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); - matrix.scale(3, 2); - expect(matrix.toString()).toBe("(a=3, b=1, c=-1.5, d=2, tx=0, ty=0)"); - }); - - it("scale test case4", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 10, 0); - matrix.scale(-1, 2); - expect(matrix.toString()).toBe("(a=-1, b=1, c=0.5, d=2, tx=-10, ty=0)"); - }); - - it("scale test case5", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 0, 10); - matrix.scale(3, -2); - expect(matrix.toString()).toBe("(a=3, b=-1, c=-1.5, d=-2, tx=0, ty=-20)"); - }); - - it("scale test case6", () => - { - let matrix = new Matrix(1, 0.5, -0.5, 1, 0, 10); - // @ts-ignore - matrix.scale("a", 2); - expect(matrix.toString()).toBe("(a=0, b=1, c=0, d=2, tx=0, ty=20)"); - }); - -}); - -describe("Matrix.js a test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.a).toBe(1); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = null; - expect(m.a).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = undefined; - expect(m.a).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = true; - expect(m.a).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = ""; - expect(m.a).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = "abc"; - expect(m.a).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.a = 0; - expect(m.a).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.a = 1; - expect(m.a).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.a = 500; - expect(m.a).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.a = 50000000000000000; - expect(m.a).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.a = -1; - expect(m.a).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.a = -500; - expect(m.a).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.a = -50000000000000000; - expect(m.a).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = { "a":0 }; - expect(m.a).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = function a() {}; - expect(m.a).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = [1]; - expect(m.a).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = [1,2]; - expect(m.a).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = {}; - expect(m.a).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = { "toString":function () { return 1 } }; - expect(m.a).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = { "toString":function () { return "1" } }; - expect(m.a).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.a = { "toString":function () { return "1a" } }; - expect(m.a).toBe(0); - }); - -}); - -describe("Matrix.js b test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.b).toBe(0); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = null; - expect(m.b).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = undefined; - expect(m.b).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = true; - expect(m.b).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = ""; - expect(m.b).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = "abc"; - expect(m.b).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.b = 0; - expect(m.b).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.b = 1; - expect(m.b).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.b = 500; - expect(m.b).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.b = 50000000000000000; - expect(m.b).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.b = -1; - expect(m.b).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.b = -500; - expect(m.b).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.b = -50000000000000000; - expect(m.b).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = { "a":0 }; - expect(m.b).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = function a() {}; - expect(m.b).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = [1]; - expect(m.b).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = [1,2]; - expect(m.b).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = {}; - expect(m.b).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = { "toString":function () { return 1 } }; - expect(m.b).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = { "toString":function () { return "1" } }; - expect(m.b).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.b = { "toString":function () { return "1a" } }; - expect(m.b).toBe(0); - }); - -}); - -describe("Matrix.js c test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.c).toBe(0); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = null; - expect(m.c).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = undefined; - expect(m.c).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = true; - expect(m.c).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = ""; - expect(m.c).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = "abc"; - expect(m.c).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.c = 0; - expect(m.c).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.c = 1; - expect(m.c).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.c = 500; - expect(m.c).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.c = 50000000000000000; - expect(m.c).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.c = -1; - expect(m.c).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.c = -500; - expect(m.c).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.c = -50000000000000000; - expect(m.c).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = { "a":0 }; - expect(m.c).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = function a() {}; - expect(m.c).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = [1]; - expect(m.c).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = [1,2]; - expect(m.c).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = {}; - expect(m.c).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = { "toString":function () { return 1 } }; - expect(m.c).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = { "toString":function () { return "1" } }; - expect(m.c).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.c = { "toString":function () { return "1a" } }; - expect(m.c).toBe(0); - }); - -}); - -describe("Matrix.js d test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.d).toBe(1); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = null; - expect(m.d).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = undefined; - expect(m.d).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = true; - expect(m.d).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = ""; - expect(m.d).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = "abc"; - expect(m.d).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.d = 0; - expect(m.d).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.d = 1; - expect(m.d).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.d = 500; - expect(m.d).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.d = 50000000000000000; - expect(m.d).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.d = -1; - expect(m.d).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.d = -500; - expect(m.d).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.d = -50000000000000000; - expect(m.d).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = { "a":0 }; - expect(m.d).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = function a() {}; - expect(m.d).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = [1]; - expect(m.d).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = [1,2]; - expect(m.d).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = {}; - expect(m.d).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = { "toString":function () { return 1 } }; - expect(m.d).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = { "toString":function () { return "1" } }; - expect(m.d).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.d = { "toString":function () { return "1a" } }; - expect(m.d).toBe(0); - }); - -}); - -describe("Matrix.js tx test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.tx).toBe(0); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = null; - expect(m.tx).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = undefined; - expect(m.tx).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = true; - expect(m.tx).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = ""; - expect(m.tx).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = "abc"; - expect(m.tx).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.tx = 0; - expect(m.tx).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.tx = 1; - expect(m.tx).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.tx = 500; - expect(m.tx).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.tx = 50000000000000000; - expect(m.tx).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.tx = -1; - expect(m.tx).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.tx = -500; - expect(m.tx).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.tx = -50000000000000000; - expect(m.tx).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = { "a":0 }; - expect(m.tx).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = function a() {}; - expect(m.tx).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = [1]; - expect(m.tx).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = [1,2]; - expect(m.tx).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = {}; - expect(m.tx).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = { "toString":function () { return 1 } }; - expect(m.tx).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = { "toString":function () { return "1" } }; - expect(m.tx).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.tx = { "toString":function () { return "1a" } }; - expect(m.tx).toBe(0); - }); - -}); - -describe("Matrix.js ty test", () => -{ - - it("default test case1", () => - { - let m = new Matrix(); - expect(m.ty).toBe(0); - }); - - it("default test case2", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = null; - expect(m.ty).toBe(0); - }); - - it("default test case3", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = undefined; - expect(m.ty).toBe(0); - }); - - it("default test case4", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = true; - expect(m.ty).toBe(1); - }); - - it("default test case5", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = ""; - expect(m.ty).toBe(0); - }); - - it("default test case6", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = "abc"; - expect(m.ty).toBe(0); - }); - - it("default test case7", () => - { - let m = new Matrix(); - m.ty = 0; - expect(m.ty).toBe(0); - }); - - it("default test case8", () => - { - let m = new Matrix(); - m.ty = 1; - expect(m.ty).toBe(1); - }); - - it("default test case9", () => - { - let m = new Matrix(); - m.ty = 500; - expect(m.ty).toBe(500); - }); - - it("default test case10", () => - { - let m = new Matrix(); - m.ty = 50000000000000000; - expect(m.ty).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let m = new Matrix(); - m.ty = -1; - expect(m.ty).toBe(-1); - }); - - it("default test case12", () => - { - let m = new Matrix(); - m.ty = -500; - expect(m.ty).toBe(-500); - }); - - it("default test case13", () => - { - let m = new Matrix(); - m.ty = -50000000000000000; - expect(m.ty).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = { "a":0 }; - expect(m.ty).toBe(0); - }); - - it("default test case15", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = function a() {}; - expect(m.ty).toBe(0); - }); - - it("default test case16", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = [1]; - expect(m.ty).toBe(1); - }); - - it("default test case17", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = [1,2]; - expect(m.ty).toBe(0); - }); - - it("default test case18", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = {}; - expect(m.ty).toBe(0); - }); - - it("default test case19", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = { "toString":function () { return 1 } }; - expect(m.ty).toBe(1); - }); - - it("default test case20", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = { "toString":function () { return "1" } }; - expect(m.ty).toBe(1); - }); - - it("default test case21", () => - { - let m = new Matrix(); - // @ts-ignore - m.ty = { "toString":function () { return "1a" } }; - expect(m.ty).toBe(0); - }); - -}); - -describe("Matrix.js BugFix", () => -{ - it("default test case10", () => - { - let mat = new Matrix(); - expect(mat.toString()).toBe("(a=1, b=0, c=0, d=1, tx=0, ty=0)"); - mat.translate(-100, -100); - expect(mat.toString()).toBe("(a=1, b=0, c=0, d=1, tx=-100, ty=-100)"); - mat.scale(0.0, 1.0); - expect(mat.toString()).toBe("(a=0, b=0, c=0, d=1, tx=0, ty=-100)"); - mat.translate(100, 100); - expect(mat.toString()).toBe("(a=0, b=0, c=0, d=1, tx=100, ty=0)"); - - }); -}); - -describe("Matrix.js copyFrom", () => -{ - it("copy test case1", () => - { - - const defaultMatrix = new Matrix(); - expect(defaultMatrix.toString()).toBe( - "(a=1, b=0, c=0, d=1, tx=0, ty=0)" - ); - - const matrix = new Matrix(1, 2, 3, 4, 5, 6); - defaultMatrix.copyFrom(matrix); - expect(defaultMatrix.toString()).toBe( - "(a=1, b=2, c=3, d=4, tx=5, ty=6)" - ); - - defaultMatrix.a = 1; - defaultMatrix.b = 0; - defaultMatrix.c = 0; - defaultMatrix.d = 1; - defaultMatrix.tx = 100; - defaultMatrix.ty = 200; - expect(defaultMatrix.toString()).toBe( - "(a=1, b=0, c=0, d=1, tx=100, ty=200)" - ); - - expect(matrix.toString()).toBe( - "(a=1, b=2, c=3, d=4, tx=5, ty=6)" - ); - - }); - -}); - -describe("Matrix.js setTo", () => -{ - it("copy test case1", () => - { - - const matrix = new Matrix(); - expect(matrix.toString()).toBe( - "(a=1, b=0, c=0, d=1, tx=0, ty=0)" - ); - - matrix.setTo(1, 2, 3, 4, 5, 6); - expect(matrix.toString()).toBe( - "(a=1, b=2, c=3, d=4, tx=5, ty=6)" - ); - - }); -}); - -describe("Matrix.js identity", () => -{ - it("clear test case1", () => - { - - const matrix = new Matrix(1, 2, 3, 4, 5, 6); - expect(matrix.toString()).toBe( - "(a=1, b=2, c=3, d=4, tx=5, ty=6)" - ); - - matrix.identity(); - expect(matrix.toString()).toBe( - "(a=1, b=0, c=0, d=1, tx=0, ty=0)" - ); - - }); -}); \ No newline at end of file diff --git a/__tests__/next2d/geom/PointTest.ts b/__tests__/next2d/geom/PointTest.ts deleted file mode 100644 index 27289183..00000000 --- a/__tests__/next2d/geom/PointTest.ts +++ /dev/null @@ -1,1456 +0,0 @@ -import { Point } from "../../../packages/geom/src/Point"; -import { - $SHORT_INT_MAX, - $SHORT_INT_MIN -} from "../../../packages/share/src/RenderUtil"; - -describe("Point.js toString test", () => -{ - it("toString test1 success", () => - { - const object = new Point(); - expect(object.toString()).toBe("(x=0, y=0)"); - }); - - it("toString test2 success", () => - { - const object = new Point(1, 2); - expect(object.toString()).toBe("(x=1, y=2)"); - }); -}); - -describe("Point.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Point.toString()).toBe("[class Point]"); - }); - -}); - -describe("Point.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Point(); - expect(object.namespace).toBe("next2d.geom.Point"); - }); - - it("namespace test static", () => - { - expect(Point.namespace).toBe("next2d.geom.Point"); - }); - -}); - -describe("Point.js property valid test and clone test", () => -{ - - it("valid and clone test", function () { - - // @ts-ignore - let p1 = new Point("a", "b"); - // @ts-ignore - p1.x = "a"; - // @ts-ignore - p1.y = "b"; - - // clone - let p2 = p1.clone(); - p2.x = 10; - p2.y = 20; - - // origin - expect(p1.x).toBe(0); - expect(p1.y).toBe(0); - - // clone - expect(p2.x).toBe(10); - expect(p2.y).toBe(20); - expect(p2.length).toBe(22.360679774997898); - expect(p2.toString()).toBe("(x=10, y=20)"); - }); - -}); - -describe("Point.js add test", () => -{ - - it("add test1", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(20, 20); - - let p3 = p1.add(p1); - let p4 = p2.add(p2); - let p5 = p1.add(p2); - let p6 = p2.add(p1); - - expect(p3.toString()).toBe("(x=20, y=20)"); - expect(p4.toString()).toBe("(x=40, y=40)"); - expect(p5.toString()).toBe("(x=30, y=30)"); - expect(p6.toString()).toBe("(x=30, y=30)"); - }); - - it("add test2", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(20, -20); - - let p3 = p1.add(p1); - let p4 = p2.add(p2); - let p5 = p1.add(p2); - let p6 = p2.add(p1); - - expect(p3.toString()).toBe("(x=-20, y=-20)"); - expect(p4.toString()).toBe("(x=40, y=-40)"); - expect(p5.toString()).toBe("(x=10, y=-30)"); - expect(p6.toString()).toBe("(x=10, y=-30)"); - }); - - it("add test3", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - let p2 = new Point(20, 0); - - let p3 = p1.add(p1); - let p4 = p2.add(p2); - let p5 = p1.add(p2); - let p6 = p2.add(p1); - - expect(p3.toString()).toBe("(x=0, y=20)"); - expect(p4.toString()).toBe("(x=40, y=0)"); - expect(p5.toString()).toBe("(x=20, y=10)"); - expect(p6.toString()).toBe("(x=20, y=10)"); - }); - -}); - -describe("Point.js copyFrom test", () => -{ - - it("copyFrom test1", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(20, 20); - - p1.copyFrom(p2); - p1.x = 10; - - expect(p1.toString()).toBe("(x=10, y=20)"); - expect(p2.toString()).toBe("(x=20, y=20)"); - }); - - it("copyFrom test2", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(20, -20); - - p1.copyFrom(p2); - - expect(p1.toString()).toBe("(x=20, y=-20)"); - expect(p2.toString()).toBe("(x=20, y=-20)"); - }); - - it("copyFrom test3", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - let p2 = new Point(20, 0); - - p1.copyFrom(p2); - - expect(p1.toString()).toBe("(x=20, y=0)"); - expect(p2.toString()).toBe("(x=20, y=0)"); - }); - - it("copyFrom test4", () => - { - let p1 = new Point(10, 10); - // @ts-ignore - let p2 = new Point("a", 20); - - p1.copyFrom(p2); - - expect(p1.toString()).toBe("(x=0, y=20)"); - expect(p2.toString()).toBe("(x=0, y=20)"); - }); - -}); - -describe("Point.js distance test", () => -{ - - it("distance test1", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - - it("distance test2", () => - { - let p1 = new Point(-10, 10); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test3", () => - { - let p1 = new Point(10, -10); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test4", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(-20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test5", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test6", () => - { - let p1 = new Point(10, -10); - let p2 = new Point(20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - - it("distance test7", () => - { - let p1 = new Point(-10, 10); - let p2 = new Point(-20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - - it("distance test8", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(42.42640687119285); - }); - - it("distance test9", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(-20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(42.42640687119285); - }); - - it("distance test10", () => - { - let p1 = new Point(-10, 10); - let p2 = new Point(20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(42.42640687119285); - }); - - it("distance test11", () => - { - let p1 = new Point(10, -10); - let p2 = new Point(-20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(42.42640687119285); - }); - - it("distance test12", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(-20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test13", () => - { - let p1 = new Point(10, -10); - let p2 = new Point(-20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test14", () => - { - let p1 = new Point(-10, 10); - let p2 = new Point(-20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test15", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(31.622776601683793); - }); - - it("distance test16", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(-20, -20); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - - it("distance test1", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(22.360679774997898); - }); - - it("distance test1", () => - { - // @ts-ignore - let p1 = new Point(10, "a"); - let p2 = new Point(20, 20); - let d = Point.distance(p1, p2); - expect(d).toBe(22.360679774997898); - }); - - it("distance test1", () => - { - let p1 = new Point(10, 10); - // @ts-ignore - let p2 = new Point("a", 20); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - - it("distance test1", () => - { - let p1 = new Point(10, 10); - // @ts-ignore - let p2 = new Point(20, "a"); - let d = Point.distance(p1, p2); - expect(d).toBe(14.142135623730951); - }); - -}); - -describe("Point.js equals test", () => -{ - - it("equals test1", () => - { - let p1 = new Point(10, 10); - let p2 = new Point(10, 10); - let p3 = new Point(10, 20); - let p4 = new Point(20, 10); - let p5 = new Point(20, 20); - - expect(p1.equals(p2)).toBe(true); - expect(p1.equals(p3)).toBe(false); - expect(p1.equals(p4)).toBe(false); - expect(p1.equals(p5)).toBe(false); - }); - - it("equals test2", () => - { - let p1 = new Point(-10, -10); - let p2 = new Point(-10, -10); - let p3 = new Point(-10, -20); - let p4 = new Point(-20, -10); - let p5 = new Point(-20, -20); - - expect(p1.equals(p2)).toBe(true); - expect(p1.equals(p3)).toBe(false); - expect(p1.equals(p4)).toBe(false); - expect(p1.equals(p5)).toBe(false); - }); - - it("equals test3", () => - { - let p1 = new Point(1, 10); - let p2 = new Point(1, 10); - // @ts-ignore - let p3 = new Point(true, 10); - // @ts-ignore - let p4 = new Point(false, 10); - // @ts-ignore - let p5 = new Point(1, false); - - expect(p1.equals(p2)).toBe(true); - expect(p1.equals(p3)).toBe(true); - expect(p1.equals(p4)).toBe(false); - expect(p1.equals(p5)).toBe(false); - }); - - it("equals test4", () => - { - // @ts-ignore - let p1 = new Point(true, 10); - let p2 = new Point(1, 10); - // @ts-ignore - let p3 = new Point(true, 10); - // @ts-ignore - let p4 = new Point(false, 10); - // @ts-ignore - let p5 = new Point(1, false); - - expect(p1.equals(p2)).toBe(true); - expect(p1.equals(p3)).toBe(true); - expect(p1.equals(p4)).toBe(false); - expect(p1.equals(p5)).toBe(false); - }); - - it("equals valid test1", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - let p2 = new Point(10, 10); - - expect(p1.equals(p2)).toBe(false); - }); - - it("equals valid test2", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - // @ts-ignore - let p2 = new Point("a", 10); - - expect(p1.equals(p2)).toBe(true); - }); - - it("equals valid test3", () => - { - // @ts-ignore - let p1 = new Point("a", "a"); - // @ts-ignore - let p2 = new Point("a", "a"); - - expect(p1.equals(p2)).toBe(true); - }); - -}); - -describe("Point.js interpolate test", () => -{ - - it("interpolate test1", () => - { - let p1 = new Point(0, 0); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=3, y=4)"); - }); - - it("interpolate test2", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=7.5, y=9)"); - }); - - it("interpolate test3", () => - { - let p1 = new Point(-9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=-1.5, y=9)"); - }); - - it("interpolate test4", () => - { - let p1 = new Point(9, -10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=7.5, y=-1)"); - }); - - it("interpolate test5", () => - { - let p1 = new Point(-9, -10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=-1.5, y=-1)"); - }); - - it("interpolate test6", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(-6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=1.5, y=9)"); - }); - - it("interpolate test7", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, -8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=7.5, y=1)"); - }); - - it("interpolate test8", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(-6, -8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=1.5, y=1)"); - }); - - it("interpolate test9", () => - { - let p1 = new Point(-9, 10); - let p2 = new Point(-6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=-7.5, y=9)"); - }); - - it("interpolate test10", () => - { - let p1 = new Point(9, -10); - let p2 = new Point(6, -8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=7.5, y=-9)"); - }); - - it("interpolate test11", () => - { - let p1 = new Point(-9, -10); - let p2 = new Point(-6, -8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=-7.5, y=-9)"); - }); - - it("interpolate test12", () => - { - // @ts-ignore - let p1 = new Point("a", 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=3, y=9)"); - }); - - it("interpolate test13", () => - { - // @ts-ignore - let p1 = new Point(9, "a"); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=7.5, y=4)"); - }); - - it("interpolate test14", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, -0.5); - expect(p3.toString()).toBe("(x=4.5, y=7)"); - }); - - it("interpolate test15", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - // @ts-ignore - let p3 = Point.interpolate(p1, p2, "a"); - expect(p3.toString()).toBe("(x=0, y=0)"); - }); - - it("interpolate test16", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(9, 8); - let p3 = Point.interpolate(p1, p2, 0.5); - expect(p3.toString()).toBe("(x=9, y=9)"); - }); - - it("interpolate test17", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 1); - expect(p3.toString()).toBe("(x=9, y=10)"); - }); - - it("interpolate test18", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0); - expect(p3.toString()).toBe("(x=6, y=8)"); - }); - - it("interpolate test19", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 1.5); - expect(p3.toString()).toBe("(x=10.5, y=11)"); - }); - - it("interpolate test20", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 0.2); - expect(p3.toString()).toBe("(x=6.6, y=8.4)"); - }); - - it("interpolate test21", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, -0.2); - expect(p3.toString()).toBe("(x=5.4, y=7.6)"); - }); - - it("interpolate test22", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, 1.2); - expect(p3.toString()).toBe("(x=9.6, y=10.4)"); - }); - - it("interpolate test23", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, -1.2); - expect(p3.toString()).toBe("(x=2.3999999999999995, y=5.6)"); - }); - - it("interpolate test24", () => - { - let p1 = new Point(9, 10); - let p2 = new Point(6, 8); - let p3 = Point.interpolate(p1, p2, -1); - expect(p3.toString()).toBe("(x=3, y=6)"); - }); - - it("interpolate test25", () => - { - let p1 = new Point(6, 8); - let p2 = new Point(9, 10); - let p3 = Point.interpolate(p1, p2, -1); - expect(p3.toString()).toBe("(x=12, y=12)"); - }); - -}); - -describe("Point.js normalize test", () => -{ - - it("normalize test1", () => - { - let p = new Point(6, 8); - p.normalize(2.5); - expect(p.toString()).toBe("(x=1.5, y=2)"); - }); - - it("normalize test2", () => - { - let p = new Point(6, 8); - p.normalize(0); - expect(p.toString()).toBe("(x=0, y=0)"); - }); - - it("normalize test3", () => - { - let p = new Point(6, 8); - p.normalize(-2.5); - expect(p.toString()).toBe("(x=-1.5, y=-2)"); - }); - - it("normalize test4", () => - { - let p = new Point(-6, 8); - p.normalize(2.5); - expect(p.toString()).toBe("(x=-1.5, y=2)"); - }); - - it("normalize test5", () => - { - let p = new Point(6, -8); - p.normalize(2.5); - expect(p.toString()).toBe("(x=1.5, y=-2)"); - }); - - it("normalize test6", () => - { - let p = new Point(-6, -8); - p.normalize(2.5); - expect(p.toString()).toBe("(x=-1.5, y=-2)"); - }); - - it("normalize test7", () => - { - // @ts-ignore - let p = new Point("a", 8); - p.normalize(2.5); - expect(p.toString()).toBe("(x=0, y=2.5)"); - }); - - it("normalize test8", () => - { - // @ts-ignore - let p = new Point(6, "a"); - p.normalize(2.5); - expect(p.toString()).toBe("(x=2.5, y=0)"); - }); - - it("normalize test9", () => - { - let p = new Point(6, 8); - // @ts-ignore - p.normalize("a"); - expect(p.toString()).toBe("(x=0, y=0)"); - }); - - it("normalize test10", () => - { - // @ts-ignore - let p = new Point("a", 8); - // @ts-ignore - p.normalize("a"); - expect(p.toString()).toBe("(x=0, y=0)"); - }); - - it("normalize test11", () => - { - // @ts-ignore - let p = new Point(6, "a"); - // @ts-ignore - p.normalize("a"); - expect(p.toString()).toBe("(x=0, y=0)"); - }); -}); - -describe("Point.js offset test", () => -{ - - it("offset test1", () => - { - let p = new Point(10, 20); - p.offset(30, 40); - expect(p.toString()).toBe("(x=40, y=60)"); - }); - - it("offset test2", () => - { - let p = new Point(10, 20); - p.offset(-30, 40); - expect(p.toString()).toBe("(x=-20, y=60)"); - }); - - it("offset test3", () => - { - let p = new Point(10, 20); - p.offset(30, -40); - expect(p.toString()).toBe("(x=40, y=-20)"); - }); - - it("offset test4", () => - { - let p = new Point(-10, 20); - p.offset(30, 40); - expect(p.toString()).toBe("(x=20, y=60)"); - }); - - it("offset test5", () => - { - let p = new Point(10, -20); - p.offset(30, 40); - expect(p.toString()).toBe("(x=40, y=20)"); - }); - - it("offset test6", () => - { - // @ts-ignore - let p = new Point("a", -20); - p.offset(30, 40); - expect(p.toString()).toBe("(x=30, y=20)"); - }); - - it("offset test7", () => - { - let p = new Point(10, -20); - // @ts-ignore - p.offset("a", 40); - expect(p.toString()).toBe("(x=0, y=20)"); - }); - - it("offset test8", () => - { - // @ts-ignore - let p = new Point(10, "a"); - // @ts-ignore - p.offset("a", 40); - expect(p.toString()).toBe("(x=0, y=40)"); - }); -}); - -describe("Point.js polar test", () => -{ - - it("polar test1", () => - { - let angle = Math.PI * 2 * (30 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=3.464101615137755, y=1.9999999999999998)" - ); - }); - - it("polar test2", () => - { - let angle = Math.PI * 2 * (45 / 360); // 30 degrees - let p = Point.polar(4, angle); - - expect(p.x | 0).toBe(2); - expect(p.y | 0).toBe(2); - - }); - - it("polar test3", () => - { - let angle = Math.PI * 2 * (90 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=2.4492935982947064e-16, y=4)" - ); - }); - - it("polar test4", () => - { - let angle = Math.PI * 2 * (135 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-2.82842712474619, y=2.8284271247461903)" - ); - }); - - it("polar test5", () => - { - let angle = Math.PI * 2 * (180 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-4, y=4.898587196589413e-16)" - ); - }); - - it("polar test6", () => - { - let angle = Math.PI * 2 * (225 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-2.8284271247461907, y=-2.82842712474619)" - ); - }); - - it("polar test7", () => - { - let angle = Math.PI * 2 * (270 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-7.347880794884119e-16, y=-4)" - ); - }); - - it("polar test8", () => - { - let angle = Math.PI * 2 * (315 / 360); // 30 degrees - let p = Point.polar(4, angle); - if (p.x > 2.8284271247461894) { - expect(p.x).toBe(2.82842712474619); - } else { - expect(p.x).toBe(2.8284271247461894); - } - expect(p.y).toBe(-2.8284271247461907); - }); - - it("polar test9", () => - { - let angle = Math.PI * 2 * (360 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=4, y=-9.797174393178826e-16)" - ); - }); - - it("polar test10", () => - { - let angle = Math.PI * 2 * (-30 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=3.464101615137755, y=-1.9999999999999998)" - ); - }); - - it("polar test11", () => - { - let angle = Math.PI * 2 * (-45 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.x | 0).toBe(2); - expect(p.y | 0).toBe(-2); - }); - - it("polar test12", () => - { - let angle = Math.PI * 2 * (-90 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=2.4492935982947064e-16, y=-4)" - ); - }); - - it("polar test13", () => - { - let angle = Math.PI * 2 * (-135 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-2.82842712474619, y=-2.8284271247461903)" - ); - }); - - it("polar test14", () => - { - let angle = Math.PI * 2 * (-180 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-4, y=-4.898587196589413e-16)" - ); - }); - - it("polar test15", () => - { - let angle = Math.PI * 2 * (-225 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-2.8284271247461907, y=2.82842712474619)" - ); - }); - - it("polar test16", () => - { - let angle = Math.PI * 2 * (-270 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=-7.347880794884119e-16, y=4)" - ); - }); - - it("polar test17", () => - { - let angle = Math.PI * 2 * (-315 / 360); // 30 degrees - let p = Point.polar(4, angle); - if (p.x > 2.8284271247461894) { - expect(p.x).toBe(2.82842712474619); - } else { - expect(p.x).toBe(2.8284271247461894); - } - expect(p.y).toBe(2.8284271247461907); - }); - - it("polar test18", () => - { - let angle = Math.PI * 2 * (-360 / 360); // 30 degrees - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=4, y=9.797174393178826e-16)" - ); - }); - - it("polar test19", () => - { - let angle = Math.PI * 2 * (30 / 360); // 30 degrees - let p = Point.polar(0, angle); - expect(p.toString()).toBe( - "(x=0, y=0)" - ); - }); - - it("polar test20", () => - { - let angle = Math.PI * 2 * (30 / 360); // 30 degrees - let p = Point.polar(-4, angle); - expect(p.toString()).toBe( - "(x=-3.464101615137755, y=-1.9999999999999998)" - ); - }); - - it("polar test21", () => - { - let angle = "a"; // 30 degrees - // @ts-ignore - let p = Point.polar(4, angle); - expect(p.toString()).toBe( - "(x=0, y=0)" - ); - }); - - it("polar test22", () => - { - let angle = Math.PI * 2 * (30 / 360); // 30 degrees - // @ts-ignore - let p = Point.polar("a", angle); - expect(p.toString()).toBe( - "(x=0, y=0)" - ); - }); - - it("polar test23", () => - { - let angle = "a"; // 30 degrees - // @ts-ignore - let p = Point.polar("a", angle); - expect(p.toString()).toBe( - "(x=0, y=0)" - ); - }); -}); - -describe("Point.js setTo test", () => -{ - - it("setTo test1", () => - { - let p = new Point(10, 20); - p.setTo(30, 40); - expect(p.toString()).toBe("(x=30, y=40)"); - }); - - it("setTo test2", () => - { - // @ts-ignore - let p = new Point("a", 20); - p.setTo(30, 40); - expect(p.toString()).toBe("(x=30, y=40)"); - }); - - it("setTo test3", () => - { - let p = new Point(10, 20); - // @ts-ignore - p.setTo("a", 40); - expect(p.toString()).toBe("(x=0, y=40)"); - }); - - it("setTo test4", () => - { - let p = new Point(10, 20); - p.setTo(0, 40); - expect(p.toString()).toBe("(x=0, y=40)"); - }); - -}); - -describe("Point.js subtract test", () => -{ - - it("subtract test1", () => - { - let p1 = new Point(6, 8); - let p2 = new Point(1.5, 2); - let p3 = p1.subtract(p2); - expect(p3.toString()).toBe("(x=4.5, y=6)"); - }); - - it("subtract test2", () => - { - let p1 = new Point(6, 8); - let p2 = new Point(-1, 2); - let p3 = p1.subtract(p2); - expect(p3.toString()).toBe("(x=7, y=6)"); - }); - - it("subtract test3", () => - { - let p1 = new Point(6, 8); - // @ts-ignore - let p2 = new Point("a", 2); - let p3 = p1.subtract(p2); - expect(p3.toString()).toBe("(x=6, y=6)"); - }); - - it("subtract test4", () => - { - let p1 = new Point(6, 8); - // @ts-ignore - let p2 = new Point(1, "a"); - let p3 = p1.subtract(p2); - expect(p3.toString()).toBe("(x=5, y=8)"); - }); - - it("subtract test5", () => - { - let p1 = new Point(6, 8); - // @ts-ignore - let p2 = new Point("a", "a"); - let p3 = p1.subtract(p2); - expect(p3.toString()).toBe("(x=6, y=8)"); - }); -}); - -//properties -describe("Point.js x test", () => -{ - - it("default test case1", () => - { - let p = new Point(); - expect(p.x).toBe(0); - }); - - it("default test case2", () => - { - let p = new Point(); - // @ts-ignore - p.x = null; - expect(p.x).toBe(0); - }); - - it("default test case3", () => - { - let p = new Point(); - // @ts-ignore - p.x = undefined; - expect(p.x).toBe(0); - }); - - it("default test case4", () => - { - let p = new Point(); - // @ts-ignore - p.x = true; - expect(p.x).toBe(1); - }); - - it("default test case5", () => - { - let p = new Point(); - // @ts-ignore - p.x = ""; - expect(p.x).toBe(0); - }); - - it("default test case6", () => - { - let p = new Point(); - // @ts-ignore - p.x = "abc"; - expect(p.x).toBe(0); - }); - - it("default test case7", () => - { - let p = new Point(); - p.x = 0; - expect(p.x).toBe(0); - }); - - it("default test case8", () => - { - let p = new Point(); - p.x = 1; - expect(p.x).toBe(1); - }); - - it("default test case9", () => - { - let p = new Point(); - p.x = 500; - expect(p.x).toBe(500); - }); - - it("default test case10", () => - { - let p = new Point(); - p.x = 50000000000000000; - expect(p.x).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let p = new Point(); - p.x = -1; - expect(p.x).toBe(-1); - }); - - it("default test case12", () => - { - let p = new Point(); - p.x = -500; - expect(p.x).toBe(-500); - }); - - it("default test case13", () => - { - let p = new Point(); - p.x = -50000000000000000; - expect(p.x).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let p = new Point(); - // @ts-ignore - p.x = { "a":0 }; - expect(p.x).toBe(0); - }); - - it("default test case15", () => - { - let p = new Point(); - // @ts-ignore - p.x = function a() {}; - expect(p.x).toBe(0); - }); - - it("default test case16", () => - { - let p = new Point(); - // @ts-ignore - p.x = [1]; - expect(p.x).toBe(1); - }); - - it("default test case17", () => - { - let p = new Point(); - // @ts-ignore - p.x = [1,2]; - expect(p.x).toBe(0); - }); - - it("default test case18", () => - { - let p = new Point(); - // @ts-ignore - p.x = {}; - expect(p.x).toBe(0); - }); - - it("default test case19", () => - { - let p = new Point(); - // @ts-ignore - p.x = { "toString":function () { return 1 } }; - expect(p.x).toBe(1); - }); - - it("default test case20", () => - { - let p = new Point(); - // @ts-ignore - p.x = { "toString":function () { return "1" } }; - expect(p.x).toBe(1); - }); - - it("default test case21", () => - { - let p = new Point(); - // @ts-ignore - p.x = { "toString":function () { return "1a" } }; - expect(p.x).toBe(0); - }); - -}); - -describe("Point.js y test", () => -{ - - it("default test case1", () => - { - let p = new Point(); - expect(p.y).toBe(0); - }); - - it("default test case2", () => - { - let p = new Point(); - // @ts-ignore - p.y = null; - expect(p.y).toBe(0); - }); - - it("default test case3", () => - { - let p = new Point(); - // @ts-ignore - p.y = undefined; - expect(p.y).toBe(0); - }); - - it("default test case4", () => - { - let p = new Point(); - // @ts-ignore - p.y = true; - expect(p.y).toBe(1); - }); - - it("default test case5", () => - { - let p = new Point(); - // @ts-ignore - p.y = ""; - expect(p.y).toBe(0); - }); - - it("default test case6", () => - { - let p = new Point(); - // @ts-ignore - p.y = "abc"; - expect(p.y).toBe(0); - }); - - it("default test case7", () => - { - let p = new Point(); - p.y = 0; - expect(p.y).toBe(0); - }); - - it("default test case8", () => - { - let p = new Point(); - p.y = 1; - expect(p.y).toBe(1); - }); - - it("default test case9", () => - { - let p = new Point(); - p.y = 500; - expect(p.y).toBe(500); - }); - - it("default test case10", () => - { - let p = new Point(); - p.y = 50000000000000000; - expect(p.y).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let p = new Point(); - p.y = -1; - expect(p.y).toBe(-1); - }); - - it("default test case12", () => - { - let p = new Point(); - p.y = -500; - expect(p.y).toBe(-500); - }); - - it("default test case13", () => - { - let p = new Point(); - p.y = -50000000000000000; - expect(p.y).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let p = new Point(); - // @ts-ignore - p.y = { "a":0 }; - expect(p.y).toBe(0); - }); - - it("default test case15", () => - { - let p = new Point(); - // @ts-ignore - p.y = function a() {}; - expect(p.y).toBe(0); - }); - - it("default test case16", () => - { - let p = new Point(); - // @ts-ignore - p.y = [1]; - expect(p.y).toBe(1); - }); - - it("default test case17", () => - { - let p = new Point(); - // @ts-ignore - p.y = [1,2]; - expect(p.y).toBe(0); - }); - - it("default test case18", () => - { - let p = new Point(); - // @ts-ignore - p.y = {}; - expect(p.y).toBe(0); - }); - - it("default test case19", () => - { - let p = new Point(); - // @ts-ignore - p.y = { "toString":function () { return 1 } }; - expect(p.y).toBe(1); - }); - - it("default test case20", () => - { - let p = new Point(); - // @ts-ignore - p.y = { "toString":function () { return "1" } }; - expect(p.y).toBe(1); - }); - - it("default test case21", () => - { - let p = new Point(); - // @ts-ignore - p.y = { "toString":function () { return "1a" } }; - expect(p.y).toBe(0); - }); - -}); - -describe("Point.js length test", () => -{ - - it("default test case1", () => - { - let p = new Point(); - expect(p.length).toBe(0); - }); - - it("default test case2", () => - { - let p = new Point(10, 30); - expect(p.length).toBe(31.622776601683793); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/geom/RectangleTest.ts b/__tests__/next2d/geom/RectangleTest.ts deleted file mode 100644 index ee886867..00000000 --- a/__tests__/next2d/geom/RectangleTest.ts +++ /dev/null @@ -1,2530 +0,0 @@ -import { Rectangle } from "../../../packages/geom/src/Rectangle"; -import { Point } from "../../../packages/geom/src/Point"; -import {$SHORT_INT_MAX, $SHORT_INT_MIN} from "../../../packages/share/src/RenderUtil"; - -describe("Rectangle.js toString test", () => -{ - it("toString test1 success", () => - { - const object = new Rectangle(); - expect(object.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); - - it("toString test2 success", () => - { - const object = new Rectangle(1, 2, 3, 4); - expect(object.toString()).toBe("(x=1, y=2, w=3, h=4)"); - }); - -}); - -describe("Rectangle.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Rectangle.toString()).toBe("[class Rectangle]"); - }); - -}); - -describe("Rectangle.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Rectangle(); - expect(object.namespace).toBe("next2d.geom.Rectangle"); - }); - - it("namespace test static", () => - { - expect(Rectangle.namespace).toBe("next2d.geom.Rectangle"); - }); - -}); - -describe("Rectangle.js property test", () => -{ - it("top test1", () => - { - - let r = new Rectangle(50, 50, 100, 100); - expect(r.top).toBe(50); - expect(r.bottom).toBe(150); - - // success - r.top = 160; - expect(r.toString()).toBe("(x=50, y=160, w=100, h=-10)"); - expect(r.bottom).toBe(150); - expect(r.y).toBe(160); - - // @ts-ignore - r.top = "a"; - expect(r.y).toBe(0); - expect(r.height).toBe(0); - }); - - it("top test2", () => - { - - let r = new Rectangle(-50, -50, -100, -100); - expect(r.top).toBe(-50); - expect(r.bottom).toBe(-150); - - // success - r.top = 160; - expect(r.toString()).toBe("(x=-50, y=160, w=-100, h=-310)"); - expect(r.bottom).toBe(-150); - expect(r.y).toBe(160); - - // @ts-ignore - r.top = "a"; - expect(r.y).toBe(0); - expect(r.height).toBe(0); - }); - - it("right test1", () => - { - - let r = new Rectangle(50, 100, 100, 100); - expect(r.right).toBe(150); - - // success - r.right = 20; - expect(r.toString()).toBe("(x=50, y=100, w=-30, h=100)"); - - // @ts-ignore - r.right = "a"; - expect(r.right).toBe(50); - }); - - it("right test2", () => - { - - let r = new Rectangle(50, -100, -100, -100); - expect(r.right).toBe(-50); - - // success - - r.right = 20; - expect(r.toString()).toBe("(x=50, y=-100, w=-30, h=-100)"); - - // @ts-ignore - r.right = "a"; - expect(r.right).toBe(50); - }); - - it("bottom test1", () => - { - - let r = new Rectangle(0, 100, 100, 100); - expect(r.bottom).toBe(200); - - // success - r.bottom = 50; - expect(r.toString()).toBe("(x=0, y=100, w=100, h=-50)"); - - // @ts-ignore - r.bottom = "a"; - expect(r.height).toBe(0); - }); - - it("bottom test2", () => - { - - let r = new Rectangle(0, -100, -100, -100); - expect(r.bottom).toBe(-200); - - // success - r.bottom = -50; - expect(r.toString()).toBe("(x=0, y=-100, w=-100, h=50)"); - - // @ts-ignore - r.bottom = "a"; - expect(r.height).toBe(0); - }); - - it("left test1", () => - { - - let r = new Rectangle(50, 50, 100, 100); - expect(r.left).toBe(50); - expect(r.right).toBe(150); - - // success - r.left = 160; - expect(r.toString()).toBe("(x=160, y=50, w=-10, h=100)"); - expect(r.right).toBe(150); - expect(r.x).toBe(160); - - // @ts-ignore - r.left = "a"; - expect(r.x).toBe(0); - expect(r.width).toBe(0); - }); - - it("left test2", () => - { - - let r = new Rectangle(-50, -50, -100, -100); - expect(r.left).toBe(-50); - expect(r.right).toBe(-150); - - // success - r.left = 160; - expect(r.toString()).toBe("(x=160, y=-50, w=-310, h=-100)"); - expect(r.right).toBe(-150); - expect(r.x).toBe(160); - - // @ts-ignore - r.left = "a"; - expect(r.x).toBe(0); - expect(r.width).toBe(0); - }); - - it("bottomRight test1", () => - { - - let r = new Rectangle(30, 50, 80, 100); - let p = r.bottomRight; - expect(p.toString()).toBe("(x=110, y=150)"); - - r.bottomRight = new Point(10 ,10); - expect(r.toString()).toBe("(x=30, y=50, w=-20, h=-40)"); - }); - - it("bottomRight test2", () => - { - - let r = new Rectangle(-30, -50, -80, -100); - let p = r.bottomRight; - expect(p.toString()).toBe("(x=-110, y=-150)"); - - r.bottomRight = new Point(10 ,10); - expect(r.toString()).toBe("(x=-30, y=-50, w=40, h=60)"); - }); - - it("topLeft test1", () => - { - - let r = new Rectangle(30, 50, 80, 100); - let p = r.topLeft; - expect(p.toString()).toBe("(x=30, y=50)"); - - r.topLeft = new Point(10 ,10); - expect(r.toString()).toBe("(x=10, y=10, w=100, h=140)"); - }); - - it("topLeft test2", () => - { - - let r = new Rectangle(-30, -50, -80, -100); - let p = r.topLeft; - expect(p.toString()).toBe("(x=-30, y=-50)"); - - r.topLeft = new Point(10 ,10); - expect(r.toString()).toBe("(x=10, y=10, w=-120, h=-160)"); - }); - - it("size test1", () => - { - - let r = new Rectangle(30, 50, 80, 100); - let p = r.size; - expect(p.toString()).toBe("(x=80, y=100)"); - - r.size = new Point(10 ,10); - expect(r.toString()).toBe("(x=30, y=50, w=10, h=10)"); - }); - - it("size test2", () => - { - - let r = new Rectangle(-30, -50, -80, -100); - let p = r.size; - expect(p.toString()).toBe("(x=-80, y=-100)"); - - r.size = new Point(10 ,10); - expect(r.toString()).toBe("(x=-30, y=-50, w=10, h=10)"); - }); -}); - -describe("Rectangle.js clone test", () => -{ - it("clone test", () => - { - let r1 = new Rectangle(30, 50, 80, 100); - let r2 = r1.clone(); - r2.x = 100; - - expect(r1.toString()).toBe("(x=30, y=50, w=80, h=100)"); - expect(r2.toString()).toBe("(x=100, y=50, w=80, h=100)"); - }); -}); - -describe("Rectangle.js contains test", () => -{ - - it("contains test1", () => - { - let r = new Rectangle(30, 50, 80, 100); - expect(r.contains(30, 50)).toBe(true); - expect(r.contains(110, 150)).toBe(false); - expect(r.contains(109, 149)).toBe(true); - expect(r.contains(20, 40)).toBe(false); - }); - - it("contains test2", () => - { - let r = new Rectangle(0, 0, 0, 0); - expect(r.contains(0, 0)).toBe(false); - // @ts-ignore - expect(r.contains("a", 0)).toBe(false); - // @ts-ignore - expect(r.contains(0, "a")).toBe(false); - }); - - it("contains test3", () => - { - let r = new Rectangle(0, 0, 1, 1); - expect(r.contains(0, 0)).toBe(true); - expect(r.contains(0.000001, 0.000001)).toBe(true); - expect(r.contains(0.999999, 0.999999)).toBe(true); - expect(r.contains(1, 0)).toBe(false); - expect(r.contains(0, 1)).toBe(false); - expect(r.contains(1, 1)).toBe(false); - }); - - it("contains test4", () => - { - let r = new Rectangle(-1, -1, 1, 1); - expect(r.contains(0, 0)).toBe(false); - expect(r.contains(-1, -1)).toBe(true); - expect(r.contains(-1, -0.5)).toBe(true); - expect(r.contains(-0.5, -1)).toBe(true); - }); - -}); - -describe("Rectangle.js containsPoint test", () => -{ - it("containsPoint test1", () => - { - let r = new Rectangle(30, 50, 80, 100); - - let p1 = new Point(30, 50); - expect(r.containsPoint(p1)).toBe(true); - - let p2 = new Point(110, 150); - expect(r.containsPoint(p2)).toBe(false); - - let p3 = new Point(109, 149); - expect(r.containsPoint(p3)).toBe(true); - - let p4 = new Point(20, 40); - expect(r.containsPoint(p4)).toBe(false); - }); - - it("containsPoint test2", () => - { - let r = new Rectangle(-30, -50, -80, -100); - - let p1 = new Point(-30, -50); - expect(r.containsPoint(p1)).toBe(false); - - let p2 = new Point(-110, -150); - expect(r.containsPoint(p2)).toBe(false); - - let p3 = new Point(-109, -149); - expect(r.containsPoint(p3)).toBe(false); - - let p5 = new Point(110, 150); - expect(r.containsPoint(p5)).toBe(false); - - let p6 = new Point(109, 149); - expect(r.containsPoint(p6)).toBe(false); - - let p4 = new Point(-20, -40); - expect(r.containsPoint(p4)).toBe(false); - }); -}); - -describe("Rectangle.js containsRect test", () => -{ - it("containsRect test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(15, 15, 5, 5); - expect(r1.containsRect(r2)).toBe(true); - - let r3 = new Rectangle(10, 10, 20, 20); - let r4 = new Rectangle(10, 10, 20, 20); - expect(r3.containsRect(r4)).toBe(true); - - let r5 = new Rectangle(10, 10, 20, 20); - let r6 = new Rectangle(9, 9, 20, 20); - expect(r5.containsRect(r6)).toBe(false); - - let r7 = new Rectangle(10, 10, 20, 20); - let r8 = new Rectangle(15, 15, 20, 20); - expect(r7.containsRect(r8)).toBe(false); - }); - - it("containsRect test2", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - let r2 = new Rectangle(-15, -15, -5, -5); - expect(r1.containsRect(r2)).toBe(false); - - let r3 = new Rectangle(-10, -10, 20, 20); - let r4 = new Rectangle(-15, -15, 5, 5); - expect(r3.containsRect(r4)).toBe(false); - }); -}); - -describe("Rectangle.js copyFrom test", () => -{ - it("copyFrom test", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(15, 15, 5, 5); - - r1.copyFrom(r2); - expect(r1.toString()).toBe("(x=15, y=15, w=5, h=5)"); - expect(r2.toString()).toBe("(x=15, y=15, w=5, h=5)"); - - r1.x = 10; - r1.y = 10; - r1.width = 20; - r1.height = 20; - expect(r1.toString()).toBe("(x=10, y=10, w=20, h=20)"); - expect(r2.toString()).toBe("(x=15, y=15, w=5, h=5)"); - }); -}); - -describe("Rectangle.js equals test", () => -{ - it("equals test", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(10, 10, 20, 20); - expect(r1.equals(r2)).toBe(true); - - let r3 = new Rectangle(10, 10, 20, 20); - let r4 = new Rectangle(15, 15, 5, 5); - expect(r3.equals(r4)).toBe(false); - }); -}); - -describe("Rectangle.js inflate test", () => -{ - it("inflate test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - r1.inflate(10, 10); - expect(r1.toString()).toBe("(x=0, y=0, w=40, h=40)"); - - let r2 = new Rectangle(10, 10, 20, 20); - r2.inflate(20, 20); - expect(r2.toString()).toBe("(x=-10, y=-10, w=60, h=60)"); - }); - - it("inflate test2", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - r1.inflate(10, 10); - expect(r1.toString()).toBe("(x=-20, y=-20, w=0, h=0)"); - - let r2 = new Rectangle(10, 10, 20, 20); - r2.inflate(-20, -20); - expect(r2.toString()).toBe("(x=30, y=30, w=-20, h=-20)"); - }); - - it("inflate test3", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - // @ts-ignore - r1.inflate("a", 10); - expect(r1.toString()).toBe("(x=0, y=-20, w=0, h=0)"); - - // @ts-ignore - let r2 = new Rectangle("a", -10, -20, -20); - r2.inflate(20, 20); - expect(r2.toString()).toBe("(x=-20, y=-30, w=20, h=20)"); - - // @ts-ignore - let r3 = new Rectangle(-10, "a", -20, -20); - r3.inflate(20, 20); - expect(r3.toString()).toBe("(x=-30, y=-20, w=20, h=20)"); - - // @ts-ignore - let r4 = new Rectangle(-10, -10, "a", -20); - r4.inflate(20, 20); - expect(r4.toString()).toBe("(x=-30, y=-30, w=40, h=20)"); - }); -}); - -describe("Rectangle.js inflatePoint test", () => -{ - it("inflatePoint test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let p1 = new Point(10, 10); - r1.inflatePoint(p1); - expect(r1.toString()).toBe("(x=0, y=0, w=40, h=40)"); - - let r2 = new Rectangle(10, 10, 20, 20); - let p2 = new Point(20, 20); - r2.inflatePoint(p2); - expect(r2.toString()).toBe("(x=-10, y=-10, w=60, h=60)"); - }); - - it("inflatePoint test2", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - let p1 = new Point(10, 10); - r1.inflatePoint(p1); - expect(r1.toString()).toBe("(x=-20, y=-20, w=0, h=0)"); - - let r2 = new Rectangle(-10, -10, 20, 20); - let p2 = new Point(20, 20); - r2.inflatePoint(p2); - expect(r2.toString()).toBe("(x=-30, y=-30, w=60, h=60)"); - }); - - it("inflatePoint test3", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - // @ts-ignore - let p1 = new Point("a", 10); - r1.inflatePoint(p1); - expect(r1.toString()).toBe("(x=-10, y=-20, w=-20, h=0)"); - - // @ts-ignore - let r2 = new Rectangle("a", -10, -20, -20); - let p2 = new Point(20, 20); - r2.inflatePoint(p2); - expect(r2.toString()).toBe("(x=-20, y=-30, w=20, h=20)"); - - // @ts-ignore - let r3 = new Rectangle(-10, "a", -20, -20); - let p3 = new Point(20, 20); - r3.inflatePoint(p3); - expect(r3.toString()).toBe("(x=-30, y=-20, w=20, h=20)"); - - // @ts-ignore - let r4 = new Rectangle(-10, -10, "a", -20); - let p4 = new Point(20, 20); - r4.inflatePoint(p4); - expect(r4.toString()).toBe("(x=-30, y=-30, w=40, h=20)"); - }); -}); - -describe("Rectangle.js intersection test", () => -{ - it("intersection test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(5, 5, 5, 5); - let r3 = r1.intersection(r2); - expect(r3.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r4 = new Rectangle(10, 10, 20, 20); - let r5 = new Rectangle(15, 15, 5, 5); - let r6 = r4.intersection(r5); - expect(r6.toString()).toBe("(x=15, y=15, w=5, h=5)"); - - let r7 = new Rectangle(10, 10, 20, 20); - let r8 = new Rectangle(5, 5, 25, 25); - let r9 = r7.intersection(r8); - expect(r9.toString()).toBe("(x=10, y=10, w=20, h=20)"); - }); - - it("intersection test2", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - let r2 = new Rectangle(-5, -5, -5, -5); - let r3 = r1.intersection(r2); - expect(r3.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r4 = new Rectangle(-10, -10, -20, -20); - let r5 = new Rectangle(-15, -15, -5, -5); - let r6 = r4.intersection(r5); - expect(r6.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r7 = new Rectangle(-10, -10, -20, -20); - let r8 = new Rectangle(-5, -5, -25, -25); - let r9 = r7.intersection(r8); - expect(r9.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); - - it("intersection test3", () => - { - let r1 = new Rectangle(-10, -10, 20, 20); - let r2 = new Rectangle(-5, -5, 5, 5); - let r3 = r1.intersection(r2); - expect(r3.toString()).toBe("(x=-5, y=-5, w=5, h=5)"); - - let r4 = new Rectangle(-10, -10, 20, 20); - let r5 = new Rectangle(-15, -15, 5, 5); - let r6 = r4.intersection(r5); - expect(r6.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r7 = new Rectangle(-10, -10, 20, 20); - let r8 = new Rectangle(-5, -5, 25, 25); - let r9 = r7.intersection(r8); - expect(r9.toString()).toBe("(x=-5, y=-5, w=15, h=15)"); - }); - - it("intersection test4", () => - { - let r1 = new Rectangle(-10, -10, 20, 20); - let r2 = new Rectangle(-10, -10, 0, 10); - let r3 = r1.intersection(r2); - expect(r3.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r4 = new Rectangle(-10, -10, 20, 20); - let r5 = new Rectangle(-10, -10, 5, 5); - let r6 = r4.intersection(r5); - expect(r6.toString()).toBe("(x=-10, y=-10, w=5, h=5)"); - - let r7 = new Rectangle(-5, -5, -25, -25); - let r8 = new Rectangle(-10, -10, -20, -20); - let r9 = r7.intersection(r8); - expect(r9.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); - - it("intersection test5", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(15, 15, 5, 5); - let r3 = r1.intersection(r2); - expect(r3.toString()).toBe("(x=15, y=15, w=5, h=5)"); - - // @ts-ignore - let r4 = new Rectangle(10, 10, "a", 20); - let r5 = new Rectangle(15, 15, 5, 5); - let r6 = r4.intersection(r5); - expect(r6.toString()).toBe("(x=0, y=0, w=0, h=0)"); - - let r7 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r8 = new Rectangle(5, "a", 25, 25); - let r9 = r7.intersection(r8); - expect(r9.toString()).toBe("(x=10, y=10, w=20, h=15)"); - - let r10 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r11 = new Rectangle(5, 5, 25, "a"); - let r12 = r10.intersection(r11); - expect(r12.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); -}); - -describe("Rectangle.js intersects test", () => -{ - it("intersects test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(5, 5, 5, 5); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - let r4 = new Rectangle(5, 5, 25, 25); - expect(r3.intersects(r4)).toBe(true); - }); - - it("intersects test2", () => - { - let r1 = new Rectangle(-10, -10, -20, -20); - let r2 = new Rectangle(-5, -5, -25, -25); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(-10, -10, 20, 20); - let r4 = new Rectangle(-5, -5, 25, 25); - expect(r3.intersects(r4)).toBe(true); - }); - - it("intersects test3", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(5, 5, 25, 25); - expect(r1.intersects(r2)).toBe(true); - - // @ts-ignore - let r3 = new Rectangle(10, 10, "a", 20); - let r4 = new Rectangle(5, 5, 25, 25); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test4", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle(5, "a", 25, 25); - - expect(r1.intersects(r2)).toBe(true); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, 5, 25, "a"); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test5", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle(5, "a", 5, 5); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, 5, 5, "a"); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test6", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle(5, "a", -5, 5); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, 5, 100, "a"); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test7", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(5, 40, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - let r4 = new Rectangle(5, 15, 10, 10); - expect(r3.intersects(r4)).toBe(true); - }); - - it("intersects test8", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(5, 40, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - // @ts-ignore - let r3 = new Rectangle(10, "a", 20, 20); - let r4 = new Rectangle(5, 40, 10, 10); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test9", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle("a", 40, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, "a", 10, 10); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test10", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(5, 15, 10, 10); - expect(r1.intersects(r2)).toBe(true); - - // @ts-ignore - let r3 = new Rectangle(10, "a", 20, 20); - let r4 = new Rectangle(5, 15, 10, 10); - expect(r3.intersects(r4)).toBe(true); - }); - - it("intersects test11", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle("a", 15, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, "a", 10, 10); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test12", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - let r2 = new Rectangle(5, 40, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - // @ts-ignore - let r3 = new Rectangle(10, 10, 20, "a"); - let r4 = new Rectangle(5, 40, 10, 10); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test13", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle(5, 40, "a", 10); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, 40, 10, "a"); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test14", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - let r2 = new Rectangle(5, 15, 10, 10); - expect(r1.intersects(r2)).toBe(false); - - // @ts-ignore - let r3 = new Rectangle(10, 10, 20, "a"); - let r4 = new Rectangle(5, 15, 10, 10); - expect(r3.intersects(r4)).toBe(false); - }); - - it("intersects test15", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r2 = new Rectangle(5, 15, "a", 10); - expect(r1.intersects(r2)).toBe(false); - - let r3 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - let r4 = new Rectangle(5, 15, 10, "a"); - expect(r3.intersects(r4)).toBe(false); - }); - -}); - -describe("Rectangle.js isEmpty test", () => -{ - it("isEmpty test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - expect(r1.isEmpty()).toBe(false); - expect(r2.isEmpty()).toBe(true); - }); - - it("isEmpty test2", () => - { - let r1 = new Rectangle(10, 10, 0, 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test3", () => - { - let r1 = new Rectangle(10, 10, 20, 0); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test4", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 0); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test5", () => - { - // @ts-ignore - let r1 = new Rectangle(10, "a", 0, 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test6", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test7", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, 20, "a"); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test8", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 0, 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test9", () => - { - // @ts-ignore - let r1 = new Rectangle(10, "a", 0, 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test10", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test11", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, 0, "a"); - expect(r1.isEmpty()).toBe(true); - }); - - it("isEmpty test12", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 0); - expect(r1.isEmpty()).toBe(true); - }); -}); - -describe("Rectangle.js offset test", () => -{ - it("offset test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offset(5, 8); - r2.offset(60, 30); - - expect(r1.toString()).toBe("(x=15, y=18, w=20, h=20)"); - expect(r2.toString()).toBe("(x=5, y=-25, w=0, h=0)"); - }); - - it("offsetPoint test2", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offset(5, 8); - // @ts-ignore - r2.offset("a", 30); - - expect(r1.toString()).toBe("(x=5, y=18, w=20, h=20)"); - expect(r2.toString()).toBe("(x=0, y=-25, w=0, h=0)"); - }); - - it("offsetPoint test3", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offset(5, 8); - // @ts-ignore - r2.offset(60, "a"); - - expect(r1.toString()).toBe("(x=15, y=18, w=0, h=20)"); - expect(r2.toString()).toBe("(x=5, y=0, w=0, h=0)"); - }); -}); - -describe("Rectangle.js offsetPoint test", () => -{ - it("offsetPoint test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offsetPoint(new Point(5, 8)); - r2.offsetPoint(new Point(60, 30)); - - expect(r1.toString()).toBe("(x=15, y=18, w=20, h=20)"); - expect(r2.toString()).toBe("(x=5, y=-25, w=0, h=0)"); - }); - - it("offsetPoint test2", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offsetPoint(new Point(5, 8)); - // @ts-ignore - r2.offsetPoint(new Point("a", 30)); - - expect(r1.toString()).toBe("(x=5, y=18, w=20, h=20)"); - expect(r2.toString()).toBe("(x=-55, y=-25, w=0, h=0)"); - }); - - it("offsetPoint test3", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.offsetPoint(new Point(5, 8)); - // @ts-ignore - r2.offsetPoint(new Point(60, "a")); - - expect(r1.toString()).toBe("(x=15, y=18, w=0, h=20)"); - expect(r2.toString()).toBe("(x=5, y=-55, w=0, h=0)"); - }); -}); - -describe("Rectangle.js setEmpty test", () => -{ - it("setEmpty test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.setEmpty(); - r2.setEmpty(); - - expect(r1.toString()).toBe("(x=0, y=0, w=0, h=0)"); - expect(r2.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); - - it("setEmpty test2", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.setEmpty(); - r2.setEmpty(); - - expect(r1.toString()).toBe("(x=0, y=0, w=0, h=0)"); - expect(r2.toString()).toBe("(x=0, y=0, w=0, h=0)"); - }); -}); - -describe("Rectangle.js setTo test", () => -{ - it("setTo test1", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - let r2 = new Rectangle(-55, -55, 0, 0); - - r1.setTo(5, 5, 5, 5); - r2.setTo(10, 10, 10, 10); - - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=5)"); - expect(r2.toString()).toBe("(x=10, y=10, w=10, h=10)"); - }); - - it("setTo test2", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 20, 20); - r1.setTo(5, 5, 5, 5); - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=5)"); - }); - - it("setTo test3", () => - { - // @ts-ignore - let r1 = new Rectangle(10, "a", 20, 20); - r1.setTo(5, 5, 5, 5); - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=5)"); - }); - - it("setTo test4", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 20); - r1.setTo(5, 5, 5, 5); - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=5)"); - }); - - it("setTo test5", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, 20, "a"); - r1.setTo(5, 5, 5, 5); - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=5)"); - }); - - it("setTo test6", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - r1.setTo("a", 5, 5, 5); - expect(r1.toString()).toBe("(x=0, y=5, w=5, h=5)"); - }); - - it("setTo test7", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - r1.setTo(5, "a", 5, 5); - expect(r1.toString()).toBe("(x=5, y=0, w=5, h=5)"); - }); - - it("setTo test8", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - r1.setTo(5, 5, "a", 5); - expect(r1.toString()).toBe("(x=5, y=5, w=0, h=5)"); - }); - - it("setTo test9", () => - { - let r1 = new Rectangle(10, 10, 20, 20); - // @ts-ignore - r1.setTo(5, 5, 5, "a"); - expect(r1.toString()).toBe("(x=5, y=5, w=5, h=0)"); - }); -}); - -describe("Rectangle.js union test", () => -{ - it("union test1", () => - { - let r1 = new Rectangle(10, 10, 0, 10); - let r2 = new Rectangle(-55, -25, 0, 20); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=-55, y=-25, w=0, h=20)"); - - let r4 = new Rectangle(10, 10, 10, 10); - let r5 = new Rectangle(-55, -25, 0, 20); - let r6 = r4.union(r5); - expect(r6.toString()).toBe("(x=10, y=10, w=10, h=10)"); - - let r7 = new Rectangle(10, 10, 10, 10); - let r8 = new Rectangle(-55, -25, 20, 20); - let r9 = r7.union(r8); - expect(r9.toString()).toBe("(x=-55, y=-25, w=75, h=45)"); - }); - - it("union test2", () => - { - let r1 = new Rectangle(10, 10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=10, y=10, w=20, h=20)"); - }); - - it("union test3", () => - { - let r1 = new Rectangle(-10, 10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=-10, y=10, w=40, h=20)"); - }); - - it("union test4", () => - { - let r1 = new Rectangle(10, -10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=10, y=-10, w=20, h=40)"); - }); - - it("union test5", () => - { - let r1 = new Rectangle(-10, -10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=-10, y=-10, w=40, h=40)"); - }); - - it("union test6", () => - { - let r1 = new Rectangle(10, 10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r2.union(r1); - expect(r3.toString()).toBe("(x=10, y=10, w=20, h=20)"); - }); - - it("union test7", () => - { - let r1 = new Rectangle(-10, 10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r2.union(r1); - expect(r3.toString()).toBe("(x=-10, y=10, w=40, h=20)"); - }); - - it("union test8", () => - { - let r1 = new Rectangle(10, -10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r2.union(r1); - expect(r3.toString()).toBe("(x=10, y=-10, w=20, h=40)"); - }); - - it("union test9", () => - { - let r1 = new Rectangle(-10, -10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r2.union(r1); - expect(r3.toString()).toBe("(x=-10, y=-10, w=40, h=40)"); - }); - - it("union test2", () => - { - // @ts-ignore - let r1 = new Rectangle("a", 10, 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=0, y=10, w=30, h=20)"); - }); - - it("union test2", () => - { - // @ts-ignore - let r1 = new Rectangle(10, "a", 10, 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=10, y=0, w=20, h=30)"); - }); - - it("union test2", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, "a", 10); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=20, y=20, w=10, h=10)"); - }); - - it("union test2", () => - { - // @ts-ignore - let r1 = new Rectangle(10, 10, 10, "a"); - let r2 = new Rectangle(20, 20, 10, 10); - let r3 = r1.union(r2); - expect(r3.toString()).toBe("(x=20, y=20, w=10, h=10)"); - }); -}); - -describe("Rectangle.js bottom test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.bottom).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = null; - expect(r.bottom).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = undefined; - expect(r.bottom).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = true; - expect(r.bottom).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = ""; - expect(r.bottom).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = "abc"; - expect(r.bottom).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.bottom = 0; - expect(r.bottom).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.bottom = 1; - expect(r.bottom).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.bottom = 500; - expect(r.bottom).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.bottom = 50000000000000000; - expect(r.bottom).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.bottom = -1; - expect(r.bottom).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.bottom = -500; - expect(r.bottom).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.bottom = -50000000000000000; - expect(r.bottom).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = { "a":0 }; - expect(r.bottom).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = function a() {}; - expect(r.bottom).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = [1]; - expect(r.bottom).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = [1,2]; - expect(r.bottom).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = {}; - expect(r.bottom).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = { "toString":() => { return 1 } }; - expect(r.bottom).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = { "toString":() => { return "1" } }; - expect(r.bottom).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.bottom = { "toString":() => { return "1a" } }; - expect(r.bottom).toBe(0); - }); - -}); - -describe("Rectangle.js height test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.height).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = null; - expect(r.height).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = undefined; - expect(r.height).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = true; - expect(r.height).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = ""; - expect(r.height).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = "abc"; - expect(r.height).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.height = 0; - expect(r.height).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.height = 1; - expect(r.height).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.height = 500; - expect(r.height).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.height = 50000000000000000; - expect(r.height).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.height = -1; - expect(r.height).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.height = -500; - expect(r.height).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.height = -50000000000000000; - expect(r.height).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = { "a":0 }; - expect(r.height).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = function a() {}; - expect(r.height).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = [1]; - expect(r.height).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = [1,2]; - expect(r.height).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = {}; - expect(r.height).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = { "toString":() => { return 1 } }; - expect(r.height).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = { "toString":() => { return "1" } }; - expect(r.height).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.height = { "toString":() => { return "1a" } }; - expect(r.height).toBe(0); - }); - -}); - -describe("Rectangle.js left test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.left).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = null; - expect(r.left).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = undefined; - expect(r.left).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = true; - expect(r.left).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = ""; - expect(r.left).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = "abc"; - expect(r.left).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.left = 0; - expect(r.left).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.left = 1; - expect(r.left).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.left = 500; - expect(r.left).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.left = 50000000000000000; - expect(r.left).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.left = -1; - expect(r.left).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.left = -500; - expect(r.left).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.left = -50000000000000000; - expect(r.left).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = { "a":0 }; - expect(r.left).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = function a() {}; - expect(r.left).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = [1]; - expect(r.left).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = [1,2]; - expect(r.left).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = {}; - expect(r.left).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = { "toString":() => { return 1 } }; - expect(r.left).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = { "toString":() => { return "1" } }; - expect(r.left).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.left = { "toString":() => { return "1a" } }; - expect(r.left).toBe(0); - }); - -}); - -describe("Rectangle.js right test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.right).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = null; - expect(r.right).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = undefined; - expect(r.right).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = true; - expect(r.right).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = ""; - expect(r.right).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = "abc"; - expect(r.right).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.right = 0; - expect(r.right).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.right = 1; - expect(r.right).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.right = 500; - expect(r.right).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.right = 50000000000000000; - expect(r.right).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.right = -1; - expect(r.right).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.right = -500; - expect(r.right).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.right = -50000000000000000; - expect(r.right).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = { "a":0 }; - expect(r.right).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = function a() {}; - expect(r.right).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = [1]; - expect(r.right).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = [1,2]; - expect(r.right).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = {}; - expect(r.right).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = { "toString":() => { return 1 } }; - expect(r.right).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = { "toString":() => { return "1" } }; - expect(r.right).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.right = { "toString":() => { return "1a" } }; - expect(r.right).toBe(0); - }); - -}); - -describe("Rectangle.js top test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.top).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = null; - expect(r.top).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = undefined; - expect(r.top).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = true; - expect(r.top).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = ""; - expect(r.top).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = "abc"; - expect(r.top).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.top = 0; - expect(r.top).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.top = 1; - expect(r.top).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.top = 500; - expect(r.top).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.top = 50000000000000000; - expect(r.top).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.top = -1; - expect(r.top).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.top = -500; - expect(r.top).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.top = -50000000000000000; - expect(r.top).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = { "a":0 }; - expect(r.top).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = function a() {}; - expect(r.top).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = [1]; - expect(r.top).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = [1,2]; - expect(r.top).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = {}; - expect(r.top).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = { "toString":() => { return 1 } }; - expect(r.top).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = { "toString":() => { return "1" } }; - expect(r.top).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.top = { "toString":() => { return "1a" } }; - expect(r.top).toBe(0); - }); - -}); - -describe("Rectangle.js width test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.width).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = null; - expect(r.width).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = undefined; - expect(r.width).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = true; - expect(r.width).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = ""; - expect(r.width).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = "abc"; - expect(r.width).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.width = 0; - expect(r.width).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.width = 1; - expect(r.width).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.width = 500; - expect(r.width).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.width = 50000000000000000; - expect(r.width).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.width = -1; - expect(r.width).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.width = -500; - expect(r.width).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.width = -50000000000000000; - expect(r.width).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = { "a":0 }; - expect(r.width).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = function a() {}; - expect(r.width).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = [1]; - expect(r.width).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = [1,2]; - expect(r.width).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = {}; - expect(r.width).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = { "toString":() => { return 1 } }; - expect(r.width).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = { "toString":() => { return "1" } }; - expect(r.width).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.width = { "toString":() => { return "1a" } }; - expect(r.width).toBe(0); - }); - -}); - -describe("Rectangle.js x test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.x).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = null; - expect(r.x).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = undefined; - expect(r.x).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = true; - expect(r.x).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = ""; - expect(r.x).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = "abc"; - expect(r.x).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.x = 0; - expect(r.x).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.x = 1; - expect(r.x).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.x = 500; - expect(r.x).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.x = 50000000000000000; - expect(r.x).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.x = -1; - expect(r.x).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.x = -500; - expect(r.x).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.x = -50000000000000000; - expect(r.x).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = { "a":0 }; - expect(r.x).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = function a() {}; - expect(r.x).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = [1]; - expect(r.x).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = [1,2]; - expect(r.x).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = {}; - expect(r.x).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = { "toString":() => { return 1 } }; - expect(r.x).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = { "toString":() => { return "1" } }; - expect(r.x).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.x = { "toString":() => { return "1a" } }; - expect(r.x).toBe(0); - }); - -}); - -describe("Rectangle.js y test", () => -{ - - it("default test case1", () => - { - let r = new Rectangle(); - expect(r.y).toBe(0); - }); - - it("default test case2", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = null; - expect(r.y).toBe(0); - }); - - it("default test case3", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = undefined; - expect(r.y).toBe(0); - }); - - it("default test case4", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = true; - expect(r.y).toBe(1); - }); - - it("default test case5", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = ""; - expect(r.y).toBe(0); - }); - - it("default test case6", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = "abc"; - expect(r.y).toBe(0); - }); - - it("default test case7", () => - { - let r = new Rectangle(); - r.y = 0; - expect(r.y).toBe(0); - }); - - it("default test case8", () => - { - let r = new Rectangle(); - r.y = 1; - expect(r.y).toBe(1); - }); - - it("default test case9", () => - { - let r = new Rectangle(); - r.y = 500; - expect(r.y).toBe(500); - }); - - it("default test case10", () => - { - let r = new Rectangle(); - r.y = 50000000000000000; - expect(r.y).toBe($SHORT_INT_MAX); - }); - - it("default test case11", () => - { - let r = new Rectangle(); - r.y = -1; - expect(r.y).toBe(-1); - }); - - it("default test case12", () => - { - let r = new Rectangle(); - r.y = -500; - expect(r.y).toBe(-500); - }); - - it("default test case13", () => - { - let r = new Rectangle(); - r.y = -50000000000000000; - expect(r.y).toBe($SHORT_INT_MIN); - }); - - it("default test case14", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = { "a":0 }; - expect(r.y).toBe(0); - }); - - it("default test case15", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = function a() {}; - expect(r.y).toBe(0); - }); - - it("default test case16", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = [1]; - expect(r.y).toBe(1); - }); - - it("default test case17", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = [1,2]; - expect(r.y).toBe(0); - }); - - it("default test case18", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = {}; - expect(r.y).toBe(0); - }); - - it("default test case19", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = { "toString":() => { return 1 } }; - expect(r.y).toBe(1); - }); - - it("default test case20", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = { "toString":() => { return "1" } }; - expect(r.y).toBe(1); - }); - - it("default test case21", () => - { - let r = new Rectangle(); - // @ts-ignore - r.y = { "toString":() => { return "1a" } }; - expect(r.y).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/geom/TransformTest.ts b/__tests__/next2d/geom/TransformTest.ts deleted file mode 100644 index 16948aea..00000000 --- a/__tests__/next2d/geom/TransformTest.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Transform } from "../../../packages/geom/src/Transform"; -import { MovieClip } from "../../../packages/display/src/MovieClip"; -import { Sprite } from "../../../packages/display/src/Sprite"; -import { ColorTransform } from "../../../packages/geom/src/ColorTransform"; -import { Shape } from "@next2d/display"; - -describe("Transform.js toString test", () => -{ - it("toString test success", () => - { - let transform = new Transform(new Sprite()); - expect(transform.toString()).toBe("[object Transform]"); - }); - -}); - -describe("Transform.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Transform.toString()).toBe("[class Transform]"); - }); - -}); - -describe("Transform.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Transform(new Sprite()); - expect(object.namespace).toBe("next2d.geom.Transform"); - }); - - it("namespace test static", () => - { - expect(Transform.namespace).toBe("next2d.geom.Transform"); - }); - -}); - -describe("Transform.js matrix test", () => -{ - - it("matrix test1", () => - { - let mc = new MovieClip(); - - let matrix = mc.transform.matrix; - expect(matrix.toString()).toBe("(a=1, b=0, c=0, d=1, tx=0, ty=0)"); - }); - -}); - -describe("colorTransform.js Transform test", () => -{ - - it("colorTransform test1", () => - { - let mc = new MovieClip(); - - let colorTransform = mc.transform.colorTransform; - expect(colorTransform.toString()).toBe( - "(redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=0, greenOffset=0, blueOffset=0, alphaOffset=0)" - ); - }); - -}); - -describe("Transform.js concatenatedMatrix test", () => -{ - it("concatenatedMatrix on stage test", () => - { - - let root = new MovieClip(); - - let mc = new MovieClip(); - mc.y = 100; - root.addChild(mc); - - let shape = mc.addChild(new Shape()); - shape - .graphics - .beginFill(0xff0000, 1) - .drawRect(100, 100, 100, 100); - - shape.x = 100; - shape.rotation = 45; - - expect(shape.transform.concatenatedMatrix.toString()).toBe( - "(a=0.7071067690849304, b=0.7071067690849304, c=-0.7071067690849304, d=0.7071067690849304, tx=100, ty=100)" - ); - }); - -}); - -describe("Transform.js concatenatedTransform test", () => -{ - - it("concatenatedTransform test", () => - { - let root = new Sprite(); - root.transform.colorTransform = new ColorTransform(1,1,1,1,0,200,0,0); - - let shape = new Shape(); - shape.graphics.beginFill(0xff0000, 1); - shape.graphics.drawRect(100, 100, 100, 100); - root.addChild(shape); - - shape.x = 100; - shape.rotation = 45; - - expect(shape.transform.concatenatedColorTransform.toString()).toBe( - "(redMultiplier=1, greenMultiplier=1, blueMultiplier=1, alphaMultiplier=1, redOffset=0, greenOffset=200, blueOffset=0, alphaOffset=0)" - ); - }); - -}); diff --git a/__tests__/next2d/media/SoundMixerTest.ts b/__tests__/next2d/media/SoundMixerTest.ts deleted file mode 100644 index d34e04fb..00000000 --- a/__tests__/next2d/media/SoundMixerTest.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { SoundMixer } from "../../../packages/media/src/SoundMixer"; - -describe("SoundMixer.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new SoundMixer(); - expect(object.namespace).toBe("next2d.media.SoundMixer"); - }); - - it("namespace test static", function() - { - expect(SoundMixer.namespace).toBe("next2d.media.SoundMixer"); - }); - -}); - -describe("SoundMixer.js toString test", function() -{ - it("toString test success", function() - { - expect(new SoundMixer().toString()).toBe("[object SoundMixer]"); - }); - -}); - -describe("SoundMixer.js static toString test", function() -{ - - it("static toString test", function() - { - expect(SoundMixer.toString()).toBe("[class SoundMixer]"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/media/SoundTest.ts b/__tests__/next2d/media/SoundTest.ts deleted file mode 100644 index 29ea9ec6..00000000 --- a/__tests__/next2d/media/SoundTest.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { Sound } from "../../../packages/media/src/Sound"; - -describe("Sound.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new Sound(); - expect(object.namespace).toBe("next2d.media.Sound"); - expect($PREFIX).toBe("__next2d__"); - }); - - it("namespace test static", () => - { - expect(Sound.namespace).toBe("next2d.media.Sound"); - }); - -}); - -describe("Sound.js toString test", () => -{ - it("toString test success", () => - { - expect(new Sound().toString()).toBe("[object Sound]"); - }); - -}); - -describe("Sound.js static toString test", () => -{ - - it("static toString test", () => - { - expect(Sound.toString()).toBe("[class Sound]"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/media/SoundTransformTest.ts b/__tests__/next2d/media/SoundTransformTest.ts deleted file mode 100644 index 7d46d120..00000000 --- a/__tests__/next2d/media/SoundTransformTest.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { SoundTransform } from "../../../packages/media/src/SoundTransform"; - -describe("SoundTransform.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new SoundTransform(); - expect(object.namespace).toBe("next2d.media.SoundTransform"); - }); - - it("namespace test static", () => - { - expect(SoundTransform.namespace).toBe("next2d.media.SoundTransform"); - }); - -}); - -describe("SoundTransform.js toString test", () => -{ - it("toString test success", () => - { - expect(new SoundTransform().toString()).toBe("[object SoundTransform]"); - }); - -}); - -describe("SoundTransform.js static toString test", () => -{ - - it("static toString test", () => - { - expect(SoundTransform.toString()).toBe("[class SoundTransform]"); - }); - -}); - -describe("SoundTransform.js property test", () => -{ - - // volume - it("volume test success default", () => - { - let soundTransform = new SoundTransform(); - expect(soundTransform.volume).toBe(1); - }); - - it("volume test success case1", () => - { - let soundTransform = new SoundTransform(); - soundTransform.volume = 100; - expect(soundTransform.volume).toBe(1); - }); - - it("volume test success case2", () => - { - let soundTransform = new SoundTransform(); - soundTransform.volume = -32; - expect(soundTransform.volume).toBe(0); - }); - - it("volume test success case3", () => - { - let soundTransform = new SoundTransform(100); - expect(soundTransform.volume).toBe(1); - }); - - it("volume test not a number case1", () => - { - let soundTransform = new SoundTransform(); - // @ts-ignore - soundTransform.volume = "test"; - expect(soundTransform.volume).toBe(0); - }); - - it("volume test not a number case2", () => - { - let soundTransform = new SoundTransform(); - // @ts-ignore - soundTransform.volume = ""; - expect(soundTransform.volume).toBe(0); - }); - -}); - -describe("SoundTransform.js volume test", () => -{ - - it("default test case1", () => - { - let st = new SoundTransform(); - expect(st.volume).toBe(1); - }); - - it("default test case2", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = null; - expect(st.volume).toBe(0); - }); - - it("default test case3", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = undefined; - expect(st.volume).toBe(0); - }); - - it("default test case4", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = true; - expect(st.volume).toBe(1); - }); - - it("default test case5", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = ""; - expect(st.volume).toBe(0); - }); - - it("default test case6", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = "abc"; - expect(st.volume).toBe(0); - }); - - it("default test case7", () => - { - let st = new SoundTransform(); - st.volume = 0; - expect(st.volume).toBe(0); - }); - - it("default test case8", () => - { - let st = new SoundTransform(); - st.volume = 1; - expect(st.volume).toBe(1); - }); - - it("default test case9", () => - { - let st = new SoundTransform(); - st.volume = 500; - expect(st.volume).toBe(1); - }); - - it("default test case10", () => - { - let st = new SoundTransform(); - st.volume = 50000000000000000; - expect(st.volume).toBe(1); - }); - - it("default test case11", () => - { - let st = new SoundTransform(); - st.volume = -1; - expect(st.volume).toBe(0); - }); - - it("default test case12", () => - { - let st = new SoundTransform(); - st.volume = -500; - expect(st.volume).toBe(0); - }); - - it("default test case13", () => - { - let st = new SoundTransform(); - st.volume = -50000000000000000; - expect(st.volume).toBe(0); - }); - - it("default test case14", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = { "a":0 }; - expect(st.volume).toBe(0); - }); - - it("default test case15", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = function a() {}; - expect(st.volume).toBe(0); - }); - - it("default test case16", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = [1]; - expect(st.volume).toBe(1); - }); - - it("default test case17", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = [1,2]; - expect(st.volume).toBe(0); - }); - - it("default test case18", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = {}; - expect(st.volume).toBe(0); - }); - - it("default test case19", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = { "toString":() => { return 1 } }; - expect(st.volume).toBe(1); - }); - - it("default test case20", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = { "toString":() => { return "10" } }; - expect(st.volume).toBe(1); - }); - - it("default test case21", () => - { - let st = new SoundTransform(); - // @ts-ignore - st.volume = { "toString":() => { return "1a" } }; - expect(st.volume).toBe(0); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/media/VideoTest.ts b/__tests__/next2d/media/VideoTest.ts deleted file mode 100644 index 87684e56..00000000 --- a/__tests__/next2d/media/VideoTest.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { Video } from "../../../packages/media/src/Video"; - -describe("Video.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new Video(); - expect(object.namespace).toBe("next2d.media.Video"); - expect($PREFIX).toBe("__next2d__"); - }); - - it("namespace test static", function() - { - expect(Video.namespace).toBe("next2d.media.Video"); - }); - -}); - -describe("Video.js toString test", function() -{ - it("toString test success", function() - { - expect(new Video().toString()).toBe("[object Video]"); - }); - -}); - -describe("Video.js static toString test", function() -{ - - it("static toString test", function() - { - expect(Video.toString()).toBe("[class Video]"); - }); - -}); - -describe("Video.js smoothing test", function() -{ - - it("default test case1", function() - { - let vi = new Video(); - expect(vi.smoothing).toBe(true); - }); - - it("default test case2", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = null; - expect(vi.smoothing).toBe(false); - }); - - it("default test case3", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = undefined; - expect(vi.smoothing).toBe(false); - }); - - it("default test case4", function() - { - let vi = new Video(); - vi.smoothing = true; - expect(vi.smoothing).toBe(true); - }); - - it("default test case5", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = ""; - expect(vi.smoothing).toBe(false); - }); - - it("default test case6", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = "abc"; - expect(vi.smoothing).toBe(true); - }); - - it("default test case7", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = 0; - expect(vi.smoothing).toBe(false); - }); - - it("default test case8", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = 1; - expect(vi.smoothing).toBe(true); - }); - - it("default test case9", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = { "a":0 }; - expect(vi.smoothing).toBe(true); - }); - - it("default test case10", function() - { - let vi = new Video(); - // @ts-ignore - vi.smoothing = function a() {}; - expect(vi.smoothing).toBe(true); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/net/URLRequestHeaderTest.ts b/__tests__/next2d/net/URLRequestHeaderTest.ts deleted file mode 100644 index 9192f7c9..00000000 --- a/__tests__/next2d/net/URLRequestHeaderTest.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { URLRequestHeader } from "../../../packages/net/src/URLRequestHeader"; - -describe("URLRequestHeader.js toString test", function() -{ - it("toString test success", function() - { - expect(new URLRequestHeader().toString()).toBe("[object URLRequestHeader]"); - }); - -}); - -describe("URLRequestHeader.js static toString test", function() -{ - - it("static toString test", function() - { - expect(`${URLRequestHeader}`).toBe("[class URLRequestHeader]"); - }); - -}); - -describe("URLRequestHeader.js namespace test", function() -{ - - it("namespace test public", function() - { - const object = new URLRequestHeader(); - expect(object.namespace).toBe("next2d.net.URLRequestHeader"); - }); - - it("namespace test static", function() - { - expect(URLRequestHeader.namespace).toBe("next2d.net.URLRequestHeader"); - }); - -}); - -describe("URLRequestHeader.js property valid test", function() -{ - it("constructor success case1", function () - { - const h = new URLRequestHeader("1", "2"); - expect(h.name).toBe("1"); - expect(h.value).toBe("2"); - }); - - it("constructor valid case1", function () - { - // @ts-ignore - const h = new URLRequestHeader(1, 2); - expect(h.name).toBe("1"); - expect(h.value).toBe("2"); - }); -}); \ No newline at end of file diff --git a/__tests__/next2d/net/URLRequestTest.ts b/__tests__/next2d/net/URLRequestTest.ts deleted file mode 100644 index 0c7274f9..00000000 --- a/__tests__/next2d/net/URLRequestTest.ts +++ /dev/null @@ -1,449 +0,0 @@ -import {URLRequest} from "../../../packages/net/src/URLRequest"; -import {URLRequestHeader} from "../../../packages/net/src/URLRequestHeader"; - -describe("URLRequest.js toString test", () => -{ - it("toString test success", () => - { - expect(new URLRequest().toString()).toBe("[object URLRequest]"); - }); -}); - -describe("URLRequest.js static toString test", () => -{ - - it("static toString test", () => - { - expect(`${URLRequest}`).toBe("[class URLRequest]"); - }); - -}); - -describe("URLRequest.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new URLRequest(); - expect(object.namespace).toBe("next2d.net.URLRequest"); - }); - - it("namespace test static", () => - { - expect(URLRequest.namespace).toBe("next2d.net.URLRequest"); - }); - -}); - -describe("URLRequest.js properties test", () => -{ - it("contentType success", () => - { - let req = new URLRequest(); - req.contentType = "text/html; charset=utf-8"; - expect(req.contentType).toBe("text/html; charset=utf-8"); - }); - - it("contentType valid", () => - { - let req = new URLRequest(); - // @ts-ignore - req.contentType = 100; - expect(req.contentType).toBe("100"); - }); - - it("data success case1", () => - { - let req = new URLRequest(); - // @ts-ignore - req.data = 100; - expect(req.data).toBe(100); - }); - - it("data success case2", () => - { - let req = new URLRequest(); - req.data = "data"; - expect(req.data).toBe("data"); - }); - - it("data success case3", () => - { - let req = new URLRequest(); - req.data = 0; - expect(req.data).toBe(0); - }); - - it("method success", () => - { - let req = new URLRequest(); - req.method = "POST"; - expect(req.method).toBe("POST"); - }); - - it("requestHeaders success", () => - { - let req = new URLRequest(); - - req.requestHeaders = [ - new URLRequestHeader("test", "aaa") - ]; - - for (let i = 0; req.requestHeaders.length > i; i++) { - let requestHeader = req.requestHeaders[i]; - expect(requestHeader.name).toBe("test"); - expect(requestHeader.value).toBe("aaa"); - } - }); - - it("url success", () => - { - let req = new URLRequest(); - req.url = "http://test.com"; - expect(req.url).toBe("http://test.com"); - }); - -}); - -describe("URLRequest.js contentType test", () => -{ - - it("default test case1", () => - { - let ur = new URLRequest(); - expect(ur.contentType).toBe("application/json"); - }); - - it("default test case2", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = null; - expect(ur.contentType).toBe("null"); - }); - - it("default test case3", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = undefined; - expect(ur.contentType).toBe("undefined"); - }); - - it("default test case4", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = true; - expect(ur.contentType).toBe("true"); - }); - - it("default test case5", () => - { - let ur = new URLRequest(); - ur.contentType = ""; - expect(ur.contentType).toBe(""); - }); - - it("default test case6", () => - { - let ur = new URLRequest(); - ur.contentType = "abc"; - expect(ur.contentType).toBe("abc"); - }); - - it("default test case7", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = 0; - expect(ur.contentType).toBe("0"); - }); - - it("default test case8", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = 1; - expect(ur.contentType).toBe("1"); - }); - - it("default test case9", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = 500; - expect(ur.contentType).toBe("500"); - }); - - it("default test case10", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = 50000000000000000; - expect(ur.contentType).toBe("50000000000000000"); - }); - - it("default test case11", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = -1; - expect(ur.contentType).toBe("-1"); - }); - - it("default test case12", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = -500; - expect(ur.contentType).toBe("-500"); - }); - - it("default test case13", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = -50000000000000000; - expect(ur.contentType).toBe("-50000000000000000"); - }); - - it("default test case14", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = { "a":0 }; - expect(ur.contentType).toBe("[object Object]"); - }); - - it("default test case15", () => - { - let test = () => {}; - test.toString = () => { return "test" }; - - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = test; - expect(ur.contentType).toBe("test"); - }); - - it("default test case16", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = [1]; - expect(ur.contentType).toBe("1"); - }); - - it("default test case17", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = [1,2]; - expect(ur.contentType).toBe("1,2"); - }); - - it("default test case18", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = {}; - expect(ur.contentType).toBe("[object Object]"); - }); - - it("default test case19", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = { "toString":() => { return 1 } }; - expect(ur.contentType).toBe("1"); - }); - - it("default test case20", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = { "toString":() => { return "10" } }; - expect(ur.contentType).toBe("10"); - }); - - it("default test case21", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.contentType = { "toString":() => { return "1a" } }; - expect(ur.contentType).toBe("1a"); - }); - -}); - -describe("URLRequest.js url test", () => -{ - it("default test case1", () => - { - let ur = new URLRequest(); - expect(ur.url).toBe(""); - }); - - it("default test case2", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = null; - expect(ur.url).toBe("null"); - }); - - it("default test case3", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = undefined; - expect(ur.url).toBe("undefined"); - }); - - it("default test case4", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = true; - expect(ur.url).toBe("true"); - }); - - it("default test case5", () => - { - let ur = new URLRequest(); - ur.url = ""; - expect(ur.url).toBe(""); - }); - - it("default test case6", () => - { - let ur = new URLRequest(); - ur.url = "abc"; - expect(ur.url).toBe("abc"); - }); - - it("default test case7", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = 0; - expect(ur.url).toBe("0"); - }); - - it("default test case8", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = 1; - expect(ur.url).toBe("1"); - }); - - it("default test case9", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = 500; - expect(ur.url).toBe("500"); - }); - - it("default test case10", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = 50000000000000000; - expect(ur.url).toBe("50000000000000000"); - }); - - it("default test case11", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = -1; - expect(ur.url).toBe("-1"); - }); - - it("default test case12", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = -500; - expect(ur.url).toBe("-500"); - }); - - it("default test case13", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = -50000000000000000; - expect(ur.url).toBe("-50000000000000000"); - }); - - it("default test case14", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = { "a":0 }; - expect(ur.url).toBe("[object Object]"); - }); - - it("default test case15", () => - { - let test = () => {}; - test.toString = () => { return "test" }; - - let ur = new URLRequest(); - // @ts-ignore - ur.url = test; - expect(ur.url).toBe("test"); - }); - - it("default test case16", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = [1]; - expect(ur.url).toBe("1"); - }); - - it("default test case17", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = [1,2]; - expect(ur.url).toBe("1,2"); - }); - - it("default test case18", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = {}; - expect(ur.url).toBe("[object Object]"); - }); - - it("default test case19", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = { "toString":() => { return 1 } }; - expect(ur.url).toBe("1"); - }); - - it("default test case20", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = { "toString":() => { return "10" } }; - expect(ur.url).toBe("10"); - }); - - it("default test case21", () => - { - let ur = new URLRequest(); - // @ts-ignore - ur.url = { "toString":() => { return "1a" } }; - expect(ur.url).toBe("1a"); - }); - -}); diff --git a/__tests__/next2d/text/TextFieldTest.ts b/__tests__/next2d/text/TextFieldTest.ts deleted file mode 100644 index 4d1c375a..00000000 --- a/__tests__/next2d/text/TextFieldTest.ts +++ /dev/null @@ -1,3505 +0,0 @@ -import { $PREFIX } from "../../../packages/util/src/Util"; -import { TextField } from "../../../packages/display/src/TextField"; -// import { TextFormat } from "../../../packages/text/src/TextFormat"; - -describe("TextField.js toString test", () => -{ - it("toString test success", () => - { - let object = new TextField(); - expect(object.toString()).toBe("[object TextField]"); - expect($PREFIX).toBe("__next2d__"); - }); - -}); - -describe("TextField.js static toString test", () => -{ - - it("static toString test", () => - { - expect(TextField.toString()).toBe("[class TextField]"); - }); - -}); - -describe("TextField.js namespace test", () => -{ - - it("namespace test public", () => - { - expect(new TextField().namespace).toBe("next2d.display.TextField"); - }); - - it("namespace test static", () => - { - expect(TextField.namespace).toBe("next2d.display.TextField"); - }); - -}); - -// // describe("TextField.js autoSize test", () => -// // { -// // -// // it("autoSize test1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.x | 0).toBe(0); -// // -// // tf.autoSize = "center"; -// // expect(tf.x | 0).toBe(48); -// // expect(tf.width | 0).toBe(4); -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 12); -// // expect(tf.x | 0).toBe(48); -// // expect(tf.width | 0).toBe(4); -// // -// // tf.text = "abcdefg"; -// // -// // const x = tf.x | 0; -// // expect(x >= 20 || 25 > x).toBe(true); -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("autoSize test2", () => -// // { -// // let tf = new TextField(); -// // expect(tf.x | 0).toBe(0); -// // -// // tf.autoSize = "center"; -// // expect(tf.x | 0).toBe(48); -// // expect(tf.width | 0).toBe(4); -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 12); -// // expect(tf.x | 0).toBe(48); -// // expect(tf.width | 0).toBe(4); -// // -// // tf.x = -100; -// // expect(tf.x | 0).toBe(-100); -// // -// // tf.width = 200; -// // expect(tf.x | 0).toBe(-2); -// // }); -// // -// // }); - -// // describe("TextField.js autoFontSize test", () => -// // { -// // -// // it("autoFontSize width test", () => -// // { -// // let tf = new TextField(); -// // tf.width = 20; -// // tf.autoFontSize = true; -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 20); -// // expect(tf.defaultTextFormat.size).toBe(20); -// // -// // tf.text = "Hello, World."; -// // expect(tf.defaultTextFormat.size).toBe(20); -// // expect(tf.width > tf.textWidth).toBe(true); -// // -// // // restore test -// // tf.text = "A"; -// // expect(tf.defaultTextFormat.size).toBe(20); -// // expect(tf.width > tf.textWidth).toBe(true); -// // }); -// // -// // it("autoFontSize width min test", () => -// // { -// // let tf = new TextField(); -// // tf.width = 1; -// // tf.autoFontSize = true; -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 100); -// // expect(tf.defaultTextFormat.size).toBe(100); -// // -// // tf.text = "Hello, World."; -// // expect(tf.defaultTextFormat.size).toBe(100); -// // expect(tf.width < tf.textWidth).toBe(true); -// // }); -// // -// // it("autoFontSize height test", () => -// // { -// // let tf = new TextField(); -// // -// // tf.height = 5; -// // tf.autoFontSize = true; -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 20); -// // expect(tf.defaultTextFormat.size).toBe(20); -// // -// // tf.text = "Hello, World."; -// // expect(tf.height > tf.textHeight).toBe(true); -// // }); -// // -// // it("autoFontSize height min test", () => -// // { -// // let tf = new TextField(); -// // tf.height = 1; -// // tf.autoFontSize = true; -// // -// // tf.defaultTextFormat = new TextFormat("_sans", 100); -// // expect(tf.defaultTextFormat.size).toBe(100); -// // -// // tf.text = "Hello, World."; -// // expect(tf.height < tf.textHeight).toBe(true); -// // }); -// // -// // }); - -// describe("TextField.js thickness test", () => -// { - -// it("thickness test case1", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// tf.thickness = 4; -// expect(tf.thickness).toBe(4); -// }); - -// it("thickness test case2", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// // @ts-ignore -// tf.thickness = "4"; -// expect(tf.thickness).toBe(4); -// }); - -// it("thickness test case3", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// // @ts-ignore -// tf.thickness = "4a"; -// expect(tf.thickness).toBe(0); -// }); - -// it("thickness test case4", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// // @ts-ignore -// tf.thickness = { "toString": () => { return "10" } }; -// expect(tf.thickness).toBe(10); -// }); - -// it("thickness test case5", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// // @ts-ignore -// tf.thickness = [100]; -// expect(tf.thickness).toBe(100); -// }); - -// it("thickness test case6", () => -// { -// let tf = new TextField(); -// expect(tf.thickness).toBe(0); - -// // @ts-ignore -// tf.thickness = [1,0,10]; -// expect(tf.thickness).toBe(0); -// }); -// }); - -// describe("TextField.js thicknessColor test", () => -// { - -// it("thicknessColor test success case1", () => -// { -// let tf = new TextField(); -// tf.thicknessColor = 0xff0000; -// expect(tf.thicknessColor).toBe(0xff0000); -// }); - -// it("thicknessColor test success case6", () => -// { -// let tf = new TextField(); -// tf.thicknessColor = 0xffffff + 100; -// expect(tf.thicknessColor).toBe(0xffffff); -// }); - -// it("thicknessColor test success case7", () => -// { -// let tf = new TextField(); -// tf.thicknessColor = -100; -// expect(tf.thicknessColor).toBe(0x000000); -// }); -// }); - -// describe("TextField.js verticalAlign test", () => -// { - -// it("thicknessColor test success case1", () => -// { -// let tf = new TextField(); -// expect(tf.verticalAlign).toBe("top"); - -// tf.verticalAlign = "middle"; -// expect(tf.verticalAlign).toBe("middle"); - -// tf.verticalAlign = "bottom"; -// expect(tf.verticalAlign).toBe("bottom"); -// }); - -// }); - -// describe("TextField.js property test", () => -// { - -// // default -// it("default test success", () => -// { -// let tf = new TextField(); -// expect(tf.length).toBe(0); -// expect(tf.textHeight).toBe(0); -// expect(tf.autoSize).toBe("none"); -// expect(tf.background).toBe(false); -// expect(tf.backgroundColor).toBe(0xffffff); -// expect(tf.border).toBe(false); -// expect(tf.borderColor).toBe(0x000000); -// expect(tf.defaultTextFormat instanceof TextFormat).toBe(true); -// expect(tf.text).toBe(""); -// expect(tf.textColor).toBe(0x000000); -// expect(tf.textWidth).toBe(0); -// expect(tf.numLines).toBe(1); -// expect(tf.wordWrap).toBe(false); -// expect(tf.multiline).toBe(false); -// expect(tf.maxChars).toBe(0); - -// }); - -// // length -// it("length test success case1", () => -// { -// let tf = new TextField(); -// tf.text = "abcアイウエオ"; -// expect(tf.length).toBe(8); -// }); - -// it("length test success case2", () => -// { -// let tf = new TextField(); -// tf.multiline = true; -// tf.text = "abc\nアイウエオ\n\n"; -// expect(tf.length).toBe(11); -// }); - -// it("length test success case4", () => -// { -// let tf = new TextField(); -// tf.multiline = false; -// tf.text = "abc\r"; -// expect(tf.length).toBe(4); -// }); - -// it("length test success case5", () => -// { -// let tf = new TextField(); -// tf.multiline = false; -// tf.text = "abc\r\n"; -// expect(tf.length).toBe(5); -// }); - -// it("length test success case5", () => -// { -// let tf = new TextField(); -// tf.multiline = true; -// tf.text = "abc"; -// expect(tf.length).toBe(3); -// }); - -// it("length test success case6", () => -// { -// let tf = new TextField(); -// tf.width = 30; -// tf.height = 20; -// tf.wordWrap = true; -// tf.text = "123456789"; -// expect(tf.length).toBe(9); -// }); - -// // it("length test success case7", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

abc

"; -// // expect(tf.length).toBe(3); -// // }); - -// // it("length test success case8", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

abc

"; -// // expect(tf.length).toBe(4); -// // }); -// // -// // it("length test success case9", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

abc\n123

"; -// // expect(tf.length).toBe(8); -// // }); - -// // it("length test success case10", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

abc\r\n123

"; -// // expect(tf.length).toBe(9); -// // }); -// // -// // it("length test success case11", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

abc
123

"; -// // expect(tf.length).toBe(6); -// // }); - -// // it("length test success case12", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

abc
123

"; -// // expect(tf.length).toBe(8); -// // }); -// // -// // it("length test success case13", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "abc"; -// // expect(tf.length).toBe(3); -// // }); - -// // it("length test success case14", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

b

"; -// // expect(tf.length).toBe(5); -// // }); -// // -// // it("length test success case15", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

a

b

"; -// // expect(tf.length).toBe(2); -// // }); - -// // autoSize -// it("autoSize test success case1", () => -// { -// let tf = new TextField(); -// tf.autoSize = "center"; -// expect(tf.autoSize).toBe("center"); -// }); - -// // background -// it("background test success case1", () => -// { -// let tf = new TextField(); -// tf.background = true; -// expect(tf.background).toBe(true); -// }); - -// it("background test success case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.background = 1; -// expect(tf.background).toBe(true); -// }); - -// it("background test success case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.background = ""; -// expect(tf.background).toBe(false); -// // @ts-ignore -// tf.background = "abc"; -// expect(tf.background).toBe(true); -// }); - -// // backgroundColor -// it("backgroundColor test success case1", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = 0xff0000; -// expect(tf.backgroundColor).toBe(0xff0000); -// }); - -// // border -// it("border test success case1", () => -// { -// let tf = new TextField(); -// tf.border = true; -// expect(tf.border).toBe(true); -// }); - -// it("border test success case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = 1; -// expect(tf.border).toBe(true); -// }); - -// it("border test success case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = ""; -// expect(tf.border).toBe(false); -// // @ts-ignore -// tf.border = "abc"; -// expect(tf.border).toBe(true); -// }); - -// // borderColor -// it("borderColor test success case1", () => -// { -// let tf = new TextField(); -// tf.borderColor = 0xff0000; -// expect(tf.borderColor).toBe(0xff0000); -// }); - -// // defaultTextFormat -// it("defaultTextFormat test success case1", () => -// { -// let tf = new TextField(); -// expect(tf.defaultTextFormat.size).toBe(12); - -// let format = new TextFormat(); -// format.size = 100; - -// tf.defaultTextFormat = format; -// expect(tf.defaultTextFormat.size).toBe(100); -// }); - -// // text -// it("text test success case1", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.text = 0; -// expect(tf.text).toBe("0"); -// }); - -// // htmlText -// it("htmlText test success case1", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.htmlText = 0; -// expect(tf.htmlText).toBe("0"); -// }); - -// // textColor -// it("textColor test success case1", () => -// { -// let tf = new TextField(); -// tf.textColor = 0xff0000; -// expect(tf.textColor).toBe(0xff0000); -// }); - -// // textWidth -// // it("textWidth test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.text = "aaa"; -// // expect(Math.round(tf.textWidth)).toBe(16); -// // }); - -// // it("textWidth test success case2", () => -// // { -// // let tf = new TextField(); -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 20; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.text = "aaa"; -// // expect(Math.ceil(tf.textWidth)).toBe(27); -// // }); - -// // it("textWidth test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.wordWrap = true; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(Math.ceil(tf.textWidth)).toBe(94); -// // }); - -// // it("textWidth test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(Math.ceil(tf.textWidth)).toBe(280); -// // }); - -// // it("textWidth test success case5", () => -// // { -// // let tf = new TextField(); -// // tf.autoSize = "left"; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.width = 40; -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(Math.ceil(tf.textWidth)).toBe(40); -// // }); - -// // it("textWidth test success case6", () => -// // { -// // let tf = new TextField(); -// // tf.autoSize = "left"; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // -// // expect(Math.ceil(tf.textWidth)).toBe(94); -// // }); - -// // it("textWidth test success case7", () => -// // { -// // let tf = new TextField(); -// // tf.autoSize = "left"; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // textFormat.italic = true; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(Math.ceil(tf.textWidth)).toBe(90); -// // }); - -// // textHeight -// // it("textHeight test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.text = "aaa"; -// // expect(Math.round(tf.textHeight)).toBe(14); -// // }); - -// // it("textHeight test success case2", () => -// // { -// // let tf = new TextField(); -// // let f = new TextFormat(); -// // f.size = 20; -// // tf.defaultTextFormat = f; -// // tf.text = "aaa"; -// // expect(Math.round(tf.textHeight)).toBe(22); -// // }); - -// // it("textHeight test success case3", () => -// // { -// // let tf = new TextField(); -// // let f = new TextFormat(); -// // f.size = 30; -// // tf.multiline = true; -// // tf.wordWrap = true; -// // tf.defaultTextFormat = f; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(Math.round(tf.textHeight)).toBe(152); // Flash is 150 -// // }); - -// // it("textHeight test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // // tf.defaultTextFormat.size = 30; // tf.defaultTextForamtのgetterが参照渡しでないため、設定できていない。 -// // let f = tf.defaultTextFormat; -// // f.size = 30; -// // tf.defaultTextFormat = f; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(tf.textHeight).toBe(92); -// // }); - -// // it("textHeight test success case5", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 30; -// // tf.defaultTextFormat = f; -// // tf.width = 40; -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(tf.textHeight).toBe(452); -// // }); - -// // it("textHeight test success case6", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 30; -// // tf.defaultTextFormat = f; -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // -// // expect(tf.textHeight).toBe(152); -// // }); - -// // it("textHeight test success case7", () => -// // { -// // let tf = new TextField(); -// // tf.autoSize = "left"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 30; -// // tf.defaultTextFormat = f; -// // tf.defaultTextFormat.italic = true; -// // tf.wordWrap = true; -// // tf.text = "aaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // -// // expect(tf.textHeight).toBe(152); -// // }); - -// // it("textHeight test success case8", () => -// // { -// // let tf = new TextField(); -// // -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.text = "\n"; -// // -// // // Chrome,FireFox,Safari -// // expect(tf.textHeight).toBe(102); -// // }); - -// // it("textHeight test success case8-2", () => -// // { -// // let tf = new TextField(); -// // -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.text = "\na"; -// // -// // expect(tf.textHeight).toBe(110); -// // }); - -// // it("textHeight test success case9", () => -// // { -// // let tf = new TextField(); -// // -// // tf.multiline = true; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.text = "\n"; -// // -// // expect(tf.textHeight).toBe(102); // Flash is 108 -// // }); - -// // it("textHeight test success case10", () => -// // { -// // let tf = new TextField(); -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // // Chrome,FireFox,Safari -// // expect(tf.textHeight).toBe(0); -// // }); - -// // it("textHeight test success case11", () => -// // { -// // let tf = new TextField(); -// // -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.type = "input"; -// // -// // // Chrome,FireFox,Safari -// // expect(Math.round(tf.textHeight)).toBe(0); -// // }); - -// // it("textHeight test success case12", () => -// // { -// // let tf = new TextField(); -// // -// // let f = tf.defaultTextFormat; -// // f.size = 50; -// // f.leading = 8; -// // tf.defaultTextFormat = f; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // -// // // Chrome,FireFox,Safari -// // expect(tf.textHeight).toBe(0); -// // }); - -// // it("textHeight test success case13", () => -// // { -// // let tf = new TextField(); -// // tf.type = "input"; -// // tf.htmlText = "

"; -// // -// // expect(tf.textHeight).toBe(0); -// // }); - -// // it("textHeight test success case14", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.type = "input"; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(3); -// // expect(tf.textHeight).toBe(38); -// // }); - -// // numLines -// it("numLines test success case1", () => -// { -// let tf = new TextField(); -// tf.text = "aaa"; -// expect(tf.numLines).toBe(1); -// }); - -// // it("numLines test success case2", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.text = "aaa\n\nbbb\n\nccc"; -// // expect(tf.numLines).toBe(5); -// // }); - -// // it("numLines test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.wordWrap = true; -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 30; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaa\nbbb\nccc"; -// // expect(tf.numLines).toBe(5); -// // }); - -// // wordWrap -// it("wordWrap test success case1", () => -// { -// let tf = new TextField(); -// tf.wordWrap = true; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("wordWrap test success case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = 1; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("wordWrap test success case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = ""; -// expect(tf.wordWrap).toBe(false); -// // @ts-ignore -// tf.wordWrap = "abc"; -// expect(tf.wordWrap).toBe(true); -// }); - -// // it("wordWrap test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.wordWrap = true; -// // tf.width = 100; -// // tf.text = "テキストテキスト、テキストテキスト"; -// // -// // expect(tf.getLineText(0)).toBe("テキストテキス"); -// // expect(tf.getLineText(1)).toBe("ト、テキストテキ"); -// // expect(tf.getLineText(2)).toBe("スト"); -// // expect(tf.getLineText(3)).toBe(""); -// // }); - -// // it("wordWrap test success case5", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.wordWrap = true; -// // tf.width = 104; -// // tf.htmlText = "テキストテ。テキスト"; -// // -// // expect(tf.getLineText(0)).toBe("テキスト"); -// // expect(tf.getLineText(1)).toBe("テ。テキス"); -// // expect(tf.getLineText(2)).toBe("ト"); -// // expect(tf.getLineText(3)).toBe(""); -// // }); - -// // it("wordWrap test success case6", () => -// // { -// // let tf = new TextField(); -// // -// // let textFormat = tf.defaultTextFormat; -// // textFormat.size = 16; -// // tf.defaultTextFormat = textFormat; -// // -// // tf.width = 90; -// // tf.height = 28.55; -// // tf.wordWrap = true; -// // tf.multiline = true; -// // tf.text = "2020/9/14 12:00"; -// // -// // expect(tf.getLineText(0)).toBe("2020/9/14 "); -// // expect(tf.getLineText(1)).toBe("12:00"); -// // }); - -// // multiline -// it("multiline test success case1", () => -// { -// let tf = new TextField(); -// tf.multiline = true; -// expect(tf.multiline).toBe(true); -// }); - -// it("multiline test success case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = 1; -// expect(tf.multiline).toBe(true); -// }); - -// it("multiline test success case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = ""; -// expect(tf.multiline).toBe(false); -// // @ts-ignore -// tf.multiline = "abc"; -// expect(tf.multiline).toBe(true); -// }); - -// // maxChars -// it("maxChars test success case1", () => -// { -// let tf = new TextField(); -// tf.maxChars = 1; -// expect(tf.maxChars).toBe(1); -// }); - -// it("maxChars test success case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = "10"; -// expect(tf.maxChars).toBe(10); -// }); - -// it("maxChars test valid case1", () => -// { -// let tf = new TextField(); -// tf.maxChars = -10; -// expect(tf.maxChars).toBe(-10); -// }); - -// // height -// // it("height test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.height = 20; -// // tf.width = 100; -// // tf.autoSize = "none"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(tf.height).toBe(20); -// // }); -// // -// // it("height test success case2", () => -// // { -// // let tf = new TextField(); -// // tf.height = 20; -// // tf.width = 100; -// // tf.autoSize = "none"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(tf.height).toBe(20); -// // }); - -// // it("height test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.height = 20; -// // tf.width = 100; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(Math.round(tf.height)).toBe(18); -// // }); -// // -// // it("height test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.height = 20; -// // tf.width = 100; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(Math.round(tf.height)).toBe(18); -// // }); - -// }); - -// describe("TextField.js appendText test", () => -// { - -// it("appendText test success case1", () => -// { -// let tf = new TextField(); -// expect(tf.text).toBe(""); - -// tf.appendText("A"); -// tf.appendText("B"); -// tf.appendText("C"); - -// expect(tf.text).toBe("ABC"); -// }); - -// it("appendText test success case2", () => -// { -// let tf = new TextField(); -// expect(tf.text).toBe(""); - -// // @ts-ignore -// tf.appendText(1); -// // @ts-ignore -// tf.appendText(2); -// // @ts-ignore -// tf.appendText(3); - -// expect(tf.text).toBe("123"); -// }); - -// }); - -// describe("TextField.js replaceText test", () => -// { - -// it("replaceText test success case1", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(0, 0, "ddd"); -// expect(textField.text).toBe("dddaaaccc"); -// }); - -// it("replaceText test success case2", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(1, 2, "ddd"); -// expect(textField.text).toBe("adddaccc"); -// }); - -// it("replaceText test success case3", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(6, 6, "ddd"); -// expect(textField.text).toBe("aaacccddd"); -// }); - -// it("replaceText test success case4", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(6, 5, "ddd"); -// expect(textField.text).toBe("aaaccc"); -// }); - -// it("replaceText test success case5", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(16, 1500, "ddd"); -// expect(textField.text).toBe("aaacccddd"); -// }); - -// it("replaceText test success case6", () => { -// let textField = new TextField(); -// textField.text = "aaaccc"; -// textField.replaceText(1, 0, "ddd"); -// expect(textField.text).toBe("aaaccc"); -// }); - -// }); - -// describe("TextField.js border test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.border).toBe(false); -// }); - -// it("default test case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = null; -// expect(tf.border).toBe(false); -// }); - -// it("default test case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = undefined; -// expect(tf.border).toBe(false); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// tf.border = true; -// expect(tf.border).toBe(true); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = ""; -// expect(tf.border).toBe(false); -// }); - -// it("default test case6", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = "abc"; -// expect(tf.border).toBe(true); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = 0; -// expect(tf.border).toBe(false); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = 1; -// expect(tf.border).toBe(true); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = 500; -// expect(tf.border).toBe(true); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = 50000000000000000; -// expect(tf.border).toBe(true); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = -1; -// expect(tf.border).toBe(true); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = -500; -// expect(tf.border).toBe(true); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = -50000000000000000; -// expect(tf.border).toBe(true); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = { "a":0 }; -// expect(tf.border).toBe(true); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = function a() {}; -// expect(tf.border).toBe(true); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = [1]; -// expect(tf.border).toBe(true); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = [1,2]; -// expect(tf.border).toBe(true); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = {}; -// expect(tf.border).toBe(true); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = { "toString":() => { return 1 } }; -// expect(tf.border).toBe(true); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = { "toString":() => { return "10" } }; -// expect(tf.border).toBe(true); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.border = { "toString":() => { return "1a" } }; -// expect(tf.border).toBe(true); -// }); - -// }); - -// describe("TextField.js multiline test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.multiline).toBe(false); -// }); - -// it("default test case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = null; -// expect(tf.multiline).toBe(false); -// }); - -// it("default test case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = undefined; -// expect(tf.multiline).toBe(false); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// tf.multiline = true; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = ""; -// expect(tf.multiline).toBe(false); -// }); - -// it("default test case6", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = "abc"; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = 0; -// expect(tf.multiline).toBe(false); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = 1; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = 500; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = 50000000000000000; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = -1; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = -500; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = -50000000000000000; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = { "a":0 }; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = function a() {}; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = [1]; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = [1,2]; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = {}; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = { "toString":() => { return 1 } }; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = { "toString":() => { return "10" } }; -// expect(tf.multiline).toBe(true); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.multiline = { "toString":() => { return "1a" } }; -// expect(tf.multiline).toBe(true); -// }); - -// }); - -// describe("TextField.js wordWrap test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.wordWrap).toBe(false); -// }); - -// it("default test case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = null; -// expect(tf.wordWrap).toBe(false); -// }); - -// it("default test case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = undefined; -// expect(tf.wordWrap).toBe(false); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// tf.wordWrap = true; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = ""; -// expect(tf.wordWrap).toBe(false); -// }); - -// it("default test case6", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = "abc"; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = 0; -// expect(tf.wordWrap).toBe(false); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = 1; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = 500; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = 50000000000000000; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = -1; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = -500; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = -50000000000000000; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = { "a":0 }; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = function a() {}; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = [1]; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = [1,2]; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = {}; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = { "toString":() => { return 1 } }; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = { "toString":() => { return "10" } }; -// expect(tf.wordWrap).toBe(true); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.wordWrap = { "toString":() => { return "1a" } }; -// expect(tf.wordWrap).toBe(true); -// }); - -// }); - -// describe("TextField.js autoSize test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.autoSize).toBe("none"); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// tf.autoSize = "center"; -// expect(tf.autoSize).toBe("center"); - -// tf.autoSize = "right"; -// expect(tf.autoSize).toBe("right"); - -// tf.autoSize = "left"; -// expect(tf.autoSize).toBe("left"); -// }); -// }); - -// describe("TextField.js backgroundColor test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.backgroundColor).toBe(16777215); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.backgroundColor = true; -// expect(tf.backgroundColor).toBe(1); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = ""; -// expect(tf.backgroundColor).toBe(0); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = 0; -// expect(tf.backgroundColor).toBe(0); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = 1; -// expect(tf.backgroundColor).toBe(1); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = 500; -// expect(tf.backgroundColor).toBe(500); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = 50000000000000000; -// expect(tf.backgroundColor).toBe(0xffffff); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = -1; -// expect(tf.backgroundColor).toBe(0); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = -500; -// expect(tf.backgroundColor).toBe(0); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// tf.backgroundColor = -50000000000000000; -// expect(tf.backgroundColor).toBe(0); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.backgroundColor = [1]; -// expect(tf.backgroundColor).toBe(1); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.backgroundColor = { "toString":() => { return 1 } }; -// expect(tf.backgroundColor).toBe(1); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.backgroundColor = { "toString":() => { return "10" } }; -// expect(tf.backgroundColor).toBe(10); -// }); - -// }); - -// describe("TextField.js borderColor test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = true; -// expect(tf.borderColor).toBe(1); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// tf.borderColor = ""; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// tf.borderColor = 0; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// tf.borderColor = 1; -// expect(tf.borderColor).toBe(1); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// tf.borderColor = 500; -// expect(tf.borderColor).toBe(500); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// tf.borderColor = 50000000000000000; -// expect(tf.borderColor).toBe(0xffffff); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// tf.borderColor = -1; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// tf.borderColor = -500; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// tf.borderColor = -50000000000000000; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = { "a":0 }; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = function a() {}; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = [1]; -// expect(tf.borderColor).toBe(1); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = [1,2]; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = {}; -// expect(tf.borderColor).toBe(0); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = { "toString":() => { return 1 } }; -// expect(tf.borderColor).toBe(1); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.borderColor = { "toString":() => { return "10" } }; -// expect(tf.borderColor).toBe(10); -// }); - -// }); - -// describe("TextField.js maxChars test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = true; -// expect(tf.maxChars).toBe(1); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = ""; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case6", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = "abc"; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// tf.maxChars = 0; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// tf.maxChars = 1; -// expect(tf.maxChars).toBe(1); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// tf.maxChars = 500; -// expect(tf.maxChars).toBe(500); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// tf.maxChars = 50000000000000000; -// expect(tf.maxChars).toBe(784662528); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// tf.maxChars = -1; -// expect(tf.maxChars).toBe(-1); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// tf.maxChars = -500; -// expect(tf.maxChars).toBe(-500); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// tf.maxChars = -50000000000000000; -// expect(tf.maxChars).toBe(-784662528); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = { "a":0 }; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = function a() {}; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = [1]; -// expect(tf.maxChars).toBe(1); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = [1,2]; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = {}; -// expect(tf.maxChars).toBe(0); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = { "toString":() => { return 1 } }; -// expect(tf.maxChars).toBe(1); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = { "toString":() => { return "10" } }; -// expect(tf.maxChars).toBe(10); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.maxChars = { "toString":() => { return "1a" } }; -// expect(tf.maxChars).toBe(0); -// }); - -// }); - -// describe("TextField.js restrict test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.restrict).toBe(""); -// }); - -// it("default test case2", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = null; -// expect(tf.restrict).toBe("null"); -// }); - -// it("default test case3", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = undefined; -// expect(tf.restrict).toBe("undefined"); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = true; -// expect(tf.restrict).toBe("true"); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// tf.restrict = ""; -// expect(tf.restrict).toBe(""); -// }); - -// it("default test case6", () => -// { -// let tf = new TextField(); -// tf.restrict = "abc"; -// expect(tf.restrict).toBe("abc"); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = 0; -// expect(tf.restrict).toBe("0"); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = 1; -// expect(tf.restrict).toBe("1"); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = 500; -// expect(tf.restrict).toBe("500"); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = 50000000000000000; -// expect(tf.restrict).toBe("50000000000000000"); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = -1; -// expect(tf.restrict).toBe("-1"); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = -500; -// expect(tf.restrict).toBe("-500"); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = -50000000000000000; -// expect(tf.restrict).toBe("-50000000000000000"); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = { "a":0 }; -// expect(tf.restrict).toBe("[object Object]"); -// }); - -// it("default test case15", () => -// { -// let test = () => {}; -// test.toString = () => { return "test_test" }; - -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = test; -// expect(tf.restrict).toBe("test_test"); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = [1]; -// expect(tf.restrict).toBe("1"); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = [1,2]; -// expect(tf.restrict).toBe("1,2"); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = {}; -// expect(tf.restrict).toBe("[object Object]"); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = { "toString":() => { return 1 } }; -// expect(tf.restrict).toBe("1"); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = { "toString":() => { return "10" } }; -// expect(tf.restrict).toBe("10"); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.restrict = { "toString":() => { return "1a" } }; -// expect(tf.restrict).toBe("1a"); -// }); - -// }); - -// // describe("TextField.js text test", () => -// // { -// // -// // it("default test case1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.text).toBe(""); -// // }); -// // -// // it("default test case2", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = true; -// // expect(tf.text).toBe("true"); -// // }); -// // -// // it("default test case3", () => -// // { -// // let tf = new TextField(); -// // tf.text = ""; -// // expect(tf.text).toBe(""); -// // }); -// // -// // it("default test case4", () => -// // { -// // let tf = new TextField(); -// // tf.text = "abc"; -// // expect(tf.text).toBe("abc"); -// // }); -// // -// // it("default test case5", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = 0; -// // expect(tf.text).toBe("0"); -// // }); -// // -// // it("default test case6", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = 1; -// // expect(tf.text).toBe("1"); -// // }); -// // -// // it("default test case7", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = 500; -// // expect(tf.text).toBe("500"); -// // }); -// // -// // it("default test case8", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = 50000000000000000; -// // expect(tf.text).toBe("50000000000000000"); -// // }); -// // -// // it("default test case9", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = -1; -// // expect(tf.text).toBe("-1"); -// // }); -// // -// // it("default test case10", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = -500; -// // expect(tf.text).toBe("-500"); -// // }); -// // -// // it("default test case11", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = -50000000000000000; -// // expect(tf.text).toBe("-50000000000000000"); -// // }); -// // -// // it("default test case12", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = { "a":0 }; -// // expect(tf.text).toBe("[object Object]"); -// // }); -// // -// // it("default test case13", () => -// // { -// // let test = () => {}; -// // test.toString = () => { return "test" }; -// // -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = test; -// // expect(tf.text).toBe("test"); -// // }); -// // -// // it("default test case14", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = [1]; -// // expect(tf.text).toBe("1"); -// // }); -// // -// // it("default test case15", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = [1,2]; -// // expect(tf.text).toBe("1,2"); -// // }); -// // -// // it("default test case16", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = {}; -// // expect(tf.text).toBe("[object Object]"); -// // }); -// // -// // it("default test case17", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = { "toString":() => { return 1 } }; -// // expect(tf.text).toBe("1"); -// // }); -// // -// // it("default test case18", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = { "toString":() => { return "10" } }; -// // expect(tf.text).toBe("10"); -// // }); -// // -// // it("default test case19", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.text = { "toString":() => { return "1a" } }; -// // expect(tf.text).toBe("1a"); -// // }); -// // -// // }); - -// describe("TextField.js textColor test", () => -// { - -// it("default test case1", () => -// { -// let tf = new TextField(); -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case4", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = true; -// expect(tf.textColor).toBe(1); -// }); - -// it("default test case5", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = ""; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case7", () => -// { -// let tf = new TextField(); -// tf.textColor = 0; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case8", () => -// { -// let tf = new TextField(); -// tf.textColor = 1; -// expect(tf.textColor).toBe(1); -// }); - -// it("default test case9", () => -// { -// let tf = new TextField(); -// tf.textColor = 500; -// expect(tf.textColor).toBe(500); -// }); - -// it("default test case10", () => -// { -// let tf = new TextField(); -// tf.textColor = 50000000000000000; -// expect(tf.textColor).toBe(0xffffff); -// }); - -// it("default test case11", () => -// { -// let tf = new TextField(); -// tf.textColor = -1; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case12", () => -// { -// let tf = new TextField(); -// tf.textColor = -500; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case13", () => -// { -// let tf = new TextField(); -// tf.textColor = -50000000000000000; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case14", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = { "a":0 }; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case15", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = function a() {}; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case16", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = [1]; -// expect(tf.textColor).toBe(1); -// }); - -// it("default test case17", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = [1,2]; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case18", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = {}; -// expect(tf.textColor).toBe(0); -// }); - -// it("default test case19", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = { "toString":() => { return 1 } }; -// expect(tf.textColor).toBe(1); -// }); - -// it("default test case20", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = { "toString":() => { return "10" } }; -// expect(tf.textColor).toBe(10); -// }); - -// it("default test case21", () => -// { -// let tf = new TextField(); -// // @ts-ignore -// tf.textColor = { "toString":() => { return "1a" } }; -// expect(tf.textColor).toBe(10); -// }); - -// }); - -// // describe("TextField.js width test", () => -// // { -// // -// // it("default test case1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case2", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = null; -// // expect(tf.width).toBe(0); -// // }); -// // -// // it("default test case3", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = undefined; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case4", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = true; -// // expect(tf.width).toBe(1); -// // }); -// // -// // it("default test case5", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = ""; -// // expect(tf.width).toBe(0); -// // }); -// // -// // it("default test case6", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = "abc"; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case7", () => -// // { -// // let tf = new TextField(); -// // tf.width = 0; -// // expect(tf.width).toBe(0); -// // }); -// // -// // it("default test case8", () => -// // { -// // let tf = new TextField(); -// // tf.width = 1; -// // expect(tf.width).toBe(1); -// // }); -// // -// // it("default test case9", () => -// // { -// // let tf = new TextField(); -// // tf.width = 500; -// // expect(tf.width).toBe(500); -// // }); -// // -// // it("default test case11", () => -// // { -// // let tf = new TextField(); -// // tf.width = -1; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case12", () => -// // { -// // let tf = new TextField(); -// // tf.width = -500; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case13", () => -// // { -// // let tf = new TextField(); -// // tf.width = -50000000000000000; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case14", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = { "a":0 }; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case15", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = function a() {}; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case16", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = [1]; -// // expect(tf.width).toBe(1); -// // }); -// // -// // it("default test case17", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = [1,2]; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case18", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = {}; -// // expect(tf.width).toBe(100); -// // }); -// // -// // it("default test case19", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = { "toString":() => { return 1 } }; -// // expect(tf.width).toBe(1); -// // }); -// // -// // it("default test case20", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = { "toString":() => { return "10" } }; -// // expect(tf.width).toBe(10); -// // }); -// // -// // it("default test case21", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.width = { "toString":() => { return "1a" } }; -// // expect(tf.width).toBe(100); -// // }); -// // -// // }); -// // -// // describe("TextField.js height test", () => -// // { -// // -// // it("default test case1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case2", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = null; -// // expect(tf.height).toBe(0); -// // }); -// // -// // it("default test case3", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = undefined; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case4", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = true; -// // expect(tf.height).toBe(1); -// // }); -// // -// // it("default test case5", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = ""; -// // expect(tf.height).toBe(0); -// // }); -// // -// // it("default test case6", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = "abc"; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case7", () => -// // { -// // let tf = new TextField(); -// // tf.height = 0; -// // expect(tf.height).toBe(0); -// // }); -// // -// // it("default test case8", () => -// // { -// // let tf = new TextField(); -// // tf.height = 1; -// // expect(tf.height).toBe(1); -// // }); -// // -// // it("default test case9", () => -// // { -// // let tf = new TextField(); -// // tf.height = 500; -// // expect(tf.height).toBe(500); -// // }); -// // -// // it("default test case11", () => -// // { -// // let tf = new TextField(); -// // tf.height = -1; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case12", () => -// // { -// // let tf = new TextField(); -// // tf.height = -500; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case13", () => -// // { -// // let tf = new TextField(); -// // tf.height = -50000000000000000; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case14", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = { "a":0 }; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case15", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = function a() {}; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case16", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = [1]; -// // expect(tf.height).toBe(1); -// // }); -// // -// // it("default test case17", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = [1,2]; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case18", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = {}; -// // expect(tf.height).toBe(100); -// // }); -// // -// // it("default test case19", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = { "toString":() => { return 1 } }; -// // expect(tf.height).toBe(1); -// // }); -// // -// // it("default test case20", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = { "toString":() => { return "10" } }; -// // expect(tf.height).toBe(10); -// // }); -// // -// // it("default test case21", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.height = { "toString":() => { return "1a" } }; -// // expect(tf.height).toBe(100); -// // }); -// // -// // }); - -// // describe("TextField.js x test", () => -// // { -// // -// // it("default test case1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case2", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = null; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case3", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = undefined; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case4", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = true; -// // expect(tf.x).toBe(1); -// // }); -// // -// // it("default test case5", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = ""; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case6", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = "abc"; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case7", () => -// // { -// // let tf = new TextField(); -// // tf.x = 0; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case8", () => -// // { -// // let tf = new TextField(); -// // tf.x = 1; -// // expect(tf.x).toBe(1); -// // }); -// // -// // it("default test case9", () => -// // { -// // let tf = new TextField(); -// // tf.x = 500; -// // expect(tf.x).toBe(500); -// // }); -// // -// // it("default test case11", () => -// // { -// // let tf = new TextField(); -// // tf.x = -1; -// // expect(tf.x).toBe(-1); -// // }); -// // -// // it("default test case12", () => -// // { -// // let tf = new TextField(); -// // tf.x = -500; -// // expect(tf.x).toBe(-500); -// // }); -// // -// // it("default test case14", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = { "a":0 }; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case15", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = function a() {}; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case16", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = [1]; -// // expect(tf.x).toBe(1); -// // }); -// // -// // it("default test case17", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = [1,2]; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case18", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = {}; -// // expect(tf.x).toBe(0); -// // }); -// // -// // it("default test case19", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = { "toString":() => { return 1 } }; -// // expect(tf.x).toBe(1); -// // }); -// // -// // it("default test case20", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = { "toString":() => { return "10" } }; -// // expect(tf.x).toBe(10); -// // }); -// // -// // it("default test case21", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.x = { "toString":() => { return "1a" } }; -// // expect(tf.x).toBe(0); -// // }); -// // -// // }); -// // -// // describe("TextField.js y test", () => -// // { -// // -// // it("default test case1", () => -// // { -// // let tf = new TextField(); -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case2", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = null; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case3", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = undefined; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case4", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = true; -// // expect(tf.y).toBe(1); -// // }); -// // -// // it("default test case5", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = ""; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case6", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = "abc"; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case7", () => -// // { -// // let tf = new TextField(); -// // tf.y = 0; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case8", () => -// // { -// // let tf = new TextField(); -// // tf.y = 1; -// // expect(tf.y).toBe(1); -// // }); -// // -// // it("default test case9", () => -// // { -// // let tf = new TextField(); -// // tf.y = 500; -// // expect(tf.y).toBe(500); -// // }); -// // -// // it("default test case11", () => -// // { -// // let tf = new TextField(); -// // tf.y = -1; -// // expect(tf.y).toBe(-1); -// // }); -// // -// // it("default test case12", () => -// // { -// // let tf = new TextField(); -// // tf.y = -500; -// // expect(tf.y).toBe(-500); -// // }); -// // -// // it("default test case14", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = { "a":0 }; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case15", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = function a() {}; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case16", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = [1]; -// // expect(tf.y).toBe(1); -// // }); -// // -// // it("default test case17", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = [1,2]; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case18", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = {}; -// // expect(tf.y).toBe(0); -// // }); -// // -// // it("default test case19", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = { "toString":() => { return 1 } }; -// // expect(tf.y).toBe(1); -// // }); -// // -// // it("default test case20", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = { "toString":() => { return "10" } }; -// // expect(tf.y).toBe(10); -// // }); -// // -// // it("default test case21", () => -// // { -// // let tf = new TextField(); -// // // @ts-ignore -// // tf.y = { "toString":() => { return "1a" } }; -// // expect(tf.y).toBe(0); -// // }); -// // -// // }); - -// // describe("TextField.js scroll test", () => -// // { -// // -// // it("maxScrollV test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.width = 100; -// // tf.height = 20; -// // tf.multiline = true; -// // tf.wordWrap = true; -// // tf.text = "テキストテキストテキストテキスト"; -// // -// // expect(tf.maxScrollV).toBe(2); -// // }); -// // -// // it("maxScrollV test success case2", () => -// // { -// // let tf = new TextField(); -// // -// // tf.width = 100; -// // tf.height = 20; -// // tf.autoSize = "none"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(tf.maxScrollV).toBe(1); -// // }); -// // -// // it("maxScrollV test success case3", () => -// // { -// // let tf = new TextField(); -// // -// // tf.width = 100; -// // tf.height = 20; -// // tf.autoSize = "none"; -// // tf.type = "input"; -// // tf.multiline = true; -// // tf.text = "\n"; -// // -// // expect(tf.maxScrollV).toBe(2); -// // }); -// // -// // it("maxScrollV test success case4", () => -// // { -// // let tf = new TextField(); -// // -// // tf.width = 100; -// // tf.height = 20; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // expect(tf.maxScrollV).toBe(1); -// // }); -// // -// // it("maxScrollV test success case6", () => -// // { -// // let tf = new TextField(); -// // -// // tf.width = 100; -// // tf.height = 20; -// // tf.autoSize = "left"; -// // tf.type = "input"; -// // tf.text = "\n"; -// // -// // tf.height; -// // -// // expect(tf.maxScrollV).toBe(1); -// // }); -// // -// // }); - -// // jestだとHTMLのElementが利用できなかったのでスキップ -// // describe("TextField.js htmlText test", () => -// // { -// // -// // it("CR and LF test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa\nbbb

"; -// // -// // expect(tf.numLines).toBe(3); -// // }); -// // -// // it("CR and LF test success case2", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa\rbbb

"; -// // -// // expect(tf.numLines).toBe(3); -// // }); -// // -// // it("CR and LF test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa\r\nbbb

"; -// // -// // expect(tf.numLines).toBe(4); -// // }); -// // -// // it("CR and LF test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa\n\rbbb

"; -// // -// // expect(tf.numLines).toBe(4); -// // }); -// // -// // it("numLines test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(2); -// // }); -// // -// // it("numLines test success case2", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(1); -// // }); -// // -// // it("numLines test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(3); -// // }); -// // -// // it("numLines test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(1); -// // }); -// // -// // it("numLines test success case5", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa
bbb

"; -// // -// // expect(tf.numLines).toBe(3); -// // }); -// // -// // it("numLines test success case6", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

aaa
bbb

"; -// // -// // expect(tf.numLines).toBe(1); -// // }); -// // -// // it("numLines test success case7", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

aaa\n

"; -// // -// // expect(tf.numLines).toBe(3); -// // }); -// // -// // it("numLines test success case8", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

aaa\n

"; -// // -// // expect(tf.numLines).toBe(1); -// // }); -// // -// // it("numLines test success case9", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = ""; -// // -// // expect(tf.numLines).toBe(1); -// // }); -// // -// // it("numLines test success case10", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

"; -// // -// // expect(tf.numLines).toBe(2); -// // }); -// // -// // it("_$parseTag test success case1", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(1)).toBe(""); -// // }); -// // -// // it("_$parseTag test success case2", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case3", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case4", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case5", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case6", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

b"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(1)).toBe("b"); -// // }); -// // -// // it("_$parseTag test success case7", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

b

"; -// // -// // expect(tf.numLines).toBe(3); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(1)).toBe("b"); -// // expect(tf.getLineText(2)).toBe(""); -// // }); -// // -// // it("_$parseTag test success case8", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

b

c

"; -// // -// // expect(tf.numLines).toBe(3); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(1)).toBe("bc"); -// // }); -// // -// // it("_$parseTag test success case9", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "abc"; -// // -// // expect(tf.numLines).toBe(1); -// // expect(tf.getLineText(0)).toBe("abc"); -// // }); -// // -// // it("_$parseTag test success case10", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "a\r\n

b

"; -// // -// // expect(tf.numLines).toBe(4); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(2)).toBe("b"); -// // }); -// // -// // it("_$parseTag test success case11", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "a\r\n

b

"; -// // -// // expect(tf.numLines).toBe(4); -// // expect(tf.getLineText(0)).toBe("a"); -// // expect(tf.getLineText(2)).toBe("b"); -// // }); -// // -// // it("_$parseTag test success case12", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "a

b

c"; -// // -// // expect(tf.numLines).toBe(2); -// // expect(tf.getLineText(0)).toBe("ab"); -// // expect(tf.getLineText(1)).toBe("c"); -// // }); -// // -// // it("_$parseTag test success case13", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = true; -// // tf.htmlText = "

a

"; -// // -// // expect(tf.numLines).toBe(3); -// // expect(tf.textHeight).toBe(38); -// // expect(tf.getLineText(1)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case14", () => -// // { -// // let tf = new TextField(); -// // tf.multiline = false; -// // tf.htmlText = "

a

b

"; -// // -// // expect(tf.numLines).toBe(1); -// // expect(Math.round(tf.textHeight)).toBe(14); -// // expect(tf.getLineText(0)).toBe("ab"); -// // }); -// // -// // it("_$parseTag test success case15", () => -// // { -// // let tf = new TextField(); -// // tf.width = 40; -// // tf.wordWrap = true; -// // tf.htmlText = "

aaaaaaaaa

"; -// // -// // expect(tf.numLines).toBe(2); -// // }); -// // -// // it("_$parseTag test success case16", () => -// // { -// // let tf = new TextField(); -// // tf.width = 40; -// // tf.wordWrap = true; -// // tf.htmlText = "

aaaaaaaaa

bbbbbbbbbbbb

"; -// // -// // expect(tf.numLines).toBe(4); -// // }); -// // -// // it("_$parseTag test success case17", () => -// // { -// // let tf = new TextField(); -// // tf.width = 40; -// // tf.wordWrap = true; -// // tf.multiline = true; -// // tf.htmlText = "

aaaaaaaaa

bbbbbbbbbbb
bbbbbb

"; -// // -// // expect(tf.numLines).toBe(6); -// // }); -// // -// // it("_$parseTag test success case18", () => -// // { -// // let tf = new TextField(); -// // tf.htmlText = "a"; -// // -// // expect(tf.numLines).toBe(1); -// // expect(tf.getLineText(0)).toBe("a"); -// // }); -// // -// // it("_$parseTag test success case19", () => -// // { -// // let tf = new TextField(); -// // tf.htmlText = "< test>ab"; -// // -// // expect(tf.text).toBe(""); -// // }); -// // -// // it("_$parseTag test success case20", () => -// // { -// // let tf = new TextField(); -// // tf.htmlText = "<>a"; -// // -// // expect(tf.text).toBe(""); -// // }); -// // -// // it("_$parseTag test success case21", () => -// // { -// // let tf = new TextField(); -// // tf.htmlText = "あいうえお、< br/>b"; -// // -// // expect(tf.text).toBe("あいうえお、"); -// // }); -// // -// // }); diff --git a/__tests__/next2d/text/TextFormatTest.ts b/__tests__/next2d/text/TextFormatTest.ts deleted file mode 100644 index 6b066faa..00000000 --- a/__tests__/next2d/text/TextFormatTest.ts +++ /dev/null @@ -1,952 +0,0 @@ -import { TextFormat } from "../../../packages/text/src/TextFormat"; - -describe("TextFormat.js toString test", () => -{ - it("toString test success", () => - { - let object = new TextFormat(); - expect(object.toString()).toBe("[object TextFormat]"); - }); - -}); - -describe("TextFormat.js static toString test", () => -{ - - it("static toString test", () => - { - expect(TextFormat.toString()).toBe("[class TextFormat]"); - }); - -}); - -describe("TextFormat.js namespace test", () => -{ - - it("namespace test public", () => - { - const object = new TextFormat(); - expect(object.namespace).toBe("next2d.text.TextFormat"); - }); - - it("namespace test static", () => - { - expect(TextFormat.namespace).toBe("next2d.text.TextFormat"); - }); - -}); - -describe("TextFormat.js property test", () => -{ - - // default - it("default test success", () => - { - let textFormat = new TextFormat(); - expect(textFormat.align).toBe(null); - expect(textFormat.bold).toBe(null); - expect(textFormat.color).toBe(null); - expect(textFormat.font).toBe(null); - expect(textFormat.italic).toBe(null); - expect(textFormat.leading).toBe(null); - expect(textFormat.leftMargin).toBe(null); - expect(textFormat.letterSpacing).toBe(0); - expect(textFormat.rightMargin).toBe(null); - expect(textFormat.underline).toBe(null); - expect(textFormat.size).toBe(null); - }); - - // align - it("align test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.align = "center"; - expect(textFormat.align).toBe("center"); - }); - - it("align test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.align = null; - expect(textFormat.align).toBe(null); - }); - - // bold - it("bold test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.bold = true; - expect(textFormat.bold).toBe(true); - }); - - it("bold test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.bold = null; - expect(textFormat.bold).toBe(null); - }); - - // color - it("color test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.color = 0xffffff; - expect(textFormat.color).toBe(0xffffff); - }); - - it("color test valid case1", function () - { - let textFormat = new TextFormat(); - // @ts-ignore - textFormat.color = "0xff0000"; - expect(textFormat.color).toBe(0xff0000); - }); - - it("color test valid case2", function () - { - let textFormat = new TextFormat(); - // @ts-ignore - textFormat.color = "test"; - expect(textFormat.color).toBe(0x000000); - }); - - // font - it("font test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.font = "ゴシック"; - expect(textFormat.font).toBe("ゴシック"); - }); - - // italic - it("italic test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.italic = true; - expect(textFormat.italic).toBe(true); - }); - - it("italic test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.italic = null; - expect(textFormat.italic).toBe(null); - }); - - // leading - it("leading test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.leading = 10; - expect(textFormat.leading).toBe(10); - }); - - it("leading test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.leading = null; - expect(textFormat.leading).toBe(null); - }); - - // leftMargin - it("leftMargin test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.leftMargin = 10; - expect(textFormat.leftMargin).toBe(10); - }); - - it("leftMargin test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.leftMargin = null; - expect(textFormat.leftMargin).toBe(null); - }); - - // letterSpacing - it("letterSpacing test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.letterSpacing = 10; - expect(textFormat.letterSpacing).toBe(10); - }); - - it("letterSpacing test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.letterSpacing = null; - expect(textFormat.letterSpacing).toBe(null); - }); - - // rightMargin - it("rightMargin test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.rightMargin = 10; - expect(textFormat.rightMargin).toBe(10); - }); - - it("rightMargin test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.rightMargin = null; - expect(textFormat.rightMargin).toBe(null); - }); - - // underline - it("underline test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.underline = true; - expect(textFormat.underline).toBe(true); - }); - - it("underline test valid case1", function () - { - let textFormat = new TextFormat(); - textFormat.underline = null; - expect(textFormat.underline).toBe(null); - }); - - // size - it("size test success case1", function () - { - let textFormat = new TextFormat(); - textFormat.size = 100; - expect(textFormat.size).toBe(100); - }); - - it("size test success case2", function () - { - let textFormat = new TextFormat(); - // @ts-ignore - textFormat.size = "1000"; - expect(textFormat.size).toBe(1000); - }); - - it("size test success case3", function () - { - let textFormat = new TextFormat(); - textFormat.size = null; - expect(textFormat.size).toBe(null); - }); - - it("size test valid case1", function () - { - let textFormat = new TextFormat(); - // @ts-ignore - textFormat.size = "abc"; - expect(textFormat.size).toBe(0); - }); - -}); - -describe("TextFormat.js bold test", () => -{ - - it("default test case1", () => - { - let tf = new TextFormat(); - expect(tf.bold).toBe(null); - }); - - it("default test case2", () => - { - let tf = new TextFormat(); - tf.bold = null; - expect(tf.bold).toBe(null); - }); - - it("default test case3", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = undefined; - expect(tf.bold).toBe(false); - }); - - it("default test case4", () => - { - let tf = new TextFormat(); - tf.bold = true; - expect(tf.bold).toBe(true); - }); - - it("default test case5", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = ""; - expect(tf.bold).toBe(false); - }); - - it("default test case6", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = "abc"; - expect(tf.bold).toBe(true); - }); - - it("default test case7", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = 0; - expect(tf.bold).toBe(false); - }); - - it("default test case8", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = 1; - expect(tf.bold).toBe(true); - }); - - it("default test case9", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = 500; - expect(tf.bold).toBe(true); - }); - - it("default test case10", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = 50000000000000000; - expect(tf.bold).toBe(true); - }); - - it("default test case11", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = -1; - expect(tf.bold).toBe(true); - }); - - it("default test case12", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = -500; - expect(tf.bold).toBe(true); - }); - - it("default test case13", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = -50000000000000000; - expect(tf.bold).toBe(true); - }); - - it("default test case14", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = { "a":0 }; - expect(tf.bold).toBe(true); - }); - - it("default test case15", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = function a() {}; - expect(tf.bold).toBe(true); - }); - - it("default test case16", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = [1]; - expect(tf.bold).toBe(true); - }); - - it("default test case17", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = [1,2]; - expect(tf.bold).toBe(true); - }); - - it("default test case18", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = {}; - expect(tf.bold).toBe(true); - }); - - it("default test case19", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = { "toString":function () { return 1 } }; - expect(tf.bold).toBe(true); - }); - - it("default test case20", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = { "toString":function () { return "10" } }; - expect(tf.bold).toBe(true); - }); - - it("default test case21", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.bold = { "toString":function () { return "1a" } }; - expect(tf.bold).toBe(true); - }); - -}); - -describe("TextFormat.js italic test", () => -{ - - it("default test case1", () => - { - let tf = new TextFormat(); - expect(tf.italic).toBe(null); - }); - - it("default test case2", () => - { - let tf = new TextFormat(); - tf.italic = null; - expect(tf.italic).toBe(null); - }); - - it("default test case3", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = undefined; - expect(tf.italic).toBe(false); - }); - - it("default test case4", () => - { - let tf = new TextFormat(); - tf.italic = true; - expect(tf.italic).toBe(true); - }); - - it("default test case5", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = ""; - expect(tf.italic).toBe(false); - }); - - it("default test case6", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = "abc"; - expect(tf.italic).toBe(true); - }); - - it("default test case7", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = 0; - expect(tf.italic).toBe(false); - }); - - it("default test case8", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = 1; - expect(tf.italic).toBe(true); - }); - - it("default test case9", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = 500; - expect(tf.italic).toBe(true); - }); - - it("default test case10", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = 50000000000000000; - expect(tf.italic).toBe(true); - }); - - it("default test case11", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = -1; - expect(tf.italic).toBe(true); - }); - - it("default test case12", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = -500; - expect(tf.italic).toBe(true); - }); - - it("default test case13", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = -50000000000000000; - expect(tf.italic).toBe(true); - }); - - it("default test case14", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = { "a":0 }; - expect(tf.italic).toBe(true); - }); - - it("default test case15", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = function a() {}; - expect(tf.italic).toBe(true); - }); - - it("default test case16", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = [1]; - expect(tf.italic).toBe(true); - }); - - it("default test case17", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = [1,2]; - expect(tf.italic).toBe(true); - }); - - it("default test case18", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = {}; - expect(tf.italic).toBe(true); - }); - - it("default test case19", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = { "toString":function () { return 1 } }; - expect(tf.italic).toBe(true); - }); - - it("default test case20", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = { "toString":function () { return "10" } }; - expect(tf.italic).toBe(true); - }); - - it("default test case21", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.italic = { "toString":function () { return "1a" } }; - expect(tf.italic).toBe(true); - }); - -}); - -describe("TextFormat.js underline test", () => -{ - - it("default test case1", () => - { - let tf = new TextFormat(); - expect(tf.underline).toBe(null); - }); - - it("default test case2", () => - { - let tf = new TextFormat(); - tf.underline = null; - expect(tf.underline).toBe(null); - }); - - it("default test case3", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = undefined; - expect(tf.underline).toBe(false); - }); - - it("default test case4", () => - { - let tf = new TextFormat(); - tf.underline = true; - expect(tf.underline).toBe(true); - }); - - it("default test case5", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = ""; - expect(tf.underline).toBe(false); - }); - - it("default test case6", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = "abc"; - expect(tf.underline).toBe(true); - }); - - it("default test case7", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = 0; - expect(tf.underline).toBe(false); - }); - - it("default test case8", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = 1; - expect(tf.underline).toBe(true); - }); - - it("default test case9", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = 500; - expect(tf.underline).toBe(true); - }); - - it("default test case10", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = 50000000000000000; - expect(tf.underline).toBe(true); - }); - - it("default test case11", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = -1; - expect(tf.underline).toBe(true); - }); - - it("default test case12", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = -500; - expect(tf.underline).toBe(true); - }); - - it("default test case13", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = -50000000000000000; - expect(tf.underline).toBe(true); - }); - - it("default test case14", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = { "a":0 }; - expect(tf.underline).toBe(true); - }); - - it("default test case15", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = function a() {}; - expect(tf.underline).toBe(true); - }); - - it("default test case16", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = [1]; - expect(tf.underline).toBe(true); - }); - - it("default test case17", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = [1,2]; - expect(tf.underline).toBe(true); - }); - - it("default test case18", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = {}; - expect(tf.underline).toBe(true); - }); - - it("default test case19", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = { "toString":function () { return 1 } }; - expect(tf.underline).toBe(true); - }); - - it("default test case20", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = { "toString":function () { return "10" } }; - expect(tf.underline).toBe(true); - }); - - it("default test case21", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.underline = { "toString":function () { return "1a" } }; - expect(tf.underline).toBe(true); - }); - -}); - -describe("TextFormat.js align test", () => -{ - - it("default test case1", () => - { - let tf = new TextFormat(); - expect(tf.align).toBe(null); - }); - - it("default test case2", () => - { - let tf = new TextFormat(); - tf.align = "right"; - expect(tf.align).toBe("right"); - }); - - it("default test case3", () => - { - let tf = new TextFormat(); - tf.align = "left"; - expect(tf.align).toBe("left"); - }); - - it("default test case4", () => - { - let tf = new TextFormat(); - tf.align = "center"; - expect(tf.align).toBe("center"); - }); - -}); - -describe("TextFormat.js font test", () => -{ - - it("default test case1", () => - { - let tf = new TextFormat(); - expect(tf.font).toBe(null); - }); - - it("default test case2", () => - { - let tf = new TextFormat(); - tf.font = null; - expect(tf.font).toBe(null); - }); - - it("default test case3", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = undefined; - expect(tf.font).toBe("undefined"); - }); - - it("default test case4", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = true; - expect(tf.font).toBe("true"); - }); - - it("default test case5", () => - { - let tf = new TextFormat(); - tf.font = ""; - expect(tf.font).toBe(""); - }); - - it("default test case6", () => - { - let tf = new TextFormat(); - tf.font = "abc"; - expect(tf.font).toBe("abc"); - }); - - it("default test case7", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = 0; - expect(tf.font).toBe("0"); - }); - - it("default test case8", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = 1; - expect(tf.font).toBe("1"); - }); - - it("default test case9", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = 500; - expect(tf.font).toBe("500"); - }); - - it("default test case10", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = 50000000000000000; - expect(tf.font).toBe("50000000000000000"); - }); - - it("default test case11", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = -1; - expect(tf.font).toBe("-1"); - }); - - it("default test case12", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = -500; - expect(tf.font).toBe("-500"); - }); - - it("default test case13", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = -50000000000000000; - expect(tf.font).toBe("-50000000000000000"); - }); - - it("default test case14", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = { "a":0 }; - expect(tf.font).toBe("[object Object]"); - }); - - it("default test case15", () => - { - let test = function () {}; - test.toString = function () { return "test" }; - - let tf = new TextFormat(); - // @ts-ignore - tf.font = test; - expect(tf.font).toBe("test"); - }); - - it("default test case16", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = [1]; - expect(tf.font).toBe("1"); - }); - - it("default test case17", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = [1,2]; - expect(tf.font).toBe("1,2"); - }); - - it("default test case18", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = {}; - expect(tf.font).toBe("[object Object]"); - }); - - it("default test case19", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = { "toString":function () { return 1 } }; - expect(tf.font).toBe("1"); - }); - - it("default test case20", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = { "toString":function () { return "10" } }; - expect(tf.font).toBe("10"); - }); - - it("default test case21", () => - { - let tf = new TextFormat(); - // @ts-ignore - tf.font = { "toString":function () { return "1a" } }; - expect(tf.font).toBe("1a"); - }); - -}); \ No newline at end of file diff --git a/__tests__/next2d/ui/EasingTest.ts b/__tests__/next2d/ui/EasingTest.ts deleted file mode 100644 index cce330c4..00000000 --- a/__tests__/next2d/ui/EasingTest.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { Easing } from "../../../packages/ui/src/Easing"; - -describe("Easing.js toString test", function() -{ - it("toString test success", function() - { - let object = new Easing(); - expect(object.toString()).toBe("[object Easing]"); - }); - -}); - -describe("Easing.js static toString test", function() -{ - - it("static toString test", function() - { - expect(Easing.toString()).toBe("[class Easing]"); - }); - -}); - -describe("Easing.js namespace test", function() -{ - - it("namespace test public", function() - { - expect(new Easing().namespace).toBe("next2d.ui.Easing"); - }); - - it("namespace test static", function() - { - expect(Easing.namespace).toBe("next2d.ui.Easing"); - }); - -}); - -describe("Easing.js method test", function() -{ - it("linear method test", function() - { - expect(Easing.linear(0.1, 0.5, 0.5, 1)).toBe(0.55); - }); - - it("inQuad method test", function() - { - expect(Easing.inQuad(0.1, 0.5, 0.5, 1)).toBe(0.505); - }); - - it("outQuad method test", function() - { - expect(Easing.outQuad(0.1, 0.5, 0.5, 1)).toBe(0.595); - }); - - it("inOutQuad method test", function() - { - expect(Easing.inOutQuad(0.1, 0.5, 0.5, 1)).toBe(0.51); - }); - - it("inCubic method test", function() - { - expect(Easing.inCubic(0.1, 0.5, 0.5, 1)).toBe(0.5005); - }); - - it("outCubic method test", function() - { - expect(Easing.outCubic(0.1, 0.5, 0.5, 1)).toBe(0.6355); - }); - - it("inOutCubic method test", function() - { - expect(Easing.inOutCubic(0.1, 0.5, 0.5, 1)).toBe(0.502); - }); - - it("inQuart method test", function() - { - expect(Easing.inQuart(0.1, 0.5, 0.5, 1)).toBe(0.50005); - }); - - it("outQuart method test", function() - { - expect(Easing.outQuart(0.1, 0.5, 0.5, 1)).toBe(0.6719499999999999); - }); - - it("inOutQuart method test", function() - { - expect(Easing.inOutQuart(0.1, 0.5, 0.5, 1)).toBe(0.5004); - }); - - it("inQuint method test", function() - { - expect(Easing.inQuint(0.1, 0.5, 0.5, 1)).toBe(0.500005); - }); - - it("outQuint method test", function() - { - expect(Easing.outQuint(0.1, 0.5, 0.5, 1)).toBe(0.7047549999999999); - }); - - it("inOutQuint method test", function() - { - expect(Easing.inOutQuint(0.1, 0.5, 0.5, 1)).toBe(0.50008); - }); - - it("inSine method test", function() - { - expect(Easing.inSine(0.1, 0.5, 0.5, 1)).toBe(0.5061558297024311); - }); - - it("outSine method test", function() - { - expect(Easing.outSine(0.1, 0.5, 0.5, 1)).toBe(0.5782172325201155); - }); - - it("inOutSine method test", function() - { - expect(Easing.inOutSine(0.1, 0.5, 0.5, 1)).toBe(0.5122358709262116); - }); - - it("inExpo method test", function() - { - expect(Easing.inExpo(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); - }); - - it("outExpo method test", function() - { - expect(Easing.outExpo(0.1, 0.5, 0.5, 1)).toBe(0.75); - }); - - it("inOutExpo method test", function() - { - expect(Easing.inOutExpo(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); - }); - - it("inCirc method test", function() - { - expect(Easing.inCirc(0.1, 0.5, 0.5, 1)).toBe(0.5025062814466901); - }); - - it("outCirc method test", function() - { - expect(Easing.outCirc(0.1, 0.5, 0.5, 1)).toBe(0.7179449471770336); - }); - - it("inOutCirc method test", function() - { - expect(Easing.inOutCirc(0.1, 0.5, 0.5, 1)).toBe(0.5003126955570227); - }); - - it("inBack method test", function() - { - expect(Easing.inBack(0.1, 0.5, 0.5, 1)).toBe(0.49284289); - }); - - it("outBack method test", function() - { - expect(Easing.outBack(0.1, 0.5, 0.5, 1)).toBe(0.7044139899999999); - }); - - it("inOutBack method test", function() - { - expect(Easing.inOutBack(0.1, 0.5, 0.5, 1)).toBe(0.481240724); - }); - - it("inElastic method test", function() - { - expect(Easing.inElastic(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); - }); - - it("outElastic method test", function() - { - expect(Easing.outElastic(0.1, 0.5, 0.5, 1)).toBe(1.125); - }); - - it("inOutElastic method test", function() - { - expect(Easing.inOutElastic(0.1, 0.5, 0.5, 1)).toBe(0.5001695782985028); - }); - - it("outBounce method test", function() - { - expect(Easing.outBounce(0.1, 0.5, 0.5, 1)).toBe(0.5378125); - }); - - it("inBounce method test", function() - { - expect(Easing.inBounce(0.1, 0.5, 0.5, 1)).toBe(0.5059375); - }); - - it("inOutBounce method test", function() - { - expect(Easing.inOutBounce(0.1, 0.5, 0.5, 1)).toBe(0.515); - }); - -}); - diff --git a/__tests__/util/CacheStoreTest.ts b/__tests__/util/CacheStoreTest.ts deleted file mode 100644 index e191bdfa..00000000 --- a/__tests__/util/CacheStoreTest.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CacheStore } from "../../packages/share/src/CacheStore"; - -describe("CacheStore.js packages test", () => -{ - - it("generateKeys test case1", () => - { - const cacheStore = new CacheStore(); - const color = new Float32Array([1,1,1,1,0,0,0,10]); - expect(cacheStore.generateKeys(1, [1, 1], color)) - .toEqual(["1", "_1492115515"]); - - expect(cacheStore.generateKeys(10, [1.2242, 2.098], color)) - .toEqual(["10", "_-103490129"]); - }); - - it("get set has remove test case1", () => - { - const cacheStore = new CacheStore(); - const color = new Float32Array([1,1,1,1,0,0,0,10]); - - const keys = cacheStore.generateKeys(1, [1, 1], color); - expect(cacheStore.has(keys)).toBe(false); - - cacheStore.set(keys, "sample"); - expect(cacheStore.has(keys)).toBe(true); - - expect(cacheStore.get(keys)).toBe("sample"); - - cacheStore.set(keys, null); - expect(cacheStore.has(keys)).toBe(false); - }); -}); \ No newline at end of file diff --git a/__tests__/webgl/BezierConverterTest.ts b/__tests__/webgl/BezierConverterTest.ts deleted file mode 100644 index e966a6a1..00000000 --- a/__tests__/webgl/BezierConverterTest.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { BezierConverter } from "../../packages/webgl/src/BezierConverter"; - -describe("BezierConverter.js test", () => -{ - // 通常ケース - it("cubicToQuad case 1-1", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 4000, 2000, - 6000, 8000, - 0, 10000 - ); - - const actual = [ - 750, 375, 1394.53125, 921.875, - 2039.0625, 1468.75, 2531.25, 2125, - 3023.4375, 2781.25, 3339.84375, 3515.625, - 3656.25, 4250, 3750, 5000, - 3843.75, 5750, 3691.40625, 6484.375, - 3539.0625, 7218.75, 3093.75, 7875, - 2648.4375, 8531.25, 1886.71875, 9078.125, - 1125, 9625, 0, 10000 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - it("cubicToQuad case 1-2", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 10000, 0, - 10000, 10000, - 0, 10000 - ); - - const actual = [ - 1875, 0, 3281.25, 429.6875, - 4687.5, 859.375, 5625, 1562.5, - 6562.5, 2265.625, 7031.25, 3164.0625, - 7500, 4062.5, 7500, 5000, - 7500, 5937.5, 7031.25, 6835.9375, - 6562.5, 7734.375, 5625, 8437.5, - 4687.5, 9140.625, 3281.25, 9570.3125, - 1875, 10000, 0, 10000 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - it("cubicToQuad case 1-3", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 5000, 15000, - 5000, -5000, - 0, 10000 - ); - - const actual = [ - 937.5, 2812.5, 1640.625, 4121.09375, - 2343.75, 5429.6875, 2812.5, 5781.25, - 3281.25, 6132.8125, 3515.625, 5800.78125, - 3750, 5468.75, 3750, 5000, - 3750, 4531.25, 3515.625, 4199.21875, - 3281.25, 3867.1875, 2812.5, 4218.75, - 2343.75, 4570.3125, 1640.625, 5878.90625, - 937.5, 7187.5, 0, 10000 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - // 全て0 - it("cubicToQuad case 2", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 0, 0, - 0, 0, - 0, 0 - ); - - const actual = [ - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - // 直線 - it("cubicToQuad case 3", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 2000, 2000, - 4000, 4000, - 6000, 6000 - ); - - const actual = [ - 375, 375, 750, 750, - 1125, 1125, 1500, 1500, - 1875, 1875, 2250, 2250, - 2625, 2625, 3000, 3000, - 3375, 3375, 3750, 3750, - 4125, 4125, 4500, 4500, - 4875, 4875, 5250, 5250, - 5625, 5625, 6000, 6000 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - // 始点と制御点1が同じ、かつ、終点と制御点2が同じ - it("cubicToQuad case 4", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 0, 0, - 10000, 10000, - 10000, 10000 - ); - - const actual = [ - 0, 0, 429.6875, 429.6875, - 859.375, 859.375, 1562.5, 1562.5, - 2265.625, 2265.625, 3164.0625, 3164.0625, - 4062.5, 4062.5, 5000, 5000, - 5937.5, 5937.5, 6835.9375, 6835.9375, - 7734.375, 7734.375, 8437.5, 8437.5, - 9140.625, 9140.625, 9570.3125, 9570.3125, - 10000, 10000, 10000, 10000 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); - - // 制御点1と制御点2が同じ - it("cubicToQuad case 5", () => - { - const bezierConverter = new BezierConverter(); - - bezierConverter.cubicToQuad( - 0, 0, - 5000, 5000, - 5000, 5000, - 10000, 0 - ); - - const actual = [ - 937.5, 937.5, 1660.15625, 1640.625, - 2382.8125, 2343.75, 2968.75, 2812.5, - 3554.6875, 3281.25, 4042.96875, 3515.625, - 4531.25, 3750, 5000, 3750, - 5468.75, 3750, 5957.03125, 3515.625, - 6445.3125, 3281.25, 7031.25, 2812.5, - 7617.1875, 2343.75, 8339.84375, 1640.625, - 9062.5, 937.5, 10000, 0 - ]; - expect(Array.from(bezierConverter._$bezierConverterBuffer)).toEqual(actual); - }); -}); \ No newline at end of file diff --git a/__tests__/webgl/CanvasToWebGLContextPathTest.ts b/__tests__/webgl/CanvasToWebGLContextPathTest.ts deleted file mode 100644 index c0a6c2a4..00000000 --- a/__tests__/webgl/CanvasToWebGLContextPathTest.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { CanvasToWebGLContextPath } from "../../packages/webgl/src/CanvasToWebGLContextPath"; - -describe("CanvasToWebGLContextPath.js test", () => -{ - it("case 1", function () - { - const path = new CanvasToWebGLContextPath(); - - expect(path.vertices).toEqual([]); - }); - - it("case 2-1", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.moveTo(100, 100); - path.lineTo(100, 200); - path.close(); - - const actual = [ - [ - 100, 100, false, - 100, 200, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 2-2", function () - { - const path = new CanvasToWebGLContextPath(); - path.moveTo(100, 100); - path.lineTo(100, 200); - path.close(); - - const actual = [ - [ - 100, 100, false, - 100, 200, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 2-3", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.moveTo(100, 100); - path.lineTo(100, 200); - - const actual = [ - [ - 100, 100, false, - 100, 200, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 2-4", function () - { - const path = new CanvasToWebGLContextPath(); - path.moveTo(100, 100); - - expect(path.vertices).toEqual([]); - }); - - it("case 2-5", function () - { - const path = new CanvasToWebGLContextPath(); - path.lineTo(100, 200); - - const actual = [ - [ - 0, 0, false, - 100, 200, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 2-6", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.moveTo(100, 100); - path.lineTo(100, 200); - path.moveTo(100, 100); - path.lineTo(200, 100); - path.close(); - - const actual = [ - [ - 100, 100, false, - 100, 200, false - ], - [ - 100, 100, false, - 200, 100, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 3-1", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.moveTo(100, 100); - path.lineTo(100, 200); - path.lineTo(200, 200); - path.close(); - - const actual = [ - [ - 100, 100, false, - 100, 200, false, - 200, 200, false, - 100, 100, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 4", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.quadTo(100, 0, 100, 100); - path.close(); - - const actual = [ - [ - 0, 0, false, - 100, 0, true, - 100, 100, false, - 0, 0, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 5", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.cubicTo(50, 0, 100, 50, 100, 100); - path.close(); - - const actual = [ - [ - 0, 0, false, - 9.375, 0, true, - 18.65234375, 2.24609375, false, - 27.9296875, 4.4921875, true, - 36.71875, 8.59375, false, - 45.5078125, 12.6953125, true, - 53.61328125, 18.45703125, false, - 61.71875, 24.21875, true, - 68.75, 31.25, false, - 75.78125, 38.28125, true, - 81.54296875, 46.38671875, false, - 87.3046875, 54.4921875, true, - 91.40625, 63.28125, false, - 95.5078125, 72.0703125, true, - 97.75390625, 81.34765625, false, - 100, 90.625, true, - 100, 100, false, - 0, 0, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); - - it("case 6", function () - { - const path = new CanvasToWebGLContextPath(); - path.begin(); - path.drawCircle(50, 50, 50); - path.close(); - - const actual = [ - [ - 0, 0, false, - 18.75, 14.552669525146484, true, - 31.99199104309082, 26.580650329589844, false, - 45.23398208618164, 38.6086311340332, true, - 53.88325119018555, 48.368507385253906, false, - 62.53252029418945, 58.128379821777344, true, - 67.04672241210938, 65.74844360351562, false, - 71.56092071533203, 73.3685073852539, true, - 72.85533905029297, 79.10533905029297, false, - 74.1497573852539, 84.84217071533203, true, - 72.68203735351562, 88.82406616210938, false, - 71.21432495117188, 92.80596160888672, true, - 67.8997573852539, 95.28950500488281, false, - 64.58518981933594, 97.77304077148438, true, - 59.881431579589844, 98.88652038574219, false, - 55.17766571044922, 100, true, - 50, 100, false, - 44.82233428955078, 100, true, - 39.923255920410156, 98.98417663574219, false, - 35.0241813659668, 97.96835327148438, true, - 30.537744522094727, 96.07075500488281, false, - 26.051305770874023, 94.17314910888672, true, - 22.044525146484375, 91.46078491210938, false, - 18.037742614746094, 88.74842071533203, true, - 14.644660949707031, 85.35533905029297, false, - 11.251578330993652, 81.9622573852539, true, - 8.539215087890625, 77.95547485351562, false, - 5.8268513679504395, 73.94869995117188, true, - 3.9292478561401367, 69.4622573852539, false, - 2.031644582748413, 64.97581481933594, true, - 1.0158222913742065, 60.076744079589844, false, - 0, 55.17766571044922, true, - 0, 50, false, - 0, 44.82233428955078, true, - 1.0158222913742065, 39.923255920410156, false, - 2.031644582748413, 35.0241813659668, true, - 3.9292478561401367, 30.537744522094727, false, - 5.8268513679504395, 26.051305770874023, true, - 8.539215087890625, 22.044525146484375, false, - 11.251578330993652, 18.037742614746094, true, - 14.644660949707031, 14.644660949707031, false, - 18.037742614746094, 11.251578330993652, true, - 22.044525146484375, 8.539215087890625, false, - 26.051305770874023, 5.8268513679504395, true, - 30.537744522094727, 3.9292478561401367, false, - 35.0241813659668, 2.031644582748413, true, - 39.923255920410156, 1.0158222913742065, false, - 44.82233428955078, 0, true, - 50, 0, false, - 55.17766571044922, 0, true, - 60.076744079589844, 1.0158222913742065, false, - 64.97581481933594, 2.031644582748413, true, - 69.4622573852539, 3.9292478561401367, false, - 73.94869995117188, 5.8268513679504395, true, - 77.95547485351562, 8.539215087890625, false, - 81.9622573852539, 11.251578330993652, true, - 85.35533905029297, 14.644660949707031, false, - 88.74842071533203, 18.037742614746094, true, - 91.46078491210938, 22.044525146484375, false, - 94.17314910888672, 26.051305770874023, true, - 96.07075500488281, 30.537744522094727, false, - 97.96835327148438, 35.0241813659668, true, - 98.98417663574219, 39.923255920410156, false, - 100, 44.82233428955078, true, - 100, 50, false, - 0, 0, false - ] - ]; - expect(path.vertices).toEqual(actual); - }); -}); diff --git a/__tests__/webgl/WebGLFillMeshGeneratorTest.ts b/__tests__/webgl/WebGLFillMeshGeneratorTest.ts deleted file mode 100644 index 4249c67c..00000000 --- a/__tests__/webgl/WebGLFillMeshGeneratorTest.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { WebGLFillMeshGenerator } from "../../packages/webgl/src/WebGLFillMeshGenerator"; - -describe("WebGLFillMeshGenerator.js test", () => -{ - // ベジェなし - it("generate case 1-1", () => - { - const mesh = WebGLFillMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 200, 200, false, - 100, 200, false - ] - ]); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 0.5, 0.5, - 200, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - - 100, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - 100, 200, 0.5, 0.5 - ]); - - const actualIndexRanges = [ - { "first": 0, "count": 6 } - ]; - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexRanges).toEqual(actualIndexRanges); - }); - - it("generate case 1-2", () => - { - const mesh = WebGLFillMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 200, 200, false, - 100, 200, false - ], - [ - 250, 200, false, - 200, 250, false, - 250, 300, false, - 300, 250, false - ], - ]); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 0.5, 0.5, - 200, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - - 100, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - 100, 200, 0.5, 0.5, - - 250, 200, 0.5, 0.5, - 200, 250, 0.5, 0.5, - 250, 300, 0.5, 0.5, - - 250, 200, 0.5, 0.5, - 250, 300, 0.5, 0.5, - 300, 250, 0.5, 0.5 - ]); - - const actualIndexRanges = [ - { "first": 0, "count": 6 }, - { "first": 6, "count": 6 } - ]; - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexRanges).toEqual(actualIndexRanges); - }); - - // ベジェあり - it("generate case 2-1", () => - { - const mesh = WebGLFillMeshGenerator.generate([ - [ - 100, 100, false, - 150, 0, true, - 200, 100, false, - 200, 200, false, - 150, 250, true, - 100, 200, false - ] - ]); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 0, 0, - 150, 0, 0.5, 0, - 200, 100, 1, 1, - - 100, 100, 0.5, 0.5, - 200, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - - 100, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - 100, 200, 0.5, 0.5, - - 200, 200, 0, 0, - 150, 250, 0.5, 0, - 100, 200, 1, 1 - ]); - - const actualIndexRanges = [ - { "first": 0, "count": 12 } - ]; - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexRanges).toEqual(actualIndexRanges); - }); - - it("generate case 2-2", () => - { - const mesh = WebGLFillMeshGenerator.generate([ - [ - 100, 100, false, - 150, 0, true, - 200, 100, false, - 200, 200, false, - 150, 250, true, - 100, 200, false - ], - [ - 250, 200, false, - 200, 250, false, - 250, 300, false, - 300, 300, true, - 300, 250, false, - 300, 200, true, - 250, 200, false - ], - ]); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 0, 0, - 150, 0, 0.5, 0, - 200, 100, 1, 1, - - 100, 100, 0.5, 0.5, - 200, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - - 100, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - 100, 200, 0.5, 0.5, - - 200, 200, 0, 0, - 150, 250, 0.5, 0, - 100, 200, 1, 1, - - 250, 200, 0.5, 0.5, - 200, 250, 0.5, 0.5, - 250, 300, 0.5, 0.5, - - 250, 200, 0.5, 0.5, - 250, 300, 0.5, 0.5, - 300, 250, 0.5, 0.5, - - 250, 300, 0, 0, - 300, 300, 0.5, 0, - 300, 250, 1, 1, - - 250, 200, 0.5, 0.5, - 300, 250, 0.5, 0.5, - 250, 200, 0.5, 0.5, - - 300, 250, 0, 0, - 300, 200, 0.5, 0, - 250, 200, 1, 1 - ]); - - const actualIndexRanges = [ - { "first": 0, "count": 12 }, - { "first": 12, "count": 15 }, - ]; - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexRanges).toEqual(actualIndexRanges); - }); - - // 複合 - it("generate case 3", () => - { - const mesh = WebGLFillMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 200, 200, false, - 100, 200, false - ], - [ - 250, 200, false, - 200, 250, false, - 250, 300, false, - 300, 300, true, - 300, 250, false, - 300, 200, true, - 250, 200, false - ], - [ - 300, 300, false, - 400, 300, false, - 400, 400, false - ] - ]); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 0.5, 0.5, - 200, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - - 100, 100, 0.5, 0.5, - 200, 200, 0.5, 0.5, - 100, 200, 0.5, 0.5, - - 250, 200, 0.5, 0.5, - 200, 250, 0.5, 0.5, - 250, 300, 0.5, 0.5, - - 250, 200, 0.5, 0.5, - 250, 300, 0.5, 0.5, - 300, 250, 0.5, 0.5, - - 250, 300, 0, 0, - 300, 300, 0.5, 0, - 300, 250, 1, 1, - - 250, 200, 0.5, 0.5, - 300, 250, 0.5, 0.5, - 250, 200, 0.5, 0.5, - - 300, 250, 0, 0, - 300, 200, 0.5, 0, - 250, 200, 1, 1, - - 300, 300, 0.5, 0.5, - 400, 300, 0.5, 0.5, - 400, 400, 0.5, 0.5 - ]); - - const actualIndexRanges = [ - { "first": 0, "count": 6 }, - { "first": 6, "count": 15 }, - { "first": 21, "count": 3 } - ]; - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexRanges).toEqual(actualIndexRanges); - }); -}); diff --git a/__tests__/webgl/WebGLStrokeMeshGeneratorTest.ts b/__tests__/webgl/WebGLStrokeMeshGeneratorTest.ts deleted file mode 100644 index e1b7997e..00000000 --- a/__tests__/webgl/WebGLStrokeMeshGeneratorTest.ts +++ /dev/null @@ -1,704 +0,0 @@ -import { WebGLStrokeMeshGenerator } from "../../packages/webgl/src/WebGLStrokeMeshGenerator"; - -describe("WebGLStrokeMeshGenerator.js test", () => -{ - it("generate CapsStyle.NONE", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate CapsStyle.ROUND", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false - ] - ], "round", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 100, 100, 0, 0, 0, 0, 0, - 100, 100, 0, 0, 0, 0, 30, - 100, 100, 0, 0, 0, 0, 31, - 100, 100, 0, 0, 0, 0, 32, - 100, 100, 0, 0, 0, 0, 33, - 100, 100, 0, 0, 0, 0, 34, - 100, 100, 0, 0, 0, 0, 35, - 100, 100, 0, 0, 0, 0, 36, - 100, 100, 0, 0, 0, 0, 37, - 100, 100, 0, 0, 0, 0, 38, - 100, 100, 0, 0, 0, 0, 39, - 100, 100, 0, 0, 0, 0, 40, - 100, 100, 0, 0, 0, 0, 41, - 100, 100, 0, 0, 0, 0, 42, - 100, 100, 0, 0, 0, 0, 43, - 100, 100, 0, 0, 0, 0, 44, - 100, 100, 0, 0, 0, 0, 45, - 100, 100, 0, 0, 0, 0, 46, - 100, 100, 0, 0, 0, 0, 47, - 200, 200, 0, 0, 0, 0, 0, - 200, 200, 0, 0, 0, 0, 30, - 200, 200, 0, 0, 0, 0, 31, - 200, 200, 0, 0, 0, 0, 32, - 200, 200, 0, 0, 0, 0, 33, - 200, 200, 0, 0, 0, 0, 34, - 200, 200, 0, 0, 0, 0, 35, - 200, 200, 0, 0, 0, 0, 36, - 200, 200, 0, 0, 0, 0, 37, - 200, 200, 0, 0, 0, 0, 38, - 200, 200, 0, 0, 0, 0, 39, - 200, 200, 0, 0, 0, 0, 40, - 200, 200, 0, 0, 0, 0, 41, - 200, 200, 0, 0, 0, 0, 42, - 200, 200, 0, 0, 0, 0, 43, - 200, 200, 0, 0, 0, 0, 44, - 200, 200, 0, 0, 0, 0, 45, - 200, 200, 0, 0, 0, 0, 46, - 200, 200, 0, 0, 0, 0, 47 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 6, - 4, 6, 7, - 4, 7, 8, - 4, 8, 9, - 4, 9, 10, - 4, 10, 11, - 4, 11, 12, - 4, 12, 13, - 4, 13, 14, - 4, 14, 15, - 4, 15, 16, - 4, 16, 17, - 4, 17, 18, - 4, 18, 19, - 4, 19, 20, - 4, 20, 21, - 4, 21, 22, - 4, 22, 5, - 23, 24, 25, - 23, 25, 26, - 23, 26, 27, - 23, 27, 28, - 23, 28, 29, - 23, 29, 30, - 23, 30, 31, - 23, 31, 32, - 23, 32, 33, - 23, 33, 34, - 23, 34, 35, - 23, 35, 36, - 23, 36, 37, - 23, 37, 38, - 23, 38, 39, - 23, 39, 40, - 23, 40, 41, - 23, 41, 24 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate CapsStyle.SQUARE", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false - ] - ], "square", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 10, - 100, 100, 200, 200, 1, 1, 10, - 200, 200, 100, 100, -1, -1, 10, - 200, 200, 100, 100, 1, 1, 10 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 0, 4, 5, - 5, 1, 0, - 3, 6, 7, - 7, 2, 3 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.BEVEL case 1", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 300, 100, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 300, 100, 1, 1, 1, - 200, 100, 300, 100, -1, -1, 1, - 300, 100, 200, 100, -1, -1, 1, - 300, 100, 200, 100, 1, 1, 1 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.BEVEL case 2", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 100, 100, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, -1, -1, 1, - 100, 100, 200, 100, -1, -1, 1, - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 0, 0, 0, 0, 0 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 1, 7, - 8, 0, 0 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.BEVEL case 3", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false, - 300, 100, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 200, 200, 300, 100, 1, 1, 1, - 200, 200, 300, 100, -1, -1, 1, - 300, 100, 200, 200, -1, -1, 1, - 300, 100, 200, 200, 1, 1, 1, - 200, 200, 0, 0, 0, 0, 0 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 4, 2, - 8, 3, 5 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.ROUND case 1", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 300, 100, false - ] - ], "none", "round"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 300, 100, 1, 1, 1, - 200, 100, 300, 100, -1, -1, 1, - 300, 100, 200, 100, -1, -1, 1, - 300, 100, 200, 100, 1, 1, 1 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.ROUND case 2", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 100, 100, false - ] - ], "none", "round"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, -1, -1, 1, - 100, 100, 200, 100, -1, -1, 1, - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 0, 0, 0, 0, 0, - 100, 100, 0, 0, 0, 0, 30, - 100, 100, 0, 0, 0, 0, 31, - 100, 100, 0, 0, 0, 0, 32, - 100, 100, 0, 0, 0, 0, 33, - 100, 100, 0, 0, 0, 0, 34, - 100, 100, 0, 0, 0, 0, 35, - 100, 100, 0, 0, 0, 0, 36, - 100, 100, 0, 0, 0, 0, 37, - 100, 100, 0, 0, 0, 0, 38, - 100, 100, 0, 0, 0, 0, 39, - 100, 100, 0, 0, 0, 0, 40, - 100, 100, 0, 0, 0, 0, 41, - 100, 100, 0, 0, 0, 0, 42, - 100, 100, 0, 0, 0, 0, 43, - 100, 100, 0, 0, 0, 0, 44, - 100, 100, 0, 0, 0, 0, 45, - 100, 100, 0, 0, 0, 0, 46, - 100, 100, 0, 0, 0, 0, 47 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 9, 10, - 8, 10, 11, - 8, 11, 12, - 8, 12, 13, - 8, 13, 14, - 8, 14, 15, - 8, 15, 16, - 8, 16, 17, - 8, 17, 18, - 8, 18, 19, - 8, 19, 20, - 8, 20, 21, - 8, 21, 22, - 8, 22, 23, - 8, 23, 24, - 8, 24, 25, - 8, 25, 26, - 8, 26, 9 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.ROUND case 3", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false, - 300, 100, false - ] - ], "none", "round"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 200, 200, 300, 100, 1, 1, 1, - 200, 200, 300, 100, -1, -1, 1, - 300, 100, 200, 200, -1, -1, 1, - 300, 100, 200, 200, 1, 1, 1, - 200, 200, 0, 0, 0, 0, 0, - 200, 200, 0, 0, 0, 0, 30, - 200, 200, 0, 0, 0, 0, 31, - 200, 200, 0, 0, 0, 0, 32, - 200, 200, 0, 0, 0, 0, 33, - 200, 200, 0, 0, 0, 0, 34, - 200, 200, 0, 0, 0, 0, 35, - 200, 200, 0, 0, 0, 0, 36, - 200, 200, 0, 0, 0, 0, 37, - 200, 200, 0, 0, 0, 0, 38, - 200, 200, 0, 0, 0, 0, 39, - 200, 200, 0, 0, 0, 0, 40, - 200, 200, 0, 0, 0, 0, 41, - 200, 200, 0, 0, 0, 0, 42, - 200, 200, 0, 0, 0, 0, 43, - 200, 200, 0, 0, 0, 0, 44, - 200, 200, 0, 0, 0, 0, 45, - 200, 200, 0, 0, 0, 0, 46, - 200, 200, 0, 0, 0, 0, 47 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 9, 10, - 8, 10, 11, - 8, 11, 12, - 8, 12, 13, - 8, 13, 14, - 8, 14, 15, - 8, 15, 16, - 8, 16, 17, - 8, 17, 18, - 8, 18, 19, - 8, 19, 20, - 8, 20, 21, - 8, 21, 22, - 8, 22, 23, - 8, 23, 24, - 8, 24, 25, - 8, 25, 26, - 8, 26, 9 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.MITER case 1", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 300, 100, false - ] - ], "none", "miter"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 300, 100, 1, 1, 1, - 200, 100, 300, 100, -1, -1, 1, - 300, 100, 200, 100, -1, -1, 1, - 300, 100, 200, 100, 1, 1, 1 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.MITER case 2", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, false, - 100, 100, false - ] - ], "none", "miter"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, -1, -1, 1, - 200, 100, 100, 100, -1, -1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, 1, 1, 1, - 200, 100, 100, 100, -1, -1, 1, - 100, 100, 200, 100, -1, -1, 1, - 100, 100, 200, 100, 1, 1, 1, - 100, 100, 200, 100, 100, 6, 0, - 100, 100, 200, 100, 100, 6, 21, - 100, 100, 200, 100, 100, 6, 22, - 100, 100, 200, 100, 100, 6, 23, - 100, 100, 200, 100, 100, 6, 24 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 1, 9, - 8, 9, 10, - 8, 10, 7, - 8, 0, 11, - 8, 11, 12, - 8, 12, 0 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate JointStyle.MITER case 3", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false, - 300, 100, false - ] - ], "none", "miter"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 200, 200, 300, 100, 1, 1, 1, - 200, 200, 300, 100, -1, -1, 1, - 300, 100, 200, 200, -1, -1, 1, - 300, 100, 200, 200, 1, 1, 1, - 200, 200, 100, 100, 300, 100, 0, - 200, 200, 100, 100, 300, 100, 21, - 200, 200, 100, 100, 300, 100, 22, - 200, 200, 100, 100, 300, 100, 23, - 200, 200, 100, 100, 300, 100, 24 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 4, 9, - 8, 9, 10, - 8, 10, 2, - 8, 3, 11, - 8, 11, 12, - 8, 12, 5 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - // ベジェ曲線 - it("generate quadratic curve", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 100, true, - 200, 200, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 117.3553695678711, 100.82644653320312, 1, 1, 1, - 100, 100, 117.3553695678711, 100.82644653320312, -1, -1, 1, - 117.3553695678711, 100.82644653320312, 100, 100, -1, -1, 2, - 117.3553695678711, 100.82644653320312, 100, 100, 1, 1, 2, - 117.3553695678711, 100.82644653320312, 133.05784606933594, 103.3057861328125, 1, 1, 1, - 117.3553695678711, 100.82644653320312, 133.05784606933594, 103.3057861328125, -1, -1, 1, - 133.05784606933594, 103.3057861328125, 117.3553695678711, 100.82644653320312, -1, -1, 2, - 133.05784606933594, 103.3057861328125, 117.3553695678711, 100.82644653320312, 1, 1, 2, - 133.05784606933594, 103.3057861328125, 147.10743713378906, 107.43801879882812, 1, 1, 1, - 133.05784606933594, 103.3057861328125, 147.10743713378906, 107.43801879882812, -1, -1, 1, - 147.10743713378906, 107.43801879882812, 133.05784606933594, 103.3057861328125, -1, -1, 2, - 147.10743713378906, 107.43801879882812, 133.05784606933594, 103.3057861328125, 1, 1, 2, - 147.10743713378906, 107.43801879882812, 159.50413513183594, 113.22313690185547, 1, 1, 1, - 147.10743713378906, 107.43801879882812, 159.50413513183594, 113.22313690185547, -1, -1, 1, - 159.50413513183594, 113.22313690185547, 147.10743713378906, 107.43801879882812, -1, -1, 2, - 159.50413513183594, 113.22313690185547, 147.10743713378906, 107.43801879882812, 1, 1, 2, - 159.50413513183594, 113.22313690185547, 170.24794006347656, 120.6611557006836, 1, 1, 1, - 159.50413513183594, 113.22313690185547, 170.24794006347656, 120.6611557006836, -1, -1, 1, - 170.24794006347656, 120.6611557006836, 159.50413513183594, 113.22313690185547, -1, -1, 2, - 170.24794006347656, 120.6611557006836, 159.50413513183594, 113.22313690185547, 1, 1, 2, - 170.24794006347656, 120.6611557006836, 179.33883666992188, 129.75205993652344, 1, 1, 1, - 170.24794006347656, 120.6611557006836, 179.33883666992188, 129.75205993652344, -1, -1, 1, - 179.33883666992188, 129.75205993652344, 170.24794006347656, 120.6611557006836, -1, -1, 2, - 179.33883666992188, 129.75205993652344, 170.24794006347656, 120.6611557006836, 1, 1, 2, - 179.33883666992188, 129.75205993652344, 186.77685546875, 140.49586486816406, 1, 1, 1, - 179.33883666992188, 129.75205993652344, 186.77685546875, 140.49586486816406, -1, -1, 1, - 186.77685546875, 140.49586486816406, 179.33883666992188, 129.75205993652344, -1, -1, 2, - 186.77685546875, 140.49586486816406, 179.33883666992188, 129.75205993652344, 1, 1, 2, - 186.77685546875, 140.49586486816406, 192.56198120117188, 152.89256286621094, 1, 1, 1, - 186.77685546875, 140.49586486816406, 192.56198120117188, 152.89256286621094, -1, -1, 1, - 192.56198120117188, 152.89256286621094, 186.77685546875, 140.49586486816406, -1, -1, 2, - 192.56198120117188, 152.89256286621094, 186.77685546875, 140.49586486816406, 1, 1, 2, - 192.56198120117188, 152.89256286621094, 196.6942138671875, 166.94215393066406, 1, 1, 1, - 192.56198120117188, 152.89256286621094, 196.6942138671875, 166.94215393066406, -1, -1, 1, - 196.6942138671875, 166.94215393066406, 192.56198120117188, 152.89256286621094, -1, -1, 2, - 196.6942138671875, 166.94215393066406, 192.56198120117188, 152.89256286621094, 1, 1, 2, - 196.6942138671875, 166.94215393066406, 199.17355346679688, 182.64462280273438, 1, 1, 1, - 196.6942138671875, 166.94215393066406, 199.17355346679688, 182.64462280273438, -1, -1, 1, - 199.17355346679688, 182.64462280273438, 196.6942138671875, 166.94215393066406, -1, -1, 2, - 199.17355346679688, 182.64462280273438, 196.6942138671875, 166.94215393066406, 1, 1, 2, - 199.17355346679688, 182.64462280273438, 200, 200, 1, 1, 1, - 199.17355346679688, 182.64462280273438, 200, 200, -1, -1, 1, - 200, 200, 199.17355346679688, 182.64462280273438, -1, -1, 1, - 200, 200, 199.17355346679688, 182.64462280273438, 1, 1, 1, - 117.3553695678711, 100.82644653320312, 0, 0, 0, 0, 0, - 133.05784606933594, 103.3057861328125, 0, 0, 0, 0, 0, - 147.10743713378906, 107.43801879882812, 0, 0, 0, 0, 0, - 159.50413513183594, 113.22313690185547, 0, 0, 0, 0, 0, - 170.24794006347656, 120.6611557006836, 0, 0, 0, 0, 0, - 179.33883666992188, 129.75205993652344, 0, 0, 0, 0, 0, - 186.77685546875, 140.49586486816406, 0, 0, 0, 0, 0, - 192.56198120117188, 152.89256286621094, 0, 0, 0, 0, 0, - 196.6942138671875, 166.94215393066406, 0, 0, 0, 0, 0, - 199.17355346679688, 182.64462280273438, 0, 0, 0, 0, 0 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4, - 8, 9, 11, - 11, 10, 8, - 12, 13, 15, - 15, 14, 12, - 16, 17, 19, - 19, 18, 16, - 20, 21, 23, - 23, 22, 20, - 24, 25, 27, - 27, 26, 24, - 28, 29, 31, - 31, 30, 28, - 32, 33, 35, - 35, 34, 32, - 36, 37, 39, - 39, 38, 36, - 40, 41, 43, - 43, 42, 40, - 44, 4, 2, - 44, 3, 5, - 45, 8, 6, - 45, 7, 9, - 46, 12, 10, - 46, 11, 13, - 47, 16, 14, - 47, 15, 17, - 48, 20, 18, - 48, 19, 21, - 49, 24, 22, - 49, 23, 25, - 50, 28, 26, - 50, 27, 29, - 51, 32, 30, - 51, 31, 33, - 52, 36, 34, - 52, 35, 37, - 53, 40, 38, - 53, 39, 41 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - - it("generate 2 mesh", () => - { - const mesh = WebGLStrokeMeshGenerator.generate([ - [ - 100, 100, false, - 200, 200, false - ], - [ - 200, 100, false, - 100, 200, false - ] - ], "none", "bevel"); - - const actualVertexBufferData = new Float32Array([ - 100, 100, 200, 200, 1, 1, 1, - 100, 100, 200, 200, -1, -1, 1, - 200, 200, 100, 100, -1, -1, 1, - 200, 200, 100, 100, 1, 1, 1, - 200, 100, 100, 200, 1, 1, 1, - 200, 100, 100, 200, -1, -1, 1, - 100, 200, 200, 100, -1, -1, 1, - 100, 200, 200, 100, 1, 1, 1 - ]); - - const actualIndexBufferData = new Int16Array([ - 0, 1, 3, - 3, 2, 0, - 4, 5, 7, - 7, 6, 4 - ]); - - expect(mesh.vertexBufferData).toEqual(actualVertexBufferData); - expect(mesh.indexBufferData).toEqual(actualIndexBufferData); - }); - -}); diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 72bf3f7d..00000000 --- a/babel.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "current" - } - } - ] - ], - "plugins": [ - "@babel/plugin-transform-modules-commonjs" - ] -}; \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..b3afcd3e --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,142 @@ +import unusedImports from "eslint-plugin-unused-imports"; +import globals from "globals"; +import tsParser from "@typescript-eslint/parser"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import js from "@eslint/js"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + "baseDirectory": __dirname, + "recommendedConfig": js.configs.recommended, + "allConfig": js.configs.all +}); + +export default [ + { + "ignores": [ + "**/node_modules/*", + "**/dist", + "**/build", + "**/json", + "**/@types", + "**/.github", + "**/*.test.ts" + ] + }, + ...compat.extends("plugin:@typescript-eslint/eslint-recommended"), + { + "plugins": { + "unused-imports": unusedImports + }, + "languageOptions": { + "globals": { + ...globals.browser + }, + + "parser": tsParser, + "ecmaVersion": "latest", + "sourceType": "module" + }, + + "rules": { + "no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", + + "unused-imports/no-unused-vars": ["warn", { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + }], + + "no-var": "error", + + "semi": ["error", "always", { + "omitLastInOneLineBlock": true + }], + + "block-spacing": "error", + + "indent": ["error", 4, { + "SwitchCase": 1 + }], + + "no-mixed-spaces-and-tabs": "error", + + "no-multiple-empty-lines": ["error", { + "max": 1 + }], + + "no-trailing-spaces": "error", + "space-infix-ops": "error", + "dot-notation": "error", + "eqeqeq": "error", + "quotes": ["error", "double"], + "no-else-return": "error", + "no-loop-func": "error", + "arrow-parens": "error", + "arrow-spacing": "error", + "no-undef": "off", + "comma-dangle": "warn", + "no-use-before-define": "off", + "no-const-assign": "error", + "space-before-blocks": "error", + "no-unexpected-multiline": "error", + "object-curly-spacing": ["error", "always"], + "quote-props": ["error", "always"], + + "max-len": ["error", { + "code": 200, + "ignoreStrings": true, + "ignoreComments": true, + "ignoreTemplateLiterals": true + }], + + "no-debugger": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-extra-parens": "error", + "no-func-assign": "error", + "no-irregular-whitespace": "error", + "no-sparse-arrays": "error", + "no-unreachable": "error", + "no-unsafe-negation": "error", + "use-isnan": "error", + "block-scoped-var": "error", + "no-caller": "error", + "curly": "error", + "no-case-declarations": "error", + "no-floating-decimal": "error", + "no-eq-null": "error", + "no-empty-function": "error", + "no-empty-pattern": "error", + "no-extend-native": "error", + "dot-location": ["error", "property"], + "no-global-assign": "error", + "no-implicit-globals": "error", + "no-invalid-this": "error", + "no-lone-blocks": "error", + "no-iterator": "error", + "no-new": "error", + "no-proto": "error", + "no-return-assign": "error", + "no-self-assign": "error", + "no-self-compare": "error", + "no-useless-concat": "error", + "no-useless-call": "error", + "no-useless-return": "error", + "no-unused-expressions": "error", + "no-class-assign": "error", + "no-sequences": "error", + "no-dupe-args": "error", + "no-extra-boolean-cast": "error", + "no-obj-calls": "error", + "no-console": "off", + "no-extra-semi": "warn" + } + } +]; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..9247ebc0 --- /dev/null +++ b/index.html @@ -0,0 +1,217 @@ + + + + + + Next2D + + + + + + \ No newline at end of file diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 20f8ace7..00000000 --- a/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/** @type {import("ts-jest/dist/types").InitialOptionsTsJest} */ -module.exports = { - "preset": "ts-jest", - "setupFilesAfterEnv": ["./jest.setup.js"], - "transformIgnorePatterns": [ - "/node_modules/(?!(next2d|@next2d))" - ], - "transform": { - "\\.jsx?$": "babel-jest", - "^.+\\.(ts|tsx)$": "ts-jest" - }, - "testEnvironment": "node" -}; diff --git a/jest.setup.js b/jest.setup.js deleted file mode 100644 index cc1b9c09..00000000 --- a/jest.setup.js +++ /dev/null @@ -1,481 +0,0 @@ -globalThis.$windowEventMap = new Map(); -globalThis.window = { - "document": { - "getElementById": (id) => - { - return globalThis.$elements.has(id) - ? globalThis.$elements.get(id) - : null; - }, - "createElement": () => - { - const attribute = new Map(); - return { - "getAttribute": (name) => - { - return attribute.has(name) - ? attribute.get(name) - : null; - }, - "setAttribute": (name, value) => - { - attribute.set(name, value); - }, - "insertBefore": (element) => - { - if (element.id) { - globalThis.$elements.set(element.id, element); - } - }, - "getContext": () => - { - return { - "measureText": () => { - return { - "width": 0 - }; - } - }; - }, - "insertAdjacentHTML": () => - { - return undefined; - }, - "childNodes": [], - "children": [], - "style": {}, - "addEventListener": () => - { - return undefined; - } - }; - } - }, - "devicePixelRatio": 2, - "addEventListener": (type, callback) => - { - globalThis.$windowEventMap.set(type, callback); - }, - "next2d": { - "display": { - "MovieClip": class MovieClip - { - constructor() - { - this.name = ""; - this.namespace = "next2d.display.MovieClip"; - } - - _$sync () - { - return undefined; - } - - _$getChildren () - { - return undefined; - } - }, - "Loader": class Loader - { - constructor () - { - this.event = new Map(); - } - - get contentLoaderInfo () - { - return { - "addEventListener": (name, callback) => - { - this.event.set(name, callback); - } - }; - } - - loadImage () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool image content" - } - } - }); - } - - load () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool content" - } - } - }); - } - }, - "Shape": class Shape - { - get graphics () - { - return this; - } - - beginBitmapFill () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - }, - "BitmapData": class BitmapData - { - draw (source, matrix, color_transform, canvas = {}, callback) - { - callback(canvas); - } - }, - "Sprite": class Sprite - { - addChild (display_object) - { - return display_object; - } - - get graphics () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - } - }, - "player": { - "cacheStore": { - "removeCache": () => - { - return undefined; - }, - "setRemoveTimer": () => - { - return undefined; - } - }, - "base": "", - "broadcastEvents": new Map(), - "_$actions": [] - } - } -}; - -globalThis.next2d = { - "createRootMovieClip": function () - { - const object = { - "_$created": true, - "_$createWorkerInstance": () => - { - return undefined; - }, - "stage": { - "canvasWidth": 100, - "canvasHeight": 100, - "_$player": { - "_$matrix": [1,0,0,1,10,10] - } - }, - "addChild": (child) => - { - return child; - }, - "numChildren": 1, - "removeChild": (number) => - { - object.numChildren = number; - }, - "getChildAt": () => - { - return 0; - } - }; - - return object; - }, - "display": { - "MovieClip": class MovieClip - { - constructor() - { - this.name = ""; - this.namespace = "next2d.display.MovieClip"; - } - - _$sync () - { - return undefined; - } - - _$getChildren () - { - return undefined; - } - }, - "Loader": class Loader - { - constructor () - { - this.event = new Map(); - } - - get contentLoaderInfo () - { - return { - "addEventListener": (name, callback) => - { - this.event.set(name, callback); - } - }; - } - - loadImage () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool image content" - } - } - }); - } - - load () - { - this.event.get("complete")({ - "currentTarget": { - "content": { - "_$loaderInfo": { - "_$data": { - "symbols": new Map([["app", "app"]]) - } - }, - "text": "NoCode Tool content" - } - } - }); - } - }, - "Shape": class Shape - { - get graphics () - { - return this; - } - - beginBitmapFill () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - }, - "BitmapData": class BitmapData - { - draw (source, matrix, color_transform, canvas = {}, callback) - { - callback(canvas); - } - }, - "Sprite": class Sprite - { - addChild (display_object) - { - return display_object; - } - - get graphics () - { - return this; - } - - beginFill () - { - return this; - } - - drawRect () - { - return this; - } - - endFill () - { - return this; - } - } - }, - "geom": { - "Matrix": class Matrix - { - - } - }, - "events": { - "Event": class Event { - static get COMPLETE () - { - return "complete"; - } - static get REMOVED () - { - return "removed"; - } - }, - "IOErrorEvent": class IOErrorEvent - { - static get IO_ERROR () { - return "io_error"; - } - } - }, - "net": { - "URLRequest": class URLRequest - { - constructor() - { - this.method = "GET"; - this.requestHeaders = []; - this.data = null; - } - }, - "URLRequestHeader": class URLRequestHeader - { - constructor (name, value) - { - this.name = name; - this.value = value; - } - }, - "URLRequestMethod": { - "GET": "GET", - "PUT": "PUT", - "POST": "POST" - } - }, - "fw": { - "loaderInfo": new Map(), - "application": "app", - "cache": new Map([["cache", "cache"]]), - "config": { - "stage": { - "width": 240, - "height": 240, - "fps": 12, - "options": {} - } - }, - "context": "context", - "packages": new Map([["class", "class"]]), - "response": new Map([["response", "response"]]), - "variable": new Map([["variable", "variable"]]), - "query": new Map([["query", "query"]]) - } -}; - -globalThis.location = { - "pathname": "/" -}; - -globalThis.OffscreenCanvasRenderingContext2D = class OffscreenCanvasRenderingContext2D -{ - -}; - -globalThis.WebGLTexture = class WebGLTexture -{ - -}; - -globalThis.CanvasRenderingContext2D = class CanvasRenderingContext2D -{ - -}; - -globalThis.OffscreenCanvas = class OffscreenCanvas -{ - getContext () - { - return new OffscreenCanvasRenderingContext2D(); - } -}; - -globalThis.history = { - "pushState": () => - { - return undefined; - } -}; - -globalThis.cancelAnimationFrame = () => -{ - return undefined; -}; - -globalThis.requestAnimationFrame = (callback) => -{ - return callback(); -}; \ No newline at end of file diff --git a/jsdoc.conf.js b/jsdoc.conf.js deleted file mode 100644 index 02593082..00000000 --- a/jsdoc.conf.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -module.exports = { - "plugins": [ - "plugins/markdown" - ], - "markdown": { - "hardwrap": true - }, - "templates": { - "cleverLinks" : false, - "monospaceLinks": false, - "applicationName": "Next2D Framework", - "path": "../../../", - "openGraph": { - "title": "Player API Documentation", - "description": "Player API Documentation.", - "type": "website", - "image": "https://next2d.app/assets/img/ogp.png", - "url": "https://next2d.app/" - }, - "meta": { - "title": "Player API Documentation", - "description": "Next2D Player API Documentation.", - "keyword": "Next2D, WebGL, WebGL2, JavaScript, HTML5" - }, - "linenums": true, - "default" : { - "outputSourceFiles" : true - } - }, - "opts": { - "encoding": "utf8", - "recurse": true, - "private": false, - "lenient": true, - "destination": "../next2d/dist/docs/player/", - "template": "node_modules/@next2d/jsdoc-template" - } -}; diff --git a/next2d.js b/next2d.js deleted file mode 100644 index a23f953d..00000000 --- a/next2d.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";let t=0;const e=()=>t++;let i=0,s=null;const r=(t=null)=>{s=t};let n="";const a=()=>n,h=t=>{n=t};let o=null;const l=()=>o,c=(t=null)=>{o=t};let _=1;const $=()=>_,u=window,d=u.document;class g{constructor(t="",e=""){this._$name=`${t}`,this._$value=`${e}`}static toString(){return"[class URLRequestHeader]"}static get namespace(){return"next2d.net.URLRequestHeader"}toString(){return"[object URLRequestHeader]"}get namespace(){return"next2d.net.URLRequestHeader"}get name(){return this._$name}get value(){return this._$value}}let f=1,p=0,m=!1;const x=(t=!0)=>{m=t},b=1/0,v=Math,T=Array,y=Map,E=Number,A=Float32Array,M=Int32Array,S=Int16Array,w=OffscreenCanvas,C=isNaN,I=requestAnimationFrame,F=cancelAnimationFrame,R=performance,B=setTimeout,L=clearTimeout,P=new A([1,0,0,1,0,0]),k=new A([1,1,1,1,0,0,0,0]),N=-32768,O=32767,D=v.PI/180,U=180/v.PI,V=[],G=[],z=[],X=[],q=[],Y=[],H=[],j=[],W=[],K=new w(1,1).getContext("2d"),Q=(t=0,e=0,i=0,s=0)=>{const r=W.pop()||{xMin:0,xMax:0,yMin:0,yMax:0};return r.xMin=t,r.xMax=e,r.yMin=i,r.yMax=s,r},J=t=>{W.push(t)},Z=(t=0,e=0,i=0,s=0)=>{const r=z.pop()||new A(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},tt=t=>{z.push(t)},et=(t=0,e=0,i=0,s=0)=>{const r=G.pop()||new M(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},it=(t=0,e=0,i=0,s=0,r=0,n=0)=>{const a=X.pop()||new A(6);return a[0]=t,a[1]=e,a[2]=i,a[3]=s,a[4]=r,a[5]=n,a},st=t=>{X.push(t)},rt=(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0)=>{const o=q.pop()||new A(8);return o[0]=t,o[1]=e,o[2]=i,o[3]=s,o[4]=r,o[5]=n,o[6]=a,o[7]=h,o},nt=t=>{q.push(t)},at=(t=0,e=0,i=0,s=0,r=0,n=0,a=0,h=0,o=0)=>{const l=Y.pop()||new A(9);return l[0]=t,l[1]=e,l[2]=i,l[3]=s,l[4]=r,l[5]=n,l[6]=a,l[7]=h,l[8]=o,l},ht=(...t)=>{const e=H.pop()||[];return t.length&&e.push(...t),e},ot=(t=null)=>{t&&(t.length&&(t.length=0),H.push(t))},lt=t=>{t.size&&t.clear(),j.push(t)},ct=()=>j.pop()||new y,_t=t=>(t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t),$t=t=>{const e=-819.2*t[0]-819.2*t[2]+t[4],i=819.2*t[0]-819.2*t[2]+t[4],s=-819.2*t[0]+819.2*t[2]+t[4],r=-819.2*t[1]-819.2*t[3]+t[5],n=819.2*t[1]-819.2*t[3]+t[5];let a=s-e,h=-819.2*t[1]+819.2*t[3]+t[5]-r;const o=v.sqrt(a*a+h*h);o?(a/=o,h/=o):(a=0,h=0);const l=(i-e)*a+(n-r)*h;return Z(e+l*a,r+l*h,i,n)},ut=t=>{const e=1/(t[0]*t[4]-t[3]*t[1]),i=t[3]*t[7]-t[4]*t[6],s=t[1]*t[6]-t[0]*t[7];return at(t[4]*e,0-t[1]*e,0,0-t[3]*e,t[0]*e,0,i*e,s*e,1)},dt=(t,e,i,s=null)=>{const r=+t;return C(r)&&null!==s?s:v.min(v.max(e,C(r)?0:r),i)},gt=(t,e)=>it(t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]),ft=(t,e)=>rt(t[0]*e[0],t[1]*e[1],t[2]*e[2],t[3]*e[3],t[0]*e[4]+t[4],t[1]*e[5]+t[5],t[2]*e[6]+t[6],t[3]*e[7]+t[7]),pt=(t,e)=>{const i=t.xMax*e[0]+t.yMax*e[2]+e[4],s=t.xMax*e[0]+t.yMin*e[2]+e[4],r=t.xMin*e[0]+t.yMax*e[2]+e[4],n=t.xMin*e[0]+t.yMin*e[2]+e[4],a=t.xMax*e[1]+t.yMax*e[3]+e[5],h=t.xMax*e[1]+t.yMin*e[3]+e[5],o=t.xMin*e[1]+t.yMax*e[3]+e[5],l=t.xMin*e[1]+t.yMin*e[3]+e[5],c=v.min(E.MAX_VALUE,i,s,r,n),_=v.max(0-E.MAX_VALUE,i,s,r,n),$=v.min(E.MAX_VALUE,a,h,o,l),u=v.max(0-E.MAX_VALUE,a,h,o,l);return Q(c,_,$,u)},mt=t=>{if(!K)return 0;K.fillStyle=t;const e=+`0x${K.fillStyle.slice(1)}`;return K.fillStyle="rgba(0, 0, 0, 1)",e},xt=t=>C(+t)?mt(`${t}`):+t,bt=t=>({A:t>>>24,R:(16711680&t)>>16,G:(65280&t)>>8,B:255&t}),vt=(t,e,i)=>(t>>16)*(i?e:1)/255,Tt=(t,e,i)=>(t>>8&255)*(i?e:1)/255,yt=(t,e,i)=>(255&t)*(i?e:1)/255,Et=(t,e=1)=>({R:(16711680&t)>>16,G:(65280&t)>>8,B:255&t,A:255*e}),At=(t,e,i=!1,s=!1)=>{let r="";return i&&(r="italic "),s&&(r+="bold "),`${r}${e}px '${t}','sans-serif'`},Mt=t=>{t.color&&nt(t.color),t.isLayer=!1,t.isUpdated=null,t.canApply=null,t.matrix=null,t.color=null,t.filters=null,t.blendMode="normal",t.sw=0,t.sh=0,V.push(t)};new Map([[1,"normal"],[2,"layer"],[3,"multiply"],[4,"screen"],[5,"lighten"],[6,"darken"],[7,"difference"],[8,"add"],[9,"subtract"],[10,"invert"],[11,"alpha"],[12,"erase"],[13,"overlay"],[14,"hardlight"]]);const St=new class{constructor(){this._$pool=[],this._$store=new Map,this._$timerMap=new Map,this._$context=null}set context(t){this._$context=t}reset(){for(const t of this._$store.values()){for(const e of t.values())this.destroy(e);lt(t)}this._$store.clear(),this._$context&&this._$context.frameBuffer.clearCache()}destroy(t=null){if(t&&"object"==typeof t)if(t instanceof WebGLTexture)I((()=>{this._$context&&this._$context.frameBuffer.releaseTexture(t)}));else{if("canvas"in t&&t instanceof CanvasRenderingContext2D){const e=t.canvas,i=e.width,s=e.height;t.clearRect(0,0,i+1,s+1),e.width=e.height=1,this._$pool.push(e)}this._$context&&"index"in t&&this._$context.frameBuffer.textureManager.releasePosition(t)}}getCanvas(){return this._$pool.pop()||document.createElement("canvas")}remove(t,e){if(!this._$store.has(t))return;const i=this._$store.get(t);i.has(e)&&(i.delete(e),i.size||(lt(i),this._$store.delete(t)))}stopTimer(t){t=`${t}`,this._$timerMap.has(t)&&(L(this._$timerMap.get(t)),this._$timerMap.delete(t))}removeCache(t){if(t=`${t}`,this._$store.has(t)){const e=this._$store.get(t);for(const t of e.values())this.destroy(t);e.clear(),lt(e),this._$store.delete(t)}this._$timerMap.delete(t)}setRemoveTimer(t){if(t=`${t}`,this.stopTimer(t),this._$store.has(t)){const e=B((()=>{this.removeCache(t)}),5e3);this._$timerMap.set(t,e)}}get(t){const e=`${t[0]}`,i=`${t[1]}`;if(this._$store.has(e)){this.stopTimer(e);const t=this._$store.get(e);if(t.has(i))return t.get(i)}return null}set(t,e=null){const i=`${t[0]}`,s=`${t[1]}`;this._$store.has(i)||this._$store.set(i,ct());const r=this._$store.get(i);if(null===e){if(!r.has(s))return;return this.destroy(r.get(s)),r.delete(s),void(r.size||(lt(r),this._$store.delete(i)))}r.set(s,e)}has(t){const e=`${t[0]}`;return!!this._$store.has(e)&&this._$store.get(e).has(`${t[1]}`)}generateKeys(t,e=null,i=null){let s="";e&&e.length&&(s+=`${e[0]}_${e[1]}`),i&&i.length&&(s+=0===i[7]?"":`_${i[7]}`);const r=ht();if(s){let t=0;const e=s.length;for(let i=0;i1&&n.sort((function(t,e){switch(!0){case t.priority>e.priority:return-1;case t.priority1&&r.sort((function(t,e){switch(!0){case t.priority>e.priority:return-1;case t.priority1&&n.sort((function(t,e){switch(!0){case t.priority>e.priority:return-1;case t.priority{if(e in t)return t[e];const i=l();return i&&e in i?i[e]:void 0}})}static toString(){return"[class MouseEvent]"}static get namespace(){return"next2d.events.MouseEvent"}toString(){return this.formatToString("MouseEvent","type","bubbles","cancelable","eventPhase","localX","localY","stageX","stageY","ctrlKey","altKey","shiftKey","buttonDown","delta","commandKey","controlKey","clickCount")}get namespace(){return"next2d.events.MouseEvent"}static get CLICK(){return"click"}static get DOUBLE_CLICK(){return"dblclick"}static get MOUSE_DOWN(){return"mouseDown"}static get MOUSE_MOVE(){return"mouseMove"}static get MOUSE_OUT(){return"mouseOut"}static get MOUSE_OVER(){return"mouseOver"}static get MOUSE_UP(){return"mouseUp"}static get MOUSE_WHEEL(){return"mouseWheel"}static get ROLL_OUT(){return"rollOut"}static get ROLL_OVER(){return"rollOver"}}class kt extends It{constructor(t,e=!1,i=!1,s=0,r=0){super(t,e,i),this._$bytesLoaded=0|s,this._$bytesTotal=0|r}static toString(){return"[class ProgressEvent]"}static get namespace(){return"next2d.events.ProgressEvent"}toString(){return this.formatToString("ProgressEvent","type","bubbles","cancelable","eventPhase","bytesLoaded","bytesTotal")}get namespace(){return"next2d.events.ProgressEvent"}static get PROGRESS(){return"progress"}get bytesLoaded(){return this._$bytesLoaded}get bytesTotal(){return this._$bytesTotal}}class Nt extends It{constructor(t,e=!1,i=!1,s=0,r=0){super(t,e,i),this._$bytesLoaded=0|s,this._$bytesTotal=0|r}static toString(){return"[class VideoEvent]"}static get namespace(){return"next2d.events.VideoEvent"}toString(){return this.formatToString("VideoEvent","type","bubbles","cancelable","eventPhase","bytesLoaded","bytesTotal")}get namespace(){return"next2d.events.VideoEvent"}static get PROGRESS(){return"progress"}static get PLAY(){return"play"}static get PLAY_START(){return"playStart"}static get PLAY_END(){return"playEnd"}static get PAUSE(){return"pause"}static get SEEK(){return"seek"}get bytesLoaded(){return this._$bytesLoaded}get bytesTotal(){return this._$bytesTotal}}class Ot{constructor(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0){this._$colorTransform=rt(),this.redMultiplier=t,this.greenMultiplier=e,this.blueMultiplier=i,this.alphaMultiplier=s,this.redOffset=r,this.greenOffset=n,this.blueOffset=a,this.alphaOffset=h}static toString(){return"[class ColorTransform]"}static get namespace(){return"next2d.geom.ColorTransform"}toString(){return"(redMultiplier="+this._$colorTransform[0]+", greenMultiplier="+this._$colorTransform[1]+", blueMultiplier="+this._$colorTransform[2]+", alphaMultiplier="+this._$colorTransform[3]+", redOffset="+this._$colorTransform[4]+", greenOffset="+this._$colorTransform[5]+", blueOffset="+this._$colorTransform[6]+", alphaOffset="+this._$colorTransform[7]+")"}get namespace(){return"next2d.geom.ColorTransform"}get alphaMultiplier(){return this._$colorTransform[3]}set alphaMultiplier(t){this._$colorTransform[3]=dt(+t,0,1,0)}get alphaOffset(){return this._$colorTransform[7]}set alphaOffset(t){this._$colorTransform[7]=dt(0|t,-255,255,0)}get blueMultiplier(){return this._$colorTransform[2]}set blueMultiplier(t){this._$colorTransform[2]=dt(+t,0,1,0)}get blueOffset(){return this._$colorTransform[6]}set blueOffset(t){this._$colorTransform[6]=dt(0|t,-255,255,0)}get greenMultiplier(){return this._$colorTransform[1]}set greenMultiplier(t){this._$colorTransform[1]=dt(+t,0,1,0)}get greenOffset(){return this._$colorTransform[5]}set greenOffset(t){this._$colorTransform[5]=dt(0|t,-255,255,0)}get redMultiplier(){return this._$colorTransform[0]}set redMultiplier(t){this._$colorTransform[0]=dt(+t,0,1,0)}get redOffset(){return this._$colorTransform[4]}set redOffset(t){this._$colorTransform[4]=dt(0|t,-255,255,0)}concat(t){const e=ft(this._$colorTransform,t._$colorTransform);this.redMultiplier=e[0],this.greenMultiplier=e[1],this.blueMultiplier=e[2],this.alphaMultiplier=e[3],this.redOffset=e[4],this.greenOffset=e[5],this.blueOffset=e[6],this.alphaOffset=e[7],nt(e)}_$clone(){return dr(this._$colorTransform[0],this._$colorTransform[1],this._$colorTransform[2],this._$colorTransform[3],this._$colorTransform[4],this._$colorTransform[5],this._$colorTransform[6],this._$colorTransform[7])}}class Dt{constructor(t=0,e=0){this._$x=0,this._$y=0,this.x=t,this.y=e}static toString(){return"[class Point]"}static get namespace(){return"next2d.geom.Point"}toString(){return`(x=${this.x}, y=${this.y})`}get namespace(){return"next2d.geom.Point"}get length(){return v.sqrt(v.pow(this.x,2)+v.pow(this.y,2))}get x(){return this._$x}set x(t){this._$x=dt(+t,N,O,0)}get y(){return this._$y}set y(t){this._$y=dt(+t,N,O,0)}add(t){return new Dt(this.x+t.x,this.y+t.y)}clone(){return new Dt(this.x,this.y)}copyFrom(t){this._$x=t._$x,this._$y=t._$y}static distance(t,e){return v.sqrt(v.pow(t._$x-e._$x,2)+v.pow(t._$y-e._$y,2))}equals(t){return this._$x===t._$x&&this._$y===t._$y}static interpolate(t,e,i){return new Dt(t.x+(e.x-t.x)*(1-i),t.y+(e.y-t.y)*(1-i))}normalize(t){const e=this.length;this.x=this.x*t/e,this.y=this.y*t/e}offset(t,e){this.x+=t,this.y+=e}static polar(t,e){return new Dt(t*v.cos(e),t*v.sin(e))}setTo(t,e){this.x=t,this.y=e}subtract(t){return new Dt(this.x-t.x,this.y-t.y)}}class Ut{constructor(t=1,e=0,i=0,s=1,r=0,n=0){this._$matrix=it(1,0,0,1,0,0),this.a=t,this.b=e,this.c=i,this.d=s,this.tx=r,this.ty=n}static toString(){return"[class Matrix]"}static get namespace(){return"next2d.geom.Matrix"}toString(){return`(a=${this.a}, b=${this.b}, c=${this.c}, d=${this.d}, tx=${this.tx}, ty=${this.ty})`}get namespace(){return"next2d.geom.Matrix"}get a(){return this._$matrix[0]}set a(t){this._$matrix[0]=dt(+t,N,O,0)}get b(){return this._$matrix[1]}set b(t){this._$matrix[1]=dt(+t,N,O,0)}get c(){return this._$matrix[2]}set c(t){this._$matrix[2]=dt(+t,N,O,0)}get d(){return this._$matrix[3]}set d(t){this._$matrix[3]=dt(+t,N,O,0)}get tx(){return this._$matrix[4]}set tx(t){this._$matrix[4]=dt(+t,N,O,0)}get ty(){return this._$matrix[5]}set ty(t){this._$matrix[5]=dt(+t,N,O,0)}_$clone(){return this.clone()}clone(){return $r(this._$matrix[0],this._$matrix[1],this._$matrix[2],this._$matrix[3],this._$matrix[4],this._$matrix[5])}concat(t){const e=this._$matrix,i=t._$matrix;let s=e[0]*i[0],r=0,n=0,a=e[3]*i[3],h=e[4]*i[0]+i[4],o=e[5]*i[3]+i[5];(e[1]||e[2]||i[1]||i[2])&&(s+=e[1]*i[2],a+=e[2]*i[1],r+=e[0]*i[1]+e[1]*i[3],n+=e[2]*i[0]+e[3]*i[2],h+=e[5]*i[2],o+=e[4]*i[1]),this.a=s,this.b=r,this.c=n,this.d=a,this.tx=h,this.ty=o}copyFrom(t){this.a=t.a,this.b=t.b,this.c=t.c,this.d=t.d,this.tx=t.tx,this.ty=t.ty}createBox(t,e,i=0,s=0,r=0){this.identity(),this.rotate(i),this.scale(t,e),this.translate(s,r)}createGradientBox(t,e,i=0,s=0,r=0){if(this.a=t/1638.4,this.d=e/1638.4,i){const t=v.cos(i),e=v.sin(i);this.b=e*this.d,this.c=-e*this.a,this.a*=t,this.d*=t}else this.b=0,this.c=0;this.tx=s+t/2,this.ty=r+e/2}deltaTransformPoint(t){return new Dt(t.x*this._$matrix[0]+t.y*this._$matrix[2],t.x*this._$matrix[1]+t.y*this._$matrix[3])}identity(){this._$matrix[0]=1,this._$matrix[1]=0,this._$matrix[2]=0,this._$matrix[3]=1,this._$matrix[4]=0,this._$matrix[5]=0}invert(){const t=this._$matrix[0],e=this._$matrix[1],i=this._$matrix[2],s=this._$matrix[3],r=this._$matrix[4],n=this._$matrix[5];if(0===e&&0===i)this.a=1/t,this.b=0,this.c=0,this.d=1/s,this.tx=-this.a*r,this.ty=-this.d*n;else{const a=t*s-e*i;if(a){const h=1/a;this.a=s*h,this.b=-e*h,this.c=-i*h,this.d=t*h,this.tx=-(this.a*r+this.c*n),this.ty=-(this.b*r+this.d*n)}}}rotate(t){const e=this._$matrix[0],i=this._$matrix[1],s=this._$matrix[2],r=this._$matrix[3],n=this._$matrix[4],a=this._$matrix[5];this.a=e*v.cos(t)-i*v.sin(t),this.b=e*v.sin(t)+i*v.cos(t),this.c=s*v.cos(t)-r*v.sin(t),this.d=s*v.sin(t)+r*v.cos(t),this.tx=n*v.cos(t)-a*v.sin(t),this.ty=n*v.sin(t)+a*v.cos(t)}scale(t,e){this.a*=t,this.c*=t,this.tx*=t,this.b*=e,this.d*=e,this.ty*=e}setTo(t,e,i,s,r,n){this.a=t,this.b=e,this.c=i,this.d=s,this.tx=r,this.ty=n}transformPoint(t){return new Dt(t.x*this._$matrix[0]+t.y*this._$matrix[2]+this._$matrix[4],t.x*this._$matrix[1]+t.y*this._$matrix[3]+this._$matrix[5])}translate(t,e){this.tx+=t,this.ty+=e}}class Vt{constructor(t=0,e=0,i=0,s=0){this._$x=0,this._$y=0,this._$width=0,this._$height=0,this.setTo(t,e,i,s)}static toString(){return"[class Rectangle]"}static get namespace(){return"next2d.geom.Rectangle"}toString(){return`(x=${this.x}, y=${this.y}, w=${this.width}, h=${this.height})`}get namespace(){return"next2d.geom.Rectangle"}get bottom(){return this.y+this.height}set bottom(t){this.height=+t-this.y}get bottomRight(){return new Dt(this.right,this.bottom)}set bottomRight(t){this.right=t.x,this.bottom=t.y}get height(){return this._$height}set height(t){this._$height=dt(+t,N,O,0)}get left(){return this.x}set left(t){this.width=this.right-+t,this.x=t}get right(){return this.x+this.width}set right(t){this.width=+t-this.x}get size(){return new Dt(this.width,this.height)}set size(t){this.width=t.x,this.height=t.y}get top(){return this.y}set top(t){this.height=+(this.bottom-+t),this.y=t}get topLeft(){return new Dt(this.x,this.y)}set topLeft(t){this.left=t.x,this.top=t.y}get width(){return this._$width}set width(t){this._$width=dt(+t,N,O,0)}get x(){return this._$x}set x(t){this._$x=dt(+t,N,O,0)}get y(){return this._$y}set y(t){this._$y=dt(+t,N,O,0)}clone(){return new Vt(this.x,this.y,this.width,this.height)}contains(t,e){return this.x<=t&&this.y<=e&&this.right>t&&this.bottom>e}containsPoint(t){return this.x<=t.x&&this.y<=t.y&&this.right>t.x&&this.bottom>t.y}containsRect(t){return this.x<=t.x&&this.y<=t.y&&this.right>=t.right&&this.bottom>=t.bottom}copyFrom(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height}equals(t){return this.x===t.x&&this.y===t.y&&this.width===t.width&&this.height===t.height}inflate(t,e){this.x=this.x-+t,this.width=this.width+2*+t,this.y=this.y-+e,this.height=this.height+2*+e}inflatePoint(t){this.x=this.x-t.x,this.width=this.width+2*t.x,this.y=this.y-t.y,this.height=this.height+2*t.y}intersection(t){const e=v.max(this.x,t.x),i=v.max(this.y,t.y),s=v.min(this.right,t.right)-e,r=v.min(this.bottom,t.bottom)-i;return s>0&&r>0?new Vt(e,i,s,r):new Vt(0,0,0,0)}intersects(t){const e=v.max(this.x,t.x),i=v.max(this.y,t.y),s=v.min(this.right,t.right),r=v.min(this.bottom,t.bottom);return s-e>0&&r-i>0}isEmpty(){return this.width<=0||this.height<=0}offset(t,e){this.x+=t,this.y+=e}offsetPoint(t){this.x+=t.x,this.y+=t.y}setEmpty(){this._$x=0,this._$y=0,this._$width=0,this._$height=0}setTo(t,e,i,s){this.x=t,this.y=e,this.width=i,this.height=s}union(t){return this.isEmpty()?t.clone():t.isEmpty()?this.clone():new Vt(v.min(this.x,t.x),v.min(this.y,t.y),v.max(this.right-t.left,t.right-this.left),v.max(this.bottom-t.top,t.bottom-this.top))}}class Gt{constructor(){this._$updated=!0}static toString(){return"[class BitmapFilter]"}static get namespace(){return"next2d.filters.BitmapFilter"}toString(){return"[object BitmapFilter]"}get namespace(){return"next2d.filters.BitmapFilter"}_$isUpdated(){return this._$updated}_$doChanged(){this._$updated=!0,x()}}class zt extends Gt{constructor(t=4,e=4,i=1){super(),this._$blurX=4,this._$blurY=4,this._$quality=1,this.blurX=t,this.blurY=e,this.quality=i}static toString(){return"[class BlurFilter]"}static get namespace(){return"next2d.filters.BlurFilter"}toString(){return"[object BlurFilter]"}get namespace(){return"next2d.filters.BlurFilter"}static get STEP(){return[.5,1.05,1.4,1.55,1.75,1.9,2,2.15,2.2,2.3,2.5,3,3,3.5,3.5]}get blurX(){return this._$blurX}set blurX(t){(t=dt(+t,0,255,0))!==this._$blurX&&(this._$blurX=t,this._$doChanged())}get blurY(){return this._$blurY}set blurY(t){(t=dt(+t,0,255,0))!==this._$blurY&&(this._$blurY=t,this._$doChanged())}get quality(){return this._$quality}set quality(t){(t=dt(0|t,0,15,1))!==this._$quality&&(this._$quality=t,this._$doChanged())}clone(){return new zt(this._$blurX,this._$blurY,this._$quality)}_$toArray(){return ht(1,this._$blurX,this._$blurY,this._$quality)}_$generateFilterRect(t,e=0,i=0){const s=Q(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$quality)return s;const r=zt.STEP[this._$quality-1];let n=0>=this._$blurX?1:this._$blurX*r,a=0>=this._$blurY?1:this._$blurY*r;return e?n*=e:n=v.round(n),i?a*=i:a=v.round(a),s.xMin-=n,s.xMax+=2*n,s.yMin-=a,s.yMax+=2*a,s}_$canApply(){return 0!==this._$blurX&&0!==this._$blurY}_$applyFilter(t,e,i=!0){this._$updated=!1;const s=t.frameBuffer,r=s.currentAttachment,n=s.getTextureFromCurrentAttachment();if(!this._$canApply())return i?n:s.createTextureFromCurrentAttachment();let a=v.sqrt(e[0]*e[0]+e[1]*e[1]),h=v.sqrt(e[2]*e[2]+e[3]*e[3]);a/=f,h/=f,a*=2,h*=2;const o=Q(0,n.width,0,n.height),l=this._$generateFilterRect(o,a,h);J(o);const c=0|v.ceil(l.xMax),_=0|v.ceil(l.yMax),$=v.ceil(v.abs(l.xMin)+.5*v.abs(c-l.xMax)),u=v.ceil(v.abs(l.yMin)+.5*v.abs(_-l.yMax));t._$offsetX=$+t._$offsetX,t._$offsetY=u+t._$offsetY;const d=this._$blurX*a,g=this._$blurY*h;let p=1,m=1;d>128?p=.0625:d>64?p=.125:d>32?p=.25:d>16&&(p=.5),g>128?m=.0625:g>64?m=.125:g>32?m=.25:g>16&&(m=.5);const x=d*p,b=g*m,T=v.ceil(c*p),y=v.ceil(_*m),E=s.createTextureAttachment(T,y),A=[E,s.createTextureAttachment(T,y)];let M=0;t._$bind(E),t.reset(),t.setTransform(p,0,0,m,0,0),t.drawImage(n,$,u,n.width,n.height),t.blend.toOneZero();let S=s.getTextureFromCurrentAttachment();for(let e=0;e0){M=(M+1)%2;const e=A[M];t._$bind(e),t._$applyBlurFilter(S,!0,x),S=s.getTextureFromCurrentAttachment()}if(this._$blurY>0){M=(M+1)%2;const e=A[M];t._$bind(e),t._$applyBlurFilter(S,!1,b),S=s.getTextureFromCurrentAttachment()}}if(t.blend.reset(),1!==p||1!==m){const e=s.createTextureAttachment(c,_);t._$bind(e),t.reset(),t.imageSmoothingEnabled=!0,t.setTransform(1/p,0,0,1/m,0,0),t.drawImage(S,0,0,T,y),S=s.getTextureFromCurrentAttachment(),t.reset(),t.setTransform(1,0,0,1,0,0),s.releaseAttachment(A[0],!0),s.releaseAttachment(A[1],!0),i?s.releaseAttachment(r,!0):s.releaseAttachment(e,!1)}else s.releaseAttachment(A[(M+1)%2],!0),i?s.releaseAttachment(r,!0):s.releaseAttachment(A[M],!1);return S}}class Xt extends Gt{constructor(t=4,e=45,i=16777215,s=1,r=0,n=1,a=4,h=4,o=1,l=1,c="inner",_=!1){super(),this._$blurFilter=new zt(a,h,l),this._$distance=4,this._$angle=45,this._$highlightColor=16777215,this._$highlightAlpha=1,this._$shadowColor=0,this._$shadowAlpha=1,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.highlightColor=i,this.highlightAlpha=s,this.shadowColor=r,this.shadowAlpha=n,this.strength=o,this.type=c,this.knockout=_}static toString(){return"[class BevelFilter]"}static get namespace(){return"next2d.filters.BevelFilter"}toString(){return"[object BevelFilter]"}get namespace(){return"next2d.filters.BevelFilter"}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=dt(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get distance(){return this._$distance}set distance(t){(t=dt(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get highlightAlpha(){return this._$highlightAlpha}set highlightAlpha(t){(t=dt(+t,0,1,0))!==this._$highlightAlpha&&(this._$highlightAlpha=t,this._$doChanged())}get highlightColor(){return this._$highlightColor}set highlightColor(t){(t=dt(xt(t),0,16777215,16777215))!==this._$highlightColor&&(this._$highlightColor=t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get shadowAlpha(){return this._$shadowAlpha}set shadowAlpha(t){(t=dt(+t,0,1,0))!==this._$shadowAlpha&&(this._$shadowAlpha=t,this._$doChanged())}get shadowColor(){return this._$shadowColor}set shadowColor(t){(t=dt(xt(t),0,16777215,0))!==this._$shadowColor&&(this._$shadowColor=t,this._$doChanged())}get strength(){return this._$strength}set strength(t){(t=dt(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}get type(){return this._$type}set type(t){(t=`${t}`)!==this._$type&&(this._$type=t,this._$doChanged())}clone(){return new Xt(this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$toArray(){return ht(0,this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=Q(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const r=this._$angle*D;let n=v.abs(v.cos(r)*this._$distance),a=v.abs(v.sin(r)*this._$distance);return e&&(n*=e),i&&(a*=i),s.xMin=v.min(s.xMin,n),n>0&&(s.xMax+=n),s.yMin=v.min(s.yMin,a),a>0&&(s.yMax+=a),s}_$canApply(){return this._$strength>0&&0!==this._$distance&&this._$blurFilter._$canApply()}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error("the current attachment is null.");t.setTransform(1,0,0,1,0,0);const r=i.getTextureFromCurrentAttachment();if(!this._$canApply())return r;const n=s.width,a=s.height,h=t._$offsetX,o=t._$offsetY;let l=v.sqrt(e[0]*e[0]+e[1]*e[1]),c=v.sqrt(e[2]*e[2]+e[3]*e[3]);l/=f,c/=f,l*=2,c*=2;const _=this._$angle*D,$=v.cos(_)*this._$distance*l,u=v.sin(_)*this._$distance*c,d=i.createTextureAttachment(n,a);t._$bind(d),t.reset(),t.drawImage(r,0,0,n,a),t.globalCompositeOperation="erase",t.drawImage(r,2*$,2*u,n,a);const g=this._$blurFilter._$applyFilter(t,e,!1),p=g.width,m=g.height,x=v.ceil(p+2*v.abs($)),b=v.ceil(m+2*v.abs(u)),T="inner"===this._$type,y=T?n:x,E=T?a:b,A=v.abs($),M=v.abs(u),S=(p-n)/2,w=(m-a)/2,C=T?0:A+S,I=T?0:M+w,F=T?-S-$:A-$,R=T?-w-u:M-u;return t._$bind(s),i.releaseAttachment(d,!0),t._$applyBitmapFilter(g,y,E,n,a,C,I,p,m,F,R,!1,this._$type,this._$knockout,this._$strength,null,null,null,vt(this._$highlightColor,this._$highlightAlpha,!0),Tt(this._$highlightColor,this._$highlightAlpha,!0),yt(this._$highlightColor,this._$highlightAlpha,!0),this._$highlightAlpha,vt(this._$shadowColor,this._$shadowAlpha,!0),Tt(this._$shadowColor,this._$shadowAlpha,!0),yt(this._$shadowColor,this._$shadowAlpha,!0),this._$shadowAlpha),t._$offsetX=h+C,t._$offsetY=o+I,i.releaseTexture(g),i.getTextureFromCurrentAttachment()}}class qt extends Gt{constructor(t=null){super(),this._$matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],this.matrix=t}static toString(){return"[class ColorMatrixFilter]"}static get namespace(){return"next2d.filters.ColorMatrixFilter"}toString(){return"[object ColorMatrixFilter]"}get namespace(){return"next2d.filters.ColorMatrixFilter"}get matrix(){return this._$matrix}set matrix(t){if(t&&T.isArray(t)&&20===t.length){for(let e=0;e<20;++e)if(t[e]!==this._$matrix[e]){this._$doChanged();break}this._$matrix=t}}clone(){return new qt(this._$matrix)}_$toArray(){return ht(2,this._$matrix)}_$generateFilterRect(t){return t}_$canApply(){return!0}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment(),r=s.width,n=s.height,a=e.createTextureAttachment(r,n);return t._$bind(a),t.reset(),t._$applyColorMatrixFilter(s,this._$matrix),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()}}class Yt extends Gt{constructor(t=0,e=0,i=null,s=1,r=0,n=!0,a=!0,h=0,o=0){super(),this._$matrixX=0,this._$matrixY=0,this._$matrix=null,this._$divisor=1,this._$bias=0,this._$preserveAlpha=!0,this._$clamp=!0,this._$color=0,this._$alpha=0,this.matrixX=t,this.matrixY=e,this.matrix=i,this.divisor=s,this.bias=r,this.preserveAlpha=n,this.clamp=a,this.color=h,this.alpha=o}static toString(){return"[class ConvolutionFilter]"}static get namespace(){return"next2d.filters.ConvolutionFilter"}toString(){return"[object ConvolutionFilter]"}get namespace(){return"next2d.filters.ConvolutionFilter"}get alpha(){return this._$alpha}set alpha(t){(t=dt(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get bias(){return this._$bias}set bias(t){t!==this._$bias&&(this._$bias=0|t,this._$doChanged())}get clamp(){return this._$clamp}set clamp(t){t!==this._$clamp&&(this._$clamp=!!t,this._$doChanged())}get color(){return this._$color}set color(t){(t=dt(xt(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get divisor(){return this._$divisor}set divisor(t){t!==this._$divisor&&(this._$divisor=0|t,this._$doChanged())}get matrix(){return this._$matrix}set matrix(t){T.isArray(this._$matrix)&&ot(this._$matrix),this._$matrix=T.isArray(t)?t:null,this._$doChanged()}get matrixX(){return this._$matrixX}set matrixX(t){(t=0|dt(0|t,0,15,0))!==this._$matrixX&&(this._$matrixX=t,this._$doChanged())}get matrixY(){return this._$matrixY}set matrixY(t){(t=0|dt(0|t,0,15,0))!==this._$matrixY&&(this._$matrixY=t,this._$doChanged())}get preserveAlpha(){return this._$preserveAlpha}set preserveAlpha(t){t!==this._$preserveAlpha&&(this._$preserveAlpha=!!t,this._$doChanged())}clone(){return new Yt(this._$matrixX,this._$matrixY,this._$matrix?this._$matrix.slice():null,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$toArray(){return ht(3,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$matrix&&this._$matrixX*this._$matrixY===this._$matrix.length}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment();return this._$canApply()&&this._$matrix?(t._$applyConvolutionFilter(s,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,vt(this._$color,this._$alpha,!1),Tt(this._$color,this._$alpha,!1),yt(this._$color,this._$alpha,!1),this._$alpha),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()):s}}class Ht extends Gt{constructor(t=null,e=null,i=0,s=0,r=0,n=0,a="wrap",h=0,o=0){super(),this._$mapBitmap=null,this._$mapPoint=null,this._$componentX=0,this._$componentY=0,this._$scaleX=0,this._$scaleY=0,this._$mode="wrap",this._$color=0,this._$alpha=0,this.mapBitmap=t,this.mapPoint=e,this.componentX=i,this.componentY=s,this.scaleX=r,this.scaleY=n,this.mode=a,this.color=h,this.alpha=o}static toString(){return"[class DisplacementMapFilter]"}static get namespace(){return"next2d.filters.DisplacementMapFilter"}toString(){return"[object DisplacementMapFilter]"}get namespace(){return"next2d.filters.DisplacementMapFilter"}get alpha(){return this._$alpha}set alpha(t){(t=dt(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get color(){return this._$color}set color(t){(t=dt(xt(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get componentX(){return this._$componentX}set componentX(t){t!==this._$componentX&&(this._$componentX=t,this._$doChanged())}get componentY(){return this._$componentY}set componentY(t){t!==this._$componentY&&(this._$componentY=t,this._$doChanged())}get mapBitmap(){return this._$mapBitmap}set mapBitmap(t){t!==this._$mapBitmap&&(this._$mapBitmap=t,this._$doChanged())}get mapPoint(){return this._$mapPoint}set mapPoint(t){t!==this._$mapPoint&&(this._$mapPoint=t,this._$doChanged())}get mode(){return this._$mode}set mode(t){t!==this._$mode&&(this._$mode=t,this._$doChanged())}get scaleX(){return this._$scaleX}set scaleX(t){(t=dt(+t,-65535,65535,0))!==this._$scaleX&&(this._$scaleX=t,this._$doChanged())}get scaleY(){return this._$scaleY}set scaleY(t){(t=dt(+t,-65535,65535,0))!==this._$scaleY&&(this._$scaleY=t,this._$doChanged())}clone(){return new Ht(this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$toArray(){return ht(4,this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$mapBitmap&&this._$componentX>0&&this._$componentY>0&&0!==this._$scaleX&&0!==this._$scaleY}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;t.setTransform(1,0,0,1,0,0);const r=i.getTextureFromCurrentAttachment();if(!this._$canApply()||!s||!this._$mapBitmap)return r;const n=v.sqrt(e[0]*e[0]+e[1]*e[1]),a=v.sqrt(e[2]*e[2]+e[3]*e[3]);return t._$applyDisplacementMapFilter(r,this._$mapBitmap,r.width/n,r.height/a,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,vt(this._$color,this._$alpha,!0),Tt(this._$color,this._$alpha,!0),yt(this._$color,this._$alpha,!0),this._$alpha),i.releaseAttachment(s,!0),i.getTextureFromCurrentAttachment()}}class jt extends Gt{constructor(t=4,e=45,i=0,s=1,r=4,n=4,a=1,h=1,o=!1,l=!1,c=!1){super(),this._$blurFilter=new zt(r,n,h),this._$distance=4,this._$angle=45,this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this._$hideObject=!1,this.distance=t,this.angle=e,this.color=i,this.alpha=s,this.strength=a,this.inner=o,this.knockout=l,this.hideObject=c}static toString(){return"[class DropShadowFilter]"}static get namespace(){return"next2d.filters.DropShadowFilter"}toString(){return"[object DropShadowFilter]"}get namespace(){return"next2d.filters.DropShadowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=dt(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=dt(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=dt(xt(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get distance(){return this._$distance}set distance(t){(t=dt(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get hideObject(){return this._$hideObject}set hideObject(t){t!==this._$hideObject&&(this._$hideObject=!!t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=dt(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new jt(this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$toArray(){return ht(5,this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=Q(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const r=this._$angle*D;let n=v.cos(r)*this._$distance,a=v.sin(r)*this._$distance;return e&&(n*=e),i&&(a*=i),s.xMin=v.min(s.xMin,n),n>0&&(s.xMax+=n),s.yMin=v.min(s.yMin,a),a>0&&(s.yMax+=a),s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(t,e){const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error("the current attachment is null.");if(t.setTransform(1,0,0,1,0,0),!this._$canApply())return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),l=o.width,c=o.height,_=t._$offsetX,$=t._$offsetY,u=_-a,d=$-h;let g=v.sqrt(e[0]*e[0]+e[1]*e[1]),p=v.sqrt(e[2]*e[2]+e[3]*e[3]);g/=f,p/=f,g*=2,p*=2;const m=this._$angle*D,x=v.cos(m)*this._$distance*g,b=v.sin(m)*this._$distance*p,T=this._$inner?r:l+v.max(0,v.abs(x)-u),y=this._$inner?n:c+v.max(0,v.abs(b)-d),E=v.ceil(T),A=v.ceil(y),M=(E-T)/2,S=(A-y)/2,w=this._$inner?0:v.max(0,u-x)+M,C=this._$inner?0:v.max(0,d-b)+S,I=this._$inner?x-_:(x>0?v.max(0,x-u):0)+M,F=this._$inner?b-$:(b>0?v.max(0,b-d):0)+S;let R,B;return this._$inner?(R="inner",B=this._$knockout||this._$hideObject):!this._$knockout&&this._$hideObject?(R="full",B=!0):(R="outer",B=this._$knockout),t._$bind(s),t._$applyBitmapFilter(o,E,A,r,n,w,C,l,c,I,F,!0,R,B,this._$strength,null,null,null,vt(this._$color,this._$alpha,!0),Tt(this._$color,this._$alpha,!0),yt(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),t._$offsetX=a+w,t._$offsetY=h+C,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class Wt extends Gt{constructor(t=0,e=1,i=4,s=4,r=1,n=1,a=!1,h=!1){super(),this._$blurFilter=new zt(i,s,n),this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this.color=t,this.alpha=e,this.strength=r,this.inner=a,this.knockout=h}static toString(){return"[class GlowFilter]"}static get namespace(){return"next2d.filters.GlowFilter"}toString(){return"[object GlowFilter]"}get namespace(){return"next2d.filters.GlowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=dt(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=dt(xt(t),0,16777215,4))!==this._$color&&(this._$color=t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=dt(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new Wt(this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$toArray(){return ht(6,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){const s=Q(t.xMin,t.xMax,t.yMin,t.yMax);return this._$canApply()?this._$blurFilter._$generateFilterRect(s,e,i):s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(t,e){const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error("the current attachment is null.");if(this._$updated=!1,t.setTransform(1,0,0,1,0,0),!this._$canApply())return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),l=o.width,c=o.height,_=t._$offsetX,$=t._$offsetY,u=this._$inner?r:l,d=this._$inner?n:c,g=this._$inner?0:_-a,f=this._$inner?0:$-h,p=this._$inner?-_:0,m=this._$inner?-$:0,x=this._$inner?"inner":"outer";return t._$bind(s),t._$applyBitmapFilter(o,u,d,r,n,g,f,l,c,p,m,!0,x,this._$knockout,this._$strength,null,null,null,vt(this._$color,this._$alpha,!0),Tt(this._$color,this._$alpha,!0),yt(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),t._$offsetX=a+g,t._$offsetY=h+f,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class Kt extends Gt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,l="inner",c=!1){super(),this._$blurFilter=new zt(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=l,this.knockout=c}static toString(){return"[class GradientBevelFilter]"}static get namespace(){return"next2d.filters.GradientBevelFilter"}toString(){return"[object GradientBevelFilter]"}get namespace(){return"next2d.filters.GradientBevelFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,T.isArray(t)){for(let e=0;e0&&(s.xMax+=n),s.yMin=v.min(s.yMin,a),a>0&&(s.yMax+=a),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;t.setTransform(1,0,0,1,0,0);const r=i.getTextureFromCurrentAttachment();if(!this._$canApply()||!s)return r;const n=s.width,a=s.height,h=t._$offsetX,o=t._$offsetY;let l=v.sqrt(e[0]*e[0]+e[1]*e[1]),c=v.sqrt(e[2]*e[2]+e[3]*e[3]);l/=f,c/=f,l*=2,c*=2;const _=+this._$angle*D,$=+v.cos(_)*this._$distance*l,u=+v.sin(_)*this._$distance*c,d=i.createTextureAttachment(n,a);t._$bind(d),t.reset(),t.drawImage(r,0,0,n,a),t.globalCompositeOperation="erase",t.drawImage(r,2*$,2*u,n,a);const g=this._$blurFilter._$applyFilter(t,e,!1),p=g.width,m=g.height,x=v.ceil(p+2*v.abs($)),b=v.ceil(m+2*v.abs(u)),T="inner"===this._$type,y=T?n:x,E=T?a:b,A=v.abs($),M=v.abs(u),S=(p-n)/2,w=(m-a)/2,C=T?0:A+S,I=T?0:M+w,F=T?-S-$:A-$,R=T?-w-u:M-u;return t._$bind(s),t._$applyBitmapFilter(g,y,E,n,a,C,I,p,m,F,R,!1,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),t._$offsetX=h+C,t._$offsetY=o+I,i.releaseAttachment(d,!0),i.getTextureFromCurrentAttachment()}}class Qt extends Gt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,l="inner",c=!1){super(),this._$blurFilter=new zt(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=l,this.knockout=c}static toString(){return"[class GradientGlowFilter]"}static get namespace(){return"next2d.filters.GradientGlowFilter"}toString(){return"[object GradientGlowFilter]"}get namespace(){return"next2d.filters.GradientGlowFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,T.isArray(t)){for(let e=0;e0&&(s.xMax+=n),s.yMin=v.min(s.yMin,a),a>0&&(s.yMax+=a),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;if(t.setTransform(1,0,0,1,0,0),!this._$canApply()||!s)return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),l=o.width,c=o.height,_=t._$offsetX,$=t._$offsetY,u=_-a,d=$-h;let g=v.sqrt(e[0]*e[0]+e[1]*e[1]),p=v.sqrt(e[2]*e[2]+e[3]*e[3]);g/=f,p/=f,g*=2,p*=2;const m=+this._$angle*D,x=+v.cos(m)*this._$distance*g,b=+v.sin(m)*this._$distance*p,T="inner"===this.type,y=T?r:l+v.max(0,v.abs(x)-u),E=T?n:c+v.max(0,v.abs(b)-d),A=v.ceil(y),M=v.ceil(E),S=(A-y)/2,w=(M-E)/2,C=T?0:v.max(0,u-x)+S,I=T?0:v.max(0,d-b)+w,F=T?x-_:(x>0?v.max(0,x-u):0)+S,R=T?b-$:(b>0?v.max(0,b-d):0)+w;return t._$bind(s),t._$applyBitmapFilter(o,A,M,r,n,C,I,l,c,F,R,!0,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),t._$offsetX=a+C,t._$offsetY=h+I,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class Jt{constructor(t){this._$displayObject=t,this._$matrix=null,this._$colorTransform=null,this._$blendMode=null,this._$filters=null}static toString(){return"[class Transform]"}static get namespace(){return"next2d.geom.Transform"}toString(){return"[object Transform]"}get namespace(){return"next2d.geom.Transform"}get colorTransform(){if(this._$colorTransform)return this._$colorTransform._$clone();const t=this._$displayObject,e=t._$placeObject||t._$getPlaceObject();if(e&&e.colorTransform){const t=e.colorTransform;return dr(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7])}return this._$transform(),this._$colorTransform||(this._$colorTransform=dr()),this._$colorTransform._$clone()}set colorTransform(t){this._$transform(null,t._$colorTransform)}get concatenatedColorTransform(){let t=this._$rawColorTransform(),e=this._$displayObject._$parent;for(;e;)t=ft(e._$transform._$rawColorTransform(),t),e=e._$parent;return dr(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7])}get matrix(){if(this._$matrix)return this._$matrix._$clone();const t=this._$displayObject,e=t._$placeObject||t._$getPlaceObject();if(e&&e.matrix){const t=e.matrix;return $r(t[0],t[1],t[2],t[3],t[4],t[5])}return this._$transform(),this._$matrix||(this._$matrix=$r()),this._$matrix._$clone()}set matrix(t){this._$transform(t._$matrix,null)}get concatenatedMatrix(){let t=this._$rawMatrix(),e=this._$displayObject._$parent;for(;e;)t=gt(e._$transform._$rawMatrix(),t),e=e._$parent;return $r(t[0],t[1],t[2],t[3],t[4],t[5])}pixelBounds(){if(!this._$displayObject)return new Vt(0,0,0,0);const t=this._$displayObject._$getBounds(null),e=new Vt(t.xMin,t.yMin,+v.abs(t.xMax-t.xMin),+v.abs(t.yMax-t.yMin));return J(t),e}_$rawMatrix(){if(null!==this._$matrix)return this._$matrix._$matrix;const t=this._$displayObject,e=t._$placeObject||t._$getPlaceObject();if(e&&e.matrix){if(T.isArray(e.matrix)){const t=e.matrix;e.matrix=it(t[0],t[1],t[2],t[3],t[4],t[5]),ot(t)}return e.matrix}return P}_$rawColorTransform(){if(null!==this._$colorTransform)return this._$colorTransform._$colorTransform;const t=this._$displayObject,e=t._$placeObject||t._$getPlaceObject();if(e&&e.colorTransform){if(T.isArray(e.colorTransform)){const t=e.colorTransform;e.colorTransform=rt(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7]),ot(t)}return e.colorTransform}return k}_$transform(t=null,e=null,i=null,s=""){const r=this._$displayObject,n=r._$placeObject||r._$getPlaceObject();this._$setMatrix(t,n),this._$setColorTransform(e,n),this._$setFilters(i,n),this._$setBlendMode(s,n)}_$setMatrix(t=null,e=null){if((t||e)&&(this._$displayObject._$doChanged(),x()),this._$matrix||(this._$matrix=$r(1,0,0,1,0,0),!t&&e&&e.matrix&&(t=e.matrix)),t){const e=this._$matrix._$matrix;e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5]}}_$setColorTransform(t=null,e=null){if((t||e)&&(this._$displayObject._$doChanged(),x()),this._$colorTransform||(this._$colorTransform=dr(1,1,1,1,0,0,0,0),!t&&e&&e.colorTransform&&(t=e.colorTransform)),t){const e=this._$colorTransform._$colorTransform;e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7]}}_$setFilters(t=null,e=null){if(T.isArray(t))return this._$filters&&ot(this._$filters),this._$filters=t.slice(0),this._$displayObject._$doChanged(),void x();if(!this._$filters)if(e){if(e.filters){this._$filters=e.filters.slice(0);for(let t=0;t-1){const e="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),i=this.rotation,s=i?pt(e,this._$transform._$rawMatrix()):e;i&&J(e);const r=v.abs(s.yMax-s.yMin);switch(J(s),r){case 0:case b:case-1/0:this.scaleY=0;break;default:this.scaleY=t/r}}}get loaderInfo(){return this._$loaderInfo}get mask(){return this._$mask}set mask(t){t!==this._$mask&&(this._$mask&&(Mr&&this._$mask.stage&&this._$mask._$removeWorkerInstance(),this._$mask._$isMask=!1,this._$mask=null),t&&(Mr&&"_$createWorkerInstance"in t&&"function"==typeof t._$createWorkerInstance&&t._$createWorkerInstance(),t._$isMask=!0,this._$mask=t),this._$doChanged())}get mouseX(){return l()?this.globalToLocal(_r()).x:0}get mouseY(){return l()?this.globalToLocal(_r()).y:0}get name(){return this._$name?this._$name:`instance${this._$instanceId}`}set name(t){this._$name=`${t}`;const e=this._$parent;if(e&&e._$names){e._$names.clear();const t=e._$getChildren();for(let i=0;it[0]?-1*e:e}set scaleX(t){if(t=dt(+t,N,O),!E.isInteger(t)){const e=t.toString(),i=e.indexOf("e");-1!==i&&(t=+e.slice(0,i)),t=+t.toFixed(4)}if(this._$scaleX===t)return;const e=this._$transform,i=null!==e._$matrix,s=i?e._$matrix:e.matrix;if(0===s.b||C(s.b))s.a=t;else{let e=v.atan2(s.b,s.a);e===-v.PI&&(e=0),s.b=t*v.sin(e),s.a=t*v.cos(e)}i?(this._$doChanged(),x()):(e.matrix=s,ur(s)),this._$scaleX=t}get scaleY(){if(null!==this._$scaleY)return this._$scaleY;const t=this._$transform._$rawMatrix();let e=v.sqrt(t[2]*t[2]+t[3]*t[3]);if(!E.isInteger(e)){const t=e.toString(),i=t.indexOf("e");-1!==i&&(e=+t.slice(0,i)),e=+e.toFixed(4)}return 0>t[3]?-1*e:e}set scaleY(t){if(t=dt(+t,N,O),!E.isInteger(t)){const e=t.toString(),i=e.indexOf("e");-1!==i&&(t=+e.slice(0,i)),t=+t.toFixed(4)}if(this._$scaleY===t)return;const e=this._$transform,i=null!==e._$matrix,s=i?e._$matrix:e.matrix;if(0===s.c||C(s.c))s.d=t;else{let e=v.atan2(-s.c,s.d);e===-v.PI&&(e=0),s.c=-t*v.sin(e),s.d=t*v.cos(e)}i?(this._$doChanged(),x()):(e.matrix=s,ur(s)),this._$scaleY=t}get stage(){if(this._$stage)return this._$stage;const t=this._$parent;return t?t._$stage:null}get transform(){return this._$transform}set transform(t){this._$transform=t}get visible(){return this._$visible}set visible(t){this._$visible!==t&&(this._$visible=!!t,this._$doChanged(),x())}get width(){const t="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),e=pt(t,this._$transform._$rawMatrix());J(t);const i=v.abs(e.xMax-e.xMin);switch(J(e),!0){case 0===i:case i===b:case i===-1/0:return 0;default:return+i.toFixed(2)}}set width(t){if(!C(t=+t)&&t>-1){const e="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),i=this.rotation,s=i?pt(e,this._$transform._$rawMatrix()):e;i&&J(e);const r=v.abs(s.xMax-s.xMin);switch(J(s),!0){case 0===r:case r===b:case r===-1/0:this.scaleX=0;break;default:this.scaleX=t/r}}}get x(){return this._$transform._$rawMatrix()[4]}set x(t){const e=this._$transform;if(e._$matrix)e._$matrix.tx=t,this._$doChanged(),x();else{const i=e.matrix;i.tx=t,e.matrix=i,ur(i)}}get y(){return this._$transform._$rawMatrix()[5]}set y(t){const e=this._$transform;if(e._$matrix)e._$matrix.ty=t,this._$doChanged(),x();else{const i=e.matrix;i.ty=t,e.matrix=i,ur(i)}}getBounds(t=null){const e="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),i=this._$transform.concatenatedMatrix,s=pt(e,i._$matrix);ur(i),J(e);const r=Q(s.xMin,s.xMax,s.yMin,s.yMax);J(s),t||(t=this);const n=t._$transform.concatenatedMatrix;n.invert();const a=pt(r,n._$matrix);J(r),ur(n);const h=a.xMin,o=a.yMin,l=a.xMax,c=a.yMax;return J(a),new Vt(h,o,v.abs(l-h),v.abs(c-o))}globalToLocal(t){const e=this._$transform.concatenatedMatrix;e.invert();const i=new Dt(t.x*e.a+t.y*e.c+e.tx,t.x*e.b+t.y*e.d+e.ty);return ur(e),i}hitTestObject(t){const e="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),i=this._$transform.concatenatedMatrix,s=pt(e,i._$matrix);ur(i),J(e);const r=t._$getBounds(null),n=t._$transform.concatenatedMatrix,a=pt(r,n._$matrix);ur(n),J(r);const h=v.max(s.xMin,a.xMin),o=v.max(s.yMin,a.yMin),l=v.min(s.xMax,a.xMax),c=v.min(s.yMax,a.yMax);return J(s),J(a),l-h>=0&&c-o>=0}hitTestPoint(t,e,i=!1){if(i){let i=P,s=this._$parent;for(;s;)i=gt(s._$transform._$rawMatrix(),i),s=s._$parent;rr.setTransform(1,0,0,1,0,0),rr.beginPath();let r=!1;return"_$hit"in this&&"function"==typeof this._$hit&&(r=this._$hit(rr,i,{x:t,y:e},!0)),i!==P&&st(i),r}const s="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),r=pt(s,this._$transform._$rawMatrix());J(s);const n=new Vt(r.xMin,r.yMin,r.xMax-r.xMin,r.yMax-r.yMin);J(r);const a=this._$parent?this._$parent.globalToLocal(new Dt(t,e)):new Dt(t,e);return n.containsPoint(a)}localToGlobal(t){const e=this._$transform.concatenatedMatrix,i=new Dt(t.x*e.a+t.y*e.c+e.tx,t.x*e.b+t.y*e.d+e.ty);return ur(e),i}getLocalVariable(t){return this._$variables?this._$variables.has(t)?this._$variables.get(t):void 0:null}setLocalVariable(t,e){this._$variables||(this._$variables=ct()),this._$variables.set(t,e)}hasLocalVariable(t){return!!this._$variables&&this._$variables.has(t)}deleteLocalVariable(t){this._$variables&&this._$variables.has(t)&&(this._$variables.delete(t),this._$variables.size||(lt(this._$variables),this._$variables=null))}getGlobalVariable(t){return Os.has(t)?Os.get(t):null}setGlobalVariable(t,e){Os.set(t,e)}hasGlobalVariable(t){return Os.has(t)}deleteGlobalVariable(t){Os.has(t)&&Os.delete(t)}clearGlobalVariable(){return Os.clear()}_$getPlaceObject(){if(!this._$placeObject){const t=this._$placeId;if(-1===t)return null;const e=this._$parent;if(!e||!e._$placeObjects)return null;const i=e._$placeMap;if(!i||!i.length)return null;const s=i["currentFrame"in e?e.currentFrame:1];if(!s)return null;const r=0|s[t],n=e._$placeObjects[r];return n?(this._$changePlace=r!==this._$currentPlaceId,this._$currentPlaceId=r,this._$placeObject=n,n):null}return this._$placeObject}_$baseBuild(t,e){const i=e._$loaderInfo;if(!i||!i._$data)throw new Error("the loaderInfo or data is nul.");return this._$parent=e,this._$root=e._$root,this._$stage=e._$stage,this._$loaderInfo=i,this._$characterId=0|t.characterId,this._$clipDepth=0|t.clipDepth,this._$startFrame=0|t.startFrame,this._$endFrame=0|t.endFrame,this._$name=t.name||"",i._$data.characters[t.characterId]}_$isUpdated(){return this._$updated}_$updateState(){this._$isNext=!0;const t=this._$parent;t&&t._$updateState()}_$doChanged(){this._$posted=!1,this._$isNext=!0,this._$updated=!0;const t=this._$parent;t&&(t._$updated||t._$doChanged())}_$drawFilter(t,e,i,s,r,n=null){const a=ht(this._$instanceId,"f");let h=St.get(a);const o=this._$isFilterUpdated(e,i,!0);if(h&&!o)return t.cachePosition=h,h;h&&St.set(a,null);const l=t.frameBuffer,c=n||t.getTextureFromRect(t.cachePosition),_=this._$applyFilter(t,i,c,e,s,r);l.textureManager.release(c);const $=this._$getLayerBounds(e);return h=l.createCachePosition(v.ceil(v.abs($.xMax-$.xMin)),v.ceil(v.abs($.yMax-$.yMin))),J($),h.filterState=!0,h.matrix=`${e[0]}_${e[1]}_${e[2]}_${e[3]}_0_0`,h.offsetX=_.offsetX,h.offsetY=_.offsetY,t.drawTextureFromRect(_,h),St.set(a,h),ot(a),h}_$getLayerBounds(t){const e="_$getBounds"in this&&"function"==typeof this._$getBounds?this._$getBounds():Q(),i=pt(e,t);J(e);const s=this._$filters||this.filters;if(!s.length)return i;let r=Q(0,v.abs(i.xMax-i.xMin),0,v.abs(i.yMax-i.yMin));J(i);let n=+v.sqrt(t[0]*t[0]+t[1]*t[1]),a=+v.sqrt(t[2]*t[2]+t[3]*t[3]);n/=f,a/=f,n*=2,a*=2;for(let t=0;t-1&&(t.depth=this._$placeId),this._$clipDepth&&(t.clipDepth=this._$clipDepth),this._$isMask&&(t.isMask=this._$isMask);const e=this._$mask;if(e){t.maskId=e._$instanceId;let i=P,s=e._$parent;for(;s;)i=gt(s._$transform._$rawMatrix(),i),s=s._$parent;t.maskMatrix=i}if(this._$visible){const e=this._$transform,i=e._$rawMatrix();1!==i[0]&&(t.a=i[0]),0!==i[1]&&(t.b=i[1]),0!==i[2]&&(t.c=i[2]),1!==i[3]&&(t.d=i[3]),0!==i[4]&&(t.tx=i[4]),0!==i[5]&&(t.ty=i[5]);const s=e._$rawColorTransform();1!==s[0]&&(t.f0=s[0]),1!==s[1]&&(t.f1=s[1]),1!==s[2]&&(t.f2=s[2]),1!==s[3]&&(t.f3=s[3]),0!==s[4]&&(t.f4=s[4]),0!==s[5]&&(t.f5=s[5]),0!==s[6]&&(t.f6=s[6]),0!==s[7]&&(t.f7=s[7]);const r=this._$filters||this.filters;if(r&&r.length){const e=ht();for(let t=0;tt._$names.size&&t._$names.has(e)?t._$names.get(e):t[e]})}get mouseChildren(){return this._$mouseChildren}set mouseChildren(t){this._$mouseChildren=!!t}get numChildren(){return this._$needsChildren?this._$getChildren().length:this._$children.length}addChild(t){return t._$parent&&t._$parent._$remove(t,!(t._$parent._$instanceId===this._$instanceId)),this._$getChildren().push(t),t._$name&&this._$names.set(t._$name,t),this._$addChild(t)}addChildAt(t,e){t._$parent&&t._$parent._$remove(t,!(t._$parent._$instanceId===this._$instanceId));const i=this._$getChildren(),s=i.length;if(0>e||e>s)throw new RangeError(`RangeError: addChildAt: index error: ${e}`);if(s&&s>e){i.splice(e,0,t);for(let t=0;tt||t>e.length)throw new RangeError(`RangeError: getChildAt: index error: ${t}`);return t in e?e[t]:null}getChildByName(t){if(!t)return null;const e=this._$getChildren();for(let i=0;it;--s)this._$remove(i[s])}}setChildIndex(t,e){const i=this.getChildIndex(t);if(i===e)return;const s=this._$getChildren();s.splice(i,1),s.splice(e,0,t),Mr&&this._$postChildrenIds(),this._$doChanged()}swapChildren(t,e){const i=this._$getChildren(),s=this.getChildIndex(t),r=this.getChildIndex(e);i[s]=e,i[r]=t,Mr&&this._$postChildrenIds(),this._$doChanged()}swapChildrenAt(t,e){this.swapChildren(this.getChildAt(t),this.getChildAt(e))}_$getBounds(t=null){let e=P;if(t){e=t;const i=this._$transform._$rawMatrix();1===i[0]&&0===i[1]&&0===i[2]&&1===i[3]&&0===i[4]&&0===i[5]||(e=gt(t,i))}const i=this._$needsChildren?this._$getChildren():this._$children;if(!i.length){const i=Q(e[4],-e[4],e[5],-e[5]);return t&&e!==t&&st(e),i}const s=E.MAX_VALUE;let r=s,n=-s,a=s,h=-s;for(let t=0;te){if(l._$isNext=!0,l._$placeObject=null,l._$filters=null,l._$blendMode=null,-1===l._$id){h.push(l),l._$name&&this._$names.set(l._$name,l);continue}const t=i[a];if(l._$id===t){l._$placeId=a,h.push(l),l._$name&&this._$names.set(l._$name,l),n.has(t)&&n.delete(t),r.set(t,!0),a++,s&&l._$postProperty();continue}n.set(l._$id,l)}else s&&l._$removeWorkerInstance(),St.setRemoveTimer(_),l._$loaderInfo&&l._$characterId&&St.setRemoveTimer(`${l._$loaderInfo._$id}@${l._$characterId}`),l._$graphics&&St.setRemoveTimer(l._$graphics._$uniqueKey),l.willTrigger(It.REMOVED)&&l.dispatchEvent(new It(It.REMOVED,!0)),l.willTrigger(It.REMOVED_FROM_STAGE)&&l.dispatchEvent(new It(It.REMOVED_FROM_STAGE,!0)),l._$added=!1,l._$addedStage=!1,l._$active=!1,l._$updated=!0,l._$filters=null,l._$blendMode=null,l._$isNext=!0,l._$placeObject=null,l._$created=!1,l._$posted=!1,l instanceof ee&&(l._$executeRemovedFromStage(),l._$removeParentAndStage())}if(i)for(let t=0;t-1;--e)t[e]._$prepareActions();this._$executeAddedEvent()}_$nextFrame(){let t=!1;const e=this._$getChildren();for(let i=e.length-1;i>-1;--i){const s=e[i];s._$isNext&&(t?s._$nextFrame():t=s._$nextFrame())}return this._$executeAddedEvent(),this._$isNext=t,!this._$posted&&Mr&&this._$postProperty(),this._$isNext}_$clip(t,e){let i=e;const s=this._$transform._$rawMatrix();1===s[0]&&0===s[1]&&0===s[2]&&1===s[3]&&0===s[4]&&0===s[5]||(i=gt(e,s));const r=this._$getChildren();for(let e=0;e0||"normal"!==a){const s=this._$getBounds(null),h=pt(s,i);J(s);const o=+h.xMax,l=+h.xMin,c=+h.yMax,_=+h.yMin;J(h);const $=v.ceil(v.abs(o-l)),u=v.ceil(v.abs(c-_));if(0>=$||0>=u)return Mt(r),i!==e&&st(i),null;let d=+v.sqrt(i[0]*i[0]+i[1]*i[1]);if(!E.isInteger(d)){const t=d.toString(),e=t.indexOf("e");-1!==e&&(d=+t.slice(0,e)),d=+d.toFixed(4)}let g=+v.sqrt(i[2]*i[2]+i[3]*i[3]);if(!E.isInteger(g)){const t=g.toString(),e=t.indexOf("e");-1!==e&&(g=+t.slice(0,e)),g=+g.toFixed(4)}r.canApply=this._$canApply(n);let f=Q(0,$,0,u);if(r.canApply&&n)for(let t=0;tp.width||_-f.yMin>p.height)return J(f),Mt(r),i!==e&&st(i),null;if(0>l+f.xMax||0>_+f.yMax)return J(f),Mt(r),i!==e&&st(i),null;let m=i[4]-l,x=i[5]-_;t._$startLayer(Q(l,o,_,c));const b=this._$isFilterUpdated(i,n,r.canApply),T=this._$getLayerBounds(i),y=v.ceil(v.abs(T.xMax-T.xMin)),A=v.ceil(v.abs(T.yMax-T.yMin));J(T);const M=y-f.xMax+f.xMin,S=A-f.yMax+f.yMin;m+=M,x+=S,r.sw=M,r.sh=S,b&&t._$saveAttachment(v.ceil($+M),v.ceil(u+S),!0),r.isLayer=!0,r.isUpdated=b,r.filters=n,r.blendMode=a,r.color=rt(),r.matrix=it(i[0],i[1],i[2],i[3],m,x),i!==e&&st(i),J(f)}return r}_$postDraw(t,e,i,s){t.drawInstacedArray();const r=ht(this._$instanceId,"f"),n=t.frameBuffer,a=s.matrix;let h=0,o=0,l=St.get(r);if(!l||s.isUpdated){l&&St.set(r,null),l=n.getTextureFromCurrentAttachment();const i=s.filters;let c=!1;if(i&&i.length){for(let s=0;s_||i._$clipDepth>0)&&(t.restore(),c&&t._$leaveClip(),_=0,c=!0),!c)continue;if(i._$clipDepth>0){_=i._$clipDepth,c=i._$shouldClip(o),c&&(t.save(),c=i._$startClip(t,o));continue}const r=i._$mask;if(r){let e;if(r._$updated=!1,this===r._$parent)e=o;else{e=P;let i=r._$parent;for(;i;)e=gt(i._$transform._$rawMatrix(),e),i=i._$parent;const s=$.scaleX,n=it(s,0,0,s,0,0);if(e=gt(n,e),st(n),t.isLayer){const i=t.getCurrentPosition();e[4]-=i.xMin,e[5]-=i.yMin}}if(!r._$shouldClip(e))continue;const i=r._$startClip(t,e);if(t.save(),!i){t.restore();continue}}i._$draw(t,o,l),i._$updated=!1,r&&(t.restore(),t._$leaveClip())}if(_&&(t.restore(),c&&t._$leaveClip()),h.isLayer)return this._$postDraw(t,e,s,h);h.matrix!==e&&st(h.matrix),s!==i&&nt(s),Mt(h)}_$mouseHit(t,e,i,s=!0){let r=e;const n=this._$transform._$rawMatrix();n!==P&&(r=gt(e,n));const a=this._$getChildren(),h=ht(),o=ht(),l=ct();let c=0,_=0;for(let t=0;tc&&(_=0,c=0),_&&l.set(e._$instanceId,_),o.push(e)))}const $=this._$mouseChildren&&s;let u=!1;const d=this._$root===this;for(;o.length;){const e=o.pop();if(e._$isMask)continue;if(d&&!(e instanceof te))continue;if(l.has(e._$instanceId)){const s=l.get(e._$instanceId);if(!s)continue;if(!h[s]._$hit(t,r,i,!0))continue}const s=e._$mask;if(s)if(this===s._$parent){if(!s._$hit(t,r,i,!0))continue}else{let e=P,r=s._$parent;for(;r;)e=gt(r._$transform._$rawMatrix(),e),r=r._$parent;if(!s._$hit(t,e,i,!0))continue}if(e._$mouseHit(t,r,i,$)||e._$hitArea&&e._$hitArea._$mouseHit(t,r,i,$)){if(e._$root===e)return!0;if(!$)return!0;if(u=!0,e instanceof te){if(!e.mouseEnabled&&!e._$hitObject)continue;return Zs||i.pointer||("_$text"in e&&"type"in e&&"input"===e.type&&(i.pointer="text"),"buttonMode"in e&&"useHandCursor"in e&&e.buttonMode&&e.useHandCursor&&(i.pointer="pointer")),i.hit||(i.hit=!e.mouseEnabled&&e._$hitObject?e._$hitObject:e),!0}}}return ot(h),ot(o),lt(l),r!==e&&st(r),u}_$hit(t,e,i,s=!1){let r=e;const n=this._$transform._$rawMatrix();n!==P&&(r=gt(e,n));const a=this._$getChildren();for(let e=a.length;e>-1;--e){const n=a[e];if(!n._$isMask&&n._$hit(t,r,i,s))return!0}return r!==e&&st(r),!1}_$createInstance(t){if(!this._$dictionary)throw new Error("the dictionary is null.");const e=this._$dictionary[t],i=this._$loaderInfo;if(!i||!i._$data)throw new Error("the loaderInfo or data is null.");const s=i._$data.characters[e.characterId],r=Ar(s.extends);return r._$build(e,this),r._$id=t,r}_$outCheck(t,e){let i=P,s=this._$parent;for(;s;)i=gt(s._$transform._$rawMatrix(),i),s=s._$parent;rr.setTransform(1,0,0,1,0,0),rr.beginPath();const r={x:t,y:e,pointer:"",hit:null};return this._$mouseHit(rr,i,r)}_$createWorkerInstance(){if(this._$created||!Mr)return;this._$created=!0,this._$posted=!0,this._$updated=!1;let t=0;const e=hr();e[t++]=this._$instanceId,e[t++]=this._$parent?this._$parent._$instanceId:-1,this._$registerProperty(e,2);const i=or();i.command="createDisplayObjectContainer",i.buffer=e;const s=ht(e.buffer);Mr.postMessage(i,s),lr(i),ot(s),this._$postChildrenIds()}_$postProperty(){if(!Mr)return;this._$postChildrenIds();const t=ht(),e=this._$createMessage();Mr.postMessage(e,t),ot(t),this._$posted=!0,this._$updated=!1}_$postChildrenIds(t=null){if(!Mr||!this._$created)return;let e=!1;if(!t){const i=this._$getChildren();t=ht();for(let e=0;eo||a>l)&&(h._$width=n,h._$height=a,h._$resizeCanvas(n,a,h.scaleX));const c=i?i._$colorTransform:k;let _=e?e._$matrix:P;if(e){const e=t._$transform.matrix;e.invert(),_=gt(_,e._$matrix),ur(e)}if(s||(s=St.getCanvas()),Mr){t._$stage||(t instanceof ee?wr&&wr(t):(t._$createWorkerInstance(),t._$postProperty())),s.width=n,s.height=a;const e=s.getContext("2d");if(!e)throw new Error("the context is null.");e.setTransform(1,0,0,1,0,0),e.clearRect(0,0,n,a);const i=t._$instanceId;Hs.set(i,{source:t,context:e,callback:r});const h=ht(),o={command:"bitmapDraw",sourceId:i,width:n,height:a};1===_[0]&&0===_[1]&&0===_[2]&&1===_[3]&&0===_[4]&&0===_[5]||(o.matrix=_.slice(),h.push(o.matrix.buffer)),1===c[0]&&1===c[1]&&1===c[2]&&1===c[3]&&0===c[4]&&0===c[5]&&0===c[6]&&0===c[7]||(o.colorTransform=c.slice(),h.push(o.colorTransform.buffer)),Mr.postMessage(o,h),ot(h)}else{const e=h.context;if(!e)throw new Error("the context is null.");e.reset(),e.setTransform(1,0,0,1,0,0),e._$setColor(0,0,0,0),e.clearRect(0,0,h._$width,h._$height),e.beginPath(),t._$draw(e,_,c),e.drawInstacedArray(),e.frameBuffer.transferToMainTexture(),s.width=n,s.height=a;const i=s.getContext("2d");if(!i)return;i.setTransform(1,0,0,1,0,0),i.clearRect(0,0,n,a),i.drawImage(h.canvas,0,0),r&&r(s)}e&&ur(e),i&&gr(i)}}class se extends Ft{constructor(t,e){super(),this._$name=`${t}`,this._$frame=0|e}static toString(){return"[class FrameLabel]"}static get namespace(){return"next2d.display.FrameLabel"}toString(){return"[object FrameLabel]"}get namespace(){return"next2d.display.FrameLabel"}get frame(){return this._$frame}get name(){return this._$name}}class re{constructor(t,e=null,i=!0,s=!1){this._$bitmapData=t,this._$matrix=e,this._$repeat=i,this._$smooth=s}clone(){return new re(this._$bitmapData.clone(),this._$matrix?this._$matrix.clone():null,this._$repeat,this._$smooth)}toArray(){return ht(this._$bitmapData,this._$matrix,this._$repeat,this._$smooth)}}class ne{constructor(t,e,i,s,r=null,n="pad",a="rgb",h=0){this._$type=t,this._$colors=e,this._$alphas=i,this._$ratios=s,this._$matrix=r,this._$spreadMethod=n,this._$interpolationMethod=a,this._$focalPointRatio=h,this._$colorStops=ht()}get colorStops(){if(!this._$colorStops.length){const t=v.min(v.min(this._$alphas.length,this._$colors.length),this._$ratios.length);for(let e=0;e7)switch(this._$recode||(this._$recode=ht()),this._$fills[2]===this._$fills[this._$fills.length-2]&&this._$fills[3]===this._$fills[this._$fills.length-1]||this._$fills.push(ae.LINE_TO,this._$fills[2],this._$fills[3]),this._$recode.push(...this._$fills),this._$fillType){case ae.FILL_STYLE:this._$recode.push(this._$fillType,this._$fillStyleR,this._$fillStyleG,this._$fillStyleB,this._$fillStyleA,ae.END_FILL);break;case ae.GRADIENT_FILL:this._$fillGradient&&this._$recode.push(this._$fillType,...this._$fillGradient.toArray());break;case ae.BITMAP_FILL:this._$fillBitmap&&this._$recode.push(this._$fillType,...this._$fillBitmap.toArray())}return this._$fills&&(ot(this._$fills),this._$fills=null),this._$fillType=0,this._$fillGradient=null,this._$fillBitmap=null,this._$fillStyleR=0,this._$fillStyleG=0,this._$fillStyleB=0,this._$fillStyleA=0,this._$doFill=!1,this._$restart(),this}endLine(){if(this._$doLine&&this._$lines)switch(this._$recode||(this._$recode=ht()),this._$recode.push(...this._$lines),ot(this._$lines),this._$lines=null,this._$lineType){case ae.STROKE_STYLE:this._$recode.push(this._$lineType,this._$lineWidth,this._$caps,this._$joints,this._$miterLimit,this._$lineStyleR,this._$lineStyleG,this._$lineStyleB,this._$lineStyleA,ae.END_STROKE);break;case ae.GRADIENT_STROKE:this._$lineGradient&&this._$recode.push(this._$lineType,this._$lineWidth,this._$caps,this._$joints,this._$miterLimit,...this._$lineGradient.toArray());break;case ae.BITMAP_STROKE:this._$fillBitmap&&this._$recode.push(this._$lineType,this._$lineWidth,this._$caps,this._$joints,this._$miterLimit,...this._$fillBitmap.toArray())}return this._$lineType=0,this._$lineWidth=0,this._$lineGradient=null,this._$lineStyleR=0,this._$lineStyleG=0,this._$lineStyleB=0,this._$lineStyleA=0,this._$caps="none",this._$joints="round",this._$miterLimit=0,this._$doLine=!1,this._$restart(),this}lineBitmapStyle(t,e=null,i=!0,s=!1){return this._$doLine&&this.endLine(),this._$lines||(this._$lines=ht()),this._$maxAlpha=1,this._$doLine=!0,this._$canDraw=!0,this._$lines.push(ae.BEGIN_PATH),this._$lineType=ae.BITMAP_STROKE,this._$fillBitmap=new re(t,e,i,s),this}lineGradientStyle(t,e,i,s,r=null,n="pad",a="rgb",h=0){if(!this._$doLine)return this;this._$lines||(this._$lines=ht());for(let t=0;t0&&a._$canApply(r);let T=Q(0,g,0,f);if(x&&r)for(let t=0;tA.width||d-T.yMin>A.height)return void J(T);if(0>$+T.xMax||0>d+T.yMax)return void J(T);J(T),""===this._$uniqueKey&&(!h&&a._$loaderInfo&&a._$characterId?this._$uniqueKey=`${a._$loaderInfo._$id}@${this._$bitmapId||a._$characterId}`:this._$uniqueKey=this._$createCacheKey());const M=cr();if("bitmap"===this._$mode)this._$cacheKeys.length||(this._$cacheKeys=St.generateKeys(this._$uniqueKey));else if(!this._$cacheKeys.length||this._$cacheParams[0]!==p||this._$cacheParams[1]!==m||this._$cacheParams[2]!==i[7]){const t=ht();t[0]=p,t[1]=m,this._$cacheKeys=St.generateKeys(this._$uniqueKey,t,i),ot(t),this._$cacheParams[0]=p,this._$cacheParams[1]=m,this._$cacheParams[2]=i[7]}if(t.cachePosition=St.get(this._$cacheKeys),!t.cachePosition){const s=y.currentAttachment;s&&s.mask&&t.stopStencil();let r=0,n=0;if("shape"===this._$mode){r=v.ceil(v.abs(l.xMax-l.xMin)*p),n=v.ceil(v.abs(l.yMax-l.yMin)*m);const e=t._$getTextureScale(r,n);e<1&&(r*=e,n*=e)}else r=v.ceil(v.abs(l.xMax-l.xMin)),n=v.ceil(v.abs(l.yMax-l.yMin));if(t.cachePosition=y.createCachePosition(r,n),t.bindRenderBuffer(t.cachePosition),t.reset(),"shape"===this._$mode?t.setTransform(p,0,0,m,-l.xMin*p,-l.yMin*m):t.setTransform(1,0,0,1,-l.xMin,-l.yMin),h){const i=M.scaleX,s=it(i,0,0,i,0,0),r=gt(s,o);st(s);const n=a._$parent._$transform.concatenatedMatrix._$matrix,h=it(n[0],n[1],n[2],n[3],n[4]*i-$,n[5]*i-d);st(n);const c=gt(h,r),_=c[4]-(e[4]-$),u=c[5]-(e[5]-d);st(c);const g=pt(l,r),f=+g.xMax,p=+g.xMin,m=+g.yMax,x=+g.yMin,b=v.ceil(v.abs(f-p)),T=v.ceil(v.abs(m-x));J(g);const y=a._$scale9Grid,E={x:y.x,y:y.y,w:y.width,h:y.height};t.grid.enable(p,x,b,T,l,E,i,r[0],r[1],r[2],r[3],r[4],r[5],h[0],h[1],h[2],h[3],h[4]-_,h[5]-u),st(r),st(h)}this._$doDraw(t,i,!1),h&&t.grid.disable(),y.transferTexture(t.cachePosition),St.set(this._$cacheKeys,t.cachePosition),t._$bind(s)}let S=0,w=0;if(x){const i=this._$createBitmapTexture(t,t.cachePosition,p,m,g,f),s=a._$drawFilter(t,e,r,g,f,i);s.offsetX&&(S=s.offsetX),s.offsetY&&(w=s.offsetY),t.cachePosition=s}if(x||"bitmap"!==this._$mode){const i=v.atan2(e[1],e[0]),s=v.atan2(-e[2],e[3]);if(x||!i&&!s)t.setTransform(1,0,0,1,$-S,d-w);else{const r=l.xMin*p,n=l.yMin*m,a=v.cos(i),h=v.sin(i),o=v.cos(s),c=v.sin(s);t.setTransform(a,h,-c,o,r*a-n*c+e[4],r*h+n*o+e[5])}}else t.setTransform(e[0],e[1],e[2],e[3],l.xMin*e[0]+l.yMin*e[2]+e[4],l.xMin*e[1]+l.yMin*e[3]+e[5]);t.cachePosition&&(t.globalAlpha=n,t.imageSmoothingEnabled="shape"===this._$mode,t.globalCompositeOperation=s,t.drawInstance($-S,d-w,_,u,i),t.cachePosition=null),J(l)}_$createBitmapTexture(t,e,i,s,r,n){if("bitmap"!==this._$mode)return null;t.drawInstacedArray();const a=t.frameBuffer,h=a.currentAttachment,o=a.createCacheAttachment(r,n);t._$bind(o),t.reset();const l=it(i,0,0,s,r/2,n/2),c=t.getTextureFromRect(e),_=it(1,0,0,1,-c.width/2,-c.height/2),$=gt(l,_);st(l),st(_),t.setTransform($[0],$[1],$[2],$[3],$[4],$[5]),t.drawImage(c,0,0,c.width,c.height);const u=a.getTextureFromCurrentAttachment();return t._$bind(h),a.releaseAttachment(o),a.textureManager.release(c),u}_$doDraw(t,e=null,i=!1){t.reset(),t.beginPath(),this._$runCommand(t,e,i)}_$hit(t,e,i,s=!1){return t.beginPath(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),this._$runCommand(t,null,s,i)}_$getBounds(){const t=this._$displayObject;return t&&t._$bounds?Q(t._$bounds.xMin,t._$bounds.xMax,t._$bounds.yMin,t._$bounds.yMax):Q(this._$xMin,this._$xMax,this._$yMin,this._$yMax)}_$restart(){this._$displayObject&&(this._$displayObject._$posted=!1,this._$displayObject._$isUpdated()||(this._$displayObject._$doChanged(),x(),St.removeCache(this._$displayObject._$instanceId),this._$displayObject._$characterId&&St.removeCache(this._$displayObject._$characterId)))}_$setBounds(t=0,e=0){this._$setFillBounds(t,e),this._$doLine&&this._$setLineBounds(t,e)}_$setFillBounds(t=0,e=0){this._$xMin=v.min(this._$xMin,t),this._$xMax=v.max(this._$xMax,t),this._$yMin=v.min(this._$yMin,e),this._$yMax=v.max(this._$yMax,e)}_$setLineBounds(t=0,e=0){this._$xMin=v.min(this._$xMin,v.min(t,this._$pointerX)),this._$xMax=v.max(this._$xMax,v.max(t,this._$pointerX)),this._$yMin=v.min(this._$yMin,v.min(e,this._$pointerY)),this._$yMax=v.max(this._$yMax,v.max(e,this._$pointerY));const i=this._$lineWidth/2,s=.5*v.PI,r=v.atan2(e-this._$pointerY,t-this._$pointerX),n=v.atan2(this._$pointerY-e,this._$pointerX-t),a=r+s,h=r-s,o=n+s,l=n-s;let c=t+i,_=-i+t,$=this._$pointerX+i,u=-i+this._$pointerX,d=e+i,g=-i+e,f=this._$pointerY+i,p=-i+this._$pointerY;switch(this._$xMin=v.min(this._$xMin,v.min(c,v.min(_,v.min($,u)))),this._$xMax=v.max(this._$xMax,v.max(c,v.max(_,v.max($,u)))),this._$yMin=v.min(this._$yMin,v.min(d,v.min(g,v.min(f,p)))),this._$yMax=v.max(this._$yMax,v.max(d,v.max(g,v.max(f,p)))),v.abs(a)%s!=0&&(c=t+v.cos(a)*i),v.abs(h)%s!=0&&(_=t+v.cos(h)*i),v.abs(o)%s!=0&&($=this._$pointerX+v.cos(o)*i),v.abs(l)%s!=0&&(u=this._$pointerX+v.cos(l)*i),a&&v.abs(a)%v.PI!=0&&(d=e+v.sin(a)*i),h&&v.abs(h)%v.PI!=0&&(g=e+v.sin(h)*i),o&&v.abs(o)%v.PI!=0&&(f=this._$pointerY+v.sin(o)*i),l&&v.abs(l)%v.PI!=0&&(p=this._$pointerY+v.sin(l)*i),this._$xMin=v.min(this._$xMin,v.min(c,v.min(_,v.min($,u)))),this._$xMax=v.max(this._$xMax,v.max(c,v.max(_,v.max($,u)))),this._$yMin=v.min(this._$yMin,v.min(d,v.min(g,v.min(f,p)))),this._$yMax=v.max(this._$yMax,v.max(d,v.max(g,v.max(f,p)))),this._$caps){case"round":if(v.abs(r)%s!=0){const e=t+v.cos(r)*i;this._$xMin=v.min(this._$xMin,e),this._$xMax=v.max(this._$xMax,e)}if(r&&v.abs(r)%v.PI!=0){const t=e+v.sin(r)*i;this._$yMin=v.min(this._$yMin,t),this._$yMax=v.max(this._$yMax,t)}if(v.abs(n)%s!=0){const t=this._$pointerX+v.cos(n)*i;this._$xMin=v.min(this._$xMin,t),this._$xMax=v.max(this._$xMax,t)}if(n&&v.abs(n)%v.PI!=0){const t=this._$pointerY+v.sin(n)*i;this._$yMin=v.min(this._$yMin,t),this._$yMax=v.max(this._$yMax,t)}break;case"square":if(v.abs(r)%s!=0){const t=v.cos(r)*i,e=c+t,s=_+t;this._$xMin=v.min(this._$xMin,v.min(e,s)),this._$xMax=v.max(this._$xMax,v.max(e,s))}if(v.abs(n)%s!=0){const t=v.cos(n)*i,e=$+t,s=u+t;this._$xMin=v.min(this._$xMin,v.min(e,s)),this._$xMax=v.max(this._$xMax,v.max(e,s))}if(r&&v.abs(r)%v.PI!=0){const t=v.sin(r)*i,e=d+t,s=g+t;this._$yMin=v.min(this._$yMin,v.min(e,s)),this._$yMax=v.max(this._$yMax,v.max(e,s))}if(n&&v.abs(n)%v.PI!=0){const t=v.sin(n)*i,e=f+t,s=p+t;this._$yMin=v.min(this._$yMin,v.min(e,s)),this._$yMax=v.max(this._$yMax,v.max(e,s))}}}_$margePath(t){this._$doFill&&this._$fills&&this._$fills.push(...t),this._$doLine&&this._$lines&&this._$lines.push(...t),ot(t)}_$createCacheKey(){if(this._$doLine&&this.endLine(),this._$doFill&&this.endFill(),!this._$recode)return"";const t=this._$getRecodes();let e=0;for(let i=0;i{_=t})(dt(t,0,1,1));const e=$(),i=cr(),s=i._$sources;for(let t=0;t{this._$loadStart(t)},progress:t=>{this._$progress(t)},loadend:t=>{this._$loadEnd(t)}}})}_$loadStart(t){this._$bytesLoaded=t.loaded,this._$bytesTotal=t.total,this.willTrigger(It.OPEN)&&this.dispatchEvent(new It(It.OPEN)),this.willTrigger(kt.PROGRESS)&&this.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total))}_$progress(t){this._$bytesLoaded=t.loaded,this._$bytesTotal=t.total,this.willTrigger(kt.PROGRESS)&&this.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total))}_$loadEnd(t){this._$bytesLoaded=t.loaded,this._$bytesTotal=t.total,this.willTrigger(kt.PROGRESS)&&this.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total));const e=t.target;if(!e)throw new Error("the Sound target is null.");199e.status?(this._$arrayBuffer=e.response,Ns?pr(this).then((t=>{(t.hasEventListener(It.INIT)||t.hasEventListener(It.COMPLETE))&&cr()._$loaders.push(t)})):Xs.push(this)):this.willTrigger(Lt.IO_ERROR)&&this.dispatchEvent(new Lt(Lt.IO_ERROR,!1,!1,e.statusText))}play(t=0){const e=this._$character?this._$character.audioBuffer:this._$audioBuffer;if(Ns&&e)this._$createBufferSource(t);else{const e=R.now(),i=()=>{if(null===(this._$character?this._$character.audioBuffer:this._$audioBuffer)||null===Ns)I(i);else{const i=(R.now()-e)/1e3;this._$createBufferSource(t,i)}};I(i)}}stop(){this._$stopFlag=!0;const t=this._$sources.length;if(t){const e=cr();if(Ns)for(let e=0;e{(t.hasEventListener(It.INIT)||t.hasEventListener(It.COMPLETE))&&cr()._$loaders.push(t)})):Xs.push(this)),this._$loopCount=0|t.loopCount,this._$volume=v.min(oe.volume,t.volume)}_$createBufferSource(t=0,e=0){if(!Ns)throw new Error("the Audio Context is null.");const i=Ns.createBufferSource();i.onended=t=>this._$endEventHandler(t),i.buffer=this._$character?this._$character.audioBuffer:this._$audioBuffer,i._$gainNode=Ns.createGain(),i._$gainNode.connect(Ns.destination);const s=v.min(oe.volume,this._$volume);i._$gainNode.gain.value=s,i._$volume=s,i.connect(i._$gainNode),i.start(0|t,e);const r=cr();-1===r._$sources.indexOf(this)&&r._$sources.push(this),this._$sources.push(i),this._$stopFlag=!1}_$endEventHandler(t){const e=t.target;if(this._$sources.splice(this._$sources.indexOf(e),1),!this._$stopFlag&&this._$loopCount>this._$currentCount)this._$createBufferSource(),this._$currentCount++;else{if(this._$currentCount=0,Ns&&(e._$gainNode&&(e._$gainNode.gain.value=0,e._$gainNode.disconnect(),e._$gainNode=null),e.onended=null,e.disconnect()),!this._$sources.length){const t=cr();t._$sources.splice(t._$sources.indexOf(this),1)}this.willTrigger(It.SOUND_COMPLETE)&&this.dispatchEvent(new It(It.SOUND_COMPLETE))}}}class ce{constructor(t=1,e=!1){this._$volume=1,this._$loop=!1,this.volume=t,this.loop=e}static toString(){return"[class SoundTransform]"}static get namespace(){return"next2d.media.SoundTransform"}toString(){return"[object SoundTransform]"}get namespace(){return"next2d.media.SoundTransform"}get loop(){return this._$loop}set loop(t){this._$loop=t}get volume(){return this._$volume}set volume(t){this._$volume=dt(+t,0,1,0)}}class _e extends Zt{constructor(t=0,e=0){super(),this._$smoothing=!0,this._$loop=!1,this._$autoPlay=!0,this._$bounds=Q(0,t,0,e),this._$bytesLoaded=0,this._$bytesTotal=0,this._$timerId=-1,this._$video=null,this._$stop=!0,this._$ready=!1,this._$volume=1,this._$context=null,this._$cacheKeys=ht(),this._$cacheParams=ht(0,0,0)}static toString(){return"[class Video]"}static get namespace(){return"next2d.media.Video"}toString(){return"[object Video]"}get namespace(){return"next2d.media.Video"}get bytesLoaded(){return this._$bytesLoaded}get bytesTotal(){return this._$bytesTotal}get currentTime(){return this._$video?this._$video.currentTime:0}get duration(){return this._$video?this._$video.duration:0}get loop(){return this._$loop}set loop(t){this._$loop=!!t}get autoPlay(){return this._$autoPlay}set autoPlay(t){this._$autoPlay=!!t}get smoothing(){return this._$smoothing}set smoothing(t){this._$smoothing=!!t}get src(){return this._$video?this._$video.src:""}set src(t){this._$video||(this._$video=this._$initializeVideo()),this._$video.src=t,this._$video.load()}get videoHeight(){return this._$video?this._$video.videoHeight:this._$bounds.yMax}get videoWidth(){return this._$video?this._$video.videoWidth:this._$bounds.xMax}get volume(){return this._$volume}set volume(t){this._$volume=dt(v.min(oe.volume,t),0,1,1),this._$video&&(this._$video.volume=this._$volume)}clear(){this._$video&&this._$video.pause(),this._$video=null,this._$bounds.xMax=0,this._$bounds.yMax=0,this._$doChanged()}pause(){if(this._$video&&!this._$stop){this._$stop=!0,this._$video.pause(),F(this._$timerId),this._$timerId=-1,this.hasEventListener(Nt.PAUSE)&&this.dispatchEvent(new Nt(Nt.PAUSE,!1,!1,this._$bytesLoaded,this._$bytesTotal));const t=cr();t._$videos.splice(t._$videos.indexOf(this),1)}}play(){this._$video&&this._$stop&&(this._$stop=!1,this._$video.volume=v.min(this._$volume,oe.volume),this._$video.play().then((()=>{this._$timerId=I((()=>{this._$update()})),this.hasEventListener(Nt.PLAY)&&this.dispatchEvent(new Nt(Nt.PLAY,!1,!1,this._$bytesLoaded,this._$bytesTotal));const t=cr();-1===t._$videos.indexOf(this)&&t._$videos.push(this),this._$ready=!0})))}seek(t){this._$video&&(this._$video.currentTime=t,this.hasEventListener(Nt.SEEK)&&this.dispatchEvent(new Nt(Nt.SEEK,!1,!1,this._$bytesLoaded,this._$bytesTotal)))}_$update(){const t=cr();if(!this.stage||!this._$video)return this._$video&&this._$video.pause(),F(this._$timerId),this._$timerId=-1,void t._$videos.splice(t._$videos.indexOf(this),1);Mr&&this._$postProperty(),this._$bytesLoaded=this._$video.currentTime,this._$video.currentTime&&(this.hasEventListener(Nt.PROGRESS)&&this.dispatchEvent(new Nt(Nt.PROGRESS,!1,!1,this._$bytesLoaded,this._$bytesTotal)),this._$doChanged()),this._$timerId=I((()=>{this._$update()}))}_$start(){if(!this._$video)return;this._$bounds.xMax=this._$video.videoWidth,this._$bounds.yMax=this._$video.videoHeight,this._$bytesTotal=this._$video.duration;const t=cr();this._$autoPlay&&(this._$stop=!1,this._$video.play().then((()=>{-1===t._$videos.indexOf(this)&&t._$videos.push(this),this.hasEventListener(Nt.PLAY_START)&&this.dispatchEvent(new Nt(Nt.PLAY_START,!1,!1,this._$bytesLoaded,this._$bytesTotal)),this._$timerId=I((()=>{this._$update()})),this._$ready=!0,this._$doChanged()}))),this._$createContext()}_$initializeVideo(){this._$cacheKeys.length=0;const t=d.createElement("video");return t.autoplay=!1,t.crossOrigin="anonymous",Ns||(t.muted=!0),Zs&&t.setAttribute("playsinline",""),t.addEventListener("canplaythrough",(()=>{this._$start()})),t.addEventListener("ended",(()=>{this._$loop?t.currentTime=0:(this.hasEventListener(Nt.PLAY_END)&&this.dispatchEvent(new Nt(Nt.PLAY_END,!1,!1,this._$bytesLoaded,this._$bytesTotal)),F(this._$timerId),this._$timerId=-1)})),t}_$createContext(){if(Mr){const t=new w(this._$bounds.xMax,this._$bounds.yMax);this._$context=t.getContext("2d")}}_$buildCharacter(t){t.buffer&&!t._$buffer&&(t._$buffer=new Uint8Array(t.buffer),t.buffer=null),this._$loop=t.loop,this._$autoPlay=t.autoPlay,this._$bounds.xMin=t.bounds.xMin,this._$bounds.yMin=t.bounds.yMin,this._$bounds.xMax=t.bounds.xMax,this._$bounds.yMax=t.bounds.yMax,this._$video||(this._$video=this._$initializeVideo()),this._$video.src=URL.createObjectURL(new Blob([t._$buffer],{type:"video/mp4"})),this._$video.volume=v.min(t.volume,oe.volume),this._$video.load(),Mr&&this._$stage&&this._$createWorkerInstance()}_$sync(t){this._$buildCharacter(t)}_$build(t,e){const i=this._$baseBuild(t,e);return this._$buildCharacter(i),i}_$clip(t,e){const i=this._$bounds.xMax,s=this._$bounds.yMax;if(!i||!s)return;let r=e;const n=this._$transform._$rawMatrix();1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(r=gt(e,n)),t.reset(),t.setTransform(r[0],r[1],r[2],r[3],r[4],r[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(i,0),t.lineTo(i,s),t.lineTo(0,s),t.lineTo(0,0),t.clip(),r!==e&&st(r)}_$draw(t,e,i){if(!this._$visible||!this._$video||!this._$ready)return;let s=i;const r=this._$transform._$rawColorTransform();1===r[0]&&1===r[1]&&1===r[2]&&1===r[3]&&0===r[4]&&0===r[5]&&0===r[6]&&0===r[7]||(s=ft(i,r));const n=dt(s[3]+s[7]/255,0,1,0);if(!n)return void(s!==i&&nt(s));let a=e;const h=this._$transform._$rawMatrix();1===h[0]&&0===h[1]&&0===h[2]&&1===h[3]&&0===h[4]&&0===h[5]||(a=gt(e,h));const o=pt(this._$bounds,a),l=+o.xMax,c=+o.xMin,_=+o.yMax,$=+o.yMin;J(o);const u=v.ceil(v.abs(l-c)),d=v.ceil(v.abs(_-$));switch(!0){case 0===u:case 0===d:case u===-1/0:case d===-1/0:case u===b:case d===b:return}let g=+v.sqrt(a[0]*a[0]+a[1]*a[1]);if(!E.isInteger(g)){const t=g.toString(),e=t.indexOf("e");-1!==e&&(g=+t.slice(0,e)),g=+g.toFixed(4)}let f=+v.sqrt(a[2]*a[2]+a[3]*a[3]);if(!E.isInteger(f)){const t=f.toString(),e=t.indexOf("e");-1!==e&&(f=+t.slice(0,e)),f=+f.toFixed(4)}const p=this._$filters||this.filters,m=p&&p.length>0&&this._$canApply(p);let x=Q(0,u,0,d);if(m)for(let t=0;ty.width||$-x.yMin>y.height)return void J(x);if(0>c+x.xMax||0>$+x.yMax)return void J(x);if(J(x),!this._$cacheKeys.length||this._$cacheParams[0]!==g||this._$cacheParams[1]!==f||this._$cacheParams[2]!==i[7]){const t=ht();t[0]=g,t[1]=f,this._$cacheKeys=St.generateKeys(this._$instanceId,t,i),ot(t),this._$cacheParams[0]=g,this._$cacheParams[1]=f,this._$cacheParams[2]=i[7]}const A=this._$blendMode||this.blendMode;if(t.cachePosition=St.get(this._$cacheKeys),!t.cachePosition){const e=v.ceil(v.abs(this._$bounds.xMax-this._$bounds.xMin)),i=v.ceil(v.abs(this._$bounds.yMax-this._$bounds.yMin)),s=T.createCachePosition(e,i);t.cachePosition=s,St.set(this._$cacheKeys,s)}const M=T.createTextureFromVideo(this._$video,this._$smoothing);let S=0,w=0;if(m){const e=T.currentAttachment,i=T.createCacheAttachment(u,d);t._$bind(i),t.reset();const s=it(g,0,0,f,u/2,d/2),r=it(1,0,0,1,-M.width/2,-M.height/2),n=gt(s,r);st(s),st(r),t.setTransform(n[0],n[1],n[2],n[3],n[4],n[5]),t.drawImage(M,0,0,M.width,M.height);const h=T.getTextureFromCurrentAttachment();t._$bind(e),T.releaseAttachment(i),t.drawTextureFromRect(M,t.cachePosition);const o=this._$drawFilter(t,a,p,u,d,h);o.offsetX&&(S=o.offsetX),o.offsetY&&(w=o.offsetY),t.cachePosition=o,t.setTransform(1,0,0,1,c-S,$-w)}else t.drawTextureFromRect(M,t.cachePosition),t.setTransform(a[0],a[1],a[2],a[3],a[4],a[5]);t.cachePosition&&(t.globalAlpha=n,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=A,t.drawInstance(c-S,$-w,l,_,i),t.cachePosition=null),a!==e&&st(a),s!==i&&nt(s)}_$mouseHit(t,e,i){return!!this._$visible&&this._$hit(t,e,i)}_$hit(t,e,i){let s=e;const r=this._$transform._$rawMatrix();r!==P&&(s=gt(e,r));const n=this._$getBounds(null),a=pt(n,s),h=+a.xMax,o=+a.xMin,l=+a.yMax,c=+a.yMin;J(a),J(n);const _=v.ceil(v.abs(h-o)),$=v.ceil(v.abs(l-c));return t.setTransform(1,0,0,1,o,c),t.beginPath(),t.moveTo(0,0),t.lineTo(_,0),t.lineTo(_,$),t.lineTo(0,$),t.lineTo(0,0),s!==e&&st(s),t.isPointInPath(i.x,i.y)}_$getBounds(t=null){if(t){let e=t;const i=this._$transform._$rawMatrix();1===i[0]&&0===i[1]&&0===i[2]&&1===i[3]&&0===i[4]&&0===i[5]||(e=gt(t,i));const s=pt(this._$bounds,e);return e!==t&&st(e),s}return Q(this._$bounds.xMin,this._$bounds.xMax,this._$bounds.yMin,this._$bounds.yMax)}_$createWorkerInstance(){if(!Mr||this._$created)return;this._$created=!0;const t={command:"createVideo",buffer:new Float32Array,instanceId:this._$instanceId,parentId:this._$parent?this._$parent._$instanceId:-1,smoothing:this._$smoothing,xMin:this._$bounds.xMin,yMin:this._$bounds.yMin,xMax:this._$bounds.xMax,yMax:this._$bounds.yMax};this._$characterId>-1&&(t.characterId=this._$characterId),this._$loaderInfo&&(t.loaderInfoId=this._$loaderInfo._$id),this._$scale9Grid&&(t.grid={x:this._$scale9Grid.x,y:this._$scale9Grid.y,w:this._$scale9Grid.width,h:this._$scale9Grid.height}),Mr.postMessage(t)}_$postProperty(){if(!Mr)return;const t=this._$createMessage();t.smoothing=this._$smoothing;const e=ht(),i=this._$context;if(i&&this._$video){t.xMin=this._$bounds.xMin,t.yMin=this._$bounds.yMin,t.xMax=this._$bounds.xMax,t.yMax=this._$bounds.yMax,i.drawImage(this._$video,0,0);const s=i.canvas.transferToImageBitmap();t.imageBitmap=s,e.push(s)}Mr.postMessage(t,e),ot(e),this._$posted=!0,this._$updated=!1}}class $e extends ee{constructor(){super(),this._$buttonMode=!1,this._$hitArea=null,this._$soundTransform=null,this._$useHandCursor=!0}static toString(){return"[class Sprite]"}static get namespace(){return"next2d.display.Sprite"}toString(){return"[object Sprite]"}get namespace(){return"next2d.display.Sprite"}get buttonMode(){return this._$buttonMode}set buttonMode(t){this._$buttonMode=!!t}get dropTarget(){return Ds}get hitArea(){return this._$hitArea}set hitArea(t){this._$hitArea&&(this._$hitArea._$hitObject=null),this._$hitArea=t,t&&(t._$hitObject=this)}get soundTransform(){return this._$soundTransform||(this._$soundTransform=new ce),this._$soundTransform}set soundTransform(t){this._$soundTransform=t}get useHandCursor(){return this._$useHandCursor}set useHandCursor(t){this._$useHandCursor=t}startDrag(t=!1,e=null){let i=0,s=0;if(!t){const t=this._$dragMousePoint();i=this.x-t.x,s=this.y-t.y}Us(this),Vs.lock=t,Vs.position.x=i,Vs.position.y=s,Vs.bounds=e}stopDrag(){Us(null),Vs.lock=!1,Vs.position.x=0,Vs.position.y=0,Vs.bounds=null}_$sync(t){Mr&&this._$stage&&this._$createWorkerInstance(),this._$controller=t.controller,this._$dictionary=t.dictionary,this._$placeMap=t.placeMap,this._$placeObjects=t.placeObjects}_$build(t,e){const i=this._$baseBuild(t,e);return Mr&&this._$stage&&this._$createWorkerInstance(),this._$controller=i.controller,this._$dictionary=i.dictionary,this._$placeMap=i.placeMap,this._$placeObjects=i.placeObjects,i}_$dragMousePoint(){return this._$parent?this._$parent.globalToLocal(_r()):this.globalToLocal(_r())}}class ue extends $e{constructor(){super(),this._$stopFlag=!1,this._$canAction=!0,this._$canSound=!0,this._$actionProcess=!1,this._$actions=ct(),this._$frameCache=ct(),this._$labels=null,this._$sounds=ct(),this._$actionOffset=0,this._$actionLimit=0,this._$currentFrame=1,this._$totalFrames=1,this._$isPlaying=!1,this._$loopConfig=null,this._$tweenFrame=0}static toString(){return"[class MovieClip]"}static get namespace(){return"next2d.display.MovieClip"}toString(){return"[object MovieClip]"}get namespace(){return"next2d.display.MovieClip"}get currentFrame(){return this._$currentFrame}get currentFrameLabel(){if(!this._$labels)return null;const t=this._$currentFrame;return this._$labels.has(t)&&this._$labels.get(t)||null}get currentLabels(){return this._$labels&&this._$labels.size?T.from(this._$labels.values()):null}get isPlaying(){return this._$isPlaying}get totalFrames(){return this._$totalFrames}get loopConfig(){if(this._$loopConfig)return this._$loopConfig;const t=this._$placeObject||this._$getPlaceObject();return t&&t.loop?(this._$tweenFrame&&(this._$changePlace=this._$tweenFrame!==this._$parent._$currentFrame,this._$tweenFrame=0),t.loop.tweenFrame&&(this._$tweenFrame=t.loop.tweenFrame),t.loop):null}set loopConfig(t){this._$loopConfig=t,t&&(t.frame=this._$startFrame,this._$loopConfig=t,this._$currentFrame=this._$getLoopFrame(t))}gotoAndPlay(t){this.play(),this._$goToFrame(t)}gotoAndStop(t){this.stop(),this._$goToFrame(t)}nextFrame(){this.stop(),this._$totalFrames>this._$currentFrame&&this._$goToFrame(this._$currentFrame+1)}play(){this._$stopFlag=!1,this._$isPlaying=!0,this._$updateState()}prevFrame(){const t=this._$currentFrame-1;t&&(this.stop(),this._$goToFrame(t))}stop(){this._$stopFlag=!0,this._$isPlaying=!1}addFrameLabel(t){this._$labels||(this._$labels=ct()),this._$labels.set(t.frame,t)}addFrameScript(...t){for(let e=0;e=s&&this._$addAction(s,r),s===this._$currentFrame){const t=cr();if(t._$actionOffset=t._$actions.length,this._$canAction=!0,this._$setAction(),t._$actionOffset!==t._$actions.length){const e=t._$actions.splice(0,t._$actionOffset);t._$actions.push(...t._$actions,...e),t._$actionOffset=0}}}}_$getFrameForLabel(t){if(!this._$labels)return 0;for(const[e,i]of this._$labels)if(i.name===t)return e;return 0}_$addAction(t,e){if(t){this._$actions.has(t)||this._$actions.set(t,ht());const i=this._$actions.get(t);i&&i.push(e)}}_$setAction(){if(this._$executeAddedEvent(),this._$canAction){const t=this._$currentFrame;if(this._$labels&&this._$labels.has(t)){const e=this._$labels.get(t);e&&e.willTrigger(It.FRAME_LABEL)&&e.dispatchEvent(new It(It.FRAME_LABEL))}if(this._$actions.size&&this._$actions.has(t)){const t=cr();-1===t._$actions.indexOf(this)&&t._$actions.push(this)}}}_$goToFrame(t){let e=+t;if(C(e)&&(e=this._$getFrameForLabel(`${t}`)),e<1&&(e=1),e>this._$totalFrames)return this._$currentFrame=this._$totalFrames,this._$clearChildren(),this._$canAction=!1,void(this._$wait=!1);const i=cr();switch(!0){case e!==this._$currentFrame:{this._$wait=!1;const t=this._$currentFrame;this._$actionProcess&&(this._$frameCache.set("nextFrame",e),this._$frameCache.set("stopFlag",this._$stopFlag),this._$frameCache.set("isPlaying",this._$isPlaying)),this._$currentFrame=e,this._$clearChildren(),i._$actionOffset=i._$actions.length;const s=i._$actionOffset?i._$actions.indexOf(this):-1;if(this._$canAction=!0,this._$prepareActions(),i._$actionOffset&&i._$actionOffset!==i._$actions.length){const t=i._$actions.splice(0,i._$actionOffset);i._$actions.push(...i._$actions,...t),i._$actionOffset=0}if(!this._$actionProcess&&(s>-1||!i._$actionOffset))for(;i._$actions.length&&i._$actions.length!==s;){const t=i._$actions.pop();if(!t)continue;t._$canAction=!1,t._$actionOffset=0,t._$actionLimit=0,t._$actionProcess&&t._$frameCache.size&&(t._$currentFrame=t._$frameCache.get("nextFrame"),t._$clearChildren(),t._$stopFlag=t._$frameCache.get("stopFlag"),t._$isPlaying=t._$frameCache.get("isPlaying"),t._$frameCache.clear());const e=t._$currentFrame;if(!t._$actions.has(e))continue;const s=t._$actions.get(e);if(s)for(let e=0;e-1:{if(!this._$actionLimit)break;this._$wait=!1;const t=i._$actions.splice(this._$actionOffset,this._$actionLimit);for(;t.length;){const e=t.pop();if(!e)continue;e._$canAction=!1,e._$actionOffset=0,e._$actionLimit=0;const i=e._$currentFrame;if(!e._$actions.has(i))continue;const s=e._$actions.get(i);if(s)for(let t=0;t-1;--e)t[e]._$prepareActions();this._$setAction()}_$nextFrame(){let t=this._$needsChildren;switch(!0){case this._$wait:t=!0,this._$wait=!1;break;case this._$stopFlag:case 1===this._$totalFrames:break;default:{t=!0,this._$canAction=!0,this._$canSound=!0;const e=this.loopConfig;if(e){const i=e.end?e.end:this._$totalFrames;switch(e.type){case 0:this._$changePlace?this._$currentFrame=e.start:(++this._$currentFrame,this._$currentFrame>i&&(this._$currentFrame=e.start));break;case 1:this._$changePlace?this._$currentFrame=e.start:(++this._$currentFrame,this._$currentFrame>i&&(this._$currentFrame=i,t=!1,this._$canAction=!1,this._$canSound=!1));break;case 2:this._$changePlace?this._$currentFrame=e.start:(t=!1,this._$canAction=!1,this._$canSound=!1);break;case 3:this._$changePlace?this._$currentFrame=i:(--this._$currentFrame,e.start>this._$currentFrame&&(this._$currentFrame=e.start,t=!1,this._$canAction=!1,this._$canSound=!1));break;case 4:this._$changePlace?this._$currentFrame=i:(--this._$currentFrame,e.start>this._$currentFrame&&(this._$currentFrame=i))}}else++this._$currentFrame,this._$currentFrame>this._$totalFrames&&(this._$currentFrame=1);if(t&&this._$clearChildren(),this._$canSound&&this._$sounds.size&&this._$sounds.has(this._$currentFrame)){const t=cr();t._$sounds.has(this._$instanceId)||t._$sounds.set(this._$instanceId,this)}}}const e=this._$needsChildren?this._$getChildren():this._$children;for(let i=e.length-1;i>-1;--i){const s=e[i];s._$isNext&&(t?s._$nextFrame():t=s._$nextFrame())}return this._$setAction(),this._$isNext=t,!this._$posted&&Mr&&this._$postProperty(),this._$isNext}_$getLoopFrame(t){const e=this._$parent._$currentFrame-t.frame;let i=1;switch(t.type){case 0:{const s=t.end?t.end:this._$totalFrames;i=t.start;for(let r=0;rs&&(i=t.start)}break;case 1:{const s=t.end?t.end:this._$totalFrames;i=v.min(s,t.start+e)}break;case 2:i=t.start;break;case 3:i=t.end?t.end:this._$totalFrames,i=v.max(t.start,i-e);break;case 4:{const s=t.end?t.end:this._$totalFrames;i=s;for(let r=0;ri&&(i=s)}}return i}_$buildCharacter(t){if(t.sounds)for(let e=0;e{this._$loadstart(t)},progress:t=>{this._$progress(t)},loadend:t=>{this._$loadend(t)}}}))}loadJSON(t){if("zlib"===t.type){if(Pr())return void Ir.push(t);Lr(!0);const e=Rr(),i=new Uint8Array(t.buffer);e.onmessage=t=>{this._$unzipHandler(t)},e.postMessage(i,[i.buffer])}else this._$build(t)}_$loadend(t){const e=this._$loaderInfo;if(!e)return;e.bytesLoaded=t.loaded,e.bytesTotal=t.total,e.willTrigger(kt.PROGRESS)&&e.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total));const i=t.target;if(e.willTrigger(Bt.HTTP_STATUS)){const t=Tr(i.getAllResponseHeaders());e.dispatchEvent(new Bt(Bt.HTTP_STATUS,!1,!1,i.status,i.responseURL,t))}199i.status?"json"===e.format?this.loadJSON(i.response):e.willTrigger(Lt.IO_ERROR)&&e.dispatchEvent(new Lt(Lt.IO_ERROR,!1,!1,"LoaderInfo format is `json`")):e.willTrigger(Lt.IO_ERROR)&&e.dispatchEvent(new Lt(Lt.IO_ERROR,!1,!1,i.statusText))}_$unzipHandler(t){if(this._$build(t.data),Ir.length){const t=Ir.pop();if(!t)return;const e=new Uint8Array(t.buffer),i=Rr();i.onmessage=t=>{this._$unzipHandler(t)},i.postMessage(e,[e.buffer])}else Lr(!1)}_$loadstart(t){const e=this._$loaderInfo;e&&(e.bytesLoaded=t.loaded,e.bytesTotal=t.total,e.willTrigger(It.OPEN)&&e.dispatchEvent(new It(It.OPEN)),e.willTrigger(kt.PROGRESS)&&e.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total)))}_$progress(t){const e=this._$loaderInfo;e&&(e.bytesLoaded=t.loaded,e.bytesTotal=t.total,e.willTrigger(kt.PROGRESS)&&e.dispatchEvent(new kt(kt.PROGRESS,!1,!1,t.loaded,t.total)))}_$build(t){const e=this._$loaderInfo;if(!e)return;const i=ct();if(t.symbols.length)for(let e=0;e{const t=e.width,i=e.height,s=new ie(t,i);s.image=e,this.graphics.beginBitmapFill(s).drawRect(0,0,t,i),this.hasEventListener(It.LOAD)&&this.dispatchEvent(new It(It.LOAD))})),this._$src=e.src=t,this.graphics._$mode="bitmap"}_$buildCharacter(t,e){const i=this.graphics;if(!e._$data)throw new Error("the loaderInfo data is null.");if(t.recodes)switch(!0){case t.bitmapId>0:{const s=e._$data.characters[t.bitmapId];if(!s.buffer)throw new Error("the bitmap buffer is null.");const r=v.abs(s.bounds.xMax-s.bounds.xMin),n=v.abs(s.bounds.yMax-s.bounds.yMin),a=new ie(r,n);s._$buffer||(s._$buffer=new Uint8Array(s.buffer),ot(s.buffer),s.buffer=null),a.buffer=s._$buffer.slice(),i._$recode=ht(),r===t.bounds.xMax-t.bounds.xMin&&n===t.bounds.yMax-t.bounds.yMin&&(i._$bitmapId=t.bitmapId,i._$mode="bitmap");const h=t.recodes;if(h[h.length-1]===ae.END_FILL){const t=h.length-6;for(let e=0;e0&&i._$canDraw){i._$posted=!0;const e=or(),s=i._$getRecodes();e.command=`shapeRecodes@${this._$instanceId}`,e.buffer=s;const r=ht(s.buffer);Mr.postMessage(e,r),lr(e),ot(r),t[2]=i._$maxAlpha,t[3]=+i._$canDraw}const s=this._$getBounds();t[e++]=s.xMin,t[e++]=s.yMin,t[e++]=s.xMax,t[e++]=s.yMax,t[e++]=this._$characterId>-1?this._$characterId:-1,t[e++]=this._$loaderInfo?this._$loaderInfo._$id:-1,this._$registerProperty(t,10);const r=or();r.command="createShape",r.buffer=t;const n=ht(t.buffer);Mr.postMessage(r,n),lr(r),ot(n)}_$postProperty(){if(!this._$created||!Mr)return;const t=this._$createMessage(),e=this._$graphics;if(e&&!e._$posted){t.maxAlpha=e._$maxAlpha,t.canDraw=e._$canDraw;const i=e._$getRecodes();t.recodes=i;const s=ht(i.buffer),r=this._$getBounds();t.xMin=r.xMin,t.yMin=r.yMin,t.xMax=r.xMax,t.yMax=r.yMax,Mr.postMessage(t,s),ot(s)}else Mr.postMessage(t);this._$posted=!0,this._$updated=!1}}class fe extends ee{constructor(){super(),this._$player=null,this._$root=this,this._$stage=this,this._$invalidate=!0,this._$color=4294967295,this._$frameRate=60}static toString(){return"[class Stage]"}static get namespace(){return"next2d.display.Stage"}toString(){return"[object Stage]"}get namespace(){return"next2d.display.Stage"}get color(){return this._$color}set color(t){this._$color=dt(xt(t),0,16777215,16777215);const e=this._$player;if(e&&e.context){const t=bt(this._$color);e.context._$setColor(t.R/255,t.G/255,t.B/255,t.A/255)}}get frameRate(){return this._$frameRate}set frameRate(t){this._$frameRate=dt(+t,1,60,60),this._$player&&!this._$player._$stopFlag&&(this._$player.stop(),this._$player.play())}get player(){return this._$player}get canvasHeight(){return this._$player?this._$player._$height/f:0}get canvasWidth(){return this._$player?this._$player._$width/f:0}get currentStageHeight(){return this._$player?this._$player.height*this._$player._$scale:0}get currentStageWidth(){return this._$player?this._$player.width*this._$player._$scale:0}get stageHeight(){return this._$player?this._$player.height:0}get stageWidth(){return this._$player?this._$player.width:0}invalidate(){this._$invalidate=!0}_$addChild(t){return t._$stage=this,t._$root=t,this._$created=!0,super._$addChild(t)}}class pe{static toString(){return"[class Easing]"}static get namespace(){return"next2d.ui.Easing"}toString(){return"[object Easing]"}get namespace(){return"next2d.ui.Easing"}static linear(t,e,i,s){return t/s*i+e}static inQuad(t,e,i,s){return(t/=s)*t*i+e}static outQuad(t,e,i,s){return-(t/=s)*(t-2)*i+e}static inOutQuad(t,e,i,s){return(t/=s/2)<1?t*t*i/2+e:-((t-=1)*(t-2)-1)*i/2+e}static inCubic(t,e,i,s){return(t/=s)*t*t*i+e}static outCubic(t,e,i,s){return t/=s,(--t*t*t+1)*i+e}static inOutCubic(t,e,i,s){return(t/=s/2)<1?t*t*t*i/2+e:((t-=2)*t*t+2)*i/2+e}static inQuart(t,e,i,s){return(t/=s)*t*t*t*i+e}static outQuart(t,e,i,s){return t/=s,(--t*t*t*t-1)*-i+e}static inOutQuart(t,e,i,s){return(t/=s/2)<1?t*t*t*t*i/2+e:((t-=2)*t*t*t-2)*-i/2+e}static inQuint(t,e,i,s){return(t/=s)*t*t*t*t*i+e}static outQuint(t,e,i,s){return t/=s,(--t*t*t*t*t+1)*i+e}static inOutQuint(t,e,i,s){return(t/=s/2)<1?t*t*t*t*t*i/2+e:((t-=2)*t*t*t*t+2)*i/2+e}static inSine(t,e,i,s){return-i*v.cos(t/s*(v.PI/2))+i+e}static outSine(t,e,i,s){return i*v.sin(t/s*(v.PI/2))+e}static inOutSine(t,e,i,s){return-i/2*(v.cos(v.PI*t/s)-1)+e}static inExpo(t,e,i,s){return i*v.pow(2,10*(t/s-1))+e}static outExpo(t,e,i,s){return i*(1-v.pow(2,-10*t/s))+e}static inOutExpo(t,e,i,s){return(t/=s/2)<1?i/2*v.pow(2,10*(t-1))+e:i/2*(2-v.pow(2,-10*(t-1)))+e}static inCirc(t,e,i,s){return(1-v.sqrt(1-(t/=s)*t))*i+e}static outCirc(t,e,i,s){return t/=s,v.sqrt(1- --t*t)*i+e}static inOutCirc(t,e,i,s){return(t/=2*s)<1?(v.sqrt(1-t*t)-1)/-2*i+e:(v.sqrt(1-(t-=2)*t)+1)/2*i+e}static inBack(t,e,i,s){return(2.70158*(t/=s)*t*t-1.70158*t*t)*i+e}static outBack(t,e,i,s){return(1+2.70158*v.pow((t/=s)-1,3)+1.70158*v.pow(t-1,2))*i+e}static inOutBack(t,e,i,s){let r=1.70158;return(t/=s/2)<1?t*t*((1+(r*=1.525))*t-r)*i/2+e:((t-=2)*t*((1+(r*=1.525))*t+r)+2)*i/2+e}static inElastic(t,e,i,s){return 0==(t/=s)?e:1===t?i+e:-v.pow(2,(t*=10)-10)*v.sin((t-10.75)*(2*v.PI/3))*i+e}static outElastic(t,e,i,s){return 0==(t/=s)?e:1===t?i+e:(v.pow(2,-10*t)*v.sin((10*t-.75)*(2*v.PI/3))+1)*i+e}static inOutElastic(t,e,i,s){return 0==(t/=s)?e:1===t?i+e:t<.5?-v.pow(2,20*t-10)*v.sin((20*t-11.125)*(2*v.PI/4.5))/2*i+e:(v.pow(2,-20*t+10)*v.sin((20*t-11.125)*(2*v.PI/4.5))/2+1)*i+e}static outBounce(t,e,i,s){return(t/=s)<1/2.75?7.5625*t*t*i+e:t<2/2.75?(7.5625*(t-=1.5/2.75)*t+.75)*i+e:t<2.5/2.75?(7.5625*(t-=2.25/2.75)*t+.9375)*i+e:(7.5625*(t-=2.625/2.75)*t+.984375)*i+e}static inBounce(t,e,i,s){return i-pe.outBounce(s-t,0,i,s)+e}static inOutBounce(t,e,i,s){return t{this.initialize()}),1e3*this._$delay):this.initialize()}stop(){this._$timerId&&F(this._$timerId),this.hasEventListener(It.STOP)&&(this.dispatchEvent(new It(It.STOP)),this.removeAllEventListener(It.STOP)),this._$names=null,this._$forceStop=!0,this._$stopFlag=!0}_$update(){if(!this._$stopFlag){if(!this._$names)return this.stop();this._$currentTime=.001*(R.now()-this._$startTime),this._$updateProperty(this._$target,this._$from,this._$to,this._$names),this.hasEventListener(It.UPDATE)&&this.dispatchEvent(new It(It.UPDATE)),this._$currentTime>=this._$duration?(this.hasEventListener(It.COMPLETE)&&this.dispatchEvent(new It(It.COMPLETE)),this._$nextJob&&this._$nextJob.start()):this._$timerId=requestAnimationFrame((()=>{this._$update()}))}}_$updateProperty(t,e,i,s){for(let r=0;rthis._$currentTime?t[a]=this._$ease(this._$currentTime,o,i[a]-o,this._$duration):t[a]=i[a]}}}class xe{static toString(){return"[class Tween]"}static get namespace(){return"next2d.ui.Tween"}toString(){return"[object Tween]"}get namespace(){return"next2d.ui.Tween"}static add(t,e,i,s=0,r=1,n=null){return new me(t,e,i,s,r,n)}}class be{constructor(t=null,e=null,i=null,s=null,r=null,n=null,a=null,h=null,o=null,l=null){this._$font=t,this._$size=e,this._$color=null===i?null:dt(xt(i),0,16777215,0),this._$bold=s,this._$italic=r,this._$underline=n,this._$align=a,this._$leftMargin=h,this._$rightMargin=o,this._$leading=l,this._$letterSpacing=0}static toString(){return"[class TextFormat]"}static get namespace(){return"next2d.text.TextFormat"}toString(){return"[object TextFormat]"}get namespace(){return"next2d.text.TextFormat"}get align(){return this._$align}set align(t){this._$align=t}get bold(){return this._$bold}set bold(t){this._$bold=null!==t?!!t:null}get color(){return this._$color}set color(t){this._$color=t,t&&(this._$color=dt(xt(t),0,16777215,0))}get font(){return this._$font}set font(t){this._$font=null!==t?`${t}`:null}get italic(){return this._$italic}set italic(t){this._$italic=null!==t?!!t:null}get leading(){return this._$leading}set leading(t){this._$leading=t}get leftMargin(){return this._$leftMargin}set leftMargin(t){this._$leftMargin=t}get letterSpacing(){return this._$letterSpacing}set letterSpacing(t){this._$letterSpacing=t}get rightMargin(){return this._$rightMargin}set rightMargin(t){this._$rightMargin=t}get size(){return this._$size}set size(t){this._$size=t?0|t:null}get underline(){return this._$underline}set underline(t){this._$underline=null!==t?!!t:null}_$toStyleString(){let t="";if(this._$font&&(t+=`font-family: ${this._$font};`),this._$size&&(t+=`font-size: ${this._$size}px;`),this._$color){const e=Et(xt(this._$color));t+=`color: #${e.R.toString(16).padStart(2,"0")}${e.G.toString(16).padStart(2,"0")}${e.B.toString(16).padStart(2,"0")};`}return this._$bold&&(t+="font-weight: bold;"),this._$italic&&(t+="font-style: italic;"),this._$underline&&(t+="text-decoration: underline;"),this._$align&&(t+=`text-align: ${this._$align};`),this._$leftMargin&&(t+=`margin-left: ${this._$leftMargin}px;`),this._$rightMargin&&(t+=`margin-right: ${this._$rightMargin}px;`),this._$leading&&(t+=`margin-bottom: ${this._$leading}px;`),this._$letterSpacing&&(t+=`letter-spacing: ${this._$letterSpacing}px;`),t}_$isSame(t){return this._$font===t.font&&this._$size===t.size&&this._$color===t.color&&this._$bold===t.bold&&this._$italic===t.italic&&this._$underline===t.underline&&this._$align===t.align&&this._$leftMargin===t.leftMargin&&this._$rightMargin===t.rightMargin&&this._$leading===t.leading&&this._$letterSpacing===t.letterSpacing}_$clone(){const t=new be(this._$font,this._$size,this._$color,this._$bold,this._$italic,this._$underline,this._$align,this._$leftMargin,this._$rightMargin,this._$leading);return t._$letterSpacing=this._$letterSpacing,t}_$setDefault(){this._$align="left",this._$bold=!1,this._$color=0,this._$font="Times New Roman",this._$italic=!1,this._$leading=0,this._$leftMargin=0,this._$letterSpacing=0,this._$rightMargin=0,this._$size=12,this._$underline=!1}_$merge(t){null===this._$align&&(this._$align=t._$align),null===this._$bold&&(this._$bold=t._$bold),null===this._$color&&(this._$color=t._$color),null===this._$font&&(this._$font=t._$font),null===this._$italic&&(this._$italic=t._$italic),null===this._$leading&&(this._$leading=t._$leading),null===this._$leftMargin&&(this._$leftMargin=t._$leftMargin),null===this._$letterSpacing&&(this._$letterSpacing=t._$letterSpacing),null===this._$rightMargin&&(this._$rightMargin=t._$rightMargin),null===this._$size&&(this._$size=t._$size),null===this._$underline&&(this._$underline=t._$underline)}_$widthMargin(){let t=0;return this._$leftMargin&&(t+=this._$leftMargin),this._$rightMargin&&(t+=this._$rightMargin),t}_$generateFontStyle(){let t="";return this._$italic&&(t="italic "),this._$bold&&(t+="bold "),`${t}${this._$size}px '${this._$font}',sans-serif`}}const ve=new Uint16Array('ᵁ<Õıʊҝջאٵ۞ޢߖࠏ੊ઑඡ๭༉༦჊ረዡᐕᒝᓃᓟᔥ\0\0\0\0\0\0ᕫᛍᦍᰒᷝ὾⁠↰⊍⏀⏻⑂⠤⤒ⴈ⹈⿎〖㊺㘹㞬㣾㨨㩱㫠㬮ࠀEMabcfglmnoprstu\\bfms„‹•˜¦³¹ÈÏlig耻Æ䃆P耻&䀦cute耻Á䃁reve;䄂Āiyx}rc耻Â䃂;䐐r;쀀𝔄rave耻À䃀pha;䎑acr;䄀d;橓Āgp¡on;䄄f;쀀𝔸plyFunction;恡ing耻Å䃅Ācs¾Ãr;쀀𝒜ign;扔ilde耻Ã䃃ml耻Ä䃄ЀaceforsuåûþėĜĢħĪĀcrêòkslash;或Ŷöø;櫧ed;挆y;䐑ƀcrtąċĔause;戵noullis;愬a;䎒r;쀀𝔅pf;쀀𝔹eve;䋘còēmpeq;扎܀HOacdefhilorsuōőŖƀƞƢƵƷƺǜȕɳɸɾcy;䐧PY耻©䂩ƀcpyŝŢźute;䄆Ā;iŧŨ拒talDifferentialD;慅leys;愭ȀaeioƉƎƔƘron;䄌dil耻Ç䃇rc;䄈nint;戰ot;䄊ĀdnƧƭilla;䂸terDot;䂷òſi;䎧rcleȀDMPTLJNjǑǖot;抙inus;抖lus;投imes;抗oĀcsǢǸkwiseContourIntegral;戲eCurlyĀDQȃȏoubleQuote;思uote;怙ȀlnpuȞȨɇɕonĀ;eȥȦ户;橴ƀgitȯȶȺruent;扡nt;戯ourIntegral;戮ĀfrɌɎ;愂oduct;成nterClockwiseContourIntegral;戳oss;樯cr;쀀𝒞pĀ;Cʄʅ拓ap;才րDJSZacefiosʠʬʰʴʸˋ˗ˡ˦̳ҍĀ;oŹʥtrahd;椑cy;䐂cy;䐅cy;䐏ƀgrsʿ˄ˇger;怡r;憡hv;櫤Āayː˕ron;䄎;䐔lĀ;t˝˞戇a;䎔r;쀀𝔇Āaf˫̧Ācm˰̢riticalȀADGT̖̜̀̆cute;䂴oŴ̋̍;䋙bleAcute;䋝rave;䁠ilde;䋜ond;拄ferentialD;慆Ѱ̽\0\0\0͔͂\0Ѕf;쀀𝔻ƀ;DE͈͉͍䂨ot;惜qual;扐blèCDLRUVͣͲ΂ϏϢϸontourIntegraìȹoɴ͹\0\0ͻ»͉nArrow;懓Āeo·ΤftƀARTΐΖΡrrow;懐ightArrow;懔eåˊngĀLRΫτeftĀARγιrrow;柸ightArrow;柺ightArrow;柹ightĀATϘϞrrow;懒ee;抨pɁϩ\0\0ϯrrow;懑ownArrow;懕erticalBar;戥ǹABLRTaВЪаўѿͼrrowƀ;BUНОТ憓ar;椓pArrow;懵reve;䌑eft˒к\0ц\0ѐightVector;楐eeVector;楞ectorĀ;Bљњ憽ar;楖ightǔѧ\0ѱeeVector;楟ectorĀ;BѺѻ懁ar;楗eeĀ;A҆҇护rrow;憧ĀctҒҗr;쀀𝒟rok;䄐ࠀNTacdfglmopqstuxҽӀӄӋӞӢӧӮӵԡԯԶՒ՝ՠեG;䅊H耻Ð䃐cute耻É䃉ƀaiyӒӗӜron;䄚rc耻Ê䃊;䐭ot;䄖r;쀀𝔈rave耻È䃈ement;戈ĀapӺӾcr;䄒tyɓԆ\0\0ԒmallSquare;旻erySmallSquare;斫ĀgpԦԪon;䄘f;쀀𝔼silon;䎕uĀaiԼՉlĀ;TՂՃ橵ilde;扂librium;懌Āci՗՚r;愰m;橳a;䎗ml耻Ë䃋Āipժկsts;戃onentialE;慇ʀcfiosօֈ֍ֲ׌y;䐤r;쀀𝔉lledɓ֗\0\0֣mallSquare;旼erySmallSquare;斪Ͱֺ\0ֿ\0\0ׄf;쀀𝔽All;戀riertrf;愱cò׋؀JTabcdfgorstר׬ׯ׺؀ؒؖ؛؝أ٬ٲcy;䐃耻>䀾mmaĀ;d׷׸䎓;䏜reve;䄞ƀeiy؇،ؐdil;䄢rc;䄜;䐓ot;䄠r;쀀𝔊;拙pf;쀀𝔾eater̀EFGLSTصلَٖٛ٦qualĀ;Lؾؿ扥ess;招ullEqual;执reater;檢ess;扷lantEqual;橾ilde;扳cr;쀀𝒢;扫ЀAacfiosuڅڋږڛڞڪھۊRDcy;䐪Āctڐڔek;䋇;䁞irc;䄤r;愌lbertSpace;愋ǰگ\0ڲf;愍izontalLine;攀Āctۃۅòکrok;䄦mpńېۘownHumðįqual;扏܀EJOacdfgmnostuۺ۾܃܇܎ܚܞܡܨ݄ݸދޏޕcy;䐕lig;䄲cy;䐁cute耻Í䃍Āiyܓܘrc耻Î䃎;䐘ot;䄰r;愑rave耻Ì䃌ƀ;apܠܯܿĀcgܴܷr;䄪inaryI;慈lieóϝǴ݉\0ݢĀ;eݍݎ戬Āgrݓݘral;戫section;拂isibleĀCTݬݲomma;恣imes;恢ƀgptݿރވon;䄮f;쀀𝕀a;䎙cr;愐ilde;䄨ǫޚ\0ޞcy;䐆l耻Ï䃏ʀcfosuެ޷޼߂ߐĀiyޱ޵rc;䄴;䐙r;쀀𝔍pf;쀀𝕁ǣ߇\0ߌr;쀀𝒥rcy;䐈kcy;䐄΀HJacfosߤߨ߽߬߱ࠂࠈcy;䐥cy;䐌ppa;䎚Āey߶߻dil;䄶;䐚r;쀀𝔎pf;쀀𝕂cr;쀀𝒦րJTaceflmostࠥࠩࠬࡐࡣ঳সে্਷ੇcy;䐉耻<䀼ʀcmnpr࠷࠼ࡁࡄࡍute;䄹bda;䎛g;柪lacetrf;愒r;憞ƀaeyࡗ࡜ࡡron;䄽dil;䄻;䐛Āfsࡨ॰tԀACDFRTUVarࡾࢩࢱࣦ࣠ࣼयज़ΐ४Ānrࢃ࢏gleBracket;柨rowƀ;BR࢙࢚࢞憐ar;懤ightArrow;懆eiling;挈oǵࢷ\0ࣃbleBracket;柦nǔࣈ\0࣒eeVector;楡ectorĀ;Bࣛࣜ懃ar;楙loor;挊ightĀAV࣯ࣵrrow;憔ector;楎Āerँगeƀ;AVउऊऐ抣rrow;憤ector;楚iangleƀ;BEतथऩ抲ar;槏qual;抴pƀDTVषूौownVector;楑eeVector;楠ectorĀ;Bॖॗ憿ar;楘ectorĀ;B॥०憼ar;楒ightáΜs̀EFGLSTॾঋকঝঢভqualGreater;拚ullEqual;扦reater;扶ess;檡lantEqual;橽ilde;扲r;쀀𝔏Ā;eঽা拘ftarrow;懚idot;䄿ƀnpw৔ਖਛgȀLRlr৞৷ਂਐeftĀAR০৬rrow;柵ightArrow;柷ightArrow;柶eftĀarγਊightáοightáϊf;쀀𝕃erĀLRਢਬeftArrow;憙ightArrow;憘ƀchtਾੀੂòࡌ;憰rok;䅁;扪Ѐacefiosuਗ਼੝੠੷੼અઋ઎p;椅y;䐜Ādl੥੯iumSpace;恟lintrf;愳r;쀀𝔐nusPlus;戓pf;쀀𝕄cò੶;䎜ҀJacefostuણધભીଔଙඑ඗ඞcy;䐊cute;䅃ƀaey઴હાron;䅇dil;䅅;䐝ƀgswે૰଎ativeƀMTV૓૟૨ediumSpace;怋hiĀcn૦૘ë૙eryThiî૙tedĀGL૸ଆreaterGreateòٳessLesóੈLine;䀊r;쀀𝔑ȀBnptଢନଷ଺reak;恠BreakingSpace;䂠f;愕ڀ;CDEGHLNPRSTV୕ୖ୪୼஡௫ఄ౞಄ದ೘ൡඅ櫬Āou୛୤ngruent;扢pCap;扭oubleVerticalBar;戦ƀlqxஃஊ஛ement;戉ualĀ;Tஒஓ扠ilde;쀀≂̸ists;戄reater΀;EFGLSTஶஷ஽௉௓௘௥扯qual;扱ullEqual;쀀≧̸reater;쀀≫̸ess;批lantEqual;쀀⩾̸ilde;扵umpń௲௽ownHump;쀀≎̸qual;쀀≏̸eĀfsఊధtTriangleƀ;BEచఛడ拪ar;쀀⧏̸qual;括s̀;EGLSTవశ఼ౄోౘ扮qual;扰reater;扸ess;쀀≪̸lantEqual;쀀⩽̸ilde;扴estedĀGL౨౹reaterGreater;쀀⪢̸essLess;쀀⪡̸recedesƀ;ESಒಓಛ技qual;쀀⪯̸lantEqual;拠ĀeiಫಹverseElement;戌ghtTriangleƀ;BEೋೌ೒拫ar;쀀⧐̸qual;拭ĀquೝഌuareSuĀbp೨೹setĀ;E೰ೳ쀀⊏̸qual;拢ersetĀ;Eഃആ쀀⊐̸qual;拣ƀbcpഓതൎsetĀ;Eഛഞ쀀⊂⃒qual;抈ceedsȀ;ESTലള഻െ抁qual;쀀⪰̸lantEqual;拡ilde;쀀≿̸ersetĀ;E൘൛쀀⊃⃒qual;抉ildeȀ;EFT൮൯൵ൿ扁qual;扄ullEqual;扇ilde;扉erticalBar;戤cr;쀀𝒩ilde耻Ñ䃑;䎝܀Eacdfgmoprstuvලෂ෉෕ෛ෠෧෼ขภยา฿ไlig;䅒cute耻Ó䃓Āiy෎ීrc耻Ô䃔;䐞blac;䅐r;쀀𝔒rave耻Ò䃒ƀaei෮ෲ෶cr;䅌ga;䎩cron;䎟pf;쀀𝕆enCurlyĀDQฎบoubleQuote;怜uote;怘;橔Āclวฬr;쀀𝒪ash耻Ø䃘iŬื฼de耻Õ䃕es;樷ml耻Ö䃖erĀBP๋๠Āar๐๓r;怾acĀek๚๜;揞et;掴arenthesis;揜Ҁacfhilors๿ງຊຏຒດຝະ໼rtialD;戂y;䐟r;쀀𝔓i;䎦;䎠usMinus;䂱Āipຢອncareplanåڝf;愙Ȁ;eio຺ູ໠໤檻cedesȀ;EST່້໏໚扺qual;檯lantEqual;扼ilde;找me;怳Ādp໩໮uct;戏ortionĀ;aȥ໹l;戝Āci༁༆r;쀀𝒫;䎨ȀUfos༑༖༛༟OT耻"䀢r;쀀𝔔pf;愚cr;쀀𝒬؀BEacefhiorsu༾གྷཇའཱིྦྷྪྭ႖ႩႴႾarr;椐G耻®䂮ƀcnrཎནབute;䅔g;柫rĀ;tཛྷཝ憠l;椖ƀaeyཧཬཱron;䅘dil;䅖;䐠Ā;vླྀཹ愜erseĀEUྂྙĀlq྇ྎement;戋uilibrium;懋pEquilibrium;楯r»ཹo;䎡ghtЀACDFTUVa࿁࿫࿳ဢဨၛႇϘĀnr࿆࿒gleBracket;柩rowƀ;BL࿜࿝࿡憒ar;懥eftArrow;懄eiling;按oǵ࿹\0စbleBracket;柧nǔည\0နeeVector;楝ectorĀ;Bဝသ懂ar;楕loor;挋Āerိ၃eƀ;AVဵံြ抢rrow;憦ector;楛iangleƀ;BEၐၑၕ抳ar;槐qual;抵pƀDTVၣၮၸownVector;楏eeVector;楜ectorĀ;Bႂႃ憾ar;楔ectorĀ;B႑႒懀ar;楓Āpuႛ႞f;愝ndImplies;楰ightarrow;懛ĀchႹႼr;愛;憱leDelayed;槴ڀHOacfhimoqstuფჱჷჽᄙᄞᅑᅖᅡᅧᆵᆻᆿĀCcჩხHcy;䐩y;䐨FTcy;䐬cute;䅚ʀ;aeiyᄈᄉᄎᄓᄗ檼ron;䅠dil;䅞rc;䅜;䐡r;쀀𝔖ortȀDLRUᄪᄴᄾᅉownArrow»ОeftArrow»࢚ightArrow»࿝pArrow;憑gma;䎣allCircle;战pf;쀀𝕊ɲᅭ\0\0ᅰt;戚areȀ;ISUᅻᅼᆉᆯ斡ntersection;抓uĀbpᆏᆞsetĀ;Eᆗᆘ抏qual;抑ersetĀ;Eᆨᆩ抐qual;抒nion;抔cr;쀀𝒮ar;拆ȀbcmpᇈᇛሉላĀ;sᇍᇎ拐etĀ;Eᇍᇕqual;抆ĀchᇠህeedsȀ;ESTᇭᇮᇴᇿ扻qual;檰lantEqual;扽ilde;承Tháྌ;我ƀ;esሒሓሣ拑rsetĀ;Eሜም抃qual;抇et»ሓրHRSacfhiorsሾቄ቉ቕ቞ቱቶኟዂወዑORN耻Þ䃞ADE;愢ĀHc቎ቒcy;䐋y;䐦Ābuቚቜ;䀉;䎤ƀaeyብቪቯron;䅤dil;䅢;䐢r;쀀𝔗Āeiቻ኉Dzኀ\0ኇefore;戴a;䎘Ācn኎ኘkSpace;쀀  Space;怉ldeȀ;EFTካኬኲኼ戼qual;扃ullEqual;扅ilde;扈pf;쀀𝕋ipleDot;惛Āctዖዛr;쀀𝒯rok;䅦ૡዷጎጚጦ\0ጬጱ\0\0\0\0\0ጸጽ፷ᎅ\0᏿ᐄᐊᐐĀcrዻጁute耻Ú䃚rĀ;oጇገ憟cir;楉rǣጓ\0጖y;䐎ve;䅬Āiyጞጣrc耻Û䃛;䐣blac;䅰r;쀀𝔘rave耻Ù䃙acr;䅪Ādiፁ፩erĀBPፈ፝Āarፍፐr;䁟acĀekፗፙ;揟et;掵arenthesis;揝onĀ;P፰፱拃lus;抎Āgp፻፿on;䅲f;쀀𝕌ЀADETadps᎕ᎮᎸᏄϨᏒᏗᏳrrowƀ;BDᅐᎠᎤar;椒ownArrow;懅ownArrow;憕quilibrium;楮eeĀ;AᏋᏌ报rrow;憥ownáϳerĀLRᏞᏨeftArrow;憖ightArrow;憗iĀ;lᏹᏺ䏒on;䎥ing;䅮cr;쀀𝒰ilde;䅨ml耻Ü䃜ҀDbcdefosvᐧᐬᐰᐳᐾᒅᒊᒐᒖash;披ar;櫫y;䐒ashĀ;lᐻᐼ抩;櫦Āerᑃᑅ;拁ƀbtyᑌᑐᑺar;怖Ā;iᑏᑕcalȀBLSTᑡᑥᑪᑴar;戣ine;䁼eparator;杘ilde;所ThinSpace;怊r;쀀𝔙pf;쀀𝕍cr;쀀𝒱dash;抪ʀcefosᒧᒬᒱᒶᒼirc;䅴dge;拀r;쀀𝔚pf;쀀𝕎cr;쀀𝒲Ȁfiosᓋᓐᓒᓘr;쀀𝔛;䎞pf;쀀𝕏cr;쀀𝒳ҀAIUacfosuᓱᓵᓹᓽᔄᔏᔔᔚᔠcy;䐯cy;䐇cy;䐮cute耻Ý䃝Āiyᔉᔍrc;䅶;䐫r;쀀𝔜pf;쀀𝕐cr;쀀𝒴ml;䅸ЀHacdefosᔵᔹᔿᕋᕏᕝᕠᕤcy;䐖cute;䅹Āayᕄᕉron;䅽;䐗ot;䅻Dzᕔ\0ᕛoWidtè૙a;䎖r;愨pf;愤cr;쀀𝒵௡ᖃᖊᖐ\0ᖰᖶᖿ\0\0\0\0ᗆᗛᗫᙟ᙭\0ᚕ᚛ᚲᚹ\0ᚾcute耻á䃡reve;䄃̀;Ediuyᖜᖝᖡᖣᖨᖭ戾;쀀∾̳;房rc耻â䃢te肻´̆;䐰lig耻æ䃦Ā;r²ᖺ;쀀𝔞rave耻à䃠ĀepᗊᗖĀfpᗏᗔsym;愵èᗓha;䎱ĀapᗟcĀclᗤᗧr;䄁g;樿ɤᗰ\0\0ᘊʀ;adsvᗺᗻᗿᘁᘇ戧nd;橕;橜lope;橘;橚΀;elmrszᘘᘙᘛᘞᘿᙏᙙ戠;榤e»ᘙsdĀ;aᘥᘦ戡ѡᘰᘲᘴᘶᘸᘺᘼᘾ;榨;榩;榪;榫;榬;榭;榮;榯tĀ;vᙅᙆ戟bĀ;dᙌᙍ抾;榝Āptᙔᙗh;戢»¹arr;捼Āgpᙣᙧon;䄅f;쀀𝕒΀;Eaeiop዁ᙻᙽᚂᚄᚇᚊ;橰cir;橯;扊d;手s;䀧roxĀ;e዁ᚒñᚃing耻å䃥ƀctyᚡᚦᚨr;쀀𝒶;䀪mpĀ;e዁ᚯñʈilde耻ã䃣ml耻ä䃤Āciᛂᛈoninôɲnt;樑ࠀNabcdefiklnoprsu᛭ᛱᜰ᜼ᝃᝈ᝸᝽០៦ᠹᡐᜍ᤽᥈ᥰot;櫭Ācrᛶ᜞kȀcepsᜀᜅᜍᜓong;扌psilon;䏶rime;怵imĀ;e᜚᜛戽q;拍Ŷᜢᜦee;抽edĀ;gᜬᜭ挅e»ᜭrkĀ;t፜᜷brk;掶Āoyᜁᝁ;䐱quo;怞ʀcmprtᝓ᝛ᝡᝤᝨausĀ;eĊĉptyv;榰séᜌnoõēƀahwᝯ᝱ᝳ;䎲;愶een;扬r;쀀𝔟g΀costuvwឍឝឳេ៕៛៞ƀaiuបពរðݠrc;旯p»፱ƀdptឤឨឭot;樀lus;樁imes;樂ɱឹ\0\0ើcup;樆ar;昅riangleĀdu៍្own;施p;斳plus;樄eåᑄåᒭarow;植ƀako៭ᠦᠵĀcn៲ᠣkƀlst៺֫᠂ozenge;槫riangleȀ;dlr᠒᠓᠘᠝斴own;斾eft;旂ight;斸k;搣Ʊᠫ\0ᠳƲᠯ\0ᠱ;斒;斑4;斓ck;斈ĀeoᠾᡍĀ;qᡃᡆ쀀=⃥uiv;쀀≡⃥t;挐Ȁptwxᡙᡞᡧᡬf;쀀𝕓Ā;tᏋᡣom»Ꮜtie;拈؀DHUVbdhmptuvᢅᢖᢪᢻᣗᣛᣬ᣿ᤅᤊᤐᤡȀLRlrᢎᢐᢒᢔ;敗;敔;敖;敓ʀ;DUduᢡᢢᢤᢦᢨ敐;敦;敩;敤;敧ȀLRlrᢳᢵᢷᢹ;敝;敚;敜;教΀;HLRhlrᣊᣋᣍᣏᣑᣓᣕ救;敬;散;敠;敫;敢;敟ox;槉ȀLRlrᣤᣦᣨᣪ;敕;敒;攐;攌ʀ;DUduڽ᣷᣹᣻᣽;敥;敨;攬;攴inus;抟lus;択imes;抠ȀLRlrᤙᤛᤝ᤟;敛;敘;攘;攔΀;HLRhlrᤰᤱᤳᤵᤷ᤻᤹攂;敪;敡;敞;攼;攤;攜Āevģ᥂bar耻¦䂦Ȁceioᥑᥖᥚᥠr;쀀𝒷mi;恏mĀ;e᜚᜜lƀ;bhᥨᥩᥫ䁜;槅sub;柈Ŭᥴ᥾lĀ;e᥹᥺怢t»᥺pƀ;Eeįᦅᦇ;檮Ā;qۜۛೡᦧ\0᧨ᨑᨕᨲ\0ᨷᩐ\0\0᪴\0\0᫁\0\0ᬡᬮ᭍᭒\0᯽\0ᰌƀcpr᦭ᦲ᧝ute;䄇̀;abcdsᦿᧀᧄ᧊᧕᧙戩nd;橄rcup;橉Āau᧏᧒p;橋p;橇ot;橀;쀀∩︀Āeo᧢᧥t;恁îړȀaeiu᧰᧻ᨁᨅǰ᧵\0᧸s;橍on;䄍dil耻ç䃧rc;䄉psĀ;sᨌᨍ橌m;橐ot;䄋ƀdmnᨛᨠᨦil肻¸ƭptyv;榲t脀¢;eᨭᨮ䂢räƲr;쀀𝔠ƀceiᨽᩀᩍy;䑇ckĀ;mᩇᩈ朓ark»ᩈ;䏇r΀;Ecefms᩟᩠ᩢᩫ᪤᪪᪮旋;槃ƀ;elᩩᩪᩭ䋆q;扗eɡᩴ\0\0᪈rrowĀlr᩼᪁eft;憺ight;憻ʀRSacd᪒᪔᪖᪚᪟»ཇ;擈st;抛irc;抚ash;抝nint;樐id;櫯cir;槂ubsĀ;u᪻᪼晣it»᪼ˬ᫇᫔᫺\0ᬊonĀ;eᫍᫎ䀺Ā;qÇÆɭ᫙\0\0᫢aĀ;t᫞᫟䀬;䁀ƀ;fl᫨᫩᫫戁îᅠeĀmx᫱᫶ent»᫩eóɍǧ᫾\0ᬇĀ;dኻᬂot;橭nôɆƀfryᬐᬔᬗ;쀀𝕔oäɔ脀©;sŕᬝr;愗Āaoᬥᬩrr;憵ss;朗Ācuᬲᬷr;쀀𝒸Ābpᬼ᭄Ā;eᭁᭂ櫏;櫑Ā;eᭉᭊ櫐;櫒dot;拯΀delprvw᭠᭬᭷ᮂᮬᯔ᯹arrĀlr᭨᭪;椸;椵ɰ᭲\0\0᭵r;拞c;拟arrĀ;p᭿ᮀ憶;椽̀;bcdosᮏᮐᮖᮡᮥᮨ截rcap;橈Āauᮛᮞp;橆p;橊ot;抍r;橅;쀀∪︀Ȁalrv᮵ᮿᯞᯣrrĀ;mᮼᮽ憷;椼yƀevwᯇᯔᯘqɰᯎ\0\0ᯒreã᭳uã᭵ee;拎edge;拏en耻¤䂤earrowĀlrᯮ᯳eft»ᮀight»ᮽeäᯝĀciᰁᰇoninôǷnt;戱lcty;挭ঀAHabcdefhijlorstuwz᰸᰻᰿ᱝᱩᱵᲊᲞᲬᲷ᳻᳿ᴍᵻᶑᶫᶻ᷆᷍rò΁ar;楥Ȁglrs᱈ᱍ᱒᱔ger;怠eth;愸òᄳhĀ;vᱚᱛ怐»ऊūᱡᱧarow;椏aã̕Āayᱮᱳron;䄏;䐴ƀ;ao̲ᱼᲄĀgrʿᲁr;懊tseq;橷ƀglmᲑᲔᲘ耻°䂰ta;䎴ptyv;榱ĀirᲣᲨsht;楿;쀀𝔡arĀlrᲳᲵ»ࣜ»သʀaegsv᳂͸᳖᳜᳠mƀ;oș᳊᳔ndĀ;ș᳑uit;晦amma;䏝in;拲ƀ;io᳧᳨᳸䃷de脀÷;o᳧ᳰntimes;拇nø᳷cy;䑒cɯᴆ\0\0ᴊrn;挞op;挍ʀlptuwᴘᴝᴢᵉᵕlar;䀤f;쀀𝕕ʀ;emps̋ᴭᴷᴽᵂqĀ;d͒ᴳot;扑inus;戸lus;戔quare;抡blebarwedgåúnƀadhᄮᵝᵧownarrowóᲃarpoonĀlrᵲᵶefôᲴighôᲶŢᵿᶅkaro÷གɯᶊ\0\0ᶎrn;挟op;挌ƀcotᶘᶣᶦĀryᶝᶡ;쀀𝒹;䑕l;槶rok;䄑Ādrᶰᶴot;拱iĀ;fᶺ᠖斿Āah᷀᷃ròЩaòྦangle;榦Āci᷒ᷕy;䑟grarr;柿ऀDacdefglmnopqrstuxḁḉḙḸոḼṉṡṾấắẽỡἪἷὄ὎὚ĀDoḆᴴoôᲉĀcsḎḔute耻é䃩ter;橮ȀaioyḢḧḱḶron;䄛rĀ;cḭḮ扖耻ê䃪lon;払;䑍ot;䄗ĀDrṁṅot;扒;쀀𝔢ƀ;rsṐṑṗ檚ave耻è䃨Ā;dṜṝ檖ot;檘Ȁ;ilsṪṫṲṴ檙nters;揧;愓Ā;dṹṺ檕ot;檗ƀapsẅẉẗcr;䄓tyƀ;svẒẓẕ戅et»ẓpĀ1;ẝẤijạả;怄;怅怃ĀgsẪẬ;䅋p;怂ĀgpẴẸon;䄙f;쀀𝕖ƀalsỄỎỒrĀ;sỊị拕l;槣us;橱iƀ;lvỚớở䎵on»ớ;䏵ȀcsuvỪỳἋἣĀioữḱrc»Ḯɩỹ\0\0ỻíՈantĀglἂἆtr»ṝess»Ṻƀaeiἒ἖Ἒls;䀽st;扟vĀ;DȵἠD;橸parsl;槥ĀDaἯἳot;打rr;楱ƀcdiἾὁỸr;愯oô͒ĀahὉὋ;䎷耻ð䃰Āmrὓὗl耻ë䃫o;悬ƀcipὡὤὧl;䀡sôծĀeoὬὴctatioîՙnentialåչৡᾒ\0ᾞ\0ᾡᾧ\0\0ῆῌ\0ΐ\0ῦῪ \0 ⁚llingdotseñṄy;䑄male;晀ƀilrᾭᾳ῁lig;耀ffiɩᾹ\0\0᾽g;耀ffig;耀ffl;쀀𝔣lig;耀filig;쀀fjƀaltῙ῜ῡt;晭ig;耀flns;斱of;䆒ǰ΅\0ῳf;쀀𝕗ĀakֿῷĀ;vῼ´拔;櫙artint;樍Āao‌⁕Ācs‑⁒ႉ‸⁅⁈\0⁐β•‥‧‪‬\0‮耻½䂽;慓耻¼䂼;慕;慙;慛Ƴ‴\0‶;慔;慖ʴ‾⁁\0\0⁃耻¾䂾;慗;慜5;慘ƶ⁌\0⁎;慚;慝8;慞l;恄wn;挢cr;쀀𝒻ࢀEabcdefgijlnorstv₂₉₟₥₰₴⃰⃵⃺⃿℃ℒℸ̗ℾ⅒↞Ā;lٍ₇;檌ƀcmpₐₕ₝ute;䇵maĀ;dₜ᳚䎳;檆reve;䄟Āiy₪₮rc;䄝;䐳ot;䄡Ȁ;lqsؾق₽⃉ƀ;qsؾٌ⃄lanô٥Ȁ;cdl٥⃒⃥⃕c;檩otĀ;o⃜⃝檀Ā;l⃢⃣檂;檄Ā;e⃪⃭쀀⋛︀s;檔r;쀀𝔤Ā;gٳ؛mel;愷cy;䑓Ȁ;Eajٚℌℎℐ;檒;檥;檤ȀEaesℛℝ℩ℴ;扩pĀ;p℣ℤ檊rox»ℤĀ;q℮ℯ檈Ā;q℮ℛim;拧pf;쀀𝕘Āci⅃ⅆr;愊mƀ;el٫ⅎ⅐;檎;檐茀>;cdlqr׮ⅠⅪⅮⅳⅹĀciⅥⅧ;檧r;橺ot;拗Par;榕uest;橼ʀadelsↄⅪ←ٖ↛ǰ↉\0↎proø₞r;楸qĀlqؿ↖lesó₈ií٫Āen↣↭rtneqq;쀀≩︀Å↪ԀAabcefkosy⇄⇇⇱⇵⇺∘∝∯≨≽ròΠȀilmr⇐⇔⇗⇛rsðᒄf»․ilôکĀdr⇠⇤cy;䑊ƀ;cwࣴ⇫⇯ir;楈;憭ar;意irc;䄥ƀalr∁∎∓rtsĀ;u∉∊晥it»∊lip;怦con;抹r;쀀𝔥sĀew∣∩arow;椥arow;椦ʀamopr∺∾≃≞≣rr;懿tht;戻kĀlr≉≓eftarrow;憩ightarrow;憪f;쀀𝕙bar;怕ƀclt≯≴≸r;쀀𝒽asè⇴rok;䄧Ābp⊂⊇ull;恃hen»ᱛૡ⊣\0⊪\0⊸⋅⋎\0⋕⋳\0\0⋸⌢⍧⍢⍿\0⎆⎪⎴cute耻í䃭ƀ;iyݱ⊰⊵rc耻î䃮;䐸Ācx⊼⊿y;䐵cl耻¡䂡ĀfrΟ⋉;쀀𝔦rave耻ì䃬Ȁ;inoܾ⋝⋩⋮Āin⋢⋦nt;樌t;戭fin;槜ta;愩lig;䄳ƀaop⋾⌚⌝ƀcgt⌅⌈⌗r;䄫ƀelpܟ⌏⌓inåގarôܠh;䄱f;抷ed;䆵ʀ;cfotӴ⌬⌱⌽⍁are;愅inĀ;t⌸⌹戞ie;槝doô⌙ʀ;celpݗ⍌⍐⍛⍡al;抺Āgr⍕⍙eróᕣã⍍arhk;樗rod;樼Ȁcgpt⍯⍲⍶⍻y;䑑on;䄯f;쀀𝕚a;䎹uest耻¿䂿Āci⎊⎏r;쀀𝒾nʀ;EdsvӴ⎛⎝⎡ӳ;拹ot;拵Ā;v⎦⎧拴;拳Ā;iݷ⎮lde;䄩ǫ⎸\0⎼cy;䑖l耻ï䃯̀cfmosu⏌⏗⏜⏡⏧⏵Āiy⏑⏕rc;䄵;䐹r;쀀𝔧ath;䈷pf;쀀𝕛ǣ⏬\0⏱r;쀀𝒿rcy;䑘kcy;䑔Ѐacfghjos␋␖␢␧␭␱␵␻ppaĀ;v␓␔䎺;䏰Āey␛␠dil;䄷;䐺r;쀀𝔨reen;䄸cy;䑅cy;䑜pf;쀀𝕜cr;쀀𝓀஀ABEHabcdefghjlmnoprstuv⑰⒁⒆⒍⒑┎┽╚▀♎♞♥♹♽⚚⚲⛘❝❨➋⟀⠁⠒ƀart⑷⑺⑼rò৆òΕail;椛arr;椎Ā;gঔ⒋;檋ar;楢ॣ⒥\0⒪\0⒱\0\0\0\0\0⒵Ⓔ\0ⓆⓈⓍ\0⓹ute;䄺mptyv;榴raîࡌbda;䎻gƀ;dlࢎⓁⓃ;榑åࢎ;檅uo耻«䂫rЀ;bfhlpst࢙ⓞⓦⓩ⓫⓮⓱⓵Ā;f࢝ⓣs;椟s;椝ë≒p;憫l;椹im;楳l;憢ƀ;ae⓿─┄檫il;椙Ā;s┉┊檭;쀀⪭︀ƀabr┕┙┝rr;椌rk;杲Āak┢┬cĀek┨┪;䁻;䁛Āes┱┳;榋lĀdu┹┻;榏;榍Ȁaeuy╆╋╖╘ron;䄾Ādi═╔il;䄼ìࢰâ┩;䐻Ȁcqrs╣╦╭╽a;椶uoĀ;rนᝆĀdu╲╷har;楧shar;楋h;憲ʀ;fgqs▋▌উ◳◿扤tʀahlrt▘▤▷◂◨rrowĀ;t࢙□aé⓶arpoonĀdu▯▴own»њp»०eftarrows;懇ightƀahs◍◖◞rrowĀ;sࣴࢧarpoonó྘quigarro÷⇰hreetimes;拋ƀ;qs▋ও◺lanôবʀ;cdgsব☊☍☝☨c;檨otĀ;o☔☕橿Ā;r☚☛檁;檃Ā;e☢☥쀀⋚︀s;檓ʀadegs☳☹☽♉♋pproøⓆot;拖qĀgq♃♅ôউgtò⒌ôছiíলƀilr♕࣡♚sht;楼;쀀𝔩Ā;Eজ♣;檑š♩♶rĀdu▲♮Ā;l॥♳;楪lk;斄cy;䑙ʀ;achtੈ⚈⚋⚑⚖rò◁orneòᴈard;楫ri;旺Āio⚟⚤dot;䅀ustĀ;a⚬⚭掰che»⚭ȀEaes⚻⚽⛉⛔;扨pĀ;p⛃⛄檉rox»⛄Ā;q⛎⛏檇Ā;q⛎⚻im;拦Ѐabnoptwz⛩⛴⛷✚✯❁❇❐Ānr⛮⛱g;柬r;懽rëࣁgƀlmr⛿✍✔eftĀar০✇ightá৲apsto;柼ightá৽parrowĀlr✥✩efô⓭ight;憬ƀafl✶✹✽r;榅;쀀𝕝us;樭imes;樴š❋❏st;戗áፎƀ;ef❗❘᠀旊nge»❘arĀ;l❤❥䀨t;榓ʀachmt❳❶❼➅➇ròࢨorneòᶌarĀ;d྘➃;業;怎ri;抿̀achiqt➘➝ੀ➢➮➻quo;怹r;쀀𝓁mƀ;egল➪➬;檍;檏Ābu┪➳oĀ;rฟ➹;怚rok;䅂萀<;cdhilqrࠫ⟒☹⟜⟠⟥⟪⟰Āci⟗⟙;檦r;橹reå◲mes;拉arr;楶uest;橻ĀPi⟵⟹ar;榖ƀ;ef⠀भ᠛旃rĀdu⠇⠍shar;楊har;楦Āen⠗⠡rtneqq;쀀≨︀Å⠞܀Dacdefhilnopsu⡀⡅⢂⢎⢓⢠⢥⢨⣚⣢⣤ઃ⣳⤂Dot;戺Ȁclpr⡎⡒⡣⡽r耻¯䂯Āet⡗⡙;時Ā;e⡞⡟朠se»⡟Ā;sျ⡨toȀ;dluျ⡳⡷⡻owîҌefôएðᏑker;斮Āoy⢇⢌mma;権;䐼ash;怔asuredangle»ᘦr;쀀𝔪o;愧ƀcdn⢯⢴⣉ro耻µ䂵Ȁ;acdᑤ⢽⣀⣄sôᚧir;櫰ot肻·Ƶusƀ;bd⣒ᤃ⣓戒Ā;uᴼ⣘;横ţ⣞⣡p;櫛ò−ðઁĀdp⣩⣮els;抧f;쀀𝕞Āct⣸⣽r;쀀𝓂pos»ᖝƀ;lm⤉⤊⤍䎼timap;抸ఀGLRVabcdefghijlmoprstuvw⥂⥓⥾⦉⦘⧚⧩⨕⨚⩘⩝⪃⪕⪤⪨⬄⬇⭄⭿⮮ⰴⱧⱼ⳩Āgt⥇⥋;쀀⋙̸Ā;v⥐௏쀀≫⃒ƀelt⥚⥲⥶ftĀar⥡⥧rrow;懍ightarrow;懎;쀀⋘̸Ā;v⥻ే쀀≪⃒ightarrow;懏ĀDd⦎⦓ash;抯ash;抮ʀbcnpt⦣⦧⦬⦱⧌la»˞ute;䅄g;쀀∠⃒ʀ;Eiop඄⦼⧀⧅⧈;쀀⩰̸d;쀀≋̸s;䅉roø඄urĀ;a⧓⧔普lĀ;s⧓ସdz⧟\0⧣p肻 ଷmpĀ;e௹ఀʀaeouy⧴⧾⨃⨐⨓ǰ⧹\0⧻;橃on;䅈dil;䅆ngĀ;dൾ⨊ot;쀀⩭̸p;橂;䐽ash;怓΀;Aadqsxஒ⨩⨭⨻⩁⩅⩐rr;懗rĀhr⨳⨶k;椤Ā;oᏲᏰot;쀀≐̸uiöୣĀei⩊⩎ar;椨í஘istĀ;s஠டr;쀀𝔫ȀEest௅⩦⩹⩼ƀ;qs஼⩭௡ƀ;qs஼௅⩴lanô௢ií௪Ā;rஶ⪁»ஷƀAap⪊⪍⪑rò⥱rr;憮ar;櫲ƀ;svྍ⪜ྌĀ;d⪡⪢拼;拺cy;䑚΀AEadest⪷⪺⪾⫂⫅⫶⫹rò⥦;쀀≦̸rr;憚r;急Ȁ;fqs఻⫎⫣⫯tĀar⫔⫙rro÷⫁ightarro÷⪐ƀ;qs఻⪺⫪lanôౕĀ;sౕ⫴»శiíౝĀ;rవ⫾iĀ;eచథiäඐĀpt⬌⬑f;쀀𝕟膀¬;in⬙⬚⬶䂬nȀ;Edvஉ⬤⬨⬮;쀀⋹̸ot;쀀⋵̸ǡஉ⬳⬵;拷;拶iĀ;vಸ⬼ǡಸ⭁⭃;拾;拽ƀaor⭋⭣⭩rȀ;ast୻⭕⭚⭟lleì୻l;쀀⫽⃥;쀀∂̸lint;樔ƀ;ceಒ⭰⭳uåಥĀ;cಘ⭸Ā;eಒ⭽ñಘȀAait⮈⮋⮝⮧rò⦈rrƀ;cw⮔⮕⮙憛;쀀⤳̸;쀀↝̸ghtarrow»⮕riĀ;eೋೖ΀chimpqu⮽⯍⯙⬄୸⯤⯯Ȁ;cerല⯆ഷ⯉uå൅;쀀𝓃ortɭ⬅\0\0⯖ará⭖mĀ;e൮⯟Ā;q൴൳suĀbp⯫⯭å೸åഋƀbcp⯶ⰑⰙȀ;Ees⯿ⰀഢⰄ抄;쀀⫅̸etĀ;eഛⰋqĀ;qണⰀcĀ;eലⰗñസȀ;EesⰢⰣൟⰧ抅;쀀⫆̸etĀ;e൘ⰮqĀ;qൠⰣȀgilrⰽⰿⱅⱇìௗlde耻ñ䃱çృiangleĀlrⱒⱜeftĀ;eచⱚñదightĀ;eೋⱥñ೗Ā;mⱬⱭ䎽ƀ;esⱴⱵⱹ䀣ro;愖p;怇ҀDHadgilrsⲏⲔⲙⲞⲣⲰⲶⳓⳣash;抭arr;椄p;쀀≍⃒ash;抬ĀetⲨⲬ;쀀≥⃒;쀀>⃒nfin;槞ƀAetⲽⳁⳅrr;椂;쀀≤⃒Ā;rⳊⳍ쀀<⃒ie;쀀⊴⃒ĀAtⳘⳜrr;椃rie;쀀⊵⃒im;쀀∼⃒ƀAan⳰⳴ⴂrr;懖rĀhr⳺⳽k;椣Ā;oᏧᏥear;椧ቓ᪕\0\0\0\0\0\0\0\0\0\0\0\0\0ⴭ\0ⴸⵈⵠⵥ⵲ⶄᬇ\0\0ⶍⶫ\0ⷈⷎ\0ⷜ⸙⸫⸾⹃Ācsⴱ᪗ute耻ó䃳ĀiyⴼⵅrĀ;c᪞ⵂ耻ô䃴;䐾ʀabios᪠ⵒⵗLjⵚlac;䅑v;樸old;榼lig;䅓Ācr⵩⵭ir;榿;쀀𝔬ͯ⵹\0\0⵼\0ⶂn;䋛ave耻ò䃲;槁Ābmⶈ෴ar;榵Ȁacitⶕ⶘ⶥⶨrò᪀Āir⶝ⶠr;榾oss;榻nå๒;槀ƀaeiⶱⶵⶹcr;䅍ga;䏉ƀcdnⷀⷅǍron;䎿;榶pf;쀀𝕠ƀaelⷔ⷗ǒr;榷rp;榹΀;adiosvⷪⷫⷮ⸈⸍⸐⸖戨rò᪆Ȁ;efmⷷⷸ⸂⸅橝rĀ;oⷾⷿ愴f»ⷿ耻ª䂪耻º䂺gof;抶r;橖lope;橗;橛ƀclo⸟⸡⸧ò⸁ash耻ø䃸l;折iŬⸯ⸴de耻õ䃵esĀ;aǛ⸺s;樶ml耻ö䃶bar;挽ૡ⹞\0⹽\0⺀⺝\0⺢⺹\0\0⻋ຜ\0⼓\0\0⼫⾼\0⿈rȀ;astЃ⹧⹲຅脀¶;l⹭⹮䂶leìЃɩ⹸\0\0⹻m;櫳;櫽y;䐿rʀcimpt⺋⺏⺓ᡥ⺗nt;䀥od;䀮il;怰enk;怱r;쀀𝔭ƀimo⺨⺰⺴Ā;v⺭⺮䏆;䏕maô੶ne;明ƀ;tv⺿⻀⻈䏀chfork»´;䏖Āau⻏⻟nĀck⻕⻝kĀ;h⇴⻛;愎ö⇴sҀ;abcdemst⻳⻴ᤈ⻹⻽⼄⼆⼊⼎䀫cir;樣ir;樢Āouᵀ⼂;樥;橲n肻±ຝim;樦wo;樧ƀipu⼙⼠⼥ntint;樕f;쀀𝕡nd耻£䂣Ԁ;Eaceinosu່⼿⽁⽄⽇⾁⾉⾒⽾⾶;檳p;檷uå໙Ā;c໎⽌̀;acens່⽙⽟⽦⽨⽾pproø⽃urlyeñ໙ñ໎ƀaes⽯⽶⽺pprox;檹qq;檵im;拨iíໟmeĀ;s⾈ຮ怲ƀEas⽸⾐⽺ð⽵ƀdfp໬⾙⾯ƀals⾠⾥⾪lar;挮ine;挒urf;挓Ā;t໻⾴ï໻rel;抰Āci⿀⿅r;쀀𝓅;䏈ncsp;怈̀fiopsu⿚⋢⿟⿥⿫⿱r;쀀𝔮pf;쀀𝕢rime;恗cr;쀀𝓆ƀaeo⿸〉〓tĀei⿾々rnionóڰnt;樖stĀ;e【】䀿ñἙô༔઀ABHabcdefhilmnoprstux぀けさすムㄎㄫㅇㅢㅲㆎ㈆㈕㈤㈩㉘㉮㉲㊐㊰㊷ƀartぇおがròႳòϝail;検aròᱥar;楤΀cdenqrtとふへみわゔヌĀeuねぱ;쀀∽̱te;䅕iãᅮmptyv;榳gȀ;del࿑らるろ;榒;榥å࿑uo耻»䂻rր;abcfhlpstw࿜ガクシスゼゾダッデナp;極Ā;f࿠ゴs;椠;椳s;椞ë≝ð✮l;楅im;楴l;憣;憝Āaiパフil;椚oĀ;nホボ戶aló༞ƀabrョリヮrò៥rk;杳ĀakンヽcĀekヹ・;䁽;䁝Āes㄂㄄;榌lĀduㄊㄌ;榎;榐Ȁaeuyㄗㄜㄧㄩron;䅙Ādiㄡㄥil;䅗ì࿲âヺ;䑀Ȁclqsㄴㄷㄽㅄa;椷dhar;楩uoĀ;rȎȍh;憳ƀacgㅎㅟངlȀ;ipsླྀㅘㅛႜnåႻarôྩt;断ƀilrㅩဣㅮsht;楽;쀀𝔯ĀaoㅷㆆrĀduㅽㅿ»ѻĀ;l႑ㆄ;楬Ā;vㆋㆌ䏁;䏱ƀgns㆕ㇹㇼht̀ahlrstㆤㆰ㇂㇘㇤㇮rrowĀ;t࿜ㆭaéトarpoonĀduㆻㆿowîㅾp»႒eftĀah㇊㇐rrowó࿪arpoonóՑightarrows;應quigarro÷ニhreetimes;拌g;䋚ingdotseñἲƀahm㈍㈐㈓rò࿪aòՑ;怏oustĀ;a㈞㈟掱che»㈟mid;櫮Ȁabpt㈲㈽㉀㉒Ānr㈷㈺g;柭r;懾rëဃƀafl㉇㉊㉎r;榆;쀀𝕣us;樮imes;樵Āap㉝㉧rĀ;g㉣㉤䀩t;榔olint;樒arò㇣Ȁachq㉻㊀Ⴜ㊅quo;怺r;쀀𝓇Ābu・㊊oĀ;rȔȓƀhir㊗㊛㊠reåㇸmes;拊iȀ;efl㊪ၙᠡ㊫方tri;槎luhar;楨;愞ൡ㋕㋛㋟㌬㌸㍱\0㍺㎤\0\0㏬㏰\0㐨㑈㑚㒭㒱㓊㓱\0㘖\0\0㘳cute;䅛quï➺Ԁ;Eaceinpsyᇭ㋳㋵㋿㌂㌋㌏㌟㌦㌩;檴ǰ㋺\0㋼;檸on;䅡uåᇾĀ;dᇳ㌇il;䅟rc;䅝ƀEas㌖㌘㌛;檶p;檺im;择olint;樓iíሄ;䑁otƀ;be㌴ᵇ㌵担;橦΀Aacmstx㍆㍊㍗㍛㍞㍣㍭rr;懘rĀhr㍐㍒ë∨Ā;oਸ਼਴t耻§䂧i;䀻war;椩mĀin㍩ðnuóñt;朶rĀ;o㍶⁕쀀𝔰Ȁacoy㎂㎆㎑㎠rp;景Āhy㎋㎏cy;䑉;䑈rtɭ㎙\0\0㎜iäᑤaraì⹯耻­䂭Āgm㎨㎴maƀ;fv㎱㎲㎲䏃;䏂Ѐ;deglnprካ㏅㏉㏎㏖㏞㏡㏦ot;橪Ā;q኱ኰĀ;E㏓㏔檞;檠Ā;E㏛㏜檝;檟e;扆lus;樤arr;楲aròᄽȀaeit㏸㐈㐏㐗Āls㏽㐄lsetmé㍪hp;樳parsl;槤Ādlᑣ㐔e;挣Ā;e㐜㐝檪Ā;s㐢㐣檬;쀀⪬︀ƀflp㐮㐳㑂tcy;䑌Ā;b㐸㐹䀯Ā;a㐾㐿槄r;挿f;쀀𝕤aĀdr㑍ЂesĀ;u㑔㑕晠it»㑕ƀcsu㑠㑹㒟Āau㑥㑯pĀ;sᆈ㑫;쀀⊓︀pĀ;sᆴ㑵;쀀⊔︀uĀbp㑿㒏ƀ;esᆗᆜ㒆etĀ;eᆗ㒍ñᆝƀ;esᆨᆭ㒖etĀ;eᆨ㒝ñᆮƀ;afᅻ㒦ְrť㒫ֱ»ᅼaròᅈȀcemt㒹㒾㓂㓅r;쀀𝓈tmîñiì㐕aræᆾĀar㓎㓕rĀ;f㓔ឿ昆Āan㓚㓭ightĀep㓣㓪psiloîỠhé⺯s»⡒ʀbcmnp㓻㕞ሉ㖋㖎Ҁ;Edemnprs㔎㔏㔑㔕㔞㔣㔬㔱㔶抂;櫅ot;檽Ā;dᇚ㔚ot;櫃ult;櫁ĀEe㔨㔪;櫋;把lus;檿arr;楹ƀeiu㔽㕒㕕tƀ;en㔎㕅㕋qĀ;qᇚ㔏eqĀ;q㔫㔨m;櫇Ābp㕚㕜;櫕;櫓c̀;acensᇭ㕬㕲㕹㕻㌦pproø㋺urlyeñᇾñᇳƀaes㖂㖈㌛pproø㌚qñ㌗g;晪ڀ123;Edehlmnps㖩㖬㖯ሜ㖲㖴㗀㗉㗕㗚㗟㗨㗭耻¹䂹耻²䂲耻³䂳;櫆Āos㖹㖼t;檾ub;櫘Ā;dሢ㗅ot;櫄sĀou㗏㗒l;柉b;櫗arr;楻ult;櫂ĀEe㗤㗦;櫌;抋lus;櫀ƀeiu㗴㘉㘌tƀ;enሜ㗼㘂qĀ;qሢ㖲eqĀ;q㗧㗤m;櫈Ābp㘑㘓;櫔;櫖ƀAan㘜㘠㘭rr;懙rĀhr㘦㘨ë∮Ā;oਫ਩war;椪lig耻ß䃟௡㙑㙝㙠ዎ㙳㙹\0㙾㛂\0\0\0\0\0㛛㜃\0㜉㝬\0\0\0㞇ɲ㙖\0\0㙛get;挖;䏄rë๟ƀaey㙦㙫㙰ron;䅥dil;䅣;䑂lrec;挕r;쀀𝔱Ȁeiko㚆㚝㚵㚼Dz㚋\0㚑eĀ4fኄኁaƀ;sv㚘㚙㚛䎸ym;䏑Ācn㚢㚲kĀas㚨㚮pproø዁im»ኬsðኞĀas㚺㚮ð዁rn耻þ䃾Ǭ̟㛆⋧es膀×;bd㛏㛐㛘䃗Ā;aᤏ㛕r;樱;樰ƀeps㛡㛣㜀á⩍Ȁ;bcf҆㛬㛰㛴ot;挶ir;櫱Ā;o㛹㛼쀀𝕥rk;櫚á㍢rime;怴ƀaip㜏㜒㝤dåቈ΀adempst㜡㝍㝀㝑㝗㝜㝟ngleʀ;dlqr㜰㜱㜶㝀㝂斵own»ᶻeftĀ;e⠀㜾ñम;扜ightĀ;e㊪㝋ñၚot;旬inus;樺lus;樹b;槍ime;樻ezium;揢ƀcht㝲㝽㞁Āry㝷㝻;쀀𝓉;䑆cy;䑛rok;䅧Āio㞋㞎xô᝷headĀlr㞗㞠eftarro÷ࡏightarrow»ཝऀAHabcdfghlmoprstuw㟐㟓㟗㟤㟰㟼㠎㠜㠣㠴㡑㡝㡫㢩㣌㣒㣪㣶ròϭar;楣Ācr㟜㟢ute耻ú䃺òᅐrǣ㟪\0㟭y;䑞ve;䅭Āiy㟵㟺rc耻û䃻;䑃ƀabh㠃㠆㠋ròᎭlac;䅱aòᏃĀir㠓㠘sht;楾;쀀𝔲rave耻ù䃹š㠧㠱rĀlr㠬㠮»ॗ»ႃlk;斀Āct㠹㡍ɯ㠿\0\0㡊rnĀ;e㡅㡆挜r»㡆op;挏ri;旸Āal㡖㡚cr;䅫肻¨͉Āgp㡢㡦on;䅳f;쀀𝕦̀adhlsuᅋ㡸㡽፲㢑㢠ownáᎳarpoonĀlr㢈㢌efô㠭ighô㠯iƀ;hl㢙㢚㢜䏅»ᏺon»㢚parrows;懈ƀcit㢰㣄㣈ɯ㢶\0\0㣁rnĀ;e㢼㢽挝r»㢽op;挎ng;䅯ri;旹cr;쀀𝓊ƀdir㣙㣝㣢ot;拰lde;䅩iĀ;f㜰㣨»᠓Āam㣯㣲rò㢨l耻ü䃼angle;榧ހABDacdeflnoprsz㤜㤟㤩㤭㦵㦸㦽㧟㧤㧨㧳㧹㧽㨁㨠ròϷarĀ;v㤦㤧櫨;櫩asèϡĀnr㤲㤷grt;榜΀eknprst㓣㥆㥋㥒㥝㥤㦖appá␕othinçẖƀhir㓫⻈㥙opô⾵Ā;hᎷ㥢ïㆍĀiu㥩㥭gmá㎳Ābp㥲㦄setneqĀ;q㥽㦀쀀⊊︀;쀀⫋︀setneqĀ;q㦏㦒쀀⊋︀;쀀⫌︀Āhr㦛㦟etá㚜iangleĀlr㦪㦯eft»थight»ၑy;䐲ash»ံƀelr㧄㧒㧗ƀ;beⷪ㧋㧏ar;抻q;扚lip;拮Ābt㧜ᑨaòᑩr;쀀𝔳tré㦮suĀbp㧯㧱»ജ»൙pf;쀀𝕧roð໻tré㦴Ācu㨆㨋r;쀀𝓋Ābp㨐㨘nĀEe㦀㨖»㥾nĀEe㦒㨞»㦐igzag;榚΀cefoprs㨶㨻㩖㩛㩔㩡㩪irc;䅵Ādi㩀㩑Ābg㩅㩉ar;機eĀ;qᗺ㩏;扙erp;愘r;쀀𝔴pf;쀀𝕨Ā;eᑹ㩦atèᑹcr;쀀𝓌ૣណ㪇\0㪋\0㪐㪛\0\0㪝㪨㪫㪯\0\0㫃㫎\0㫘ៜ៟tré៑r;쀀𝔵ĀAa㪔㪗ròσrò৶;䎾ĀAa㪡㪤ròθrò৫að✓is;拻ƀdptឤ㪵㪾Āfl㪺ឩ;쀀𝕩imåឲĀAa㫇㫊ròώròਁĀcq㫒ីr;쀀𝓍Āpt៖㫜ré។Ѐacefiosu㫰㫽㬈㬌㬑㬕㬛㬡cĀuy㫶㫻te耻ý䃽;䑏Āiy㬂㬆rc;䅷;䑋n耻¥䂥r;쀀𝔶cy;䑗pf;쀀𝕪cr;쀀𝓎Ācm㬦㬩y;䑎l耻ÿ䃿Ԁacdefhiosw㭂㭈㭔㭘㭤㭩㭭㭴㭺㮀cute;䅺Āay㭍㭒ron;䅾;䐷ot;䅼Āet㭝㭡træᕟa;䎶r;쀀𝔷cy;䐶grarr;懝pf;쀀𝕫cr;쀀𝓏Ājn㮅㮇;怍j;怌'.split("").map((t=>t.charCodeAt(0)))),Te=new Uint16Array("Ȁaglq\tɭ\0\0p;䀦os;䀧t;䀾t;䀼uot;䀢".split("").map((t=>t.charCodeAt(0))));var ye;const Ee=new Map([[0,65533],[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]),Ae=null!==(ye=String.fromCodePoint)&&void 0!==ye?ye:function(t){let e="";return t>65535&&(t-=65536,e+=String.fromCharCode(t>>>10&1023|55296),t=56320|1023&t),e+=String.fromCharCode(t),e};var Me,Se,we,Ce,Ie,Fe,Re,Be;function Le(t){return t>=Me.ZERO&&t<=Me.NINE}!function(t){t[t.NUM=35]="NUM",t[t.SEMI=59]="SEMI",t[t.EQUALS=61]="EQUALS",t[t.ZERO=48]="ZERO",t[t.NINE=57]="NINE",t[t.LOWER_A=97]="LOWER_A",t[t.LOWER_F=102]="LOWER_F",t[t.LOWER_X=120]="LOWER_X",t[t.LOWER_Z=122]="LOWER_Z",t[t.UPPER_A=65]="UPPER_A",t[t.UPPER_F=70]="UPPER_F",t[t.UPPER_Z=90]="UPPER_Z"}(Me||(Me={})),function(t){t[t.VALUE_LENGTH=49152]="VALUE_LENGTH",t[t.BRANCH_LENGTH=16256]="BRANCH_LENGTH",t[t.JUMP_TABLE=127]="JUMP_TABLE"}(Se||(Se={})),function(t){t[t.EntityStart=0]="EntityStart",t[t.NumericStart=1]="NumericStart",t[t.NumericDecimal=2]="NumericDecimal",t[t.NumericHex=3]="NumericHex",t[t.NamedEntity=4]="NamedEntity"}(we||(we={})),(Ie=Ce||(Ce={}))[Ie.Legacy=0]="Legacy",Ie[Ie.Strict=1]="Strict",Ie[Ie.Attribute=2]="Attribute";class Pe{constructor(t,e,i){this.decodeTree=t,this.emitCodePoint=e,this.errors=i,this.state=we.EntityStart,this.consumed=1,this.result=0,this.treeIndex=0,this.excess=1,this.decodeMode=Ce.Strict}startEntity(t){this.decodeMode=t,this.state=we.EntityStart,this.result=0,this.treeIndex=0,this.excess=1,this.consumed=1}write(t,e){switch(this.state){case we.EntityStart:return t.charCodeAt(e)===Me.NUM?(this.state=we.NumericStart,this.consumed+=1,this.stateNumericStart(t,e+1)):(this.state=we.NamedEntity,this.stateNamedEntity(t,e));case we.NumericStart:return this.stateNumericStart(t,e);case we.NumericDecimal:return this.stateNumericDecimal(t,e);case we.NumericHex:return this.stateNumericHex(t,e);case we.NamedEntity:return this.stateNamedEntity(t,e)}}stateNumericStart(t,e){return e>=t.length?-1:(32|t.charCodeAt(e))===Me.LOWER_X?(this.state=we.NumericHex,this.consumed+=1,this.stateNumericHex(t,e+1)):(this.state=we.NumericDecimal,this.stateNumericDecimal(t,e))}addToNumericResult(t,e,i,s){if(e!==i){const r=i-e;this.result=this.result*Math.pow(s,r)+parseInt(t.substr(e,r),s),this.consumed+=r}}stateNumericHex(t,e){const i=e;for(;e=Me.UPPER_A&&s<=Me.UPPER_F||s>=Me.LOWER_A&&s<=Me.LOWER_F)))return this.addToNumericResult(t,i,e,16),this.emitNumericEntity(r,3);e+=1}var s;return this.addToNumericResult(t,i,e,16),-1}stateNumericDecimal(t,e){const i=e;for(;e=55296&&t<=57343||t>1114111?65533:null!==(e=Ee.get(t))&&void 0!==e?e:t}(this.result),this.consumed),this.errors&&(t!==Me.SEMI&&this.errors.missingSemicolonAfterCharacterReference(),this.errors.validateNumericCharacterReference(this.result)),this.consumed}stateNamedEntity(t,e){const{decodeTree:i}=this;let s=i[this.treeIndex],r=(s&Se.VALUE_LENGTH)>>14;for(;e=Me.UPPER_A&&t<=Me.UPPER_Z||t>=Me.LOWER_A&&t<=Me.LOWER_Z||Le(t)}(n)))?0:this.emitNotTerminatedNamedEntity();if(s=i[this.treeIndex],r=(s&Se.VALUE_LENGTH)>>14,0!==r){if(a===Me.SEMI)return this.emitNamedEntityData(this.treeIndex,r,this.consumed+this.excess);this.decodeMode!==Ce.Strict&&(this.result=this.treeIndex,this.consumed+=this.excess,this.excess=0)}}var n;return-1}emitNotTerminatedNamedEntity(){var t;const{result:e,decodeTree:i}=this,s=(i[e]&Se.VALUE_LENGTH)>>14;return this.emitNamedEntityData(e,s,this.consumed),null===(t=this.errors)||void 0===t||t.missingSemicolonAfterCharacterReference(),this.consumed}emitNamedEntityData(t,e,i){const{decodeTree:s}=this;return this.emitCodePoint(1===e?s[t]&~Se.VALUE_LENGTH:s[t+1],i),3===e&&this.emitCodePoint(s[t+2],i),i}end(){var t;switch(this.state){case we.NamedEntity:return 0===this.result||this.decodeMode===Ce.Attribute&&this.result!==this.treeIndex?0:this.emitNotTerminatedNamedEntity();case we.NumericDecimal:return this.emitNumericEntity(0,2);case we.NumericHex:return this.emitNumericEntity(0,3);case we.NumericStart:return null===(t=this.errors)||void 0===t||t.absenceOfDigitsInNumericCharacterReference(this.consumed),0;case we.EntityStart:return 0}}}function ke(t){let e="";const i=new Pe(t,(t=>e+=Ae(t)));return function(t,s){let r=0,n=0;for(;(n=t.indexOf("&",n))>=0;){e+=t.slice(r,n),i.startEntity(s);const a=i.write(t,n+1);if(a<0){r=n+i.end();break}r=n+a,n=0===a?r+1:r}const a=e+t.slice(r);return e="",a}}function Ne(t,e,i,s){const r=(e&Se.BRANCH_LENGTH)>>7,n=e&Se.JUMP_TABLE;if(0===r)return 0!==n&&s===n?i:-1;if(n){const e=s-n;return e<0||e>=r?-1:t[i+e]-1}let a=i,h=a+r-1;for(;a<=h;){const e=a+h>>>1,i=t[e];if(is))return t[e+r];h=e-1}}return-1}function Oe(t){return t===Fe.Space||t===Fe.NewLine||t===Fe.Tab||t===Fe.FormFeed||t===Fe.CarriageReturn}function De(t){return t===Fe.Slash||t===Fe.Gt||Oe(t)}ke(ve),ke(Te),function(t){t[t.Tab=9]="Tab",t[t.NewLine=10]="NewLine",t[t.FormFeed=12]="FormFeed",t[t.CarriageReturn=13]="CarriageReturn",t[t.Space=32]="Space",t[t.ExclamationMark=33]="ExclamationMark",t[t.Number=35]="Number",t[t.Amp=38]="Amp",t[t.SingleQuote=39]="SingleQuote",t[t.DoubleQuote=34]="DoubleQuote",t[t.Dash=45]="Dash",t[t.Slash=47]="Slash",t[t.Zero=48]="Zero",t[t.Nine=57]="Nine",t[t.Semi=59]="Semi",t[t.Lt=60]="Lt",t[t.Eq=61]="Eq",t[t.Gt=62]="Gt",t[t.Questionmark=63]="Questionmark",t[t.UpperA=65]="UpperA",t[t.LowerA=97]="LowerA",t[t.UpperF=70]="UpperF",t[t.LowerF=102]="LowerF",t[t.UpperZ=90]="UpperZ",t[t.LowerZ=122]="LowerZ",t[t.LowerX=120]="LowerX",t[t.OpeningSquareBracket=91]="OpeningSquareBracket"}(Fe||(Fe={})),function(t){t[t.Text=1]="Text",t[t.BeforeTagName=2]="BeforeTagName",t[t.InTagName=3]="InTagName",t[t.InSelfClosingTag=4]="InSelfClosingTag",t[t.BeforeClosingTagName=5]="BeforeClosingTagName",t[t.InClosingTagName=6]="InClosingTagName",t[t.AfterClosingTagName=7]="AfterClosingTagName",t[t.BeforeAttributeName=8]="BeforeAttributeName",t[t.InAttributeName=9]="InAttributeName",t[t.AfterAttributeName=10]="AfterAttributeName",t[t.BeforeAttributeValue=11]="BeforeAttributeValue",t[t.InAttributeValueDq=12]="InAttributeValueDq",t[t.InAttributeValueSq=13]="InAttributeValueSq",t[t.InAttributeValueNq=14]="InAttributeValueNq",t[t.BeforeDeclaration=15]="BeforeDeclaration",t[t.InDeclaration=16]="InDeclaration",t[t.InProcessingInstruction=17]="InProcessingInstruction",t[t.BeforeComment=18]="BeforeComment",t[t.CDATASequence=19]="CDATASequence",t[t.InSpecialComment=20]="InSpecialComment",t[t.InCommentLike=21]="InCommentLike",t[t.BeforeSpecialS=22]="BeforeSpecialS",t[t.BeforeSpecialT=23]="BeforeSpecialT",t[t.SpecialStartSequence=24]="SpecialStartSequence",t[t.InSpecialTag=25]="InSpecialTag",t[t.InEntity=26]="InEntity"}(Re||(Re={})),function(t){t[t.NoValue=0]="NoValue",t[t.Unquoted=1]="Unquoted",t[t.Single=2]="Single",t[t.Double=3]="Double"}(Be||(Be={}));const Ue={Cdata:new Uint8Array([67,68,65,84,65,91]),CdataEnd:new Uint8Array([93,93,62]),CommentEnd:new Uint8Array([45,45,62]),ScriptEnd:new Uint8Array([60,47,115,99,114,105,112,116]),StyleEnd:new Uint8Array([60,47,115,116,121,108,101]),TitleEnd:new Uint8Array([60,47,116,105,116,108,101]),TextareaEnd:new Uint8Array([60,47,116,101,120,116,97,114,101,97])};class Ve{constructor({xmlMode:t=!1,decodeEntities:e=!0},i){this.cbs=i,this.state=Re.Text,this.buffer="",this.sectionStart=0,this.index=0,this.entityStart=0,this.baseState=Re.Text,this.isSpecial=!1,this.running=!0,this.offset=0,this.currentSequence=void 0,this.sequenceIndex=0,this.xmlMode=t,this.decodeEntities=e,this.entityDecoder=new Pe(t?Te:ve,((t,e)=>this.emitCodePoint(t,e)))}reset(){this.state=Re.Text,this.buffer="",this.sectionStart=0,this.index=0,this.baseState=Re.Text,this.currentSequence=void 0,this.running=!0,this.offset=0}write(t){this.offset+=this.buffer.length,this.buffer=t,this.parse()}end(){this.running&&this.finish()}pause(){this.running=!1}resume(){this.running=!0,this.indexthis.sectionStart&&this.cbs.ontext(this.sectionStart,this.index),this.state=Re.BeforeTagName,this.sectionStart=this.index):this.decodeEntities&&t===Fe.Amp&&this.startEntity()}stateSpecialStartSequence(t){const e=this.sequenceIndex===this.currentSequence.length;if(e?De(t):(32|t)===this.currentSequence[this.sequenceIndex]){if(!e)return void this.sequenceIndex++}else this.isSpecial=!1;this.sequenceIndex=0,this.state=Re.InTagName,this.stateInTagName(t)}stateInSpecialTag(t){if(this.sequenceIndex===this.currentSequence.length){if(t===Fe.Gt||Oe(t)){const e=this.index-this.currentSequence.length;if(this.sectionStart=Fe.LowerA&&t<=Fe.LowerZ||t>=Fe.UpperA&&t<=Fe.UpperZ}(t)}startSpecial(t,e){this.isSpecial=!0,this.currentSequence=t,this.sequenceIndex=e,this.state=Re.SpecialStartSequence}stateBeforeTagName(t){if(t===Fe.ExclamationMark)this.state=Re.BeforeDeclaration,this.sectionStart=this.index+1;else if(t===Fe.Questionmark)this.state=Re.InProcessingInstruction,this.sectionStart=this.index+1;else if(this.isTagStartChar(t)){const e=32|t;this.sectionStart=this.index,this.xmlMode?this.state=Re.InTagName:e===Ue.ScriptEnd[2]?this.state=Re.BeforeSpecialS:e===Ue.TitleEnd[2]?this.state=Re.BeforeSpecialT:this.state=Re.InTagName}else t===Fe.Slash?this.state=Re.BeforeClosingTagName:(this.state=Re.Text,this.stateText(t))}stateInTagName(t){De(t)&&(this.cbs.onopentagname(this.sectionStart,this.index),this.sectionStart=-1,this.state=Re.BeforeAttributeName,this.stateBeforeAttributeName(t))}stateBeforeClosingTagName(t){Oe(t)||(t===Fe.Gt?this.state=Re.Text:(this.state=this.isTagStartChar(t)?Re.InClosingTagName:Re.InSpecialComment,this.sectionStart=this.index))}stateInClosingTagName(t){(t===Fe.Gt||Oe(t))&&(this.cbs.onclosetag(this.sectionStart,this.index),this.sectionStart=-1,this.state=Re.AfterClosingTagName,this.stateAfterClosingTagName(t))}stateAfterClosingTagName(t){(t===Fe.Gt||this.fastForwardTo(Fe.Gt))&&(this.state=Re.Text,this.sectionStart=this.index+1)}stateBeforeAttributeName(t){t===Fe.Gt?(this.cbs.onopentagend(this.index),this.isSpecial?(this.state=Re.InSpecialTag,this.sequenceIndex=0):this.state=Re.Text,this.sectionStart=this.index+1):t===Fe.Slash?this.state=Re.InSelfClosingTag:Oe(t)||(this.state=Re.InAttributeName,this.sectionStart=this.index)}stateInSelfClosingTag(t){t===Fe.Gt?(this.cbs.onselfclosingtag(this.index),this.state=Re.Text,this.sectionStart=this.index+1,this.isSpecial=!1):Oe(t)||(this.state=Re.BeforeAttributeName,this.stateBeforeAttributeName(t))}stateInAttributeName(t){(t===Fe.Eq||De(t))&&(this.cbs.onattribname(this.sectionStart,this.index),this.sectionStart=this.index,this.state=Re.AfterAttributeName,this.stateAfterAttributeName(t))}stateAfterAttributeName(t){t===Fe.Eq?this.state=Re.BeforeAttributeValue:t===Fe.Slash||t===Fe.Gt?(this.cbs.onattribend(Be.NoValue,this.sectionStart),this.sectionStart=-1,this.state=Re.BeforeAttributeName,this.stateBeforeAttributeName(t)):Oe(t)||(this.cbs.onattribend(Be.NoValue,this.sectionStart),this.state=Re.InAttributeName,this.sectionStart=this.index)}stateBeforeAttributeValue(t){t===Fe.DoubleQuote?(this.state=Re.InAttributeValueDq,this.sectionStart=this.index+1):t===Fe.SingleQuote?(this.state=Re.InAttributeValueSq,this.sectionStart=this.index+1):Oe(t)||(this.sectionStart=this.index,this.state=Re.InAttributeValueNq,this.stateInAttributeValueNoQuotes(t))}handleInAttributeValue(t,e){t===e||!this.decodeEntities&&this.fastForwardTo(e)?(this.cbs.onattribdata(this.sectionStart,this.index),this.sectionStart=-1,this.cbs.onattribend(e===Fe.DoubleQuote?Be.Double:Be.Single,this.index+1),this.state=Re.BeforeAttributeName):this.decodeEntities&&t===Fe.Amp&&this.startEntity()}stateInAttributeValueDoubleQuotes(t){this.handleInAttributeValue(t,Fe.DoubleQuote)}stateInAttributeValueSingleQuotes(t){this.handleInAttributeValue(t,Fe.SingleQuote)}stateInAttributeValueNoQuotes(t){Oe(t)||t===Fe.Gt?(this.cbs.onattribdata(this.sectionStart,this.index),this.sectionStart=-1,this.cbs.onattribend(Be.Unquoted,this.index),this.state=Re.BeforeAttributeName,this.stateBeforeAttributeName(t)):this.decodeEntities&&t===Fe.Amp&&this.startEntity()}stateBeforeDeclaration(t){t===Fe.OpeningSquareBracket?(this.state=Re.CDATASequence,this.sequenceIndex=0):this.state=t===Fe.Dash?Re.BeforeComment:Re.InDeclaration}stateInDeclaration(t){(t===Fe.Gt||this.fastForwardTo(Fe.Gt))&&(this.cbs.ondeclaration(this.sectionStart,this.index),this.state=Re.Text,this.sectionStart=this.index+1)}stateInProcessingInstruction(t){(t===Fe.Gt||this.fastForwardTo(Fe.Gt))&&(this.cbs.onprocessinginstruction(this.sectionStart,this.index),this.state=Re.Text,this.sectionStart=this.index+1)}stateBeforeComment(t){t===Fe.Dash?(this.state=Re.InCommentLike,this.currentSequence=Ue.CommentEnd,this.sequenceIndex=2,this.sectionStart=this.index+1):this.state=Re.InDeclaration}stateInSpecialComment(t){(t===Fe.Gt||this.fastForwardTo(Fe.Gt))&&(this.cbs.oncomment(this.sectionStart,this.index,0),this.state=Re.Text,this.sectionStart=this.index+1)}stateBeforeSpecialS(t){const e=32|t;e===Ue.ScriptEnd[3]?this.startSpecial(Ue.ScriptEnd,4):e===Ue.StyleEnd[3]?this.startSpecial(Ue.StyleEnd,4):(this.state=Re.InTagName,this.stateInTagName(t))}stateBeforeSpecialT(t){const e=32|t;e===Ue.TitleEnd[3]?this.startSpecial(Ue.TitleEnd,4):e===Ue.TextareaEnd[3]?this.startSpecial(Ue.TextareaEnd,4):(this.state=Re.InTagName,this.stateInTagName(t))}startEntity(){this.baseState=this.state,this.state=Re.InEntity,this.entityStart=this.index,this.entityDecoder.startEntity(this.xmlMode?Ce.Strict:this.baseState===Re.Text||this.baseState===Re.InSpecialTag?Ce.Legacy:Ce.Attribute)}stateInEntity(){const t=this.entityDecoder.write(this.buffer,this.index-this.offset);t>=0?(this.state=this.baseState,0===t&&(this.index=this.entityStart)):this.index=this.offset+this.buffer.length-1}cleanup(){this.running&&this.sectionStart!==this.index&&(this.state===Re.Text||this.state===Re.InSpecialTag&&0===this.sequenceIndex?(this.cbs.ontext(this.sectionStart,this.index),this.sectionStart=this.index):this.state!==Re.InAttributeValueDq&&this.state!==Re.InAttributeValueSq&&this.state!==Re.InAttributeValueNq||(this.cbs.onattribdata(this.sectionStart,this.index),this.sectionStart=this.index))}shouldContinue(){return this.index=t||(this.state===Re.InCommentLike?this.currentSequence===Ue.CdataEnd?this.cbs.oncdata(this.sectionStart,t,0):this.cbs.oncomment(this.sectionStart,t,0):this.state===Re.InTagName||this.state===Re.BeforeAttributeName||this.state===Re.BeforeAttributeValue||this.state===Re.AfterAttributeName||this.state===Re.InAttributeName||this.state===Re.InAttributeValueSq||this.state===Re.InAttributeValueDq||this.state===Re.InAttributeValueNq||this.state===Re.InClosingTagName||this.cbs.ontext(this.sectionStart,t))}emitCodePoint(t,e){this.baseState!==Re.Text&&this.baseState!==Re.InSpecialTag?(this.sectionStart0&&n.has(this.stack[0]);){const t=this.stack.shift();null===(i=(e=this.cbs).onclosetag)||void 0===i||i.call(e,t,!0)}this.isVoidElement(t)||(this.stack.unshift(t),this.htmlMode&&(We.has(t)?this.foreignContext.unshift(!0):Ke.has(t)&&this.foreignContext.unshift(!1))),null===(r=(s=this.cbs).onopentagname)||void 0===r||r.call(s,t),this.cbs.onopentag&&(this.attribs={})}endOpenTag(t){var e,i;this.startIndex=this.openTagStart,this.attribs&&(null===(i=(e=this.cbs).onopentag)||void 0===i||i.call(e,this.tagname,this.attribs,t),this.attribs=null),this.cbs.onclosetag&&this.isVoidElement(this.tagname)&&this.cbs.onclosetag(this.tagname,!0),this.tagname=""}onopentagend(t){this.endIndex=t,this.endOpenTag(!1),this.startIndex=t+1}onclosetag(t,e){var i,s,r,n,a,h,o,l;this.endIndex=e;let c=this.getSlice(t,e);if(this.lowerCaseTagNames&&(c=c.toLowerCase()),this.htmlMode&&(We.has(c)||Ke.has(c))&&this.foreignContext.shift(),this.isVoidElement(c))this.htmlMode&&"br"===c&&(null===(n=(r=this.cbs).onopentagname)||void 0===n||n.call(r,"br"),null===(h=(a=this.cbs).onopentag)||void 0===h||h.call(a,"br",{},!0),null===(l=(o=this.cbs).onclosetag)||void 0===l||l.call(o,"br",!1));else{const t=this.stack.indexOf(c);if(-1!==t)for(let e=0;e<=t;e++){const r=this.stack.shift();null===(s=(i=this.cbs).onclosetag)||void 0===s||s.call(i,r,e!==t)}else this.htmlMode&&"p"===c&&(this.emitOpenTag("p"),this.closeCurrentTag(!0))}this.startIndex=e+1}onselfclosingtag(t){this.endIndex=t,this.recognizeSelfClosing||this.foreignContext[0]?(this.closeCurrentTag(!1),this.startIndex=t+1):this.onopentagend(t)}closeCurrentTag(t){var e,i;const s=this.tagname;this.endOpenTag(t),this.stack[0]===s&&(null===(i=(e=this.cbs).onclosetag)||void 0===i||i.call(e,s,!t),this.stack.shift())}onattribname(t,e){this.startIndex=t;const i=this.getSlice(t,e);this.attribname=this.lowerCaseAttributeNames?i.toLowerCase():i}onattribdata(t,e){this.attribvalue+=this.getSlice(t,e)}onattribentity(t){this.attribvalue+=Ae(t)}onattribend(t,e){var i,s;this.endIndex=e,null===(s=(i=this.cbs).onattribute)||void 0===s||s.call(i,this.attribname,this.attribvalue,t===Be.Double?'"':t===Be.Single?"'":t===Be.NoValue?void 0:null),this.attribs&&!Object.prototype.hasOwnProperty.call(this.attribs,this.attribname)&&(this.attribs[this.attribname]=this.attribvalue),this.attribvalue=""}getInstructionName(t){const e=t.search(Qe);let i=e<0?t:t.substr(0,e);return this.lowerCaseTagNames&&(i=i.toLowerCase()),i}ondeclaration(t,e){this.endIndex=e;const i=this.getSlice(t,e);if(this.cbs.onprocessinginstruction){const t=this.getInstructionName(i);this.cbs.onprocessinginstruction(`!${t}`,`!${i}`)}this.startIndex=e+1}onprocessinginstruction(t,e){this.endIndex=e;const i=this.getSlice(t,e);if(this.cbs.onprocessinginstruction){const t=this.getInstructionName(i);this.cbs.onprocessinginstruction(`?${t}`,`?${i}`)}this.startIndex=e+1}oncomment(t,e,i){var s,r,n,a;this.endIndex=e,null===(r=(s=this.cbs).oncomment)||void 0===r||r.call(s,this.getSlice(t,e-i)),null===(a=(n=this.cbs).oncommentend)||void 0===a||a.call(n),this.startIndex=e+1}oncdata(t,e,i){var s,r,n,a,h,o,l,c,_,$;this.endIndex=e;const u=this.getSlice(t,e-i);!this.htmlMode||this.options.recognizeCDATA?(null===(r=(s=this.cbs).oncdatastart)||void 0===r||r.call(s),null===(a=(n=this.cbs).ontext)||void 0===a||a.call(n,u),null===(o=(h=this.cbs).oncdataend)||void 0===o||o.call(h)):(null===(c=(l=this.cbs).oncomment)||void 0===c||c.call(l,`[CDATA[${u}]]`),null===($=(_=this.cbs).oncommentend)||void 0===$||$.call(_)),this.startIndex=e+1}onend(){var t,e;if(this.cbs.onclosetag){this.endIndex=this.startIndex;for(let t=0;t=this.buffers[0].length;)this.shiftBuffer();let i=this.buffers[0].slice(t-this.bufferOffset,e-this.bufferOffset);for(;e-this.bufferOffset>this.buffers[0].length;)this.shiftBuffer(),i+=this.buffers[0].slice(0,e-this.bufferOffset);return i}shiftBuffer(){this.bufferOffset+=this.buffers[0].length,this.writeIndex--,this.buffers.shift()}write(t){var e,i;this.ended?null===(i=(e=this.cbs).onerror)||void 0===i||i.call(e,new Error(".write() after done!")):(this.buffers.push(t),this.tokenizer.running&&(this.tokenizer.write(t),this.writeIndex++))}end(t){var e,i;this.ended?null===(i=(e=this.cbs).onerror)||void 0===i||i.call(e,new Error(".end() after done!")):(t&&this.write(t),this.ended=!0,this.tokenizer.end())}pause(){this.tokenizer.pause()}resume(){for(this.tokenizer.resume();this.tokenizer.running&&this.writeIndex0?this.children[this.children.length-1]:null}get childNodes(){return this.children}set childNodes(t){this.children=t}}class hi extends ai{constructor(){super(...arguments),this.type=Ze.CDATA}get nodeType(){return 4}}class oi extends ai{constructor(){super(...arguments),this.type=Ze.Root}get nodeType(){return 9}}class li extends ai{constructor(t,e,i=[],s=("script"===t?Ze.Script:"style"===t?Ze.Style:Ze.Tag)){super(i),this.name=t,this.attribs=e,this.type=s}get nodeType(){return 1}get tagName(){return this.name}set tagName(t){this.name=t}get attributes(){return Object.keys(this.attribs).map((t=>{var e,i;return{name:t,value:this.attribs[t],namespace:null===(e=this["x-attribsNamespace"])||void 0===e?void 0:e[t],prefix:null===(i=this["x-attribsPrefix"])||void 0===i?void 0:i[t]}}))}}function ci(t,e=!1){let i;if(function(t){return t.type===Ze.Text}(t))i=new si(t.data);else if(function(t){return t.type===Ze.Comment}(t))i=new ri(t.data);else if(function(t){return(e=t).type===Ze.Tag||e.type===Ze.Script||e.type===Ze.Style;var e}(t)){const s=e?_i(t.children):[],r=new li(t.name,{...t.attribs},s);s.forEach((t=>t.parent=r)),null!=t.namespace&&(r.namespace=t.namespace),t["x-attribsNamespace"]&&(r["x-attribsNamespace"]={...t["x-attribsNamespace"]}),t["x-attribsPrefix"]&&(r["x-attribsPrefix"]={...t["x-attribsPrefix"]}),i=r}else if(function(t){return t.type===Ze.CDATA}(t)){const s=e?_i(t.children):[],r=new hi(s);s.forEach((t=>t.parent=r)),i=r}else if(function(t){return t.type===Ze.Root}(t)){const s=e?_i(t.children):[],r=new oi(s);s.forEach((t=>t.parent=r)),t["x-mode"]&&(r["x-mode"]=t["x-mode"]),i=r}else{if(!function(t){return t.type===Ze.Directive}(t))throw new Error(`Not implemented yet: ${t.type}`);{const e=new ni(t.name,t.data);null!=t["x-name"]&&(e["x-name"]=t["x-name"],e["x-publicId"]=t["x-publicId"],e["x-systemId"]=t["x-systemId"]),i=e}}return i.startIndex=t.startIndex,i.endIndex=t.endIndex,null!=t.sourceCodeLocation&&(i.sourceCodeLocation=t.sourceCodeLocation),i}function _i(t){const e=t.map((t=>ci(t,!0)));for(let t=1;t'"]/g,gi),fi(/["&\u00A0]/g,new Map([[34,"""],[38,"&"],[160," "]])),fi(/[&<>\u00A0]/g,new Map([[38,"&"],[60,"<"],[62,">"],[160," "]])),function(t){t[t.XML=0]="XML",t[t.HTML=1]="HTML"}(pi||(pi={})),function(t){t[t.UTF8=0]="UTF8",t[t.ASCII=1]="ASCII",t[t.Extensive=2]="Extensive",t[t.Attribute=3]="Attribute",t[t.Text=4]="Text"}(mi||(mi={})),new Map(["altGlyph","altGlyphDef","altGlyphItem","animateColor","animateMotion","animateTransform","clipPath","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","foreignObject","glyphRef","linearGradient","radialGradient","textPath"].map((t=>[t.toLowerCase(),t]))),new Map(["definitionURL","attributeName","attributeType","baseFrequency","baseProfile","calcMode","clipPathUnits","diffuseConstant","edgeMode","filterUnits","glyphRef","gradientTransform","gradientUnits","kernelMatrix","kernelUnitLength","keyPoints","keySplines","keyTimes","lengthAdjust","limitingConeAngle","markerHeight","markerUnits","markerWidth","maskContentUnits","maskUnits","numOctaves","pathLength","patternContentUnits","patternTransform","patternUnits","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","refX","refY","repeatCount","repeatDur","requiredExtensions","requiredFeatures","specularConstant","specularExponent","spreadMethod","startOffset","stdDeviation","stitchTiles","surfaceScale","systemLanguage","tableValues","targetX","targetY","textLength","viewBox","viewTarget","xChannelSelector","yChannelSelector","zoomAndPan"].map((t=>[t.toLowerCase(),t]))),new Set(["style","script","xmp","iframe","noembed","noframes","plaintext","noscript"]),new Set(["area","base","basefont","br","col","command","embed","frame","hr","img","input","isindex","keygen","link","meta","param","source","track","wbr"]),new Set(["mi","mo","mn","ms","mtext","annotation-xml","foreignObject","desc","title"]),new Set(["svg","math"]),function(t){t[t.DISCONNECTED=1]="DISCONNECTED",t[t.PRECEDING=2]="PRECEDING",t[t.FOLLOWING=4]="FOLLOWING",t[t.CONTAINS=8]="CONTAINS",t[t.CONTAINED_BY=16]="CONTAINED_BY"}(xi||(xi={}));class bi{constructor(){this._$textWidth=-1,this._$textHeight=-1,this._$widthTable=[],this._$heightTable=[],this._$ascentTable=[],this._$textTable=[],this._$lineTable=[]}get textWidth(){if(-1===this._$textWidth){this._$textWidth=0;for(let t=0;t{let r=i.lineTable.length-1;const n=s.width-e._$widthMargin()-4;for(let a=0;an){Ti=_,r++,l.line=r;const t={mode:"wrap",text:"",x:0,y:0,w:0,h:0,line:r,textFormat:h._$clone()};let e=1,s=!0;const n=/[0-9a-zA-Z?!;:.,?!。、;:〜]/g;for(;;){const t=i.textTable.length-e;if(0>=t){s=!1,e=0;break}const r=i.textTable[t];if(!r){s=!1,e=0;break}if("text"!==r.mode){s=!1;break}if(" "===r.text){e--;break}if(!r.text.match(n)){e--;break}e++}if(i.widthTable[r]=0,i.heightTable[r]=0,i.ascentTable[r]=0,e>0&&s){const s=i.textTable.length-e;i.textTable.splice(s,0,t),i.lineTable.push(t);const n=r-1;i.widthTable[n]=0,i.heightTable[n]=0,i.ascentTable[n]=0;for(let t=0;t{const s=t.trim().split(";"),r=[];for(let t=0;t{for(let s=0;se.size&&(e.size=1));break;case"color":e.color=xt(r.value);break;case"letterSpacing":e.letterSpacing=+r.value;break;case"leading":e.leading=+r.value;break;case"leftMargin":e.leftMargin=+r.value;break;case"rightMargin":e.rightMargin=+r.value;break;case"underline":e.underline=!0;break;case"bold":e.bold=!0;break;case"italic":e.italic=!0}}},Mi=(t,e)=>{Ti=0;const i=t.lineTable.length;vi.font=e._$generateFontStyle();const s=vi.measureText(""),r={mode:"break",text:"",x:0,y:0,w:0,h:s.fontBoundingBoxAscent+s.fontBoundingBoxDescent,line:i,textFormat:e._$clone()};t.heightTable[i]=0,t.ascentTable[i]=0,t.widthTable[i]=0,t.lineTable.push(r),t.textTable.push(r)},Si=(t,e,i,s)=>{for(let r=0;r{const e=t.heightTable.length-1;for(let i=1;i0)continue;const e=t.lineTable[i];t.heightTable[i]=e.h=t.heightTable[i-1]}};class Ci extends te{constructor(){super(),this._$background=!1,this._$backgroundColor=16777215,this._$border=!1,this._$borderColor=0,this._$htmlText="",this._$multiline=!1,this._$text="",this._$wordWrap=!1,this._$scrollX=0,this._$scrollY=0,this._$maxChars=0,this._$stopIndex=-1,this._$compositionStartIndex=-1,this._$compositionEndIndex=-1;const t=new be;t._$setDefault(),this._$defaultTextFormat=t,this._$rawHtmlText="",this._$bounds={xMin:0,xMax:100,yMin:0,yMax:100},this._$originBounds={xMin:0,xMax:100,yMin:0,yMax:100},this._$restrict="",this._$isHTML=!1,this._$textData=null,this._$autoSize="none",this._$autoFontSize=!1,this._$focusVisible=!1,this._$timerId=-1,this._$focusIndex=-1,this._$selectIndex=-1,this._$scrollEnabled=!0,this._$xScrollShape=null,this._$yScrollShape=null,this._$type="static",this._$focus=!1,this._$copyText="",this._$thickness=0,this._$thicknessColor=0,this._$textFormats=null,this._$cacheKeys=ht(),this._$cacheParams=ht(0,0,0)}static toString(){return"[class TextField]"}static get namespace(){return"next2d.display.TextField"}toString(){return"[object TextField]"}get namespace(){return"next2d.display.TextField"}get autoFontSize(){return this._$autoFontSize}set autoFontSize(t){t!==this._$autoFontSize&&(this._$autoFontSize=t,this._$reload())}get autoSize(){return this._$autoSize}set autoSize(t){t!==this._$autoSize&&(this._$autoSize=t,this._$reload())}get background(){return this._$background}set background(t){t!==this._$background&&(this._$background=!!t,this._$reset())}get backgroundColor(){return this._$backgroundColor}set backgroundColor(t){(t=dt(xt(t),0,16777215,16777215))!==this._$backgroundColor&&(this._$backgroundColor=t,this._$reset())}get border(){return this._$border}set border(t){t!==this._$border&&(this._$border=!!t,this._$reset())}get borderColor(){return this._$borderColor}set borderColor(t){(t=dt(xt(t),0,16777215,0))!==this._$borderColor&&(this._$borderColor=t,this._$reset())}get stopIndex(){return this._$stopIndex}set stopIndex(t){if(t|=0,this._$stopIndex===t)return;this._$stopIndex=t;const e=this.getTextData();if(!e.textTable.length)return;let i=2,s=0;for(let r=0;r=t){s=r;break}"break"===n.mode&&(a=!0,this._$scrollX=0,i=2),a&&s++}const r=e.textTable[s].line;let n=0;for(let t=0;t<=r;++t)n+=e.heightTable[t];const a=this.height;let h=0;for(let t=r;t>-1;--t){const i=e.heightTable[t];if(aa){const t=(this.textHeight-a)/a;this._$scrollY=v.min((n-h)/t,a)}const o=this.width;let l=0;for(let t=s;t>0;--t){const i=e.textTable[t];if("text"===i.mode){if(oo){const t=(this.textWidth-o)/o;this._$scrollX=v.min((i-l)/t,o+.5)}this._$doChanged()}get defaultTextFormat(){return this._$defaultTextFormat._$clone()}set defaultTextFormat(t){t._$merge(this._$defaultTextFormat),this._$defaultTextFormat=t,this._$reset()}get focus(){return this._$focus}set focus(t){if(this._$focus===t)return;if("input"!==this._$type)return;this._$focus=!!t;const e=this._$focus?Rt.FOCUS_IN:Rt.FOCUS_OUT;this.willTrigger(e)&&this.dispatchEvent(new Rt(e)),tr.value="",this._$focus?tr.focus():(this._$focusIndex=-1,this._$selectIndex=-1,this._$focusVisible=!1,L(this._$timerId),tr.blur()),this._$doChanged(),x()}get htmlText(){if(this._$htmlText)return this._$htmlText;const t=this.getTextData();let e=t.textTable[0].textFormat,i="";continue}const n=r.textFormat;if(!e._$isSame(n)){i+="e){this._$doChanged(),this._$scrollX=t,this._$xScrollShape.width=e*e/this.textWidth;const i=this._$parent;if(i){this._$xScrollShape.hasLocalVariable("job")&&this._$xScrollShape.getLocalVariable("job").stop(),this._$xScrollShape.alpha=.9,this._$xScrollShape.x=this.x+1+(e-1-this._$xScrollShape.width)/(e-1)*(this._$scrollX-1),this._$xScrollShape.y=this.y+this.height-this._$xScrollShape.height-.5,i.addChildAt(this._$xScrollShape,i.getChildIndex(this)+1);const t=xe.add(this._$xScrollShape,{alpha:.9},{alpha:0},.5,.2,pe.outQuad);t.addEventListener(It.COMPLETE,(t=>{const e=t.target.target;e.deleteLocalVariable("job"),e.parent&&e.parent.removeChild(e)})),t.start(),this._$xScrollShape.setLocalVariable("job",t)}}this.willTrigger(It.SCROLL)&&this.dispatchEvent(new It(It.SCROLL,!0))}}get scrollY(){return this._$scrollY}set scrollY(t){if(this._$scrollEnabled&&"none"===this._$autoSize&&(this._$multiline||this._$wordWrap)&&!(this._$xScrollShape&&this._$xScrollShape.hasLocalVariable("job")||(t=dt(t,0,this.height,0),this._$scrollY===t))){const e=this.height;if(this._$yScrollShape&&this.textHeight>e){this._$doChanged(),this._$scrollY=t,this._$yScrollShape.height=e*e/this.textHeight;const i=this._$parent;if(i){this._$yScrollShape.hasLocalVariable("job")&&this._$yScrollShape.getLocalVariable("job").stop(),this._$yScrollShape.alpha=.9,this._$yScrollShape.x=this.x+this.width-this._$yScrollShape.width-.5,this._$yScrollShape.y=this.y+.5+(e-1-this._$yScrollShape.height)/(e-1)*(this._$scrollY-1),i.addChildAt(this._$yScrollShape,i.getChildIndex(this)+1);const t=xe.add(this._$yScrollShape,{alpha:.9},{alpha:0},.5,.2,pe.outQuad);t.addEventListener(It.COMPLETE,(t=>{const e=t.target.target;e.deleteLocalVariable("job"),e.parent&&e.parent.removeChild(e)})),t.start(),this._$yScrollShape.setLocalVariable("job",t)}}this.willTrigger(It.SCROLL)&&this.dispatchEvent(new It(It.SCROLL,!0))}}get text(){if(!this._$isHTML)return this._$text;if(this._$rawHtmlText)return this._$rawHtmlText;let t="";const e=this.getTextData();for(let i=1;i-1){const e=this._$getBounds(null),i=v.abs(e.xMin);this._$originBounds.xMax=t+i,this._$originBounds.xMin=i,this._$bounds.xMax=this._$originBounds.xMax,this._$bounds.xMin=this._$originBounds.xMin,super.width=t,this._$reload()}}get height(){return super.height}set height(t){if(!C(t=+t)&&t>-1){const e=this._$getBounds(null),i=v.abs(e.yMin);this._$originBounds.yMax=t+i,this._$bounds.yMax=this._$originBounds.yMax,this._$bounds.yMin=this._$originBounds.yMin,super.height=t,this._$reload()}}get x(){const t=this._$transform.matrix,e=this._$getBounds(null);return t._$matrix[4]+e.xMin}set x(t){const e=this._$getBounds(null);super.x=t-e.xMin}get y(){const t=this._$transform.matrix,e=this._$getBounds(null);return t._$matrix[5]+e.yMin}set y(t){const e=this._$getBounds(null);super.y=t-e.yMin}appendText(t){const e=this.text;this.text=e+`${t}`}getLineText(t){if(!this._$text&&!this._$htmlText)return"";t|=0;let e="";const i=this.getTextData();for(let s=0;st)break;r.line===t&&"text"===r.mode&&(e+=r.text)}return e}replaceText(t,e,i){if(e|=0,(t|=0)>-1&&e>-1&&e>=t){const s=this.text;t>=s.length?e>=s.length&&e>=t&&(this.text=s+`${i}`):this.text=s.slice(0,t)+`${i}`+s.slice(e,s.length)}}getTextData(t=0){return null!==this._$textData||(this._$isHTML?this._$textData=((t,e,i)=>{const s=new bi;if(!t)return s;const r=t.trim().replace(/\r?\n/g,"").replace(/\t/g,""),n=e._$clone();i.subFontSize&&i.subFontSize>0&&n.size&&(n.size-=i.subFontSize,1>n.size&&(n.size=1));const a=function(t,e){const i=new ui(void 0,e);return new Je(i,e).end(t),i.root}(r);return Mi(s,n),Si(a,n,s,i),wi(s),s})(this._$htmlText,this._$defaultTextFormat,{width:this.width,multiline:this._$multiline,wordWrap:this._$wordWrap,subFontSize:t,textFormats:this._$textFormats}):this._$textData=((t,e,i)=>{const s=new bi;if(!t)return s;const r=i.multiline?t.split("\n"):[t.replace("\n","")];for(let t=0;t0&&n.size&&(n.size-=i.subFontSize,1>n.size&&(n.size=1)),(0===t||i.wordWrap||i.multiline)&&Mi(s,n);const a=r[t];a&&(Ti=0,yi(a,n,s,i))}return wi(s),s})(this._$text,this._$defaultTextFormat,{width:this.width,multiline:this._$multiline,wordWrap:this._$wordWrap,subFontSize:t,textFormats:this._$textFormats})),this._$textData}selectAll(){const t=this.getTextData();t.textTable.length&&(this._$selectIndex=1,this._$focusIndex=t.textTable.length)}copy(){if(-1===this._$focusIndex||-1===this._$selectIndex)return;let t="";const e=v.min(this._$focusIndex,this._$selectIndex),i=v.max(this._$focusIndex,this._$selectIndex)+1,s=this.getTextData();for(let r=e;rs)break;i.line===s&&"text"===i.mode&&(r+=i.w)}let n=2;const a=s-1;for(let e=1;ea)return this._$focusIndex="text"===i.mode?e-1:e,this._$selectIndex=-1,L(this._$timerId),void this._$blinking();if(i.line===a&&"text"===i.mode&&(n+=i.w,n>r))return this._$focusIndex=e,this._$selectIndex=-1,L(this._$timerId),void this._$blinking()}}arrowDown(){if(-1===this._$focusIndex)return;const t=this.getTextData();if(!t.textTable.length)return;const e=t.textTable[this._$focusIndex],i="text"===e.mode?e.line:e.line-1;if(i===t.lineTable.length-1)return;let s=2;for(let e=1;ei)break;r.line===i&&"text"===r.mode&&(s+=r.w)}let r=2;const n=i+1;for(let e=1;en)return this._$focusIndex="text"===i.mode?e-1:e,this._$selectIndex=-1,L(this._$timerId),void this._$blinking();if(i.line===n&&"text"===i.mode&&(r+=i.w,r>s))return this._$focusIndex=e,this._$selectIndex=-1,L(this._$timerId),void this._$blinking()}this._$focusIndex=t.textTable.length,this._$selectIndex=-1,L(this._$timerId),this._$blinking()}arrowLeft(){this._$focusIndex&&(this.getTextData().textTable.length&&this._$focusIndex<2?this._$focusIndex=1:(this._$focusIndex--,this._$selectIndex=-1,L(this._$timerId),this._$blinking()))}arrowRight(){this.getTextData().textTable.length!==this._$focusIndex&&(this._$focusIndex++,this._$selectIndex=-1,L(this._$timerId),this._$blinking())}deleteText(){if(this._$compositionStartIndex>-1)return;let t=0,e=0;if(this._$selectIndex>-1)t=v.min(this._$focusIndex,this._$selectIndex),e=v.max(this._$focusIndex,this._$selectIndex)+1,this._$focusIndex=t;else{if(2>this._$focusIndex)return;this._$focusIndex--}const i=this.getTextData(),s=i.textTable[this._$focusIndex];s&&"wrap"===s.mode&&this._$focusIndex--;const r=ht();let n="";for(let s=1;ss))switch(a.mode){case"break":r.push(a.textFormat),n+="\n";break;case"text":r.push(a.textFormat),n+=a.text;break;default:continue}}if(i.textTable.length===this._$focusIndex&&(r.pop(),n=n.slice(0,-1)),this._$selectIndex=-1,n){const t=this.textWidth,e=this.textHeight;if(this._$textFormats=r,this.text=n,this._$scrollX>0){const e=this.textWidth,i=this.width;switch(!0){case i>e:this._$scrollY=0;break;case t!==e:this._$scrollY-=(t-e)/(e/i)}}if(this._$scrollY>0){const t=this.textHeight,i=this.height;switch(!0){case i>t:this._$scrollY=0;break;case e!==t:this._$scrollY-=(e-t)/(t/i)}}this._$textFormats=null,ot(r)}else this.text=null,this._$scrollX=0,this._$scrollY=0,this._$focusIndex=0}compositionStart(){this._$compositionStartIndex=this._$focusIndex}compositionUpdate(t){if(this._$compositionEndIndex>-1){const t=this._$compositionStartIndex;this._$focusIndex=this._$compositionStartIndex,this._$selectIndex=this._$compositionEndIndex-1,this._$compositionStartIndex=-1,this.deleteText(),this._$compositionStartIndex=t,this._$selectIndex=-1}let e=this.getTextData();const i=ht(),s=t.length;let r="";if(e.textTable.length){for(let n=1;nthis._$compositionStartIndex&&n++}}this._$compositionEndIndex=this._$focusIndex=n;const a=cr(),h=v.min(e.textTable.length-1,this._$compositionEndIndex),o=e.textTable[h];if(o){const t=o.line;let i=0;for(let s=0;s-1){const t=this.getTextData();for(let e=this._$compositionStartIndex;e-1)return;this._$selectIndex>-1&&this.deleteText();const e=this.getTextData(),i=ht();let s="";for(let r=1;ri&&this.textWidth+4>this.width;)this._$reset(),this.getTextData(i++);if(this.height&&this.textHeight)for(;t>i&&this.textHeight+4>this.height;)this._$reset(),this.getTextData(i++)}this._$resize()}_$blinking(){this._$focusVisible=!this._$focusVisible,this._$doChanged(),x(),this._$timerId=+B((()=>{this._$blinking()}),500),this._$timerId|=0}_$setIndex(t,e){if("input"!==this._$type)return;const i=this.getTextData();if(!i.textTable.length)return this._$focusIndex=0,this._$selectIndex=-1,void this.setBlinkingTimer();const s=this.width,r=this.height;let n=0;this._$scrollX>0&&(n+=this._$scrollX*(this.textWidth-s)/s);let h=0;this._$scrollY&&(h+=this._$scrollY*(this.textHeight-r)/r);const o=a(),l=this.globalToLocal(new Dt(t,e)),c=l.x+n,_=l.y+h;let $=2,u=2,d=u+i.heightTable[0];for(let t=1;t$&&_>u&&d>_&&s>c){const e=t;switch(o){case Cs:case Rs:this._$selectIndex!==e&&this._$focusIndex===e&&(this._$selectIndex=e,this._$focusIndex!==e&&(this._$focusVisible=!1,L(this._$timerId),this._$doChanged(),x()));break;default:(this._$focusIndex!==e||this._$selectIndex>-1)&&(this._$focusIndex=e,this._$selectIndex=-1,this.setBlinkingTimer())}return}$=2,u+=i.heightTable[e.line-1],d=u+i.heightTable[e.line];break;case"text":if(t===i.textTable.length-1&&c>$&&_>u&&d>_&&s>c){const t=i.textTable.length;switch(o){case Cs:case Rs:this._$selectIndex!==t&&(this._$selectIndex=t,this._$focusIndex!==t&&(this._$focusVisible=!1,L(this._$timerId),this._$doChanged(),x()));break;default:(this._$focusIndex!==t||this._$selectIndex>-1)&&(this._$focusIndex=t,this._$selectIndex=-1,this.setBlinkingTimer())}return}if(c>$&&_>u&&d>_&&$+e.w>c){let s=t;switch(o){case Cs:case Rs:this._$focusIndex>s?this._$focusIndex===s+1?$+e.w/2c&&(s=-1):$+e.w/2>c&&(s-=1),this._$selectIndex!==s&&(this._$selectIndex=s,this._$selectIndex>-1&&(this._$focusVisible=!1,L(this._$timerId)),this._$doChanged(),x());break;default:if($+e.w/2-1)&&(this._$focusIndex=s,this._$selectIndex=-1,this.setBlinkingTimer())}return}$+=e.w}}switch(o){case Cs:case Rs:this._$focusIndex=-1,this._$selectIndex=-1;break;default:this._$focusIndex=i.textTable.length,this._$selectIndex=-1,this.setBlinkingTimer()}}setBlinkingTimer(){this._$focusVisible=!1,this._$doChanged(),x(),L(this._$timerId),this._$timerId=+B((()=>{this._$blinking()}),500),this._$timerId|=0}_$resize(){if("none"!==this._$autoSize){const t=this._$defaultTextFormat,e=this.textWidth+4+t._$widthMargin();if(this._$wordWrap)this._$bounds.xMax=this._$originBounds.xMax,this._$bounds.xMin=this._$originBounds.xMin;else switch(this._$autoSize){case"left":case"center":this._$bounds.xMax=e+this._$bounds.xMin;break;case"right":this._$bounds.xMax=this._$originBounds.xMax-(this._$originBounds.xMax-this._$originBounds.xMin-(e-this._$originBounds.xMin))}this._$bounds.yMax=this.textHeight+this._$originBounds.yMin+4}else this._$scrollEnabled&&(this._$xScrollShape||(this._$xScrollShape=new ge,this._$xScrollShape.graphics.beginFill("#000",.3).drawRoundRect(0,0,3,3,3),this._$xScrollShape.scale9Grid=new Vt(1.5,1.5,.1,.1)),this._$yScrollShape||(this._$yScrollShape=new ge,this._$yScrollShape.graphics.beginFill("#000",.3).drawRoundRect(0,0,3,3,3),this._$yScrollShape.scale9Grid=new Vt(1.5,1.5,.1,.1)))}_$getAlignOffset(t,e){const i=this.getTextData().getLineWidth(t.line),s=t.textFormat,r=s.leftMargin||0;if(!this._$wordWrap&&i>e)return v.max(0,r);const n=s.rightMargin||0;return"center"===s.align||"center"===this._$autoSize?v.max(0,e/2-r-n-i/2-2):"right"===s.align||"right"===this._$autoSize?v.max(0,e-r-i-n-4):v.max(0,r)}_$getBounds(t=null){if(t){let e=t;const i=this._$transform._$rawMatrix();return 1===i[0]&&0===i[1]&&0===i[2]&&1===i[3]&&0===i[4]&&0===i[5]||(e=gt(t,i)),pt(this._$bounds,e)}return Q(this._$bounds.xMin,this._$bounds.xMax,this._$bounds.yMin,this._$bounds.yMax)}_$buildCharacter(t){const e=this._$defaultTextFormat;switch(e.font=t.font,e.size=0|t.size,e.align=t.align,e.color=0|t.color,e.leading=t.leading,e.letterSpacing=t.letterSpacing,e.leftMargin=t.leftMargin,e.rightMargin=t.rightMargin,t.fontType){case 1:e.bold=!0;break;case 2:e.italic=!0;break;case 3:e.bold=!0,e.italic=!0}switch(this._$type=t.inputType,this._$multiline=!!t.multiline,this._$wordWrap=!!t.wordWrap,this._$border=!!t.border,this._$scrollEnabled=!!t.scroll,this._$thickness=0|t.thickness,this._$thicknessColor=0|t.thicknessColor,this._$bounds.xMin=t.originBounds.xMin,this._$bounds.xMax=t.originBounds.xMax,this._$bounds.yMin=t.originBounds.yMin,this._$bounds.yMax=t.originBounds.yMax,this._$originBounds.xMin=t.originBounds.xMin,this._$originBounds.xMax=t.originBounds.xMax,this._$originBounds.yMin=t.originBounds.yMin,this._$originBounds.yMax=t.originBounds.yMax,t.autoSize){case 1:this.autoSize=t.align;break;case 2:this.autoFontSize=!0}this.text=t.text,Mr&&this._$stage&&this._$createWorkerInstance()}_$sync(t){this._$buildCharacter(t)}_$build(t,e){const i=this._$baseBuild(t,e);return this._$buildCharacter(i),i}_$clip(t,e){const i=this._$getBounds(),s=i.xMax,r=i.xMin,n=i.yMax,a=i.yMin;J(i);const h=v.ceil(v.abs(s-r)),o=v.ceil(v.abs(n-a));if(!h||!o)return;let l=e;const c=this._$transform._$rawMatrix();1===c[0]&&0===c[1]&&0===c[2]&&1===c[3]&&0===c[4]&&0===c[5]||(l=gt(e,c)),t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(h,0),t.lineTo(h,o),t.lineTo(0,o),t.lineTo(0,0),t.clip(),l!==e&&st(l)}_$draw(t,e,i){if(!this._$visible)return;if(-1===this._$focusIndex&&!this._$background&&!this._$border&&!this.text)return;let s=i;const r=this._$transform._$rawColorTransform();1===r[0]&&1===r[1]&&1===r[2]&&1===r[3]&&0===r[4]&&0===r[5]&&0===r[6]&&0===r[7]||(s=ft(i,r));const n=dt(s[3]+s[7]/255,0,1);if(!n)return;let a=e;const h=this._$transform._$rawMatrix();1===h[0]&&0===h[1]&&0===h[2]&&1===h[3]&&0===h[4]&&0===h[5]||(a=gt(e,h));const o=this._$getBounds(null);o.xMin-=this._$thickness,o.xMax+=this._$thickness,o.yMin-=this._$thickness,o.yMax+=this._$thickness;const l=pt(o,a),c=+l.xMax,_=+l.xMin,$=+l.yMax,u=+l.yMin;J(l);const d=v.ceil(v.abs(c-_)),g=v.ceil(v.abs($-u));switch(!0){case 0===d:case 0===g:case d===-1/0:case g===-1/0:case d===b:case g===b:return}let f=+v.sqrt(a[0]*a[0]+a[1]*a[1]);if(!E.isInteger(f)){const t=f.toString(),e=t.indexOf("e");-1!==e&&(f=+t.slice(0,e)),f=+f.toFixed(4)}let p=+v.sqrt(a[2]*a[2]+a[3]*a[3]);if(!E.isInteger(p)){const t=p.toString(),e=t.indexOf("e");-1!==e&&(p=+t.slice(0,e)),p=+p.toFixed(4)}const m=this._$filters||this.filters,x=null!==m&&m.length>0&&this._$canApply(m);let T=Q(0,d,0,g);if(x&&m)for(let t=0;tA.width||u-T.yMin>A.height)return void J(T);if(0>_+T.xMax||0>u+T.yMax)return void J(T);if(J(T),this._$isUpdated()&&(St.removeCache(this._$instanceId),t.cachePosition=null,this._$cacheKeys.length=0),!this._$cacheKeys.length||this._$cacheParams[0]!==f||this._$cacheParams[1]!==p||this._$cacheParams[2]!==i[7]){const t=ht(f,p);this._$cacheKeys=St.generateKeys(this._$instanceId,t),ot(t),this._$cacheParams[0]=f,this._$cacheParams[1]=p,this._$cacheParams[2]=i[7]}const M=this._$blendMode||this.blendMode;if(t.cachePosition=St.get(this._$cacheKeys),!t.cachePosition){const e=v.min(1,v.max(f,p)),i=v.ceil(v.abs(o.xMax-o.xMin)*f),r=v.ceil(v.abs(o.yMax-o.yMin)*p),n=St.getCanvas();n.width=i+2*e,n.height=r+2*e;const a=n.getContext("2d");if(!a)throw new Error("the context is null.");if(this._$background||this._$border){if(a.beginPath(),a.moveTo(0,0),a.lineTo(i,0),a.lineTo(i,r),a.lineTo(0,r),a.lineTo(0,0),this._$background){const t=Et(this._$backgroundColor),e=v.max(0,v.min(255*t.A+s[7],255))/255;a.fillStyle=`rgba(${t.R},${t.G},${t.B},${e})`,a.fill()}if(this._$border){const t=Et(this._$borderColor),i=v.max(0,v.min(255*t.A+s[7],255))/255;a.lineWidth=e,a.strokeStyle=`rgba(${t.R},${t.G},${t.B},${i})`,a.stroke()}}a.save(),a.beginPath(),a.moveTo(2,2),a.lineTo(i-2,2),a.lineTo(i-2,r-2),a.lineTo(2,r-2),a.lineTo(2,2),a.clip();let h=2;if(this._$scrollX>0){const t=(this.textWidth-this.width)/this.width;h+=-this._$scrollX*t}let l=2;if(this._$scrollY>0){const t=(this.textHeight-this.height)/this.height;l+=-this._$scrollY*t}a.setTransform(f,0,0,p,h*f,l*p),a.beginPath(),this._$doDraw(a,s,i/f,e),a.restore();const c=y.createCachePosition(i,r),_=y.createTextureFromCanvas(a.canvas);t.drawTextureFromRect(_,c),t.cachePosition=c,St.set(this._$cacheKeys,c),St.destroy(a)}let S=!1,w=0,C=0;if(m&&m.length&&this._$canApply(m)){S=!0;const e=this._$drawFilter(t,a,m,d,g);e.offsetX&&(w=e.offsetX),e.offsetY&&(C=e.offsetY),t.cachePosition=e}const I=v.atan2(a[1],a[0]),F=v.atan2(-a[2],a[3]);if(S||!I&&!F)t.setTransform(1,0,0,1,_-w,u-C);else{const e=o.xMin*f,i=o.yMin*p,s=v.cos(I),r=v.sin(I),n=v.cos(F),h=v.sin(F);t.setTransform(s,r,-h,n,e*s-i*h+a[4],e*r+i*n+a[5])}t.cachePosition&&(t.globalAlpha=n,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=M,t.drawInstance(_-w,u-C,c,$,i),t.cachePosition=null),J(o),a!==e&&st(a),s!==i&&nt(s)}_$doDraw(t,e,i,s){const r=this.getTextData();if(!r.textTable.length&&this._$focusIndex>-1&&this._$focusVisible){const i=this._$defaultTextFormat,s=Et(i.color||0),r=v.max(0,v.min(255*s.A+e[7],255))/255;return t.strokeStyle=`rgba(${s.R},${s.G},${s.B},${r})`,t.beginPath(),t.moveTo(0,0),t.lineTo(0,0+(i.size||12)),void t.stroke()}if(this._$selectIndex>-1&&this._$focusIndex>-1){const e=r.textTable.length-1;let s=0,n=0;this._$focusIndex<=this._$selectIndex?(s=v.min(this._$focusIndex,e),n=v.min(this._$selectIndex,e)):(s=v.min(this._$selectIndex,e),n=v.min(this._$focusIndex-1,e));const a=r.textTable[s],h=r.lineTable[a.line],o=this._$getAlignOffset(h,i);let l=0;if(s&&"text"===a.mode){let t=s;for(;t;){const e=r.textTable[--t];if("text"!==e.mode)break;l+=e.w}}t.fillStyle="#b4d7ff";let c=0;for(let e=s;e<=n;++e){const i=r.textTable[e];if("text"===i.mode&&(c+=i.w,e!==n))continue;let s=0;const a="text"===i.mode?i.line:i.line-1;for(let t=0;t0){const t=(this.textWidth-n)/n;a=this._$scrollX*t}const h=n+a,o=this.height;let l=0;if(this._$scrollY>0){const t=(this.textHeight-o)/o;l=this._$scrollY*t}const c=o+l;let _=0,$=0,u=0,d=0,g=!1,f=-1;for(let n=0;n-1&&f>this._$stopIndex))break;if(g&&"text"===o.mode)continue;const p=o.textFormat;if("none"===this._$autoSize){if($>c)break;if("text"===o.mode&&(a>_+o.w||_>h)){_+=o.w;continue}}const m=Et(p.color||0),x=v.max(0,v.min(255*m.A+e[7],255))/255;if(t.fillStyle=`rgba(${m.R},${m.G},${m.B},${x})`,this._$focusVisible&&this._$focusIndex===n){const e=_+u+.1;let i=o.line,s=o.y,n=r.ascentTable[i];"text"!==o.mode&&(s="break"===o.mode?o.h:r.ascentTable[i-1],i>0&&!r.ascentTable[i-1]?(i=o.line,n=r.ascentTable[i-1]):(i=o.line-1,n=r.ascentTable[i]));for(let t=0;t$+r.heightTable[b]){g=!0;continue}d=r.ascentTable[b],u=this._$getAlignOffset(o,i),g=!1;break;case"text":{t.beginPath(),t.font=At(p.font||"",p.size||0,!!p.italic,!!p.bold);const i=_+u,r=$+d;if(p.underline){const n=Et(p.color||0),a=v.max(0,v.min(255*n.A+e[7],255))/255;t.lineWidth=s,t.strokeStyle=`rgba(${n.R},${n.G},${n.B},${a})`,t.beginPath(),t.moveTo(i,r+2),t.lineTo(i+o.w,r+2),t.stroke()}this._$thickness&&t.strokeText(o.text,i,r),t.fillText(o.text,i,r),_+=o.w}}}if(this._$focusVisible&&this._$focusIndex>=r.textTable.length){const i=r.textTable[this._$focusIndex-1];if(i){const s=Et(i.textFormat.color||0),r=v.max(0,v.min(255*s.A+e[7],255))/255;t.strokeStyle=`rgba(${s.R},${s.G},${s.B},${r})`;const n=_+u+.1,a=$+d;t.beginPath(),"text"===i.mode?(t.moveTo(n,a-i.y),t.lineTo(n,a)):(t.moveTo(n,a+i.h),t.lineTo(n,a)),t.stroke()}}}_$mouseHit(t,e,i){return!!this._$visible&&this._$hit(t,e,i)}_$hit(t,e,i){let s=e;const r=this._$transform._$rawMatrix();1===r[0]&&0===r[1]&&0===r[2]&&1===r[3]&&0===r[4]&&0===r[5]||(s=gt(e,r));const n=this._$getBounds(null),a=pt(n,s),h=+a.xMax,o=+a.xMin,l=+a.yMax,c=+a.yMin;J(a),J(n);const _=v.ceil(v.abs(h-o)),$=v.ceil(v.abs(l-c));return t.setTransform(1,0,0,1,o,c),t.beginPath(),t.moveTo(0,0),t.lineTo(_,0),t.lineTo(_,$),t.lineTo(0,$),t.lineTo(0,0),s!==e&&st(s),t.isPointInPath(i.x,i.y)}_$createWorkerInstance(){if(this._$created||!Mr)return;this._$created=!0;const t=this._$getBounds(),e={command:"createTextField",buffer:new Float32Array,instanceId:this._$instanceId,parentId:this._$parent?this._$parent._$instanceId:-1,xMin:t.xMin,yMin:t.yMin,xMax:t.xMax,yMax:t.yMax,limitWidth:this.width,limitHeight:this.height,textHeight:this.textHeight,autoSize:this._$autoSize,wordWrap:this._$wordWrap,border:this._$border,background:this._$background,thickness:this._$thickness};this._$border&&(e.borderColor=this._$borderColor),this._$background&&(e.backgroundColor=this._$backgroundColor),this._$thickness&&(e.thicknessColor=this._$backgroundColor),this._$characterId>-1&&(e.characterId=this._$characterId),this._$loaderInfo&&(e.loaderInfoId=this._$loaderInfo._$id),this._$scale9Grid&&(e.grid={x:this._$scale9Grid.x,y:this._$scale9Grid.y,w:this._$scale9Grid.width,h:this._$scale9Grid.height}),Mr.postMessage(e)}_$postProperty(){if(!Mr)return;const t=this._$createMessage(),e=this._$getBounds(null);t.xMin=e.xMin,t.yMin=e.yMin,t.xMax=e.xMax,t.yMax=e.yMax,J(e),this._$isUpdated()&&(t.limitWidth=this.width,t.limitHeight=this.height,t.textHeight=this.textHeight,t.autoSize=this._$autoSize,t.wordWrap=this._$wordWrap,t.border=this._$border,this._$border&&(t.borderColor=this._$borderColor),t.background=this._$background,this._$background&&(t.backgroundColor=this._$backgroundColor),t.thickness=this._$thickness,this._$thickness&&(t.thicknessColor=this._$backgroundColor)),Mr.postMessage(t),this._$posted=!0,this._$updated=!1}}class Ii{constructor(){this._$rgb="rgb",this._$mode="pad",this._$type="linear",this._$focalPointRatio=0,this._$points=it(),this._$stops=ht()}dispose(){const t=this._$stops;for(let i=0;i{switch(!0){case t[0]>e[0]:return 1;case e[0]>t[0]:return-1;default:return 0}})),this._$stops}linear(t,e,i,s,r="rgb",n="pad"){return this._$type="linear",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$rgb=r,this._$mode=n,this._$stops.length&&(this._$stops.length=0),this}radial(t,e,i,s,r,n,a="rgb",h="pad",o=0){return this._$type="radial",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$points[4]=r,this._$points[5]=n,this._$rgb=a,this._$mode=h,this._$focalPointRatio=dt(o,-.975,.975,0),this._$stops.length&&(this._$stops.length=0),this}addColorStop(t,e){this._$stops.push(ht(t,e))}}class Fi{constructor(t,e,i){this._$texture=t,this._$repeat=e,this._$colorTransform=i}get texture(){return this._$texture}get repeat(){return this._$repeat}get colorTransform(){return this._$colorTransform}}class Ri{constructor(){this._$fillStyle=Z(1,1,1,1),this._$strokeStyle=Z(1,1,1,1),this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5}get miterLimit(){return this._$miterLimit}set miterLimit(t){this._$miterLimit=t}get lineWidth(){return this._$lineWidth}set lineWidth(t){this._$lineWidth=t}get lineCap(){return this._$lineCap}set lineCap(t){this._$lineCap=t}get lineJoin(){return this._$lineJoin}set lineJoin(t){this._$lineJoin=t}get fillStyle(){return this._$fillStyle}set fillStyle(t){this._$fillStyle instanceof A&&tt(this._$fillStyle),this._$fillStyle=t}get strokeStyle(){return this._$strokeStyle}set strokeStyle(t){this._$strokeStyle instanceof A&&tt(this._$strokeStyle),this._$strokeStyle=t}clear(){this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5,this._$clearFill(),this._$clearStroke()}_$clearFill(){if(this._$fillStyle instanceof Ii)return this._$fillStyle.dispose(),void(this._$fillStyle=Z(1,1,1,1));this._$fillStyle instanceof Fi?this._$fillStyle=Z(1,1,1,1):this._$fillStyle.fill(1)}_$clearStroke(){if(this._$strokeStyle instanceof Ii)return this._$strokeStyle.dispose(),void(this._$strokeStyle=Z(1,1,1,1));this._$strokeStyle instanceof Fi?this._$strokeStyle=Z(1,1,1,1):this._$strokeStyle.fill(1)}}let Bi=2048;class Li{constructor(t){t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this._$gl=t,this._$objectPool=[],this._$objectPoolArea=0,this._$activeTexture=-1,this._$boundTextures=[null,null,null],this._$maxWidth=0,this._$maxHeight=0,this._$atlasTextures=[],this._$atlasCacheMap=new Map,this._$positionObjectArray=[],this._$nodeObjectArray=[],this._$atlasNodes=new Map}createTextureAtlas(){const t=this._$gl.createTexture();t.width=Bi,t.height=Bi,this._$gl.activeTexture(this._$gl.TEXTURE3),this._$gl.bindTexture(this._$gl.TEXTURE_2D,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,this._$gl.NEAREST),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,this._$gl.NEAREST),this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,Bi,Bi),this._$gl.bindTexture(this._$gl.TEXTURE_2D,null),this._$activeTexture>-1&&this._$gl.activeTexture(this._$activeTexture);const e=this._$atlasTextures.length;this._$atlasNodes.set(e,[]),this._$atlasCacheMap.set(e,[]),this._$atlasTextures.push(t)}getAtlasTexture(t){return this._$atlasTextures[t]}getNode(t,e,i,s){const r=this._$nodeObjectArray.length?this._$nodeObjectArray.pop():{x:0,y:0,w:0,h:0};return r.x=t,r.y=e,r.w=i,r.h=s,r}createCachePosition(t,e){const i=this._$positionObjectArray.length?this._$positionObjectArray.pop():{index:0,x:0,y:0,w:0,h:0};i.x=i.y=0,i.w=t,i.h=e;for(const[s,r]of this._$atlasNodes){if(!r.length)return t>e?(Bi-t-1>0&&r.push(this.getNode(t+1,0,Bi-t-1,e)),Bi-e-1>0&&r.push(this.getNode(0,e+1,Bi,Bi-e-1))):(Bi-e-1>0&&r.push(this.getNode(0,e+1,t,Bi-e-1)),Bi-t-1>0&&r.push(this.getNode(t+1,0,Bi-t-1,Bi))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i;const n=r.length;for(let a=0;an.w||e>n.h))return i.index=s,i.x=n.x,i.y=n.y,this._$atlasCacheMap.get(i.index).push(i),n.w!==t||n.h!==e?t>e?(n.h-e-1>0&&r.push(this.getNode(n.x,n.y+e+1,n.w,n.h-e-1)),n.w-t-1>0?(n.x=n.x+t+1,n.w=n.w-t-1,n.h=e):(r.splice(a,1),this._$nodeObjectArray.push(n))):(n.w-t-1>0&&r.push(this.getNode(n.x+t+1,n.y,n.w-t-1,n.h)),n.h-e-1>0?(n.y=n.y+e+1,n.w=t,n.h=n.h-e-1):(r.splice(a,1),this._$nodeObjectArray.push(n))):(r.splice(a,1),this._$nodeObjectArray.push(n)),i}}const s=this._$atlasTextures.length;this.createTextureAtlas();const r=this._$atlasNodes.get(s);return t>e?(Bi-t-1>0&&r.push(this.getNode(t+1,0,Bi-t-1,e)),Bi-e-1>0&&r.push(this.getNode(0,e+1,Bi,Bi-e-1))):(Bi-e-1>0&&r.push(this.getNode(0,e+1,t,Bi-e-1)),Bi-t-1>0&&r.push(this.getNode(t+1,0,Bi-t-1,Bi))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i}releasePosition(t){var e;this._$atlasNodes.has(t.index)&&(null===(e=this._$atlasNodes.get(t.index))||void 0===e||e.unshift(this.getNode(t.x,t.y,t.w,t.h)),this._$positionObjectArray.push(t))}clearCache(){for(const t of this._$atlasCacheMap.values())t.length=0;for(const t of this._$atlasNodes.values())t.length=0}_$createTexture(t,e){const i=this._$gl.createTexture();return i.width=0,i.height=0,i.area=0,i.dirty=!0,i.smoothing=!0,this.bind0(i,!1),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,t,e),i}_$getTexture(t,e){for(let i=0;ithis._$maxWidth*this._$maxHeight*2)this._$gl.deleteTexture(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPool.length&&this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();this._$objectPoolArea-=t.area,this._$gl.deleteTexture(t)}}bind0(t,e=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,e)}bind01(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,e,i),this._$bindTexture(0,this._$gl.TEXTURE0,t,i)}bind012(t,e,i,s=null){this._$bindTexture(2,this._$gl.TEXTURE2,i,s),this._$bindTexture(1,this._$gl.TEXTURE1,e,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}bind02(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,e,i),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}_$bindTexture(t,e,i=null,s=null){const r=i!==this._$boundTextures[t],n=null!==s&&null!==i&&s!==i.smoothing;if((r||n||e===this._$gl.TEXTURE0)&&e!==this._$activeTexture&&(this._$activeTexture=e,this._$gl.activeTexture(e)),r&&(this._$boundTextures[t]=i,this._$gl.bindTexture(this._$gl.TEXTURE_2D,i)),n){i&&(i.smoothing=!!s);const t=s?this._$gl.LINEAR:this._$gl.NEAREST;this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,t)}}}class Pi{constructor(t){this._$gl=t,this._$objectPool=ht(),this._$objectPoolArea=0,this._$maxWidth=0,this._$maxHeight=0}set maxWidth(t){this._$maxWidth=t}set maxHeight(t){this._$maxHeight=t}_$createStencilBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the stencil buffer is null.");return t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getStencilBuffer(t,e){const i=this._$objectPool.length;for(let s=0;s100){const t=this._$objectPool.shift();if(t)return this._$objectPoolArea-=t.area,t}return this._$createStencilBuffer()}create(t,e){const i=this._$getStencilBuffer(t,e);return i.width===t&&i.height===e||(i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,i),this._$gl.renderbufferStorage(this._$gl.RENDERBUFFER,this._$gl.STENCIL_INDEX8,t,e)),i}release(t){if(t.area>this._$maxWidth*this._$maxHeight*2)this._$gl.deleteRenderbuffer(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();t&&(this._$objectPoolArea-=t.area,this._$gl.deleteRenderbuffer(t))}}}class ki{constructor(t,e){this._$gl=t,this._$samples=e,this._$objectPool=ht()}set samples(t){this._$samples=t}_$createColorBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the color buffer is null.");const e=this._$gl.createRenderbuffer();if(!e)throw new Error("the stencil buffer is null.");return t.stencil=e,t.samples=0,t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getColorBuffer(t){if(!this._$objectPool.length)return this._$createColorBuffer();const e=this._$bsearch(t);if(e1;){const s=v.floor((i+e)/2);t<=this._$objectPool[s].area?i=s:e=s}return i}}class Ni{constructor(t,e){this._$gl=t,this._$objectPool=[],this._$frameBuffer=t.createFramebuffer(),t.bindFramebuffer(t.READ_FRAMEBUFFER,this._$frameBuffer),this._$frameBufferTexture=t.createFramebuffer(),this._$currentAttachment=null,this._$isBinding=!1,this._$textureManager=new Li(t),this._$stencilBufferPool=new Pi(t),this._$colorBufferPool=new ki(t,e),this._$isRenderBinding=!1,this._$colorBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.RGBA8,Bi,Bi),this._$stencilBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.STENCIL_INDEX8,Bi,Bi)}bindRenderBuffer(){this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),this._$isRenderBinding||(this._$isRenderBinding=!0,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,this._$stencilBuffer))}get currentAttachment(){return this._$currentAttachment}get textureManager(){return this._$textureManager}createCacheAttachment(t,e,i=!1,s=0){const r=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},n=this._$textureManager.create(t,e);return r.width=t,r.height=e,i?(r.color=this._$colorBufferPool.create(t,e,s),r.texture=n,r.msaa=!0,r.stencil=r.color.stencil):(r.color=n,r.texture=n,r.msaa=!1,r.stencil=this._$stencilBufferPool.create(t,e)),r.mask=!1,r.clipLevel=0,r.isActive=!0,r}clearCache(){this._$textureManager.clearCache()}setMaxSize(t,e){this._$stencilBufferPool._$maxWidth=t,this._$stencilBufferPool._$maxHeight=e,this._$textureManager._$maxWidth=t,this._$textureManager._$maxHeight=e}createTextureAttachment(t,e){const i=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},s=this._$textureManager.create(t,e);return i.width=t,i.height=e,i.color=s,i.texture=s,i.msaa=!1,i.stencil=null,i.mask=!1,i.clipLevel=0,i.isActive=!0,i}createTextureAttachmentFrom(t){const e=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!0};return e.width=t.width,e.height=t.height,e.color=t,e.texture=t,e.msaa=!1,e.stencil=null,e.mask=!1,e.clipLevel=0,e.isActive=!0,e}releaseAttachment(t=null,e=!1){t&&t.isActive&&(t.msaa?t.color instanceof WebGLRenderbuffer&&this._$colorBufferPool.release(t.color):t.stencil&&this._$stencilBufferPool.release(t.stencil),e&&t.texture&&this._$textureManager.release(t.texture),t.color=null,t.texture=null,t.stencil=null,t.isActive=!1,this._$objectPool.push(t))}bind(t){this._$currentAttachment=t,this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),t.msaa?t.color instanceof WebGLRenderbuffer&&(this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.color),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,t.color)):t.color instanceof WebGLTexture&&(t.color&&this._$textureManager.bind0(t.color),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,t.color,0)),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.stencil),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,t.stencil),this._$isRenderBinding=!1}unbind(){this._$currentAttachment=null,this._$isBinding&&(this._$isBinding=!1,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,null))}transferToMainTexture(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,null),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBuffer)}createCachePosition(t,e){return this._$textureManager.createCachePosition(t,e)}transferTexture(t){this._$gl.disable(this._$gl.SCISSOR_TEST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture);const e=this._$textureManager.getAtlasTexture(t.index);this._$textureManager.bind0(e),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,e,0);const i=v.max(0,t.x-1),s=v.max(0,t.y-1),r=v.min(Bi,t.x+t.w+1),n=v.min(Bi,t.y+t.h+1);this._$gl.blitFramebuffer(i,s,r,n,i,s,r,n,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)}getTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");if(!this._$currentAttachment.msaa&&this._$currentAttachment.texture)return this._$currentAttachment.texture;const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");return i.dirty=!1,this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer),i}createTextureFromPixels(t,e,i=null,s=!1,r=!0){return this._$textureManager.create(t,e,i,s,r)}createTextureFromCanvas(t){return this._$textureManager.createFromCanvas(t)}createTextureFromImage(t,e=!1){return this._$textureManager.createFromImage(t,e)}createTextureFromVideo(t,e=!1){return this._$textureManager.createFromVideo(t,e)}createTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$textureManager.create(t,e);return this._$textureManager.bind0(i),this._$gl.copyTexSubImage2D(this._$gl.TEXTURE_2D,0,0,0,0,0,t,e),i}releaseTexture(t){this._$textureManager.release(t)}}class Oi{constructor(){this._$bezierConverterBuffer=new A(32)}cubicToQuad(t,e,i,s,r,n,a,h){this._$split2Cubic(t,e,i,s,r,n,a,h,0,16),this._$split2Cubic(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0,8),this._$split2Cubic(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16,24),this._$split2Quad(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0),this._$split2Quad(this._$bezierConverterBuffer[8],this._$bezierConverterBuffer[9],this._$bezierConverterBuffer[10],this._$bezierConverterBuffer[11],this._$bezierConverterBuffer[12],this._$bezierConverterBuffer[13],this._$bezierConverterBuffer[14],this._$bezierConverterBuffer[15],8),this._$split2Quad(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16),this._$split2Quad(this._$bezierConverterBuffer[24],this._$bezierConverterBuffer[25],this._$bezierConverterBuffer[26],this._$bezierConverterBuffer[27],this._$bezierConverterBuffer[28],this._$bezierConverterBuffer[29],this._$bezierConverterBuffer[30],this._$bezierConverterBuffer[31],24)}_$split2Cubic(t,e,i,s,r,n,a,h,o,l){const c=.125*(t+3*(i+r)+a),_=.125*(e+3*(s+n)+h),$=.125*(a+r-i-t),u=.125*(h+n-s-e);this._$bezierConverterBuffer[o]=t,this._$bezierConverterBuffer[o+1]=e,this._$bezierConverterBuffer[o+2]=.5*(t+i),this._$bezierConverterBuffer[o+3]=.5*(e+s),this._$bezierConverterBuffer[o+4]=c-$,this._$bezierConverterBuffer[o+5]=_-u,this._$bezierConverterBuffer[o+6]=c,this._$bezierConverterBuffer[o+7]=_,this._$bezierConverterBuffer[l]=c,this._$bezierConverterBuffer[l+1]=_,this._$bezierConverterBuffer[l+2]=c+$,this._$bezierConverterBuffer[l+3]=_+u,this._$bezierConverterBuffer[l+4]=.5*(r+a),this._$bezierConverterBuffer[l+5]=.5*(n+h),this._$bezierConverterBuffer[l+6]=a,this._$bezierConverterBuffer[l+7]=h}_$split2Quad(t,e,i,s,r,n,a,h,o){const l=.125*(t+3*(i+r)+a),c=.125*(e+3*(s+n)+h);this._$bezierConverterBuffer[o]=.25*t+.75*i,this._$bezierConverterBuffer[o+1]=.25*e+.75*s,this._$bezierConverterBuffer[o+2]=l,this._$bezierConverterBuffer[o+3]=c,this._$bezierConverterBuffer[o+4]=.75*r+.25*a,this._$bezierConverterBuffer[o+5]=.75*n+.25*h,this._$bezierConverterBuffer[o+6]=a,this._$bezierConverterBuffer[o+7]=h}}class Di{constructor(){this._$currentPath=ht(),this._$vertices=ht(),this._$bezierConverter=new Oi}get vertices(){return this._$pushCurrentPathToVertices(),this._$vertices}begin(){for(this._$currentPath.length=0;this._$vertices.length;)ot(this._$vertices.pop())}moveTo(t,e){this._$currentPath.length?this._$equalsToLastPoint(t,e)||(this._$pushCurrentPathToVertices(),this._$pushPointToCurrentPath(t,e,!1)):this._$pushPointToCurrentPath(t,e,!1)}lineTo(t,e){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}quadTo(t,e,i,s){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(i,s)||(this._$pushPointToCurrentPath(t,e,!0),this._$pushPointToCurrentPath(i,s,!1))}cubicTo(t,e,i,s,r,n){if(this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(r,n))return;const a=+this._$currentPath[this._$currentPath.length-3],h=+this._$currentPath[this._$currentPath.length-2];this._$bezierConverter.cubicToQuad(a,h,t,e,i,s,r,n);const o=this._$bezierConverter._$bezierConverterBuffer;for(let t=0;t<32;)this.quadTo(o[t++],o[t++],o[t++],o[t++])}drawCircle(t,e,i){const s=i,r=.5522847498307936*i;this.cubicTo(t+s,e+r,t+r,e+s,t,e+s),this.cubicTo(t-r,e+s,t-s,e+r,t-s,e),this.cubicTo(t-s,e-r,t-r,e-s,t,e-s),this.cubicTo(t+r,e-s,t+s,e-r,t+s,e)}close(){if(this._$currentPath.length<=6)return;const t=+this._$currentPath[0],e=+this._$currentPath[1];this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}_$equalsToLastPoint(t,e){const i=+this._$currentPath[this._$currentPath.length-3],s=+this._$currentPath[this._$currentPath.length-2];return t===i&&e===s}_$pushPointToCurrentPath(t,e,i){this._$currentPath.push(t,e,i)}_$pushCurrentPathToVertices(){this._$currentPath.length<4?this._$currentPath.length=0:(this._$vertices.push(this._$currentPath),this._$currentPath=ht())}createRectVertices(t,e,i,s){return ht(ht(t,e,!1,t+i,e,!1,t+i,e+s,!1,t,e+s,!1))}}class Ui{constructor(){this.enabled=!1,this.parentMatrixA=1,this.parentMatrixB=0,this.parentMatrixC=0,this.parentMatrixD=0,this.parentMatrixE=1,this.parentMatrixF=0,this.parentMatrixG=0,this.parentMatrixH=0,this.parentMatrixI=1,this.ancestorMatrixA=1,this.ancestorMatrixB=0,this.ancestorMatrixC=0,this.ancestorMatrixD=0,this.ancestorMatrixE=1,this.ancestorMatrixF=0,this.ancestorMatrixG=0,this.ancestorMatrixH=0,this.ancestorMatrixI=1,this.parentViewportX=0,this.parentViewportY=0,this.parentViewportW=0,this.parentViewportH=0,this.minXST=1e-5,this.minYST=1e-5,this.minXPQ=1e-5,this.minYPQ=1e-5,this.maxXST=.99999,this.maxYST=.99999,this.maxXPQ=.99999,this.maxYPQ=.99999}enable(t,e,i,s,r,n,a,h,o,l,c,_,$,u,d,g,f,p,m){const x=r.xMax-r.xMin,b=r.yMax-r.yMin,T=n.w,y=n.h,E=v.abs(v.ceil(x*a)),A=v.abs(v.ceil(b*a)),M=T>0?(n.x-r.xMin)/x:1e-5,S=y>0?(n.y-r.yMin)/b:1e-5,w=T>0?(n.x+n.w-r.xMin)/x:.99999,C=y>0?(n.y+n.h-r.yMin)/b:.99999;let I=E*M/i,F=A*S/s,R=(i-E*(1-w))/i,B=(s-A*(1-C))/s;if(I>=R){const t=M/(M+(1-w));I=v.max(t-1e-5,0),R=v.min(t+1e-5,1)}if(F>=B){const t=S/(S+(1-C));F=v.max(t-1e-5,0),B=v.min(t+1e-5,1)}this.enabled=!0,this.parentMatrixA=h,this.parentMatrixB=o,this.parentMatrixD=l,this.parentMatrixE=c,this.parentMatrixG=_,this.parentMatrixH=$,this.ancestorMatrixA=u,this.ancestorMatrixB=d,this.ancestorMatrixD=g,this.ancestorMatrixE=f,this.ancestorMatrixG=p,this.ancestorMatrixH=m,this.parentViewportX=t,this.parentViewportY=e,this.parentViewportW=i,this.parentViewportH=s,this.minXST=M,this.minYST=S,this.minXPQ=I,this.minYPQ=F,this.maxXST=w,this.maxYST=C,this.maxXPQ=R,this.maxYPQ=B}disable(){this.enabled=!1}}class Vi{constructor(t,e){this._$gl=t,this._$array=[],this._$map=ct();const i=this._$gl.getProgramParameter(e,this._$gl.ACTIVE_UNIFORMS);for(let t=0;t0&&(t.assign--,t.method(t.array)))}}}class Gi{constructor(){this._$attributes=[],this._$count=0}get attributes(){return this._$attributes}get count(){return this._$count}set count(t){this._$count=t}clear(){this._$attributes.length=0,this._$count=0}}class zi{constructor(t,e,i,s){this._$gl=t,this._$context=e,this._$program=this._$createProgram(i,s),this._$uniform=new Vi(t,this._$program),this._$instance=null}get instance(){return this._$instance||(this._$instance=new Gi),this._$instance}get uniform(){return this._$uniform}_$createProgram(t,e){const i=this._$gl.createProgram();i.id=p++;const s=this._$gl.createShader(this._$gl.VERTEX_SHADER);this._$gl.shaderSource(s,t),this._$gl.compileShader(s);const r=this._$gl.createShader(this._$gl.FRAGMENT_SHADER);return this._$gl.shaderSource(r,e),this._$gl.compileShader(r),this._$gl.attachShader(i,s),this._$gl.attachShader(i,r),this._$gl.linkProgram(i),this._$gl.detachShader(i,s),this._$gl.detachShader(i,r),this._$gl.deleteShader(s),this._$gl.deleteShader(r),i}_$attachProgram(){const t=this._$context.shaderList;t.currentProgramId!==this._$program.id&&(t.currentProgramId=this._$program.id,this._$gl.useProgram(this._$program))}drawArraysInstanced(t){this._$attachProgram(),this._$context.vao.bindInstnceArray(t),this._$gl.drawArraysInstanced(this._$gl.TRIANGLE_STRIP,0,4,t.count)}_$drawImage(){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindCommonVertexArray(),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$drawGradient(t,e){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindGradientVertexArray(t,e),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$stroke(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawElements(this._$gl.TRIANGLES,t.indexCount,this._$gl.UNSIGNED_SHORT,0)}_$fill(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t);const e=t.indexRanges,i=e[e.length-1];this._$gl.drawArrays(this._$gl.TRIANGLES,0,i.first+i.count)}_$containerClip(t,e,i){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.TRIANGLES,e,i)}_$drawPoints(t,e,i){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.POINTS,e,i)}}class Xi{static FUNCTION_GRID_OFF(){return"\n\nvec2 applyMatrix(in vec2 vertex) {\n mat3 matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n\n vec2 position = (matrix * vec3(vertex, 1.0)).xy;\n\n return position;\n}\n\n"}static FUNCTION_GRID_ON(t){return`\n\nvec2 applyMatrix(in vec2 vertex) {\n mat3 parent_matrix = mat3(\n u_highp[${t}].xyz,\n u_highp[${t+1}].xyz,\n u_highp[${t+2}].xyz\n );\n mat3 ancestor_matrix = mat3(\n u_highp[${t+3}].xyz,\n u_highp[${t+4}].xyz,\n u_highp[${t+5}].xyz\n );\n vec2 parent_offset = vec2(u_highp[${t+2}].w, u_highp[${t+3}].w);\n vec2 parent_size = vec2(u_highp[${t+4}].w, u_highp[${t+5}].w);\n vec4 grid_min = u_highp[${t+6}];\n vec4 grid_max = u_highp[${t+7}];\n\n vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy;\n position = (position - parent_offset) / parent_size;\n\n vec4 ga = grid_min;\n vec4 gb = grid_max - grid_min;\n vec4 gc = vec4(1.0) - grid_max;\n\n vec2 pa = position;\n vec2 pb = position - grid_min.st;\n vec2 pc = position - grid_max.st;\n\n position = (ga.pq / ga.st) * min(pa, ga.st)\n + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st)\n + (gc.pq / gc.st) * max(vec2(0.0), pc);\n\n position = position * parent_size + parent_offset;\n position = (ancestor_matrix * vec3(position, 1.0)).xy;\n\n return position;\n}\n\n`}}class qi{static TEMPLATE(t,e,i,s){const r=e-1,n=i?this.VARYING_UV_ON():"",a=i?this.STATEMENT_UV_ON():"";return`#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\nlayout (location = 1) in vec2 a_option1;\nlayout (location = 2) in vec2 a_option2;\nlayout (location = 3) in float a_type;\n\nuniform vec4 u_highp[${t}];\n\n${n}\n\n${s?Xi.FUNCTION_GRID_ON(i?5:0):Xi.FUNCTION_GRID_OFF()}\n\nfloat crossVec2(in vec2 v1, in vec2 v2) {\n return v1.x * v2.y - v2.x * v1.y;\n}\n\nvec2 perpendicularVec2(in vec2 v1) {\n float face = u_highp[${r}][1];\n\n return face * vec2(v1.y, -v1.x);\n}\n\nvec2 calculateNormal(in vec2 direction) {\n vec2 normalized = normalize(direction);\n return perpendicularVec2(normalized);\n}\n\nvec2 calculateIntersection(in vec2 v1, in vec2 v2, in vec2 o1, in vec2 o2) {\n float t = crossVec2(o2 - o1, v2) / crossVec2(v1, v2);\n return (o1 + t * v1);\n}\n\nvec2 calculateAnchor(in vec2 position, in float convex, out vec2 v1, out vec2 v2, out vec2 o1, out vec2 o2) {\n float miter_limit = u_highp[${r}][2];\n\n vec2 a = applyMatrix(a_option1);\n vec2 b = applyMatrix(a_option2);\n\n v1 = convex * (position - a);\n v2 = convex * (b - position);\n o1 = calculateNormal(v1) + a;\n o2 = calculateNormal(v2) + position;\n\n vec2 anchor = calculateIntersection(v1, v2, o1, o2) - position;\n return normalize(anchor) * min(length(anchor), miter_limit);\n}\n\nvoid main() {\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\n float half_width = u_highp[${r}][0];\n\n vec2 position = applyMatrix(a_vertex);\n vec2 offset = vec2(0.0);\n vec2 v1, v2, o1, o2;\n\n if (a_type == 1.0 || a_type == 2.0) { // 線分\n offset = calculateNormal(a_option2 * (applyMatrix(a_option1) - position));\n } else if (a_type == 10.0) { // スクエア線端\n offset = normalize(position - applyMatrix(a_option1));\n offset += a_option2 * perpendicularVec2(offset);\n } else if (a_type == 21.0) { // マイター結合(線分Bの凸側)\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\n } else if (a_type == 22.0) { // マイター結合(線分Aの凸側)\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\n } else if (a_type == 23.0) { // マイター結合(線分Aの凹側)\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\n } else if (a_type == 24.0) { // マイター結合(線分Bの凹側)\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\n } else if (a_type >= 30.0) { // ラウンド結合\n float face = u_highp[${r}][1];\n float rad = face * (a_type - 30.0) * 0.3488888889; /* 0.3488888889 = PI / 9.0 */\n offset = mat2(cos(rad), sin(rad), -sin(rad), cos(rad)) * vec2(1.0, 0.0);\n }\n \n offset *= half_width;\n position += offset;\n ${a}\n\n position /= viewport;\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n`}static VARYING_UV_ON(){return"\nout vec2 v_uv;\n"}static STATEMENT_UV_ON(){return"\n mat3 uv_matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n mat3 inverse_matrix = mat3(\n u_highp[3].xyz,\n u_highp[4].xyz,\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\n );\n\n v_uv = (uv_matrix * vec3(a_vertex, 1.0)).xy;\n v_uv += offset;\n v_uv = (inverse_matrix * vec3(v_uv, 1.0)).xy;\n"}}class Yi{static TEMPLATE(t,e,i,s){const r=i?this.ATTRIBUTE_BEZIER_ON():"",n=i?this.VARYING_BEZIER_ON():e?this.VARYING_UV_ON():"",a=i?this.STATEMENT_BEZIER_ON():e?this.STATEMENT_UV_ON():"";return`#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n${r}\n\nuniform vec4 u_highp[${t}];\n\n${n}\n\n${s?Xi.FUNCTION_GRID_ON(e?5:0):Xi.FUNCTION_GRID_OFF()}\n\nvoid main() {\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\n\n ${a}\n\n vec2 pos = applyMatrix(a_vertex) / viewport;\n pos = pos * 2.0 - 1.0;\n gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n}\n\n`}static ATTRIBUTE_BEZIER_ON(){return"\nlayout (location = 1) in vec2 a_bezier;\n"}static VARYING_UV_ON(){return"\nout vec2 v_uv;\n"}static VARYING_BEZIER_ON(){return"\nout vec2 v_bezier;\n"}static STATEMENT_UV_ON(){return"\n mat3 uv_matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n mat3 inverse_matrix = mat3(\n u_highp[3].xyz,\n u_highp[4].xyz,\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\n );\n\n v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy;\n"}static STATEMENT_BEZIER_ON(){return"\n v_bezier = a_bezier;\n"}}class Hi{static FUNCTION_IS_INSIDE(){return"\n\nfloat isInside(in vec2 uv) {\n return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0)));\n}\n\n"}static STATEMENT_INSTANCED_COLOR_TRANSFORM_ON(){return"\n src.rgb /= max(0.0001, src.a);\n src = clamp(src * mul + add, 0.0, 1.0);\n src.rgb *= src.a;\n"}static STATEMENT_COLOR_TRANSFORM_ON(t){return`\n vec4 mul = u_mediump[${t}];\n vec4 add = u_mediump[${t+1}];\n${Hi.STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()}\n`}}class ji{static SOLID_COLOR(){return"#version 300 es\nprecision mediump float;\n\nuniform vec4 u_mediump;\n\nout vec4 o_color;\n\nvoid main() {\n o_color = vec4(u_mediump.rgb * u_mediump.a, u_mediump.a);\n}\n\n"}static BITMAP_CLIPPED(){return`#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_mediump[3];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy;\n\n vec4 src = texture(u_texture, uv);\n ${Hi.STATEMENT_COLOR_TRANSFORM_ON(1)}\n o_color = src;\n}`}static BITMAP_PATTERN(){return`#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_mediump[3];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy);\n \n vec4 src = texture(u_texture, uv);\n ${Hi.STATEMENT_COLOR_TRANSFORM_ON(1)}\n o_color = src;\n}`}static MASK(){return"#version 300 es\nprecision mediump float;\n\nin vec2 v_bezier;\nout vec4 o_color;\n\nvoid main() {\n vec2 px = dFdx(v_bezier);\n vec2 py = dFdy(v_bezier);\n\n vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y);\n float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f);\n\n if (alpha > 0.0) {\n o_color = vec4(min(alpha, 1.0));\n } else {\n discard;\n } \n}\n\n"}}class Wi{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=ct()}getSolidColorShapeShader(t,e){const i=`s${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?qi.TEMPLATE(s,r,!1,e):Yi.TEMPLATE(s,!1,!1,e);const a=new zi(this._$gl,this._$context,n,ji.SOLID_COLOR());return this._$collection.set(i,a),a}getBitmapShapeShader(t,e,i){const s=`b${t?"y":"n"}${e?"y":"n"}${i?"y":"n"}`;if(this._$collection.has(s)){const t=this._$collection.get(s);if(t)return t}const r=(i?13:5)+(t?1:0),n=r;let a;a=t?qi.TEMPLATE(r,n,!0,i):Yi.TEMPLATE(r,!0,!1,i);const h=e?ji.BITMAP_PATTERN():ji.BITMAP_CLIPPED(),o=new zi(this._$gl,this._$context,a,h);return this._$collection.set(s,o),o}getMaskShapeShader(t,e){const i=`m${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?qi.TEMPLATE(s,r,!1,e):Yi.TEMPLATE(s,!1,!0,e);const a=new zi(this._$gl,this._$context,n,ji.MASK());return this._$collection.set(i,a),a}setSolidColorShapeUniform(t,e,i,s,r,n,a,h,o,l,c,_){const $=t.highp;let u;n?($[0]=l.parentMatrixA,$[1]=l.parentMatrixB,$[2]=l.parentMatrixC,$[4]=l.parentMatrixD,$[5]=l.parentMatrixE,$[6]=l.parentMatrixF,$[8]=l.parentMatrixG,$[9]=l.parentMatrixH,$[10]=l.parentMatrixI,$[12]=l.ancestorMatrixA,$[13]=l.ancestorMatrixB,$[14]=l.ancestorMatrixC,$[16]=l.ancestorMatrixD,$[17]=l.ancestorMatrixE,$[18]=l.ancestorMatrixF,$[20]=l.ancestorMatrixG,$[21]=l.ancestorMatrixH,$[22]=l.ancestorMatrixI,$[3]=h,$[7]=o,$[11]=l.parentViewportX,$[15]=l.parentViewportY,$[19]=l.parentViewportW,$[23]=l.parentViewportH,$[24]=l.minXST,$[25]=l.minYST,$[26]=l.minXPQ,$[27]=l.minYPQ,$[28]=l.maxXST,$[29]=l.maxYST,$[30]=l.maxXPQ,$[31]=l.maxYPQ,u=32):($[0]=a[0],$[1]=a[1],$[2]=a[2],$[4]=a[3],$[5]=a[4],$[6]=a[5],$[8]=a[6],$[9]=a[7],$[10]=a[8],$[3]=h,$[7]=o,u=12),e&&($[u]=i,$[u+1]=s,$[u+2]=r);const d=t.mediump;d[0]=c[0],d[1]=c[1],d[2]=c[2],d[3]=c[3]*_}setBitmapShapeUniform(t,e,i,s,r,n,a,h,o,l,c,_,$,u,d,g,f,p,m,x,b){const v=t.highp;let T;v[0]=a[0],v[1]=a[1],v[2]=a[2],v[4]=a[3],v[5]=a[4],v[6]=a[5],v[8]=a[6],v[9]=a[7],v[10]=a[8],v[12]=h[0],v[13]=h[1],v[14]=h[2],v[16]=h[3],v[17]=h[4],v[18]=h[5],v[11]=h[6],v[15]=h[7],v[19]=h[8],v[3]=o,v[7]=l,T=20,n&&(v[T]=c.parentMatrixA,v[T+1]=c.parentMatrixB,v[T+2]=c.parentMatrixC,v[T+4]=c.parentMatrixD,v[T+5]=c.parentMatrixE,v[T+6]=c.parentMatrixF,v[T+8]=c.parentMatrixG,v[T+9]=c.parentMatrixH,v[T+10]=c.parentMatrixI,v[T+12]=c.ancestorMatrixA,v[T+13]=c.ancestorMatrixB,v[T+14]=c.ancestorMatrixC,v[T+16]=c.ancestorMatrixD,v[T+17]=c.ancestorMatrixE,v[T+18]=c.ancestorMatrixF,v[T+20]=c.ancestorMatrixG,v[T+21]=c.ancestorMatrixH,v[T+22]=c.ancestorMatrixI,v[T+11]=c.parentViewportX,v[T+15]=c.parentViewportY,v[T+19]=c.parentViewportW,v[T+23]=c.parentViewportH,v[T+24]=c.minXST,v[T+25]=c.minYST,v[T+26]=c.minXPQ,v[T+27]=c.minYPQ,v[T+28]=c.maxXST,v[T+29]=c.maxYST,v[T+30]=c.maxXPQ,v[T+31]=c.maxYPQ,T=52),e&&(v[T]=i,v[T+1]=s,v[T+2]=r);const y=t.mediump;y[0]=_,y[1]=$,y[4]=u,y[5]=d,y[6]=g,y[7]=f,y[8]=p,y[9]=m,y[10]=x,y[11]=b}setMaskShapeUniform(t,e,i,s,r,n,a,h,o,l,c,_,$,u=null){const d=t.highp;e&&u?(d[0]=u.parentMatrixA,d[1]=u.parentMatrixB,d[2]=u.parentMatrixC,d[4]=u.parentMatrixD,d[5]=u.parentMatrixE,d[6]=u.parentMatrixF,d[8]=u.parentMatrixG,d[9]=u.parentMatrixH,d[10]=u.parentMatrixI,d[12]=u.ancestorMatrixA,d[13]=u.ancestorMatrixB,d[14]=u.ancestorMatrixC,d[16]=u.ancestorMatrixD,d[17]=u.ancestorMatrixE,d[18]=u.ancestorMatrixF,d[20]=u.ancestorMatrixG,d[21]=u.ancestorMatrixH,d[22]=u.ancestorMatrixI,d[3]=_,d[7]=$,d[11]=u.parentViewportX,d[15]=u.parentViewportY,d[19]=u.parentViewportW,d[23]=u.parentViewportH,d[24]=u.minXST,d[25]=u.minYST,d[26]=u.minXPQ,d[27]=u.minYPQ,d[28]=u.maxXST,d[29]=u.maxYST,d[30]=u.maxXPQ,d[31]=u.maxYPQ):(d[0]=i,d[1]=s,d[2]=r,d[4]=n,d[5]=a,d[6]=h,d[8]=o,d[9]=l,d[10]=c,d[3]=_,d[7]=$)}setMaskShapeUniformIdentity(t,e,i){const s=t.highp;s[0]=1,s[1]=0,s[2]=0,s[4]=0,s[5]=1,s[6]=0,s[8]=0,s[9]=0,s[10]=1,s[3]=e,s[7]=i}}class Ki{static TEMPLATE(t,e,i,s,r){const n=i?this.STATEMENT_GRADIENT_TYPE_RADIAL(e,s):this.STATEMENT_GRADIENT_TYPE_LINEAR(e);let a;switch(r){case"reflect":a="1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)";break;case"repeat":a="fract(t)";break;default:a="clamp(t, 0.0, 1.0)"}return`#version 300 es\nprecision highp float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_highp[${t}];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 p = v_uv;\n ${n}\n t = ${a};\n o_color = texture(u_texture, vec2(t, 0.5));\n}\n\n`}static STATEMENT_GRADIENT_TYPE_LINEAR(t){return`\n vec2 a = u_highp[${t}].xy;\n vec2 b = u_highp[${t}].zw;\n\n vec2 ab = b - a;\n vec2 ap = p - a;\n\n float t = dot(ab, ap) / dot(ab, ab);\n`}static STATEMENT_GRADIENT_TYPE_RADIAL(t,e){return`\n float radius = u_highp[${t}][0];\n\n vec2 coord = p / radius;\n ${e?this.STATEMENT_FOCAL_POINT_ON(t):this.STATEMENT_FOCAL_POINT_OFF()}\n`}static STATEMENT_FOCAL_POINT_OFF(){return"\n float t = length(coord);\n"}static STATEMENT_FOCAL_POINT_ON(t){return`\n vec2 focal = vec2(u_highp[${t}][1], 0.0);\n\n vec2 dir = normalize(coord - focal);\n\n float a = dot(dir, dir);\n float b = 2.0 * dot(dir, focal);\n float c = dot(focal, focal) - 1.0;\n float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);\n\n float t = distance(focal, coord) / distance(focal, focal + dir * x);\n`}}class Qi{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=ct()}getGradientShapeShader(t,e,i,s,r){const n=this.createCollectionKey(t,e,i,s,r);if(this._$collection.has(n)){const t=this._$collection.get(n);if(t)return t}const a=(e?13:5)+(t?1:0)+1,h=a-1;let o;o=t?qi.TEMPLATE(a,h,!0,e):Yi.TEMPLATE(a,!0,!1,e);const l=new zi(this._$gl,this._$context,o,Ki.TEMPLATE(a,h,i,s,r));return this._$collection.set(n,l),l}createCollectionKey(t,e,i,s,r){const n=t?"y":"n",a=e?"y":"n",h=i?"y":"n",o=i&&s?"y":"n";let l=0;switch(r){case"reflect":l=1;break;case"repeat":l=2}return`${n}${a}${h}${o}${l}`}setGradientShapeUniform(t,e,i,s,r,n,a,h,o,l,c,_,$,u){const d=t.highp;d[0]=a[0],d[1]=a[1],d[2]=a[2],d[4]=a[3],d[5]=a[4],d[6]=a[5],d[8]=a[6],d[9]=a[7],d[10]=a[8],d[12]=h[0],d[13]=h[1],d[14]=h[2],d[16]=h[3],d[17]=h[4],d[18]=h[5],d[11]=h[6],d[15]=h[7],d[19]=h[8],d[3]=o,d[7]=l;let g=20;n&&(d[g]=c.parentMatrixA,d[g+1]=c.parentMatrixB,d[g+2]=c.parentMatrixC,d[g+4]=c.parentMatrixD,d[g+5]=c.parentMatrixE,d[g+6]=c.parentMatrixF,d[g+8]=c.parentMatrixG,d[g+9]=c.parentMatrixH,d[g+10]=c.parentMatrixI,d[g+12]=c.ancestorMatrixA,d[g+13]=c.ancestorMatrixB,d[g+14]=c.ancestorMatrixC,d[g+16]=c.ancestorMatrixD,d[g+17]=c.ancestorMatrixE,d[g+18]=c.ancestorMatrixF,d[g+20]=c.ancestorMatrixG,d[g+21]=c.ancestorMatrixH,d[g+22]=c.ancestorMatrixI,d[g+11]=c.parentViewportX,d[g+15]=c.parentViewportY,d[g+19]=c.parentViewportW,d[g+23]=c.parentViewportH,d[g+24]=c.minXST,d[g+25]=c.minYST,d[g+26]=c.minXPQ,d[g+27]=c.minYPQ,d[g+28]=c.maxXST,d[g+29]=c.maxYST,d[g+30]=c.maxXPQ,d[g+31]=c.maxYPQ,g=52),e&&(d[g]=i,d[g+1]=s,d[g+2]=r,g+=4),_?(d[g]=$[5],d[g+1]=u):(d[g]=$[0],d[g+1]=$[1],d[g+2]=$[2],d[g+3]=$[3])}}class Ji{static TEXTURE(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 position = a_vertex * 2.0 - 1.0;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n\n"}static BLEND(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[4];\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 offset = u_highp[0].xy;\n vec2 size = u_highp[0].zw;\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * size + offset;\n position = (matrix * vec3(position, 1.0)).xy;\n position /= viewport;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static INSTANCE_BLEND(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[5];\n\nout vec2 v_src_coord;\nout vec2 v_dst_coord;\n\nvoid main() {\n vec4 rect = vec4(u_highp[0].x, u_highp[0].y, u_highp[0].z, u_highp[0].w);\n vec2 size = vec2(u_highp[4].x, u_highp[4].y);\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n v_src_coord = a_vertex * rect.zw + rect.xy;\n v_dst_coord = a_vertex;\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * size;\n position = (matrix * vec3(position, 1.0)).xy;\n position /= viewport;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static INSTANCE(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\nlayout (location = 1) in vec4 a_rect;\nlayout (location = 2) in vec4 a_size;\nlayout (location = 3) in vec2 a_offset;\nlayout (location = 4) in vec4 a_matrix;\nlayout (location = 5) in vec4 a_mul;\nlayout (location = 6) in vec4 a_add;\n\nout vec2 v_coord;\nout vec4 mul;\nout vec4 add;\n\nvoid main() {\n v_coord = a_vertex * a_rect.zw + a_rect.xy;\n mul = a_mul;\n add = a_add;\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * a_size.xy;\n mat3 matrix = mat3(a_matrix.x, a_matrix.y, 0.0, a_matrix.z, a_matrix.w, 0.0, a_offset.x, a_offset.y, 1.0);\n position = (matrix * vec3(position, 1.0)).xy;\n position /= a_size.zw;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static BLEND_CLIP(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[4];\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 offset = u_highp[0].xy;\n vec2 size = u_highp[0].zw;\n mat3 inv_matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position *= viewport;\n position = (inv_matrix * vec3(position, 1.0)).xy;\n position = (position - offset) / size;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}}class Zi{static TEMPLATE(t,e,i){let s="";for(let t=1;t>16)/255,h[a++]=(e>>8&255)/255,h[a++]=(255&e)/255,h[a++]=s[t]}for(let t=r;tthis._$vertexBufferData.length){const t=new A(2*this._$vertexBufferData.length);t.set(this._$vertexBufferData),this._$vertexBufferData=t}}static _$expandIndexBufferIfNeeded(t){if(this._$indexBufferPos+t>this._$indexBufferData.length){const t=new S(2*this._$indexBufferData.length);t.set(this._$indexBufferData),this._$indexBufferData=t}}static _$generateLineSegment(t){const e=t.length-5;for(let i=0;it*s-i*e;class ds{constructor(t){this._$gl=t,this._$fillVertexArrayPool=[],this._$strokeVertexArrayPool=[],this._$boundVertexArray=null,this._$fillAttrib_vertex=0,this._$fillAttrib_bezier=1,this._$strokeAttrib_vertex=0,this._$strokeAttrib_option1=1,this._$strokeAttrib_option2=2,this._$strokeAttrib_type=3,this._$vertexBufferData=new Float32Array([0,0,0,1,1,0,1,1]),this._$attributeVertexBuffer=t.createBuffer(),this._$attributeBuffer=new Float32Array(22),this._$instanceVertexArray=this._$getCommonVertexArray(),this._$commonVertexArray=this._$getVertexArray(0,1)}_$getCommonVertexArray(){const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,this._$attributeVertexBuffer),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(1,4,this._$gl.FLOAT,!1,88,0),this._$gl.vertexAttribDivisor(1,1),this._$gl.enableVertexAttribArray(2),this._$gl.vertexAttribPointer(2,4,this._$gl.FLOAT,!1,88,16),this._$gl.vertexAttribDivisor(2,1),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(3,2,this._$gl.FLOAT,!1,88,32),this._$gl.vertexAttribDivisor(3,1),this._$gl.enableVertexAttribArray(4),this._$gl.vertexAttribPointer(4,4,this._$gl.FLOAT,!1,88,40),this._$gl.vertexAttribDivisor(4,1),this._$gl.enableVertexAttribArray(5),this._$gl.vertexAttribPointer(5,4,this._$gl.FLOAT,!1,88,56),this._$gl.vertexAttribDivisor(5,1),this._$gl.enableVertexAttribArray(6),this._$gl.vertexAttribPointer(6,4,this._$gl.FLOAT,!1,88,72),this._$gl.vertexAttribDivisor(6,1),t}_$getVertexArray(t,e){const i=this._$gl.createVertexArray();this.bind(i);const s=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s),this._$vertexBufferData[0]=t,this._$vertexBufferData[2]=t,this._$vertexBufferData[4]=e,this._$vertexBufferData[6]=e,this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$vertexBufferData,this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),i}_$getFillVertexArray(){if(this._$fillVertexArrayPool.length){const t=this._$fillVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(this._$fillAttrib_vertex,2,this._$gl.FLOAT,!1,16,0),this._$gl.vertexAttribPointer(this._$fillAttrib_bezier,2,this._$gl.FLOAT,!1,16,8),t}_$getStrokeVertexArray(){if(this._$strokeVertexArrayPool.length){const t=this._$strokeVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e);const i=this._$gl.createBuffer();return t.indexBuffer=i,t.indexLength=0,this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER,i),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.enableVertexAttribArray(2),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(this._$strokeAttrib_vertex,2,this._$gl.FLOAT,!1,28,0),this._$gl.vertexAttribPointer(this._$strokeAttrib_option1,2,this._$gl.FLOAT,!1,28,8),this._$gl.vertexAttribPointer(this._$strokeAttrib_option2,2,this._$gl.FLOAT,!1,28,16),this._$gl.vertexAttribPointer(this._$strokeAttrib_type,1,this._$gl.FLOAT,!1,28,24),t}createFill(t){const e=$s.generate(t),i=e.vertexBufferData,s=this._$getFillVertexArray();return s.indexRanges=e.indexRanges,this.bind(s),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s.vertexBuffer),s.vertexLengththis._$attributeBuffer.length&&(this._$attributeBuffer=new Float32Array(t.attributes.length),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW)),this._$attributeBuffer.set(t.attributes),this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER,0,this._$attributeBuffer.subarray(0,t.attributes.length))}bindCommonVertexArray(){this.bind(this._$commonVertexArray)}bindGradientVertexArray(t,e){const i=this._$getVertexArray(t,e);this.bind(i)}}class gs{constructor(t,e){this._$context=t,this._$gl=e,this._$clips=[],this._$poolClip=[],this._$clipStatus=!1,this._$containerClip=!1,this._$currentClip=!1}get containerClip(){return this._$containerClip}set containerClip(t){this._$containerClip=t}_$onClear(t){t&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0)}_$onBind(t){!t&&this._$currentClip?(this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1):t&&!this._$currentClip&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0,this._$endClipDef())}_$onClearRect(){this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1}_$enterClip(){this._$currentClip||(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0);const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");t.mask=!0,++t.clipLevel}_$beginClipDef(){const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.stencilMask(1<7&&(this._$unionStencilMask(e,a,h),n=e)}n>e+1&&this._$unionStencilMask(e,a,h)}_$unionStencilMask(t,e,i){const s=this._$context.path.createRectVertices(0,0,e,i),r=this._$context.vao.createFill(s);ot(s.pop()),ot(s);const n=this._$context.shaderList.shapeShaderVariants,a=n.getMaskShapeShader(!1,!1),h=a.uniform;n.setMaskShapeUniformIdentity(h,e,i);const o=r.indexRanges[0];this._$gl.stencilFunc(this._$gl.LEQUAL,1<this._$maxTextureSize?this._$maxTextureSize/i:1}drawInstacedArray(){this._$blend.drawInstacedArray()}clearInstacedArray(){this._$blend.clearInstacedArray()}bindRenderBuffer(t){this._$frameBufferManager.bindRenderBuffer(),this._$gl.clearColor(0,0,0,0),this._$gl.clear(this._$gl.COLOR_BUFFER_BIT|this._$gl.STENCIL_BUFFER_BIT),this._$viewportWidth=t.w,this._$viewportHeight=t.h,this._$gl.viewport(t.x,t.y,t.w,t.h),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(t.x,t.y,t.w,t.h)}getTextureFromRect(t){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t.index),s=e.currentAttachment,r=e.createTextureAttachment(t.w,t.h);this._$bind(r),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(i,-t.x,-i.height+t.h+t.y,i.width,i.height),this.restore();const n=r.texture;return e.releaseAttachment(r),this._$bind(s),n}drawBitmap(t){const e=this._$shaderList.blendShaderVariants,i=e.getNormalBlendShader(!1);e.setNormalBlendUniform(i.uniform,0,0,t.width,t.height,this._$matrix,this._$viewportWidth,this._$viewportHeight,!1,1,1,1,1,0,0,0,0),this._$frameBufferManager.textureManager.bind0(t,this._$imageSmoothingEnabled),this._$blend.toOperation("normal"),i._$drawImage()}drawTextureFromRect(t,e){const i=this._$frameBufferManager,s=i.currentAttachment;this.bindRenderBuffer(e),i.transferTexture(e);const r=i.textureManager.getAtlasTexture(e.index),n=i.createTextureAttachmentFrom(r);this._$bind(n),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(e.x,e.y,e.w,e.h),this._$gl.clearColor(0,0,0,0),this._$gl.disable(this._$gl.SCISSOR_TEST),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(t,e.x,r.height-e.h-e.y,t.width,t.height),this.restore(),i.releaseAttachment(n),this._$bind(s),i.textureManager.release(t)}stopStencil(){this._$mask._$onClearRect()}_$bind(t=null){if(!t)return;this._$frameBufferManager.bind(t);const e=t.color,i=t.stencil,s=t.width,r=t.height;this._$viewportWidth===s&&this._$viewportHeight===r||(this._$viewportWidth=s,this._$viewportHeight=r,this._$gl.viewport(0,0,s,r)),(e&&e.dirty||i&&i.dirty)&&(e&&(e.dirty=!1),i&&(i.dirty=!1),this._$gl.clearColor(0,0,0,0),this.clearRect(0,0,this._$viewportWidth,this._$viewportHeight),this._$gl.clearColor(this._$clearColorR,this._$clearColorG,this._$clearColorB,this._$clearColorA),this._$mask._$onClear(t.mask)),this._$mask._$onBind(t.mask)}setTransform(t,e,i,s,r,n){this._$matrix[0]=t,this._$matrix[1]=e,this._$matrix[3]=i,this._$matrix[4]=s,this._$matrix[6]=r,this._$matrix[7]=n}setMaxSize(t,e){this._$frameBufferManager.setMaxSize(t,e)}transform(t,e,i,s,r,n){const a=this._$matrix[0],h=this._$matrix[1],o=this._$matrix[3],l=this._$matrix[4],c=this._$matrix[6],_=this._$matrix[7];this._$matrix[0]=t*a+e*o,this._$matrix[1]=t*h+e*l,this._$matrix[3]=i*a+s*o,this._$matrix[4]=i*h+s*l,this._$matrix[6]=r*a+n*o+c,this._$matrix[7]=r*h+n*l+_}debug(t=0){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t),s=e.currentAttachment,r=e.createTextureAttachmentFrom(i);this._$bind(r);const n=new Uint8Array(i.width*i.height*4);this._$gl.readPixels(0,0,i.width,i.height,this._$gl.RGBA,this._$gl.UNSIGNED_BYTE,n);const a=document.createElement("canvas");a.width=i.width,a.height=i.height;const h=a.getContext("2d"),o=new ImageData(i.width,i.height);for(let t=0;ts.length||e.push(s)}if(!e.length)return void ot(e);const i=this._$vao.createFill(e),s=this.fillStyle;let r,n,a,h=this._$matrix;const o=this._$grid.enabled;if(s instanceof Ii){const t=s.stops,e="linearRGB"===s.rgb;if(r=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(r,!0),this._$frameBufferManager.bindRenderBuffer(),n=this._$shaderList.gradientShapeShaderVariants,"linear"===s.type)a=n.getGradientShapeShader(!1,o,!1,!1,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,s.points,0);else{h=this._$stack[this._$stack.length-1];const t=0!==s.focalPointRatio;a=n.getGradientShapeShader(!1,o,!0,t,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,s.points,s.focalPointRatio)}}else if(s instanceof Fi){h=this._$stack[this._$stack.length-1];const t=s.colorTransform;r=s.texture,this._$frameBufferManager.textureManager.bind0(r,this._$imageSmoothingEnabled),n=this._$shaderList.shapeShaderVariants,a=n.getBitmapShapeShader(!1,s.repeat,o),t?n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,1,1,1,this._$globalAlpha,0,0,0,0)}else n=this._$shaderList.shapeShaderVariants,a=n.getSolidColorShapeShader(!1,this._$grid.enabled),n.setSolidColorShapeUniform(a.uniform,!1,0,0,0,o,h,this._$viewportWidth,this._$viewportHeight,this._$grid,s,this._$globalAlpha);const l=this._$shaderList.shapeShaderVariants,c=l.getMaskShapeShader(!1,o);l.setMaskShapeUniform(c.uniform,o,h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],this._$viewportWidth,this._$viewportHeight,this._$grid),this._$gl.enable(this._$gl.STENCIL_TEST),this._$gl.stencilMask(255),this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.colorMask(!1,!1,!1,!1),c._$fill(i),this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.NOTEQUAL,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.ZERO,this._$gl.ZERO),this._$gl.colorMask(!0,!0,!0,!0),a._$fill(i),this._$gl.disable(this._$gl.STENCIL_TEST),this.releaseFillVertexArray(i)}releaseFillVertexArray(t){this._$vao.releaseFill(t);const e=t.indexRanges;for(let t=0;tn.width||i>n.height||0>e&&0>=s+e||0>i&&0>=r+i||(this._$maskBounds.xMin=v.max(0,v.min(this._$maskBounds.xMin,e)),this._$maskBounds.yMin=v.max(0,v.min(this._$maskBounds.yMin,i)),this._$maskBounds.xMax=v.min(n.width,v.min(this._$maskBounds.xMax,s)),this._$maskBounds.yMax=v.min(n.height,v.min(this._$maskBounds.yMax,r)),0))}_$endClipDef(){this._$mask._$endClipDef()}_$leaveClip(){this.drawInstacedArray(),this._$mask._$leaveClip()}_$drawContainerClip(){this._$mask._$drawContainerClip()}closePath(){this._$path.close()}stroke(){const t=this._$path.vertices;if(!t.length)return;const e=ht();for(let i=0;is.length||e.push(s)}if(!e.length)return void ot(e);const i=this._$vao.createStroke(t,this.lineCap,this.lineJoin);let s=this._$matrix;const r=this.strokeStyle;let n=v.sign(s[0]*s[4]);n>0&&0!==s[1]&&0!==s[3]&&(n=-v.sign(s[1]*s[3]));let a,h,o=.5*this.lineWidth;this._$grid.enabled?(a=v.abs(this._$grid.ancestorMatrixA+this._$grid.ancestorMatrixD),h=v.abs(this._$grid.ancestorMatrixB+this._$grid.ancestorMatrixE)):(a=v.abs(s[0]+s[3]),h=v.abs(s[1]+s[4]));const l=v.min(a,h),c=v.max(a,h);o*=c*(1-.3*v.cos(.5*v.PI*(l/c))),o=v.max(1,o);const _=this._$grid.enabled;let $,u,d;if(r instanceof Ii){"radial"===r.type&&(s=this._$stack[this._$stack.length-1]);const t=r.stops,e="linearRGB"===r.rgb;if($=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0($,!0),u=this._$shaderList.gradientShapeShaderVariants,"linear"===r.type)d=u.getGradientShapeShader(!0,_,!1,!1,r.mode),u.setGradientShapeUniform(d.uniform,!0,o,n,this.miterLimit,_,s,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,r.points,0);else{s=this._$stack[this._$stack.length-1];const t=0!==r.focalPointRatio;d=u.getGradientShapeShader(!0,_,!0,t,r.mode),u.setGradientShapeUniform(d.uniform,!0,o,n,this.miterLimit,_,s,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,r.points,r.focalPointRatio)}}else if(r instanceof Fi){s=this._$stack[this._$stack.length-1];const t=r.colorTransform;$=r.texture,this._$frameBufferManager.textureManager.bind0($),u=this._$shaderList.shapeShaderVariants,d=u.getBitmapShapeShader(!0,r.repeat,this._$grid.enabled),t?u.setBitmapShapeUniform(d.uniform,!0,o,n,this.miterLimit,_,s,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,$.width,$.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):u.setBitmapShapeUniform(d.uniform,!0,o,n,this.miterLimit,_,s,ut(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,$.width,$.height,1,1,1,this._$globalAlpha,0,0,0,0)}else u=this._$shaderList.shapeShaderVariants,d=u.getSolidColorShapeShader(!0,this._$grid.enabled),u.setSolidColorShapeUniform(d.uniform,!0,o,n,this.miterLimit,_,s,this._$viewportWidth,this._$viewportHeight,this._$grid,r,this._$globalAlpha);d._$stroke(i),this._$vao.releaseStroke(i)}arc(t,e,i){this._$path.drawCircle(t,e,i)}clip(){const t=this._$path.vertices;if(!t.length)return;const e=ht();for(let i=0;is.length||e.push(s)}if(!e.length)return void ot(e);const i=this._$vao.createFill(e),s=this._$shaderList.shapeShaderVariants,r=s.getMaskShapeShader(!1,!1),n=r.uniform;s.setMaskShapeUniform(n,!1,this._$matrix[0],this._$matrix[1],this._$matrix[2],this._$matrix[3],this._$matrix[4],this._$matrix[5],this._$matrix[6],this._$matrix[7],this._$matrix[8],this._$viewportWidth,this._$viewportHeight,null),this._$mask._$onClip(i,this._$matrix,this._$viewportWidth,this._$viewportHeight)||(r._$fill(i),this.beginPath())}save(){const t=this._$matrix;this._$stack.push(at(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])),this._$mask._$onSave()}restore(){var t;this._$stack.length&&(t=this._$matrix,Y.push(t),this._$matrix=this._$stack.pop()||at()),this._$mask._$onRestore()}createPattern(t,e,i){return new Fi(t,e,i)}createLinearGradient(t,e,i,s,r="rgb",n="pad"){return(new Ii).linear(t,e,i,s,r,n)}createRadialGradient(t,e,i,s,r,n,a="rgb",h="pad",o=0){return(new Ii).radial(t,e,i,s,r,n,a,h,o)}_$applyBlurFilter(t,e,i){const s=this._$frameBufferManager,r=s.currentAttachment;if(!r)throw new Error("the current attachment is null.");const n=r.width,a=r.height;s.textureManager.bind0(t,!0);const h=v.ceil(.5*i),o=1-(h-.5*i),l=1+i,c=this._$shaderList.filterShaderVariants,_=c.getBlurFilterShader(h);c.setBlurFilterUniform(_.uniform,n,a,e,o,l),_._$drawImage()}_$applyBitmapFilter(t,e,i,s,r,n,a,h,o,l,c,_,$,u,d,g=null,f=null,p=null,m=0,x=0,b=0,v=0,T=0,y=0,E=0,A=0){const M=this._$frameBufferManager,S="inner"===$,w=M.currentAttachment,C=M.getTextureFromCurrentAttachment();let I=null;const F=null!==g&&null!==f&&null!==p;let R;null!==g&&null!==f&&null!==p&&(I=this._$gradientLUT.generateForFilter(g,f,p)),S?F&&I?M.textureManager.bind02(t,I,!0):M.textureManager.bind0(t):(R=this._$frameBufferManager.createTextureAttachment(e,i),this._$bind(R),F&&I?M.textureManager.bind012(t,C,I,!0):M.textureManager.bind01(t,C));const B=!(S||"full"===$&&u),L=!(e===h&&i===o&&0===l&&0===c),P=!(1===d),k=this._$shaderList.filterShaderVariants,N=k.getBitmapFilterShader(B,L,_,$,u,P,F);k.setBitmapFilterUniform(N.uniform,e,i,s,r,n,a,h,o,l,c,_,d,m,x,b,v,T,y,E,A,B,L,P,F),S?u?this._$blend.toSourceIn():this._$blend.toSourceAtop():this._$blend.toOneZero(),N._$drawImage(),S||M.releaseAttachment(w,!0)}_$applyColorMatrixFilter(t,e){this._$frameBufferManager.textureManager.bind0(t,!0);const i=this._$shaderList.filterShaderVariants,s=i.getColorMatrixFilterShader();i.setColorMatrixFilterUniform(s.uniform,e),this._$blend.reset(),s._$drawImage()}_$applyConvolutionFilter(t,e,i,s,r,n,a,h,o,l,c,_){const $=t.width,u=t.height,d=this._$frameBufferManager.createTextureAttachment($,u);this._$bind(d),this._$frameBufferManager.textureManager.bind0(t,!0);const g=this._$shaderList.filterShaderVariants,f=g.getConvolutionFilterShader(e,i,a,h);g.setConvolutionFilterUniform(f.uniform,$,u,s,r,n,h,o,l,c,_),this._$blend.reset(),f._$drawImage()}_$applyDisplacementMapFilter(t,e,i,s,r,n,a,h,o,l,c,_,$,u){const d=t.width,g=t.height,f=this._$frameBufferManager.createTextureAttachment(d,g);this._$bind(f),r||(r={x:0,y:0});const p=this._$frameBufferManager.createTextureFromImage(e);this._$frameBufferManager.textureManager.bind01(t,p);const m=this._$shaderList.filterShaderVariants,x=m.getDisplacementMapFilterShader(n,a,l);m.setDisplacementMapFilterUniform(x.uniform,e.width,e.height,i,s,r.x,r.y,h,o,l,c,_,$,u),this._$blend.reset(),x._$drawImage(),this._$frameBufferManager.releaseTexture(p)}_$startLayer(t){this._$positions.push(t),this._$blends.push(this._$isLayer),this._$isLayer=!0}_$endLayer(){const t=this._$positions.pop();t&&J(t),this._$isLayer=!!this._$blends.pop()}_$saveAttachment(t,e,i=!1){this.drawInstacedArray();const s=this._$frameBufferManager;this._$attachmentArray.push(s.currentAttachment),this._$bind(s.createCacheAttachment(t,e,i))}_$restoreAttachment(t=!1){const e=this._$frameBufferManager;e.releaseAttachment(e.currentAttachment,t),this._$bind(this._$attachmentArray.pop())}getCurrentPosition(){return this._$positions[this._$positions.length-1]}textureScale(t,e){const i=v.max(t,e);return i>this._$maxTextureSize?this._$maxTextureSize/i:1}}class ms{constructor(){var t;t=window.devicePixelRatio,f=t,this._$stage=new fe,this._$stage._$player=this,this._$mode="loader",this._$actionOffset=0,this._$actions=ht(),this._$loaders=ht(),this._$sounds=ct(),this._$hitObject={x:0,y:0,pointer:"",hit:null},this._$rollOverObject=null,this._$mouseOverTarget=null,this._$ratio=f,this._$stopFlag=!0,this._$startTime=0,this._$fps=16,this._$loadStatus=0,this._$width=0,this._$height=0,this._$baseWidth=0,this._$baseHeight=0,this._$scale=1,this._$matrix=it(1,0,0,1,0,0),this._$tx=0,this._$ty=0,this._$state="up",this._$hitTestStart=!1,this._$stageX=-1,this._$stageY=-1,this._$deltaX=0,this._$deltaY=0,this._$broadcastEvents=ct(),this._$optionWidth=0,this._$optionHeight=0,this._$tagId="",this._$bgColor="transparent",this._$base="",this._$fullScreen=!1,this._$quality="high",this._$sources=ht(),this._$videos=ht(),this._$textField=null,this._$timerId=-1,this._$loadId=-1,this._$context=null,this._$attachment=null,this._$clickTarget=null,this._$actionProcess=!1,this._$canvas=d.createElement("canvas")}static get LOAD_START(){return 1}static get LOAD_END(){return 2}get cacheStore(){return St}get canvas(){return this._$canvas}get broadcastEvents(){return this._$broadcastEvents}get context(){return this._$context}set context(t){this._$context=t}get base(){return this._$base}set base(t){if(-1===t.indexOf("//")){const e=t.split("/");""!==e[0]&&"."!==e[0]||e.shift(),e.pop(),this._$base=`${location.origin}/`,e.length&&(this._$base+=`${e.join("/")}/`)}else if(-1===t.indexOf("?"))this._$base="/"===t.slice(-1)?t:`${t}/`;else{const e=t.split("?")[0];this._$base="/"===e.slice(-1)?e:`${e}/`}}get stage(){return this._$stage}get x(){return this._$tx}get y(){return this._$ty}get scaleX(){return this._$matrix[0]}get scaleY(){return this._$matrix[3]}get tx(){return this._$matrix[4]/this._$scale/f}get ty(){return this._$matrix[5]/this._$scale/f}get mode(){return this._$mode}set mode(t){this._$mode=t}get contentElementId(){return Ss}get width(){return this._$baseWidth}set width(t){this._$baseWidth=0|t}get height(){return this._$baseHeight}set height(t){this._$baseHeight=0|t}get bgColor(){return this._$bgColor}set bgColor(t){this._$bgColor=`${t}`}play(){if(this._$stopFlag){this._$stopFlag=!1,this._$timerId>-1&&F(this._$timerId),this._$startTime=R.now();const t=this._$stage._$frameRate;this._$fps=1e3/t|0,this._$timerId=I((t=>{this._$run(t)}))}}stop(){this._$timerId>-1&&F(this._$timerId),this._$stopFlag=!0,this._$timerId=-1,oe.stopAll(),St.reset(),Mr&&Mr.postMessage({command:"stop"})}removeCache(t){St.removeCache(t),Mr&&Mr.postMessage({command:"removeCache",id:t})}setOptions(t=null){t&&(this._$optionWidth=t.width||this._$optionWidth,this._$optionHeight=t.height||this._$optionHeight,this._$tagId=t.tagId||this._$tagId,this.base=t.base||this._$base,this._$bgColor=t.bgColor||this._$bgColor,this._$fullScreen=!!t.fullScreen)}_$loadWebAudio(t=null){t&&this._$canvas.removeEventListener(Bs,this._$loadWebAudio),Ns||mr()}_$updateLoadStatus(){if(this._$loadStatus===ms.LOAD_END)return this._$loadId>-1&&F(this._$loadId),this._$loadId=-1,void this._$loaded();this._$loadId=I((()=>{this._$updateLoadStatus()}))}_$loaded(){const t=d.getElementById(this.contentElementId);if(t){this._$setBackgroundColor(this._$bgColor),this._$deleteNode(),t.appendChild(this._$canvas),t.appendChild(tr),this._$stage._$prepareActions(),this._$broadcastEvents.has(It.FRAME_CONSTRUCTED)&&this._$dispatchEvent(new It(It.FRAME_CONSTRUCTED)),this._$doAction(),this._$broadcastEvents.has(It.EXIT_FRAME)&&this._$dispatchEvent(new It(It.EXIT_FRAME));const e=this._$loaders.length;for(let t=0;t`);const e=d.getElementById(t);if(!e)throw new Error("the content element is null.");const i=e.parentElement;if(i){this._$initStyle(e),this._$buildWait();const t=this._$optionWidth?this._$optionWidth:"BODY"===i.tagName?u.innerWidth:i.offsetWidth,s=this._$optionHeight?this._$optionHeight:"BODY"===i.tagName?u.innerHeight:i.offsetHeight;"loader"===this._$mode&&t&&s&&(this._$baseWidth=t,this._$baseHeight=s,this._$resize())}"loader"===this._$mode?(this._$loadStatus=ms.LOAD_START,this._$updateLoadStatus()):(this._$resize(),this._$loaded())}_$initStyle(t){const e=t.style;e.position="relative",e.top="0",e.left="0",e.backgroundColor="transparent",e.overflow="hidden",e.padding="0",e.margin="0",e.userSelect="none",e.outline="none";const i=this._$optionWidth,s=this._$optionHeight,r=t.parentElement;if(!r)throw new Error("the parentElement is null.");if("BODY"===r.tagName)return e.width=i?`${i}px`:`${window.innerWidth}px`,void(e.height=s?`${s}px`:`${window.innerHeight}px`);e.width=i?`${i}px`:`${r.offsetWidth}px`,e.height=s?`${s}px`:`${r.offsetHeight}px`}_$buildWait(){const t=d.getElementById(this.contentElementId);if(t){const e=`${this.contentElementId}_loading`;t.innerHTML=``;const i=d.createElement("div");i.id=e,t.appendChild(i)}}_$deleteNode(){const t=d.getElementById(this.contentElementId);if(t)for(;t.childNodes.length;)t.removeChild(t.childNodes[0])}_$initializeCanvas(){if(this._$canvas.width=1,this._$canvas.height=1,Mr){const t=this._$canvas.transferControlToOffscreen(),e=hr();let i=0;e[i++]=this._$stage._$instanceId,e[i++]=+Ws,e[i++]=f,e[i++]=this._$getSamples();const s=ht(t,e.buffer);Mr.postMessage({command:"initialize",canvas:t,buffer:e},s),ot(s)}else{const t=this._$canvas.getContext("webgl2",{stencil:!0,premultipliedAlpha:!0,antialias:!1,depth:!1,preserveDrawingBuffer:!0});t?(this._$context=new ps(t,this._$getSamples()),St.context=this._$context):alert("WebGL setting is off. Please turn the setting on.")}const t=()=>{if(this._$canvas.removeEventListener(Bs,t),this._$canvas.removeEventListener(Is,t),!Ns){mr();for(let t=0;t{c(t),h(ws),this._$hitTest()})),this._$canvas.addEventListener(Cs,(t=>{c(t),h(Cs),this._$hitTest()})),this._$canvas.addEventListener(Is,(t=>{c(t),h(Is),this._$hitTest()})),this._$canvas.addEventListener(Cs,(t=>{c(t),h(Cs),this._$hitTest()}),{passive:!1}),this._$canvas.addEventListener(Fs,(t=>{c(t),h(Fs),t.button||this._$hitTest()})),this._$canvas.addEventListener(Ps,(t=>{c(t),h(Ps),t.button||this._$hitTest()})),this._$canvas.addEventListener(ks,(t=>{c(t),h(ks),this._$hitTest(),c(null),this._$stageX=-1,this._$stageY=-1})),this._$canvas.addEventListener(Bs,(t=>{c(t),h(Bs),t.button||this._$hitTest()})),this._$canvas.addEventListener(Rs,(t=>{c(t),h(Rs),this._$hitTest()})),this._$canvas.addEventListener(Ls,(t=>{t.defaultPrevented||(c(t),h(Ls),this._$hitTest())}),{passive:!1});let e="";e+="position: absolute;",e+="top: 0;",e+="left: 0;",e+="-webkit-tap-highlight-color: rgba(0,0,0,0);",e+="backface-visibility: hidden;",e+="transform-origin: 0 0;",1!==f&&(e+=`transform: scale(${1/f});`),this._$canvas.setAttribute("style",e)}_$resize(){const t=d.getElementById(this.contentElementId);if(t){const e=t.parentElement;if(!e)throw new Error("the parentElement is null.");const i=this._$optionWidth?this._$optionWidth:"BODY"===e.tagName?u.innerWidth:e.offsetWidth?e.offsetWidth:parseFloat(e.style.width),s=this._$optionHeight?this._$optionHeight:"BODY"===e.tagName?u.innerHeight:e.offsetHeight?e.offsetHeight:parseFloat(e.style.height),r="BODY"===e.tagName?u.innerWidth:e.offsetWidth,n=v.min(i/this._$baseWidth,s/this._$baseHeight);let a=this._$fullScreen?i:this._$baseWidth*n|0,h=this._$fullScreen?s:this._$baseHeight*n|0;const o=t.style;if(o.width=`${a}px`,o.height=`${h}px`,o.top="0",o.left=this._$fullScreen?"0":r/2-a/2+"px",a*=f,h*=f,this._$width===a&&this._$height===h)return;this._$stage._$doChanged(),St.reset(),this._$scale=n,this._$width=a,this._$height=h;const l=this._$scale*this._$ratio;this._$matrix[0]=l,this._$matrix[3]=l,this._$fullScreen&&(this._$tx=(a-this._$baseWidth*n*f)/2,this._$ty=(h-this._$baseHeight*n*f)/2,this._$matrix[4]=this._$tx,this._$matrix[5]=this._$ty),this._$resizeCanvas(a,h,l,this._$tx,this._$ty),this._$ratio>1&&f>1&&(this._$canvas.style.transform=`scale(${1/this._$ratio})`),t.children.length>1&&t.children[1].dispatchEvent(new Event(`${Ss}_blur`))}}_$setBackgroundColor(t="transparent"){if(Mr){const e=hr();e[0]="transparent"===t?-1:xt(t);const i=or();i.command="setBackgroundColor",i.buffer=e;const s=ht(e.buffer);Mr.postMessage(i,s),lr(i),ot(s)}else{const e=this._$context;if(!e)return;if("transparent"===t)e._$setColor(0,0,0,0);else{const i=bt(xt(t));e._$setColor(i.R/255,i.G/255,i.B/255,1)}}}_$resizeCanvas(t,e,i,s=0,r=0){if(Mr){const n=hr();let a=0;n[a++]=t,n[a++]=e,n[a++]=i,n[a++]=s,n[a++]=r;const h=or(),o=ht(n.buffer);h.command="resize",h.buffer=n,Mr.postMessage(h,o),lr(h),ot(o)}else{const i=this._$context;if(!i)return;i.clearInstacedArray(),this._$canvas.width=t,this._$canvas.height=e,i._$gl.viewport(0,0,t,e);const s=i.frameBuffer;this._$attachment&&(s.unbind(),s.releaseAttachment(this._$attachment,!0)),this._$attachment=s.createCacheAttachment(t,e,!0),i.setMaxSize(t,e),i._$bind(this._$attachment)}}_$getSamples(){switch(this._$quality){case"high":return 4;case"medium":return 2;default:return 0}}_$dispatchEvent(t){if(this._$broadcastEvents.size&&this._$broadcastEvents.has(t.type)){const e=this._$broadcastEvents.get(t.type).slice(0);t.eventPhase=Ct.AT_TARGET;for(let i=0;ithis._$fps){if(this._$startTime=t-e%this._$fps,this._$action(),this._$sounds.size){for(const t of this._$sounds.values())t._$soundPlay();this._$sounds.clear()}this._$draw(),!Zs&&!this._$hitTestStart&&"up"===this._$state&&this._$stageX>-1&&this._$stageY>-1&&l()&&this._$pointerCheck()}else this._$videos.length&&!Mr&&this._$draw();this._$timerId=I((t=>{this._$run(t)}))}_$pointerCheck(){const t=this._$stageX,e=this._$stageY;this._$hitObject.x=t,this._$hitObject.y=e,this._$hitObject.pointer="",this._$hitObject.hit=null,rr.setTransform(1,0,0,1,0,0),rr.beginPath(),zs[4]=this._$tx/this._$scale/f,zs[5]=this._$ty/this._$scale/f,this._$stage._$mouseHit(rr,zs,this._$hitObject,!0);let i=null,s=null,r=!1,n=!1;if(this._$hitObject.hit){if(i=this._$hitObject.hit,this._$mouseOverTarget&&this._$mouseOverTarget!==i){const t=this._$mouseOverTarget;t.willTrigger(Pt.MOUSE_OUT)&&t.dispatchEvent(new Pt(Pt.MOUSE_OUT,!0,!1))}if(this._$rollOverObject!==i){let r=null;if(this._$rollOverObject)for(s=this._$rollOverObject,s.willTrigger(Pt.ROLL_OUT)&&s.dispatchEvent(new Pt(Pt.ROLL_OUT,!1,!1)),r=s._$parent;r&&r._$root!==r&&r!==i;){if(r._$mouseEnabled&&r._$outCheck(t,e)){let t=!1,e=i;for(;e&&e._$root!==e;){if(e===r){t=!0;break}e=e._$parent}if(!t&&r._$parent===i._$parent&&r._$index>i._$index&&(t=!0),t)break}r.willTrigger(Pt.ROLL_OUT)&&r.dispatchEvent(new Pt(Pt.ROLL_OUT,!1,!1)),r=r._$parent}for(s=i;s.willTrigger(Pt.ROLL_OVER)&&s.dispatchEvent(new Pt(Pt.ROLL_OVER,!1,!1)),s=s._$parent,s&&s!==r&&s.stage!==s;);}switch(this._$rollOverObject=i,!0){case null===this._$mouseOverTarget:case this._$mouseOverTarget!==i:i&&i.willTrigger(Pt.MOUSE_OVER)&&i.dispatchEvent(new Pt(Pt.MOUSE_OVER,!0,!1)),this._$mouseOverTarget=i}if("up"===this._$state&&(this._$clickTarget=null),!Zs&&"up"===this._$state)for(s=i;s&&s.root!==s;){if("_$text"in s&&"input"===s.type){r=!0;break}if("buttonMode"in s&&s.buttonMode){n=!0;break}s=s._$parent}}else{if(this._$mouseOverTarget&&(i=this._$mouseOverTarget,i.willTrigger(Pt.MOUSE_OUT)&&i.dispatchEvent(new Pt(Pt.MOUSE_OUT,!0,!1))),this._$rollOverObject)for(s=this._$rollOverObject;s&&s.root!==s;)s.willTrigger(Pt.ROLL_OUT)&&s.dispatchEvent(new Pt(Pt.ROLL_OUT,!1,!1)),s=s._$parent;this._$rollOverObject=null,this._$mouseOverTarget=null}switch(!0){case r:this._$canvas.style.cursor="text";break;case n:this._$canvas.style.cursor="pointer";break;case!Zs&&"up"===this._$state:this._$canvas.style.cursor="auto"}this._$actions.length>1&&this._$doAction()}_$action(){if(this._$stopFlag)return;let t=null;const e=this._$loaders.length;if(e){t=this._$loaders.slice(0),this._$loaders.length=0;for(let i=0;ie._$index&&(i=!0),i)break}t.willTrigger(Pt.ROLL_OUT)&&t.dispatchEvent(new Pt(Pt.ROLL_OUT,!1,!1)),t=t._$parent}for(i=e;i.willTrigger(Pt.ROLL_OVER)&&i.dispatchEvent(new Pt(Pt.ROLL_OVER,!1,!1)),i=i._$parent,i&&i!==t&&i.stage!==i;);}switch(this._$rollOverObject=e,!0){case null===this._$mouseOverTarget:case this._$mouseOverTarget!==e:e.willTrigger(Pt.MOUSE_OVER)&&e.dispatchEvent(new Pt(Pt.MOUSE_OVER,!0,!1)),this._$mouseOverTarget=e}"up"===this._$state?this._$clickTarget=null:this._$textField&&this._$textField._$setIndex(c-zs[4],_-zs[5]);break;case ws:case Fs:this._$textField&&e!==this._$textField&&(this._$textField.focus=!1,this._$textField=null),"_$text"in e&&(e.focus=!0,e._$setIndex(c-zs[4],_-zs[5]),this._$textField=e,tr.style.left=`${h}px`,tr.style.top=`${o}px`),e.willTrigger(Pt.MOUSE_DOWN)&&e.dispatchEvent(new Pt(Pt.MOUSE_DOWN,!0,!1)),this._$clickTarget=e;break;case Is:case Bs:e.willTrigger(Pt.MOUSE_UP)&&e.dispatchEvent(new Pt(Pt.MOUSE_UP,!0,!1)),this._$clickTarget===e&&e.willTrigger(Pt.CLICK)&&e.dispatchEvent(new Pt(Pt.CLICK,!0,!1)),this._$clickTarget=null;break;case Ls:e.willTrigger(Pt.MOUSE_WHEEL)&&e.dispatchEvent(new Pt(Pt.MOUSE_WHEEL)),e.scrollEnabled&&("deltaX"in t&&(e.scrollX+=t.deltaX/(e.textWidth/e.width)),"deltaY"in t&&(e.scrollY+=t.deltaY/(e.textHeight/e.height)));break;case Ps:e.willTrigger(Pt.DOUBLE_CLICK)&&e.dispatchEvent(new Pt(Pt.DOUBLE_CLICK))}if(!g&&!Zs&&"up"===this._$state)for(i=e;i&&i.root!==i;){if("_$text"in i){if("input"===i.type){$=!0;break}}else if(i._$buttonMode){p=!0;break}i=i._$parent}}switch(!0){case $:this._$canvas.style.cursor="text";break;case p:this._$canvas.style.cursor="pointer";break;case!Zs&&"up"===this._$state:this._$canvas.style.cursor="auto"}!this._$actionProcess&&this._$actions.length>1&&this._$doAction(),m&&(this._$stage._$prepareActions(),this._$actionProcess||this._$doAction()),this._$hitTestStart=!1}}const xs={Event:It,EventDispatcher:Ft,EventPhase:Ct,FocusEvent:Rt,HTTPStatusEvent:Bt,IOErrorEvent:Lt,MouseEvent:Pt,ProgressEvent:kt,VideoEvent:Nt};Object.entries(xs).forEach((([t,e])=>{Object.defineProperty(xs,t,{get:()=>e})}));const bs={DisplayObject:Zt,InteractiveObject:te,DisplayObjectContainer:ee,Sprite:$e,MovieClip:ue,BitmapData:ie,BlendMode:class{static toString(){return"[class BlendMode]"}static get namespace(){return"next2d.display.BlendMode"}toString(){return"[object BlendMode]"}get namespace(){return"next2d.display.BlendMode"}static get ADD(){return"add"}static get ALPHA(){return"alpha"}static get DARKEN(){return"darken"}static get DIFFERENCE(){return"difference"}static get ERASE(){return"erase"}static get HARDLIGHT(){return"hardlight"}static get INVERT(){return"invert"}static get LAYER(){return"layer"}static get LIGHTEN(){return"lighten"}static get MULTIPLY(){return"multiply"}static get NORMAL(){return"normal"}static get OVERLAY(){return"overlay"}static get SCREEN(){return"screen"}static get SUBTRACT(){return"subtract"}},FrameLabel:se,Graphics:ae,Loader:de,LoaderInfo:he,Shape:ge,Stage:fe,TextField:Ci};Object.entries(bs).forEach((([t,e])=>{Object.defineProperty(bs,t,{get:()=>e})}));const vs={BevelFilter:Xt,BlurFilter:zt,ColorMatrixFilter:qt,ConvolutionFilter:Yt,DisplacementMapFilter:Ht,DropShadowFilter:jt,GlowFilter:Wt,GradientBevelFilter:Kt,GradientGlowFilter:Qt};Object.entries(vs).forEach((([t,e])=>{Object.defineProperty(vs,t,{get:()=>e})}));const Ts={ColorTransform:Ot,Matrix:Ut,Point:Dt,Rectangle:Vt,Transform:Jt};Object.entries(Ts).forEach((([t,e])=>{Object.defineProperty(Ts,t,{get:()=>e})}));const ys={Sound:le,SoundMixer:oe,SoundTransform:ce,Video:_e};Object.entries(ys).forEach((([t,e])=>{Object.defineProperty(ys,t,{get:()=>e})}));const Es={URLRequest:wt,URLRequestHeader:g};Object.entries(Es).forEach((([t,e])=>{Object.defineProperty(Es,t,{get:()=>e})}));const As={TextFormat:be};Object.entries(As).forEach((([t,e])=>{Object.defineProperty(As,t,{get:()=>e})}));const Ms={Easing:pe,Job:me,Tween:xe};Object.entries(Ms).forEach((([t,e])=>{Object.defineProperty(Ms,t,{get:()=>e})}));const Ss="__next2d__",ws="touchstart",Cs="touchmove",Is="touchend",Fs="mousedown",Rs="mousemove",Bs="mouseup",Ls="wheel",Ps="dblclick",ks="mouseleave";let Ns=null;const Os=new Map;let Ds=null;const Us=t=>{Ds=t},Vs={lock:!1,position:{x:0,y:0},bounds:null},Gs=new Float32Array(256);new Float32Array(256);for(let t=0;t<256;++t)Gs[t]=v.pow(t/255,2.23333333),Gs[t]=t/255;const zs=new Float32Array([1,0,0,1,0,0]),Xs=[],qs=[],Ys=[],Hs=new Map;let js=!1,Ws=!1,Ks=!1,Qs=!1,Js=!1,Zs=!1;const tr=d.createElement("textarea");let er="";er+="position: fixed;",er+="top: 0;",er+="left: 0;",er+="font-size: 16px;",er+="border: 0;",er+="resize: none;",er+="opacity: 0;",er+="z-index: -1;",er+="pointer-events: none;",tr.setAttribute("style","position: fixed;top: 0;left: 0;font-size: 16px;border: 0;resize: none;opacity: 0;z-index: -1;pointer-events: none;"),tr.tabIndex=-1,tr.addEventListener("compositionstart",(()=>{const t=u.next2d.player._$textField;t&&t.compositionStart()})),tr.addEventListener("compositionupdate",(t=>{const e=u.next2d.player._$textField;e&&e.compositionUpdate(t.data)})),tr.addEventListener("compositionend",(()=>{const t=u.next2d.player._$textField;t&&t.compositionEnd()})),tr.addEventListener("input",(t=>{if(!t.data)return;const e=u.next2d.player._$textField;e&&e.insertText(t.data)})),tr.addEventListener("keydown",(t=>{const e=u.next2d.player._$textField;if(e)switch(t.key){case"Backspace":case"Delete":e.deleteText();break;case"Enter":e.insertText("\n");break;case"ArrowLeft":e.arrowLeft();break;case"ArrowRight":e.arrowRight();break;case"ArrowUp":e.arrowUp();break;case"ArrowDown":e.arrowDown();break;case"a":(t.metaKey||t.ctrlKey)&&(t.preventDefault(),e.selectAll());break;case"v":(t.metaKey||t.ctrlKey)&&(t.preventDefault(),e.paste());break;case"c":(t.metaKey||t.ctrlKey)&&(t.preventDefault(),e.copy())}}));const ir=d.createElement("canvas");ir.width=1,ir.height=1;const sr=ir.getContext("2d");if(!sr)throw new Error("the CanvasRenderingContext2D is null.");sr.globalAlpha=0,sr.imageSmoothingEnabled=!1;const rr=sr,nr=[],ar=[],hr=()=>nr.length?nr.pop():new Float32Array(64),or=()=>ar.length?ar.pop():{command:""},lr=t=>{t.buffer=null,ar.push(t),console.log("renderMessageArray: ",ar)},cr=()=>u.next2d.player,_r=()=>{const t=l();if(!t)return new Dt;const e=cr();let i=u.scrollX,s=u.scrollY;const r=d.getElementById(e.contentElementId);if(r){const t=r.getBoundingClientRect();i+=t.left,s+=t.top}let n=0,a=0;if("changedTouches"in t){const e=t.changedTouches[0];n=e.pageX,a=e.pageY}else"pageX"in t&&(n=t.pageX,a=t.pageY);const h=(n-i)/e._$scale-e.x/e._$scale/f,o=(a-s)/e._$scale-e.y/e._$scale/f;return new Dt(h,o)},$r=(t=1,e=0,i=0,s=1,r=0,n=0)=>{const a=qs.pop();return a?(a.setTo(t,e,i,s,r,n),a):new Ut(t,e,i,s,r,n)},ur=t=>{qs.push(t)},dr=(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0)=>{const o=Ys.length?Ys.pop():null;return o?(o.redMultiplier=t,o.greenMultiplier=e,o.blueMultiplier=i,o.alphaMultiplier=s,o.redOffset=r,o.greenOffset=n,o.blueOffset=a,o.alphaOffset=h,o):new Ot(t,e,i,s,r,n,a,h)},gr=t=>{Ys.push(t)},fr=(t,e)=>{t._$character?t._$character.audioBuffer=e:t._$audioBuffer=e},pr=t=>{if(!Ns)throw new Error("the AudioContext is null.");let e=null;if(t._$character){const i=t._$character.buffer;i&&(e=new Uint8Array(i).buffer,ot(i),t._$character.buffer=null)}else e=t._$arrayBuffer;return e?Ns.decodeAudioData(e).then((e=>(fr(t,e),Promise.resolve(t)))).catch((()=>{if(!e)throw new Error;return((t,e)=>{if(!Ns)throw new Error("the Audio Context is null.");const i=new Uint8Array(e);let s=0;for(;s=i.indexOf(255,s),-1!==s&&224&~i[s+1];)++s;if(s>-1)return Ns.decodeAudioData(i.subarray(s).buffer).then((e=>(fr(t,e),Promise.resolve(t)))).catch((()=>{throw new Error("This voice data is not available.")}));throw new Error("This voice data is not available.")})(t,e)})):Promise.resolve(t)},mr=()=>{if(Ns||(Ns=new AudioContext,Ns.resume()),Ns){const t=ht();for(let e=0;e{Xs.length=0,cr()._$loaders.push(...t)}))}};let xr=-1;const br=()=>{const t=cr();if(t._$loadStatus===ms.LOAD_END){t._$resize();const e=t.stage;e.willTrigger(It.RESIZE)&&e.dispatchEvent(new It(It.RESIZE))}};u.addEventListener("resize",(()=>{L(xr),xr=B(br,300)}));const vr=t=>{let e=null;switch(t.method.toUpperCase()){case"GET":if(t.data){const e=t.url.split("?");e[1]=1===e.length?t.data.toString():`${e[1]}&${t.data.toString()}`,t.url=e.join("?")}break;case"PUT":case"POST":t.data&&(e=t.data.toString())}const i=new XMLHttpRequest;if(i.open(t.method,t.url,!0),i.responseType=t.format,i.withCredentials=t.withCredentials,t.event){const e=Object.keys(t.event);for(let s=0;s{const e=ht();if(t){const i=t.trim().split("\n"),s=i.length;for(let t=0;tyr.has(t)&&yr.get(t)||1,Ar=t=>{switch(t){case ue.namespace:return new ue;case ge.namespace:return new ge;case Ci.namespace:return new Ci;case $e.namespace:return new $e;case _e.namespace:return new _e}};let Mr=null,Sr=null,wr=null;const Cr=URL.createObjectURL(new Blob(['(()=>{"use strict";var r=Uint8Array,n=Uint16Array,e=Int32Array,a=new r([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),t=new r([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),i=new r([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),o=function(r,a){for(var t=new n(31),i=0;i<31;++i)t[i]=a+=1<>1|(21845&s)<<1;w=(61680&(w=(52428&w)>>2|(13107&w)<<2))>>4|(3855&w)<<4,d[s]=((65280&w)>>8|(255&w)<<8)>>1}var h=function(r,e,a){for(var t=r.length,i=0,o=new n(e);i>v]=c}else for(f=new n(t),i=0;i>15-r[i]);return f},y=new r(288);for(s=0;s<144;++s)y[s]=8;for(s=144;s<256;++s)y[s]=9;for(s=256;s<280;++s)y[s]=7;for(s=280;s<288;++s)y[s]=8;var b=new r(32);for(s=0;s<32;++s)b[s]=5;var g=h(y,9,1),p=h(b,5,1),m=function(r){for(var n=r[0],e=1;en&&(n=r[e]);return n},k=function(r,n,e){var a=n/8|0;return(r[a]|r[a+1]<<8)>>(7&n)&e},x=function(r,n){var e=n/8|0;return(r[e]|r[e+1]<<8|r[e+2]<<16)>>(7&n)},T=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],z=function(r,n,e){var a=new Error(n||T[r]);if(a.code=r,Error.captureStackTrace&&Error.captureStackTrace(a,z),!e)throw a;return a},E=function(n,e,o,f){var v=n.length,c=f?f.length:0;if(!v||e.f&&!e.l)return o||new r(0);var d=!o,s=d||2!=e.i,w=e.i;d&&(o=new r(3*v));var y,b=function(n){var e=o.length;if(n>e){var a=new r(Math.max(2*e,n));a.set(o),o=a}},T=e.f||0,E=e.p||0,M=e.b||0,S=e.l,U=e.d,A=e.m,C=e.n,q=8*v;do{if(!S){T=k(n,E,1);var D=k(n,E+1,3);if(E+=3,!D){var F=n[(y=E,(H=4+((y+7)/8|0))-4)]|n[H-3]<<8,I=H+F;if(I>v){w&&z(0);break}s&&b(M+F),o.set(n.subarray(H,I),M),e.b=M+=F,e.p=E=8*I,e.f=T;continue}if(1==D)S=g,U=p,A=9,C=5;else if(2==D){var O=k(n,E,31)+257,J=k(n,E+10,15)+4,L=O+k(n,E+5,31)+1;E+=14;for(var N=new r(L),P=new r(19),R=0;R>4)<16)N[R++]=H;else{var Q=0,V=0;for(16==H?(V=3+k(n,E,3),E+=2,Q=N[R-1]):17==H?(V=3+k(n,E,7),E+=3):18==H&&(V=11+k(n,E,127),E+=7);V--;)N[R++]=Q}}var W=N.subarray(0,O),X=N.subarray(O);A=m(W),C=m(X),S=h(W,A,1),U=h(X,C,1)}else z(1);if(E>q){w&&z(0);break}}s&&b(M+131072);for(var Y=(1<>4;if((E+=15&Q)>q){w&&z(0);break}if(Q||z(2),_<256)o[M++]=_;else{if(256==_){$=E,S=null;break}var rr=_-254;if(_>264){var nr=a[R=_-257];rr=k(n,E,(1<>4;if(er||z(3),E+=15&er,X=l[ar],ar>3&&(nr=t[ar],X+=x(n,E)&(1<q){w&&z(0);break}s&&b(M+131072);var tr=M+rr;if(Mn.length)&&(a=n.length),new r(n.subarray(e,a))}(o,0,M):o.subarray(0,M)},M=new r(0);function S(n,e){var a,t,i=function(r){31==r[0]&&139==r[1]&&8==r[2]||z(6,"invalid gzip data");var n=r[3],e=10;4&n&&(e+=2+(r[10]|r[11]<<8));for(var a=(n>>3&1)+(n>>4&1);a>0;a-=!r[e++]);return e+(2&n)}(n);return i+8>n.length&&z(6,"invalid gzip data"),E(n.subarray(i,-8),{i:2},e&&e.out||new r((t=(a=n).length,(a[t-4]|a[t-3]<<8|a[t-2]<<16|a[t-1]<<24)>>>0)),e&&e.dictionary)}function U(r,n){return E(r.subarray((e=r,a=n&&n.dictionary,(8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31)&&z(6,"invalid zlib data"),(e[1]>>5&1)==+!a&&z(6,"invalid zlib data: "+(32&e[1]?"need":"unexpected")+" dictionary"),2+(e[1]>>3&4)),-4),{i:2},n&&n.out,n&&n.dictionary);var e,a}var A="undefined"!=typeof TextDecoder&&new TextDecoder;try{A.decode(M,{stream:!0})}catch(r){}"function"==typeof queueMicrotask?queueMicrotask:"function"==typeof setTimeout&&setTimeout;self.addEventListener("message",(r=>{return n=void 0,e=void 0,t=function*(){const n=31==(e=r.data)[0]&&139==e[1]&&8==e[2]?S(e,a):8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31?function(r,n){return E(r,{i:2},n&&n.out,n&&n.dictionary)}(e,a):U(e,a);var e,a;let t="";for(let r=0;r(Fr||(Fr=new Worker(Cr)),Fr);let Br=!1;const Lr=t=>{Br=t},Pr=()=>Br,kr=()=>{if("OffscreenCanvas"in window){const t=new OffscreenCanvas(0,0).getContext("webgl2");Mr=null!==t?new Worker(URL.createObjectURL(new Blob(['(()=>{"use strict";let t=1,e=0,i=!1;const s=1/0,r=Math,n=Array,a=Map,h=Number,o=Float32Array,_=Int32Array,l=Int16Array,c=OffscreenCanvas,$=isNaN,u=requestAnimationFrame,d=setTimeout,g=clearTimeout,f=new o([1,0,0,1,0,0]),m=new o([1,1,1,1,0,0,0,0]),p=r.PI/180,x=(r.PI,[]),b=[],v=[],T=[],A=[],M=[],y=[],E=[],C=[],S=new c(1,1).getContext("2d"),F=(t=0,e=0,i=0,s=0)=>{const r=C.pop()||{xMin:0,xMax:0,yMin:0,yMax:0};return r.xMin=t,r.xMax=e,r.yMin=i,r.yMax=s,r},B=t=>{C.push(t)},w=(t=0,e=0,i=0,s=0)=>{const r=v.pop()||new o(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},R=t=>{v.push(t)},I=(t=0,e=0,i=0,s=0)=>{const r=b.pop()||new _(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},P=(t=0,e=0,i=0,s=0,r=0,n=0)=>{const a=T.pop()||new o(6);return a[0]=t,a[1]=e,a[2]=i,a[3]=s,a[4]=r,a[5]=n,a},N=t=>{T.push(t)},k=(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0)=>{const _=A.pop()||new o(8);return _[0]=t,_[1]=e,_[2]=i,_[3]=s,_[4]=r,_[5]=n,_[6]=a,_[7]=h,_},L=t=>{A.push(t)},O=(t=0,e=0,i=0,s=0,r=0,n=0,a=0,h=0,_=0)=>{const l=M.pop()||new o(9);return l[0]=t,l[1]=e,l[2]=i,l[3]=s,l[4]=r,l[5]=n,l[6]=a,l[7]=h,l[8]=_,l},U=(...t)=>{const e=y.pop()||[];return t.length&&e.push(...t),e},D=(t=null)=>{t&&(t.length&&(t.length=0),y.push(t))},X=t=>{t.size&&t.clear(),E.push(t)},V=()=>E.pop()||new a,Y=t=>(t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t),z=t=>{const e=1/(t[0]*t[4]-t[3]*t[1]),i=t[3]*t[7]-t[4]*t[6],s=t[1]*t[6]-t[0]*t[7];return O(t[4]*e,0-t[1]*e,0,0-t[3]*e,t[0]*e,0,i*e,s*e,1)},G=(t,e,i,s=null)=>{const n=+t;return $(n)&&null!==s?s:r.min(r.max(e,$(n)?0:n),i)},H=(t,e)=>P(t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]),W=(t,e)=>k(t[0]*e[0],t[1]*e[1],t[2]*e[2],t[3]*e[3],t[0]*e[4]+t[4],t[1]*e[5]+t[5],t[2]*e[6]+t[6],t[3]*e[7]+t[7]),q=(t,e)=>{const i=t.xMax*e[0]+t.yMax*e[2]+e[4],s=t.xMax*e[0]+t.yMin*e[2]+e[4],n=t.xMin*e[0]+t.yMax*e[2]+e[4],a=t.xMin*e[0]+t.yMin*e[2]+e[4],o=t.xMax*e[1]+t.yMax*e[3]+e[5],_=t.xMax*e[1]+t.yMin*e[3]+e[5],l=t.xMin*e[1]+t.yMax*e[3]+e[5],c=t.xMin*e[1]+t.yMin*e[3]+e[5],$=r.min(h.MAX_VALUE,i,s,n,a),u=r.max(0-h.MAX_VALUE,i,s,n,a),d=r.min(h.MAX_VALUE,o,_,l,c),g=r.max(0-h.MAX_VALUE,o,_,l,c);return F($,u,d,g)},j=t=>$(+t)?(t=>{if(!S)return 0;S.fillStyle=t;const e=+`0x${S.fillStyle.slice(1)}`;return S.fillStyle="rgba(0, 0, 0, 1)",e})(`${t}`):+t,K=(t,e,i)=>(t>>16)*(i?e:1)/255,Q=(t,e,i)=>(t>>8&255)*(i?e:1)/255,J=(t,e,i)=>(255&t)*(i?e:1)/255,Z=(t,e=1)=>({R:(16711680&t)>>16,G:(65280&t)>>8,B:255&t,A:255*e}),tt=(t,e,i=!1,s=!1)=>{let r="";return i&&(r="italic "),s&&(r+="bold "),`${r}${e}px \'${t}\',\'sans-serif\'`},et=t=>{t.color&&L(t.color),t.isLayer=!1,t.isUpdated=null,t.canApply=null,t.matrix=null,t.color=null,t.filters=null,t.blendMode="normal",t.sw=0,t.sh=0,x.push(t)},it=new Map([[1,"normal"],[2,"layer"],[3,"multiply"],[4,"screen"],[5,"lighten"],[6,"darken"],[7,"difference"],[8,"add"],[9,"subtract"],[10,"invert"],[11,"alpha"],[12,"erase"],[13,"overlay"],[14,"hardlight"]]),st=t=>it.has(t)&&it.get(t)||"normal",rt=new class{constructor(){this._$pool=[],this._$store=new Map,this._$timerMap=new Map,this._$context=null}set context(t){this._$context=t}reset(){for(const t of this._$store.values()){for(const e of t.values())this.destroy(e);X(t)}this._$store.clear(),this._$context&&this._$context.frameBuffer.clearCache()}destroy(t=null){if(t&&"object"==typeof t)if(t instanceof WebGLTexture)u((()=>{this._$context&&this._$context.frameBuffer.releaseTexture(t)}));else{if("canvas"in t&&t instanceof CanvasRenderingContext2D){const e=t.canvas,i=e.width,s=e.height;t.clearRect(0,0,i+1,s+1),e.width=e.height=1,this._$pool.push(e)}this._$context&&"index"in t&&this._$context.frameBuffer.textureManager.releasePosition(t)}}getCanvas(){return this._$pool.pop()||document.createElement("canvas")}remove(t,e){if(!this._$store.has(t))return;const i=this._$store.get(t);i.has(e)&&(i.delete(e),i.size||(X(i),this._$store.delete(t)))}stopTimer(t){t=`${t}`,this._$timerMap.has(t)&&(g(this._$timerMap.get(t)),this._$timerMap.delete(t))}removeCache(t){if(t=`${t}`,this._$store.has(t)){const e=this._$store.get(t);for(const t of e.values())this.destroy(t);e.clear(),X(e),this._$store.delete(t)}this._$timerMap.delete(t)}setRemoveTimer(t){if(t=`${t}`,this.stopTimer(t),this._$store.has(t)){const e=d((()=>{this.removeCache(t)}),5e3);this._$timerMap.set(t,e)}}get(t){const e=`${t[0]}`,i=`${t[1]}`;if(this._$store.has(e)){this.stopTimer(e);const t=this._$store.get(e);if(t.has(i))return t.get(i)}return null}set(t,e=null){const i=`${t[0]}`,s=`${t[1]}`;this._$store.has(i)||this._$store.set(i,V());const r=this._$store.get(i);if(null===e){if(!r.has(s))return;return this.destroy(r.get(s)),r.delete(s),void(r.size||(X(r),this._$store.delete(i)))}r.set(s,e)}has(t){const e=`${t[0]}`;return!!this._$store.has(e)&&this._$store.get(e).has(`${t[1]}`)}generateKeys(t,e=null,i=null){let s="";e&&e.length&&(s+=`${e[0]}_${e[1]}`),i&&i.length&&(s+=0===i[7]?"":`_${i[7]}`);const r=U();if(s){let t=0;const e=s.length;for(let i=0;i{i=t})()}}class at extends nt{constructor(t=4,e=4,i=1){super(),this._$blurX=4,this._$blurY=4,this._$quality=1,this.blurX=t,this.blurY=e,this.quality=i}static toString(){return"[class BlurFilter]"}static get namespace(){return"next2d.filters.BlurFilter"}toString(){return"[object BlurFilter]"}get namespace(){return"next2d.filters.BlurFilter"}static get STEP(){return[.5,1.05,1.4,1.55,1.75,1.9,2,2.15,2.2,2.3,2.5,3,3,3.5,3.5]}get blurX(){return this._$blurX}set blurX(t){(t=G(+t,0,255,0))!==this._$blurX&&(this._$blurX=t,this._$doChanged())}get blurY(){return this._$blurY}set blurY(t){(t=G(+t,0,255,0))!==this._$blurY&&(this._$blurY=t,this._$doChanged())}get quality(){return this._$quality}set quality(t){(t=G(0|t,0,15,1))!==this._$quality&&(this._$quality=t,this._$doChanged())}clone(){return new at(this._$blurX,this._$blurY,this._$quality)}_$toArray(){return U(1,this._$blurX,this._$blurY,this._$quality)}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$quality)return s;const n=at.STEP[this._$quality-1];let a=0>=this._$blurX?1:this._$blurX*n,h=0>=this._$blurY?1:this._$blurY*n;return e?a*=e:a=r.round(a),i?h*=i:h=r.round(h),s.xMin-=a,s.xMax+=2*a,s.yMin-=h,s.yMax+=2*h,s}_$canApply(){return 0!==this._$blurX&&0!==this._$blurY}_$applyFilter(e,i,s=!0){this._$updated=!1;const n=e.frameBuffer,a=n.currentAttachment,h=n.getTextureFromCurrentAttachment();if(!this._$canApply())return s?h:n.createTextureFromCurrentAttachment();let o=r.sqrt(i[0]*i[0]+i[1]*i[1]),_=r.sqrt(i[2]*i[2]+i[3]*i[3]);o/=t,_/=t,o*=2,_*=2;const l=F(0,h.width,0,h.height),c=this._$generateFilterRect(l,o,_);B(l);const $=0|r.ceil(c.xMax),u=0|r.ceil(c.yMax),d=r.ceil(r.abs(c.xMin)+.5*r.abs($-c.xMax)),g=r.ceil(r.abs(c.yMin)+.5*r.abs(u-c.yMax));e._$offsetX=d+e._$offsetX,e._$offsetY=g+e._$offsetY;const f=this._$blurX*o,m=this._$blurY*_;let p=1,x=1;f>128?p=.0625:f>64?p=.125:f>32?p=.25:f>16&&(p=.5),m>128?x=.0625:m>64?x=.125:m>32?x=.25:m>16&&(x=.5);const b=f*p,v=m*x,T=r.ceil($*p),A=r.ceil(u*x),M=n.createTextureAttachment(T,A),y=[M,n.createTextureAttachment(T,A)];let E=0;e._$bind(M),e.reset(),e.setTransform(p,0,0,x,0,0),e.drawImage(h,d,g,h.width,h.height),e.blend.toOneZero();let C=n.getTextureFromCurrentAttachment();for(let t=0;t0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!0,b),C=n.getTextureFromCurrentAttachment()}if(this._$blurY>0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!1,v),C=n.getTextureFromCurrentAttachment()}}if(e.blend.reset(),1!==p||1!==x){const t=n.createTextureAttachment($,u);e._$bind(t),e.reset(),e.imageSmoothingEnabled=!0,e.setTransform(1/p,0,0,1/x,0,0),e.drawImage(C,0,0,T,A),C=n.getTextureFromCurrentAttachment(),e.reset(),e.setTransform(1,0,0,1,0,0),n.releaseAttachment(y[0],!0),n.releaseAttachment(y[1],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(t,!1)}else n.releaseAttachment(y[(E+1)%2],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(y[E],!1);return C}}class ht extends nt{constructor(t=4,e=45,i=16777215,s=1,r=0,n=1,a=4,h=4,o=1,_=1,l="inner",c=!1){super(),this._$blurFilter=new at(a,h,_),this._$distance=4,this._$angle=45,this._$highlightColor=16777215,this._$highlightAlpha=1,this._$shadowColor=0,this._$shadowAlpha=1,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.highlightColor=i,this.highlightAlpha=s,this.shadowColor=r,this.shadowAlpha=n,this.strength=o,this.type=l,this.knockout=c}static toString(){return"[class BevelFilter]"}static get namespace(){return"next2d.filters.BevelFilter"}toString(){return"[object BevelFilter]"}get namespace(){return"next2d.filters.BevelFilter"}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get highlightAlpha(){return this._$highlightAlpha}set highlightAlpha(t){(t=G(+t,0,1,0))!==this._$highlightAlpha&&(this._$highlightAlpha=t,this._$doChanged())}get highlightColor(){return this._$highlightColor}set highlightColor(t){(t=G(j(t),0,16777215,16777215))!==this._$highlightColor&&(this._$highlightColor=t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get shadowAlpha(){return this._$shadowAlpha}set shadowAlpha(t){(t=G(+t,0,1,0))!==this._$shadowAlpha&&(this._$shadowAlpha=t,this._$doChanged())}get shadowColor(){return this._$shadowColor}set shadowColor(t){(t=G(j(t),0,16777215,0))!==this._$shadowColor&&(this._$shadowColor=t,this._$doChanged())}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}get type(){return this._$type}set type(t){(t=`${t}`)!==this._$type&&(this._$type=t,this._$doChanged())}clone(){return new ht(this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$toArray(){return U(0,this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.abs(r.cos(n)*this._$distance),h=r.abs(r.sin(n)*this._$distance);return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&0!==this._$distance&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply())return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=this._$angle*p,d=r.cos(u)*this._$distance*c,g=r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation="erase",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A="inner"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),s.releaseAttachment(f,!0),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,null,null,null,K(this._$highlightColor,this._$highlightAlpha,!0),Q(this._$highlightColor,this._$highlightAlpha,!0),J(this._$highlightColor,this._$highlightAlpha,!0),this._$highlightAlpha,K(this._$shadowColor,this._$shadowAlpha,!0),Q(this._$shadowColor,this._$shadowAlpha,!0),J(this._$shadowColor,this._$shadowAlpha,!0),this._$shadowAlpha),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseTexture(m),s.getTextureFromCurrentAttachment()}}class ot extends nt{constructor(t=null){super(),this._$matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],this.matrix=t}static toString(){return"[class ColorMatrixFilter]"}static get namespace(){return"next2d.filters.ColorMatrixFilter"}toString(){return"[object ColorMatrixFilter]"}get namespace(){return"next2d.filters.ColorMatrixFilter"}get matrix(){return this._$matrix}set matrix(t){if(t&&n.isArray(t)&&20===t.length){for(let e=0;e<20;++e)if(t[e]!==this._$matrix[e]){this._$doChanged();break}this._$matrix=t}}clone(){return new ot(this._$matrix)}_$toArray(){return U(2,this._$matrix)}_$generateFilterRect(t){return t}_$canApply(){return!0}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment(),r=s.width,n=s.height,a=e.createTextureAttachment(r,n);return t._$bind(a),t.reset(),t._$applyColorMatrixFilter(s,this._$matrix),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()}}class _t extends nt{constructor(t=0,e=0,i=null,s=1,r=0,n=!0,a=!0,h=0,o=0){super(),this._$matrixX=0,this._$matrixY=0,this._$matrix=null,this._$divisor=1,this._$bias=0,this._$preserveAlpha=!0,this._$clamp=!0,this._$color=0,this._$alpha=0,this.matrixX=t,this.matrixY=e,this.matrix=i,this.divisor=s,this.bias=r,this.preserveAlpha=n,this.clamp=a,this.color=h,this.alpha=o}static toString(){return"[class ConvolutionFilter]"}static get namespace(){return"next2d.filters.ConvolutionFilter"}toString(){return"[object ConvolutionFilter]"}get namespace(){return"next2d.filters.ConvolutionFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get bias(){return this._$bias}set bias(t){t!==this._$bias&&(this._$bias=0|t,this._$doChanged())}get clamp(){return this._$clamp}set clamp(t){t!==this._$clamp&&(this._$clamp=!!t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get divisor(){return this._$divisor}set divisor(t){t!==this._$divisor&&(this._$divisor=0|t,this._$doChanged())}get matrix(){return this._$matrix}set matrix(t){n.isArray(this._$matrix)&&D(this._$matrix),this._$matrix=n.isArray(t)?t:null,this._$doChanged()}get matrixX(){return this._$matrixX}set matrixX(t){(t=0|G(0|t,0,15,0))!==this._$matrixX&&(this._$matrixX=t,this._$doChanged())}get matrixY(){return this._$matrixY}set matrixY(t){(t=0|G(0|t,0,15,0))!==this._$matrixY&&(this._$matrixY=t,this._$doChanged())}get preserveAlpha(){return this._$preserveAlpha}set preserveAlpha(t){t!==this._$preserveAlpha&&(this._$preserveAlpha=!!t,this._$doChanged())}clone(){return new _t(this._$matrixX,this._$matrixY,this._$matrix?this._$matrix.slice():null,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$toArray(){return U(3,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$matrix&&this._$matrixX*this._$matrixY===this._$matrix.length}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment();return this._$canApply()&&this._$matrix?(t._$applyConvolutionFilter(s,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,K(this._$color,this._$alpha,!1),Q(this._$color,this._$alpha,!1),J(this._$color,this._$alpha,!1),this._$alpha),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()):s}}class lt extends nt{constructor(t=null,e=null,i=0,s=0,r=0,n=0,a="wrap",h=0,o=0){super(),this._$mapBitmap=null,this._$mapPoint=null,this._$componentX=0,this._$componentY=0,this._$scaleX=0,this._$scaleY=0,this._$mode="wrap",this._$color=0,this._$alpha=0,this.mapBitmap=t,this.mapPoint=e,this.componentX=i,this.componentY=s,this.scaleX=r,this.scaleY=n,this.mode=a,this.color=h,this.alpha=o}static toString(){return"[class DisplacementMapFilter]"}static get namespace(){return"next2d.filters.DisplacementMapFilter"}toString(){return"[object DisplacementMapFilter]"}get namespace(){return"next2d.filters.DisplacementMapFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get componentX(){return this._$componentX}set componentX(t){t!==this._$componentX&&(this._$componentX=t,this._$doChanged())}get componentY(){return this._$componentY}set componentY(t){t!==this._$componentY&&(this._$componentY=t,this._$doChanged())}get mapBitmap(){return this._$mapBitmap}set mapBitmap(t){t!==this._$mapBitmap&&(this._$mapBitmap=t,this._$doChanged())}get mapPoint(){return this._$mapPoint}set mapPoint(t){t!==this._$mapPoint&&(this._$mapPoint=t,this._$doChanged())}get mode(){return this._$mode}set mode(t){t!==this._$mode&&(this._$mode=t,this._$doChanged())}get scaleX(){return this._$scaleX}set scaleX(t){(t=G(+t,-65535,65535,0))!==this._$scaleX&&(this._$scaleX=t,this._$doChanged())}get scaleY(){return this._$scaleY}set scaleY(t){(t=G(+t,-65535,65535,0))!==this._$scaleY&&(this._$scaleY=t,this._$doChanged())}clone(){return new lt(this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$toArray(){return U(4,this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$mapBitmap&&this._$componentX>0&&this._$componentY>0&&0!==this._$scaleX&&0!==this._$scaleY}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;t.setTransform(1,0,0,1,0,0);const n=i.getTextureFromCurrentAttachment();if(!this._$canApply()||!s||!this._$mapBitmap)return n;const a=r.sqrt(e[0]*e[0]+e[1]*e[1]),h=r.sqrt(e[2]*e[2]+e[3]*e[3]);return t._$applyDisplacementMapFilter(n,this._$mapBitmap,n.width/a,n.height/h,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha),i.releaseAttachment(s,!0),i.getTextureFromCurrentAttachment()}}class ct extends nt{constructor(t=4,e=45,i=0,s=1,r=4,n=4,a=1,h=1,o=!1,_=!1,l=!1){super(),this._$blurFilter=new at(r,n,h),this._$distance=4,this._$angle=45,this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this._$hideObject=!1,this.distance=t,this.angle=e,this.color=i,this.alpha=s,this.strength=a,this.inner=o,this.knockout=_,this.hideObject=l}static toString(){return"[class DropShadowFilter]"}static get namespace(){return"next2d.filters.DropShadowFilter"}toString(){return"[object DropShadowFilter]"}get namespace(){return"next2d.filters.DropShadowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get hideObject(){return this._$hideObject}set hideObject(t){t!==this._$hideObject&&(this._$hideObject=!!t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new ct(this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$toArray(){return U(5,this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.cos(n)*this._$distance,h=r.sin(n)*this._$distance;return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(e,i){const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");if(e.setTransform(1,0,0,1,0,0),!this._$canApply())return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=this._$angle*p,v=r.cos(b)*this._$distance*m,T=r.sin(b)*this._$distance*x,A=this._$inner?a:c+r.max(0,r.abs(v)-g),M=this._$inner?h:$+r.max(0,r.abs(T)-f),y=r.ceil(A),E=r.ceil(M),C=(y-A)/2,S=(E-M)/2,F=this._$inner?0:r.max(0,g-v)+C,B=this._$inner?0:r.max(0,f-T)+S,w=this._$inner?v-u:(v>0?r.max(0,v-g):0)+C,R=this._$inner?T-d:(T>0?r.max(0,T-f):0)+S;let I,P;return this._$inner?(I="inner",P=this._$knockout||this._$hideObject):!this._$knockout&&this._$hideObject?(I="full",P=!0):(I="outer",P=this._$knockout),e._$bind(n),e._$applyBitmapFilter(l,y,E,a,h,F,B,c,$,w,R,!0,I,P,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),e._$offsetX=o+F,e._$offsetY=_+B,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class $t extends nt{constructor(t=0,e=1,i=4,s=4,r=1,n=1,a=!1,h=!1){super(),this._$blurFilter=new at(i,s,n),this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this.color=t,this.alpha=e,this.strength=r,this.inner=a,this.knockout=h}static toString(){return"[class GlowFilter]"}static get namespace(){return"next2d.filters.GlowFilter"}toString(){return"[object GlowFilter]"}get namespace(){return"next2d.filters.GlowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,4))!==this._$color&&(this._$color=t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new $t(this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$toArray(){return U(6,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);return this._$canApply()?this._$blurFilter._$generateFilterRect(s,e,i):s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(t,e){const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error("the current attachment is null.");if(this._$updated=!1,t.setTransform(1,0,0,1,0,0),!this._$canApply())return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),_=o.width,l=o.height,c=t._$offsetX,$=t._$offsetY,u=this._$inner?r:_,d=this._$inner?n:l,g=this._$inner?0:c-a,f=this._$inner?0:$-h,m=this._$inner?-c:0,p=this._$inner?-$:0,x=this._$inner?"inner":"outer";return t._$bind(s),t._$applyBitmapFilter(o,u,d,r,n,g,f,_,l,m,p,!0,x,this._$knockout,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),t._$offsetX=a+g,t._$offsetY=h+f,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class ut extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_="inner",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return"[class GradientBevelFilter]"}static get namespace(){return"next2d.filters.GradientBevelFilter"}toString(){return"[object GradientBevelFilter]"}get namespace(){return"next2d.filters.GradientBevelFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply()||!n)return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=+this._$angle*p,d=+r.cos(u)*this._$distance*c,g=+r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation="erase",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A="inner"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseAttachment(f,!0),s.getTextureFromCurrentAttachment()}}class dt extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_="inner",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return"[class GradientGlowFilter]"}static get namespace(){return"next2d.filters.GradientGlowFilter"}toString(){return"[object GradientGlowFilter]"}get namespace(){return"next2d.filters.GradientGlowFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(e.setTransform(1,0,0,1,0,0),!this._$canApply()||!n)return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=+this._$angle*p,v=+r.cos(b)*this._$distance*m,T=+r.sin(b)*this._$distance*x,A="inner"===this.type,M=A?a:c+r.max(0,r.abs(v)-g),y=A?h:$+r.max(0,r.abs(T)-f),E=r.ceil(M),C=r.ceil(y),S=(E-M)/2,F=(C-y)/2,B=A?0:r.max(0,g-v)+S,w=A?0:r.max(0,f-T)+F,R=A?v-u:(v>0?r.max(0,v-g):0)+S,I=A?T-d:(T>0?r.max(0,T-f):0)+F;return e._$bind(n),e._$applyBitmapFilter(l,E,C,a,h,B,w,c,$,R,I,!0,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=o+B,e._$offsetY=_+w,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class gt{constructor(){this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$clipDepth=0,this._$depth=0,this._$isMask=!1,this._$updated=!0,this._$matrix=P(1,0,0,1,0,0),this._$colorTransform=k(1,1,1,1,0,0,0,0),this._$blendMode="normal",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$maskMatrix=null,this._$isMask=!1,this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$scale9Grid=null,this._$matrixBase=null}_$shouldClip(t){const e=this._$getBounds(t),i=r.abs(e.xMax-e.xMin),s=r.abs(e.yMax-e.yMin);return B(e),!(!i||!s)}_$getLayerBounds(e){const i=this._$getBounds(),s=q(i,e);B(i);const n=this._$filters;if(!n||!n.length)return s;let a=F(0,r.abs(s.xMax-s.xMin),0,r.abs(s.yMax-s.yMin));B(s);let h=+r.sqrt(e[0]*e[0]+e[1]*e[1]),o=+r.sqrt(e[2]*e[2]+e[3]*e[3]);h/=t,o/=t,h*=2,o*=2;for(let t=0;t-1){const t=le.instances;if(!t.has(this._$parentId))return;const e=t.get(this._$parentId);e._$updated||e._$doChanged()}}_$update(t){if(this._$doChanged(),this._$visible=t.visible,"depth"in t&&(this._$depth=t.depth),"isMask"in t&&(this._$isMask=t.isMask),"clipDepth"in t&&(this._$clipDepth=t.clipDepth),"maskId"in t&&(this._$maskId=t.maskId,this._$maskId>-1&&t.maskMatrix&&(this._$maskMatrix=t.maskMatrix)),this._$matrix[0]="a"in t?t.a:1,this._$matrix[1]="b"in t?t.b:0,this._$matrix[2]="c"in t?t.c:0,this._$matrix[3]="d"in t?t.d:1,this._$matrix[4]="tx"in t?t.tx:0,this._$matrix[5]="ty"in t?t.ty:0,this._$colorTransform[0]="f0"in t?t.f0:1,this._$colorTransform[1]="f1"in t?t.f1:1,this._$colorTransform[2]="f2"in t?t.f2:1,this._$colorTransform[3]="f3"in t?t.f3:1,this._$colorTransform[4]="f4"in t?t.f4:0,this._$colorTransform[5]="f5"in t?t.f5:0,this._$colorTransform[6]="f6"in t?t.f6:0,this._$colorTransform[7]="f7"in t?t.f7:0,this._$blendMode=t.blendMode||"normal",this._$filters=null,t.filters&&t.filters.length){this._$filters=U();for(let e=0;e-1&&this._$characterId&&rt.setRemoveTimer(`${this._$loaderInfoId}@${this._$characterId}`),t.instances.delete(this._$instanceId),this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$blendMode="normal",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$isMask=!1,this._$depth=0,this._$clipDepth=0,this._$scale9Grid=null}_$isUpdated(){return this._$updated}_$isFilterUpdated(t,e=null,i=!1){if(this._$isUpdated())return!0;if(i&&e)for(let t=0;tc||s._$clipDepth>0)&&(t.restore(),l&&t._$leaveClip(),c=0,l=!0),!l)continue;if(s._$clipDepth>0){c=s._$clipDepth,l=s._$shouldClip(o),l&&(t.save(),l=s._$startClip(t,o));continue}const a=s._$maskId>-1&&$.has(s._$maskId)?$.get(s._$maskId):null;if(a){let e;if(a._$updated=!1,this._$instanceId===a._$parentId)e=o;else{e=f;let i=$.get(a._$parentId);for(;i||i._$instanceId!==i._$parentId;)e=H(i._$matrix,e),i=$.get(i._$parentId);const s=le.scaleX,r=P(s,0,0,s,0,0);if(e=H(r,e),N(r),t.isLayer){const i=t.getCurrentPosition();e[4]-=i.xMin,e[5]-=i.yMin}}if(!a._$shouldClip(e))continue;const i=a._$startClip(t,e);if(t.save(),!i){t.restore();continue}}s._$draw(t,o,_),s._$updated=!1,a&&(t.restore(),t._$leaveClip())}if(c&&(t.restore(),l&&t._$leaveClip()),h.isLayer)return this._$postDraw(t,e,s,h);h.matrix!==e&&N(h.matrix),s!==i&&L(s),et(h)}_$getLayerBounds(e){const i=this._$children;if(!i.length)return F(0,0,0,0);const s=h.MAX_VALUE;let n=s,a=-s,o=s,_=-s;const l=le.instances;for(let t=0;t0){const s=this._$getBounds(null),o=q(s,i);B(s);const _=+o.xMax,l=+o.xMin,c=+o.yMax,$=+o.yMin;B(o);const u=r.ceil(r.abs(_-l)),d=r.ceil(r.abs(c-$));if(0>=u||0>=d)return et(n),i!==e&&N(i),null;let g=+r.sqrt(i[0]*i[0]+i[1]*i[1]);if(!h.isInteger(g)){const t=g.toString(),e=t.indexOf("e");-1!==e&&(g=+t.slice(0,e)),g=+g.toFixed(4)}let f=+r.sqrt(i[2]*i[2]+i[3]*i[3]);if(!h.isInteger(f)){const t=f.toString(),e=t.indexOf("e");-1!==e&&(f=+t.slice(0,e)),f=+f.toFixed(4)}n.canApply=this._$canApply(this._$filters);let m=F(0,u,0,d);if(n.canApply&&this._$filters)for(let t=0;tp.width||$-m.yMin>p.height)return B(m),et(n),i!==e&&N(i),null;if(0>l+m.xMax||0>$+m.yMax)return B(m),et(n),i!==e&&N(i),null;let x=i[4]-l,b=i[5]-$;t._$startLayer(F(l,_,$,c));const v=this._$isFilterUpdated(i,this._$filters,n.canApply),T=this._$getLayerBounds(i),A=r.ceil(r.abs(T.xMax-T.xMin)),M=r.ceil(r.abs(T.yMax-T.yMin));B(T);const y=A-m.xMax+m.xMin,E=M-m.yMax+m.yMin;x+=y,b+=E,n.sw=y,n.sh=E,v&&t._$saveAttachment(r.ceil(u+y),r.ceil(d+E),!0),n.isLayer=!0,n.isUpdated=v,n.filters=this._$filters,n.blendMode=a,n.color=k(),n.matrix=P(i[0],i[1],i[2],i[3],x,b),i!==e&&N(i),B(m)}return n}_$postDraw(t,e,i,s){t.drawInstacedArray();const r=U(this._$instanceId,"f"),n=t.frameBuffer,a=s.matrix;let h=0,o=0,_=rt.get(r);if(!_||s.isUpdated){_&&rt.set(r,null),_=n.getTextureFromCurrentAttachment();const i=s.filters;let l=!1;if(i&&i.length){for(let s=0;s{switch(!0){case t[0]>e[0]:return 1;case e[0]>t[0]:return-1;default:return 0}})),this._$stops}linear(t,e,i,s,r="rgb",n="pad"){return this._$type="linear",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$rgb=r,this._$mode=n,this._$stops.length&&(this._$stops.length=0),this}radial(t,e,i,s,r,n,a="rgb",h="pad",o=0){return this._$type="radial",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$points[4]=r,this._$points[5]=n,this._$rgb=a,this._$mode=h,this._$focalPointRatio=G(o,-.975,.975,0),this._$stops.length&&(this._$stops.length=0),this}addColorStop(t,e){this._$stops.push(U(t,e))}}class pt{constructor(t,e,i){this._$texture=t,this._$repeat=e,this._$colorTransform=i}get texture(){return this._$texture}get repeat(){return this._$repeat}get colorTransform(){return this._$colorTransform}}class xt{constructor(){this._$fillStyle=w(1,1,1,1),this._$strokeStyle=w(1,1,1,1),this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5}get miterLimit(){return this._$miterLimit}set miterLimit(t){this._$miterLimit=t}get lineWidth(){return this._$lineWidth}set lineWidth(t){this._$lineWidth=t}get lineCap(){return this._$lineCap}set lineCap(t){this._$lineCap=t}get lineJoin(){return this._$lineJoin}set lineJoin(t){this._$lineJoin=t}get fillStyle(){return this._$fillStyle}set fillStyle(t){this._$fillStyle instanceof o&&R(this._$fillStyle),this._$fillStyle=t}get strokeStyle(){return this._$strokeStyle}set strokeStyle(t){this._$strokeStyle instanceof o&&R(this._$strokeStyle),this._$strokeStyle=t}clear(){this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5,this._$clearFill(),this._$clearStroke()}_$clearFill(){if(this._$fillStyle instanceof mt)return this._$fillStyle.dispose(),void(this._$fillStyle=w(1,1,1,1));this._$fillStyle instanceof pt?this._$fillStyle=w(1,1,1,1):this._$fillStyle.fill(1)}_$clearStroke(){if(this._$strokeStyle instanceof mt)return this._$strokeStyle.dispose(),void(this._$strokeStyle=w(1,1,1,1));this._$strokeStyle instanceof pt?this._$strokeStyle=w(1,1,1,1):this._$strokeStyle.fill(1)}}let bt=2048;class vt{constructor(t){t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this._$gl=t,this._$objectPool=[],this._$objectPoolArea=0,this._$activeTexture=-1,this._$boundTextures=[null,null,null],this._$maxWidth=0,this._$maxHeight=0,this._$atlasTextures=[],this._$atlasCacheMap=new Map,this._$positionObjectArray=[],this._$nodeObjectArray=[],this._$atlasNodes=new Map}createTextureAtlas(){const t=this._$gl.createTexture();t.width=bt,t.height=bt,this._$gl.activeTexture(this._$gl.TEXTURE3),this._$gl.bindTexture(this._$gl.TEXTURE_2D,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,this._$gl.NEAREST),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,this._$gl.NEAREST),this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,bt,bt),this._$gl.bindTexture(this._$gl.TEXTURE_2D,null),this._$activeTexture>-1&&this._$gl.activeTexture(this._$activeTexture);const e=this._$atlasTextures.length;this._$atlasNodes.set(e,[]),this._$atlasCacheMap.set(e,[]),this._$atlasTextures.push(t)}getAtlasTexture(t){return this._$atlasTextures[t]}getNode(t,e,i,s){const r=this._$nodeObjectArray.length?this._$nodeObjectArray.pop():{x:0,y:0,w:0,h:0};return r.x=t,r.y=e,r.w=i,r.h=s,r}createCachePosition(t,e){const i=this._$positionObjectArray.length?this._$positionObjectArray.pop():{index:0,x:0,y:0,w:0,h:0};i.x=i.y=0,i.w=t,i.h=e;for(const[s,r]of this._$atlasNodes){if(!r.length)return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i;const n=r.length;for(let a=0;an.w||e>n.h))return i.index=s,i.x=n.x,i.y=n.y,this._$atlasCacheMap.get(i.index).push(i),n.w!==t||n.h!==e?t>e?(n.h-e-1>0&&r.push(this.getNode(n.x,n.y+e+1,n.w,n.h-e-1)),n.w-t-1>0?(n.x=n.x+t+1,n.w=n.w-t-1,n.h=e):(r.splice(a,1),this._$nodeObjectArray.push(n))):(n.w-t-1>0&&r.push(this.getNode(n.x+t+1,n.y,n.w-t-1,n.h)),n.h-e-1>0?(n.y=n.y+e+1,n.w=t,n.h=n.h-e-1):(r.splice(a,1),this._$nodeObjectArray.push(n))):(r.splice(a,1),this._$nodeObjectArray.push(n)),i}}const s=this._$atlasTextures.length;this.createTextureAtlas();const r=this._$atlasNodes.get(s);return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i}releasePosition(t){var e;this._$atlasNodes.has(t.index)&&(null===(e=this._$atlasNodes.get(t.index))||void 0===e||e.unshift(this.getNode(t.x,t.y,t.w,t.h)),this._$positionObjectArray.push(t))}clearCache(){for(const t of this._$atlasCacheMap.values())t.length=0;for(const t of this._$atlasNodes.values())t.length=0}_$createTexture(t,e){const i=this._$gl.createTexture();return i.width=0,i.height=0,i.area=0,i.dirty=!0,i.smoothing=!0,this.bind0(i,!1),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,t,e),i}_$getTexture(t,e){for(let i=0;ithis._$maxWidth*this._$maxHeight*2)this._$gl.deleteTexture(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPool.length&&this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();this._$objectPoolArea-=t.area,this._$gl.deleteTexture(t)}}bind0(t,e=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,e)}bind01(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,e,i),this._$bindTexture(0,this._$gl.TEXTURE0,t,i)}bind012(t,e,i,s=null){this._$bindTexture(2,this._$gl.TEXTURE2,i,s),this._$bindTexture(1,this._$gl.TEXTURE1,e,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}bind02(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,e,i),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}_$bindTexture(t,e,i=null,s=null){const r=i!==this._$boundTextures[t],n=null!==s&&null!==i&&s!==i.smoothing;if((r||n||e===this._$gl.TEXTURE0)&&e!==this._$activeTexture&&(this._$activeTexture=e,this._$gl.activeTexture(e)),r&&(this._$boundTextures[t]=i,this._$gl.bindTexture(this._$gl.TEXTURE_2D,i)),n){i&&(i.smoothing=!!s);const t=s?this._$gl.LINEAR:this._$gl.NEAREST;this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,t)}}}class Tt{constructor(t){this._$gl=t,this._$objectPool=U(),this._$objectPoolArea=0,this._$maxWidth=0,this._$maxHeight=0}set maxWidth(t){this._$maxWidth=t}set maxHeight(t){this._$maxHeight=t}_$createStencilBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the stencil buffer is null.");return t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getStencilBuffer(t,e){const i=this._$objectPool.length;for(let s=0;s100){const t=this._$objectPool.shift();if(t)return this._$objectPoolArea-=t.area,t}return this._$createStencilBuffer()}create(t,e){const i=this._$getStencilBuffer(t,e);return i.width===t&&i.height===e||(i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,i),this._$gl.renderbufferStorage(this._$gl.RENDERBUFFER,this._$gl.STENCIL_INDEX8,t,e)),i}release(t){if(t.area>this._$maxWidth*this._$maxHeight*2)this._$gl.deleteRenderbuffer(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();t&&(this._$objectPoolArea-=t.area,this._$gl.deleteRenderbuffer(t))}}}class At{constructor(t,e){this._$gl=t,this._$samples=e,this._$objectPool=U()}set samples(t){this._$samples=t}_$createColorBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the color buffer is null.");const e=this._$gl.createRenderbuffer();if(!e)throw new Error("the stencil buffer is null.");return t.stencil=e,t.samples=0,t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getColorBuffer(t){if(!this._$objectPool.length)return this._$createColorBuffer();const e=this._$bsearch(t);if(e1;){const s=r.floor((i+e)/2);t<=this._$objectPool[s].area?i=s:e=s}return i}}class Mt{constructor(t,e){this._$gl=t,this._$objectPool=[],this._$frameBuffer=t.createFramebuffer(),t.bindFramebuffer(t.READ_FRAMEBUFFER,this._$frameBuffer),this._$frameBufferTexture=t.createFramebuffer(),this._$currentAttachment=null,this._$isBinding=!1,this._$textureManager=new vt(t),this._$stencilBufferPool=new Tt(t),this._$colorBufferPool=new At(t,e),this._$isRenderBinding=!1,this._$colorBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.RGBA8,bt,bt),this._$stencilBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.STENCIL_INDEX8,bt,bt)}bindRenderBuffer(){this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),this._$isRenderBinding||(this._$isRenderBinding=!0,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,this._$stencilBuffer))}get currentAttachment(){return this._$currentAttachment}get textureManager(){return this._$textureManager}createCacheAttachment(t,e,i=!1,s=0){const r=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},n=this._$textureManager.create(t,e);return r.width=t,r.height=e,i?(r.color=this._$colorBufferPool.create(t,e,s),r.texture=n,r.msaa=!0,r.stencil=r.color.stencil):(r.color=n,r.texture=n,r.msaa=!1,r.stencil=this._$stencilBufferPool.create(t,e)),r.mask=!1,r.clipLevel=0,r.isActive=!0,r}clearCache(){this._$textureManager.clearCache()}setMaxSize(t,e){this._$stencilBufferPool._$maxWidth=t,this._$stencilBufferPool._$maxHeight=e,this._$textureManager._$maxWidth=t,this._$textureManager._$maxHeight=e}createTextureAttachment(t,e){const i=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},s=this._$textureManager.create(t,e);return i.width=t,i.height=e,i.color=s,i.texture=s,i.msaa=!1,i.stencil=null,i.mask=!1,i.clipLevel=0,i.isActive=!0,i}createTextureAttachmentFrom(t){const e=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!0};return e.width=t.width,e.height=t.height,e.color=t,e.texture=t,e.msaa=!1,e.stencil=null,e.mask=!1,e.clipLevel=0,e.isActive=!0,e}releaseAttachment(t=null,e=!1){t&&t.isActive&&(t.msaa?t.color instanceof WebGLRenderbuffer&&this._$colorBufferPool.release(t.color):t.stencil&&this._$stencilBufferPool.release(t.stencil),e&&t.texture&&this._$textureManager.release(t.texture),t.color=null,t.texture=null,t.stencil=null,t.isActive=!1,this._$objectPool.push(t))}bind(t){this._$currentAttachment=t,this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),t.msaa?t.color instanceof WebGLRenderbuffer&&(this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.color),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,t.color)):t.color instanceof WebGLTexture&&(t.color&&this._$textureManager.bind0(t.color),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,t.color,0)),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.stencil),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,t.stencil),this._$isRenderBinding=!1}unbind(){this._$currentAttachment=null,this._$isBinding&&(this._$isBinding=!1,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,null))}transferToMainTexture(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,null),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBuffer)}createCachePosition(t,e){return this._$textureManager.createCachePosition(t,e)}transferTexture(t){this._$gl.disable(this._$gl.SCISSOR_TEST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture);const e=this._$textureManager.getAtlasTexture(t.index);this._$textureManager.bind0(e),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,e,0);const i=r.max(0,t.x-1),s=r.max(0,t.y-1),n=r.min(bt,t.x+t.w+1),a=r.min(bt,t.y+t.h+1);this._$gl.blitFramebuffer(i,s,n,a,i,s,n,a,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)}getTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");if(!this._$currentAttachment.msaa&&this._$currentAttachment.texture)return this._$currentAttachment.texture;const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");return i.dirty=!1,this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer),i}createTextureFromPixels(t,e,i=null,s=!1,r=!0){return this._$textureManager.create(t,e,i,s,r)}createTextureFromCanvas(t){return this._$textureManager.createFromCanvas(t)}createTextureFromImage(t,e=!1){return this._$textureManager.createFromImage(t,e)}createTextureFromVideo(t,e=!1){return this._$textureManager.createFromVideo(t,e)}createTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$textureManager.create(t,e);return this._$textureManager.bind0(i),this._$gl.copyTexSubImage2D(this._$gl.TEXTURE_2D,0,0,0,0,0,t,e),i}releaseTexture(t){this._$textureManager.release(t)}}class yt{constructor(){this._$bezierConverterBuffer=new o(32)}cubicToQuad(t,e,i,s,r,n,a,h){this._$split2Cubic(t,e,i,s,r,n,a,h,0,16),this._$split2Cubic(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0,8),this._$split2Cubic(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16,24),this._$split2Quad(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0),this._$split2Quad(this._$bezierConverterBuffer[8],this._$bezierConverterBuffer[9],this._$bezierConverterBuffer[10],this._$bezierConverterBuffer[11],this._$bezierConverterBuffer[12],this._$bezierConverterBuffer[13],this._$bezierConverterBuffer[14],this._$bezierConverterBuffer[15],8),this._$split2Quad(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16),this._$split2Quad(this._$bezierConverterBuffer[24],this._$bezierConverterBuffer[25],this._$bezierConverterBuffer[26],this._$bezierConverterBuffer[27],this._$bezierConverterBuffer[28],this._$bezierConverterBuffer[29],this._$bezierConverterBuffer[30],this._$bezierConverterBuffer[31],24)}_$split2Cubic(t,e,i,s,r,n,a,h,o,_){const l=.125*(t+3*(i+r)+a),c=.125*(e+3*(s+n)+h),$=.125*(a+r-i-t),u=.125*(h+n-s-e);this._$bezierConverterBuffer[o]=t,this._$bezierConverterBuffer[o+1]=e,this._$bezierConverterBuffer[o+2]=.5*(t+i),this._$bezierConverterBuffer[o+3]=.5*(e+s),this._$bezierConverterBuffer[o+4]=l-$,this._$bezierConverterBuffer[o+5]=c-u,this._$bezierConverterBuffer[o+6]=l,this._$bezierConverterBuffer[o+7]=c,this._$bezierConverterBuffer[_]=l,this._$bezierConverterBuffer[_+1]=c,this._$bezierConverterBuffer[_+2]=l+$,this._$bezierConverterBuffer[_+3]=c+u,this._$bezierConverterBuffer[_+4]=.5*(r+a),this._$bezierConverterBuffer[_+5]=.5*(n+h),this._$bezierConverterBuffer[_+6]=a,this._$bezierConverterBuffer[_+7]=h}_$split2Quad(t,e,i,s,r,n,a,h,o){const _=.125*(t+3*(i+r)+a),l=.125*(e+3*(s+n)+h);this._$bezierConverterBuffer[o]=.25*t+.75*i,this._$bezierConverterBuffer[o+1]=.25*e+.75*s,this._$bezierConverterBuffer[o+2]=_,this._$bezierConverterBuffer[o+3]=l,this._$bezierConverterBuffer[o+4]=.75*r+.25*a,this._$bezierConverterBuffer[o+5]=.75*n+.25*h,this._$bezierConverterBuffer[o+6]=a,this._$bezierConverterBuffer[o+7]=h}}class Et{constructor(){this._$currentPath=U(),this._$vertices=U(),this._$bezierConverter=new yt}get vertices(){return this._$pushCurrentPathToVertices(),this._$vertices}begin(){for(this._$currentPath.length=0;this._$vertices.length;)D(this._$vertices.pop())}moveTo(t,e){this._$currentPath.length?this._$equalsToLastPoint(t,e)||(this._$pushCurrentPathToVertices(),this._$pushPointToCurrentPath(t,e,!1)):this._$pushPointToCurrentPath(t,e,!1)}lineTo(t,e){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}quadTo(t,e,i,s){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(i,s)||(this._$pushPointToCurrentPath(t,e,!0),this._$pushPointToCurrentPath(i,s,!1))}cubicTo(t,e,i,s,r,n){if(this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(r,n))return;const a=+this._$currentPath[this._$currentPath.length-3],h=+this._$currentPath[this._$currentPath.length-2];this._$bezierConverter.cubicToQuad(a,h,t,e,i,s,r,n);const o=this._$bezierConverter._$bezierConverterBuffer;for(let t=0;t<32;)this.quadTo(o[t++],o[t++],o[t++],o[t++])}drawCircle(t,e,i){const s=i,r=.5522847498307936*i;this.cubicTo(t+s,e+r,t+r,e+s,t,e+s),this.cubicTo(t-r,e+s,t-s,e+r,t-s,e),this.cubicTo(t-s,e-r,t-r,e-s,t,e-s),this.cubicTo(t+r,e-s,t+s,e-r,t+s,e)}close(){if(this._$currentPath.length<=6)return;const t=+this._$currentPath[0],e=+this._$currentPath[1];this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}_$equalsToLastPoint(t,e){const i=+this._$currentPath[this._$currentPath.length-3],s=+this._$currentPath[this._$currentPath.length-2];return t===i&&e===s}_$pushPointToCurrentPath(t,e,i){this._$currentPath.push(t,e,i)}_$pushCurrentPathToVertices(){this._$currentPath.length<4?this._$currentPath.length=0:(this._$vertices.push(this._$currentPath),this._$currentPath=U())}createRectVertices(t,e,i,s){return U(U(t,e,!1,t+i,e,!1,t+i,e+s,!1,t,e+s,!1))}}class Ct{constructor(){this.enabled=!1,this.parentMatrixA=1,this.parentMatrixB=0,this.parentMatrixC=0,this.parentMatrixD=0,this.parentMatrixE=1,this.parentMatrixF=0,this.parentMatrixG=0,this.parentMatrixH=0,this.parentMatrixI=1,this.ancestorMatrixA=1,this.ancestorMatrixB=0,this.ancestorMatrixC=0,this.ancestorMatrixD=0,this.ancestorMatrixE=1,this.ancestorMatrixF=0,this.ancestorMatrixG=0,this.ancestorMatrixH=0,this.ancestorMatrixI=1,this.parentViewportX=0,this.parentViewportY=0,this.parentViewportW=0,this.parentViewportH=0,this.minXST=1e-5,this.minYST=1e-5,this.minXPQ=1e-5,this.minYPQ=1e-5,this.maxXST=.99999,this.maxYST=.99999,this.maxXPQ=.99999,this.maxYPQ=.99999}enable(t,e,i,s,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x){const b=n.xMax-n.xMin,v=n.yMax-n.yMin,T=a.w,A=a.h,M=r.abs(r.ceil(b*h)),y=r.abs(r.ceil(v*h)),E=T>0?(a.x-n.xMin)/b:1e-5,C=A>0?(a.y-n.yMin)/v:1e-5,S=T>0?(a.x+a.w-n.xMin)/b:.99999,F=A>0?(a.y+a.h-n.yMin)/v:.99999;let B=M*E/i,w=y*C/s,R=(i-M*(1-S))/i,I=(s-y*(1-F))/s;if(B>=R){const t=E/(E+(1-S));B=r.max(t-1e-5,0),R=r.min(t+1e-5,1)}if(w>=I){const t=C/(C+(1-F));w=r.max(t-1e-5,0),I=r.min(t+1e-5,1)}this.enabled=!0,this.parentMatrixA=o,this.parentMatrixB=_,this.parentMatrixD=l,this.parentMatrixE=c,this.parentMatrixG=$,this.parentMatrixH=u,this.ancestorMatrixA=d,this.ancestorMatrixB=g,this.ancestorMatrixD=f,this.ancestorMatrixE=m,this.ancestorMatrixG=p,this.ancestorMatrixH=x,this.parentViewportX=t,this.parentViewportY=e,this.parentViewportW=i,this.parentViewportH=s,this.minXST=E,this.minYST=C,this.minXPQ=B,this.minYPQ=w,this.maxXST=S,this.maxYST=F,this.maxXPQ=R,this.maxYPQ=I}disable(){this.enabled=!1}}class St{constructor(t,e){this._$gl=t,this._$array=[],this._$map=V();const i=this._$gl.getProgramParameter(e,this._$gl.ACTIVE_UNIFORMS);for(let t=0;t0&&(t.assign--,t.method(t.array)))}}}class Ft{constructor(){this._$attributes=[],this._$count=0}get attributes(){return this._$attributes}get count(){return this._$count}set count(t){this._$count=t}clear(){this._$attributes.length=0,this._$count=0}}class Bt{constructor(t,e,i,s){this._$gl=t,this._$context=e,this._$program=this._$createProgram(i,s),this._$uniform=new St(t,this._$program),this._$instance=null}get instance(){return this._$instance||(this._$instance=new Ft),this._$instance}get uniform(){return this._$uniform}_$createProgram(t,i){const s=this._$gl.createProgram();s.id=e++;const r=this._$gl.createShader(this._$gl.VERTEX_SHADER);this._$gl.shaderSource(r,t),this._$gl.compileShader(r);const n=this._$gl.createShader(this._$gl.FRAGMENT_SHADER);return this._$gl.shaderSource(n,i),this._$gl.compileShader(n),this._$gl.attachShader(s,r),this._$gl.attachShader(s,n),this._$gl.linkProgram(s),this._$gl.detachShader(s,r),this._$gl.detachShader(s,n),this._$gl.deleteShader(r),this._$gl.deleteShader(n),s}_$attachProgram(){const t=this._$context.shaderList;t.currentProgramId!==this._$program.id&&(t.currentProgramId=this._$program.id,this._$gl.useProgram(this._$program))}drawArraysInstanced(t){this._$attachProgram(),this._$context.vao.bindInstnceArray(t),this._$gl.drawArraysInstanced(this._$gl.TRIANGLE_STRIP,0,4,t.count)}_$drawImage(){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindCommonVertexArray(),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$drawGradient(t,e){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindGradientVertexArray(t,e),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$stroke(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawElements(this._$gl.TRIANGLES,t.indexCount,this._$gl.UNSIGNED_SHORT,0)}_$fill(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t);const e=t.indexRanges,i=e[e.length-1];this._$gl.drawArrays(this._$gl.TRIANGLES,0,i.first+i.count)}_$containerClip(t,e,i){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.TRIANGLES,e,i)}_$drawPoints(t,e,i){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.POINTS,e,i)}}class wt{static FUNCTION_GRID_OFF(){return"\\n\\nvec2 applyMatrix(in vec2 vertex) {\\n mat3 matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n\\n vec2 position = (matrix * vec3(vertex, 1.0)).xy;\\n\\n return position;\\n}\\n\\n"}static FUNCTION_GRID_ON(t){return`\\n\\nvec2 applyMatrix(in vec2 vertex) {\\n mat3 parent_matrix = mat3(\\n u_highp[${t}].xyz,\\n u_highp[${t+1}].xyz,\\n u_highp[${t+2}].xyz\\n );\\n mat3 ancestor_matrix = mat3(\\n u_highp[${t+3}].xyz,\\n u_highp[${t+4}].xyz,\\n u_highp[${t+5}].xyz\\n );\\n vec2 parent_offset = vec2(u_highp[${t+2}].w, u_highp[${t+3}].w);\\n vec2 parent_size = vec2(u_highp[${t+4}].w, u_highp[${t+5}].w);\\n vec4 grid_min = u_highp[${t+6}];\\n vec4 grid_max = u_highp[${t+7}];\\n\\n vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy;\\n position = (position - parent_offset) / parent_size;\\n\\n vec4 ga = grid_min;\\n vec4 gb = grid_max - grid_min;\\n vec4 gc = vec4(1.0) - grid_max;\\n\\n vec2 pa = position;\\n vec2 pb = position - grid_min.st;\\n vec2 pc = position - grid_max.st;\\n\\n position = (ga.pq / ga.st) * min(pa, ga.st)\\n + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st)\\n + (gc.pq / gc.st) * max(vec2(0.0), pc);\\n\\n position = position * parent_size + parent_offset;\\n position = (ancestor_matrix * vec3(position, 1.0)).xy;\\n\\n return position;\\n}\\n\\n`}}class Rt{static TEMPLATE(t,e,i,s){const r=e-1,n=i?this.VARYING_UV_ON():"",a=i?this.STATEMENT_UV_ON():"";return`#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\nlayout (location = 1) in vec2 a_option1;\\nlayout (location = 2) in vec2 a_option2;\\nlayout (location = 3) in float a_type;\\n\\nuniform vec4 u_highp[${t}];\\n\\n${n}\\n\\n${s?wt.FUNCTION_GRID_ON(i?5:0):wt.FUNCTION_GRID_OFF()}\\n\\nfloat crossVec2(in vec2 v1, in vec2 v2) {\\n return v1.x * v2.y - v2.x * v1.y;\\n}\\n\\nvec2 perpendicularVec2(in vec2 v1) {\\n float face = u_highp[${r}][1];\\n\\n return face * vec2(v1.y, -v1.x);\\n}\\n\\nvec2 calculateNormal(in vec2 direction) {\\n vec2 normalized = normalize(direction);\\n return perpendicularVec2(normalized);\\n}\\n\\nvec2 calculateIntersection(in vec2 v1, in vec2 v2, in vec2 o1, in vec2 o2) {\\n float t = crossVec2(o2 - o1, v2) / crossVec2(v1, v2);\\n return (o1 + t * v1);\\n}\\n\\nvec2 calculateAnchor(in vec2 position, in float convex, out vec2 v1, out vec2 v2, out vec2 o1, out vec2 o2) {\\n float miter_limit = u_highp[${r}][2];\\n\\n vec2 a = applyMatrix(a_option1);\\n vec2 b = applyMatrix(a_option2);\\n\\n v1 = convex * (position - a);\\n v2 = convex * (b - position);\\n o1 = calculateNormal(v1) + a;\\n o2 = calculateNormal(v2) + position;\\n\\n vec2 anchor = calculateIntersection(v1, v2, o1, o2) - position;\\n return normalize(anchor) * min(length(anchor), miter_limit);\\n}\\n\\nvoid main() {\\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\\n float half_width = u_highp[${r}][0];\\n\\n vec2 position = applyMatrix(a_vertex);\\n vec2 offset = vec2(0.0);\\n vec2 v1, v2, o1, o2;\\n\\n if (a_type == 1.0 || a_type == 2.0) { // 線分\\n offset = calculateNormal(a_option2 * (applyMatrix(a_option1) - position));\\n } else if (a_type == 10.0) { // スクエア線端\\n offset = normalize(position - applyMatrix(a_option1));\\n offset += a_option2 * perpendicularVec2(offset);\\n } else if (a_type == 21.0) { // マイター結合(線分Bの凸側)\\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\\n } else if (a_type == 22.0) { // マイター結合(線分Aの凸側)\\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\\n } else if (a_type == 23.0) { // マイター結合(線分Aの凹側)\\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\\n } else if (a_type == 24.0) { // マイター結合(線分Bの凹側)\\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\\n } else if (a_type >= 30.0) { // ラウンド結合\\n float face = u_highp[${r}][1];\\n float rad = face * (a_type - 30.0) * 0.3488888889; /* 0.3488888889 = PI / 9.0 */\\n offset = mat2(cos(rad), sin(rad), -sin(rad), cos(rad)) * vec2(1.0, 0.0);\\n }\\n \\n offset *= half_width;\\n position += offset;\\n ${a}\\n\\n position /= viewport;\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n`}static VARYING_UV_ON(){return"\\nout vec2 v_uv;\\n"}static STATEMENT_UV_ON(){return"\\n mat3 uv_matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n mat3 inverse_matrix = mat3(\\n u_highp[3].xyz,\\n u_highp[4].xyz,\\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\\n );\\n\\n v_uv = (uv_matrix * vec3(a_vertex, 1.0)).xy;\\n v_uv += offset;\\n v_uv = (inverse_matrix * vec3(v_uv, 1.0)).xy;\\n"}}class It{static TEMPLATE(t,e,i,s){const r=i?this.ATTRIBUTE_BEZIER_ON():"",n=i?this.VARYING_BEZIER_ON():e?this.VARYING_UV_ON():"",a=i?this.STATEMENT_BEZIER_ON():e?this.STATEMENT_UV_ON():"";return`#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n${r}\\n\\nuniform vec4 u_highp[${t}];\\n\\n${n}\\n\\n${s?wt.FUNCTION_GRID_ON(e?5:0):wt.FUNCTION_GRID_OFF()}\\n\\nvoid main() {\\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\\n\\n ${a}\\n\\n vec2 pos = applyMatrix(a_vertex) / viewport;\\n pos = pos * 2.0 - 1.0;\\n gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\\n}\\n\\n`}static ATTRIBUTE_BEZIER_ON(){return"\\nlayout (location = 1) in vec2 a_bezier;\\n"}static VARYING_UV_ON(){return"\\nout vec2 v_uv;\\n"}static VARYING_BEZIER_ON(){return"\\nout vec2 v_bezier;\\n"}static STATEMENT_UV_ON(){return"\\n mat3 uv_matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n mat3 inverse_matrix = mat3(\\n u_highp[3].xyz,\\n u_highp[4].xyz,\\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\\n );\\n\\n v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy;\\n"}static STATEMENT_BEZIER_ON(){return"\\n v_bezier = a_bezier;\\n"}}class Pt{static FUNCTION_IS_INSIDE(){return"\\n\\nfloat isInside(in vec2 uv) {\\n return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0)));\\n}\\n\\n"}static STATEMENT_INSTANCED_COLOR_TRANSFORM_ON(){return"\\n src.rgb /= max(0.0001, src.a);\\n src = clamp(src * mul + add, 0.0, 1.0);\\n src.rgb *= src.a;\\n"}static STATEMENT_COLOR_TRANSFORM_ON(t){return`\\n vec4 mul = u_mediump[${t}];\\n vec4 add = u_mediump[${t+1}];\\n${Pt.STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()}\\n`}}class Nt{static SOLID_COLOR(){return"#version 300 es\\nprecision mediump float;\\n\\nuniform vec4 u_mediump;\\n\\nout vec4 o_color;\\n\\nvoid main() {\\n o_color = vec4(u_mediump.rgb * u_mediump.a, u_mediump.a);\\n}\\n\\n"}static BITMAP_CLIPPED(){return`#version 300 es\\nprecision mediump float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_mediump[3];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy;\\n\\n vec4 src = texture(u_texture, uv);\\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\\n o_color = src;\\n}`}static BITMAP_PATTERN(){return`#version 300 es\\nprecision mediump float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_mediump[3];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy);\\n \\n vec4 src = texture(u_texture, uv);\\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\\n o_color = src;\\n}`}static MASK(){return"#version 300 es\\nprecision mediump float;\\n\\nin vec2 v_bezier;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 px = dFdx(v_bezier);\\n vec2 py = dFdy(v_bezier);\\n\\n vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y);\\n float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f);\\n\\n if (alpha > 0.0) {\\n o_color = vec4(min(alpha, 1.0));\\n } else {\\n discard;\\n } \\n}\\n\\n"}}class kt{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getSolidColorShapeShader(t,e){const i=`s${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!1,e);const a=new Bt(this._$gl,this._$context,n,Nt.SOLID_COLOR());return this._$collection.set(i,a),a}getBitmapShapeShader(t,e,i){const s=`b${t?"y":"n"}${e?"y":"n"}${i?"y":"n"}`;if(this._$collection.has(s)){const t=this._$collection.get(s);if(t)return t}const r=(i?13:5)+(t?1:0),n=r;let a;a=t?Rt.TEMPLATE(r,n,!0,i):It.TEMPLATE(r,!0,!1,i);const h=e?Nt.BITMAP_PATTERN():Nt.BITMAP_CLIPPED(),o=new Bt(this._$gl,this._$context,a,h);return this._$collection.set(s,o),o}getMaskShapeShader(t,e){const i=`m${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!0,e);const a=new Bt(this._$gl,this._$context,n,Nt.MASK());return this._$collection.set(i,a),a}setSolidColorShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.highp;let u;n?($[0]=_.parentMatrixA,$[1]=_.parentMatrixB,$[2]=_.parentMatrixC,$[4]=_.parentMatrixD,$[5]=_.parentMatrixE,$[6]=_.parentMatrixF,$[8]=_.parentMatrixG,$[9]=_.parentMatrixH,$[10]=_.parentMatrixI,$[12]=_.ancestorMatrixA,$[13]=_.ancestorMatrixB,$[14]=_.ancestorMatrixC,$[16]=_.ancestorMatrixD,$[17]=_.ancestorMatrixE,$[18]=_.ancestorMatrixF,$[20]=_.ancestorMatrixG,$[21]=_.ancestorMatrixH,$[22]=_.ancestorMatrixI,$[3]=h,$[7]=o,$[11]=_.parentViewportX,$[15]=_.parentViewportY,$[19]=_.parentViewportW,$[23]=_.parentViewportH,$[24]=_.minXST,$[25]=_.minYST,$[26]=_.minXPQ,$[27]=_.minYPQ,$[28]=_.maxXST,$[29]=_.maxYST,$[30]=_.maxXPQ,$[31]=_.maxYPQ,u=32):($[0]=a[0],$[1]=a[1],$[2]=a[2],$[4]=a[3],$[5]=a[4],$[6]=a[5],$[8]=a[6],$[9]=a[7],$[10]=a[8],$[3]=h,$[7]=o,u=12),e&&($[u]=i,$[u+1]=s,$[u+2]=r);const d=t.mediump;d[0]=l[0],d[1]=l[1],d[2]=l[2],d[3]=l[3]*c}setBitmapShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x,b){const v=t.highp;let T;v[0]=a[0],v[1]=a[1],v[2]=a[2],v[4]=a[3],v[5]=a[4],v[6]=a[5],v[8]=a[6],v[9]=a[7],v[10]=a[8],v[12]=h[0],v[13]=h[1],v[14]=h[2],v[16]=h[3],v[17]=h[4],v[18]=h[5],v[11]=h[6],v[15]=h[7],v[19]=h[8],v[3]=o,v[7]=_,T=20,n&&(v[T]=l.parentMatrixA,v[T+1]=l.parentMatrixB,v[T+2]=l.parentMatrixC,v[T+4]=l.parentMatrixD,v[T+5]=l.parentMatrixE,v[T+6]=l.parentMatrixF,v[T+8]=l.parentMatrixG,v[T+9]=l.parentMatrixH,v[T+10]=l.parentMatrixI,v[T+12]=l.ancestorMatrixA,v[T+13]=l.ancestorMatrixB,v[T+14]=l.ancestorMatrixC,v[T+16]=l.ancestorMatrixD,v[T+17]=l.ancestorMatrixE,v[T+18]=l.ancestorMatrixF,v[T+20]=l.ancestorMatrixG,v[T+21]=l.ancestorMatrixH,v[T+22]=l.ancestorMatrixI,v[T+11]=l.parentViewportX,v[T+15]=l.parentViewportY,v[T+19]=l.parentViewportW,v[T+23]=l.parentViewportH,v[T+24]=l.minXST,v[T+25]=l.minYST,v[T+26]=l.minXPQ,v[T+27]=l.minYPQ,v[T+28]=l.maxXST,v[T+29]=l.maxYST,v[T+30]=l.maxXPQ,v[T+31]=l.maxYPQ,T=52),e&&(v[T]=i,v[T+1]=s,v[T+2]=r);const A=t.mediump;A[0]=c,A[1]=$,A[4]=u,A[5]=d,A[6]=g,A[7]=f,A[8]=m,A[9]=p,A[10]=x,A[11]=b}setMaskShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u=null){const d=t.highp;e&&u?(d[0]=u.parentMatrixA,d[1]=u.parentMatrixB,d[2]=u.parentMatrixC,d[4]=u.parentMatrixD,d[5]=u.parentMatrixE,d[6]=u.parentMatrixF,d[8]=u.parentMatrixG,d[9]=u.parentMatrixH,d[10]=u.parentMatrixI,d[12]=u.ancestorMatrixA,d[13]=u.ancestorMatrixB,d[14]=u.ancestorMatrixC,d[16]=u.ancestorMatrixD,d[17]=u.ancestorMatrixE,d[18]=u.ancestorMatrixF,d[20]=u.ancestorMatrixG,d[21]=u.ancestorMatrixH,d[22]=u.ancestorMatrixI,d[3]=c,d[7]=$,d[11]=u.parentViewportX,d[15]=u.parentViewportY,d[19]=u.parentViewportW,d[23]=u.parentViewportH,d[24]=u.minXST,d[25]=u.minYST,d[26]=u.minXPQ,d[27]=u.minYPQ,d[28]=u.maxXST,d[29]=u.maxYST,d[30]=u.maxXPQ,d[31]=u.maxYPQ):(d[0]=i,d[1]=s,d[2]=r,d[4]=n,d[5]=a,d[6]=h,d[8]=o,d[9]=_,d[10]=l,d[3]=c,d[7]=$)}setMaskShapeUniformIdentity(t,e,i){const s=t.highp;s[0]=1,s[1]=0,s[2]=0,s[4]=0,s[5]=1,s[6]=0,s[8]=0,s[9]=0,s[10]=1,s[3]=e,s[7]=i}}class Lt{static TEMPLATE(t,e,i,s,r){const n=i?this.STATEMENT_GRADIENT_TYPE_RADIAL(e,s):this.STATEMENT_GRADIENT_TYPE_LINEAR(e);let a;switch(r){case"reflect":a="1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)";break;case"repeat":a="fract(t)";break;default:a="clamp(t, 0.0, 1.0)"}return`#version 300 es\\nprecision highp float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_highp[${t}];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 p = v_uv;\\n ${n}\\n t = ${a};\\n o_color = texture(u_texture, vec2(t, 0.5));\\n}\\n\\n`}static STATEMENT_GRADIENT_TYPE_LINEAR(t){return`\\n vec2 a = u_highp[${t}].xy;\\n vec2 b = u_highp[${t}].zw;\\n\\n vec2 ab = b - a;\\n vec2 ap = p - a;\\n\\n float t = dot(ab, ap) / dot(ab, ab);\\n`}static STATEMENT_GRADIENT_TYPE_RADIAL(t,e){return`\\n float radius = u_highp[${t}][0];\\n\\n vec2 coord = p / radius;\\n ${e?this.STATEMENT_FOCAL_POINT_ON(t):this.STATEMENT_FOCAL_POINT_OFF()}\\n`}static STATEMENT_FOCAL_POINT_OFF(){return"\\n float t = length(coord);\\n"}static STATEMENT_FOCAL_POINT_ON(t){return`\\n vec2 focal = vec2(u_highp[${t}][1], 0.0);\\n\\n vec2 dir = normalize(coord - focal);\\n\\n float a = dot(dir, dir);\\n float b = 2.0 * dot(dir, focal);\\n float c = dot(focal, focal) - 1.0;\\n float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);\\n\\n float t = distance(focal, coord) / distance(focal, focal + dir * x);\\n`}}class Ot{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getGradientShapeShader(t,e,i,s,r){const n=this.createCollectionKey(t,e,i,s,r);if(this._$collection.has(n)){const t=this._$collection.get(n);if(t)return t}const a=(e?13:5)+(t?1:0)+1,h=a-1;let o;o=t?Rt.TEMPLATE(a,h,!0,e):It.TEMPLATE(a,!0,!1,e);const _=new Bt(this._$gl,this._$context,o,Lt.TEMPLATE(a,h,i,s,r));return this._$collection.set(n,_),_}createCollectionKey(t,e,i,s,r){const n=t?"y":"n",a=e?"y":"n",h=i?"y":"n",o=i&&s?"y":"n";let _=0;switch(r){case"reflect":_=1;break;case"repeat":_=2}return`${n}${a}${h}${o}${_}`}setGradientShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.highp;d[0]=a[0],d[1]=a[1],d[2]=a[2],d[4]=a[3],d[5]=a[4],d[6]=a[5],d[8]=a[6],d[9]=a[7],d[10]=a[8],d[12]=h[0],d[13]=h[1],d[14]=h[2],d[16]=h[3],d[17]=h[4],d[18]=h[5],d[11]=h[6],d[15]=h[7],d[19]=h[8],d[3]=o,d[7]=_;let g=20;n&&(d[g]=l.parentMatrixA,d[g+1]=l.parentMatrixB,d[g+2]=l.parentMatrixC,d[g+4]=l.parentMatrixD,d[g+5]=l.parentMatrixE,d[g+6]=l.parentMatrixF,d[g+8]=l.parentMatrixG,d[g+9]=l.parentMatrixH,d[g+10]=l.parentMatrixI,d[g+12]=l.ancestorMatrixA,d[g+13]=l.ancestorMatrixB,d[g+14]=l.ancestorMatrixC,d[g+16]=l.ancestorMatrixD,d[g+17]=l.ancestorMatrixE,d[g+18]=l.ancestorMatrixF,d[g+20]=l.ancestorMatrixG,d[g+21]=l.ancestorMatrixH,d[g+22]=l.ancestorMatrixI,d[g+11]=l.parentViewportX,d[g+15]=l.parentViewportY,d[g+19]=l.parentViewportW,d[g+23]=l.parentViewportH,d[g+24]=l.minXST,d[g+25]=l.minYST,d[g+26]=l.minXPQ,d[g+27]=l.minYPQ,d[g+28]=l.maxXST,d[g+29]=l.maxYST,d[g+30]=l.maxXPQ,d[g+31]=l.maxYPQ,g=52),e&&(d[g]=i,d[g+1]=s,d[g+2]=r,g+=4),c?(d[g]=$[5],d[g+1]=u):(d[g]=$[0],d[g+1]=$[1],d[g+2]=$[2],d[g+3]=$[3])}}class Ut{static TEXTURE(){return"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 position = a_vertex * 2.0 - 1.0;\\n gl_Position = vec4(position, 0.0, 1.0);\\n}\\n\\n"}static BLEND(){return"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[4];\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 offset = u_highp[0].xy;\\n vec2 size = u_highp[0].zw;\\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * size + offset;\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= viewport;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n"}static INSTANCE_BLEND(){return"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[5];\\n\\nout vec2 v_src_coord;\\nout vec2 v_dst_coord;\\n\\nvoid main() {\\n vec4 rect = vec4(u_highp[0].x, u_highp[0].y, u_highp[0].z, u_highp[0].w);\\n vec2 size = vec2(u_highp[4].x, u_highp[4].y);\\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n v_src_coord = a_vertex * rect.zw + rect.xy;\\n v_dst_coord = a_vertex;\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * size;\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= viewport;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n"}static INSTANCE(){return"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\nlayout (location = 1) in vec4 a_rect;\\nlayout (location = 2) in vec4 a_size;\\nlayout (location = 3) in vec2 a_offset;\\nlayout (location = 4) in vec4 a_matrix;\\nlayout (location = 5) in vec4 a_mul;\\nlayout (location = 6) in vec4 a_add;\\n\\nout vec2 v_coord;\\nout vec4 mul;\\nout vec4 add;\\n\\nvoid main() {\\n v_coord = a_vertex * a_rect.zw + a_rect.xy;\\n mul = a_mul;\\n add = a_add;\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * a_size.xy;\\n mat3 matrix = mat3(a_matrix.x, a_matrix.y, 0.0, a_matrix.z, a_matrix.w, 0.0, a_offset.x, a_offset.y, 1.0);\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= a_size.zw;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n"}static BLEND_CLIP(){return"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[4];\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 offset = u_highp[0].xy;\\n vec2 size = u_highp[0].zw;\\n mat3 inv_matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position *= viewport;\\n position = (inv_matrix * vec3(position, 1.0)).xy;\\n position = (position - offset) / size;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n"}}class Dt{static TEMPLATE(t,e,i){let s="";for(let t=1;t>16)/255,h[a++]=(e>>8&255)/255,h[a++]=(255&e)/255,h[a++]=s[t]}for(let t=r;tthis._$vertexBufferData.length){const t=new o(2*this._$vertexBufferData.length);t.set(this._$vertexBufferData),this._$vertexBufferData=t}}static _$expandIndexBufferIfNeeded(t){if(this._$indexBufferPos+t>this._$indexBufferData.length){const t=new l(2*this._$indexBufferData.length);t.set(this._$indexBufferData),this._$indexBufferData=t}}static _$generateLineSegment(t){const e=t.length-5;for(let i=0;it*s-i*e;class ee{constructor(t){this._$gl=t,this._$fillVertexArrayPool=[],this._$strokeVertexArrayPool=[],this._$boundVertexArray=null,this._$fillAttrib_vertex=0,this._$fillAttrib_bezier=1,this._$strokeAttrib_vertex=0,this._$strokeAttrib_option1=1,this._$strokeAttrib_option2=2,this._$strokeAttrib_type=3,this._$vertexBufferData=new Float32Array([0,0,0,1,1,0,1,1]),this._$attributeVertexBuffer=t.createBuffer(),this._$attributeBuffer=new Float32Array(22),this._$instanceVertexArray=this._$getCommonVertexArray(),this._$commonVertexArray=this._$getVertexArray(0,1)}_$getCommonVertexArray(){const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,this._$attributeVertexBuffer),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(1,4,this._$gl.FLOAT,!1,88,0),this._$gl.vertexAttribDivisor(1,1),this._$gl.enableVertexAttribArray(2),this._$gl.vertexAttribPointer(2,4,this._$gl.FLOAT,!1,88,16),this._$gl.vertexAttribDivisor(2,1),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(3,2,this._$gl.FLOAT,!1,88,32),this._$gl.vertexAttribDivisor(3,1),this._$gl.enableVertexAttribArray(4),this._$gl.vertexAttribPointer(4,4,this._$gl.FLOAT,!1,88,40),this._$gl.vertexAttribDivisor(4,1),this._$gl.enableVertexAttribArray(5),this._$gl.vertexAttribPointer(5,4,this._$gl.FLOAT,!1,88,56),this._$gl.vertexAttribDivisor(5,1),this._$gl.enableVertexAttribArray(6),this._$gl.vertexAttribPointer(6,4,this._$gl.FLOAT,!1,88,72),this._$gl.vertexAttribDivisor(6,1),t}_$getVertexArray(t,e){const i=this._$gl.createVertexArray();this.bind(i);const s=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s),this._$vertexBufferData[0]=t,this._$vertexBufferData[2]=t,this._$vertexBufferData[4]=e,this._$vertexBufferData[6]=e,this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$vertexBufferData,this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),i}_$getFillVertexArray(){if(this._$fillVertexArrayPool.length){const t=this._$fillVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(this._$fillAttrib_vertex,2,this._$gl.FLOAT,!1,16,0),this._$gl.vertexAttribPointer(this._$fillAttrib_bezier,2,this._$gl.FLOAT,!1,16,8),t}_$getStrokeVertexArray(){if(this._$strokeVertexArrayPool.length){const t=this._$strokeVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e);const i=this._$gl.createBuffer();return t.indexBuffer=i,t.indexLength=0,this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER,i),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.enableVertexAttribArray(2),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(this._$strokeAttrib_vertex,2,this._$gl.FLOAT,!1,28,0),this._$gl.vertexAttribPointer(this._$strokeAttrib_option1,2,this._$gl.FLOAT,!1,28,8),this._$gl.vertexAttribPointer(this._$strokeAttrib_option2,2,this._$gl.FLOAT,!1,28,16),this._$gl.vertexAttribPointer(this._$strokeAttrib_type,1,this._$gl.FLOAT,!1,28,24),t}createFill(t){const e=Zt.generate(t),i=e.vertexBufferData,s=this._$getFillVertexArray();return s.indexRanges=e.indexRanges,this.bind(s),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s.vertexBuffer),s.vertexLengththis._$attributeBuffer.length&&(this._$attributeBuffer=new Float32Array(t.attributes.length),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW)),this._$attributeBuffer.set(t.attributes),this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER,0,this._$attributeBuffer.subarray(0,t.attributes.length))}bindCommonVertexArray(){this.bind(this._$commonVertexArray)}bindGradientVertexArray(t,e){const i=this._$getVertexArray(t,e);this.bind(i)}}class ie{constructor(t,e){this._$context=t,this._$gl=e,this._$clips=[],this._$poolClip=[],this._$clipStatus=!1,this._$containerClip=!1,this._$currentClip=!1}get containerClip(){return this._$containerClip}set containerClip(t){this._$containerClip=t}_$onClear(t){t&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0)}_$onBind(t){!t&&this._$currentClip?(this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1):t&&!this._$currentClip&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0,this._$endClipDef())}_$onClearRect(){this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1}_$enterClip(){this._$currentClip||(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0);const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");t.mask=!0,++t.clipLevel}_$beginClipDef(){const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.stencilMask(1<7&&(this._$unionStencilMask(e,a,h),n=e)}n>e+1&&this._$unionStencilMask(e,a,h)}_$unionStencilMask(t,e,i){const s=this._$context.path.createRectVertices(0,0,e,i),r=this._$context.vao.createFill(s);D(s.pop()),D(s);const n=this._$context.shaderList.shapeShaderVariants,a=n.getMaskShapeShader(!1,!1),h=a.uniform;n.setMaskShapeUniformIdentity(h,e,i);const o=r.indexRanges[0];this._$gl.stencilFunc(this._$gl.LEQUAL,1<this._$maxTextureSize?this._$maxTextureSize/i:1}drawInstacedArray(){this.blend.drawInstacedArray()}clearInstacedArray(){this.blend.clearInstacedArray()}bindRenderBuffer(t){this._$frameBufferManager.bindRenderBuffer(),this._$gl.clearColor(0,0,0,0),this._$gl.clear(this._$gl.COLOR_BUFFER_BIT|this._$gl.STENCIL_BUFFER_BIT),this._$viewportWidth=t.w,this._$viewportHeight=t.h,this._$gl.viewport(t.x,t.y,t.w,t.h),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(t.x,t.y,t.w,t.h)}getTextureFromRect(t){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t.index),s=e.currentAttachment,r=e.createTextureAttachment(t.w,t.h);this._$bind(r),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(i,-t.x,-i.height+t.h+t.y,i.width,i.height),this.restore();const n=r.texture;return e.releaseAttachment(r),this._$bind(s),n}drawBitmap(t){const e=this._$shaderList.blendShaderVariants,i=e.getNormalBlendShader(!1);e.setNormalBlendUniform(i.uniform,0,0,t.width,t.height,this._$matrix,this._$viewportWidth,this._$viewportHeight,!1,1,1,1,1,0,0,0,0),this._$frameBufferManager.textureManager.bind0(t,this._$imageSmoothingEnabled),this.blend.toOperation("normal"),i._$drawImage()}drawTextureFromRect(t,e){const i=this._$frameBufferManager,s=i.currentAttachment;this.bindRenderBuffer(e),i.transferTexture(e);const r=i.textureManager.getAtlasTexture(e.index),n=i.createTextureAttachmentFrom(r);this._$bind(n),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(e.x,e.y,e.w,e.h),this._$gl.clearColor(0,0,0,0),this._$gl.disable(this._$gl.SCISSOR_TEST),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(t,e.x,r.height-e.h-e.y,t.width,t.height),this.restore(),i.releaseAttachment(n),this._$bind(s),i.textureManager.release(t)}stopStencil(){this._$mask._$onClearRect()}_$bind(t=null){if(!t)return;this._$frameBufferManager.bind(t);const e=t.color,i=t.stencil,s=t.width,r=t.height;this._$viewportWidth===s&&this._$viewportHeight===r||(this._$viewportWidth=s,this._$viewportHeight=r,this._$gl.viewport(0,0,s,r)),(e&&e.dirty||i&&i.dirty)&&(e&&(e.dirty=!1),i&&(i.dirty=!1),this._$gl.clearColor(0,0,0,0),this.clearRect(0,0,this._$viewportWidth,this._$viewportHeight),this._$gl.clearColor(this._$clearColorR,this._$clearColorG,this._$clearColorB,this._$clearColorA),this._$mask._$onClear(t.mask)),this._$mask._$onBind(t.mask)}setTransform(t,e,i,s,r,n){this._$matrix[0]=t,this._$matrix[1]=e,this._$matrix[3]=i,this._$matrix[4]=s,this._$matrix[6]=r,this._$matrix[7]=n}setMaxSize(t,e){this._$frameBufferManager.setMaxSize(t,e)}transform(t,e,i,s,r,n){const a=this._$matrix[0],h=this._$matrix[1],o=this._$matrix[3],_=this._$matrix[4],l=this._$matrix[6],c=this._$matrix[7];this._$matrix[0]=t*a+e*o,this._$matrix[1]=t*h+e*_,this._$matrix[3]=i*a+s*o,this._$matrix[4]=i*h+s*_,this._$matrix[6]=r*a+n*o+l,this._$matrix[7]=r*h+n*_+c}debug(t=0){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t),s=e.currentAttachment,r=e.createTextureAttachmentFrom(i);this._$bind(r);const n=new Uint8Array(i.width*i.height*4);this._$gl.readPixels(0,0,i.width,i.height,this._$gl.RGBA,this._$gl.UNSIGNED_BYTE,n);const a=document.createElement("canvas");a.width=i.width,a.height=i.height;const h=a.getContext("2d"),o=new ImageData(i.width,i.height);for(let t=0;ts.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this.fillStyle;let r,n,a,h=this._$matrix;const o=this._$grid.enabled;if(s instanceof mt){const t=s.stops,e="linearRGB"===s.rgb;if(r=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(r,!0),this._$frameBufferManager.bindRenderBuffer(),n=this._$shaderList.gradientShapeShaderVariants,"linear"===s.type)a=n.getGradientShapeShader(!1,o,!1,!1,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,s.points,0);else{h=this._$stack[this._$stack.length-1];const t=0!==s.focalPointRatio;a=n.getGradientShapeShader(!1,o,!0,t,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,s.points,s.focalPointRatio)}}else if(s instanceof pt){h=this._$stack[this._$stack.length-1];const t=s.colorTransform;r=s.texture,this._$frameBufferManager.textureManager.bind0(r,this._$imageSmoothingEnabled),n=this._$shaderList.shapeShaderVariants,a=n.getBitmapShapeShader(!1,s.repeat,o),t?n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,1,1,1,this._$globalAlpha,0,0,0,0)}else n=this._$shaderList.shapeShaderVariants,a=n.getSolidColorShapeShader(!1,this._$grid.enabled),n.setSolidColorShapeUniform(a.uniform,!1,0,0,0,o,h,this._$viewportWidth,this._$viewportHeight,this._$grid,s,this._$globalAlpha);const _=this._$shaderList.shapeShaderVariants,l=_.getMaskShapeShader(!1,o);_.setMaskShapeUniform(l.uniform,o,h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],this._$viewportWidth,this._$viewportHeight,this._$grid),this._$gl.enable(this._$gl.STENCIL_TEST),this._$gl.stencilMask(255),this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.colorMask(!1,!1,!1,!1),l._$fill(i),this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.NOTEQUAL,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.ZERO,this._$gl.ZERO),this._$gl.colorMask(!0,!0,!0,!0),a._$fill(i),this._$gl.disable(this._$gl.STENCIL_TEST),this.releaseFillVertexArray(i)}releaseFillVertexArray(t){this._$vao.releaseFill(t);const e=t.indexRanges;for(let t=0;ta.width||i>a.height||0>e&&0>=s+e||0>i&&0>=n+i||(this._$maskBounds.xMin=r.max(0,r.min(this._$maskBounds.xMin,e)),this._$maskBounds.yMin=r.max(0,r.min(this._$maskBounds.yMin,i)),this._$maskBounds.xMax=r.min(a.width,r.min(this._$maskBounds.xMax,s)),this._$maskBounds.yMax=r.min(a.height,r.min(this._$maskBounds.yMax,n)),0))}_$endClipDef(){this._$mask._$endClipDef()}_$leaveClip(){this.drawInstacedArray(),this._$mask._$leaveClip()}_$drawContainerClip(){this._$mask._$drawContainerClip()}closePath(){this._$path.close()}stroke(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createStroke(t,this.lineCap,this.lineJoin);let s=this._$matrix;const n=this.strokeStyle;let a=r.sign(s[0]*s[4]);a>0&&0!==s[1]&&0!==s[3]&&(a=-r.sign(s[1]*s[3]));let h,o,_=.5*this.lineWidth;this._$grid.enabled?(h=r.abs(this._$grid.ancestorMatrixA+this._$grid.ancestorMatrixD),o=r.abs(this._$grid.ancestorMatrixB+this._$grid.ancestorMatrixE)):(h=r.abs(s[0]+s[3]),o=r.abs(s[1]+s[4]));const l=r.min(h,o),c=r.max(h,o);_*=c*(1-.3*r.cos(.5*r.PI*(l/c))),_=r.max(1,_);const $=this._$grid.enabled;let u,d,g;if(n instanceof mt){"radial"===n.type&&(s=this._$stack[this._$stack.length-1]);const t=n.stops,e="linearRGB"===n.rgb;if(u=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(u,!0),d=this._$shaderList.gradientShapeShaderVariants,"linear"===n.type)g=d.getGradientShapeShader(!0,$,!1,!1,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,n.points,0);else{s=this._$stack[this._$stack.length-1];const t=0!==n.focalPointRatio;g=d.getGradientShapeShader(!0,$,!0,t,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,n.points,n.focalPointRatio)}}else if(n instanceof pt){s=this._$stack[this._$stack.length-1];const t=n.colorTransform;u=n.texture,this._$frameBufferManager.textureManager.bind0(u),d=this._$shaderList.shapeShaderVariants,g=d.getBitmapShapeShader(!0,n.repeat,this._$grid.enabled),t?d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,1,1,1,this._$globalAlpha,0,0,0,0)}else d=this._$shaderList.shapeShaderVariants,g=d.getSolidColorShapeShader(!0,this._$grid.enabled),d.setSolidColorShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,this._$viewportWidth,this._$viewportHeight,this._$grid,n,this._$globalAlpha);g._$stroke(i),this._$vao.releaseStroke(i)}arc(t,e,i){this._$path.drawCircle(t,e,i)}clip(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this._$shaderList.shapeShaderVariants,r=s.getMaskShapeShader(!1,!1),n=r.uniform;s.setMaskShapeUniform(n,!1,this._$matrix[0],this._$matrix[1],this._$matrix[2],this._$matrix[3],this._$matrix[4],this._$matrix[5],this._$matrix[6],this._$matrix[7],this._$matrix[8],this._$viewportWidth,this._$viewportHeight,null),this._$mask._$onClip(i,this._$matrix,this._$viewportWidth,this._$viewportHeight)||(r._$fill(i),this.beginPath())}save(){const t=this._$matrix;this._$stack.push(O(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])),this._$mask._$onSave()}restore(){var t;this._$stack.length&&(t=this._$matrix,M.push(t),this._$matrix=this._$stack.pop()||O()),this._$mask._$onRestore()}createPattern(t,e,i){return new pt(t,e,i)}createLinearGradient(t,e,i,s,r="rgb",n="pad"){return(new mt).linear(t,e,i,s,r,n)}createRadialGradient(t,e,i,s,r,n,a="rgb",h="pad",o=0){return(new mt).radial(t,e,i,s,r,n,a,h,o)}_$applyBlurFilter(t,e,i){const s=this._$frameBufferManager,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");const a=n.width,h=n.height;s.textureManager.bind0(t,!0);const o=r.ceil(.5*i),_=1-(o-.5*i),l=1+i,c=this._$shaderList.filterShaderVariants,$=c.getBlurFilterShader(o);c.setBlurFilterUniform($.uniform,a,h,e,_,l),$._$drawImage()}_$applyBitmapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g=null,f=null,m=null,p=0,x=0,b=0,v=0,T=0,A=0,M=0,y=0){const E=this._$frameBufferManager,C="inner"===$,S=E.currentAttachment,F=E.getTextureFromCurrentAttachment();let B=null;const w=null!==g&&null!==f&&null!==m;let R;null!==g&&null!==f&&null!==m&&(B=this._$gradientLUT.generateForFilter(g,f,m)),C?w&&B?E.textureManager.bind02(t,B,!0):E.textureManager.bind0(t):(R=this._$frameBufferManager.createTextureAttachment(e,i),this._$bind(R),w&&B?E.textureManager.bind012(t,F,B,!0):E.textureManager.bind01(t,F));const I=!(C||"full"===$&&u),P=!(e===h&&i===o&&0===_&&0===l),N=!(1===d),k=this._$shaderList.filterShaderVariants,L=k.getBitmapFilterShader(I,P,c,$,u,N,w);k.setBitmapFilterUniform(L.uniform,e,i,s,r,n,a,h,o,_,l,c,d,p,x,b,v,T,A,M,y,I,P,N,w),C?u?this.blend.toSourceIn():this.blend.toSourceAtop():this.blend.toOneZero(),L._$drawImage(),C||E.releaseAttachment(S,!0)}_$applyColorMatrixFilter(t,e){this._$frameBufferManager.textureManager.bind0(t,!0);const i=this._$shaderList.filterShaderVariants,s=i.getColorMatrixFilterShader();i.setColorMatrixFilterUniform(s.uniform,e),this.blend.reset(),s._$drawImage()}_$applyConvolutionFilter(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.width,u=t.height,d=this._$frameBufferManager.createTextureAttachment($,u);this._$bind(d),this._$frameBufferManager.textureManager.bind0(t,!0);const g=this._$shaderList.filterShaderVariants,f=g.getConvolutionFilterShader(e,i,a,h);g.setConvolutionFilterUniform(f.uniform,$,u,s,r,n,h,o,_,l,c),this.blend.reset(),f._$drawImage()}_$applyDisplacementMapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.width,g=t.height,f=this._$frameBufferManager.createTextureAttachment(d,g);this._$bind(f),r||(r={x:0,y:0});const m=this._$frameBufferManager.createTextureFromImage(e);this._$frameBufferManager.textureManager.bind01(t,m);const p=this._$shaderList.filterShaderVariants,x=p.getDisplacementMapFilterShader(n,a,_);p.setDisplacementMapFilterUniform(x.uniform,e.width,e.height,i,s,r.x,r.y,h,o,_,l,c,$,u),this.blend.reset(),x._$drawImage(),this._$frameBufferManager.releaseTexture(m)}_$startLayer(t){this._$positions.push(t),this._$blends.push(this._$isLayer),this._$isLayer=!0}_$endLayer(){const t=this._$positions.pop();t&&B(t),this._$isLayer=!!this._$blends.pop()}_$saveAttachment(t,e,i=!1){this.drawInstacedArray();const s=this._$frameBufferManager;this._$attachmentArray.push(s.currentAttachment),this._$bind(s.createCacheAttachment(t,e,i))}_$restoreAttachment(t=!1){const e=this._$frameBufferManager;e.releaseAttachment(e.currentAttachment,t),this._$bind(this._$attachmentArray.pop())}getCurrentPosition(){return this._$positions[this._$positions.length-1]}textureScale(t,e){const i=r.max(t,e);return i>this._$maxTextureSize?this._$maxTextureSize/i:1}}class ne extends gt{constructor(){super(),this._$recodes=null,this._$maxAlpha=0,this._$canDraw=!1,this._$uniqueKey="",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0),this._$bitmapId=0,this._$mode="shape"}_$clip(t,e){if(!this._$recodes)return;const i=this._$getBounds(),n=q(i,e);B(i);const a=r.ceil(r.abs(n.xMax-n.xMin)),h=r.ceil(r.abs(n.yMax-n.yMin));switch(B(n),!0){case 0===a:case 0===h:case a===-1/0:case h===-1/0:case a===s:case h===s:return}t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),this._$runCommand(t,this._$recodes,null,!0),t.clip()}_$createCacheKey(){if(!this._$recodes)return"";let t=0;for(let e=0;e0&&this._$canApply(a);let T=F(0,m,0,p);if(v&&a)for(let t=0;tM.width||f-T.yMin>M.height)return void B(T);if(0>d+T.xMax||0>f+T.yMax)return void B(T);if(B(T),""===this._$uniqueKey&&(!l&&this._$loaderInfoId>-1&&this._$characterId>-1?this._$uniqueKey=`${this._$loaderInfoId}@${this._$characterId}`:this._$uniqueKey=this._$createCacheKey()),"bitmap"===this._$mode)this._$cacheKeys.length||(this._$cacheKeys=rt.generateKeys(this._$uniqueKey));else if(!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$uniqueKey,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=A.currentAttachment;s&&s.mask&&t.stopStencil();let n=0,a=0;if("shape"===this._$mode){n=r.ceil(r.abs(c.xMax-c.xMin)*x),a=r.ceil(r.abs(c.yMax-c.yMin)*b);const e=t._$getTextureScale(n,a);e<1&&(n*=e,a*=e)}else n=r.ceil(r.abs(c.xMax-c.xMin)),a=r.ceil(r.abs(c.yMax-c.yMin));if(t.cachePosition=A.createCachePosition(n,a),t.bindRenderBuffer(t.cachePosition),t.reset(),"shape"===this._$mode?t.setTransform(x,0,0,b,-c.xMin*x,-c.yMin*b):t.setTransform(1,0,0,1,-c.xMin,-c.yMin),l){const i=le.scaleX,s=P(i,0,0,i,0,0),n=H(s,_);N(s);const a=this._$matrixBase,h=P(a[0],a[1],a[2],a[3],a[4]*i-d,a[5]*i-f),o=H(h,n),l=o[4]-(e[4]-d),$=o[5]-(e[5]-f);N(o);const u=q(c,n),g=+u.xMax,m=+u.xMin,p=+u.yMax,x=+u.yMin,b=r.ceil(r.abs(g-m)),v=r.ceil(r.abs(p-x));B(u),t.grid.enable(m,x,b,v,c,this._$scale9Grid,i,n[0],n[1],n[2],n[3],n[4],n[5],h[0],h[1],h[2],h[3],h[4]-l,h[5]-$),N(n),N(h)}this._$runCommand(t,this._$recodes,i,!1),l&&t.grid.disable(),A.transferTexture(t.cachePosition),rt.set(this._$cacheKeys,t.cachePosition),t._$bind(s)}let y=0,E=0;if(v&&a){const i=this._$createBitmapTexture(t,t.cachePosition,x,b,m,p),s=this._$drawFilter(t,e,a,m,p,i);s.offsetX&&(y=s.offsetX),s.offsetY&&(E=s.offsetY),t.cachePosition=s}if(v||"bitmap"!==this._$mode){const i=r.atan2(e[1],e[0]),s=r.atan2(-e[2],e[3]);if(v||!i&&!s)t.setTransform(1,0,0,1,d-y,f-E);else{const n=c.xMin*x,a=c.yMin*b,h=r.cos(i),o=r.sin(i),_=r.cos(s),l=r.sin(s);t.setTransform(h,o,-l,_,n*h-a*l+e[4],n*o+a*_+e[5])}}else t.setTransform(e[0],e[1],e[2],e[3],c.xMin*e[0]+c.yMin*e[2]+e[4],c.xMin*e[1]+c.yMin*e[3]+e[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled="shape"===this._$mode,t.globalCompositeOperation=n,t.drawInstance(d-y,f-E,u,g,i),t.cachePosition=null),B(c)}setupStroke(t,e,i,s,r){switch(t.lineWidth=e,i){case 0:t.lineCap="none";break;case 1:t.lineCap="round";break;case 2:t.lineCap="square"}switch(s){case 0:t.lineJoin="bevel";break;case 1:t.lineJoin="miter";break;case 2:t.lineJoin="round"}t.miterLimit=r}createGradientStyle(t,e,i,s,n,a,h,o=null){let _,l="pad";switch(n){case 0:l="reflect";break;case 1:l="repeat"}if(0===e){const e=(t=>{const e=-819.2*t[0]-819.2*t[2]+t[4],i=819.2*t[0]-819.2*t[2]+t[4],s=-819.2*t[0]+819.2*t[2]+t[4],n=-819.2*t[1]-819.2*t[3]+t[5],a=819.2*t[1]-819.2*t[3]+t[5];let h=s-e,o=-819.2*t[1]+819.2*t[3]+t[5]-n;const _=r.sqrt(h*h+o*o);_?(h/=_,o/=_):(h=0,o=0);const l=(i-e)*h+(a-n)*o;return w(e+l*h,n+l*o,i,a)})(s);_=t.createLinearGradient(e[0],e[1],e[2],e[3],a?"rgb":"linearRGB",l)}else t.save(),t.transform(s[0],s[1],s[2],s[3],s[4],s[5]),_=t.createRadialGradient(0,0,0,0,0,819.2,a?"rgb":"linearRGB",l,h);for(let t=0;t-1&&this._$characterId>-1&&rt.removeCache(`${this._$loaderInfoId}@${this._$characterId}`))}}class ae extends ne{_$clip(t,e){let i=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(i=H(e,n));const a=this._$getBounds(),h=q(a,i);B(a);const o=r.ceil(r.abs(h.xMax-h.xMin)),_=r.ceil(r.abs(h.yMax-h.yMin));switch(B(h),!0){case 0===o:case 0===_:case o===-1/0:case _===-1/0:case o===s:case _===s:return}super._$clip(t,i),i!==e&&N(i)}_$draw(t,e,i){if(!this._$visible||!this._$maxAlpha||!this._$canDraw)return;let s=i;const r=this._$colorTransform;if(1===r[0]&&1===r[1]&&1===r[2]&&1===r[3]&&0===r[4]&&0===r[5]&&0===r[6]&&0===r[7]||(s=W(i,r)),!G(s[3]+s[7]/255,0,1,0))return void(s!==i&&L(s));let n=e;const a=this._$matrix;1===a[0]&&0===a[1]&&0===a[2]&&1===a[3]&&0===a[4]&&0===a[5]||(n=H(e,a)),super._$draw(t,n,s,this._$blendMode,this._$filters),n!==e&&N(n),s!==i&&L(s)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$recodes=null,super._$remove(),ce.push(this)}}class he extends gt{constructor(){super(),this._$background=!1,this._$backgroundColor=16777215,this._$border=!1,this._$borderColor=0,this._$wordWrap=!1,this._$textData=U(),this._$textAreaActive=!1,this._$thickness=0,this._$thicknessColor=0,this._$limitWidth=0,this._$limitHeight=0,this._$autoSize="none",this._$widthTable=U(),this._$heightTable=U(),this._$objectTable=U(),this._$textHeightTable=U(),this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$maxScrollV=null,this._$scrollV=1,this._$textHeight=0,this._$verticalAlign="top",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}get width(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.xMax-t.xMin);switch(B(t),!0){case 0===e:case e===s:case e===-1/0:return 0;default:return e}}get height(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.yMax-t.yMin);switch(B(t),e){case 0:case s:case-1/0:return 0;default:return e}}get maxScrollV(){if(null===this._$maxScrollV){this._$maxScrollV=1;const t=this._$textHeightTable.length,e=this.height;if(e>this._$textHeight)return this._$maxScrollV;let i=0,s=0;for(;t>s&&(i+=this._$textHeightTable[s++],!(i>e));)this._$maxScrollV++}return this._$maxScrollV}_$clip(t,e){const i=this._$getBounds(),s=i.xMax,n=i.xMin,a=i.yMax,h=i.yMin;B(i);const o=r.ceil(r.abs(s-n)),_=r.ceil(r.abs(a-h));if(!o||!_)return;let l=e;const c=this._$matrix;1===c[0]&&0===c[1]&&0===c[2]&&1===c[3]&&0===c[4]&&0===c[5]||(l=H(e,c)),t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(o,0),t.lineTo(o,_),t.lineTo(0,_),t.lineTo(0,0),t.clip(),l!==e&&N(l)}_$draw(t,e,i){if(!this._$visible||this._$textAreaActive)return;if(!this._$background&&!this._$border&&2>this._$textData.length)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1);if(!o)return;let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds(null);c.xMin-=this._$thickness,c.xMax+=this._$thickness,c.yMin-=this._$thickness,c.yMax+=this._$thickness;const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf("e");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf("e");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),this._$isUpdated()&&(rt.removeCache(this._$instanceId),t.cachePosition=null,this._$cacheKeys.length=0),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U(x,b);this._$cacheKeys=rt.generateKeys(this._$instanceId,t),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=r.min(1,r.max(x,b)),a=r.ceil(r.abs(c.xMax-c.xMin)*x),h=r.ceil(r.abs(c.yMax-c.yMin)*b);n[3]=1;const o=new OffscreenCanvas(a+2*s,h+2*s).getContext("2d");if(!o)return;if(this._$background||this._$border){if(o.beginPath(),o.moveTo(0,0),o.lineTo(a,0),o.lineTo(a,h),o.lineTo(0,h),o.lineTo(0,0),this._$background){const t=Z(this._$backgroundColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.fillStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.fill()}if(this._$border){const t=Z(this._$borderColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.lineWidth=s,o.strokeStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.stroke()}}o.save(),o.beginPath(),o.moveTo(2,2),o.lineTo(a-2,2),o.lineTo(a-2,h-2),o.lineTo(2,h-2),o.lineTo(2,2),o.clip(),o.beginPath(),o.setTransform(x,0,0,b,0,0),this._$doDraw(o,e,i,a/x),o.restore();const _=M.createCachePosition(m,p),l=M.createTextureFromCanvas(o.canvas);t.drawTextureFromRect(l,_),t.cachePosition=_,rt.set(this._$cacheKeys,_)}let E=!1,C=0,S=0;if(v&&v.length&&this._$canApply(v)){E=!0;const e=this._$drawFilter(t,_,v,m,p);e.offsetX&&(C=e.offsetX),e.offsetY&&(S=e.offsetY),t.cachePosition=e}const w=r.atan2(_[1],_[0]),R=r.atan2(-_[2],_[3]);if(E||!w&&!R)t.setTransform(1,0,0,1,d-C,f-S);else{const e=c.xMin*x,i=c.yMin*b,s=r.cos(w),n=r.sin(w),a=r.cos(R),h=r.sin(R);t.setTransform(s,n,-h,a,e*s-i*h+_[4],e*n+i*a+_[5])}t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),B(c),_!==e&&N(_),n!==i&&L(n)}_$doDraw(t,e,i,s){const n=this.width,a=this.height;let h=0,o=0,_=0,l=0;if("top"!==this._$verticalAlign&&this.height>this._$textHeight)switch(this._$verticalAlign){case"middle":l=(this.height-this._$textHeight+2)/2;break;case"bottom":l=this.height-this._$textHeight+2}const c=this._$textData.length;for(let $=0;$a||u>n))continue;const d=c.textFormat,g=Z(c.textFormat._$color),f=r.max(0,r.min(255*g.A*i[3]+i[7],255))/255;if(t.fillStyle=`rgba(${g.R},${g.G},${g.B},${f})`,this._$thickness){const e=Z(this._$thicknessColor),s=r.max(0,r.min(255*e.A*i[3]+i[7],255))/255;t.lineWidth=this._$thickness,t.strokeStyle=`rgba(${e.R},${e.G},${e.B},${s})`}const m=c.yIndex;switch(c.mode){case"break":case"wrap":if(_++,this._$scrollV>_)continue;if(o+=this._$textHeightTable[m],h=this._$getAlignOffset(this._$objectTable[m],s),d._$underline){const s=c.textFormat._$size/12,n=Z(d._$color),a=r.max(0,r.min(255*n.A*i[3]+i[7],255))/255;t.lineWidth=r.max(1,1/r.min(e[0],e[3])),t.strokeStyle=`rgba(${n.R},${n.G},${n.B},${a})`,t.beginPath(),t.moveTo(h,l+o-s),t.lineTo(h+this._$widthTable[m],l+o-s),t.stroke()}break;case"text":{if(this._$scrollV>_)continue;let e=o-this._$heightTable[0];_e||(e+=c.textFormat._$size/12*2),t.beginPath(),t.textBaseline="top",t.font=tt(d._$font,d._$size,d._$italic,d._$bold),this._$thickness&&t.strokeText(c.text,u,l+e),t.fillText(c.text,u,l+e)}break;case"image":if(!c.loaded)continue;t.beginPath(),t.drawImage(c.image,c.hspace,l+c.y,c.width,c.height)}}}_$getAlignOffset(t,e){const i=this._$widthTable[t.yIndex],s=t.textFormat,n=s._$blockIndent+s._$leftMargin>0?s._$blockIndent+s._$leftMargin:0;switch(!0){case!this._$wordWrap&&i>e:return r.max(0,n);case"center"===s._$align:case"center"===this._$autoSize:return r.max(0,e/2-n-s._$rightMargin-i/2);case"right"===s._$align:case"right"===this._$autoSize:return r.max(0,e-n-i-s._$rightMargin-2);default:return r.max(0,n+2)}}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textAreaActive=!1,super._$remove(),$e.push(this)}_$updateProperty(t){this._$textAreaActive=!!t.textAreaActive,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textData.push(...t.textData),this._$widthTable.push(...t.widthTable),this._$heightTable.push(...t.heightTable),this._$objectTable.push(...t.objectTable),this._$textHeightTable.push(...t.textHeightTable),this._$wordWrap=t.wordWrap,this._$limitWidth=t.limitWidth,this._$limitHeight=t.limitHeight,this._$autoSize=t.autoSize,this._$scrollV=t.scrollV,this._$textHeight=t.textHeight,this._$verticalAlign=t.verticalAlign,this._$border=t.border,this._$border&&(this._$borderColor=t.borderColor),this._$background=t.background,this._$background&&(this._$backgroundColor=t.backgroundColor),"thickness"in t&&(this._$thickness=t.thickness,this._$thicknessColor=t.thicknessColor)}_$update(t){super._$update(t),this._$textAreaActive=!!t.textAreaActive,this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,t.textData&&this._$updateProperty(t)}}class oe extends gt{constructor(){super(),this._$imageBitmap=null,this._$context=null,this._$smoothing=!0,this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}_$clip(t,e){const i=this._$xMax,s=this._$yMax;if(!i||!s)return;let r=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(r=H(e,n)),t.reset(),t.setTransform(r[0],r[1],r[2],r[3],r[4],r[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(i,0),t.lineTo(i,s),t.lineTo(0,s),t.lineTo(0,0),t.clip(),r!==e&&N(r)}_$draw(t,e,i){if(!this._$visible||!this._$imageBitmap||!this._$context)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1,0);if(!o)return void(n!==i&&L(n));let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds();B(c);const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf("e");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf("e");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$instanceId,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const e=r.ceil(r.abs(this._$xMax-this._$xMin)),i=r.ceil(r.abs(this._$yMax-this._$yMin)),s=M.createCachePosition(e,i);t.cachePosition=s,rt.set(this._$cacheKeys,s)}this._$context.drawImage(this._$imageBitmap,0,0);const E=M.textureManager._$createFromElement(this._$imageBitmap.width,this._$imageBitmap.height,this._$context.canvas,this._$smoothing);let C=0,S=0;if(T&&v){const e=M.currentAttachment,i=M.createCacheAttachment(m,p);t._$bind(i),t.reset();const s=P(x,0,0,b,m/2,p/2),r=P(1,0,0,1,-E.width/2,-E.height/2),n=H(s,r);N(s),N(r),t.setTransform(n[0],n[1],n[2],n[3],n[4],n[5]),t.drawImage(E,0,0,E.width,E.height);const a=M.getTextureFromCurrentAttachment();t._$bind(e),M.releaseAttachment(i),t.drawTextureFromRect(E,t.cachePosition);const h=this._$drawFilter(t,_,v,m,p,a);h.offsetX&&(C=h.offsetX),h.offsetY&&(S=h.offsetY),t.cachePosition=h,t.setTransform(1,0,0,1,d-C,f-S)}else t.drawTextureFromRect(E,t.cachePosition),t.setTransform(_[0],_[1],_[2],_[3],_[4],_[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),_!==e&&N(_),n!==i&&L(n)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$context=null,this._$imageBitmap=null,this._$smoothing=!0,super._$remove(),de.push(this)}_$updateProperty(t){if(this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,this._$imageBitmap=t.imageBitmap,this._$smoothing=t.smoothing,!this._$context&&this._$imageBitmap){const t=new c(this._$imageBitmap.width,this._$imageBitmap.height);this._$context=t.getContext("2d")}}_$update(t){super._$update(t),this._$updateProperty(t)}}let _e=!1;const le=new class{constructor(){this._$instances=new Map,this._$matrix=P(1,0,0,1,0,0),this._$width=0,this._$height=0,this._$stage=new ft,this._$canvas=null,this._$context=null,this._$attachment=null}get instances(){return this._$instances}get context(){return this._$context}get scaleX(){return this._$matrix[0]}stop(){rt.reset()}_$initialize(e,i){let s=0;var r,n;this._$setStage(e[s++]),n=1===e[s++],_e=n,r=e[s++],t=r,this._$canvas=i;const a=i.getContext("webgl2",{stencil:!0,premultipliedAlpha:!0,antialias:!1,depth:!1,preserveDrawingBuffer:!0});if(a){const t=new re(a,e[s++]);this._$context=t,rt.context=t}}_$setBackgroundColor(t){if(!this._$context)return;const e=t[0];if(-1===e)this._$context._$setColor(0,0,0,0);else{const t={A:(i=e)>>>24,R:(16711680&i)>>16,G:(65280&i)>>8,B:255&i};this._$context._$setColor(t.R/255,t.G/255,t.B/255,1)}var i}_$bitmapDraw(t,e,i,s){const r=this._$context;if(!r)return;r._$bind(this._$attachment),r.reset(),r.setTransform(1,0,0,1,0,0),r.clearRect(0,0,this._$width,this._$height),r.beginPath(),t._$draw(r,e,i),r.frameBuffer.transferToMainTexture();const n=s.getContext("2d");n&&this._$canvas&&n.drawImage(this._$canvas,0,0)}_$draw(){if(!this._$width||!this._$height)return;const t=this._$context;t&&(t.reset(),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,this._$width,this._$height),t.beginPath(),this._$stage._$draw(t,this._$matrix,m),this._$stage._$updated=!1,t.drawInstacedArray(),t.frameBuffer.transferToMainTexture())}_$resize(t){let e=0;const i=t[e++],s=t[e++];if(this._$width=i,this._$height=s,!this._$canvas)return;if(this._$canvas.width===i&&this._$canvas.height===s)return;const r=this._$context;if(!r)return;const n=t[e++];this._$matrix[0]=n,this._$matrix[3]=n,this._$matrix[4]=t[e++],this._$matrix[5]=t[e++],this._$stage._$updated=!0,rt.reset(),r.clearInstacedArray(),this._$canvas.width=i,this._$canvas.height=s,r._$gl.viewport(0,0,i,s);const a=r.frameBuffer;this._$attachment&&(a.unbind(),a.releaseAttachment(this._$attachment,!0)),this._$attachment=a.createCacheAttachment(i,s,!0),r.setMaxSize(i,s),r._$bind(this._$attachment)}_$setStage(t){this._$stage._$instanceId=t,this._$instances.set(t,this._$stage)}_$updateStage(){this._$stage._$updated=!0}_$createDisplayObjectContainer(t){const e=ge();let i=0;e._$instanceId=t[i++],e._$parentId=t[i++],this._$setProperty(e,t,2),this._$instances.set(e._$instanceId,e)}_$setProperty(t,e,i){t._$visible=1===e[i++],t._$depth=e[i++],t._$clipDepth=e[i++],t._$isMask=1===e[i++],1===e[i++]?(t._$maskId=e[i++],t._$maskMatrix||(t._$maskMatrix=P()),t._$maskMatrix[0]=e[i++],t._$maskMatrix[1]=e[i++],t._$maskMatrix[2]=e[i++],t._$maskMatrix[3]=e[i++],t._$maskMatrix[4]=e[i++],t._$maskMatrix[5]=e[i++]):(t._$maskId=-1,t._$maskMatrix&&(N(t._$maskMatrix),t._$maskMatrix=null),i+=7),t._$visible?(t._$matrix[0]=e[i++],t._$matrix[1]=e[i++],t._$matrix[2]=e[i++],t._$matrix[3]=e[i++],t._$matrix[4]=e[i++],t._$matrix[5]=e[i++],t._$colorTransform[0]=e[i++],t._$colorTransform[1]=e[i++],t._$colorTransform[2]=e[i++],t._$colorTransform[3]=e[i++],t._$colorTransform[4]=e[i++],t._$colorTransform[5]=e[i++],t._$colorTransform[6]=e[i++],t._$colorTransform[7]=e[i++]):(i+=6,i+=8),t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null,t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null}_$registerShapeRecodes(t,e){this._$instances.has(t)||this._$instances.set(t,pe()),this._$instances.get(t)._$recodes=e}_$createShape(t){let e=0;const i=t[e++];this._$instances.has(i)||this._$instances.set(i,pe());const s=this._$instances.get(i);s._$instanceId=i,s._$parentId=t[e++],s._$maxAlpha=t[e++],s._$canDraw=1===t[e++],s._$xMin=t[e++],s._$yMin=t[e++],s._$xMax=t[e++],s._$yMax=t[e++],s._$characterId=t[e++],s._$loaderInfoId=t[e++],this._$setProperty(s,t,10)}_$createVideo(t){const e=me();t.characterId&&(e._$characterId=t.characterId),"loaderInfoId"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}_$createTextField(t){const e=fe();e._$xMin=t.xMin||0,e._$yMin=t.yMin||0,e._$xMax=t.xMax||0,e._$yMax=t.yMax||0,t.characterId&&(e._$characterId=t.characterId),"loaderInfoId"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}},ce=[],$e=[],ue=[],de=[],ge=()=>ue.pop()||new ft,fe=()=>$e.pop()||new he,me=()=>de.pop()||new oe,pe=()=>ce.pop()||new ae;const xe=new class{constructor(){this.state="deactivate",this.queue=[],this._$options=[]}execute(){this.state="active";let t=!0;for(;this.queue.length;){const e=this.queue.shift();if(console.log(e),e){switch(t=!0,e.command){case"draw":le._$draw();break;case"setProperty":if(!le.instances.has(e.instanceId))continue;break;case"setChildren":{t=!1;const i=e.buffer,s=le.instances;if(!s.has(i[0]))continue;const r=s.get(i[0]);r._$doChanged(),r._$children=i.subarray(1)}break;case"remove":{const t=le.instances;if(!t.has(e.instanceId))continue;t.get(e.instanceId)._$remove(),t.delete(e.instanceId)}break;case"createShape":le._$createShape(e.buffer);break;case"createDisplayObjectContainer":le._$createDisplayObjectContainer(e.buffer);break;case"createTextField":le._$createTextField(e);break;case"createVideo":le._$createVideo(e);break;case"resize":le._$resize(e.buffer);break;case"initialize":le._$initialize(e.buffer,e.canvas);break;case"setBackgroundColor":le._$setBackgroundColor(e.buffer);break;case"stop":le.stop();break;case"removeCache":rt.removeCache(e.id);break;case"bitmapDraw":{const t=le.instances;if(!t.has(e.sourceId))continue;const i=t.get(e.sourceId),s=new c(e.width,e.height);le._$bitmapDraw(i,e.matrix||f,e.colorTransform||m,s);const r=s.transferToImageBitmap();globalThis.postMessage({command:"bitmapDraw",sourceId:e.sourceId,imageBitmap:r},[r])}break;default:if(e.command.indexOf("shapeRecodes")>-1){t=!1;const i=+e.command.split("@")[1];le._$registerShapeRecodes(i,e.buffer)}}e.buffer&&t&&(this._$options.length=0)}}this.state="deactivate"}};self.addEventListener("message",(t=>{return e=void 0,i=void 0,r=function*(){xe.queue.push(t.data),"deactivate"===xe.state&&xe.execute()},new((s=void 0)||(s=Promise))((function(t,n){function a(t){try{o(r.next(t))}catch(t){n(t)}}function h(t){try{o(r.throw(t))}catch(t){n(t)}}function o(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,h)}o((r=r.apply(e,i||[])).next())}));var e,i,s,r}))})();'],{type:"text/javascript"}))):null,Mr=null,Mr&&(wr=t=>{t._$createWorkerInstance(),t._$postProperty();const e=t._$needsChildren?t._$getChildren():t._$children,i=ht();for(let t=0;t{t._$removeWorkerInstance();const e=t._$needsChildren?t._$getChildren():t._$children;for(let t=0;t{const e=navigator.userAgentData;if(e)e.getHighEntropyValues(["platform","mobile"]).then((e=>{const i=e.brands;for(let t=0;t-1,Js=e.indexOf("iPhone")>-1||e.indexOf("iPod")>-1,js=e.indexOf("Chrome")>-1,Ks=e.indexOf("Firefox")>-1,Ws=-1===e.indexOf("Chrome")&&e.indexOf("Safari")>-1,Zs=Qs||Js,t()}}))};"next2d"in window||(console.log("%c Next2D Player %c 1.18.12 %c https://next2d.app","color: #fff; background: #5f5f5f","color: #fff; background: #4bc729",""),window.next2d=new class{constructor(t){this._$promises=t,this._$player=new ms,this.display=bs,this.events=xs,this.filters=vs,this.geom=Ts,this.media=ys,this.net=Es,this.text=As,this.ui=Ms}get player(){return this._$player}load(t,e){Promise.all(this._$promises).then((()=>{if(ot(this._$promises),"develop"===t){const e=location.search.slice(1).split("&")[0];if(!e)return;t=`${location.origin}/${e}`}if(!t)return;"/"===t.charAt(1)&&(t=t.slice(1)),e&&"base"in e||!(t.indexOf("//")>-1)||(this._$player.base=t),this._$player.setOptions(e),this._$player._$initialize();const i=new de;i.contentLoaderInfo.addEventListener(Lt.IO_ERROR,(t=>{t.target&&t.target.removeEventListener(Lt.IO_ERROR,t.listener),alert("Error: "+t.text)})),i.contentLoaderInfo.addEventListener(It.COMPLETE,(t=>{const e=t.target,i=this._$player;if(e.removeEventListener(It.COMPLETE,t.listener),e._$data){const t=e._$data.stage;i.bgColor=t.bgColor,i._$setBackgroundColor(t.bgColor),i.stage.addChild(e.content),i.width=t.width,i.height=t.height,i.stage._$frameRate=dt(+t.fps,1,60,60)}i._$resize()})),i.load(new wt(t))}))}createRootMovieClip(){return t=this,e=arguments,s=function*(t=240,e=240,i=24,s=null){yield Promise.all(this._$promises),ot(this._$promises);const r=this._$player;r.width=0|t,r.height=0|e,r.mode="create",r.stage._$frameRate=0|i,r.setOptions(s),r._$initialize();const n=r.stage.addChild(new $e);return r._$loadStatus=ms.LOAD_END,r.play(),n},new((i=void 0)||(i=Promise))((function(r,n){function a(t){try{o(s.next(t))}catch(t){n(t)}}function h(t){try{o(s.throw(t))}catch(t){n(t)}}function o(t){var e;t.done?r(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(a,h)}o((s=s.apply(t,e||[])).next())}));var t,e,i,s}}([new Promise((t=>{if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),kr().then((()=>{cr()._$initializeCanvas(),t()}))};window.addEventListener("DOMContentLoaded",e)}else kr().then((()=>{cr()._$initializeCanvas(),t()}))}))]))})(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c841e315 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4092 @@ +{ + "name": "@next2d/player", + "version": "2.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@next2d/player", + "version": "2.0.0", + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "fflate": "^0.8.2", + "htmlparser2": "^10.0.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.23.0", + "@types/node": "^22.13.11", + "@typescript-eslint/eslint-plugin": "^8.27.0", + "@typescript-eslint/parser": "^8.27.0", + "@vitest/web-worker": "^3.0.9", + "eslint": "^9.23.0", + "eslint-plugin-unused-imports": "^4.1.4", + "globals": "^16.0.0", + "jsdom": "^26.0.0", + "typescript": "^5.8.2", + "vite": "^6.2.2", + "vitest": "^3.0.9", + "vitest-webgl-canvas-mock": "^1.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Next2D" + }, + "peerDependencies": { + "@next2d/cache": "file:packages/cache", + "@next2d/core": "file:packages/core", + "@next2d/display": "file:packages/display", + "@next2d/events": "file:packages/events", + "@next2d/filters": "file:packages/filters", + "@next2d/geom": "file:packages/geom", + "@next2d/media": "file:packages/media", + "@next2d/net": "file:packages/net", + "@next2d/render-queue": "file:packages/render-queue", + "@next2d/renderer": "file:packages/renderer", + "@next2d/text": "file:packages/text", + "@next2d/texture-packer": "file:packages/texture-packer", + "@next2d/ui": "file:packages/ui", + "@next2d/webgl": "file:packages/webgl" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", + "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.2", + "@csstools/css-color-parser": "^3.0.8", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", + "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", + "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@next2d/cache": { + "resolved": "packages/cache", + "link": true + }, + "node_modules/@next2d/core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@next2d/display": { + "resolved": "packages/display", + "link": true + }, + "node_modules/@next2d/events": { + "resolved": "packages/events", + "link": true + }, + "node_modules/@next2d/filters": { + "resolved": "packages/filters", + "link": true + }, + "node_modules/@next2d/geom": { + "resolved": "packages/geom", + "link": true + }, + "node_modules/@next2d/media": { + "resolved": "packages/media", + "link": true + }, + "node_modules/@next2d/net": { + "resolved": "packages/net", + "link": true + }, + "node_modules/@next2d/render-queue": { + "resolved": "packages/render-queue", + "link": true + }, + "node_modules/@next2d/renderer": { + "resolved": "packages/renderer", + "link": true + }, + "node_modules/@next2d/text": { + "resolved": "packages/text", + "link": true + }, + "node_modules/@next2d/texture-packer": { + "resolved": "packages/texture-packer", + "link": true + }, + "node_modules/@next2d/ui": { + "resolved": "packages/ui", + "link": true + }, + "node_modules/@next2d/webgl": { + "resolved": "packages/webgl", + "link": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "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", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz", + "integrity": "sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.36.0.tgz", + "integrity": "sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.36.0.tgz", + "integrity": "sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.36.0.tgz", + "integrity": "sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.36.0.tgz", + "integrity": "sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.36.0.tgz", + "integrity": "sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.36.0.tgz", + "integrity": "sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.36.0.tgz", + "integrity": "sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.36.0.tgz", + "integrity": "sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.36.0.tgz", + "integrity": "sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.36.0.tgz", + "integrity": "sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.36.0.tgz", + "integrity": "sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.36.0.tgz", + "integrity": "sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.36.0.tgz", + "integrity": "sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.36.0.tgz", + "integrity": "sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.36.0.tgz", + "integrity": "sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.36.0.tgz", + "integrity": "sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.36.0.tgz", + "integrity": "sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.36.0.tgz", + "integrity": "sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", + "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz", + "integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.27.0", + "@typescript-eslint/type-utils": "8.27.0", + "@typescript-eslint/utils": "8.27.0", + "@typescript-eslint/visitor-keys": "8.27.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.27.0.tgz", + "integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.27.0", + "@typescript-eslint/types": "8.27.0", + "@typescript-eslint/typescript-estree": "8.27.0", + "@typescript-eslint/visitor-keys": "8.27.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz", + "integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.27.0", + "@typescript-eslint/visitor-keys": "8.27.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz", + "integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.27.0", + "@typescript-eslint/utils": "8.27.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.27.0.tgz", + "integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz", + "integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.27.0", + "@typescript-eslint/visitor-keys": "8.27.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.27.0.tgz", + "integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.27.0", + "@typescript-eslint/types": "8.27.0", + "@typescript-eslint/typescript-estree": "8.27.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz", + "integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.27.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", + "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.9", + "@vitest/utils": "3.0.9", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", + "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", + "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", + "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", + "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.9", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", + "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", + "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.9", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/web-worker": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@vitest/web-worker/-/web-worker-3.0.9.tgz", + "integrity": "sha512-6ofXRWDelTlFIOaGLVtHPiIEzoTQK/DkH6DMrG7ZurrTF9szaiH86/u3qncA1cXsgp+vN19Kj+YYl4mbL+EDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "3.0.9" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", + "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.1", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz", + "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.0.tgz", + "integrity": "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "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-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", + "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.1", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nwsapi": { + "version": "2.2.19", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.19.tgz", + "integrity": "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "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.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "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", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", + "integrity": "sha512-fuDHYgFHJGbpGMgw9skY/bj3HL/Jrn4l/5rSspy00DoT4RyLnDcRvPxdZ+r6OFwIsgAuhDh4I09tAId4mI12bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "~0.5.0" + } + }, + "node_modules/parse-color/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", + "dev": true + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.36.0.tgz", + "integrity": "sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.36.0", + "@rollup/rollup-android-arm64": "4.36.0", + "@rollup/rollup-darwin-arm64": "4.36.0", + "@rollup/rollup-darwin-x64": "4.36.0", + "@rollup/rollup-freebsd-arm64": "4.36.0", + "@rollup/rollup-freebsd-x64": "4.36.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.36.0", + "@rollup/rollup-linux-arm-musleabihf": "4.36.0", + "@rollup/rollup-linux-arm64-gnu": "4.36.0", + "@rollup/rollup-linux-arm64-musl": "4.36.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.36.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.36.0", + "@rollup/rollup-linux-riscv64-gnu": "4.36.0", + "@rollup/rollup-linux-s390x-gnu": "4.36.0", + "@rollup/rollup-linux-x64-gnu": "4.36.0", + "@rollup/rollup-linux-x64-musl": "4.36.0", + "@rollup/rollup-win32-arm64-msvc": "4.36.0", + "@rollup/rollup-win32-ia32-msvc": "4.36.0", + "@rollup/rollup-win32-x64-msvc": "4.36.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", + "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.85", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", + "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.85" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.85", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz", + "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", + "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", + "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", + "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", + "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.9", + "@vitest/mocker": "3.0.9", + "@vitest/pretty-format": "^3.0.9", + "@vitest/runner": "3.0.9", + "@vitest/snapshot": "3.0.9", + "@vitest/spy": "3.0.9", + "@vitest/utils": "3.0.9", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.9", + "@vitest/ui": "3.0.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest-webgl-canvas-mock": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vitest-webgl-canvas-mock/-/vitest-webgl-canvas-mock-1.1.0.tgz", + "integrity": "sha512-F/5+XvBs7cSZPe41IGQTbSjNimB4NntPnRqv4eWb42voFKQINH8y2xZkibNUxYJCGIuDFsYp1lDQgTvWLahSzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssfontparser": "^1.2.1", + "parse-color": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/cache": { + "name": "@next2d/cache", + "version": "*", + "license": "MIT" + }, + "packages/core": { + "name": "@next2d/core", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/core": "file:../core", + "@next2d/display": "file:../display", + "@next2d/events": "file:../events", + "@next2d/filters": "file:../filters", + "@next2d/geom": "file:../geom", + "@next2d/media": "file:../media", + "@next2d/net": "file:../net", + "@next2d/render-queue": "file:../render-queue", + "@next2d/renderer": "file:../renderer", + "@next2d/text": "file:../text", + "@next2d/ui": "file:../ui" + } + }, + "packages/display": { + "name": "@next2d/display", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/events": "file:../events", + "@next2d/filters": "file:../filters", + "@next2d/geom": "file:../geom", + "@next2d/media": "file:../media", + "@next2d/net": "file:../net", + "@next2d/render-queue": "file:../render-queue", + "@next2d/text": "file:../text", + "@next2d/ui": "file:../ui" + } + }, + "packages/events": { + "name": "@next2d/events", + "version": "*", + "license": "MIT" + }, + "packages/filters": { + "name": "@next2d/filters", + "version": "*", + "license": "MIT" + }, + "packages/geom": { + "name": "@next2d/geom", + "version": "*", + "license": "MIT" + }, + "packages/media": { + "name": "@next2d/media", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/display": "file:../display", + "@next2d/events": "file:../events", + "@next2d/geom": "file:../geom", + "@next2d/net": "file:../net" + } + }, + "packages/net": { + "name": "@next2d/net", + "version": "*", + "license": "MIT" + }, + "packages/render-queue": { + "name": "@next2d/render-queue", + "version": "*", + "license": "MIT" + }, + "packages/renderer": { + "name": "@next2d/renderer", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/cache": "file:../cache", + "@next2d/texture-packer": "file:../texture-packer", + "@next2d/webgl": "file:../webgl" + } + }, + "packages/text": { + "name": "@next2d/text", + "version": "*", + "license": "MIT", + "dependencies": { + "htmlparser2": "^10.0.0" + }, + "peerDependencies": { + "@next2d/cache": "file:../cache", + "@next2d/display": "file:../display", + "@next2d/events": "file:../events", + "@next2d/geom": "file:../geom", + "@next2d/ui": "file:../ui" + } + }, + "packages/texture-packer": { + "name": "@next2d/texture-packer", + "version": "*", + "license": "MIT" + }, + "packages/ui": { + "name": "@next2d/ui", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/events": "file:../events" + } + }, + "packages/webgl": { + "name": "@next2d/webgl", + "version": "*", + "license": "MIT", + "peerDependencies": { + "@next2d/render-queue": "file:../render-queue", + "@next2d/texture-packer": "file:../texture-packer" + } + } + } +} diff --git a/package.json b/package.json index 872919eb..a0c1a250 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,21 @@ { "name": "@next2d/player", - "version": "1.18.12", + "version": "2.0.0", "description": "Experience the fast and beautiful anti-aliased rendering of WebGL. You can create rich, interactive graphics, cross-platform applications and games without worrying about browser or device compatibility.", "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "index.js", - "bundle": "dist/next2d.min.js", - "types": "index.d.ts", + "main": "src/index.js", + "bundle": "next2d.js", + "type": "module", + "types": "src/index.d.ts", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.js" + } + }, "keywords": [ "Next2D", "Next2D Player" @@ -21,54 +28,51 @@ "packages/*" ], "scripts": { - "start": "tsc && node ./scripts/build.js && webpack serve", - "lint": "eslint src/**/*.ts packages/**/*.ts worker/**/*.ts", - "publish:dist": "tsc && node ./scripts/publish.js", + "start": "vite --host", + "lint": "eslint src/**/*.ts packages/**/*.ts", + "test": "vitest", "clean": "node ./scripts/clean.js", - "build": "tsc && node ./scripts/build.js && webpack --mode production", - "test": "jest" + "build:vite": "node ./scripts/version.js && vite build", + "publish:dist": "node ./scripts/clean.js && node ./scripts/version.js && tsc && vite build && node ./scripts/publish.js" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Next2D" }, "dependencies": { - "htmlparser2": "^9.1.0" + "fflate": "^0.8.2", + "htmlparser2": "^10.0.0" }, "devDependencies": { - "@babel/core": "^7.24.8", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/preset-env": "^7.24.8", - "@types/jest": "^29.5.12", - "@typescript-eslint/eslint-plugin": "^7.16.0", - "@typescript-eslint/parser": "^7.16.0", - "eslint": "^8.52.0", - "eslint-webpack-plugin": "^4.2.0", - "fflate": "^0.8.2", - "jest": "^29.7.0", - "jsdoc": "^4.0.3", - "ts-jest": "^29.2.2", - "ts-loader": "^9.5.1", - "ts-node": "^10.9.2", - "typescript": "^5.5.3", - "webpack": "^5.93.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.0.4" + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.23.0", + "@types/node": "^22.13.11", + "@typescript-eslint/eslint-plugin": "^8.27.0", + "@typescript-eslint/parser": "^8.27.0", + "@vitest/web-worker": "^3.0.9", + "eslint": "^9.23.0", + "eslint-plugin-unused-imports": "^4.1.4", + "globals": "^16.0.0", + "jsdom": "^26.0.0", + "typescript": "^5.8.2", + "vite": "^6.2.2", + "vitest": "^3.0.9", + "vitest-webgl-canvas-mock": "^1.1.0" }, "peerDependencies": { + "@next2d/cache": "file:packages/cache", "@next2d/core": "file:packages/core", "@next2d/display": "file:packages/display", "@next2d/events": "file:packages/events", "@next2d/filters": "file:packages/filters", "@next2d/geom": "file:packages/geom", - "@next2d/interface": "file:packages/interface", "@next2d/media": "file:packages/media", "@next2d/net": "file:packages/net", - "@next2d/share": "file:packages/share", + "@next2d/render-queue": "file:packages/render-queue", + "@next2d/renderer": "file:packages/renderer", "@next2d/text": "file:packages/text", + "@next2d/texture-packer": "file:packages/texture-packer", "@next2d/ui": "file:packages/ui", - "@next2d/util": "file:packages/util", - "@next2d/webgl": "file:packages/webgl", - "@next2d/webpack-worker-loader-plugin": "file:packages/webpack-worker-loader-plugin" + "@next2d/webgl": "file:packages/webgl" } } diff --git a/packages/webpack-worker-loader-plugin/LICENSE b/packages/cache/LICENSE similarity index 99% rename from packages/webpack-worker-loader-plugin/LICENSE rename to packages/cache/LICENSE index a536abed..7223b9d6 100644 --- a/packages/webpack-worker-loader-plugin/LICENSE +++ b/packages/cache/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/packages/cache/README.md b/packages/cache/README.md new file mode 100644 index 00000000..d30f7f1a --- /dev/null +++ b/packages/cache/README.md @@ -0,0 +1,11 @@ +@next2d/cache +============= + +## Installation + +``` +npm install @next2d/cache +``` + +## License +This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/cache/package.json b/packages/cache/package.json new file mode 100644 index 00000000..55035279 --- /dev/null +++ b/packages/cache/package.json @@ -0,0 +1,26 @@ +{ + "name": "@next2d/cache", + "version": "*", + "description": "Next2D Cache Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "license": "MIT", + "homepage": "https://next2d.app", + "bugs": "https://github.com/Next2D/Player/issues", + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.js" + } + }, + "keywords": [ + "Next2D", + "Next2D Cache" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Next2D/Player.git" + } +} \ No newline at end of file diff --git a/packages/cache/src/CacheStore.ts b/packages/cache/src/CacheStore.ts new file mode 100644 index 00000000..c84a8cb9 --- /dev/null +++ b/packages/cache/src/CacheStore.ts @@ -0,0 +1,282 @@ +import { execute as cacheStoreResetService } from "./CacheStore/service/CacheStoreResetService"; +import { execute as cacheStoreDestroyService } from "./CacheStore/service/CacheStoreDestroyService"; +import { execute as cacheStoreRemoveService } from "./CacheStore/service/CacheStoreRemoveService"; +import { execute as cacheStoreRemoveByIdService } from "./CacheStore/service/CacheStoreRemoveByIdService"; +import { execute as cacheStoreGetService } from "./CacheStore/service/CacheStoreGetService"; +import { execute as cacheStoreSetService } from "./CacheStore/service/CacheStoreSetService"; +import { execute as cacheStoreHasService } from "./CacheStore/service/CacheStoreHasService"; +import { execute as cacheStoreGenerateKeysService } from "./CacheStore/service/CacheStoreGenerateKeysService"; +import { execute as cacheStoreGenerateFilterKeysService } from "./CacheStore/service/CacheStoreGenerateFilterKeysService"; +import { execute as cacheStoreRemoveTimerService } from "./CacheStore/service/CacheStoreRemoveTimerService"; +import { execute as cacheStoreRemoveTimerScheduledCacheService } from "./CacheStore/service/CacheStoreRemoveTimerScheduledCacheService"; + +/** + * @description キャッシュ管理クラス + * Cache management class + * + * @class + * @private + */ +export class CacheStore +{ + /** + * @description キャッシュプール + * Cache pool + * + * @type {HTMLCanvasElement[]} + * @private + */ + private readonly _$pool: HTMLCanvasElement[]; + + /** + * @description キャッシュストア + * Cache store + * + * @type {Map} + * @private + */ + private readonly _$store: Map; + + /** + * @description キャッシュトラッシュ + * Cache trash + * + * @type {Map} + * @private + */ + private readonly _$trash: Map; + + /** + * @description キャッシュ削除用のタイマーID + * Timer ID for cache deletion + * + * @type {NodeJS.Timeout | null} + * @public + */ + public $timerId: NodeJS.Timeout | null; + + /** + * @description キャッシュタイマーの削除フラグ + * Deletion flag of cache timer + * + * @type {boolean} + * @default false + * @public + */ + public $removeCache: boolean; + + /** + * @description キャッシュ削除用のIDリスト + * ID list for cache deletion + * + * @type {number[]} + * @public + */ + public readonly $removeIds: number[]; + + /** + * @constructor + */ + constructor () + { + this._$pool = []; + this._$store = new Map(); + this._$trash = new Map(); + this.$timerId = null; + this.$removeIds = []; + this.$removeCache = false; + } + + /** + * @description 登録された全てのキャッシュをリセット・破棄する + * Reset and destroy all registered caches + * + * @return {void} + * @method + * @public + */ + reset (): void + { + cacheStoreResetService(this, this._$store, this._$trash); + } + + /** + * @description 指定のオブジェクトを破棄する + * Destroy the specified object + * + * @param {object} [object=null] + * @return {void} + * @method + * @public + */ + destroy (object: any = null): void + { + cacheStoreDestroyService(this._$pool, object); + } + + /** + * @description HTMLCanvasElementを返却 + * Returns HTMLCanvasElement + * + * @return {HTMLCanvasElement} + * @method + * @public + */ + getCanvas (): HTMLCanvasElement + { + return this._$pool.pop() || document.createElement("canvas"); + } + + /** + * @description HTMLCanvasElementを再利用する為に、内部配列にプール + * Pool in an internal array to reuse HTMLCanvasElement + * + * @param {string} id + * @param {string} type + * @returns {void} + * @method + * @public + */ + remove (id: string, type: string): void + { + cacheStoreRemoveService(this._$store, id, type); + } + + /** + * @description 指定IDのキャッシュを削除タイマーに登録 + * Register the cache for the specified ID in the delete timer + * + * @param {string} id + * @returns {void} + * @method + * @public + */ + removeTimer (id: string): void + { + cacheStoreRemoveTimerService(this, this._$store, this._$trash, id); + } + + /** + * @description タイマーでセットされた削除フラグを持つIDをキャッシュストアから削除する + * Remove the ID with the deletion flag set by the timer from the cache store + * + * @returns {void} + * @method + * @public + */ + removeTimerScheduledCache () : void + { + cacheStoreRemoveTimerScheduledCacheService(this, this._$trash); + } + + /** + * @description 指定IDのキャッシュを削除する + * Delete the cache for the specified ID + * + * @param {string} id + * @returns {void} + * @method + * @public + */ + removeById (id: string): void + { + cacheStoreRemoveByIdService(this, this._$store, id); + } + + /** + * @description 指定IDのキャッシュデータを返却 + * Returns the cache data for the specified ID + * + * @param {string} id + * @return {Map} + * @method + * @public + */ + getById (id: string): Map + { + return this._$store.get(id); + } + + /** + * @description 指定のキーのキャッシュデータを返却 + * Returns the cache data for the specified key + * + * @param {string} unique_key + * @param {string} key + * @return {*} + * @method + * @public + */ + get (unique_key: string, key: string): any + { + return cacheStoreGetService(this._$store, unique_key, key); + } + + /** + * @description キャッシュストアにデータをセット + * Set data in the cache store + * + * @param {string} unique_key + * @param {string} key + * @param {*} value + * @return {void} + * @method + * @public + */ + set (unique_key: string, key: string, value: any = null): void + { + cacheStoreSetService( + this, this._$store, unique_key, key, value + ); + } + + /** + * @description 指定キーのキャッシュが存在するかどうか + * Whether the specified key cache exists + * + * @param {string} unique_key + * @param {string} key + * @return {boolean} + * @method + * @public + */ + has (unique_key: string, key: string = ""): boolean + { + return cacheStoreHasService(this._$store, unique_key, key); + } + + /** + * @description キャッシュストアのキーを生成 + * Generate cache store keys + * + * @param {number} x_scale + * @param {number} y_scale + * @param {number} alpha + * @return {number} + * @method + * @public + */ + generateKeys (x_scale: number, y_scale: number, alpha: number): number + { + return cacheStoreGenerateKeysService(x_scale, y_scale, alpha); + } + + /** + * @description フィルター用のキャッシュストアのキーを生成 + * Generate cache store keys for filters + * + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @return {string} + * @method + * @public + */ + generateFilterKeys (a: number, b: number, c: number, d: number): string + { + return cacheStoreGenerateFilterKeysService(a, b, c, d); + } +} + +export const $cacheStore = new CacheStore(); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreDestroyService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreDestroyService.test.ts new file mode 100644 index 00000000..b103746e --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreDestroyService.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./CacheStoreDestroyService"; +import { describe, expect, it, vi } from "vitest"; + +describe("CacheStoreDestroyService.js test", () => +{ + it("test case1", () => + { + const pool = []; + expect(pool.length).toBe(0); + + execute(pool, null); + expect(pool.length).toBe(0); + }); + + it("test case2", () => + { + let state = ""; + const MockContext = vi.fn().mockImplementation(() => + { + return { + "canvas": { + "width": 100, + "height": 200 + }, + "clearRect": vi.fn(() => { state = "clear" }) + } as unknown as CanvasRenderingContext2D; + }); + + const pool = []; + const mockContext = new MockContext(); + expect(pool.length).toBe(0); + expect(state).toBe(""); + expect(mockContext.canvas.width).toBe(100); + expect(mockContext.canvas.height).toBe(200); + + execute(pool, mockContext); + + expect(pool.length).toBe(1); + expect(state).toBe("clear"); + expect(mockContext.canvas.width).toBe(1); + expect(mockContext.canvas.height).toBe(1); + }); +}); diff --git a/packages/cache/src/CacheStore/service/CacheStoreDestroyService.ts b/packages/cache/src/CacheStore/service/CacheStoreDestroyService.ts new file mode 100644 index 00000000..d1e403af --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreDestroyService.ts @@ -0,0 +1,34 @@ +/** + * @description 破棄するHTMLCanvasElementをプールに保管 + * Store the HTMLCanvasElement to be destroyed in the pool + * + * @param {HTMLCanvasElement[]} pool + * @param {object} object + * @return {void} + * @method + * @public + */ +export const execute = ( + pool: HTMLCanvasElement[], + object: any +): void => { + + if (!object || typeof object !== "object") { + return ; + } + + if ("canvas" in object) { + + const canvas: HTMLCanvasElement = object.canvas; + const width = canvas.width; + const height = canvas.height; + + object.clearRect(0, 0, width + 1, height + 1); + + // canvas reset + canvas.width = canvas.height = 1; + + // pool + pool.push(canvas); + } +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.test.ts new file mode 100644 index 00000000..9b2b7517 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.test.ts @@ -0,0 +1,15 @@ +import { execute } from "./CacheStoreGenerateFilterKeysService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreGenerateFilterKeysService.js test", () => +{ + it("test case1", () => + { + expect(execute(1, 0, 0, 1)).toBe("1001"); + }); + + it("test case2", () => + { + expect(execute(0.25, 0.5, -0.3, 1.25)).toBe("0.250.5-0.31.25"); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.ts b/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.ts new file mode 100644 index 00000000..853aaf15 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGenerateFilterKeysService.ts @@ -0,0 +1,16 @@ +/** + * @description キャッシュストアのキーを生成 + * Generate cache store keys + * + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @return {string} + * @method + * @public + */ +export const execute = (a: number, b: number, c: number, d: number): string => +{ + return `${a}${b}${c}${d}`; +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.test.ts new file mode 100644 index 00000000..e27bf8f4 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.test.ts @@ -0,0 +1,15 @@ +import { execute } from "./CacheStoreGenerateKeysService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreGenerateKeysService.js test", () => +{ + it("test case1", () => + { + expect(execute(0.25, 0.5, 0)).toBe(15480923); + }); + + it("test case2", () => + { + expect(execute(0.25, 0.5, 0.3)).toBe(6600597); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.ts b/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.ts new file mode 100644 index 00000000..888dda0c --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGenerateKeysService.ts @@ -0,0 +1,34 @@ +/** + * @description キャッシュストアのキーを生成 + * Generate cache store keys + * + * @param {number} x_scale + * @param {number} y_scale + * @param {number} alpha + * @return {number} + * @method + * @public + */ +export const execute = (x_scale: number, y_scale: number, alpha: number): number => +{ + const values = [x_scale * 10000, y_scale * 10000]; + if (alpha) { + values.push(alpha * 100); + } + + let hash = 2166136261; // FNV-1aオフセット basis + for (let idx = 0; idx < values.length; ++idx) { + + let num = values[idx] | 0; // 整数として扱う + + // 32bit整数の各バイトを処理 + for (let i = 0; i < 4; i++) { + const byte = num & 0xff; + hash ^= byte; + hash = Math.imul(hash, 16777619); // FNV-1a の FNV prime + num >>>= 8; + } + } + + return (hash >>> 0) % 16777216; +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreGetService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreGetService.test.ts new file mode 100644 index 00000000..ce72a409 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGetService.test.ts @@ -0,0 +1,28 @@ +import { execute } from "./CacheStoreGetService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreGetService.js test", () => +{ + it("test case1", () => + { + const store = new Map(); + expect(execute(store, "1", "0")).toBe(null); + }); + + it("test case2", () => + { + const store = new Map(); + store.set("1", new Map()); + expect(execute(store, "1", "0")).toBe(null); + }); + + it("test case3", () => + { + const data = new Map(); + data.set("0", "test"); + + const store = new Map(); + store.set("1", data); + expect(execute(store, "1", "0")).toBe("test"); + }); +}); diff --git a/packages/cache/src/CacheStore/service/CacheStoreGetService.ts b/packages/cache/src/CacheStore/service/CacheStoreGetService.ts new file mode 100644 index 00000000..98d5cde7 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreGetService.ts @@ -0,0 +1,25 @@ +/** + * @description 指定のキーからデータを取得 + * Get data from the specified key + * + * @param {Map} data_store + * @param {string} unique_key + * @param {string} key + * @return {*} + * @method +* @public +*/ +export const execute = ( + data_store: Map>, + unique_key: string, + key: string +): any => { + + const data = data_store.get(unique_key) || null; + if (!data) { + return null; + } + + data.delete("trash"); + return data.get(key) || null; +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreHasService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreHasService.test.ts new file mode 100644 index 00000000..005adb10 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreHasService.test.ts @@ -0,0 +1,29 @@ +import { execute } from "./CacheStoreHasService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreHasService.js test", () => +{ + it("test case1", () => + { + const store = new Map(); + expect(execute(store, "1", "0")).toBe(false); + }); + + it("test case2", () => + { + const store = new Map(); + store.set("1", new Map()); + expect(execute(store, "1")).toBe(true); + expect(execute(store, "1", "0")).toBe(false); + }); + + it("test case3", () => + { + const data = new Map(); + data.set("0", "test"); + + const store = new Map(); + store.set("1", data); + expect(execute(store, "1", "0")).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreHasService.ts b/packages/cache/src/CacheStore/service/CacheStoreHasService.ts new file mode 100644 index 00000000..120a7c0c --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreHasService.ts @@ -0,0 +1,25 @@ +/** + * @description 指定キーのキャッシュが存在するかどうか + * Whether the specified key cache exists + * + * @param {Map} data_store + * @param {string} unique_key + * @param {string} key + * @return {boolean} + * @method + * @public + */ +export const execute = ( + data_store: Map>, + unique_key: string, + key: string = "" +): boolean => { + + if (!key) { + return data_store.has(unique_key); + } + + return !data_store.has(unique_key) + ? false + : (data_store.get(unique_key) as Map).has(key); +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.test.ts new file mode 100644 index 00000000..5f7bade3 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.test.ts @@ -0,0 +1,22 @@ +import { $cacheStore } from "../../CacheStore"; +import { execute } from "./CacheStoreRemoveByIdService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreRemoveByIdService.js test", () => +{ + it("test case1", () => + { + const data = new Map(); + data.set("0", "test"); + + const store = new Map(); + store.set("1", data); + + expect(data.size).toBe(1); + expect(store.size).toBe(1); + + execute($cacheStore, store, "1"); + expect(data.size).toBe(0); + expect(store.size).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.ts new file mode 100644 index 00000000..096980a9 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveByIdService.ts @@ -0,0 +1,29 @@ +import { CacheStore } from "../../CacheStore"; +import { $poolMap } from "../../CacheUtil"; + +/** + * @description 指定IDのキャッシュを削除する + * Delete the cache for the specified ID + * + * @param {CacheStore} cache_store + * @param {Map} data_store + * @param {string} id + */ +export const execute = ( + cache_store: CacheStore, + data_store: Map>, + id: string +): void => { + + if (!data_store.has(id)) { + return ; + } + + const data = data_store.get(id) as NonNullable>; + for (const value of data.values()) { + cache_store.destroy(value); + } + + data_store.delete(id); + $poolMap(data); +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveService.test.ts new file mode 100644 index 00000000..55b40866 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveService.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./CacheStoreRemoveService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreRemoveService.js test", () => +{ + it("test case1", () => + { + const data = new Map(); + data.set("0", "test"); + + const store = new Map(); + store.set("1", data); + + expect(data.size).toBe(1); + expect(store.size).toBe(1); + + execute(store, "1", "0"); + + expect(data.size).toBe(0); + expect(store.size).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveService.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveService.ts new file mode 100644 index 00000000..737353b7 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveService.ts @@ -0,0 +1,36 @@ +import { $poolMap } from "../../CacheUtil"; + +/** + * @description キャッシュストアから指定したキャッシュを削除 + * Remove the specified cache from the cache store + * + * @param {Map} data_store + * @param {string} id + * @param {string} type + * @return {void} + * @method + * @public + */ +export const execute = ( + data_store: Map>, + id: string, + type: string +): void => { + + if (!data_store.has(id)) { + return ; + } + + const data: Map = data_store.get(id) as NonNullable>; + if (!data.has(type)) { + return ; + } + + // delete key + data.delete(type); + + if (!data.size) { + $poolMap(data); + data_store.delete(id); + } +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.test.ts new file mode 100644 index 00000000..01639b42 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./CacheStoreRemoveTimerScheduledCacheService"; +import { describe, expect, it } from "vitest"; +import { $cacheStore } from "../.."; + +describe("CacheStoreRemoveTimerScheduledCacheService.js test", () => +{ + it("test case1", () => + { + const data = new Map(); + data.set("trash", true); + + const trash = new Map(); + trash.set("2", data); + + $cacheStore.$removeIds.length = 0; + $cacheStore.$removeCache = true; + expect($cacheStore.$removeIds.length).toBe(0); + expect($cacheStore.$removeCache).toBe(true); + expect(trash.size).toBe(1); + + execute($cacheStore, trash); + + expect($cacheStore.$removeIds.length).toBe(1); + expect($cacheStore.$removeCache).toBe(false); + expect(trash.size).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.ts new file mode 100644 index 00000000..28ef4ecc --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerScheduledCacheService.ts @@ -0,0 +1,34 @@ +import type { CacheStore } from "../../CacheStore"; + +/** + * @description タイマーでセットされた削除フラグを持つIDをキャッシュストアから削除する + * Remove the ID with the deletion flag set by the timer from the cache store + * + * @param {CacheStore} cache_store + * @param {Map} trash_store + * @return {void} + * @method + * @protected + */ +export const execute = ( + cache_store: CacheStore, + trash_store: Map> +): void => { + + if (!trash_store.size) { + return ; + } + + for (const [id, data] of trash_store) { + + if (!data.has("trash")) { + continue ; + } + + cache_store.removeById(id); + cache_store.$removeIds.push(+id); + } + + trash_store.clear(); + cache_store.$removeCache = false; +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.test.ts new file mode 100644 index 00000000..8846576a --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./CacheStoreRemoveTimerService"; +import { describe, expect, it } from "vitest"; +import { $cacheStore } from "../.."; + +describe("CacheStoreRemoveTimerService.js test", () => +{ + it("test case1", () => + { + const trash = new Map(); + const data = new Map(); + const store = new Map(); + store.set("2", data); + + expect(data.has("trash")).toBe(false); + expect(trash.size).toBe(0); + + execute($cacheStore, store, trash, "2"); + + expect(data.has("trash")).toBe(true); + expect(trash.size).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.ts b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.ts new file mode 100644 index 00000000..cbef62a5 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreRemoveTimerService.ts @@ -0,0 +1,45 @@ +import type { CacheStore } from "../../CacheStore"; + +/** + * @description キャッシュストアの指定IDの削除フラグを立てる + * Set the deletion flag of the specified ID in the cache store + * + * @param {CacheStore} cache_store + * @param {Map} data_store + * @param {Map} trash_store + * @param {string} id + * @return {void} + * @method + * @protected + */ +export const execute = ( + cache_store: CacheStore, + data_store: Map>, + trash_store: Map>, + id: string +): void => { + + if (trash_store.has(id)) { + return ; + } + + const data = data_store.get(id) as Map; + if (!data) { + return ; + } + + data.set("trash", true); + trash_store.set(id, data); + + if (cache_store.$timerId !== null) { + clearTimeout(cache_store.$timerId); + } + + // 1秒後に削除処理を行う + cache_store.$removeCache = false; + cache_store.$timerId = setTimeout((): void => + { + cache_store.$removeCache = true; + cache_store.$timerId = null; + }, 1000); +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreResetService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreResetService.test.ts new file mode 100644 index 00000000..af1b9c3a --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreResetService.test.ts @@ -0,0 +1,28 @@ +import { execute } from "./CacheStoreResetService"; +import { describe, expect, it } from "vitest"; +import { $cacheStore } from "../.."; + +describe("CacheStoreResetService.js test", () => +{ + it("test case1", () => + { + const data = new Map(); + data.set("0", "test"); + + const store = new Map(); + store.set("1", data); + + const trash = new Map(); + trash.set("2", "data"); + + expect(data.size).toBe(1); + expect(trash.size).toBe(1); + expect(store.size).toBe(1); + + execute($cacheStore, store, trash); + + expect(data.size).toBe(0); + expect(store.size).toBe(0); + expect(trash.size).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreResetService.ts b/packages/cache/src/CacheStore/service/CacheStoreResetService.ts new file mode 100644 index 00000000..12d5f07f --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreResetService.ts @@ -0,0 +1,39 @@ +import type { CacheStore } from "../../CacheStore"; +import { $poolMap } from "../../CacheUtil"; + +/** + * @description キャッシュストアを全てリセット + * Reset all cache stores + * + * @param {CacheStore} cache_store + * @param {Map} data_store + * @return {void} + * @method + * @public + */ +export const execute = ( + cache_store: CacheStore, + data_store: Map>, + trash_store: Map> +): void => { + + // タイマーをクリア + trash_store.clear(); + if (cache_store.$timerId !== null) { + clearTimeout(cache_store.$timerId); + } + + for (const data of data_store.values()) { + + for (const value of data.values()) { + if (!value) { + continue; + } + cache_store.destroy(value); + } + + $poolMap(data); + } + + data_store.clear(); +}; \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreSetService.test.ts b/packages/cache/src/CacheStore/service/CacheStoreSetService.test.ts new file mode 100644 index 00000000..64fe514a --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreSetService.test.ts @@ -0,0 +1,20 @@ +import { $cacheStore } from "../../CacheStore"; +import { execute } from "./CacheStoreSetService"; +import { describe, expect, it } from "vitest"; + +describe("CacheStoreSetService.js test", () => +{ + it("test case1", () => + { + const store = new Map(); + expect(store.size).toBe(0); + + execute($cacheStore, store, "1", "0", "test"); + + expect(store.size).toBe(1); + + const data = store.get("1"); + expect(data.size).toBe(1); + expect(data.get("0")).toBe("test"); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheStore/service/CacheStoreSetService.ts b/packages/cache/src/CacheStore/service/CacheStoreSetService.ts new file mode 100644 index 00000000..7dab6028 --- /dev/null +++ b/packages/cache/src/CacheStore/service/CacheStoreSetService.ts @@ -0,0 +1,53 @@ +import type { CacheStore } from "../../CacheStore"; +import { + $getMap, + $poolMap +} from "../../CacheUtil"; + +/** + * @description キャッシュストアにデータをセット + * Set data in the cache store + * + * @param {CacheStore} cache_store + * @param {Map} data_store + * @param {array} keys + * @param {*} value + * @return {void} + * @method + * @public + */ +export const execute = ( + cache_store: CacheStore, + data_store: Map>, + unique_key: string, + key: string, + value: any = null +): void => { + + // init + if (!data_store.has(unique_key)) { + data_store.set(unique_key, $getMap()); + } + + const data = data_store.get(unique_key) as NonNullable>; + if (value === null) { + + if (!data.has(key)) { + return ; + } + + cache_store.destroy(data.get(key)); + + data.delete(key); + + if (!data.size) { + data_store.delete(unique_key); + $poolMap(data); + } + + return ; + } + + // set cache + data.set(key, value); +}; \ No newline at end of file diff --git a/packages/cache/src/CacheUtil.test.ts b/packages/cache/src/CacheUtil.test.ts new file mode 100644 index 00000000..5d27fc11 --- /dev/null +++ b/packages/cache/src/CacheUtil.test.ts @@ -0,0 +1,18 @@ +import { $maps, $poolMap, $getMap } from "./CacheUtil"; +import { describe, expect, it } from "vitest"; + +describe("CacheUtil.js test", () => +{ + it("test case", () => + { + $maps.length = 0; + expect($maps.length).toBe(0); + + const map = $getMap(); + $poolMap(map); + expect($maps.length).toBe(1); + + $getMap(); + expect($maps.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/cache/src/CacheUtil.ts b/packages/cache/src/CacheUtil.ts new file mode 100644 index 00000000..96656f83 --- /dev/null +++ b/packages/cache/src/CacheUtil.ts @@ -0,0 +1,39 @@ +/** + * @description 使用済みになったMapオブジェクトをプール + * Pool Map objects that are no longer in use. + * + * @type {Map[]} + * @const + * @static + */ +export const $maps: Map[] = []; + +/** + * @description Mapオブジェクトをプール + * Pool Map object. + * + * @param {Map} map + * @return void + * @method + * @static + */ +export const $poolMap = (map: Map): void => +{ + if (map.size) { + map.clear(); + } + $maps.push(map); +}; + +/** + * @description プールしたMapオブジェクト、もしくは新規のMapを返却 + * Returns a pooled Map object or a new Map. + * + * @return {Map} + * @method + * @static + */ +export const $getMap = (): Map => +{ + return $maps.pop() || new Map(); +}; \ No newline at end of file diff --git a/packages/cache/src/index.ts b/packages/cache/src/index.ts new file mode 100644 index 00000000..97e2dffd --- /dev/null +++ b/packages/cache/src/index.ts @@ -0,0 +1 @@ +export * from "./CacheStore"; \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index f6041019..523ea878 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/core", "version": "*", - "description": "Next2D Core Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Core Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -32,16 +24,16 @@ "url": "git+https://github.com/Next2D/Player.git" }, "peerDependencies": { - "@next2d/interface": "file:../interface", - "@next2d/webgl": "file:../webgl", - "@next2d/display": "file:../display", "@next2d/core": "file:../core", - "@next2d/net": "file:../net", - "@next2d/share": "file:../share", + "@next2d/display": "file:../display", "@next2d/events": "file:../events", + "@next2d/filters": "file:../filters", + "@next2d/geom": "file:../geom", "@next2d/media": "file:../media", + "@next2d/net": "file:../net", + "@next2d/renderer": "file:../renderer", "@next2d/text": "file:../text", - "@next2d/geom": "file:../geom", - "@next2d/util": "file:../util" + "@next2d/ui": "file:../ui", + "@next2d/render-queue": "file:../render-queue" } } diff --git a/packages/core/src/Canvas.ts b/packages/core/src/Canvas.ts new file mode 100644 index 00000000..e8542799 --- /dev/null +++ b/packages/core/src/Canvas.ts @@ -0,0 +1,20 @@ +import { $devicePixelRatio, $setCanvas } from "./CoreUtil"; +import { execute as canvasInitializeService } from "./Canvas/service/CanvasInitializeService"; +import { execute as canvasBootOffscreenCanvasService } from "./Canvas/service/CanvasBootOffscreenCanvasService"; +import { execute as canvasRegisterEventUseCase } from "./Canvas/usecase/CanvasRegisterEventUseCase"; + +/** + * @type {HTMLCanvasElement} + * @public + */ +export const $canvas: HTMLCanvasElement = document.createElement("canvas"); +$setCanvas($canvas); + +// initial invoking function +canvasInitializeService($canvas, $devicePixelRatio); + +// Register an event +canvasRegisterEventUseCase($canvas); + +// Boot offscreen canvas +canvasBootOffscreenCanvasService($canvas); \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.test.ts b/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.test.ts new file mode 100644 index 00000000..a26b0f95 --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.test.ts @@ -0,0 +1,20 @@ +import { execute } from "./CanvasBootOffscreenCanvasService"; +import { describe, expect, it, vi } from "vitest"; + +describe("CanvasBootOffscreenCanvasService.js test", () => +{ + it("execute test case1", () => + { + let state = ""; + const MockCanvas = vi.fn().mockImplementation(() => + { + return { + "transferControlToOffscreen": vi.fn(() => { state = "ok" }) + } as unknown as HTMLCanvasElement; + }); + + expect(state).toBe(""); + execute(new MockCanvas()); + expect(state).toBe("ok"); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.ts b/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.ts new file mode 100644 index 00000000..bec9f88b --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasBootOffscreenCanvasService.ts @@ -0,0 +1,22 @@ +import { $rendererWorker } from "../../RendererWorker"; + +/** + * @description OffscreenCanvasを起動 + * Boot offscreen canvas + * + * @param {HTMLCanvasElement} canvas + * @return {void} + * @method + * @public + */ +export const execute = (canvas: HTMLCanvasElement): void => +{ + const offscreenCanvas = canvas.transferControlToOffscreen(); + + // postMessage + $rendererWorker.postMessage({ + "command": "initialize", + "canvas": offscreenCanvas, + "devicePixelRatio": window.devicePixelRatio + }, [offscreenCanvas]); +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasInitializeService.test.ts b/packages/core/src/Canvas/service/CanvasInitializeService.test.ts new file mode 100644 index 00000000..5408faa3 --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasInitializeService.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./CanvasInitializeService"; +import { describe, expect, it } from "vitest"; + +describe("CanvasInitializeService.js test", () => +{ + it("execute test case1", () => + { + const canvas: HTMLCanvasElement = document.createElement("canvas"); + execute(canvas, 1); + expect(canvas.width).toBe(1); + expect(canvas.height).toBe(1); + expect(canvas.getAttribute("style")).toBe( + "-webkit-tap-highlight-color: rgba(0,0,0,0);backface-visibility: hidden;touch-action: none;" + ); + }); + + it("execute test case2", () => + { + const canvas: HTMLCanvasElement = document.createElement("canvas"); + execute(canvas, 2); + expect(canvas.width).toBe(1); + expect(canvas.height).toBe(1); + expect(canvas.getAttribute("style")).toBe( + "-webkit-tap-highlight-color: rgba(0,0,0,0);backface-visibility: hidden;touch-action: none;transform: scale(0.5);" + ); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasInitializeService.ts b/packages/core/src/Canvas/service/CanvasInitializeService.ts new file mode 100644 index 00000000..8f82a780 --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasInitializeService.ts @@ -0,0 +1,29 @@ +/** + * @description canvasのスタイルの初期設定 + * Initial setting of canvas style + * + * @param {HTMLCanvasElement} canvas + * @param {number} [ratio=1] + * @return {void} + * @method + * @public + */ +export const execute = ( + canvas: HTMLCanvasElement, + ratio: number = 1 +): void => { + + // Set canvas style + let style = ""; + style += "-webkit-tap-highlight-color: rgba(0,0,0,0);"; + style += "backface-visibility: hidden;"; + style += "touch-action: none;"; + + if (ratio > 1) { + style += `transform: scale(${1 / ratio});`; + } + + canvas.width = 1; + canvas.height = 1; + canvas.setAttribute("style", style); +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasSetPositionService.test.ts b/packages/core/src/Canvas/service/CanvasSetPositionService.test.ts new file mode 100644 index 00000000..5b929385 --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasSetPositionService.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./CanvasSetPositionService"; +import { $mainCanvasPosition } from "@next2d/text"; +import { $setMainElement } from "../../CoreUtil"; +import { stage } from "@next2d/display"; +import { describe, expect, it } from "vitest"; + +describe("CanvasSetPositionService.js test", () => +{ + it("execute test case", () => + { + $mainCanvasPosition.x = 0; + $mainCanvasPosition.y = 0; + + const div = document.createElement("div"); + div.appendChild(document.createElement("canvas")); + $setMainElement(div); + + expect($mainCanvasPosition.x).toBe(0); + expect($mainCanvasPosition.y).toBe(0); + + stage.rendererWidth = 100; + stage.rendererHeight = 200; + execute(); + + expect($mainCanvasPosition.x).toBe(-50); + expect($mainCanvasPosition.y).toBe(-100); + + stage.rendererWidth = 0; + stage.rendererHeight = 0; + }); +}); \ No newline at end of file diff --git a/packages/core/src/Canvas/service/CanvasSetPositionService.ts b/packages/core/src/Canvas/service/CanvasSetPositionService.ts new file mode 100644 index 00000000..90c25f0e --- /dev/null +++ b/packages/core/src/Canvas/service/CanvasSetPositionService.ts @@ -0,0 +1,36 @@ +import { $mainCanvasPosition } from "@next2d/text"; +import { stage } from "@next2d/display"; +import { $getMainElement } from "../../CoreUtil"; + +/** + * @type {number} + * @private + */ +const $devicePixelRatio: number = window.devicePixelRatio || 1; + +/** + * @description メインキャンバスの位置を設定します。 + * Set the position of the main canvas. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $mainCanvasPosition.x = 0; + $mainCanvasPosition.y = 0; + + const element: HTMLDivElement = $getMainElement(); + if (!element) { + return ; + } + + const canvas = element.children[0] as HTMLCanvasElement; + if (!canvas || canvas.localName !== "canvas") { + return ; + } + + $mainCanvasPosition.x = (element.clientWidth - stage.rendererWidth / $devicePixelRatio) / 2; + $mainCanvasPosition.y = (element.clientHeight - stage.rendererHeight / $devicePixelRatio) / 2; +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasPointerDownEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasPointerDownEventUseCase.ts new file mode 100644 index 00000000..d7ff0ef5 --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasPointerDownEventUseCase.ts @@ -0,0 +1,74 @@ +import { $setEvent } from "@next2d/events"; +import { $player } from "../../Player"; +import { execute as playerHitTestUseCase } from "../../Player/usecase/PlayerHitTestUseCase"; +import { execute as playerSetCurrentMousePointService } from "../../Player/service/PlayerSetCurrentMousePointService"; +import { execute as playerPointerDownEventService } from "../../Player/service/PlayerPointerDownEventService"; +import { execute as playerDoubleClickEventService } from "../../Player/service/PlayerDoubleClickEventService"; +import { $hitObject } from "../../CoreUtil"; + +/** + * @type {NodeJS.Timeout} + * @private + */ +let $timerId: NodeJS.Timeout; + +/** + * @type {boolean} + * @private + */ +let $wait: boolean = false; + +/** + * @description プレイヤーのポインターダウンイベントを処理します。 + * Processes the player's pointer down event. + * + * @param {PointerEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: PointerEvent): void => +{ + const element = event.target as HTMLCanvasElement; + if (!element) { + return ; + } + + $player.mouseState = "down"; + element.setPointerCapture(event.pointerId); + + $setEvent(event); + playerSetCurrentMousePointService(event); + + // start position + playerHitTestUseCase(); + + if ($hitObject.hit) { + event.preventDefault(); + } + + // fixed logic + clearTimeout($timerId); + + if (!$wait) { + + // 初回のタップであればダブルタップを待機モードに変更 + $wait = true; + + // ダブルタップ有効期限をセット + $timerId = setTimeout((): void => + { + $wait = false; + }, 300); + + playerPointerDownEventService(); + + } else { + + // ダブルタップを終了 + $wait = false; + + playerDoubleClickEventService(); + + } +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasPointerLeaveEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasPointerLeaveEventUseCase.ts new file mode 100644 index 00000000..5ccbc1e3 --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasPointerLeaveEventUseCase.ts @@ -0,0 +1,34 @@ +import type { DisplayObject } from "@next2d/display"; +import { $getRollOverDisplayObject } from "../../CoreUtil"; +import { + PointerEvent as Next2D_PointerEvent, + $setEvent +} from "@next2d/events"; + +/** + * @description マウス、タップがDisplayObjectから離れた時に発生します。 + * Occurs when the mouse or tap leaves the DisplayObject. + * + * @param {PointerEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: PointerEvent): void => +{ + $setEvent(event); + + const rollOverDisplayObject = $getRollOverDisplayObject() as DisplayObject; + if (!rollOverDisplayObject) { + return ; + } + + if (rollOverDisplayObject.willTrigger(Next2D_PointerEvent.POINTER_LEAVE)) { + // イベントの伝播を止める + event.preventDefault(); + + rollOverDisplayObject.dispatchEvent( + new Next2D_PointerEvent(Next2D_PointerEvent.POINTER_LEAVE) + ); + } +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasPointerMoveEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasPointerMoveEventUseCase.ts new file mode 100644 index 00000000..55c416cb --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasPointerMoveEventUseCase.ts @@ -0,0 +1,36 @@ +import { $setEvent } from "@next2d/events"; +import { execute as playerHitTestUseCase } from "../../Player/usecase/PlayerHitTestUseCase"; +import { execute as playerSetCurrentMousePoint } from "../../Player/service/PlayerSetCurrentMousePointService"; +import { execute as playerPointerMoveEventService } from "../../Player/service/PlayerPointerMoveEventService"; +import { $hitObject } from "../../CoreUtil"; + +/** + * @description プレイヤーのポインタームーブイベントを処理します。 + * Handles the player's pointer move event. + * + * @param {PointerEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: PointerEvent): void => +{ + const element = event.target as HTMLCanvasElement; + if (!element) { + return ; + } + + $setEvent(event); + playerSetCurrentMousePoint(event); + + // start position + playerHitTestUseCase(); + + if ($hitObject.hit) { + event.preventDefault(); + } + + // fixed logic + // ポインタームーブイベントを発火 + playerPointerMoveEventService(); +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasPointerUpEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasPointerUpEventUseCase.ts new file mode 100644 index 00000000..f4a48b3c --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasPointerUpEventUseCase.ts @@ -0,0 +1,35 @@ +import { $setEvent } from "@next2d/events"; +import { $player } from "../../Player"; +import { execute as playerHitTestUseCase } from "../../Player/usecase/PlayerHitTestUseCase"; +import { execute as playerSetCurrentMousePoint } from "../../Player/service/PlayerSetCurrentMousePointService"; +import { execute as playerPointerUpEventService } from "../../Player/service/PlayerPointerUpEventService"; + +/** + * @description プレイヤーのポインターアップイベントを処理します。 + * Processes the player's pointer up event. + * + * @param {PointerEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: PointerEvent): void => +{ + const element = event.target as HTMLCanvasElement; + if (!element) { + return ; + } + + $player.mouseState = "up"; + element.releasePointerCapture(event.pointerId); + + $setEvent(event); + playerSetCurrentMousePoint(event); + + // start position + playerHitTestUseCase(); + + // fixed logic + // ポインターアップイベントを発火します。 + playerPointerUpEventService(); +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.test.ts b/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.test.ts new file mode 100644 index 00000000..f0fbf74d --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./CanvasRegisterEventUseCase"; +import { + PointerEvent, + WheelEvent +} from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("CanvasRegisterEventUseCase.js test", () => +{ + it("execute test case1", () => + { + let results: string[] = []; + const MockCanvas = { + "addEventListener": vi.fn((event: string) => + { + results.push(event); + }) + } as unknown as HTMLCanvasElement; + + execute(MockCanvas); + expect(results.length).toBe(6); + + expect(results[0]).toBe(PointerEvent.POINTER_UP); + expect(results[1]).toBe(PointerEvent.POINTER_DOWN); + expect(results[2]).toBe(PointerEvent.POINTER_UP); + expect(results[3]).toBe(PointerEvent.POINTER_MOVE); + expect(results[4]).toBe(PointerEvent.POINTER_LEAVE); + expect(results[5]).toBe(WheelEvent.WHEEL); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.ts new file mode 100644 index 00000000..2bd549a4 --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasRegisterEventUseCase.ts @@ -0,0 +1,52 @@ +import { execute as canvasPointerDownEventUseCase } from "./CanvasPointerDownEventUseCase"; +import { execute as canvasPointerUpEventUseCase } from "./CanvasPointerUpEventUseCase"; +import { execute as canvasPointerMoveEventUseCase } from "./CanvasPointerMoveEventUseCase"; +import { execute as canvasPointerLeaveEventUseCase } from "./CanvasPointerLeaveEventUseCase"; +import { execute as canvasWheelEventUseCase } from "./CanvasWheelEventUseCase"; +import { + PointerEvent, + WheelEvent +} from "@next2d/events"; +import { + $bootAudioContext, + $getMutedVideos +} from "@next2d/media"; + +/** + * @description HTMLCanvasElementにイベントを登録します。 + * Register events on HTMLCanvasElement. + * + * @param {HTMLCanvasElement} canvas + * @return {void} + * @method + * @public + */ +export const execute = (canvas: HTMLCanvasElement): void => +{ + const $loadAudioContext = (): void => + { + // audio contextを起動 + $bootAudioContext(); + + // ミュートになっているビデオの音声をon + const mutedVideos = $getMutedVideos(); + for (let idx = 0; idx < mutedVideos.length; ++idx) { + const video = mutedVideos[idx]; + if (!video) { + continue; + } + + video.muted = false; + } + mutedVideos.length = 0; + + canvas.removeEventListener(PointerEvent.POINTER_UP, $loadAudioContext as EventListener); + }; + canvas.addEventListener(PointerEvent.POINTER_UP, $loadAudioContext as EventListener); + + canvas.addEventListener(PointerEvent.POINTER_DOWN, canvasPointerDownEventUseCase as EventListener, { "passive": false }); + canvas.addEventListener(PointerEvent.POINTER_UP, canvasPointerUpEventUseCase as EventListener); + canvas.addEventListener(PointerEvent.POINTER_MOVE, canvasPointerMoveEventUseCase as EventListener, { "passive": false }); + canvas.addEventListener(PointerEvent.POINTER_LEAVE, canvasPointerLeaveEventUseCase as EventListener); + canvas.addEventListener(WheelEvent.WHEEL, canvasWheelEventUseCase as EventListener); +}; \ No newline at end of file diff --git a/packages/core/src/Canvas/usecase/CanvasWheelEventUseCase.ts b/packages/core/src/Canvas/usecase/CanvasWheelEventUseCase.ts new file mode 100644 index 00000000..77a1030e --- /dev/null +++ b/packages/core/src/Canvas/usecase/CanvasWheelEventUseCase.ts @@ -0,0 +1,49 @@ +import type { DisplayObject } from "@next2d/display"; +import type { TextField } from "@next2d/text"; +import { stage } from "@next2d/display"; +import { $hitObject } from "../../CoreUtil"; +import { + $setEvent, + WheelEvent as Next2D_WheelEvent +} from "@next2d/events"; + +/** + * @description ホイールイベントを実行します。 + * Executes the wheel event. + * + * @param {WheelEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: WheelEvent): void => +{ + $setEvent(event); + + const displayObject = $hitObject.hit as unknown as D; + if (displayObject && displayObject.isText + && (displayObject as unknown as TextField).scrollEnabled + ) { + + event.preventDefault(); + + if (event.deltaX) { + (displayObject as unknown as TextField).scrollX += event.deltaX / ((displayObject as unknown as TextField).textWidth / (displayObject as unknown as TextField).width); + } + + if (event.deltaY) { + (displayObject as unknown as TextField).scrollY += event.deltaY / ((displayObject as unknown as TextField).textHeight / (displayObject as unknown as TextField).height); + } + + return ; + } + + if (stage.willTrigger(Next2D_WheelEvent.WHEEL)) { + // イベントの伝播を止める + event.preventDefault(); + + stage.dispatchEvent(new Next2D_WheelEvent( + Next2D_WheelEvent.WHEEL + )); + } +}; \ No newline at end of file diff --git a/packages/core/src/CoreUtil.ts b/packages/core/src/CoreUtil.ts new file mode 100644 index 00000000..d7f14cc2 --- /dev/null +++ b/packages/core/src/CoreUtil.ts @@ -0,0 +1,199 @@ +import type { IPlayerHitObject } from "./interface/IPlayerHitObject"; +import type { IDisplayObject } from "./interface/IDisplayObject"; + +/** + * @type {string} + * @const + */ +export const $PREFIX: string = "__next2d__"; + +/** + * @type {number} + * @const + */ +export const $devicePixelRatio: number = Math.min(2, window.devicePixelRatio); + +/** + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @static + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +const canvas = document.createElement("canvas"); +canvas.width = canvas.height = 1; + +/** + * @type {CanvasRenderingContext2D} + * @const + */ +export const $hitContext: CanvasRenderingContext2D = canvas.getContext("2d") as NonNullable; + +/** + * @type {HTMLDivElement} + * @private + */ +let $mainElement: HTMLDivElement | null = null; + +/** + * @description メインのコンテナになるDivElementを設定します。 + * Set the DivElement to be the main container. + * + * @param {HTMLDivElement} element + * @method + * @protected + */ +export const $setMainElement = (element: HTMLDivElement): void => +{ + $mainElement = element; +}; + +/** + * @type {HTMLCanvasElement | null} + * @private + */ +let $canvas: HTMLCanvasElement | null = null; + +/** + * @description メインのCanvasになるCanvasElementを設定します。 + * Set the CanvasElement to be the main canvas. + * + * @param {HTMLCanvasElement} element + * @method + * @protected + */ +export const $setCanvas = (element: HTMLCanvasElement): void => +{ + $canvas = element; +}; + +/** + * @description メインのCanvasになるCanvasElementを取得します。 + * Get the CanvasElement to be the main canvas. + * + * @return {HTMLCanvasElement} + * @method + * @protected + */ +export const $getCanvas = (): HTMLCanvasElement => +{ + return $canvas as NonNullable; +}; + +/** + * @description メインのコンテナになるDivElementを取得します。 + * Get the DivElement to be the main container. + * + * @return {HTMLDivElement} + * @method + * @protected + */ +export const $getMainElement = (): HTMLDivElement => +{ + return $mainElement as NonNullable; +}; + +/** + * @description 描画用のmatrix情報 + * Matrix information for drawing + * + * @type {Float32Array} + * @protected + */ +export const $renderMatrix: Float32Array = new Float32Array([1, 0, 0, 1, 0, 0]); + +/** + * @description マウス、タップ時の画面のmatrix情報 + * Screen matrix information when mouse or tap is pressed + * + * @type {Float32Array} + * @protected + */ +export const $hitMatrix: Float32Array = new Float32Array([1, 0, 0, 1, 0, 0]); + +/** + * @description マウス、タップがヒットしたDisplayObjectを取得します。 + * Get the DisplayObject that the mouse or tap hit. + * + * @type {IPlayerHitObject} + * @protected + */ +export const $hitObject: IPlayerHitObject = { + "x": 0, + "y": 0, + "pointer": "", + "hit": null +}; + +/** + * @description マウス、タップがヒットしたDisplayObjectを取得します。 + * Get the DisplayObject that the mouse or tap hit. + * + * @type {IDisplayObject | null} + * @private + */ +let $rollOverDisplayObject: IDisplayObject | null = null; + +/** + * @description マウス、タップがヒットしたDisplayObjectを取得します。 + * Get the DisplayObject that the mouse or tap hit. + * + * @param {IDisplayObject} displayObject + * @return {void} + * @method + * @protected + */ +export const $setRollOverDisplayObject = (displayObject: IDisplayObject | null): void => +{ + $rollOverDisplayObject = displayObject; +}; + +/** + * @description マウス、タップがヒットしたDisplayObjectを取得します。 + * Get the DisplayObject that the mouse or tap hit. + * + * @return {IDisplayObject} + * @method + * @protected + */ +export const $getRollOverDisplayObject = (): IDisplayObject | null => +{ + return $rollOverDisplayObject; +}; + +/** + * @description 指定された値を2の累乗に切り上げます。 + * Rounds the specified value up to a power of two. + * + * @param {number} v + * @return {number} + * @method + * @protected + */ +export const $upperPowerOfTwo = (v: number): number => +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +}; \ No newline at end of file diff --git a/packages/core/src/Display.ts b/packages/core/src/Display.ts index 4c6a99e8..bf583498 100644 --- a/packages/core/src/Display.ts +++ b/packages/core/src/Display.ts @@ -1,36 +1,32 @@ -import type { DisplayImpl } from "@next2d/interface"; +import type { IDisplay } from "./interface/IDisplay"; import { - DisplayObject, - InteractiveObject, - DisplayObjectContainer, Sprite, MovieClip, BitmapData, BlendMode, + DisplayObject, + DisplayObjectContainer, FrameLabel, Graphics, + InteractiveObject, Loader, - LoaderInfo, Shape, - Stage, - TextField + stage } from "@next2d/display"; -const display: DisplayImpl = { - DisplayObject, - InteractiveObject, - DisplayObjectContainer, +const display: IDisplay = { Sprite, MovieClip, BitmapData, BlendMode, + DisplayObject, + DisplayObjectContainer, FrameLabel, Graphics, + InteractiveObject, Loader, - LoaderInfo, Shape, - Stage, - TextField + stage }; Object.entries(display).forEach(([key, DisplayClass]) => diff --git a/packages/core/src/Events.ts b/packages/core/src/Events.ts index de7b63d2..9b101ea1 100644 --- a/packages/core/src/Events.ts +++ b/packages/core/src/Events.ts @@ -1,4 +1,4 @@ -import { EventsImpl } from "@next2d/interface"; +import type { IEvents } from "./interface/IEvents"; import { Event, EventDispatcher, @@ -6,19 +6,21 @@ import { FocusEvent, HTTPStatusEvent, IOErrorEvent, - MouseEvent, + PointerEvent, + JobEvent, ProgressEvent, VideoEvent } from "@next2d/events"; -const events: EventsImpl = { +const events: IEvents = { Event, EventDispatcher, EventPhase, FocusEvent, HTTPStatusEvent, IOErrorEvent, - MouseEvent, + PointerEvent, + JobEvent, ProgressEvent, VideoEvent }; diff --git a/packages/core/src/Filters.ts b/packages/core/src/Filters.ts index 306ee19c..5f8ceb0e 100644 --- a/packages/core/src/Filters.ts +++ b/packages/core/src/Filters.ts @@ -1,4 +1,4 @@ -import type { FiltersImpl } from "@next2d/interface"; +import type { IFilters } from "./interface/IFilters"; import { BevelFilter, BlurFilter, @@ -11,7 +11,7 @@ import { GradientGlowFilter } from "@next2d/filters"; -const filters: FiltersImpl = { +const filters: IFilters = { BevelFilter, BlurFilter, ColorMatrixFilter, diff --git a/packages/core/src/Geom.ts b/packages/core/src/Geom.ts index 08d31ade..6b35948d 100644 --- a/packages/core/src/Geom.ts +++ b/packages/core/src/Geom.ts @@ -1,18 +1,16 @@ -import { GeomImpl } from "@next2d/interface"; +import type { IGeom } from "./interface/IGeom"; import { ColorTransform, Matrix, Point, - Rectangle, - Transform + Rectangle } from "@next2d/geom"; -const geom: GeomImpl = { +const geom: IGeom = { ColorTransform, Matrix, Point, - Rectangle, - Transform + Rectangle }; Object.entries(geom).forEach(([key, GeomClass]) => diff --git a/packages/core/src/Media.ts b/packages/core/src/Media.ts index c97f7480..1323d847 100644 --- a/packages/core/src/Media.ts +++ b/packages/core/src/Media.ts @@ -1,4 +1,4 @@ -import { MediaImpl } from "@next2d/interface"; +import type { IMedia } from "./interface/IMedia"; import { Sound, SoundMixer, @@ -6,7 +6,7 @@ import { Video } from "@next2d/media"; -const media: MediaImpl = { +const media: IMedia = { Sound, SoundMixer, SoundTransform, diff --git a/packages/core/src/Net.ts b/packages/core/src/Net.ts index a71595b6..2034ab61 100644 --- a/packages/core/src/Net.ts +++ b/packages/core/src/Net.ts @@ -1,12 +1,8 @@ -import { NetImpl } from "@next2d/interface"; -import { - URLRequest, - URLRequestHeader -} from "@next2d/net"; +import type { INet } from "./interface/INet"; +import { URLRequest } from "@next2d/net"; -const net: NetImpl = { - URLRequest, - URLRequestHeader +const net: INet = { + URLRequest }; Object.entries(net).forEach(([key, NetClass]) => diff --git a/packages/core/src/Next2D.ts b/packages/core/src/Next2D.ts index 5df11d66..f09926b0 100644 --- a/packages/core/src/Next2D.ts +++ b/packages/core/src/Next2D.ts @@ -1,3 +1,17 @@ +import type { IDisplay } from "./interface/IDisplay"; +import type { IEvents } from "./interface/IEvents"; +import type { IFilters } from "./interface/IFilters"; +import type { IGeom } from "./interface/IGeom"; +import type { IMedia } from "./interface/IMedia"; +import type { INet } from "./interface/INet"; +import type { IText } from "./interface/IText"; +import type { IUI } from "./interface/IUI"; +import type { ICaptureOptions } from "./interface/ICaptureOptions"; +import type { IPlayerOptions } from "./interface/IPlayerOptions"; +import type { + Sprite, + DisplayObject +} from "@next2d/display"; import { events } from "./Events"; import { display } from "./Display"; import { filters } from "./Filters"; @@ -6,127 +20,124 @@ import { media } from "./Media"; import { net } from "./Net"; import { text } from "./Text"; import { ui } from "./UI"; -import { Player } from "@next2d/core"; -import { URLRequest } from "@next2d/net"; -import { - Loader, - Sprite, - LoaderInfo -} from "@next2d/display"; -import { - Event, - IOErrorEvent -} from "@next2d/events"; -import { - PlayerOptionsImpl, - DisplayImpl, - EventsImpl, - FiltersImpl, - GeomImpl, - MediaImpl, - NetImpl, - TextImpl, - UIImpl, - StageDataImpl -} from "@next2d/interface"; -import { - $clamp, - $poolArray -} from "@next2d/share"; +import { execute as loadService } from "./Next2D/usecase/LoadUseCase"; +import { execute as createRootMovieClipUseCase } from "./Next2D/usecase/CreateRootMovieClipUseCase"; +import { execute as captureToCanvasUseCase } from "./Next2D/usecase/CaptureToCanvasUseCase"; /** - * playerの起動管理クラス - * player startup management class + * @description Next2Dの起動管理クラス + * Boot management class of Next2D + * * @class + * @public */ export class Next2D { - private readonly _$promises: Promise[]; - private readonly _$player: Player; - public readonly display: DisplayImpl; - public readonly events: EventsImpl; - public readonly filters: FiltersImpl; - public readonly geom: GeomImpl; - public readonly media: MediaImpl; - public readonly net: NetImpl; - public readonly text: TextImpl; - public readonly ui: UIImpl; - /** - * @constructor + * @description Displayパッケージ + * Display package + * + * @type {IDisplay} * @public */ - constructor (promises: Promise[]) - { - /** - * @type {array} - * @private - */ - this._$promises = promises; + public readonly display: IDisplay; - /** - * @type {Player} - * @private - */ - this._$player = new Player(); - - /** - * @type {DisplayImpl} - * @public - */ - this.display = display; + /** + * @description Eventsパッケージ + * Events package + * + * @type {IEvents} + * @public + */ + public readonly events: IEvents; - /** - * @type {EventsImpl} - * @public - */ - this.events = events; + /** + * @description Filtersパッケージ + * Filters package + * + * @type {IFilters} + * @public + */ + public readonly filters: IFilters; - /** - * @type {FiltersImpl} - * @public - */ - this.filters = filters; + /** + * @description Geomパッケージ + * Geom package + * + * @type {IGeom} + * @public + */ + public readonly geom: IGeom; - /** - * @type {GeomImpl} - * @public - */ - this.geom = geom; + /** + * @description Mediaパッケージ + * Media package + * + * @type {IMedia} + * @public + */ + public readonly media: IMedia; - /** - * @type {MediaImpl} - * @public - */ - this.media = media; + /** + * @description Netパッケージ + * Net package + * + * @type {INet} + * @public + */ + public readonly net: INet; - /** - * @type {NetImpl} - * @public - */ - this.net = net; + /** + * @description Textパッケージ + * Text package + * + * @type {IText} + * @public + */ + public readonly text: IText; - /** - * @type {TextImpl} - * @public - */ - this.text = text; + /** + * @description UIパッケージ + * UI package + * + * @type {IUI} + * @public + */ + public readonly ui: IUI; - /** - * @type {UIImpl} - * @public - */ - this.ui = ui; - } + /** + * @description 初期起動Promise + * Initial boot Promise + * + * @type {Promise} + * @private + */ + private readonly _$promise: Promise; /** - * @member {Player} - * @readonly - * @return {Player} + * @constructor + * @public */ - get player (): Player + constructor () { - return this._$player; + // packages + this.display = display; + this.events = events; + this.filters = filters; + this.geom = geom; + this.media = media; + this.net = net; + this.text = text; + this.ui = ui; + + this._$promise = new Promise((resolve): void => + { + if (document.readyState === "loading") { + window.addEventListener("DOMContentLoaded", (): void => resolve(), { "once": true }); + } else { + resolve(); + } + }); } /** @@ -136,95 +147,19 @@ export class Next2D * @param {string} url JSONファイルのURL * URL of the JSON file * - * @param {object} [options=null] {number} width = Stageの幅 | Stage width - * {number} height = Stageの高さ | Stage height + * @param {object} [options=null] {number} width Stageの幅 | Stage width + * {number} height Stageの高さ | Stage height * {string} [tagId=null] canvasを追加対象のDOMのID | ID of the DOM to which the canvas is added - * {string} [base="/"] Loaderが読み込む際の絶対パス | Absolute path for Loader to load. - * {number|string|boolean} [bgColor=null] 背景色 | background color + * {string} [bgColor=null] 背景色 | background color * * @return {void} * @method * @public */ - load (url: string, options: PlayerOptionsImpl): void + async load (url: string, options: IPlayerOptions | null = null): Promise { - Promise - .all(this._$promises) - .then(() => - { - $poolArray(this._$promises); - - if (url === "develop") { - const path: string = location - .search - .slice(1) - .split("&")[0]; - - if (!path) { - return ; - } - url = `${location.origin}/${path}`; - } - - if (!url) { - return ; - } - - if (url.charAt(1) === "/") { - url = url.slice(1); - } - - // base set - if ((!options || !("base" in options)) && url.indexOf("//") > -1) { - this._$player.base = url; - } - - this._$player.setOptions(options); - this._$player._$initialize(); - - const loader: Loader = new Loader(); - - loader - .contentLoaderInfo - .addEventListener(IOErrorEvent.IO_ERROR, (event: IOErrorEvent) => - { - if (event.target) { - event.target.removeEventListener(IOErrorEvent.IO_ERROR, event.listener); - } - alert("Error: " + event.text); - }); - - loader - .contentLoaderInfo - .addEventListener(Event.COMPLETE, (event: Event) => - { - const loaderInfo: LoaderInfo = event.target as NonNullable; - const player: Player = this._$player; - - loaderInfo - .removeEventListener(Event.COMPLETE, event.listener); - - if (loaderInfo._$data) { - - const stage: StageDataImpl = loaderInfo._$data.stage; - - player.bgColor = stage.bgColor; - player._$setBackgroundColor(stage.bgColor); - - player.stage.addChild(loaderInfo.content); - - player.width = stage.width; - player.height = stage.height; - - // set fps fixed logic - player.stage._$frameRate = $clamp(+stage.fps, 1, 60, 60); - } - - player._$resize(); - }); - - loader.load(new URLRequest(url)); - }); + await Promise.all([this._$promise]); + await loadService(url, options); } /** @@ -233,35 +168,38 @@ export class Next2D * * @param {number} [width=240] * @param {number} [height=240] - * @param {number} [fps=24] + * @param {number} [fps=60] * @param {object} [options=null] * @return {Sprite} * @method * @public */ async createRootMovieClip ( - width: number = 240, height: number = 240, - fps: number = 24, options: PlayerOptionsImpl|null = null + width: number = 240, + height: number = 240, + fps: number = 60, + options: IPlayerOptions | null = null ): Promise { + await Promise.all([this._$promise]); + return createRootMovieClipUseCase( + width, height, fps, options + ); + } - await Promise.all(this._$promises); - $poolArray(this._$promises); - - const player: Player = this._$player; - - // setup - player.width = width | 0; - player.height = height | 0; - player.mode = "create"; - player.stage._$frameRate = fps | 0; - player.setOptions(options); - player._$initialize(); - - const root: Sprite = player.stage.addChild(new Sprite()); - - player._$loadStatus = Player.LOAD_END; - player.play(); - - return root; + /** + * @description 指定のDisplayObjectをcanvasにキャプチャする + * Capture the specified DisplayObject to the canvas + * + * @param {D} display_object + * @param {ICaptureOptions} [opstions=null] + * @return {void} + * @method + * @public + */ + async captureToCanvas ( + display_object: D, + opstions: ICaptureOptions | null = null + ): Promise { + return captureToCanvasUseCase(display_object, opstions); } -} +} \ No newline at end of file diff --git a/packages/core/src/Next2D/service/VideoSyncService.ts b/packages/core/src/Next2D/service/VideoSyncService.ts new file mode 100644 index 00000000..cd01740a --- /dev/null +++ b/packages/core/src/Next2D/service/VideoSyncService.ts @@ -0,0 +1,64 @@ +import type { DisplayObject, DisplayObjectContainer } from "@next2d/display"; + +/** + * @description DisplayObject の子要素に Video が含まれている場合、ロードが完了するまで待機します。 + * If the child element of DisplayObject contains Video, wait until the loading is complete. + * + * @param {DisplayObject} display_object + * @return {Promise} + * @method + * @protected + */ +export const execute = async (display_object: D): Promise => +{ + switch (true) { + + case display_object.isVideo: + break; + + case display_object.isContainerEnabled: + { + const children = (display_object as unknown as DisplayObjectContainer).children; + for (let idx = 0; idx < children.length; ++idx) { + + const displayObject = children[idx]; + if (!displayObject) { + continue; + } + + if (displayObject.isVideo) { + + const muted = displayObject.muted; + displayObject.muted = true; + + await displayObject.play(); + displayObject.pause(); + displayObject.muted = muted; + + await new Promise((resolve) => + { + const wait = async (): Promise => + { + if (displayObject.loaded) { + displayObject.seek(0); + resolve(); + } else { + requestAnimationFrame(wait); + } + }; + requestAnimationFrame(wait); + }); + } + + if (displayObject.isContainerEnabled) { + await execute(displayObject as DisplayObjectContainer); + } + } + } + break; + + default: + break; + + } +}; \ No newline at end of file diff --git a/packages/core/src/Next2D/usecase/CaptureToCanvasUseCase.ts b/packages/core/src/Next2D/usecase/CaptureToCanvasUseCase.ts new file mode 100644 index 00000000..6a33e4ac --- /dev/null +++ b/packages/core/src/Next2D/usecase/CaptureToCanvasUseCase.ts @@ -0,0 +1,128 @@ +import type { DisplayObject } from "@next2d/display"; +import type { ICaptureOptions } from "../../interface/ICaptureOptions"; +import { stage } from "@next2d/display"; +import { $cacheStore } from "@next2d/cache"; +import { $player } from "../../Player"; +import { $devicePixelRatio } from "../../CoreUtil"; +import { execute as playerResizePostMessageService } from "../../Player/service/PlayerResizePostMessageService"; +import { execute as playerTransferCanvasPostMessageService } from "../../Player/service/PlayerTransferCanvasPostMessageService"; +import { execute as videoSyncService } from "../service/VideoSyncService"; +import { + Matrix, + ColorTransform +} from "@next2d/geom"; + +/** + * @type {Float32Array} + * @private + */ +const $MATRIX_ARRAY_IDENTITY: Float32Array = new Float32Array([1, 0, 0, 1, 0, 0]); + +/** + * @type {Float32Array} + * @private + */ +const $COLOR_ARRAY_IDENTITY: Float32Array = new Float32Array([1, 1, 1, 1, 0, 0, 0, 0]); + +/** + * @description 指定した DisplayObject を Canvas に描画する + * Draw the specified DisplayObject in Canvas + * + * @param {D} display_object + * @param {ICaptureOptions} [opstions=null] + * @method + * @protected + */ +export const execute = async ( + display_object: D, + opstions: ICaptureOptions | null = null +): Promise => { + + if (opstions && opstions.videoSync) { + await videoSyncService(display_object); + } + + const tColorTransform = opstions && opstions.colorTransform + ? opstions.colorTransform.rawData + : $COLOR_ARRAY_IDENTITY; + + const rectangle = display_object.getBounds(); + const translateMatrix = new Float32Array([ + 1, 0, 0, 1, -rectangle.x, -rectangle.y + ]); + + const tMatrix = opstions && opstions.matrix + ? Matrix.multiply(opstions.matrix.rawData, translateMatrix) + : Matrix.multiply($MATRIX_ARRAY_IDENTITY, translateMatrix); + + const transferredCanvas = opstions && opstions.canvas + ? opstions.canvas + : $cacheStore.getCanvas(); + + const xScale = Math.sqrt(tMatrix[0] * tMatrix[0] + tMatrix[1] * tMatrix[1]); + const yScale = Math.sqrt(tMatrix[2] * tMatrix[2] + tMatrix[3] * tMatrix[3]); + + const width = Math.ceil(display_object.width * xScale); + const height = Math.ceil(display_object.height * yScale); + if (width <= 0 || height <= 0) { + return transferredCanvas; + } + + // resize canvas + transferredCanvas.width = width; + transferredCanvas.height = height; + + const stopFlag = $player.stopFlag; + if (!stopFlag) { + $player.stop(); + } + + // resize + let isResize = false; + const cacheWidth = $player.rendererWidth; + const cacheHeight = $player.rendererHeight; + const cacheScale = $player.rendererScale; + if (width > cacheWidth || height > cacheHeight) { + + isResize = true; + + const scale = Math.min( + width / stage.stageWidth, + height / stage.stageHeight + ) * $devicePixelRatio; + + // update + stage.rendererScale = $player.rendererScale = scale; + stage.rendererWidth = $player.rendererWidth = width; + stage.rendererHeight = $player.rendererHeight = height; + + // workerにリサイズを通知 + playerResizePostMessageService(false); + } + + // draw + await playerTransferCanvasPostMessageService( + display_object, tMatrix, tColorTransform, transferredCanvas + ); + + // restore + if (isResize) { + stage.rendererScale = $player.rendererScale = cacheScale; + stage.rendererWidth = $player.rendererWidth = cacheWidth; + stage.rendererHeight = $player.rendererHeight = cacheHeight; + + // workerにリサイズを通知 + playerResizePostMessageService(false); + } + + if (!stopFlag) { + $player.play(); + } + + Matrix.release(tMatrix); + if (opstions && opstions.colorTransform) { + ColorTransform.release(tColorTransform); + } + + return transferredCanvas; +}; \ No newline at end of file diff --git a/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts b/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts new file mode 100644 index 00000000..bd3d45aa --- /dev/null +++ b/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts @@ -0,0 +1,66 @@ +import type { IPlayerOptions } from "../../interface/IPlayerOptions"; +import { $clamp } from "../../CoreUtil"; +import { execute as playerRemoveLoadingElementService } from "../../Player/service/PlayerRemoveLoadingElementService"; +import { execute as playerAppendElementService } from "../../Player/service/PlayerAppendElementService"; +import { execute as playerReadyCompleteUseCase } from "../../Player/usecase/PlayerReadyCompleteUseCase"; +import { execute as playerBootUseCase } from "../../Player/usecase/PlayerBootUseCase"; +import { execute as canvasSetPositionService } from "../../Canvas/service/CanvasSetPositionService"; +import { + Sprite, + stage +} from "@next2d/display"; + +/** + * @type {boolean} + * @private + */ +let $boot: boolean = false; + +/** + * @description RootのMovieClipを作成します。 + * Create a MovieClip for Root. + * + * @param {number} [width=240] + * @param {number} [height=240] + * @param {number} [fps=60] + * @param {object} [options=null] + * @return {Sprite} + * @method + * @protected + */ +export const execute = ( + width: number = 240, + height: number = 240, + fps: number = 60, + options: IPlayerOptions | null = null +): Sprite => { + + if ($boot) { + return stage.getChildAt(0) as Sprite; + } + $boot = true; + + // setup + stage.stageWidth = width | 0; + stage.stageHeight = height | 0; + stage.frameRate = $clamp(fps, 1, 60, 60); + + // boot player + playerBootUseCase(options); + + const root = stage.addChild(new Sprite()); + + // ready complete + playerReadyCompleteUseCase(); + + // remove loading + playerRemoveLoadingElementService(); + + // append canvas + playerAppendElementService(); + + // set position + canvasSetPositionService(); + + return root; +}; \ No newline at end of file diff --git a/packages/core/src/Next2D/usecase/LoadUseCase.ts b/packages/core/src/Next2D/usecase/LoadUseCase.ts new file mode 100644 index 00000000..19ad9a7e --- /dev/null +++ b/packages/core/src/Next2D/usecase/LoadUseCase.ts @@ -0,0 +1,89 @@ +import type { IPlayerOptions } from "../../interface/IPlayerOptions"; +import type { MovieClip } from "@next2d/display"; +import { $clamp } from "../../CoreUtil"; +import { URLRequest } from "@next2d/net"; +import { IOErrorEvent } from "@next2d/events"; +import { execute as playerResizeEventUseCase } from "../../Player/usecase/PlayerResizeEventUseCase"; +import { execute as playerRemoveLoadingElementService } from "../../Player/service/PlayerRemoveLoadingElementService"; +import { execute as playerAppendCanvasElementService } from "../../Player/service/PlayerAppendElementService"; +import { execute as playerReadyCompleteUseCase } from "../../Player/usecase/PlayerReadyCompleteUseCase"; +import { execute as playerBootUseCase } from "../../Player/usecase/PlayerBootUseCase"; +import { execute as canvasSetPositionService } from "../../Canvas/service/CanvasSetPositionService"; +import { + Loader, + stage +} from "@next2d/display"; + +/** + * @description 指定のURLからJSONファイルを読み込みます。 + * Reads a JSON file from the specified URL. + * + * @param {string} url + * @param {object} [options=null] + * @return {Promise} + * @method + * @protected + */ +export const execute = async (url: string, options: IPlayerOptions | null = null): Promise => +{ + if (url === "develop") { + const path: string = location + .search + .slice(1) + .split("&")[0]; + + if (!path) { + return ; + } + url = `${location.origin}/${path}`; + } + + if (!url) { + return ; + } + + if (url.charAt(1) === "/") { + url = url.slice(1); + } + + // player + playerBootUseCase(options); + + const loader = new Loader(); + + const loaderInfo = loader.contentLoaderInfo; + loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, (event: IOErrorEvent): void => + { + alert("Error: " + event.text); + }); + + await loader.load(new URLRequest(url)); + + if (!loaderInfo.data) { + return ; + } + + // update properties + const stageData = loaderInfo.data.stage; + stage.stageWidth = stageData.width; + stage.stageHeight = stageData.height; + stage.frameRate = $clamp(stageData.fps, 1, 60, 60); + stage.backgroundColor = options && options.bgColor ? options.bgColor : stageData.bgColor; + + stage.addChild(loaderInfo.content as MovieClip); + + // resize + playerResizeEventUseCase(); + + // // ready complete + playerReadyCompleteUseCase(); + + // // remove loading + playerRemoveLoadingElementService(); + + // // append canvas + playerAppendCanvasElementService(); + + // // set position + canvasSetPositionService(); +}; \ No newline at end of file diff --git a/packages/core/src/Player.ts b/packages/core/src/Player.ts index 916ce7bc..6a5bb3f2 100644 --- a/packages/core/src/Player.ts +++ b/packages/core/src/Player.ts @@ -1,2669 +1,281 @@ -import { - Stage, - MovieClip, - TextField -} from "@next2d/display"; -import { - Event as Next2DEvent, - MouseEvent as Next2DMouseEvent, - EventPhase -} from "@next2d/events"; -import { - Video, - Sound, - SoundMixer -} from "@next2d/media"; -import { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - StageQualityImpl, - PlayerOptionsImpl, - PlayerHitObjectImpl, - PlayerModeImpl, - AttachmentImpl, - EventListenerImpl, - DisplayObjectImpl, - EventDispatcherImpl, - ParentImpl, - RGBAImpl, - PropertyMessageMapImpl -} from "@next2d/interface"; -import { - Point, - Rectangle -} from "@next2d/geom"; -import { - $document, - $window, - $rendererWorker, - $PREFIX, - $audioContext, - $TOUCH_START, - $TOUCH_MOVE, - $TOUCH_END, - $MOUSE_DOWN, - $MOUSE_MOVE, - $MOUSE_UP, - $MOUSE_WHEEL, - $DOUBLE_CLICK, - $MOUSE_LEAVE, - $loadAudioData, - $MATRIX_HIT_ARRAY_IDENTITY, - $hitContext, - $isTouch, - $dropTarget, - $dragRules, - $isSafari, - $getEvent, - $setEvent, - $setEventType, - $setCurrentLoaderInfo, - $getEventType, - $getRenderBufferArray, - $getRenderMessageObject, - $poolRenderMessageObject, - $textArea -} from "@next2d/util"; -import { - $Math, - $performance, - $COLOR_ARRAY_IDENTITY, - $doUpdated, - $isUpdated, - $getArray, - $getFloat32Array6, - $getMap, - $uintToRGBA, - $toColorInt, - $requestAnimationFrame, - $cancelAnimationFrame, - $poolArray, - $clamp, - $devicePixelRatio, - $setDevicePixelRatio, - $cacheStore, - CacheStore -} from "@next2d/share"; +import type { IPlayerOptions } from "./interface/IPlayerOptions"; +import { $cacheStore } from "@next2d/cache"; +import { stage } from "@next2d/display"; +import { $rendererWorker } from "./RendererWorker"; +import { execute as playerResizeEventService } from "./Player/usecase/PlayerResizeEventUseCase"; +import { execute as playerPlayUseCase } from "./Player/usecase/PlayerPlayUseCase"; +import { execute as playerStopService } from "./Player/service/PlayerStopService"; +import { execute as playerRegisterEventUseCase } from "./Player/usecase/PlayerRegisterEventUseCase"; /** - * 描画のイベントや設定やコントロールの管理クラス - * Management classes for drawing events, settings and controls + * @description Next2Dの描画、イベント、設定、コントロールの管理クラスです。 + * This class manages Next2D drawings, events, settings, and controls. * * @class + * @public */ -export class Player +class Player { - public _$stopFlag: boolean; - public _$actionOffset: number; - public _$actions: MovieClip[]; - public _$loaders: EventDispatcherImpl[]; - public _$sounds: Map; - public _$loadStatus: number; - public _$width: number; - public _$height: number; - public _$scale: number; - public _$state: "up" | "down"; - public _$attachment: AttachmentImpl | null; - public _$textField: TextField | null; - public readonly _$videos: Video[]; - public readonly _$sources: Sound[]; - private _$mode: PlayerModeImpl; - private _$context: CanvasToWebGLContext|null; - private _$rollOverObject: DisplayObjectImpl | null; - private _$mouseOverTarget: DisplayObjectImpl | null; - private _$startTime: number; - private _$fps: number; - private _$baseWidth: number; - private _$baseHeight: number; - private _$tx: number; - private _$ty: number; - private _$hitTestStart: boolean; - private _$stageX: number; - private _$stageY: number; - private _$optionWidth: number; - private _$optionHeight: number; - private _$tagId: string; - private _$bgColor: string; - private _$base: string; - private _$fullScreen: boolean; - private _$timerId: number; - private _$loadId: number; - private _$deltaX: number; - private _$deltaY: number; - private _$clickTarget: ParentImpl | null; - private _$actionProcess: boolean; - private readonly _$stage: Stage; - private readonly _$hitObject: PlayerHitObjectImpl; - private readonly _$ratio: number; - private readonly _$matrix: Float32Array; - private readonly _$broadcastEvents: Map; - private readonly _$quality: StageQualityImpl; - private readonly _$canvas: HTMLCanvasElement; - - /** - * @constructor - * @public - */ - constructor () - { - // init - $setDevicePixelRatio(window.devicePixelRatio); - - /** - * @type {Stage} - * @private - */ - this._$stage = new Stage(); - this._$stage._$player = this; - - /** - * @type {string} - * @private - */ - this._$mode = "loader"; - - /** - * @type {number} - * @private - */ - this._$actionOffset = 0; - - /** - * @type {array} - * @private - */ - this._$actions = $getArray(); - - /** - * @type {array} - * @public - */ - this._$loaders = $getArray(); - - /** - * @type {Map} - * @private - */ - this._$sounds = $getMap(); - - /** - * @type {object} - * @private - */ - this._$hitObject = { - "x": 0, - "y": 0, - "pointer": "", - "hit": null - }; - - /** - * @type {DisplayObject} - * @default null - * @private - */ - this._$rollOverObject = null; - - /** - * @type {DisplayObject} - * @default null - * @private - */ - this._$mouseOverTarget = null; - - /** - * @type {number} - * @private - */ - this._$ratio = $devicePixelRatio; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$stopFlag = true; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$startTime = 0; - - /** - * @type {number} - * @default 16 - * @private - */ - this._$fps = 16; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$loadStatus = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$width = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$height = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$baseWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$baseHeight = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$scale = 1; - - /** - * @type {Float32Array} - * @private - */ - this._$matrix = $getFloat32Array6(1, 0, 0, 1, 0, 0); // fixed size 6 - - /** - * @type {number} - * @default 0 - * @private - */ - this._$tx = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$ty = 0; - - /** - * @type {string} - * @default up - * @private - */ - this._$state = "up"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$hitTestStart = false; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$stageX = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$stageY = -1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$deltaX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$deltaY = 0; - - /** - * @type {Map} - * @private - */ - this._$broadcastEvents = $getMap(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$optionWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$optionHeight = 0; - - /** - * @type {string} - * @default "" - * @private - */ - this._$tagId = ""; - - /** - * @type {string} - * @default "transparent" - * @private - */ - this._$bgColor = "transparent"; - - /** - * @type {string} - * @default "" - * @private - */ - this._$base = ""; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$fullScreen = false; - - /** - * @type {string} - * @default high - * @private - */ - this._$quality = "high"; - - /** - * @type {array} - * @private - */ - this._$sources = $getArray(); - - /** - * @type {array} - * @private - */ - this._$videos = $getArray(); - - /** - * @type {TextField} - * @default null - * @private - */ - this._$textField = null; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$timerId = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$loadId = -1; - - /** - * @type {CanvasToWebGLContext} - * @default null - * @private - */ - this._$context = null; - - /** - * @type {AttachmentImpl} - * @default null - * @private - */ - this._$attachment = null; - - /** - * @type {DisplayObject} - * @default null - * @private - */ - this._$clickTarget = null; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$actionProcess = false; - - /** - * @type {HTMLCanvasElement} - * @private - */ - this._$canvas = $document.createElement("canvas"); - } - /** - * @return {number} - * @default 1 - * @const - * @static + * @description devicePixelRatioを含んだcanvasの描画領域の幅 + * The width of the drawing area of the canvas including devicePixelRatio + * + * @type {number} + * @default 0 + * @private */ - static get LOAD_START (): number - { - return 1; - } + public rendererWidth: number; /** - * @return {number} - * @default 2 - * @const - * @static + * @description devicePixelRatioを含んだcanvasの描画領域の高さ + * The height of the drawing area of the canvas including devicePixelRatio + * + * @type {number} + * @default 0 + * @private */ - static get LOAD_END (): number - { - return 2; - } + public rendererHeight: number; /** - * @return {CacheStore} - * @readonly + * @description メインのdiv elementの幅 + * Width of the main div element + * + * @type {number} + * @default 0 * @public */ - get cacheStore (): CacheStore - { - return $cacheStore; - } + public screenWidth: number; /** - * @type {HTMLCanvasElement} - * @readonly + * @description メインのdiv elementの高さ + * Height of the main div element + * + * @type {number} + * @default 0 * @public */ - get canvas (): HTMLCanvasElement - { - return this._$canvas; - } + public screenHeight: number; /** - * @return {Map} - * @readonly - * @public + * @description devicePixelRatioを含んだcanvasの描画領域の拡大率 + * The magnification of the drawing area of the canvas including devicePixelRatio + * + * @type {number} + * @default 1 + * @private */ - get broadcastEvents (): Map - { - return this._$broadcastEvents; - } + public rendererScale: number; /** - * @member {CanvasToWebGLContext|null} - * @default null + * @description optionで指定された描画領域の固定幅、optionで指定されない場合は0 + * The fixed width of the drawing area specified by the option, 0 if not specified by the option + * + * @type {number} + * @default 0 * @public */ - get context (): CanvasToWebGLContext|null - { - return this._$context; - } - set context (context: CanvasToWebGLContext|null) - { - this._$context = context; - } + public fixedWidth: number; /** - * @member {string} - * @default "" + * @description optionで指定された描画領域の固定高さ、optionで指定されない場合は0 + * The fixed height of the drawing area specified by the option, 0 if not specified by the option + * + * @type {number} + * @default 0 * @public */ - get base (): string - { - return this._$base; - } - set base (base: string) - { - if (base.indexOf("//") === -1) { - - const urls = base.split("/"); - if (urls[0] === "" || urls[0] === ".") { - urls.shift(); - } - urls.pop(); - - this._$base = `${location.origin}/`; - if (urls.length) { - this._$base += `${urls.join("/")}/`; - } - - } else { - - if (base.indexOf("?") === -1) { - - this._$base = base.slice(-1) === "/" ? base : `${base}/`; - - } else { - - const path = base.split("?")[0]; - this._$base = path.slice(-1) === "/" ? path : `${path}/`; - - } - - } - } + public fixedHeight: number; /** - * @return {Stage} - * @readonly - * @public + * @description Playerの停止フラグ + * Player stop flag + * + * @type {boolean} + * @default true + * @private */ - get stage (): Stage - { - return this._$stage; - } + public stopFlag: boolean; /** - * @member {number} - * @readonly - * @public + * @description Playerの描画開始時間 + * Player drawing start time + * + * @type {number} + * @default 0 + * @private */ - get x (): number - { - return this._$tx; - } + public startTime: number; /** - * @member {number} - * @readonly - * @public + * @description PlayerのFPS + * Player FPS + * + * @type {number} + * @default 16 + * @private */ - get y (): number - { - return this._$ty; - } + public fps: number; /** - * @member {number} - * @readonly - * @public + * @description optionで指定されたcanvasのID、optionで指定されない場合は空文字 + * The ID of the canvas specified by the option, an empty string if not specified by the option + * + * @type {string} + * @default "" + * @private */ - get scaleX (): number - { - return this._$matrix[0]; - } + public tagId: string; /** - * @member {number} - * @readonly - * @public + * @description フルスクリーンモードの設定 + * Full screen mode setting + * + * @type {boolean} + * @default false + * @private */ - get scaleY (): number - { - return this._$matrix[3]; - } + private _$fullScreen: boolean; /** - * @member {number} - * @readonly - * @public + * @description Playerの描画処理関数のタイマーID + * Timer ID of the drawing process function of Player + * + * @type {number} + * @default -1 + * @private */ - get tx (): number - { - return this._$matrix[4] / this._$scale / $devicePixelRatio; - } + public timerId: number; /** - * @member {number} - * @readonly + * @description マウスの状態 + * Mouse state + * + * @type {"up" | "down"} + * @default "up" * @public */ - get ty (): number - { - return this._$matrix[5] / this._$scale / $devicePixelRatio; - } + public mouseState: "up" | "down"; /** - * @member {string} + * @constructor * @public */ - get mode (): PlayerModeImpl - { - return this._$mode; - } - set mode (mode: PlayerModeImpl) + constructor () { - this._$mode = mode; - } + this.rendererWidth = 0; + this.rendererHeight = 0; + this.rendererScale = 1; + this.screenWidth = 0; + this.screenHeight = 0; + + this.stopFlag = true; + this.startTime = 0; + this.fps = 16; + this.timerId = -1; + this.mouseState = "up"; + + // options + this.fixedWidth = 0; + this.fixedHeight = 0; + this.tagId = ""; + this._$fullScreen = false; - /** - * @return {string} - * @readonly - * @public - */ - get contentElementId (): string - { - return $PREFIX; + playerRegisterEventUseCase(); } /** - * @member {number} + * @description フルスクリーンモードの設定 + * Full screen mode setting + * + * @member {boolean} + * @default false * @public */ - get width (): number + get fullScreen (): boolean { - return this._$baseWidth; + return this._$fullScreen; } - set width (width: number) + set fullScreen (full_screen: boolean) { - this._$baseWidth = width | 0; - } + if (this._$fullScreen === full_screen) { + return ; + } - /** - * @member {number} - * @public - */ - get height (): number - { - return this._$baseHeight; - } - set height (height: number) - { - this._$baseHeight = height | 0; - } + this._$fullScreen = full_screen; - /** - * @member {string} - * @public - */ - get bgColor (): string - { - return this._$bgColor; - } - set bgColor (bg_color: string) - { - this._$bgColor = `${bg_color}`; + // display resize + playerResizeEventService(); } /** + * @description Playerの描画を開始します。 + * Start drawing Player. + * * @return {void} * @method * @public */ play (): void { - if (this._$stopFlag) { - - this._$stopFlag = false; - - if (this._$timerId > -1) { - $cancelAnimationFrame(this._$timerId); - } - - this._$startTime = $performance.now(); - - const frameRate: number = this._$stage._$frameRate; - this._$fps = 1000 / frameRate | 0; - - this._$timerId = $requestAnimationFrame((timestamp: number) => - { - this._$run(timestamp); - }); - } + playerPlayUseCase(); } /** + * @description Playerの描画を停止します。 + * Stop drawing Player. + * * @return {void} * @method * @public */ stop (): void { - if (this._$timerId > -1) { - $cancelAnimationFrame(this._$timerId); - } - - this._$stopFlag = true; - this._$timerId = -1; - - SoundMixer.stopAll(); - $cacheStore.reset(); - - if ($rendererWorker) { - $rendererWorker.postMessage({ - "command": "stop" - }); - } + playerStopService(); } /** - * @param {string} id + * @description Playerの描画キャッシュを全て初期化 + * Initialize all drawing caches of Player + * * @return {void} * @method * @public */ - removeCache (id: string): void + cacheClear (): void { - $cacheStore.removeCache(id); - if ($rendererWorker) { - $rendererWorker.postMessage({ - "command": "removeCache", - "id": id - }); - } + $cacheStore.reset(); + $rendererWorker.postMessage({ + "command": "cacheClear" + }); } /** + * @description Playerのオプション設定を変更 + * Change the Player option settings + * * @param {object} [options=null] * @return {void} * @public */ - setOptions (options: PlayerOptionsImpl | null = null): void - { - if (options) { - this._$optionWidth = options.width || this._$optionWidth; - this._$optionHeight = options.height || this._$optionHeight; - this._$tagId = options.tagId || this._$tagId; - this.base = options.base || this._$base; - this._$bgColor = options.bgColor || this._$bgColor; - this._$fullScreen = !!options.fullScreen; - } - } - - /** - * @description NoCode Toolからのアクセスのみ - * Access from NoCode Tool only - * - * @param {MouseEvent} [event = null] - * @return {void} - * @method - * @private - */ - _$loadWebAudio (event: MouseEvent | null = null): void - { - if (event) { - // @ts-ignore - this._$canvas.removeEventListener($MOUSE_UP, this._$loadWebAudio); - } - - if (!$audioContext) { - $loadAudioData(); - } - } - - /** - * @return {void} - * @method - * @private - */ - _$updateLoadStatus (): void + setOptions (options: IPlayerOptions | null = null): void { - if (this._$loadStatus === Player.LOAD_END) { - if (this._$loadId > -1) { - $cancelAnimationFrame(this._$loadId); - } - - this._$loadId = -1; - this._$loaded(); + if (!options) { return ; } - this._$loadId = $requestAnimationFrame(() => - { - this._$updateLoadStatus(); - }); - } - - /** - * @return {void} - * @method - * @private - */ - _$loaded (): void - { - const element: HTMLElement | null = $document - .getElementById(this.contentElementId); - - if (element) { - - // background color - this._$setBackgroundColor(this._$bgColor); - - // DOM - this._$deleteNode(); - - // append canvas - element.appendChild(this._$canvas); - element.appendChild($textArea); - - // stage init action - this._$stage._$prepareActions(); - - // constructed event - if (this._$broadcastEvents.has(Next2DEvent.FRAME_CONSTRUCTED)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.FRAME_CONSTRUCTED)); - } - - // frame1 action - this._$doAction(); - - // exit event - if (this._$broadcastEvents.has(Next2DEvent.EXIT_FRAME)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.EXIT_FRAME)); - } - - // loader events - const length: number = this._$loaders.length; - for (let idx: number = 0; idx < length; ++idx) { - - const loader: EventDispatcherImpl = this._$loaders.shift(); - - // init event - if (loader.hasEventListener(Next2DEvent.INIT)) { - loader.dispatchEvent(new Next2DEvent(Next2DEvent.INIT)); - } - - // complete event - if (loader.hasEventListener(Next2DEvent.COMPLETE)) { - loader.dispatchEvent(new Next2DEvent(Next2DEvent.COMPLETE)); - } - } - - // activate event - if (this._$broadcastEvents.has(Next2DEvent.ACTIVATE)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.ACTIVATE)); - } - - // frame action - this._$doAction(); - - // render - this._$draw(); - - // start - this.play(); - } - - } - - /** - * @return {void} - * @method - * @private - */ - _$initialize (): void - { - const contentElementId: string = this.contentElementId; - if (!this._$tagId) { - - $document - .body - .insertAdjacentHTML( - "beforeend", `
` - ); - - } else { - - const container: HTMLElement | null = $document.getElementById(this._$tagId); - if (!container) { - alert("Not Found Tag ID:" + this._$tagId); - return ; - } - - const div: HTMLElement | null = $document.getElementById(contentElementId); - if (!div) { - - const element: HTMLDivElement = $document.createElement("div"); - element.id = contentElementId; - element.tabIndex = -1; - container.appendChild(element); - - } else { - - this._$deleteNode(); - - } - - } - - const element: HTMLElement | null = $document.getElementById(contentElementId); - if (!element) { - throw new Error("the content element is null."); - } - - const parent: HTMLElement | null = element.parentElement; - if (parent) { - - this._$initStyle(element); - this._$buildWait(); - - const width: number = this._$optionWidth - ? this._$optionWidth - : parent.tagName === "BODY" - ? $window.innerWidth - : parent.offsetWidth; + this.fixedWidth = options.width || this.fixedWidth; + this.fixedHeight = options.height || this.fixedHeight; + this.tagId = options.tagId || this.tagId; + this._$fullScreen = !!options.fullScreen; - const height: number = this._$optionHeight - ? this._$optionHeight - : parent.tagName === "BODY" - ? $window.innerHeight - : parent.offsetHeight; - - // set center - if (this._$mode === "loader" && width && height) { - this._$baseWidth = width; - this._$baseHeight = height; - this._$resize(); - } - } - - if (this._$mode === "loader") { - this._$loadStatus = Player.LOAD_START; - this._$updateLoadStatus(); - } else { - this._$resize(); - this._$loaded(); + if (options.bgColor) { + stage.backgroundColor = options.bgColor; } } +} - /** - * @param {object} element - * @returns {void} - * @method - * @private - */ - _$initStyle (element: HTMLElement): void - { - const style: CSSStyleDeclaration = element.style; - - // set css - style.position = "relative"; - style.top = "0"; - style.left = "0"; - style.backgroundColor = "transparent"; - style.overflow = "hidden"; - style.padding = "0"; - style.margin = "0"; - style.userSelect = "none"; - style.outline = "none"; - - const width: number = this._$optionWidth; - const height: number = this._$optionHeight; - - const parent: HTMLElement | null = element.parentElement; - if (!parent) { - throw new Error("the parentElement is null."); - } - - if (parent.tagName === "BODY") { - style.width = width ? `${width}px` : `${window.innerWidth}px`; - style.height = height ? `${height}px` : `${window.innerHeight}px`; - return ; - } - - style.width = width ? `${width}px` : `${parent.offsetWidth}px`; - style.height = height ? `${height}px` : `${parent.offsetHeight}px`; - } - - /** - * @return {void} - * @method - * @private - */ - _$buildWait (): void - { - const element: HTMLElement | null = $document - .getElementById(this.contentElementId); - - if (element) { - - const loadingId: string = `${this.contentElementId}_loading`; - - element.innerHTML = ``; - - const div: HTMLDivElement = $document.createElement("div"); - div.id = loadingId; - - element.appendChild(div); - } - } - - /** - * @returns {void} - * @method - * @private - */ - _$deleteNode (): void - { - const element: HTMLElement | null = $document.getElementById(this.contentElementId); - if (element) { - while (element.childNodes.length) { - element.removeChild(element.childNodes[0]); - } - } - } - - /** - * @return {void} - * @private - */ - _$initializeCanvas (): void - { - // main canvas - this._$canvas.width = 1; - this._$canvas.height = 1; - - if ($rendererWorker) { - - const offscreenCanvas: OffscreenCanvas = this - ._$canvas - .transferControlToOffscreen(); - - const buffer: Float32Array = $getRenderBufferArray(); - - let index: number = 0; - buffer[index++] = this._$stage._$instanceId; - buffer[index++] = +$isSafari; - buffer[index++] = $devicePixelRatio; - buffer[index++] = this._$getSamples(); - - const options: ArrayBuffer[] = $getArray(offscreenCanvas, buffer.buffer); - $rendererWorker.postMessage({ - "command": "initialize", - "canvas": offscreenCanvas, - "buffer": buffer - }, options); - - $poolArray(options); - - } else { - // create gl context - const gl: WebGL2RenderingContext | null = this._$canvas.getContext("webgl2", { - "stencil": true, - "premultipliedAlpha": true, - "antialias": false, - "depth": false, - "preserveDrawingBuffer": true - }); - - if (gl) { - - this._$context = new CanvasToWebGLContext( - gl, this._$getSamples() - ); - - $cacheStore.context = this._$context; - - } else { - alert("WebGL setting is off. Please turn the setting on."); - } - - } - - /** - * @return {void} - * @method - * @private - */ - const loadWebAudio = (): void => - { - this._$canvas.removeEventListener($MOUSE_UP, loadWebAudio); - this._$canvas.removeEventListener($TOUCH_END, loadWebAudio); - - if (!$audioContext) { - $loadAudioData(); - - for (let idx = 0; idx < this._$videos.length; ++idx) { - const video: Video = this._$videos[idx]; - if (!video._$video) { - continue; - } - - video._$video.muted = false; - } - } - }; - - // @ts-ignore - this._$canvas.addEventListener($TOUCH_END, loadWebAudio); - - // @ts-ignore - this._$canvas.addEventListener($MOUSE_UP, loadWebAudio); - - // touch event - this._$canvas.addEventListener($TOUCH_START, (event: TouchEvent) => - { - $setEvent(event); - $setEventType($TOUCH_START); - - // start position - this._$hitTest(); - }); - - this._$canvas.addEventListener($TOUCH_MOVE, (event: TouchEvent) => - { - $setEvent(event); - $setEventType($TOUCH_MOVE); - this._$hitTest(); - }); - - this._$canvas.addEventListener($TOUCH_END, (event: TouchEvent) => - { - $setEvent(event); - $setEventType($TOUCH_END); - this._$hitTest(); - }); - - // mouse wheel - this._$canvas.addEventListener($TOUCH_MOVE, (event: TouchEvent) => - { - $setEvent(event); - $setEventType($TOUCH_MOVE); - - this._$hitTest(); - }, { "passive": false }); - - // mouse event - this._$canvas.addEventListener($MOUSE_DOWN, (event: MouseEvent) => - { - $setEvent(event); - $setEventType($MOUSE_DOWN); - - if (!event.button) { - this._$hitTest(); - } - }); - - this._$canvas.addEventListener($DOUBLE_CLICK, (event: MouseEvent) => - { - $setEvent(event); - $setEventType($DOUBLE_CLICK); - - if (!event.button) { - this._$hitTest(); - } - }); - - this._$canvas.addEventListener($MOUSE_LEAVE, (event: MouseEvent) => - { - $setEvent(event); - $setEventType($MOUSE_LEAVE); - - this._$hitTest(); - - $setEvent(null); - this._$stageX = -1; - this._$stageY = -1; - }); - - this._$canvas.addEventListener($MOUSE_UP, (event: MouseEvent) => - { - $setEvent(event); - $setEventType($MOUSE_UP); - - if (!event.button) { - this._$hitTest(); - } - }); - - this._$canvas.addEventListener($MOUSE_MOVE, (event: MouseEvent) => - { - $setEvent(event); - $setEventType($MOUSE_MOVE); - - this._$hitTest(); - }); - - // mouse wheel - this._$canvas.addEventListener($MOUSE_WHEEL, (event: MouseEvent) => - { - if (!event.defaultPrevented) { - - $setEvent(event); - $setEventType($MOUSE_WHEEL); - - this._$hitTest(); - } - }, { "passive": false }); - - // set css - let style: string = ""; - style += "position: absolute;"; - style += "top: 0;"; - style += "left: 0;"; - style += "-webkit-tap-highlight-color: rgba(0,0,0,0);"; - style += "backface-visibility: hidden;"; - style += "transform-origin: 0 0;"; - - if ($devicePixelRatio !== 1) { - style += `transform: scale(${1 / $devicePixelRatio});`; - } - - this._$canvas.setAttribute("style", style); - } - - /** - * @return {void} - * @method - * @private - */ - _$resize (): void - { - const div: HTMLElement | null = $document - .getElementById(this.contentElementId); - - if (div) { - - const parent: HTMLElement | null = div.parentElement; - if (!parent) { - throw new Error("the parentElement is null."); - } - - const innerWidth: number = this._$optionWidth - ? this._$optionWidth - : parent.tagName === "BODY" - ? $window.innerWidth - : parent.offsetWidth - ? parent.offsetWidth - : parseFloat(parent.style.width); - - const innerHeight: number = this._$optionHeight - ? this._$optionHeight - : parent.tagName === "BODY" - ? $window.innerHeight - : parent.offsetHeight - ? parent.offsetHeight - : parseFloat(parent.style.height); - - const screenWidth: number = parent.tagName === "BODY" - ? $window.innerWidth - : parent.offsetWidth; - - const scale: number = $Math.min( - innerWidth / this._$baseWidth, - innerHeight / this._$baseHeight - ); - - let width: number = this._$fullScreen - ? innerWidth - : this._$baseWidth * scale | 0; - - let height: number = this._$fullScreen - ? innerHeight - : this._$baseHeight * scale | 0; - - // div - const style: CSSStyleDeclaration = div.style; - style.width = `${width}px`; - style.height = `${height}px`; - style.top = "0"; - style.left = this._$fullScreen - ? "0" - : `${screenWidth / 2 - width / 2}px`; - - width *= $devicePixelRatio; - height *= $devicePixelRatio; - - // no resize - if (this._$width === width && this._$height === height) { - return ; - } - - // cache reset - this._$stage._$doChanged(); - $cacheStore.reset(); - - // params - this._$scale = scale; - this._$width = width; - this._$height = height; - - const mScale: number = this._$scale * this._$ratio; - this._$matrix[0] = mScale; - this._$matrix[3] = mScale; - - if (this._$fullScreen) { - - this._$tx = (width - - this._$baseWidth - * scale - * $devicePixelRatio) / 2; - - this._$ty = (height - - this._$baseHeight - * scale - * $devicePixelRatio) / 2; - - this._$matrix[4] = this._$tx; - this._$matrix[5] = this._$ty; - - } - - // main canvas resize - this._$resizeCanvas(width, height, mScale, this._$tx, this._$ty); - - if (this._$ratio > 1 && $devicePixelRatio > 1) { - this._$canvas.style.transform = `scale(${1 / this._$ratio})`; - } - - if (div.children.length > 1) { - div.children[1].dispatchEvent( - new Event(`${$PREFIX}_blur`) - ); - } - } - } - - /** - * @description 表示用のcanvasを更新 - * Update canvas for display - * - * @param {string} [background_color=transparent] - * @return {void} - * @method - * @public - */ - _$setBackgroundColor (background_color: string = "transparent"): void - { - if ($rendererWorker) { - - const buffer: Float32Array = $getRenderBufferArray(); - - buffer[0] = background_color === "transparent" - ? -1 - : $toColorInt(background_color); - - const message: PropertyMessageMapImpl = $getRenderMessageObject(); - message.command = "setBackgroundColor"; - message.buffer = buffer; - - const options: ArrayBuffer[] = $getArray(buffer.buffer); - - $rendererWorker.postMessage(message, options); - - $poolRenderMessageObject(message); - $poolArray(options); - - } else { - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { - return ; - } - - if (background_color === "transparent") { - - context._$setColor(0, 0, 0, 0); - - } else { - - const color: RGBAImpl = $uintToRGBA( - $toColorInt(background_color) - ); - - context._$setColor( - color.R / 255, - color.G / 255, - color.B / 255, - 1 - ); - - } - - } - } - - /** - * @param {number} width - * @param {number} height - * @param {number} scale - * @param {number} [tx = 0] - * @param {number} [ty = 0] - * @return {void} - * @method - * @private - */ - _$resizeCanvas ( - width: number, height: number, - scale: number, tx: number = 0, ty: number = 0 - ): void { - - if ($rendererWorker) { - - const buffer: Float32Array = $getRenderBufferArray(); - - let index: number = 0; - buffer[index++] = width; - buffer[index++] = height; - buffer[index++] = scale; - buffer[index++] = tx; - buffer[index++] = ty; - - const message: PropertyMessageMapImpl = $getRenderMessageObject(); - const options: ArrayBuffer[] = $getArray(buffer.buffer); - - message.command = "resize"; - message.buffer = buffer; - $rendererWorker.postMessage(message, options); - - // reset - $poolRenderMessageObject(message); - $poolArray(options); - - } else { - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { // unit test - return ; - } - - context.clearInstacedArray(); - - this._$canvas.width = width; - this._$canvas.height = height; - - context._$gl.viewport(0, 0, width, height); - - const manager: FrameBufferManager = context.frameBuffer; - if (this._$attachment) { - manager.unbind(); - manager.releaseAttachment(this._$attachment, true); - } - - this._$attachment = manager - .createCacheAttachment(width, height, true); - - // update cache max size - context.setMaxSize(width, height); - - context._$bind(this._$attachment); - } - } - - /** - * @return {number} - * @method - * @private - */ - _$getSamples (): number - { - switch (this._$quality) { - - case "high": - return 4; - - case "medium": - return 2; - - default: - return 0; - - } - } - - /** - * @param {Event} event - * @return {boolean} - * @method - * @private - */ - _$dispatchEvent (event: Next2DEvent): boolean - { - if (this._$broadcastEvents.size - && this._$broadcastEvents.has(event.type) - ) { - - // clone - const events: EventListenerImpl[] = this - ._$broadcastEvents - .get(event.type) - .slice(0); - - // start target - event.eventPhase = EventPhase.AT_TARGET; - - for (let idx: number = 0; idx < events.length; ++idx) { - - const obj: EventListenerImpl = events[idx]; - - // event execute - event.currentTarget = obj.target; - - event.listener = obj.listener; - obj.listener.call(null, event); - - if (event._$stopImmediatePropagation) { - break; - } - - } - - $poolArray(events); - - return true; - - } - - return false; - } - - /** - * @param {number} timestamp - * @return {void} - * @method - * @private - */ - _$run (timestamp: number = 0): void - { - if (this._$stopFlag) { - return ; - } - - // delay action - this._$doAction(); - - const delta: number = timestamp - this._$startTime; - if (delta > this._$fps) { - - // update - this._$startTime = timestamp - delta % this._$fps; - - // execute - this._$action(); - - // start sound - if (this._$sounds.size) { - for (const movieClip of this._$sounds.values()) { - movieClip._$soundPlay(); - } - this._$sounds.clear(); - } - - // draw - this._$draw(); - - // draw event - if (!$isTouch - && !this._$hitTestStart - && this._$state === "up" - && this._$stageX > -1 - && this._$stageY > -1 - && $getEvent() - ) { - this._$pointerCheck(); - } - - } else { - - if (this._$videos.length && !$rendererWorker) { - this._$draw(); - } - - } - - // next frame - this._$timerId = $requestAnimationFrame((timestamp: number) => - { - this._$run(timestamp); - }); - } - - /** - * @return {void} - * @method - * @private - */ - _$pointerCheck (): void - { - const stageX: number = this._$stageX; - const stageY: number = this._$stageY; - - // setup - this._$hitObject.x = stageX; - this._$hitObject.y = stageY; - this._$hitObject.pointer = ""; - this._$hitObject.hit = null; - - // reset - $hitContext.setTransform(1, 0, 0, 1, 0, 0); - $hitContext.beginPath(); - - // hit test - $MATRIX_HIT_ARRAY_IDENTITY[4] = this._$tx / this._$scale / $devicePixelRatio; - $MATRIX_HIT_ARRAY_IDENTITY[5] = this._$ty / this._$scale / $devicePixelRatio; - this._$stage._$mouseHit( - $hitContext, $MATRIX_HIT_ARRAY_IDENTITY, - this._$hitObject, true - ); - - // change state - // params - let instance: DisplayObjectImpl = null; - let target: DisplayObjectImpl = null; - let canPointerText: boolean = false; - let canPointer: boolean = false; - - // execute - if (this._$hitObject.hit) { - - instance = this._$hitObject.hit; - - // (1) mouseOut - if (this._$mouseOverTarget - && this._$mouseOverTarget !== instance - ) { - - const outInstance: DisplayObjectImpl = this._$mouseOverTarget; - - if (outInstance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - outInstance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OUT, true, false - )); - } - - } - - // rollOut and rollOver - if (this._$rollOverObject !== instance) { - - let hitParent = null; - if (this._$rollOverObject) { - - // (2) prev object rollOut - target = this._$rollOverObject; - - if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - // rollOver flag instance - hitParent = target._$parent; - while (hitParent && hitParent._$root !== hitParent) { - - if (hitParent === instance) { - break; - } - - if (hitParent._$mouseEnabled - && hitParent._$outCheck(stageX, stageY) - ) { - - let isUpperLayer = false; - let check = instance; - while (check && check._$root !== check) { - - if (check !== hitParent) { - check = check._$parent; - continue; - } - - isUpperLayer = true; - - break; - } - - if (!isUpperLayer && hitParent._$parent === instance._$parent - && hitParent._$index > instance._$index - ) { - isUpperLayer = true; - } - - if (isUpperLayer) { - break; - } - - } - - if (hitParent.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - hitParent.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - hitParent = hitParent._$parent; - - } - } - - // (3) current object rollOver - target = instance; - for (;;) { - - if (target.willTrigger(Next2DMouseEvent.ROLL_OVER)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OVER, false, false - )); - } - - target = target._$parent; - if (!target || target === hitParent - || target.stage === target - ) { - break; - } - - } - - } - - this._$rollOverObject = instance; - - // (4) mouseOver - switch (true) { - - case this._$mouseOverTarget === null: - case this._$mouseOverTarget !== instance: - - if (instance && instance.willTrigger(Next2DMouseEvent.MOUSE_OVER)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OVER, true, false - )); - } - - // set target - this._$mouseOverTarget = instance; - break; - - } - - // click reset - if (this._$state === "up") { - this._$clickTarget = null; - } - - // PC - if (!$isTouch && this._$state === "up") { - - target = instance; - while (target && target.root !== target) { - - if ("_$text" in target) { - if (target.type === "input") { - canPointerText = true; - break; - } - } - - if ("buttonMode" in target && target.buttonMode) { - canPointer = true; - break; - } - - target = target._$parent; - - } - - } - - } else { - - // (1) mouseOut - if (this._$mouseOverTarget) { - - instance = this._$mouseOverTarget; - - if (instance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OUT, true, false - )); - } - } - - // (2) rollOut - if (this._$rollOverObject) { - - target = this._$rollOverObject; - - // parent target - while (target && target.root !== target) { - - if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - target = target._$parent; - - } - - } - - // reset - this._$rollOverObject = null; - this._$mouseOverTarget = null; - } - - // change cursor - switch (true) { - - case canPointerText: - this._$canvas.style.cursor = "text"; - break; - - case canPointer: - this._$canvas.style.cursor = "pointer"; - break; - - case !$isTouch && this._$state === "up": - this._$canvas.style.cursor = "auto"; - break; - - } - - if (this._$actions.length > 1) { - this._$doAction(); - } - } - - /** - * @return {void} - * @method - * @private - */ - _$action (): void - { - - if (this._$stopFlag) { - return ; - } - - let loaders = null; - - const length = this._$loaders.length; - if (length) { - - // clone - loaders = this._$loaders.slice(0); - - // array reset - this._$loaders.length = 0; - - for (let idx = 0; idx < length; ++idx) { - - const loader = loaders[idx]; - - // first action - if ("content" in loader) { - loader.content._$prepareActions(); - } - } - } - - // next frame - this._$stage._$nextFrame(); - - // enter frame event - if (this._$broadcastEvents.has(Next2DEvent.ENTER_FRAME)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.ENTER_FRAME)); - } - - // constructed event - if (this._$broadcastEvents.has(Next2DEvent.FRAME_CONSTRUCTED)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.FRAME_CONSTRUCTED)); - } - - // execute frame action - this._$doAction(); - - // exit event - if (this._$broadcastEvents.has(Next2DEvent.EXIT_FRAME)) { - this._$dispatchEvent(new Next2DEvent(Next2DEvent.EXIT_FRAME)); - } - - // render event - if (this._$stage._$invalidate) { - - // reset - this._$stage._$invalidate = false; - - // execute render event - this._$dispatchEvent(new Next2DEvent(Next2DEvent.RENDER)); - - } - - // loader events - if (loaders) { - - for (let idx: number = 0; idx < loaders.length; ++idx) { - - const loader = loaders[idx]; - - // init event - if (loader.hasEventListener(Next2DEvent.INIT)) { - loader.dispatchEvent(new Next2DEvent(Next2DEvent.INIT)); - } - - // complete event - if (loader.hasEventListener(Next2DEvent.COMPLETE)) { - loader.dispatchEvent(new Next2DEvent(Next2DEvent.COMPLETE)); - } - - } - - // pool - $poolArray(loaders); - } - - // execute frame action - this._$doAction(); - } - - /** - * @returns void - * @private - */ - _$draw (): void - { - if (!this._$width || !this._$height) { - return ; - } - - if (!this._$stage._$isUpdated()) { - return ; - } - - if ($rendererWorker) { - $rendererWorker.postMessage({ - "command": "draw" - }); - } - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { - return ; - } - - // reset - context.reset(); - context.setTransform(1, 0, 0, 1, 0, 0); - context.clearRect(0, 0, this._$width, this._$height); - context.beginPath(); - - this._$stage._$draw( - context, - this._$matrix, - $COLOR_ARRAY_IDENTITY - ); - - // stage end - this._$stage._$updated = false; - - context.drawInstacedArray(); - context - .frameBuffer - .transferToMainTexture(); - } - - /** - * @return {void} - * @method - * @private - */ - _$doAction (): void - { - while (this._$actions.length) { - - this._$actionProcess = true; - - // target object - const mc: MovieClip | void = this._$actions.pop(); - if (!mc) { - continue; - } - - mc._$canAction = false; - mc._$actionOffset = 0; - mc._$actionLimit = 0; - - const frame: number = mc._$currentFrame; - if (!mc._$actions.has(frame)) { - continue; - } - - const actions: Function[] | void = mc._$actions.get(frame); - if (!actions) { - continue; - } - - mc._$actionProcess = true; - for (let idx: number = 0; idx < actions.length; ++idx) { - $setCurrentLoaderInfo(mc._$loaderInfo); - actions[idx].apply(mc); - } - mc._$actionProcess = false; - - // adjustment - if (mc._$frameCache.size) { - mc._$currentFrame = mc._$frameCache.get("nextFrame"); - mc._$clearChildren(); - - mc._$stopFlag = mc._$frameCache.get("stopFlag"); - mc._$isPlaying = mc._$frameCache.get("isPlaying"); - mc._$frameCache.clear(); - } - - } - - this._$actionProcess = false; - $setCurrentLoaderInfo(null); - } - - /** - * @return {void} - * @method - * @private - */ - _$hitTest (): void - { - if (this._$stopFlag) { - return ; - } - - // setup - const event: MouseEvent | TouchEvent | Event | null = $getEvent(); - if (!event) { - return ; - } - - // update flags - this._$hitTestStart = true; - $doUpdated(false); - - // params - let instance: DisplayObjectImpl | null = null; - let target: DisplayObjectImpl | null = null; - - let x = $window.scrollX; - let y = $window.scrollY; - - const div: HTMLElement | null = $document - .getElementById(this.contentElementId); - - if (div) { - const rect: DOMRect = div.getBoundingClientRect(); - x += rect.left; - y += rect.top; - } - - let pageX: number = 0; - let pageY: number = 0; - if ("changedTouches" in event) { - const changedTouche: Touch = event.changedTouches[0]; - pageX = changedTouche.pageX; - pageY = changedTouche.pageY; - } else if ("pageX" in event) { - pageX = event.pageX; - pageY = event.pageY; - } - - // drop point - const stageX: number = (pageX - x) / this._$scale; - const stageY: number = (pageY - y) / this._$scale; - - // update - this._$stageX = stageX; - this._$stageY = stageY; - - // setup - this._$hitObject.x = stageX; - this._$hitObject.y = stageY; - this._$hitObject.pointer = ""; - this._$hitObject.hit = null; - - // reset - $hitContext.setTransform(1, 0, 0, 1, 0, 0); - $hitContext.beginPath(); - - // hit test - $MATRIX_HIT_ARRAY_IDENTITY[4] = this._$tx / this._$scale / $devicePixelRatio; - $MATRIX_HIT_ARRAY_IDENTITY[5] = this._$ty / this._$scale / $devicePixelRatio; - this._$stage._$mouseHit( - $hitContext, $MATRIX_HIT_ARRAY_IDENTITY, - this._$hitObject, true - ); - - // stop event - if (this._$hitObject.hit) { - event.preventDefault(); - } - - // change state - let canPointerText: boolean = false; - let staticPointer: boolean = false; - let canPointer: boolean = false; - - const eventType: string = $getEventType(); - switch (eventType) { - - case $TOUCH_MOVE: - case $MOUSE_MOVE: - - if ($dropTarget) { - - const point: Point = $dropTarget._$dragMousePoint(); - - let dragX: number = point.x; - let dragY: number = point.y; - - if (!$dragRules.lock) { - dragX += $dragRules.position.x; - dragY += $dragRules.position.y; - } - - const bounds: Rectangle | null = $dragRules.bounds; - if (bounds) { - dragX = $clamp(dragX, bounds.left, bounds.right); - dragY = $clamp(dragY, bounds.top, bounds.bottom); - } - - // set move xy - $dropTarget.x = dragX; - $dropTarget.y = dragY; - - } - - if (this._$clickTarget - && "_$text" in this._$clickTarget - && this._$clickTarget.scrollEnabled - && this._$clickTarget.selectIndex === -1 - ) { - - const deltaX: number = this._$deltaX - pageX; - const deltaY: number = this._$deltaY - pageY; - - // @ts-ignore - this._$clickTarget.scrollX += deltaX / (this._$clickTarget.textWidth / this._$clickTarget.width); - - // @ts-ignore - this._$clickTarget.scrollY += deltaY / (this._$clickTarget.textHeight / this._$clickTarget.height); - } - - this._$deltaX = pageX; - this._$deltaY = pageY; - break; - - case $TOUCH_START: - case $MOUSE_DOWN: - this._$deltaX = pageX; - this._$deltaY = pageY; - this._$state = "down"; - canPointer = this._$canvas.style.cursor === "pointer"; - staticPointer = true; - break; - - case $TOUCH_END: - case $MOUSE_UP: - case $DOUBLE_CLICK: - this._$deltaX = 0; - this._$deltaY = 0; - this._$state = "up"; - break; - - } - - // execute - switch (true) { - - case this._$hitObject.hit === null: - case eventType === $MOUSE_LEAVE: - - // (1) mouseOut - if (this._$mouseOverTarget) { - - instance = this._$mouseOverTarget; - if (instance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OUT, true, false - )); - } - - } - - // (2) rollOut - if (this._$rollOverObject) { - - target = this._$rollOverObject; - - // parent target - while (target && target.root !== target) { - - if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - target = target._$parent; - - } - - } - - // reset - this._$rollOverObject = null; - this._$mouseOverTarget = null; - - // stage event - switch (eventType) { - - case $MOUSE_WHEEL: - if (this._$stage.hasEventListener(Next2DMouseEvent.MOUSE_WHEEL)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_WHEEL, true, false - )); - } - break; - - case $TOUCH_START: - case $MOUSE_DOWN: - if (this._$textField && "focus" in this._$textField) { - this._$textField.focus = false; - this._$textField = null; - } - - if (this._$stage.hasEventListener(Next2DMouseEvent.MOUSE_DOWN)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_DOWN, true, false - )); - } - break; - - case $TOUCH_END: - case $MOUSE_UP: - - if (this._$stage.hasEventListener(Next2DMouseEvent.CLICK)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.CLICK, true, false - )); - } - - if (this._$stage.hasEventListener(Next2DMouseEvent.MOUSE_UP)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_UP, true, false - )); - } - - break; - - case $TOUCH_MOVE: - case $MOUSE_MOVE: - if (this._$stage.hasEventListener(Next2DMouseEvent.MOUSE_MOVE)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_MOVE, true, false - )); - } - break; - - case $DOUBLE_CLICK: - if (this._$stage.hasEventListener(Next2DMouseEvent.DOUBLE_CLICK)) { - this._$stage.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.DOUBLE_CLICK, true, false - )); - } - break; - - } - - break; - - default: - - instance = this._$hitObject.hit; - switch (eventType) { - - // move event - case $TOUCH_MOVE: - case $MOUSE_MOVE: - - // (1) mouseMove - if (instance.willTrigger(Next2DMouseEvent.MOUSE_MOVE)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_MOVE, true, false - )); - } - - // (2) mouseOut - if (this._$mouseOverTarget - && this._$mouseOverTarget !== instance - ) { - - const outInstance: DisplayObjectImpl = this._$mouseOverTarget; - - if (outInstance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - outInstance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OUT, true, false - )); - } - - } - - // rollOut and rollOver - if (this._$rollOverObject !== instance) { - - let hitParent: DisplayObjectImpl | null = null; - if (this._$rollOverObject) { - - // (3) prev object rollOut - target = this._$rollOverObject; - - if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - // rollOver flag instance - hitParent = target._$parent; - while (hitParent && hitParent._$root !== hitParent) { - - if (hitParent === instance) { - break; - } - - if (hitParent._$mouseEnabled - && hitParent._$outCheck(stageX, stageY) - ) { - - let isUpperLayer: boolean = false; - let check: DisplayObjectImpl | null = instance; - while (check && check._$root !== check) { - - if (check !== hitParent) { - check = check._$parent; - continue; - } - - isUpperLayer = true; - - break; - } - - if (!isUpperLayer && hitParent._$parent === instance._$parent - && hitParent._$index > instance._$index - ) { - isUpperLayer = true; - } - - if (isUpperLayer) { - break; - } - - } - - if (hitParent.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - hitParent.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OUT, false, false - )); - } - - hitParent = hitParent._$parent; - - } - } - - // (4) current object rollOver - target = instance; - for (;;) { - - if (target.willTrigger(Next2DMouseEvent.ROLL_OVER)) { - target.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.ROLL_OVER, false, false - )); - } - - target = target._$parent; - if (!target || target === hitParent - || target.stage === target - ) { - break; - } - - } - - } - - this._$rollOverObject = instance; - - // (5) mouseOver - switch (true) { - - case this._$mouseOverTarget === null: - case this._$mouseOverTarget !== instance: - - if (instance.willTrigger(Next2DMouseEvent.MOUSE_OVER)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_OVER, true, false - )); - } - - // set target - this._$mouseOverTarget = instance; - break; - - } - - // click reset - if (this._$state === "up") { - this._$clickTarget = null; - } else { - if (this._$textField) { - this._$textField._$setIndex( - stageX - $MATRIX_HIT_ARRAY_IDENTITY[4], - stageY - $MATRIX_HIT_ARRAY_IDENTITY[5] - ); - } - } - - break; - - // down event - case $TOUCH_START: - case $MOUSE_DOWN: - - if (this._$textField - && instance !== this._$textField - ) { - this._$textField.focus = false; - this._$textField = null; - } - - // TextField focus out - if ("_$text" in instance) { - instance.focus = true; - instance._$setIndex( - stageX - $MATRIX_HIT_ARRAY_IDENTITY[4], - stageY - $MATRIX_HIT_ARRAY_IDENTITY[5] - ); - this._$textField = instance; - - // move text area element - $textArea.style.left = `${pageX}px`; - $textArea.style.top = `${pageY}px`; - } - - // (3) mouseDown - if (instance.willTrigger(Next2DMouseEvent.MOUSE_DOWN)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_DOWN, true, false - )); - } - - // (4) click - this._$clickTarget = instance; - - break; - - // up event - case $TOUCH_END: - case $MOUSE_UP: - - // (1) mouseUp - if (instance.willTrigger(Next2DMouseEvent.MOUSE_UP)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.MOUSE_UP, true, false - )); - } - - // (2) click - if (this._$clickTarget === instance) { - - if (instance.willTrigger(Next2DMouseEvent.CLICK)) { - instance.dispatchEvent(new Next2DMouseEvent( - Next2DMouseEvent.CLICK, true, false - )); - } - - } - - // reset - this._$clickTarget = null; - - break; - - case $MOUSE_WHEEL: - if (instance.willTrigger(Next2DMouseEvent.MOUSE_WHEEL)) { - instance.dispatchEvent( - new Next2DMouseEvent(Next2DMouseEvent.MOUSE_WHEEL) - ); - } - - if (instance.scrollEnabled) { - if ("deltaX" in event) { - // @ts-ignore - instance.scrollX += event.deltaX / (instance.textWidth / instance.width); - } - - if ("deltaY" in event) { - // @ts-ignore - instance.scrollY += event.deltaY / (instance.textHeight / instance.height); - } - } - - break; - - case $DOUBLE_CLICK: - if (instance.willTrigger(Next2DMouseEvent.DOUBLE_CLICK)) { - instance.dispatchEvent( - new Next2DMouseEvent(Next2DMouseEvent.DOUBLE_CLICK) - ); - } - break; - - default: - break; - - } - - // PC - if (!staticPointer) { - - if (!$isTouch && this._$state === "up") { - - target = instance; - while (target && target.root !== target) { - - if ("_$text" in target) { - - if (target.type === "input") { - canPointerText = true; - break; - } - - } else { - - if (target._$buttonMode) { - canPointer = true; - break; - } - - } - - target = target._$parent; - - } - } - } - break; - - } - - // change cursor - switch (true) { - - case canPointerText: - this._$canvas.style.cursor = "text"; - break; - - case canPointer: - this._$canvas.style.cursor = "pointer"; - break; - - case !$isTouch && this._$state === "up": - this._$canvas.style.cursor = "auto"; - break; - - } - - // execute action - if (!this._$actionProcess && this._$actions.length > 1) { - this._$doAction(); - } - - if ($isUpdated()) { - - // action script - this._$stage._$prepareActions(); - if (!this._$actionProcess) { - this._$doAction(); - } - - } - - this._$hitTestStart = false; - } -} +/** + * @type {Player} + * @protected + */ +export const $player: Player = new Player(); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerAppendElementService.test.ts b/packages/core/src/Player/service/PlayerAppendElementService.test.ts new file mode 100644 index 00000000..2bc6d92f --- /dev/null +++ b/packages/core/src/Player/service/PlayerAppendElementService.test.ts @@ -0,0 +1,23 @@ +import { execute } from "./PlayerAppendElementService"; +import { $setCanvas, $setMainElement } from "../../CoreUtil"; +import { $textArea } from "@next2d/text"; +import { describe, expect, it } from "vitest"; + +describe("PlayerAppendElementService.js test", () => +{ + it("execute test case", () => + { + const div = document.createElement("div"); + $setMainElement(div); + + const canvas = document.createElement("canvas"); + $setCanvas(canvas); + + expect(div.children.length).toBe(0); + execute(); + + expect(div.children[0]).toBe(canvas); + expect(div.children[1]).toBe($textArea); + expect(div.children.length).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerAppendElementService.ts b/packages/core/src/Player/service/PlayerAppendElementService.ts new file mode 100644 index 00000000..bc812ac3 --- /dev/null +++ b/packages/core/src/Player/service/PlayerAppendElementService.ts @@ -0,0 +1,25 @@ +import { $getMainElement, $getCanvas } from "../../CoreUtil"; +import { $textArea } from "@next2d/text"; + +/** + * @description canvas と textarea elementをメインのdivに追加 + * Add canvas and textarea element to main div. + * + * @return {void} + * @method + * @public + */ +export const execute = (): void => +{ + const element: HTMLDivElement = $getMainElement(); + if (!element) { + return ; + } + const canvas: HTMLCanvasElement = $getCanvas(); + if (!canvas) { + return ; + } + + element.appendChild(canvas); + element.appendChild($textArea); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.test.ts b/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.test.ts new file mode 100644 index 00000000..697ba45f --- /dev/null +++ b/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.test.ts @@ -0,0 +1,64 @@ +import { execute } from "./PlayerApplyContainerElementStyleService"; +import { describe, expect, it } from "vitest"; + +describe("PlayerApplyContainerElementStyleService.js test", () => +{ + it("execute test case1", () => + { + try { + + execute(document.createElement("div")); + + } catch (error) { + + expect(error.message).toBe("parent element is null."); + + } + }); + + it("execute test case2", () => + { + const div = document.createElement("div"); + + const parent = document.createElement("div"); + parent.appendChild(div); + + expect(div.style.display).toBe(""); + expect(div.style.alignItems).toBe(""); + expect(div.style.justifyContent).toBe(""); + expect(div.style.backgroundColor).toBe(""); + expect(div.style.overflow).toBe(""); + expect(div.style.padding).toBe(""); + expect(div.style.margin).toBe(""); + expect(div.style.userSelect).toBe(""); + expect(div.style.outline).toBe(""); + + execute(div); + + expect(div.style.display).toBe("flex"); + expect(div.style.alignItems).toBe("center"); + expect(div.style.justifyContent).toBe("center"); + expect(div.style.backgroundColor).toBe("transparent"); + expect(div.style.overflow).toBe("hidden"); + expect(div.style.padding).toBe("0px"); + expect(div.style.margin).toBe("0px"); + expect(div.style.userSelect).toBe("none"); + expect(div.style.outline).toBe("none"); + }); + + it("execute test case3", () => + { + const div = document.createElement("div"); + + const parent = document.createElement("div"); + parent.appendChild(div); + + expect(div.style.width).toBe(""); + expect(div.style.height).toBe(""); + + execute(div, 100, 200); + + expect(div.style.width).toBe("100px"); + expect(div.style.height).toBe("200px"); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.ts b/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.ts new file mode 100644 index 00000000..71ae5bb9 --- /dev/null +++ b/packages/core/src/Player/service/PlayerApplyContainerElementStyleService.ts @@ -0,0 +1,51 @@ +/** + * @description コンテナのelementにベースのスタイルを適用 + * Apply base style to container element + * + * @param {HTMLDivElement} element + * @param {number} [fixed_width=0] + * @param {number} [fixed_height=0] + * @return {void} + * @method + * @public + */ +export const execute = ( + element: HTMLDivElement, + fixed_width: number = 0, + fixed_height: number = 0 +): void => { + + let style = ""; + + style += "display:flex;"; + style += "align-items:center;"; + style += "justify-content:center;"; + style += "background-color:transparent;"; + style += "overflow:hidden;"; + style += "padding:0;"; + style += "margin:0;"; + style += "user-select:none;"; + style += "outline:none;"; + + if (fixed_width && fixed_height) { + // fixed size + style += `width:${fixed_width}px;`; + style += `height:${fixed_height}px;`; + } else { + const parent = element.parentElement; + if (!parent) { + throw new Error("parent element is null."); + } + + if (parent.tagName === "BODY") { + // If the parent is BODY, adjust to window size. + style += `width:${window.innerWidth}px;`; + style += `height:${window.innerHeight}px;`; + } else { + style += `width:${parent.clientWidth}px;`; + style += `height:${parent.clientHeight}px;`; + } + } + + element.setAttribute("style", style); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerCreateContainerElementService.test.ts b/packages/core/src/Player/service/PlayerCreateContainerElementService.test.ts new file mode 100644 index 00000000..665f0605 --- /dev/null +++ b/packages/core/src/Player/service/PlayerCreateContainerElementService.test.ts @@ -0,0 +1,13 @@ +import { execute } from "./PlayerCreateContainerElementService"; +import { $PREFIX } from "../../CoreUtil"; +import { describe, expect, it } from "vitest"; + +describe("PlayerCreateContainerElementService.js test", () => +{ + it("execute test case1", () => + { + const div = execute(); + expect(div.id).toBe($PREFIX); + expect(div.tabIndex).toBe(-1); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerCreateContainerElementService.ts b/packages/core/src/Player/service/PlayerCreateContainerElementService.ts new file mode 100644 index 00000000..7efc7eac --- /dev/null +++ b/packages/core/src/Player/service/PlayerCreateContainerElementService.ts @@ -0,0 +1,35 @@ +import { $player } from "../../Player"; +import { + $PREFIX, + $setMainElement +} from "../../CoreUtil"; + +/** + * @description コンテナとなるElementを作成して返却 + * Create and return an Element that serves as a container + * + * @return {HTMLDivElement} + * @method + * @protected + */ +export const execute = (): HTMLDivElement => +{ + const div: HTMLDivElement = document.createElement("div"); + $setMainElement(div); + + div.id = $PREFIX; + div.tabIndex = -1; + + if (!$player.tagId) { + document.body.appendChild(div); + } else { + const element: HTMLElement | null = document.getElementById($player.tagId); + if (!element) { + alert(`Element not found with tag ID: ${$player.tagId}`); + return div; + } + element.appendChild(div); + } + + return div; +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerDoubleClickEventService.test.ts b/packages/core/src/Player/service/PlayerDoubleClickEventService.test.ts new file mode 100644 index 00000000..af6dd6b1 --- /dev/null +++ b/packages/core/src/Player/service/PlayerDoubleClickEventService.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./PlayerDoubleClickEventService"; +import { $hitObject } from "../../CoreUtil"; +import { DisplayObject, stage } from "@next2d/display"; +import { PointerEvent } from "@next2d/events"; +import { describe, expect, it } from "vitest"; + +describe("PlayerDoubleClickEventService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + + let state = "none"; + displayObject.addEventListener(PointerEvent.DOUBLE_CLICK, () => + { + state = "double_click"; + }); + + $hitObject.hit = displayObject; + + expect(state).toBe("none"); + execute(); + expect(state).toBe("double_click"); + + $hitObject.hit = null; + }); + + it("execute test case2", () => + { + let state = "none"; + stage.addEventListener(PointerEvent.DOUBLE_CLICK, () => + { + state = "double_click"; + }); + + $hitObject.hit = null; + + expect($hitObject.hit).toBe(null); + expect(state).toBe("none"); + execute(); + expect(state).toBe("double_click"); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerDoubleClickEventService.ts b/packages/core/src/Player/service/PlayerDoubleClickEventService.ts new file mode 100644 index 00000000..d84a72a3 --- /dev/null +++ b/packages/core/src/Player/service/PlayerDoubleClickEventService.ts @@ -0,0 +1,30 @@ +import type { DisplayObject } from "@next2d/display"; +import { stage } from "@next2d/display"; +import { PointerEvent } from "@next2d/events"; +import { $hitObject } from "../../CoreUtil"; + +/** + * @description ポインターのダブルタップイベントを処理します。 + * Processes the pointer double tap event. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const displayObject = $hitObject.hit as D; + if (displayObject) { + if (displayObject.willTrigger(PointerEvent.DOUBLE_CLICK)) { + displayObject.dispatchEvent( + new PointerEvent(PointerEvent.DOUBLE_CLICK) + ); + } + } else { + if (stage.willTrigger(PointerEvent.DOUBLE_CLICK)) { + stage.dispatchEvent( + new PointerEvent(PointerEvent.DOUBLE_CLICK) + ); + } + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerKeyDownEventService.test.ts b/packages/core/src/Player/service/PlayerKeyDownEventService.test.ts new file mode 100644 index 00000000..7c7be03e --- /dev/null +++ b/packages/core/src/Player/service/PlayerKeyDownEventService.test.ts @@ -0,0 +1,40 @@ +import { execute } from "./PlayerKeyDownEventService"; +import { stage } from "@next2d/display"; +import { describe, expect, it, vi } from "vitest"; + +describe("PlayerKeyDownEventService.js test", () => +{ + it("execute test case1", () => + { + const mockKeyboardEvent = { + "type": "keydown", + } as unknown as KeyboardEvent; + + let result = "ok" + stage.hasEventListener = vi.fn().mockReturnValue(false); + stage.dispatchEvent = vi.fn((event) => + { + result = event.type; + }); + + execute(mockKeyboardEvent); + expect(result).toBe("ok"); + }); + + it("execute test case2", () => + { + const mockKeyboardEvent = { + "type": "keydown", + } as unknown as KeyboardEvent; + + let result = "ok" + stage.hasEventListener = vi.fn().mockReturnValue(true); + stage.dispatchEvent = vi.fn((event) => + { + result = event.type; + }); + + execute(mockKeyboardEvent); + expect(result).toBe("keydown"); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerKeyDownEventService.ts b/packages/core/src/Player/service/PlayerKeyDownEventService.ts new file mode 100644 index 00000000..28390be4 --- /dev/null +++ b/packages/core/src/Player/service/PlayerKeyDownEventService.ts @@ -0,0 +1,31 @@ +import { stage } from "@next2d/display"; +import { $getSelectedTextField } from "@next2d/text"; +import { + KeyboardEvent as Event, + $setEvent +} from "@next2d/events"; + +/** + * @description キーボードダウンイベントを実行する + * Execute the keyboard down event + * + * @param {KeyboardEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: KeyboardEvent): void => +{ + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + selectedTextField.keyDown(event); + return ; + } + + if (!stage.hasEventListener(Event.KEY_DOWN)) { + return ; + } + + $setEvent(event); + stage.dispatchEvent(new Event(Event.KEY_DOWN)); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerKeyUpEventService.test.ts b/packages/core/src/Player/service/PlayerKeyUpEventService.test.ts new file mode 100644 index 00000000..bb77871e --- /dev/null +++ b/packages/core/src/Player/service/PlayerKeyUpEventService.test.ts @@ -0,0 +1,40 @@ +import { execute } from "./PlayerKeyUpEventService"; +import { stage } from "@next2d/display"; +import { describe, expect, it, vi } from "vitest"; + +describe("PlayerKeyUpEventService.js test", () => +{ + it("execute test case1", () => + { + const mockKeyboardEvent = { + "type": "keyup", + } as unknown as KeyboardEvent; + + let result = "ok" + stage.hasEventListener = vi.fn().mockReturnValue(false); + stage.dispatchEvent = vi.fn((event) => + { + result = event.type; + }); + + execute(mockKeyboardEvent); + expect(result).toBe("ok"); + }); + + it("execute test case2", () => + { + const mockKeyboardEvent = { + "type": "keyup", + } as unknown as KeyboardEvent; + + let result = "ok" + stage.hasEventListener = vi.fn().mockReturnValue(true); + stage.dispatchEvent = vi.fn((event) => + { + result = event.type; + }); + + execute(mockKeyboardEvent); + expect(result).toBe("keyup"); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerKeyUpEventService.ts b/packages/core/src/Player/service/PlayerKeyUpEventService.ts new file mode 100644 index 00000000..15777a37 --- /dev/null +++ b/packages/core/src/Player/service/PlayerKeyUpEventService.ts @@ -0,0 +1,30 @@ +import { stage } from "@next2d/display"; +import { $getSelectedTextField } from "@next2d/text"; +import { + KeyboardEvent as Event, + $setEvent +} from "@next2d/events"; + +/** + * @description キーボードアップイベントを実行する + * Execute the keyboard up event + * + * @param {KeyboardEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: KeyboardEvent): void => +{ + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + return ; + } + + if (!stage.hasEventListener(Event.KEY_UP)) { + return ; + } + + $setEvent(event); + stage.dispatchEvent(new Event(Event.KEY_UP)); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerLoadingAnimationService.test.ts b/packages/core/src/Player/service/PlayerLoadingAnimationService.test.ts new file mode 100644 index 00000000..1caf37b5 --- /dev/null +++ b/packages/core/src/Player/service/PlayerLoadingAnimationService.test.ts @@ -0,0 +1,13 @@ +import { execute } from "./PlayerLoadingAnimationService"; +import { describe, expect, it } from "vitest"; + +describe("PlayerLoadingAnimationService.js test", () => +{ + it("execute test case1", () => + { + const div = document.createElement("div"); + expect(div.children.length).toBe(0); + execute(div); + expect(div.children.length).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerLoadingAnimationService.ts b/packages/core/src/Player/service/PlayerLoadingAnimationService.ts new file mode 100644 index 00000000..187e9b0e --- /dev/null +++ b/packages/core/src/Player/service/PlayerLoadingAnimationService.ts @@ -0,0 +1,37 @@ +import { $PREFIX } from "../../CoreUtil"; + +/** + * @description ローディングアニメーションを登録 + * Register loading animation + * + * @param {HTMLDivElement} element + * @return {void} + * @method + * @public + */ +export const execute = (element: HTMLDivElement): void => +{ + const loadingId = `${$PREFIX}_loading`; + + element.innerHTML = ` +
`; + +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerPointerDownEventService.test.ts b/packages/core/src/Player/service/PlayerPointerDownEventService.test.ts new file mode 100644 index 00000000..fc5ace96 --- /dev/null +++ b/packages/core/src/Player/service/PlayerPointerDownEventService.test.ts @@ -0,0 +1,74 @@ +import { execute } from "./PlayerPointerDownEventService"; +import { $hitObject } from "../../CoreUtil"; +import { PointerEvent } from "@next2d/events"; +import { TextField } from "@next2d/text"; +import { DisplayObject, stage } from "@next2d/display"; +import { describe, expect, it } from "vitest"; + +describe("PlayerPointerDownEventService.js test", () => +{ + it("execute test case1", () => + { + let state = "none"; + stage.addEventListener(PointerEvent.POINTER_DOWN, () => + { + state = "pointerdown"; + }); + + $hitObject.hit = null; + + expect($hitObject.hit).toBe(null); + expect(state).toBe("none"); + execute(); + expect(state).toBe("pointerdown"); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + + let state = "none"; + displayObject.addEventListener(PointerEvent.POINTER_DOWN, () => + { + state = "pointerdown"; + }); + + $hitObject.hit = displayObject; + + expect(state).toBe("none"); + execute(); + expect(state).toBe("pointerdown"); + + $hitObject.hit = null; + }); + + it("execute test case3", () => + { + const textField = new TextField(); + textField.type = "input"; + + $hitObject.hit = textField; + + expect(textField.focus).toBe(false); + execute(); + expect(textField.focus).toBe(true); + execute(); + expect(textField.focus).toBe(true); + + $hitObject.hit = null; + execute(); + expect(textField.focus).toBe(false); + + $hitObject.hit = textField; + execute(); + expect(textField.focus).toBe(true); + + $hitObject.hit = new DisplayObject(); + execute(); + expect(textField.focus).toBe(false); + + $hitObject.hit = null; + execute(); + expect(textField.focus).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerPointerDownEventService.ts b/packages/core/src/Player/service/PlayerPointerDownEventService.ts new file mode 100644 index 00000000..d6b39410 --- /dev/null +++ b/packages/core/src/Player/service/PlayerPointerDownEventService.ts @@ -0,0 +1,71 @@ +import type { DisplayObject } from "@next2d/display"; +import type { TextField } from "@next2d/text"; +import { stage } from "@next2d/display"; +import { PointerEvent } from "@next2d/events"; +import { + $setSelectedTextField, + $getSelectedTextField, + $textArea +} from "@next2d/text"; +import { + $hitObject, + $hitMatrix +} from "../../CoreUtil"; + +/** + * @description ポインターダウンイベントを処理します。 + * Processes the pointer down event. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const displayObject = $hitObject.hit as unknown as D; + + // 選択中のTextFieldがある場合はフォーカスを解除します。 + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + if (!displayObject || selectedTextField.instanceId !== displayObject.instanceId) { + selectedTextField.focus = false; + $setSelectedTextField(null); + } + } + + if (displayObject) { + + if (displayObject.isText) { + + if (!(displayObject as unknown as TextField).focus) { + (displayObject as unknown as TextField).focus = true; + $setSelectedTextField(displayObject as unknown as TextField); + } else { + setTimeout((): void => + { + $textArea.focus(); + }, 300); + } + + (displayObject as unknown as TextField).setFocusIndex( + $hitObject.x - $hitMatrix[4], + $hitObject.y - $hitMatrix[5] + ); + } + + // ヒットしたDisplayObjectポインターダウンイベントを発火します。 + if (displayObject.willTrigger(PointerEvent.POINTER_DOWN)) { + displayObject.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_DOWN) + ); + } + + } else { + // ステージ全体のポインターダウンイベントを発火します。 + if (stage.willTrigger(PointerEvent.POINTER_DOWN)) { + stage.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_DOWN) + ); + } + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerPointerMoveEventService.ts b/packages/core/src/Player/service/PlayerPointerMoveEventService.ts new file mode 100644 index 00000000..4c9e7ee8 --- /dev/null +++ b/packages/core/src/Player/service/PlayerPointerMoveEventService.ts @@ -0,0 +1,126 @@ +import type { DisplayObject, Sprite } from "@next2d/display"; +import { stage } from "@next2d/display"; +import { $getSelectedTextField } from "@next2d/text"; +import { PointerEvent } from "@next2d/events"; +import { $player } from "../../Player"; +import { + $hitObject, + $hitMatrix, + $setRollOverDisplayObject, + $getRollOverDisplayObject, + $clamp +} from "../../CoreUtil"; + +/** + * @description ポインタームーブイベントを処理します。 + * Processes the pointer move event. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const dropTarget = stage.dropTarget as Sprite | null; + if (dropTarget) { + + const point = dropTarget.parent + ? dropTarget.parent.globalToLocal(stage.pointer) + : dropTarget.globalToLocal(stage.pointer); + + let dragX = 0; + let dragY = 0; + + if (!(dropTarget as Sprite).$lockCenter) { + dragX = point.x + (dropTarget as Sprite).$offsetX; + dragY = point.y + (dropTarget as Sprite).$offsetY; + } else { + dragX = point.x - dropTarget.width / 2; + dragY = point.y - dropTarget.height / 2; + } + + const bounds = (dropTarget as Sprite).$boundedRect; + if (bounds) { + dragX = $clamp(dragX, bounds.left, bounds.right); + dragY = $clamp(dragY, bounds.top, bounds.bottom); + } + + // set move xy + dropTarget.x = dragX; + dropTarget.y = dragY; + } + + // text field + const selectedTextField = $getSelectedTextField(); + if (selectedTextField && $player.mouseState === "down") { + selectedTextField.setFocusIndex( + $hitObject.x - $hitMatrix[4], + $hitObject.y - $hitMatrix[5], + true + ); + + return ; + } + + const rollOverDisplayObject = $getRollOverDisplayObject(); + const displayObject = $hitObject.hit as D; + if (displayObject) { + + // pointerMove + if (displayObject.willTrigger(PointerEvent.POINTER_MOVE)) { + displayObject.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_MOVE + )); + } + + // rollOut and rollOver + if (rollOverDisplayObject) { + + if (rollOverDisplayObject.instanceId !== displayObject.instanceId) { + + // rollOut + if (rollOverDisplayObject.willTrigger(PointerEvent.POINTER_OUT)) { + rollOverDisplayObject.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_OUT + )); + } + + // rollOver + if (displayObject.willTrigger(PointerEvent.POINTER_OVER)) { + displayObject.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_OVER + )); + } + } + + } else { + // rollOver + if (displayObject.willTrigger(PointerEvent.POINTER_OVER)) { + displayObject.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_OVER + )); + } + } + + // set rollOver DisplayObject + $setRollOverDisplayObject(displayObject); + + } else { + + // rollOut + if (rollOverDisplayObject) { + if (rollOverDisplayObject.willTrigger(PointerEvent.POINTER_OUT)) { + rollOverDisplayObject.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_OUT + )); + } + $setRollOverDisplayObject(null); + } + + if (stage.hasEventListener(PointerEvent.POINTER_MOVE)) { + stage.dispatchEvent(new PointerEvent( + PointerEvent.POINTER_MOVE + )); + } + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerPointerUpEventService.ts b/packages/core/src/Player/service/PlayerPointerUpEventService.ts new file mode 100644 index 00000000..57b6b53e --- /dev/null +++ b/packages/core/src/Player/service/PlayerPointerUpEventService.ts @@ -0,0 +1,46 @@ +import type { DisplayObject } from "@next2d/display"; +import { stage } from "@next2d/display"; +import { PointerEvent } from "@next2d/events"; +import { $hitObject } from "../../CoreUtil"; + +/** + * @description ポインターアップイベントを処理します。 + * Processes the pointer up event. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const dropTarget = stage.dropTarget as D | null; + const displayObject = $hitObject.hit as D; + if (displayObject) { + if (displayObject.willTrigger(PointerEvent.POINTER_UP)) { + displayObject.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_UP) + ); + } + + if (dropTarget + && dropTarget.instanceId !== displayObject.instanceId + && dropTarget.willTrigger(PointerEvent.POINTER_UP) + ) { + dropTarget.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_UP) + ); + } + } else { + if (dropTarget && dropTarget.willTrigger(PointerEvent.POINTER_UP)) { + dropTarget.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_UP) + ); + } else { + if (stage.willTrigger(PointerEvent.POINTER_UP)) { + stage.dispatchEvent( + new PointerEvent(PointerEvent.POINTER_UP) + ); + } + } + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerRemoveCachePostMessageService.ts b/packages/core/src/Player/service/PlayerRemoveCachePostMessageService.ts new file mode 100644 index 00000000..68925a5b --- /dev/null +++ b/packages/core/src/Player/service/PlayerRemoveCachePostMessageService.ts @@ -0,0 +1,44 @@ +import type { IRemoveCacheMessage } from "../../interface/IRemoveCacheMessage"; +import { $cacheStore } from "@next2d/cache"; +import { $rendererWorker } from "../../RendererWorker"; + +/** + * @description リサイズメッセージ + * Resize message + * + * @type {object} + * @private + */ +const message: IRemoveCacheMessage = { + "command": "removeCache", + "buffer": null +}; + +/** + * @description Transferableオブジェクト + * Transferable object + * + * @type {Transferable[]} + * @private + */ +const options: Transferable[] = []; + +/** + * @description worker側のキャッシュキーを削除する + * Remove the cache key on the worker side + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const buffer = new Float32Array($cacheStore.$removeIds); + $cacheStore.$removeIds.length = 0; + + message.buffer = buffer; + options[0] = buffer.buffer; + + // postMessage + $rendererWorker.postMessage(message, options); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerRemoveLoadingElementService.test.ts b/packages/core/src/Player/service/PlayerRemoveLoadingElementService.test.ts new file mode 100644 index 00000000..1d5353d7 --- /dev/null +++ b/packages/core/src/Player/service/PlayerRemoveLoadingElementService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./PlayerRemoveLoadingElementService"; +import { execute as playerLoadingAnimationService } from "./PlayerLoadingAnimationService"; +import { $PREFIX, $setMainElement } from "../../CoreUtil"; +import { describe, expect, it } from "vitest"; + +describe("PlayerRemoveLoadingElementService.js test", () => +{ + it("execute test case1", () => + { + const parent = document.createElement("div"); + $setMainElement(parent); + parent.id = $PREFIX; + document.body.appendChild(parent); + expect(parent.children.length).toBe(0); + + playerLoadingAnimationService(parent); + expect(parent.children.length).toBe(2); + + execute(); + expect(parent.children.length).toBe(0); + + parent.remove(); + }); +}); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerRemoveLoadingElementService.ts b/packages/core/src/Player/service/PlayerRemoveLoadingElementService.ts new file mode 100644 index 00000000..5c2d327c --- /dev/null +++ b/packages/core/src/Player/service/PlayerRemoveLoadingElementService.ts @@ -0,0 +1,21 @@ +import { $getMainElement } from "../../CoreUtil"; + +/** + * @description ローディングのelementを削除 + * Remove the loading element + * + * @return {void} + * @method + * @public + */ +export const execute = (): void => +{ + const element: HTMLDivElement = $getMainElement(); + if (!element) { + return ; + } + + while (element.firstChild) { + element.removeChild(element.firstChild); + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerRenderingPostMessageService.ts b/packages/core/src/Player/service/PlayerRenderingPostMessageService.ts new file mode 100644 index 00000000..30425d23 --- /dev/null +++ b/packages/core/src/Player/service/PlayerRenderingPostMessageService.ts @@ -0,0 +1,92 @@ + +import type { IRenderMessage } from "../../interface/IRenderMessage"; +import { $rendererWorker } from "../../RendererWorker"; +import { stage } from "@next2d/display"; +import { renderQueue } from "@next2d/render-queue"; +import { $renderMatrix } from "../../CoreUtil"; + +/** + * @type {Float32Array} + * @private + */ +export const $COLOR_ARRAY_IDENTITY: Float32Array = new Float32Array([1, 1, 1, 1, 0, 0, 0, 0]); + +/** + * @type {ImageBitmap[]} + * @private + */ +const $imageBitmaps: ImageBitmap[] = []; + +/** + * @description レンダリングメッセージ + * Rendering message + * + * @type {object} + * @private + */ +const $message: IRenderMessage = { + "command": "render", + "buffer": null, + "length": 0, + "imageBitmaps": null +}; + +/** + * @description Transferableオブジェクト + * Transferable object + * + * @type {Transferable[]} + * @private + */ +const $options: Transferable[] = []; + +// 受け取りイベントを登録 +$rendererWorker.addEventListener("message", (event: MessageEvent): void => +{ + if (event.data.message !== "render") { + return ; + } + + const buffer = event.data.buffer; + if (renderQueue.buffer.length > buffer.length) { + return ; + } + + renderQueue.buffer = buffer; +}); + +/** + * @description レンダリングデータを生成してworkerに送る + * Generate rendering data and send it to the worker + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + renderQueue.offset = 0; + $options.length = 0; + $imageBitmaps.length = 0; + stage.$generateRenderQueue( + stage, $imageBitmaps, $renderMatrix, $COLOR_ARRAY_IDENTITY + ); + + if (!renderQueue.offset) { + return ; + } + + // update buffer + $message.buffer = renderQueue.buffer; + $message.length = renderQueue.offset; + $options.push(renderQueue.buffer.buffer); + + // postMessage + $message.imageBitmaps = null; + if ($imageBitmaps.length) { + $message.imageBitmaps = $imageBitmaps; + $options.push(...$imageBitmaps); + } + + $rendererWorker.postMessage($message, $options); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerResizePostMessageService.ts b/packages/core/src/Player/service/PlayerResizePostMessageService.ts new file mode 100644 index 00000000..5f0aeb7a --- /dev/null +++ b/packages/core/src/Player/service/PlayerResizePostMessageService.ts @@ -0,0 +1,47 @@ +import type { IResizeMessage } from "../../interface/IResizeMessage"; +import { $player } from "../../Player"; +import { $rendererWorker } from "../../RendererWorker"; + +/** + * @description リサイズメッセージ + * Resize message + * + * @type {object} + * @private + */ +const message: IResizeMessage = { + "command": "resize", + "buffer": null +}; + +/** + * @description Transferableオブジェクト + * Transferable object + * + * @type {Transferable[]} + * @private + */ +const options: Transferable[] = []; + +/** + * @description 画面リサイズ情報をworkerに送る + * Send screen resize information to worker + * + * @param {boolean} [cache_clear=true] + * @return {void} + * @method + * @protected + */ +export const execute = (cache_clear: boolean = true): void => +{ + // postMessage + message.buffer = new Float32Array([ + $player.rendererWidth, + $player.rendererHeight, + cache_clear ? 1 : 0 + ]); + + options[0] = message.buffer.buffer; + + $rendererWorker.postMessage(message, options); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerSetCurrentMousePointService.ts b/packages/core/src/Player/service/PlayerSetCurrentMousePointService.ts new file mode 100644 index 00000000..e882e89d --- /dev/null +++ b/packages/core/src/Player/service/PlayerSetCurrentMousePointService.ts @@ -0,0 +1,42 @@ +import { $player } from "../../Player"; +import { stage } from "@next2d/display"; +import { + $getMainElement, + $devicePixelRatio +} from "../../CoreUtil"; + +/** + * @description 現在のマウスの位置を取得します。 + * Get the current mouse position. + * + * @param {PointerEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: PointerEvent): void => +{ + let x = window.scrollX; + let y = window.scrollY; + + const div: HTMLDivElement = $getMainElement(); + if (div) { + const rect = div.getBoundingClientRect(); + x += rect.left; + y += rect.top; + } + + const canvas = event.target as HTMLCanvasElement; + if (canvas) { + const rect = canvas.getBoundingClientRect(); + x += rect.left; + y += rect.top; + } + + const tx = ($player.rendererWidth - stage.stageWidth * $player.rendererScale) / 2; + const ty = ($player.rendererHeight - stage.stageHeight * $player.rendererScale) / 2; + + const scale = $player.rendererScale / $devicePixelRatio; + stage.pointer.x = (event.pageX - x) / scale - tx / $player.rendererScale; + stage.pointer.y = (event.pageY - y) / scale - ty / $player.rendererScale; +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerStopService.ts b/packages/core/src/Player/service/PlayerStopService.ts new file mode 100644 index 00000000..ae054c5e --- /dev/null +++ b/packages/core/src/Player/service/PlayerStopService.ts @@ -0,0 +1,22 @@ +import { $player } from "../../Player"; +import { SoundMixer } from "@next2d/media"; + +/** + * @description Playerの定期処理を停止 + * Stop the regular processing of the Player + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($player.timerId > -1) { + cancelAnimationFrame($player.timerId); + } + + $player.stopFlag = true; + $player.timerId = -1; + + SoundMixer.stopAll(); +}; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerTransferCanvasPostMessageService.ts b/packages/core/src/Player/service/PlayerTransferCanvasPostMessageService.ts new file mode 100644 index 00000000..42bf945d --- /dev/null +++ b/packages/core/src/Player/service/PlayerTransferCanvasPostMessageService.ts @@ -0,0 +1,106 @@ + +import type { ICaptureMessage } from "../../interface/ICaptureMessage"; +import type { DisplayObject } from "@next2d/display"; +import { stage } from "@next2d/display"; +import { renderQueue } from "@next2d/render-queue"; +import { $rendererWorker } from "../../RendererWorker"; + +/** + * @description キャプチャーメッセージ + * Capture message + * + * @type {object} + * @private + */ +const $message: ICaptureMessage = { + "command": "capture", + "buffer": null, + "width": 0, + "height": 0, + "length": 0, + "imageBitmaps": null +}; + +/** + * @description Transferableオブジェクト + * Transferable object + * + * @type {Transferable[]} + * @private + */ +const $options: Transferable[] = []; + +/** + * @type {ImageBitmap[]} + * @private + */ +const $imageBitmaps: ImageBitmap[] = []; + +/** + * @description レンダリングデータを生成してworkerに送る + * Generate rendering data and send it to the worker + * + * @param {D} display_object + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @return {Promise} + * @method + * @protected + */ +export const execute = async ( + display_object: D, + matrix: Float32Array, + color_transform: Float32Array, + transferred_canvas: HTMLCanvasElement +): Promise => { + + return await new Promise((resolve): void => + { + renderQueue.offset = 0; + $options.length = 0; + $imageBitmaps.length = 0; + + stage.$generateRenderQueue( + display_object, $imageBitmaps, matrix, color_transform + ); + + if (!renderQueue.offset) { + return resolve(transferred_canvas); + } + + // update buffer + $message.buffer = renderQueue.buffer; + $message.width = transferred_canvas.width; + $message.height = transferred_canvas.height; + $message.length = renderQueue.offset; + $options.push(renderQueue.buffer.buffer); + + // postMessage + $message.imageBitmaps = null; + if ($imageBitmaps.length) { + $message.imageBitmaps = $imageBitmaps; + $options.push(...$imageBitmaps); + } + + const drawCanvas = (event: MessageEvent): void => + { + if (event.data.message !== "capture") { + return ; + } + + const buffer = event.data.buffer; + if (renderQueue.buffer.length < buffer.length) { + renderQueue.buffer = buffer; + } + + const context = transferred_canvas.getContext("2d") as CanvasRenderingContext2D; + context.drawImage(event.data.imageBitmap, 0, 0); + + $rendererWorker.removeEventListener("message", drawCanvas); + return resolve(transferred_canvas); + }; + + $rendererWorker.addEventListener("message", drawCanvas); + $rendererWorker.postMessage($message, $options); + }); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerBootUseCase.ts b/packages/core/src/Player/usecase/PlayerBootUseCase.ts new file mode 100644 index 00000000..1fbaf946 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerBootUseCase.ts @@ -0,0 +1,40 @@ +import type { IPlayerOptions } from "../../interface/IPlayerOptions"; +import { $player } from "../../Player"; +import { execute as playerCreateContainerElementService } from "../service/PlayerCreateContainerElementService"; +import { execute as playerApplyContainerElementStyleService } from "../service/PlayerApplyContainerElementStyleService"; +import { execute as playerLoadingAnimationService } from "../service/PlayerLoadingAnimationService"; +import { execute as playerResizeEventService } from "./PlayerResizeEventUseCase"; +import { execute as playerResizeRegisterService } from "./PlayerResizeRegisterUseCase"; + +/** + * @description Playerの初期起動処理 + * Initial startup processing of Player + * + * @param {IPlayerOptions} [options=null] + * @return {void} + * @method + * @protected + */ +export const execute = (options: IPlayerOptions | null = null): void => +{ + $player.setOptions(options); + + // create element + const element = playerCreateContainerElementService(); + + // apply base style + playerApplyContainerElementStyleService( + element, $player.fixedWidth, $player.fixedHeight + ); + + // start loading + playerLoadingAnimationService(element); + + // register resize event + if (!$player.fixedWidth && !$player.fixedHeight) { + playerResizeRegisterService(); + } + + // initialize resize + playerResizeEventService(); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerHitTestUseCase.ts b/packages/core/src/Player/usecase/PlayerHitTestUseCase.ts new file mode 100644 index 00000000..db9f0545 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerHitTestUseCase.ts @@ -0,0 +1,48 @@ +import { $player } from "../../Player"; +import { stage } from "@next2d/display"; +import { + $hitContext, + $hitObject, + $hitMatrix, + $getCanvas +} from "../../CoreUtil"; + +/** + * @type {string} + * @private + */ +let $currentCursor: string = "auto"; + +/** + * @description Playerの当たり判定 + * Player hit test + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($player.stopFlag) { + return ; + } + + $hitObject.x = stage.pointer.x; + $hitObject.y = stage.pointer.y; + $hitObject.pointer = "auto"; + $hitObject.hit = null; + + // reset + $hitContext.beginPath(); + $hitContext.setTransform(1, 0, 0, 1, 0, 0); + + // ヒット判定 + stage.$mouseHit($hitContext, $hitMatrix, $hitObject); + + // カーソルの表示を更新 + if ($player.mouseState === "up" + && $currentCursor !== $hitObject.pointer + ) { + $getCanvas().style.cursor = $currentCursor = $hitObject.pointer; + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerPlayUseCase.ts b/packages/core/src/Player/usecase/PlayerPlayUseCase.ts new file mode 100644 index 00000000..3d030cfe --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerPlayUseCase.ts @@ -0,0 +1,33 @@ +import { $player } from "../../Player"; +import { stage } from "@next2d/display"; +import { execute as playerTickerUseCase } from "./PlayerTickerUseCase"; + +/** + * @description Playerの再生を開始 + * Start playing the Player + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if (!$player.stopFlag) { + return ; + } + + $player.stopFlag = false; + stage.changed = true; + + if ($player.timerId > -1) { + cancelAnimationFrame($player.timerId); + } + + $player.fps = 1000 / stage.frameRate | 0; + + $player.startTime = performance.now(); + $player.timerId = requestAnimationFrame((timestamp: number): void => + { + playerTickerUseCase(timestamp); + }); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerReadyCompleteUseCase.ts b/packages/core/src/Player/usecase/PlayerReadyCompleteUseCase.ts new file mode 100644 index 00000000..098f69f0 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerReadyCompleteUseCase.ts @@ -0,0 +1,20 @@ +import { stage } from "@next2d/display"; +import { $player } from "../../Player"; +import { execute as playerRenderingPostMessageService } from "../service/PlayerRenderingPostMessageService"; + +/** + * @description Playerの起動準備完了時のユースーケース + * Use case when Player is ready to start + */ +export const execute = (): void => +{ + // stage complete + stage.ready = true; + + // postMessage + playerRenderingPostMessageService(); + stage.changed = false; + + // run player + $player.play(); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerRegisterEventUseCase.ts b/packages/core/src/Player/usecase/PlayerRegisterEventUseCase.ts new file mode 100644 index 00000000..6ca6b44d --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerRegisterEventUseCase.ts @@ -0,0 +1,17 @@ +import { execute as playerKeyDownEventService } from "../service/PlayerKeyDownEventService"; +import { execute as playerKeyUpEventService } from "../service/PlayerKeyUpEventService"; +import { KeyboardEvent } from "@next2d/events"; + +/** + * @description キーボードイベントを登録する + * Register keyboard events + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + window.addEventListener(KeyboardEvent.KEY_DOWN, playerKeyDownEventService as EventListener); + window.addEventListener(KeyboardEvent.KEY_UP, playerKeyUpEventService as EventListener); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerResizeEventUseCase.ts b/packages/core/src/Player/usecase/PlayerResizeEventUseCase.ts new file mode 100644 index 00000000..6a9c93e3 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerResizeEventUseCase.ts @@ -0,0 +1,110 @@ +import { $player } from "../../Player"; +import { Event as ResizeEvent } from "@next2d/events"; +import { stage } from "@next2d/display"; +import { execute as playerResizePostMessageService } from "../service/PlayerResizePostMessageService"; +import { execute as canvasSetPositionService } from "../../Canvas/service/CanvasSetPositionService"; +import { $cacheStore } from "@next2d/cache"; +import { + $PREFIX, + $getMainElement, + $devicePixelRatio, + $renderMatrix +} from "../../CoreUtil"; + +/** + * @description 画面リサイズ時にcanvasのリサイズを行う + * Resize the canvas when resizing the screen + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const element: HTMLDivElement = $getMainElement(); + if (!element) { + return ; + } + + const parent: HTMLElement = element.parentElement as HTMLElement; + if (!parent) { + return ; + } + + const screenWidth = $player.fullScreen || parent.tagName === "BODY" + ? window.innerWidth + : parent.clientWidth; + + const screenHeight = $player.fullScreen || parent.tagName === "BODY" + ? window.innerHeight + : parent.clientHeight; + + const style = element.style; + style.width = `${screenWidth}px`; + style.height = `${screenHeight}px`; + + if (!stage.stageWidth || !stage.stageHeight) { + return ; + } + + const scale = Math.min( + screenWidth / stage.stageWidth, + screenHeight / stage.stageHeight + ) * $devicePixelRatio; + + const width = $player.fullScreen + ? window.innerWidth * $devicePixelRatio + : stage.stageWidth * scale | 0; + + const height = $player.fullScreen + ? window.innerHeight * $devicePixelRatio + : stage.stageHeight * scale | 0; + + // 同じサイズの場合は、ここれで終了 + if (width === $player.screenWidth + && height === $player.screenHeight + ) { + return ; + } + + // update + $player.screenWidth = screenWidth; + $player.screenHeight = screenHeight; + stage.changed = true; + + if (element.children.length > 1) { + element.children[1].dispatchEvent( + new Event(`${$PREFIX}_blur`) + ); + } + + // set canvas position + canvasSetPositionService(); + + if (width === $player.rendererWidth + && height === $player.rendererHeight + ) { + return ; + } + + // update + stage.rendererScale = $player.rendererScale = scale; + stage.rendererWidth = $player.rendererWidth = width; + stage.rendererHeight = $player.rendererHeight = height; + + // 描画用の matrix を更新 + $renderMatrix[0] = $renderMatrix[3] = scale; + $renderMatrix[4] = ($player.rendererWidth - stage.stageWidth * scale) / 2; + $renderMatrix[5] = ($player.rendererHeight - stage.stageHeight * scale) / 2; + + // cache clear + $cacheStore.reset(); + + // worker postMessage + playerResizePostMessageService(); + + // ステージのリサイズイベントを実行 + if (stage.hasEventListener(ResizeEvent.RESIZE)) { + stage.dispatchEvent(new ResizeEvent(ResizeEvent.RESIZE)); + } +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerResizeRegisterUseCase.ts b/packages/core/src/Player/usecase/PlayerResizeRegisterUseCase.ts new file mode 100644 index 00000000..764bbde9 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerResizeRegisterUseCase.ts @@ -0,0 +1,24 @@ +import { execute as playerResizeEventUseCase } from "./PlayerResizeEventUseCase"; + +/** + * @type {number} + * @private + */ +let timerId: number = -1; + +/** + * @description 画面リサイズのイベントを登録 + * Register screen resize event + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + window.addEventListener("resize", (): void => + { + cancelAnimationFrame(timerId); + timerId = requestAnimationFrame(playerResizeEventUseCase); + }); +}; \ No newline at end of file diff --git a/packages/core/src/Player/usecase/PlayerTickerUseCase.ts b/packages/core/src/Player/usecase/PlayerTickerUseCase.ts new file mode 100644 index 00000000..94963474 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerTickerUseCase.ts @@ -0,0 +1,61 @@ +import { $player } from "../../Player"; +import { stage } from "@next2d/display"; +import { Event } from "@next2d/events"; +import { $cacheStore } from "@next2d/cache"; +import { execute as playerRenderingPostMessageService } from "../service/PlayerRenderingPostMessageService"; +import { execute as playerRemoveCachePostMessageService } from "../service/PlayerRemoveCachePostMessageService"; + +/** + * @description Playerの定期処理 + * Regular processing of Player + * + * @param {number} timestamp + * @return {void} + * @method + * @protected + */ +export const execute = (timestamp: number): void => +{ + if ($player.stopFlag) { + return ; + } + + // キャッシュ削除 + if ($cacheStore.$removeIds.length) { + playerRemoveCachePostMessageService(); + } + + const time = timestamp - $player.startTime; + if (time > $player.fps) { + + $player.startTime = timestamp - time % $player.fps; + + // 定期処理 + stage.$ticker(); + + // enter frame event + if (stage.hasEventListener(Event.ENTER_FRAME)) { + stage.dispatchEvent(new Event(Event.ENTER_FRAME)); + } + + // 描画情報を生成してworkerに送る + if (stage.changed) { + playerRenderingPostMessageService(); + } + + // タイマーにセットされたキャッシュを削除 + if ($cacheStore.$removeCache) { + $cacheStore.removeTimerScheduledCache(); + // キャッシュ削除 + if ($cacheStore.$removeIds.length) { + playerRemoveCachePostMessageService(); + } + } + } + + // next frame + $player.timerId = requestAnimationFrame((timestamp: number): void => + { + execute(timestamp); + }); +}; \ No newline at end of file diff --git a/packages/core/src/RendererWorker.ts b/packages/core/src/RendererWorker.ts new file mode 100644 index 00000000..104a9626 --- /dev/null +++ b/packages/core/src/RendererWorker.ts @@ -0,0 +1,8 @@ +// @ts-ignore +import RendererWorker from "@next2d/renderer?worker&inline"; + +/** + * @type {Worker} + * @public + */ +export const $rendererWorker: Worker = new RendererWorker(); \ No newline at end of file diff --git a/packages/core/src/Text.ts b/packages/core/src/Text.ts index 56facf35..49313111 100644 --- a/packages/core/src/Text.ts +++ b/packages/core/src/Text.ts @@ -1,8 +1,12 @@ -import { TextImpl } from "@next2d/interface"; -import { TextFormat } from "@next2d/text"; +import type { IText } from "./interface/IText"; +import { + TextFormat, + TextField +} from "@next2d/text"; -const text: TextImpl = { - TextFormat +const text: IText = { + TextFormat, + TextField }; Object.entries(text).forEach(([key, TextClass]) => diff --git a/packages/core/src/UI.ts b/packages/core/src/UI.ts index f55ca260..0bac8339 100644 --- a/packages/core/src/UI.ts +++ b/packages/core/src/UI.ts @@ -1,13 +1,11 @@ -import { UIImpl } from "@next2d/interface"; +import type { IUI } from "./interface/IUI"; import { Easing, - Job, Tween } from "@next2d/ui"; -const ui: UIImpl = { +const ui: IUI = { Easing, - Job, Tween }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3b3647ac..675bb776 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,2 +1,3 @@ +export * from "./Canvas"; export * from "./Player"; export * from "./Next2D"; \ No newline at end of file diff --git a/packages/core/src/interface/ICaptureMessage.ts b/packages/core/src/interface/ICaptureMessage.ts new file mode 100644 index 00000000..cfd1cb3d --- /dev/null +++ b/packages/core/src/interface/ICaptureMessage.ts @@ -0,0 +1,8 @@ +export interface ICaptureMessage { + command: "capture"; + buffer: Float32Array | null; + width: number; + height: number; + length: number; + imageBitmaps: ImageBitmap[] | null; +} \ No newline at end of file diff --git a/packages/core/src/interface/ICaptureOptions.ts b/packages/core/src/interface/ICaptureOptions.ts new file mode 100644 index 00000000..71f10359 --- /dev/null +++ b/packages/core/src/interface/ICaptureOptions.ts @@ -0,0 +1,11 @@ +import type { + Matrix, + ColorTransform +} from "@next2d/geom"; + +export interface ICaptureOptions { + matrix?: Matrix; + colorTransform?: ColorTransform; + canvas?: HTMLCanvasElement; + videoSync?: boolean; +} \ No newline at end of file diff --git a/packages/core/src/interface/IDisplay.ts b/packages/core/src/interface/IDisplay.ts new file mode 100644 index 00000000..4d5c6bc0 --- /dev/null +++ b/packages/core/src/interface/IDisplay.ts @@ -0,0 +1,29 @@ +import type { + BitmapData, + BlendMode, + DisplayObject, + DisplayObjectContainer, + FrameLabel, + Graphics, + InteractiveObject, + Loader, + MovieClip, + Shape, + Sprite, + Stage +} from "@next2d/display"; + +export interface IDisplay { + BitmapData: typeof BitmapData; + BlendMode: typeof BlendMode; + DisplayObject: typeof DisplayObject; + DisplayObjectContainer: typeof DisplayObjectContainer; + FrameLabel: typeof FrameLabel; + Graphics: typeof Graphics; + InteractiveObject: typeof InteractiveObject; + Loader: typeof Loader; + MovieClip: typeof MovieClip; + Shape: typeof Shape; + Sprite: typeof Sprite; + stage: Stage; +} diff --git a/packages/core/src/interface/IDisplayObject.ts b/packages/core/src/interface/IDisplayObject.ts new file mode 100644 index 00000000..8276a4c9 --- /dev/null +++ b/packages/core/src/interface/IDisplayObject.ts @@ -0,0 +1,3 @@ +import type { DisplayObject } from "@next2d/display"; + +export type IDisplayObject = T; \ No newline at end of file diff --git a/packages/core/src/interface/IEvents.ts b/packages/core/src/interface/IEvents.ts new file mode 100644 index 00000000..832dfdd8 --- /dev/null +++ b/packages/core/src/interface/IEvents.ts @@ -0,0 +1,25 @@ +import type { + Event, + EventDispatcher, + EventPhase, + FocusEvent, + HTTPStatusEvent, + IOErrorEvent, + PointerEvent, + JobEvent, + ProgressEvent, + VideoEvent +} from "@next2d/events"; + +export interface IEvents { + Event: typeof Event; + EventDispatcher: typeof EventDispatcher; + EventPhase: typeof EventPhase; + FocusEvent: typeof FocusEvent; + HTTPStatusEvent: typeof HTTPStatusEvent; + IOErrorEvent: typeof IOErrorEvent; + PointerEvent: typeof PointerEvent; + JobEvent: typeof JobEvent; + ProgressEvent: typeof ProgressEvent; + VideoEvent: typeof VideoEvent; +} \ No newline at end of file diff --git a/packages/core/src/interface/IFilters.ts b/packages/core/src/interface/IFilters.ts new file mode 100644 index 00000000..d75bd17b --- /dev/null +++ b/packages/core/src/interface/IFilters.ts @@ -0,0 +1,23 @@ +import type { + BevelFilter, + BlurFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; + +export interface IFilters { + BevelFilter: typeof BevelFilter; + BlurFilter: typeof BlurFilter; + ColorMatrixFilter: typeof ColorMatrixFilter; + ConvolutionFilter: typeof ConvolutionFilter; + DisplacementMapFilter: typeof DisplacementMapFilter; + DropShadowFilter: typeof DropShadowFilter; + GlowFilter: typeof GlowFilter; + GradientBevelFilter: typeof GradientBevelFilter; + GradientGlowFilter: typeof GradientGlowFilter; +} \ No newline at end of file diff --git a/packages/core/src/interface/IGeom.ts b/packages/core/src/interface/IGeom.ts new file mode 100644 index 00000000..90a47626 --- /dev/null +++ b/packages/core/src/interface/IGeom.ts @@ -0,0 +1,13 @@ +import type { + ColorTransform, + Matrix, + Point, + Rectangle +} from "@next2d/geom"; + +export interface IGeom { + ColorTransform: typeof ColorTransform; + Matrix: typeof Matrix; + Point: typeof Point; + Rectangle: typeof Rectangle; +} \ No newline at end of file diff --git a/packages/core/src/interface/IMedia.ts b/packages/core/src/interface/IMedia.ts new file mode 100644 index 00000000..9aaac19c --- /dev/null +++ b/packages/core/src/interface/IMedia.ts @@ -0,0 +1,13 @@ +import type { + Sound, + SoundMixer, + SoundTransform, + Video +} from "@next2d/media"; + +export interface IMedia { + Sound: typeof Sound; + SoundMixer: typeof SoundMixer; + SoundTransform: typeof SoundTransform; + Video: typeof Video; +} \ No newline at end of file diff --git a/packages/core/src/interface/INet.ts b/packages/core/src/interface/INet.ts new file mode 100644 index 00000000..8c85a392 --- /dev/null +++ b/packages/core/src/interface/INet.ts @@ -0,0 +1,5 @@ +import type { URLRequest } from "@next2d/net"; + +export interface INet { + URLRequest: typeof URLRequest; +} \ No newline at end of file diff --git a/packages/core/src/interface/IPlayerHitObject.ts b/packages/core/src/interface/IPlayerHitObject.ts new file mode 100644 index 00000000..67b691fe --- /dev/null +++ b/packages/core/src/interface/IPlayerHitObject.ts @@ -0,0 +1,8 @@ +import type { IDisplayObject } from "./IDisplayObject"; + +export interface IPlayerHitObject { + x: number; + y: number; + pointer: string; + hit: IDisplayObject | null; +} \ No newline at end of file diff --git a/packages/core/src/interface/IPlayerOptions.ts b/packages/core/src/interface/IPlayerOptions.ts new file mode 100644 index 00000000..3a51384b --- /dev/null +++ b/packages/core/src/interface/IPlayerOptions.ts @@ -0,0 +1,7 @@ +export interface IPlayerOptions { + width?: number; + height?: number; + tagId?: string; + bgColor?: string; + fullScreen?: boolean; +} \ No newline at end of file diff --git a/packages/core/src/interface/IRemoveCacheMessage.ts b/packages/core/src/interface/IRemoveCacheMessage.ts new file mode 100644 index 00000000..89edf5de --- /dev/null +++ b/packages/core/src/interface/IRemoveCacheMessage.ts @@ -0,0 +1,4 @@ +export interface IRemoveCacheMessage { + command: "removeCache"; + buffer: Float32Array | null; +} \ No newline at end of file diff --git a/packages/core/src/interface/IRenderMessage.ts b/packages/core/src/interface/IRenderMessage.ts new file mode 100644 index 00000000..82c81a4a --- /dev/null +++ b/packages/core/src/interface/IRenderMessage.ts @@ -0,0 +1,6 @@ +export interface IRenderMessage { + command: "render"; + buffer: Float32Array | null; + length: number; + imageBitmaps: ImageBitmap[] | null; +} \ No newline at end of file diff --git a/packages/core/src/interface/IResizeMessage.ts b/packages/core/src/interface/IResizeMessage.ts new file mode 100644 index 00000000..0425da3d --- /dev/null +++ b/packages/core/src/interface/IResizeMessage.ts @@ -0,0 +1,4 @@ +export interface IResizeMessage { + command: "resize"; + buffer: Float32Array | null; +} \ No newline at end of file diff --git a/packages/core/src/interface/IStageData.ts b/packages/core/src/interface/IStageData.ts new file mode 100644 index 00000000..e6d87748 --- /dev/null +++ b/packages/core/src/interface/IStageData.ts @@ -0,0 +1,6 @@ +export interface IStageData { + width: number; + height: number; + fps: number + bgColor: string; +} \ No newline at end of file diff --git a/packages/core/src/interface/IText.ts b/packages/core/src/interface/IText.ts new file mode 100644 index 00000000..d2076c37 --- /dev/null +++ b/packages/core/src/interface/IText.ts @@ -0,0 +1,7 @@ +import type { TextFormat } from "@next2d/text"; +import type { TextField } from "@next2d/text"; + +export interface IText { + TextFormat: typeof TextFormat; + TextField: typeof TextField; +} \ No newline at end of file diff --git a/packages/core/src/interface/IUI.ts b/packages/core/src/interface/IUI.ts new file mode 100644 index 00000000..85395824 --- /dev/null +++ b/packages/core/src/interface/IUI.ts @@ -0,0 +1,9 @@ +import type { + Easing, + Tween +} from "@next2d/ui"; + +export interface IUI { + Easing: typeof Easing; + Tween: typeof Tween; +} \ No newline at end of file diff --git a/packages/display/package.json b/packages/display/package.json index 23b99b43..4b4ae78b 100644 --- a/packages/display/package.json +++ b/packages/display/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/display", "version": "*", - "description": "Next2D Display Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Display Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -32,16 +24,13 @@ "url": "git+https://github.com/Next2D/Player.git" }, "peerDependencies": { - "@next2d/interface": "file:../interface", - "@next2d/core": "file:../core", "@next2d/ui": "file:../ui", "@next2d/text": "file:../text", "@next2d/geom": "file:../geom", - "@next2d/util": "file:../util", - "@next2d/share": "file:../share", - "@next2d/webgl": "file:../webgl", "@next2d/media": "file:../media", "@next2d/net": "file:../net", - "@next2d/events": "file:../events" + "@next2d/events": "file:../events", + "@next2d/filters": "file:../filters", + "@next2d/render-queue": "file:../render-queue" } } diff --git a/packages/display/src/BitmapData.ts b/packages/display/src/BitmapData.ts index 59f81355..96cd6c65 100644 --- a/packages/display/src/BitmapData.ts +++ b/packages/display/src/BitmapData.ts @@ -1,53 +1,45 @@ -import { DisplayObjectContainer } from "./DisplayObjectContainer"; -import type { Player } from "@next2d/core"; -import type { CanvasToWebGLContext } from "@next2d/webgl"; -import type { - DisplayObjectImpl, - PropertyBitmapDataMessageImpl -} from "@next2d/interface"; -import type { - Matrix, - ColorTransform -} from "@next2d/geom"; -import { - $COLOR_ARRAY_IDENTITY, - $getArray, - $MATRIX_ARRAY_IDENTITY, - $multiplicationMatrix, - $poolArray, - $cacheStore -} from "@next2d/share"; -import { - $getInstanceId, - $bitmapDrawMap, - $currentPlayer, - $poolColorTransform, - $poolMatrix, - $postContainerWorker, - $rendererWorker -} from "@next2d/util"; +import { execute as bitmapDataImageToBufferService } from "./BitmapData/service/BitmapDataImageToBufferService"; +import { execute as bitmapDataCanvasToBufferService } from "./BitmapData/service/BitmapDataCanvasToBufferService"; /** - * BitmapData クラスを使用すると、Bitmap オブジェクトのデータ (ピクセル) を処理できます。 - * BitmapData クラスのメソッドを使用して、任意のサイズの透明または不透明のビットマップイメージを作成し - * 実行時に様々な方法で操作できます。 + * @description BitmapData クラスを使用すると、Bitmap オブジェクトのデータ (ピクセル) を処理できます。 + * BitmapData クラスのメソッドを使用して、任意のサイズの透明または不透明のビットマップイメージを作成し + * 実行時に様々な方法で操作できます。 * - * The BitmapData class lets you work with the data (pixels) of a Bitmap object. - * You can use the methods of the BitmapData class to create arbitrarily sized transparent or - * opaque bitmap images and manipulate them in various ways at runtime. + * The BitmapData class lets you work with the data (pixels) of a Bitmap object. + * You can use the methods of the BitmapData class to create arbitrarily sized transparent or + * opaque bitmap images and manipulate them in various ways at runtime. * * @class * @memberOf next2d.display */ export class BitmapData { - private readonly _$instanceId: number; - private _$width: number; - private _$height: number; - _$buffer: Uint8Array | null; - private _$image: HTMLImageElement | null; - private _$canvas: HTMLCanvasElement | null; - private _$texture: WebGLTexture | null; + /** + * @description ビットマップイメージの幅(ピクセル単位)です。 + * The width of the bitmap image in pixels. + * + * @return {number} + * @readonly + * @public + */ + public width: number; + + /** + * @description ビットマップイメージの高さ(ピクセル単位)です。 + * The height of the bitmap image in pixels. + * + * @return {number} + * @readonly + * @public + */ + public height: number; + + /** + * @type {Uint8Array | null} + * @private + */ + public buffer: Uint8Array | null; /** * @param {number} [width=0] @@ -57,246 +49,68 @@ export class BitmapData */ constructor (width: number = 0, height: number = 0) { - - /** - * @type {number} - * @private - */ - this._$instanceId = $getInstanceId(); - /** * @type {number} * @default 0 * @private */ - this._$width = width | 0; + this.width = width | 0; /** * @type {number} * @default 0 * @private */ - this._$height = height | 0; + this.height = height | 0; /** * @type {Uint8Array} * @default null * @private */ - this._$buffer = null; - - /** - * @type {HTMLImageElement} - * @default null - * @private - */ - this._$image = null; - - /** - * @type {HTMLCanvasElement} - * @default null - * @private - */ - this._$canvas = null; - - /** - * @type {WebGLTexture} - * @type {null} - * @private - */ - this._$texture = null; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class BitmapData] - * @method - * @static - */ - static toString (): string - { - return "[class BitmapData]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.BitmapData - * @const - * @static - */ - static get namespace (): string - { - return "next2d.display.BitmapData"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object BitmapData] - * @method - * @public - */ - toString (): string - { - return "[object BitmapData]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.BitmapData - * @const - * @public - */ - get namespace (): string - { - return "next2d.display.BitmapData"; - } - - /** - * @description ビットマップイメージのID - * Bitmap image ID. - * - * @return {number} - * @readonly - * @public - */ - get instanceId (): number - { - return this._$instanceId; - } - - /** - * @description ビットマップイメージの高さ(ピクセル単位)です。 - * The height of the bitmap image in pixels. - * - * @return {number} - * @readonly - * @public - */ - get height (): number - { - return this._$height; - } - - /** - * @description Uint8Arrayを利用して BitmapData を生成します。 - * Generates BitmapData using Uint8Array. - * - * @return {Uint8Array} - * @public - */ - get buffer (): Uint8Array | null - { - return this._$buffer; - } - set buffer (buffer: Uint8Array | null) - { - this._$canvas = null; - this._$image = null; - this._$buffer = buffer; - - if (this._$texture) { - const player: Player = $currentPlayer(); - const context: CanvasToWebGLContext | null = player.context; - if (context) { - context.frameBuffer.releaseTexture(this._$texture); - } - - this._$texture = null; - } + this.buffer = null; } /** * @description Imageクラスを利用して BitmapData を生成します。 * Use the Image class to generate BitmapData. * - * @return {HTMLImageElement} + * @writeonly * @public */ - get image (): HTMLImageElement|null - { - return this._$image; - } - set image (image: HTMLImageElement|null) + set image (image: HTMLImageElement | null) { - this._$canvas = null; - this._$buffer = null; - this._$image = image; - - if (this._$texture) { - const player: Player = $currentPlayer(); - const context: CanvasToWebGLContext | null = player.context; - if (context) { - context.frameBuffer.releaseTexture(this._$texture); - } - - this._$texture = null; - } - if (!image) { + this.width = 0; + this.height = 0; + this.buffer = null; return ; } - this._$width = image.width; - this._$height = image.height; + this.buffer = bitmapDataImageToBufferService(image); + this.width = image.width; + this.height = image.height; } /** * @description Canvasクラス利用して BitmapData を生成します。 * Use the Canvas class to generate BitmapData. * - * @return {HTMLCanvasElement} + * @writeonly * @public */ - get canvas (): HTMLCanvasElement|null - { - return this._$canvas; - } - set canvas (canvas: HTMLCanvasElement|null) + set canvas (canvas: HTMLCanvasElement | null) { - this._$image = null; - this._$buffer = null; - this._$canvas = canvas; - - if (this._$texture) { - const player: Player = $currentPlayer(); - const context: CanvasToWebGLContext | null = player.context; - if (context) { - context.frameBuffer.releaseTexture(this._$texture); - } - this._$texture = null; - } - if (!canvas) { + this.width = 0; + this.height = 0; + this.buffer = null; return ; } - this._$width = canvas.width; - this._$height = canvas.height; - } - - /** - * @description ビットマップイメージの幅(ピクセル単位)です。 - * The width of the bitmap image in pixels. - * - * @return {number} - * @readonly - * @public - */ - get width (): number - { - return this._$width; + this.buffer = bitmapDataCanvasToBufferService(canvas); + this.width = canvas.width; + this.height = canvas.height; } /** @@ -311,244 +125,10 @@ export class BitmapData */ clone (): BitmapData { - const bitmapData: BitmapData = new BitmapData(this.width, this.height); - if (this._$image !== null || this._$canvas !== null) { - - const canvas: HTMLCanvasElement = $cacheStore.getCanvas(); - canvas.width = this.width; - canvas.height = this.height; - - const context: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!context) { - throw new Error("the context is null."); - } - - if (this._$image) { - context.drawImage(this._$image, 0, 0); - } - - if (this._$canvas) { - context.drawImage(this._$canvas, 0, 0); - } - - bitmapData.canvas = canvas; - - } else if (this._$buffer !== null) { - - bitmapData._$buffer = this._$buffer.slice(); - + const bitmapData = new BitmapData(this.width, this.height); + if (this.buffer !== null) { + bitmapData.buffer = this.buffer.slice(); } - return bitmapData; } - - /** - * @member {WebGLTexture} - * @method - * @private - */ - getTexture (): WebGLTexture | null - { - const { width, height } = this; - if (!width || !height) { - return null; - } - - const player: Player = $currentPlayer(); - const context: CanvasToWebGLContext | null = player.context; - if (!context) { - throw new Error("the context is null."); - } - - if (this._$texture !== null) { - return this._$texture; - } - - if (this._$image !== null) { - this._$texture = context - .frameBuffer - .createTextureFromImage(this._$image); - } - - if (this._$canvas !== null) { - this._$texture = context - .frameBuffer - .createTextureFromCanvas(this._$canvas); - } - - if (this._$buffer !== null) { - this._$texture = context - .frameBuffer - .createTextureFromPixels( - width, height, this._$buffer, true - ); - } - - return this._$texture; - } - - /** - * @param {DisplayObject} source - * @param {Matrix} [matrix=null] - * @param {ColorTransform} [color_transform=null] - * @param {HTMLCanvasElement} [canvas=null] - * @param {function} [callback=null] - * @return {void} - * @public - */ - draw ( - source: DisplayObjectImpl, - matrix: Matrix | null = null, - color_transform: ColorTransform | null = null, - canvas: HTMLCanvasElement | null = null, - callback: Function | null = null - ): void { - - const { width, height } = this; - if (!width || !height) { - return ; - } - - const player: Player = $currentPlayer(); - const cacheWidth: number = player._$width; - const cacheHeight: number = player._$height; - const resize: boolean = width > cacheWidth || height > cacheHeight; - if (resize) { - player._$width = width; - player._$height = height; - player._$resizeCanvas(width, height, player.scaleX); - } - - const colorTransform: Float32Array = color_transform - ? color_transform._$colorTransform - : $COLOR_ARRAY_IDENTITY; - - let tMatrix: Float32Array = matrix - ? matrix._$matrix - : $MATRIX_ARRAY_IDENTITY; - - if (matrix) { - - const matrix: Matrix = source._$transform.matrix; - matrix.invert(); - - tMatrix = $multiplicationMatrix( - tMatrix, matrix._$matrix - ); - - $poolMatrix(matrix); - } - - if (!canvas) { - canvas = $cacheStore.getCanvas(); - } - - if ($rendererWorker) { - - if (!source._$stage) { - if (source instanceof DisplayObjectContainer) { - - if ($postContainerWorker) { - $postContainerWorker(source); - } - - } else { - - source._$createWorkerInstance(); - source._$postProperty(); - - } - } - - canvas.width = width; - canvas.height = height; - const context: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!context) { - throw new Error("the context is null."); - } - - context.setTransform(1, 0, 0, 1, 0, 0); - context.clearRect(0, 0, width, height); - - const instanceId: number = source._$instanceId; - $bitmapDrawMap.set(instanceId, { - "source": source, - "context": context, - "callback": callback - }); - - const options: ArrayBuffer[] = $getArray(); - const message: PropertyBitmapDataMessageImpl = { - "command": "bitmapDraw", - "sourceId": instanceId, - "width": width, - "height": height - }; - - if (tMatrix[0] !== 1 || tMatrix[1] !== 0 - || tMatrix[2] !== 0 || tMatrix[3] !== 1 - || tMatrix[4] !== 0 || tMatrix[5] !== 0 - ) { - message.matrix = tMatrix.slice(); - options.push(message.matrix.buffer); - } - - if (colorTransform[0] !== 1 || colorTransform[1] !== 1 - || colorTransform[2] !== 1 || colorTransform[3] !== 1 - || colorTransform[4] !== 0 || colorTransform[5] !== 0 - || colorTransform[6] !== 0 || colorTransform[7] !== 0 - ) { - message.colorTransform = colorTransform.slice(); - options.push(message.colorTransform.buffer); - } - - $rendererWorker.postMessage(message, options); - - $poolArray(options); - - } else { - - const context: CanvasToWebGLContext | null = player.context; - if (!context) { - throw new Error("the context is null."); - } - - // reset - context.reset(); - context.setTransform(1, 0, 0, 1, 0, 0); - context._$setColor(0, 0, 0, 0); - context.clearRect(0, 0, player._$width, player._$height); - context.beginPath(); - - source._$draw(context, tMatrix, colorTransform); - - context.drawInstacedArray(); - context - .frameBuffer - .transferToMainTexture(); - - canvas.width = width; - canvas.height = height; - const ctx: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!ctx) { - return ; - } - - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, width, height); - ctx.drawImage(player.canvas, 0, 0); - - if (callback) { - callback(canvas); - } - } - - if (matrix) { - $poolMatrix(matrix); - } - - if (color_transform) { - $poolColorTransform(color_transform); - } - } -} +} \ No newline at end of file diff --git a/packages/display/src/BitmapData/service/BitmapDataCanvasToBufferService.ts b/packages/display/src/BitmapData/service/BitmapDataCanvasToBufferService.ts new file mode 100644 index 00000000..0da8fd4b --- /dev/null +++ b/packages/display/src/BitmapData/service/BitmapDataCanvasToBufferService.ts @@ -0,0 +1,28 @@ +import { $cacheStore } from "@next2d/cache"; + +/** + * @description 画像をバッファに変換します + * Convert image to buffer + * + * @param {HTMLCanvasElement} canvas + * @return {Uint8Array} + * @method + * @protected + */ +export const execute = (canvas: HTMLCanvasElement): Uint8Array => +{ + const cloneCanvas = $cacheStore.getCanvas(); + cloneCanvas.width = canvas.width; + cloneCanvas.height = canvas.height; + + const context = cloneCanvas.getContext("2d") as CanvasRenderingContext2D; + context.setTransform(1, 0, 0, 1, 0, 0); + context.drawImage(canvas, 0, 0); + + const imageData = context.getImageData(0, 0, canvas.width, canvas.height); + const buffer = new Uint8Array(imageData.data.buffer); + + $cacheStore.destroy(context); + + return buffer; +}; \ No newline at end of file diff --git a/packages/display/src/BitmapData/service/BitmapDataImageToBufferService.ts b/packages/display/src/BitmapData/service/BitmapDataImageToBufferService.ts new file mode 100644 index 00000000..e4c5f2f4 --- /dev/null +++ b/packages/display/src/BitmapData/service/BitmapDataImageToBufferService.ts @@ -0,0 +1,27 @@ +import { $cacheStore } from "@next2d/cache"; + +/** + * @description 画像をバッファに変換します + * Convert image to buffer + * + * @param {HTMLImageElement} image + * @return {Uint8Array} + * @method + * @protected + */ +export const execute = (image: HTMLImageElement): Uint8Array => +{ + const canvas = $cacheStore.getCanvas(); + canvas.width = image.width; + canvas.height = image.height; + + const context = canvas.getContext("2d", { "willReadFrequently": true }) as CanvasRenderingContext2D; + context.drawImage(image, 0, 0); + + const imageData = context.getImageData(0, 0, image.width, image.height); + const buffer = new Uint8Array(imageData.data.buffer); + + $cacheStore.destroy(context); + + return buffer; +}; \ No newline at end of file diff --git a/packages/display/src/BlendMode.test.ts b/packages/display/src/BlendMode.test.ts new file mode 100644 index 00000000..89b41fb3 --- /dev/null +++ b/packages/display/src/BlendMode.test.ts @@ -0,0 +1,75 @@ +import { BlendMode } from "./BlendMode"; +import { describe, expect, it } from "vitest"; + +describe("BlendMode.js property test", () => +{ + it("ADD test", () => + { + expect(BlendMode.ADD).toBe("add"); + }); + + it("ALPHA test", () => + { + expect(BlendMode.ALPHA).toBe("alpha"); + }); + + it("DARKEN test", () => + { + expect(BlendMode.DARKEN).toBe("darken"); + }); + + it("DIFFERENCE test", () => + { + expect(BlendMode.DIFFERENCE).toBe("difference"); + }); + + it("ERASE test", () => + { + expect(BlendMode.ERASE).toBe("erase"); + }); + + it("HARDLIGHT test", () => + { + expect(BlendMode.HARDLIGHT).toBe("hardlight"); + }); + + it("INVERT test", () => + { + expect(BlendMode.INVERT).toBe("invert"); + }); + + it("LAYER test", () => + { + expect(BlendMode.LAYER).toBe("layer"); + }); + + it("LIGHTEN test", () => + { + expect(BlendMode.LIGHTEN).toBe("lighten"); + }); + + it("MULTIPLY test", () => + { + expect(BlendMode.MULTIPLY).toBe("multiply"); + }); + + it("NORMAL test", () => + { + expect(BlendMode.NORMAL).toBe("normal"); + }); + + it("OVERLAY test", () => + { + expect(BlendMode.OVERLAY).toBe("overlay"); + }); + + it("SCREEN test", () => + { + expect(BlendMode.SCREEN).toBe("screen"); + }); + + it("SUBTRACT test", () => + { + expect(BlendMode.SUBTRACT).toBe("subtract"); + }); +}); \ No newline at end of file diff --git a/packages/display/src/BlendMode.ts b/packages/display/src/BlendMode.ts index dc2effa9..8d9fccc1 100644 --- a/packages/display/src/BlendMode.ts +++ b/packages/display/src/BlendMode.ts @@ -1,77 +1,14 @@ /** - * ブレンドモードの視覚効果のために定数値を提供するクラスです。 - * 全てのDisplayObjectに設定が可能です。 - * A class that provides constant values for visual blend mode effects. - * It can be set for all DisplayObjects. - * - * @example usage of BlendMode. - * // static BlendMode - * const {BlendMode, MovieClip} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.blendMode = BlendMode.ADD; + * @description ブレンドモードの視覚効果のために定数値を提供するクラスです。 + * 全てのDisplayObjectに設定が可能です。 + * A class that provides constant values for visual blend mode effects. + * It can be set for all DisplayObjects. * * @class * @memberOf next2d.display */ export class BlendMode { - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class BlendMode] - * @method - * @static - */ - static toString () - { - return "[class BlendMode]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.BlendMode - * @const - * @static - */ - static get namespace () - { - return "next2d.display.BlendMode"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object BlendMode] - * @method - * @public - */ - toString () - { - return "[object BlendMode]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.BlendMode - * @const - * @public - */ - get namespace () - { - return "next2d.display.BlendMode"; - } - /** * @description 表示オブジェクトの要素カラーの値を背景色に加算し、その際に上限 0xFF を適用します。 * Adds the values of the constituent colors of the display object @@ -82,7 +19,7 @@ export class BlendMode * @const * @static */ - static get ADD () + static get ADD (): string { return "add"; } @@ -96,7 +33,7 @@ export class BlendMode * @const * @static */ - static get ALPHA () + static get ALPHA (): string { return "alpha"; } @@ -111,7 +48,7 @@ export class BlendMode * @const * @static */ - static get DARKEN () + static get DARKEN (): string { return "darken"; } @@ -126,7 +63,7 @@ export class BlendMode * @const * @static */ - static get DIFFERENCE () + static get DIFFERENCE (): string { return "difference"; } @@ -140,7 +77,7 @@ export class BlendMode * @const * @static */ - static get ERASE () + static get ERASE (): string { return "erase"; } @@ -154,7 +91,7 @@ export class BlendMode * @const * @static */ - static get HARDLIGHT () + static get HARDLIGHT (): string { return "hardlight"; } @@ -168,7 +105,7 @@ export class BlendMode * @const * @static */ - static get INVERT () + static get INVERT (): string { return "invert"; } @@ -182,7 +119,7 @@ export class BlendMode * @const * @static */ - static get LAYER () + static get LAYER (): string { return "layer"; } @@ -197,7 +134,7 @@ export class BlendMode * @const * @static */ - static get LIGHTEN () + static get LIGHTEN (): string { return "lighten"; } @@ -212,7 +149,7 @@ export class BlendMode * @const * @static */ - static get MULTIPLY () + static get MULTIPLY (): string { return "multiply"; } @@ -226,7 +163,7 @@ export class BlendMode * @const * @static */ - static get NORMAL () + static get NORMAL (): string { return "normal"; } @@ -240,7 +177,7 @@ export class BlendMode * @const * @static */ - static get OVERLAY () + static get OVERLAY (): string { return "overlay"; } @@ -255,7 +192,7 @@ export class BlendMode * @const * @static */ - static get SCREEN () + static get SCREEN (): string { return "screen"; } @@ -270,8 +207,8 @@ export class BlendMode * @const * @static */ - static get SUBTRACT () + static get SUBTRACT (): string { return "subtract"; } -} +} \ No newline at end of file diff --git a/packages/display/src/DisplayObject.ts b/packages/display/src/DisplayObject.ts index 9983220a..472e396c 100644 --- a/packages/display/src/DisplayObject.ts +++ b/packages/display/src/DisplayObject.ts @@ -1,83 +1,67 @@ -import type { Stage } from "./Stage"; import type { LoaderInfo } from "./LoaderInfo"; import type { Sprite } from "./Sprite"; -import { - Event as Next2DEvent, - EventDispatcher -} from "@next2d/events"; -import { - Transform, - Rectangle, - Point, +import type { IParent } from "./interface/IParent"; +import type { IPlaceObject } from "./interface/IPlaceObject"; +import type { IBlendMode } from "./interface/IBlendMode"; +import type { IFilterArray } from "./interface/IFilterArray"; +import type { MovieClip } from "./MovieClip"; +import type { ISprite } from "./interface/ISprite"; +import type { ColorTransform, - Matrix + Matrix, + Rectangle, + Point } from "@next2d/geom"; -import type { - FilterArrayImpl, - BlendModeImpl, - ParentImpl, - PlaceObjectImpl, - BoundsImpl, - DictionaryTagImpl, - PropertyMessageMapImpl, - DisplayObjectImpl, - AttachmentImpl, - PropertyMessageImpl, - CachePositionImpl -} from "@next2d/interface"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; +import { EventDispatcher } from "@next2d/events"; +import { execute as displayObjectApplyChangesService } from "./DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as displayObjectConcatenatedMatrixUseCase } from "./DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase"; +import { execute as displayObjectGetAlphaUseCase } from "./DisplayObject/usecase/DisplayObjectGetAlphaUseCase"; +import { execute as displayObjectSetAlphaUseCase } from "./DisplayObject/usecase/DisplayObjectSetAlphaUseCase"; +import { execute as displayObjectGetFiltersUseCase } from "./DisplayObject/usecase/DisplayObjectGetFiltersUseCase"; +import { execute as displayObjectSetFiltersUseCase } from "./DisplayObject/usecase/DisplayObjectSetFiltersUseCase"; +import { execute as displayObjectGetBlendModeUseCase } from "./DisplayObject/usecase/DisplayObjectGetBlendModeUseCase"; +import { execute as displayObjectSetBlendModeUseCase } from "./DisplayObject/usecase/DisplayObjectSetBlendModeUseCase"; +import { execute as displayObjectGetRotationUseCase } from "./DisplayObject/usecase/DisplayObjectGetRotationUseCase"; +import { execute as displayObjectSetRotationUseCase } from "./DisplayObject/usecase/DisplayObjectSetRotationUseCase"; +import { execute as displayObjectGetScaleXUseCase } from "./DisplayObject/usecase/DisplayObjectGetScaleXUseCase"; +import { execute as displayObjectSetScaleXUseCase } from "./DisplayObject/usecase/DisplayObjectSetScaleXUseCase"; +import { execute as displayObjectGetScaleYUseCase } from "./DisplayObject/usecase/DisplayObjectGetScaleYUseCase"; +import { execute as displayObjectSetScaleYUseCase } from "./DisplayObject/usecase/DisplayObjectSetScaleYUseCase"; +import { execute as displayObjectGetXUseCase } from "./DisplayObject/usecase/DisplayObjectGetXUseCase"; +import { execute as displayObjectSetXUseCase } from "./DisplayObject/usecase/DisplayObjectSetXUseCase"; +import { execute as displayObjectGetYUseCase } from "./DisplayObject/usecase/DisplayObjectGetYUseCase"; +import { execute as displayObjectSetYUseCase } from "./DisplayObject/usecase/DisplayObjectSetYUseCase"; +import { execute as displayObjectGetWidthUseCase } from "./DisplayObject/usecase/DisplayObjectGetWidthUseCase"; +import { execute as displayObjectSetWidthUseCase } from "./DisplayObject/usecase/DisplayObjectSetWidthUseCase"; +import { execute as displayObjectLocalToGlobalService } from "./DisplayObject/service/DisplayObjectLocalToGlobalService"; +import { execute as displayObjectGlobalToLocalService } from "./DisplayObject/service/DisplayObjectGlobalToLocalService"; +import { execute as displayObjectGetHeightUseCase } from "./DisplayObject/usecase/DisplayObjectGetHeightUseCase"; +import { execute as displayObjectSetHeightUseCase } from "./DisplayObject/usecase/DisplayObjectSetHeightUseCase"; +import { execute as displayObjectRemoveService } from "./DisplayObject/service/DisplayObjectRemoveService"; +import { execute as displayObjectGetBoundsUseCase } from "./DisplayObject/usecase/DisplayObjectGetBoundsUseCase"; +import { execute as displayObjectHitTestObjectUseCase } from "./DisplayObject/usecase/DisplayObjectHitTestObjectUseCase"; +import { execute as displayObjectHitTestPointUseCase } from "./DisplayObject/usecase/DisplayObjectHitTestPointUseCase"; +import { execute as displayObjectGetMatrixUseCase } from "./DisplayObject/usecase/DisplayObjectGetMatrixUseCase"; +import { execute as displayObjectGetColorTransformUseCase } from "./DisplayObject/usecase/DisplayObjectGetColorTransformUseCase"; import { - $getEvent, $getInstanceId, - $currentMousePoint, - $poolColorTransform, - $rendererWorker, - $poolMatrix, - $hitContext, + $loaderInfoMap, + $rootMap, $variables, - $blendToNumber -} from "@next2d/util"; -import { - $doUpdated, - $clamp, - $getArray, - $boundsMatrix, - $Math, - $poolBoundsObject, - $Infinity, - $getBoundsObject, - $isNaN, - $Deg2Rad, - $Number, - $Rad2Deg, - $SHORT_INT_MIN, - $SHORT_INT_MAX, - $MATRIX_ARRAY_IDENTITY, - $multiplicationMatrix, - $poolFloat32Array6, - $getMap, - $poolMap, - $getFloat32Array6, - $devicePixelRatio, - $poolArray, - $cacheStore -} from "@next2d/share"; + $getDraggingDisplayObject, + $pointer +} from "./DisplayObjectUtil"; /** - * DisplayObject クラスは、表示リストに含めることのできるすべてのオブジェクトに関する基本クラスです。 - * DisplayObject クラス自体は、画面上でのコンテンツの描画のための API を含みません。 - * そのため、DisplayObject クラスのカスタムサブクラスを作成する場合は、 - * Shape、Sprite、Bitmap、TextField または MovieClip など、 - * 画面上にコンテンツを描画する API を持つサブクラスの 1 つを拡張する必要があります。 + * @description DisplayObject クラスは、表示リストに含めることのできるすべてのオブジェクトに関する基本クラスです。 + * DisplayObject クラス自体は、画面上でのコンテンツの描画のための API を含みません。 + * そのため、DisplayObject クラスのカスタムサブクラスを作成する場合は、 + * Shape、Sprite、TextField または MovieClip など、画面上にコンテンツを描画する API を持つサブクラスの 1 つを拡張する必要があります。 * - * The DisplayObject class is the base class for all objects that can be placed on the display list. - * The DisplayObject class itself does not include any APIs for rendering content onscreen. - * For that reason, if you want create a custom subclass of the DisplayObject class, - * you will want to extend one of its subclasses that do have APIs for rendering content onscreen, - * such as the Shape, Sprite, Bitmap, TextField, or MovieClip class. + * The DisplayObject class is the base class for all objects that can be placed on the display list. + * The DisplayObject class itself does not include any APIs for rendering content onscreen. + * For that reason, if you want create a custom subclass of the DisplayObject class, + * you will want to extend one of its subclasses that do have APIs for rendering content onscreen, such as the Shape, Sprite, TextField, or MovieClip class. * * @class * @memberOf next2d.display @@ -85,2348 +69,1003 @@ import { */ export class DisplayObject extends EventDispatcher { - public readonly _$instanceId: number; - protected _$id: number; - protected _$stage: Stage | null; - protected _$parent: ParentImpl | null; - protected _$scale9Grid: Rectangle | null; - protected _$characterId: number; - protected _$active: boolean; - protected _$isMask: boolean; - public _$updated: boolean; - protected _$added: boolean; - protected _$addedStage: boolean; - protected _$filters: FilterArrayImpl | null; - protected _$blendMode: BlendModeImpl | null; - protected _$transform: Transform; - public _$hitObject: Sprite | null; - protected _$isNext: boolean; - protected _$created: boolean; - protected _$posted: boolean; - protected _$clipDepth: number; - protected _$name: string; - protected _$mask: DisplayObjectImpl | null; - protected _$visible: boolean; - protected _$root: ParentImpl | null; - public _$loaderInfo: LoaderInfo | null; - protected _$scaleX: number | null; - protected _$scaleY: number | null; - protected _$variables: Map | null; - protected _$placeObject: PlaceObjectImpl | null; - protected _$rotation: number | null; - protected _$changePlace: boolean; - protected _$currentPlaceId: number; - protected _$placeId: number; - protected _$startFrame: number; - protected _$endFrame: number; - protected _$postArray: Float32Array | null; - /** - * @constructor + * @description DisplayObject のユニークなインスタンスID + * Unique instance ID of DisplayObject + * + * @type {number} + * @readonly * @public */ - constructor () - { - super(); - - /** - * @type {number} - * @private - */ - this._$id = -1; - - /** - * @type {number} - * @private - */ - this._$instanceId = $getInstanceId(); - - /** - * @type {number} - * @private - */ - this._$characterId = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$active = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isMask = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$updated = true; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$added = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$addedStage = false; - - /** - * @type {array|null} - * @default null - * @private - */ - this._$filters = null; - - /** - * @type {string|null} - * @default null - * @private - */ - this._$blendMode = null; - - /** - * @type {Sprite|null} - * @default null - * @private - */ - this._$hitObject = null; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$isNext = true; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$created = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$posted = false; - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$postArray = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$clipDepth = 0; - - /** - * @type {string} - * @default "" - * @private - */ - this._$name = ""; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$visible = true; - - /** - * @type {DisplayObject|null} - * @default null - * @private - */ - this._$mask = null; - - /** - * @type {Rectangle|null} - * @default null - * @private - */ - this._$scale9Grid = null; - - /** - * @type {Sprite | null} - * @default null - * @private - */ - this._$parent = null; - - /** - * @type {Stage|null} - * @default null - * @private - */ - this._$stage = null; - - /** - * @type {Sprite|null} - * @default null - * @private - */ - this._$root = null; - - /** - * @type {number|null} - * @default null - * @private - */ - this._$loaderInfo = null; - - /** - * @type {number|null} - * @default null - * @private - */ - this._$placeId = -1; - - /** - * @type {number} - * @default null - * @private - */ - this._$startFrame = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$endFrame = 0; - - /** - * @type {Transform} - * @private - */ - this._$transform = new Transform(this); - - /** - * @type {Map} - * @default null - * @private - */ - this._$variables = null; - - /** - * @type {object} - * @default null - * @private - */ - this._$placeObject = null; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$currentPlaceId = -1; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$changePlace = false; - - /** - * @type {number} - * @default null - * @private - */ - this._$scaleX = null; - - /** - * @type {number} - * @default null - * @private - */ - this._$scaleY = null; - - /** - * @type {number} - * @default null - * @private - */ - this._$rotation = null; - } + public readonly instanceId: number; /** - * @description 指定されたオブジェクトのアルファ透明度値を示します。 - * 有効な値は 0.0(完全な透明)~ 1.0(完全な不透明)です。 - * デフォルト値は 1.0 です。alpha が 0.0 に設定されている表示オブジェクトは、 - * 表示されない場合でも、アクティブです。 - * Indicates the alpha transparency value of the object specified. - * Valid values are 0.0 (fully transparent) to 1.0 (fully opaque). - * The default value is 1.0. Display objects with alpha set to 0.0 are active, - * even though they are invisible. + * @description DisplayObject の生成元ID + * Source ID of DisplayObject * - * @member {number} - * @default 1 + * @type {number} + * @default -1 * @public */ - get alpha (): number - { - const colorTransform: Float32Array = this - ._$transform - ._$rawColorTransform(); - - return colorTransform[3] + colorTransform[7] / 255; - } - set alpha (alpha: number) - { - alpha = $clamp(alpha, 0, 1, 0); - - const transform: Transform = this._$transform; - - // clone - if (!transform._$colorTransform) { - const colorTransform: ColorTransform = transform.colorTransform; - - colorTransform._$colorTransform[3] = alpha; - colorTransform._$colorTransform[7] = 0; - - transform.colorTransform = colorTransform; - $poolColorTransform(colorTransform); - - } else { - const colorTransform: ColorTransform = transform._$colorTransform; - - colorTransform._$colorTransform[3] = alpha; - colorTransform._$colorTransform[7] = 0; - - this._$doChanged(); - $doUpdated(); - } - } + public dictionaryId: number; /** - * @description 使用するブレンドモードを指定する BlendMode クラスの値です。 - * A value from the BlendMode class that specifies which blend mode to use. + * @description Spriteの機能を所持しているかを返却 + * Returns whether Sprite functions are possessed. * - * @member {string} - * @default BlendMode.NORMAL + * @type {boolean} + * @readonly * @public */ - get blendMode (): BlendModeImpl - { - // use cache - if (this._$blendMode) { - return this._$blendMode; - } - - const transform: Transform = this._$transform; - if (transform._$blendMode) { - - // cache - this._$blendMode = transform._$blendMode; - - return transform._$blendMode; - } - - const placeObject: PlaceObjectImpl | null = this._$placeObject || this._$getPlaceObject(); - if (placeObject && placeObject.blendMode) { - - // cache - this._$blendMode = placeObject.blendMode; - - return placeObject.blendMode; - } - - // cache - this._$blendMode = "normal"; - - return "normal"; - } - set blendMode (blend_mode: BlendModeImpl) - { - const transform: Transform = this._$transform; - if (!transform._$blendMode) { - transform._$transform(null, null, null, blend_mode); - } else { - transform._$blendMode = blend_mode; - this._$doChanged(); - $doUpdated(); - } - this._$blendMode = blend_mode; - } + public readonly isSprite: boolean; /** - * @description 表示オブジェクトに現在関連付けられている各フィルターオブジェクトが - * 格納されているインデックス付きの配列です。 - * An indexed array that contains each filter object - * currently associated with the display object. + * @description InteractiveObject の機能を所持しているかを返却 + * Returns whether InteractiveObject functions are possessed. * - * @member {array} - * @default {array} + * @type {boolean} + * @readonly * @public */ - get filters (): FilterArrayImpl - { - // use cache - if (this._$filters) { - const filters: FilterArrayImpl = $getArray(); - for (let idx: number = 0; idx < this._$filters.length; ++idx) { - filters[idx] = this._$filters[idx].clone(); - } - return filters; - } - - const transform: Transform = this._$transform; - if (transform._$filters) { - - const clone: FilterArrayImpl = $getArray(); - const filters: FilterArrayImpl = $getArray(); - for (let idx: number = 0; idx < transform._$filters.length; ++idx) { - const filter = transform._$filters[idx]; - clone[idx] = filter.clone(); - filters[idx] = filter.clone(); - } - - // cache - this._$filters = clone; - - return filters; - } - - const placeObject: PlaceObjectImpl | null = this._$placeObject || this._$getPlaceObject(); - if (placeObject && placeObject.surfaceFilterList) { - - // create filter - if (!placeObject.filters) { - placeObject.filters = transform - ._$buildFilter(placeObject.surfaceFilterList); - } - - const clone: FilterArrayImpl = $getArray(); - - // @ts-ignore - const filters: FilterArrayImpl = $getArray(); - for (let idx: number = 0; idx < placeObject.filters.length; ++idx) { - const filter = placeObject.filters[idx]; - clone[idx] = filter.clone(); - filters[idx] = filter.clone(); - } - - // cache - this._$filters = clone; - - return filters; - } - - const filters: FilterArrayImpl = $getArray(); - - // cache - this._$filters = filters; - - return filters; - } - set filters (filters: FilterArrayImpl | null) - { - if (!filters) { - filters = $getArray(); - } - - this._$transform._$transform(null, null, filters); - this._$filters = filters; - } + public readonly isInteractive: boolean; /** - * @description 表示オブジェクトの高さを示します(ピクセル単位)。 - * Indicates the height of the display object, in pixels. + * @description コンテナの機能を所持しているかを返却 + * Returns whether the display object has container functionality. * - * @member {number} + * @type {boolean} + * @readonly * @public */ - get height (): number - { - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const bounds: BoundsImpl = $boundsMatrix( - baseBounds, - this._$transform._$rawMatrix() - ); - $poolBoundsObject(baseBounds); - - const height: number = $Math.abs(bounds.yMax - bounds.yMin); - - // object pool - $poolBoundsObject(bounds); - - switch (height) { - - case 0: - case $Infinity: - case -$Infinity: - return 0; - - default: - return +height.toFixed(2); - - } - } - set height (height: number) - { - height = +height; - if (!$isNaN(height) && height > -1) { - - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const rotation: number = this.rotation; - const bounds: BoundsImpl = rotation - ? $boundsMatrix(baseBounds, this._$transform._$rawMatrix()) - : baseBounds; - - if (rotation) { - $poolBoundsObject(baseBounds); - } - - const exHeight: number = $Math.abs(bounds.yMax - bounds.yMin); - $poolBoundsObject(bounds); - - switch (exHeight) { - - case 0: - case $Infinity: - case -$Infinity: - this.scaleY = 0; - break; - - default: - this.scaleY = height / exHeight; - break; - - } - } - } + public readonly isContainerEnabled: boolean; /** - * @description この表示オブジェクトが属するファイルの読み込み情報を含む LoaderInfo オブジェクトを返します。 - * Returns a LoaderInfo object containing information - * about loading the file to which this display object belongs. + * @description MovieClipの機能を所持しているかを返却 + * Returns whether the display object has MovieClip functionality. * - * @member {LoaderInfo} - * @default null + * @type {boolean} * @readonly * @public */ - get loaderInfo (): LoaderInfo | null - { - return this._$loaderInfo; - } + public readonly isTimelineEnabled: boolean; /** - * @description 呼び出し元の表示オブジェクトは、指定された mask オブジェクトによってマスクされます。 - * The calling display object is masked by the specified mask object. + * @description Shapeの機能を所持しているかを返却 + * Returns whether the display object has Shape functionality. * - * @member {DisplayObject|null} + * @type {boolean} + * @readonly * @public */ - get mask (): DisplayObjectImpl | null - { - return this._$mask; - } - set mask (mask: DisplayObjectImpl | null) - { - if (mask === this._$mask) { - return ; - } - - // reset - if (this._$mask) { - if ($rendererWorker && this._$mask.stage) { - this._$mask._$removeWorkerInstance(); - } - - this._$mask._$isMask = false; - this._$mask = null; - } - - if (mask) { - if ($rendererWorker - && "_$createWorkerInstance" in mask - && typeof mask._$createWorkerInstance === "function" - ) { - mask._$createWorkerInstance(); - } - - mask._$isMask = true; - this._$mask = mask; - } - - this._$doChanged(); - } + public readonly isShape: boolean; /** - * @description マウスまたはユーザー入力デバイスの x 軸の位置をピクセルで示します。 - * Indicates the x coordinate of the mouse or user input device position, in pixels. + * @description Videoの機能を所持しているかを返却 + * Returns whether the display object has Video functionality. * - * @member {number} - * @default 0 + * @type {boolean} * @readonly * @public */ - get mouseX (): number - { - return $getEvent() - ? this.globalToLocal($currentMousePoint()).x - : 0; - } + public readonly isVideo: boolean; /** - * @description マウスまたはユーザー入力デバイスの y 軸の位置をピクセルで示します。 - * Indicates the y coordinate of the mouse or user input device position, in pixels. + * @description Textの機能を所持しているかを返却 + * Returns whether the display object has Text functionality. * - * @member {number} - * @default 0 + * @type {boolean} * @readonly * @public */ - get mouseY (): number - { - return $getEvent() - ? this.globalToLocal($currentMousePoint()).y - : 0; - } + public readonly isText: boolean; /** - * @description DisplayObject のインスタンス名を示します。 - * Indicates the instance name of the DisplayObject. + * @description 表示オブジェクトのPlaceObjectのIDを返却します。 + * Returns the ID of the PlaceObject of the display object. * - * @member {string} + * @type {number} + * @default -1 * @public */ - get name (): string - { - if (this._$name) { - return this._$name; - } - return `instance${this._$instanceId}`; - } - set name (name: string) - { - this._$name = `${name}`; - - const parent: ParentImpl | null = this._$parent; - if (parent && parent._$names) { - - parent._$names.clear(); - - const children: DisplayObjectImpl[] = parent._$getChildren(); - for (let idx: number = 0; idx < children.length; ++idx) { - const child: DisplayObjectImpl = children[idx]; - if (child._$name) { - parent._$names.set(child.name, child); - } - } - } - } + public placeId: number; /** - * @description この表示オブジェクトを含む DisplayObjectContainer オブジェクトを示します。 - * Indicates the DisplayObjectContainer object that contains this display object. + * @description 現在のフレームの表示オブジェクトのPlaceObjectを返却します。 + * Returns the PlaceObject of the current frame of the display object. * - * @member {DisplayObjectContainer | null} - * @readonly + * @type {IPlaceObject|null} + * @default null * @public */ - get parent (): ParentImpl | null - { - return this._$parent; - } + public placeObject: IPlaceObject | null; /** - * @description 読み込まれた SWF ファイル内の表示オブジェクトの場合、 - * root プロパティはその SWF ファイルが表す表示リストのツリー構造部分の一番上にある表示オブジェクトとなります。 - * For a display object in a loaded SWF file, - * the root property is the top-most display object - * in the portion of the display list's tree structure represented by that SWF file. + * @description 構築に利用したキャラクターIDを返却します。 + * Returns the character ID used for construction. * - * @member {DisplayObject|null} - * @readonly + * @type {number} + * @default -1 * @public */ - get root (): ParentImpl - { - return this._$root; - } + public characterId: number; /** - * @description DisplayObject インスタンスの元の位置からの回転角を度単位で示します。 - * Indicates the rotation of the DisplayObject instance, - * in degrees, from its original orientation. + * @description マスク対象の深度を返却します。 + * Returns the depth of the mask target. * - * @member {number} + * @type {number} + * @default 0 * @public */ - get rotation (): number - { - if (this._$rotation !== null) { - return this._$rotation; - } - - const matrix: Float32Array = this._$transform._$rawMatrix(); - return $Math.atan2(matrix[1], matrix[0]) * $Rad2Deg; - } - set rotation (rotation: number) - { - rotation = $clamp(rotation % 360, 0 - 360, 360, 0); - if (this._$rotation === rotation) { - return ; - } - - const transform: Transform = this._$transform; - - const hasMatrix: boolean = transform._$matrix !== null; - - const matrix: Matrix = hasMatrix - ? transform._$matrix as NonNullable - : transform.matrix; - - const scaleX: number = $Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b); - const scaleY: number = $Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d); - if (rotation === 0) { - - matrix.a = scaleX; - matrix.b = 0; - matrix.c = 0; - matrix.d = scaleY; + public clipDepth: number; - } else { - - let radianX: number = $Math.atan2(matrix.b, matrix.a); - let radianY: number = $Math.atan2(0 - matrix.c, matrix.d); - - const radian: number = rotation * $Deg2Rad; - radianY = radianY + radian - radianX; - radianX = radian; - - matrix.b = scaleX * $Math.sin(radianX); - if (matrix.b === 1 || matrix.b === -1) { - matrix.a = 0; - } else { - matrix.a = scaleX * $Math.cos(radianX); - } - - matrix.c = -scaleY * $Math.sin(radianY); - if (matrix.c === 1 || matrix.c === -1) { - matrix.d = 0; - } else { - matrix.d = scaleY * $Math.cos(radianY); - } - } - - if (hasMatrix) { - this._$doChanged(); - $doUpdated(); - } else { - transform.matrix = matrix; - $poolMatrix(matrix); - } + /** + * @description 名前を返却します。 getChildByName() で使用されます。 + * Returns the name. Used by getChildByName(). + * + * @see {DisplayObjectContainer.getChildByName} + * @type {string} + * @default "" + * @public + */ + public name: string; - this._$rotation = rotation; - } + /** + * @description 開始フレームを返却します。 + * Returns the start frame. + * + * @type {number} + * @default 1 + * @public + */ + public startFrame: number; /** - * @description 現在有効な拡大 / 縮小グリッドです。 - * The current scaling grid that is in effect. + * @description 終了フレームを返却します。 + * Returns the end frame. * - * @member {Rectangle} + * @type {number} + * @default 0 * @public */ - get scale9Grid (): Rectangle | null - { - return this._$scale9Grid; - } - set scale9Grid (scale_9_grid: Rectangle | null) - { - if (this._$scale9Grid !== scale_9_grid) { - this._$scale9Grid = scale_9_grid; - this._$doChanged(); - $doUpdated(); - } - } + public endFrame: number; /** - * @description 基準点から適用されるオブジェクトの水平スケール(パーセンテージ)を示します。 - * Indicates the horizontal scale (percentage) - * of the object as applied from the registration point. + * @description 描画に関連する何らかの変更が加えられたかを示します。 + * Indicates whether any changes related to drawing have been made. * - * @member {number} + * @type {boolean} + * @default true * @public */ - get scaleX (): number - { - if (this._$scaleX !== null) { - return this._$scaleX; - } + public changed: boolean; - const matrix: Float32Array = this._$transform._$rawMatrix(); - - let xScale: number = $Math.sqrt( - matrix[0] * matrix[0] - + matrix[1] * matrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } + /** + * @description DisplayObjectの追加イベントが発火したかを示します。 + * Indicates whether the DisplayObject addition event has been fired. + * + * @type {boolean} + * @default false + * @public + */ + public $added: boolean; - return 0 > matrix[0] ? xScale * -1 : xScale; - } - set scaleX (scale_x: number) - { - scale_x = $clamp(+scale_x, - $SHORT_INT_MIN, $SHORT_INT_MAX - ); - - if (!$Number.isInteger(scale_x)) { - const value: string = scale_x.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - scale_x = +value.slice(0, index); - } - scale_x = +scale_x.toFixed(4); - } + /** + * @description DisplayObjectのステージ追加イベントが発火したかを示します。 + * Indicates whether the DisplayObject stage addition event has been fired. + * + * @type {boolean} + * @default false + * @public + */ + public $addedToStage: boolean; - if (this._$scaleX === scale_x) { - return ; - } + /** + * @description キャッシュで利用するユニークキー + * Unique key used for caching + * + * @type {string} + * @public + */ + public uniqueKey: string; - const transform: Transform = this._$transform; + /** + * @description 固定された変換行列、nullの場合はPlaceObjectの変換行列を検索します。 + * Fixed transformation matrix, if null, search for PlaceObject transformation matrix. + * + * @type {Matrix} + * @default null + * @protected + */ + public $matrix: Matrix | null; - const hasMatrix: boolean = transform._$matrix !== null; + /** + * @description 固定されたカラートランスフォーム、nullの場合はPlaceObjectのカラートランスフォームを検索します。 + * Fixed color transform, if null, search for PlaceObject color transform. + * + * @type {ColorTransform} + * @default null + * @protected + */ + public $colorTransform: ColorTransform | null; - const matrix: Matrix = hasMatrix - ? transform._$matrix as NonNullable - : transform.matrix; - - if (matrix.b === 0 || $isNaN(matrix.b)) { - - matrix.a = scale_x; - - } else { - - let radianX = $Math.atan2(matrix.b, matrix.a); - if (radianX === -$Math.PI) { - radianX = 0; - } - - matrix.b = scale_x * $Math.sin(radianX); - matrix.a = scale_x * $Math.cos(radianX); - - } - - if (hasMatrix) { - this._$doChanged(); - $doUpdated(); - } else { - transform.matrix = matrix; - $poolMatrix(matrix); - } - - this._$scaleX = scale_x; - } + /** + * @description 表示オブジェクトに現在関連付けられている各フィルターオブジェクトの配列です。 + * An array of filter objects currently associated with the display object. + * + * @type {array} + * @default null + * @protected + */ + public $filters: IFilterArray | null; /** - * @description 基準点から適用されるオブジェクトの垂直スケール(パーセンテージ)を示します。 - * IIndicates the vertical scale (percentage) - * of an object as applied from the registration point. + * @description 使用するブレンドモードを指定する BlendMode クラスの値です。 + * A value from the BlendMode class that specifies which blend mode to use. * - * @member {number} - * @public + * @type {string} + * @default BlendMode.NORMAL + * @protected */ - get scaleY (): number - { - if (this._$scaleY !== null) { - return this._$scaleY; - } + public $blendMode: IBlendMode | null; - const matrix: Float32Array = this._$transform._$rawMatrix(); + /** + * @description キャッシュした scaleX の値を返却します。 + * Returns the cached scaleX value. + * + * @type {number} + * @default null + * @protected + */ + public $scaleX: number | null; - let yScale: number = $Math.sqrt( - matrix[2] * matrix[2] - + matrix[3] * matrix[3] - ); + /** + * @description キャッシュした scaleY の値を返却します。 + * Returns the cached scaleY value. + * + * @type {number} + * @default null + * @protected + */ + public $scaleY: number | null; - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } + /** + * @description キャッシュした rotation の値を返却します。 + * Returns the cached rotation value. + * + * @type {number} + * @default null + * @protected + */ + public $rotation: number | null; - return 0 > matrix[3] ? yScale * -1 : yScale; - } - set scaleY (scale_y: number) - { - scale_y = $clamp(+scale_y, - $SHORT_INT_MIN, $SHORT_INT_MAX - ); - - if (!$Number.isInteger(scale_y)) { - const value: string = scale_y.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - scale_y = +value.slice(0, index); - } - scale_y = +scale_y.toFixed(4); - } + /** + * @description キャッシュした x の値を返却します。 + * Returns the cached x value. + * + * @type {number} + * @default null + * @protected + */ + public $x: number | null; - if (this._$scaleY === scale_y) { - return ; - } + /** + * @description キャッシュした y の値を返却します。 + * Returns the cached y value. + * + * @type {number} + * @default null + * @protected + */ + public $y: number | null; - const transform: Transform = this._$transform; + /** + * @description キャッシュした alpha の値を返却します。 + * Returns the cached alpha value. + * + * @type {number} + * @default null + * @protected + */ + public $alpha: number | null; - const hasMatrix: boolean = transform._$matrix !== null; + /** + * @description 描画情報を保持するキャッシュ + * Cache that holds drawing information + * + * @type {Map | null} + * @default null + * @protected + */ + public $cache: Map | null; - const matrix: Matrix = hasMatrix - ? transform._$matrix as NonNullable - : transform.matrix; + /** + * @description 表示オブジェクトのスケール9グリッドを示します。 + * Indicates the scale9 grid of the display object. + * + * @type {Rectangle} + * @private + */ + private _$scale9Grid: Rectangle | null; - if (matrix.c === 0 || $isNaN(matrix.c)) { + /** + * @description 表示オブジェクトの可視性を示します。 + * Indicates the visibility of the display object. + * + * @type {boolean} + * @private + */ + private _$visible: boolean; - matrix.d = scale_y; + /** + * @description 表示オブジェクト単位の変数を保持するマップ + * Map that holds variables for display objects + * + * @type {Map} + * @default null + * @private + */ + private _$variables: Map | null; - } else { + /** + * @description マスクとしてDisplayObjectにセットされているかを示します。 + * Indicates whether the DisplayObject is set as a mask. + * + * @type {boolean} + * @default false + * @private + */ + public isMask: boolean; - let radianY = $Math.atan2(-matrix.c, matrix.d); - if (radianY === -$Math.PI) { - radianY = 0; - } - matrix.c = -scale_y * $Math.sin(radianY); - matrix.d = scale_y * $Math.cos(radianY); + /** + * @description このDisplayObjectの親のDisplayObjectContainerを返却します。 + * 通常であれば、親のDisplayObjectContainerを継承しているのは、Sprite、または MovieClip となります。 + * Returns the DisplayObjectContainer of this DisplayObject's parent. + * Under normal circumstances, the parent DisplayObjectContainer would inherit from Sprite or MovieClip. + * + * @member {Sprite | MovieClip | null} + * @public + */ + public parent: ISprite | null; - } + /** + * @constructor + * @public + */ + constructor () + { + super(); - if (hasMatrix) { - this._$doChanged(); - $doUpdated(); - } else { - transform.matrix = matrix; - $poolMatrix(matrix); - } + this.instanceId = $getInstanceId(); + this.dictionaryId = -1; + this.uniqueKey = ""; + + // 各小クラスの機能を所持しているか + this.isSprite = false; + this.isContainerEnabled = false; + this.isTimelineEnabled = false; + this.isShape = false; + this.isVideo = false; + this.isText = false; + this.isInteractive = false; + + // PlaceObject + this.placeId = -1; + this.placeObject = null; + + // Characterパラメーター + this.characterId = -1; + this.clipDepth = 0; + this.name = ""; + this.startFrame = 1; + this.endFrame = 0; + this.parent = null; + + // フラグ + this.isMask = false; + this.changed = true; + this.$added = false; + this.$addedToStage = false; + + // Transform変数 + this.$matrix = null; + this.$colorTransform = null; + this.$filters = null; + this.$blendMode = null; + + this._$visible = true; + this._$scale9Grid = null; + this._$variables = null; - this._$scaleY = scale_y; + // キャッシュ + this.$x = null; + this.$y = null; + this.$alpha = null; + this.$scaleX = null; + this.$scaleY = null; + this.$rotation = null; + this.$cache = null; } /** - * @description 表示オブジェクトのステージです。 - * The Stage of the display object. + * @description この表示オブジェクトおよびルートレベルまでのそのすべての親オブジェクトの結合された Matrix を返却します。 + * Returns a concatenated Matrix object representing the combined transformation matrixes of the display object and all of its parent objects, back to the root level. * - * @member {Stage} + * @member {Matrix} * @readonly - * @public */ - get stage (): Stage | null + get concatenatedMatrix (): Matrix { - if (this._$stage) { - return this._$stage; - } - - // find parent - const parent: ParentImpl | null = this._$parent; - if (parent) { - return parent._$stage; - } - - return null; + return displayObjectConcatenatedMatrixUseCase(this); } /** - * @description 表示オブジェクトのマトリックス、カラー変換、 - * ピクセル境界に関係するプロパティを持つオブジェクトです。 - * An object with properties pertaining - * to a display object's matrix, color transform, and pixel bounds. + * @description 指定されたオブジェクトのアルファ透明度値を示します。 + * 有効な値は 0.0(完全な透明)~ 1.0(完全な不透明)です。 + * デフォルト値は 1.0 です。alpha が 0.0 に設定されている表示オブジェクトは、 + * 表示されない場合でも、アクティブです。 + * Indicates the alpha transparency value of the object specified. + * Valid values are 0.0 (fully transparent) to 1.0 (fully opaque). + * The default value is 1.0. Display objects with alpha set to 0.0 are active, + * even though they are invisible. * - * @member {Transform} + * @member {number} + * @default 1 * @public */ - get transform (): Transform + get alpha (): number { - return this._$transform; + return displayObjectGetAlphaUseCase(this); } - set transform (transform: Transform) + set alpha (alpha: number) { - this._$transform = transform; + displayObjectSetAlphaUseCase(this, alpha); } /** - * @description 表示オブジェクトが可視かどうかを示します。 - * Whether or not the display object is visible. + * @description 使用するブレンドモードを指定する BlendMode クラスの値です。 + * A value from the BlendMode class that specifies which blend mode to use. * - * @member {boolean} + * @member {string} + * @default BlendMode.NORMAL * @public */ - get visible (): boolean + get blendMode (): IBlendMode { - return this._$visible; + return displayObjectGetBlendModeUseCase(this); } - set visible (visible: boolean) + set blendMode (blend_mode: IBlendMode) { - if (this._$visible !== visible) { - this._$visible = !!visible; - this._$doChanged(); - $doUpdated(); - } + displayObjectSetBlendModeUseCase(this, blend_mode); } /** - * @description 表示オブジェクトの幅を示します(ピクセル単位)。 - * Indicates the width of the display object, in pixels. + * @description 表示オブジェクトに現在関連付けられている各フィルターオブジェクトの配列です。 + * An array of filter objects currently associated with the display object. * - * @member {number} + * @member {array} + * @default null * @public */ - get width (): number + get filters (): IFilterArray | null { - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const bounds: BoundsImpl = $boundsMatrix( - baseBounds, - this._$transform._$rawMatrix() - ); - - $poolBoundsObject(baseBounds); - - const width: number = $Math.abs(bounds.xMax - bounds.xMin); - $poolBoundsObject(bounds); - - switch (true) { - - case width === 0: - case width === $Infinity: - case width === 0 - $Infinity: - return 0; - - default: - return +width.toFixed(2); - - } + return displayObjectGetFiltersUseCase(this); } - set width (width: number) + set filters (filters: IFilterArray | null) { - width = +width; - if (!$isNaN(width) && width > -1) { - - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const rotation: number = this.rotation; - const bounds = rotation - ? $boundsMatrix(baseBounds, this._$transform._$rawMatrix()) - : baseBounds; - - if (rotation) { - $poolBoundsObject(baseBounds); - } - - const exWidth = $Math.abs(bounds.xMax - bounds.xMin); - $poolBoundsObject(bounds); - - switch (true) { - - case exWidth === 0: - case exWidth === $Infinity: - case exWidth === -$Infinity: - this.scaleX = 0; - break; - - default: - this.scaleX = width / exWidth; - break; - - } - } + displayObjectSetFiltersUseCase(this, filters); } /** - * @description 親 DisplayObjectContainer のローカル座標を基準にした - * DisplayObject インスタンスの x 座標を示します。 - * Indicates the x coordinate - * of the DisplayObject instance relative to the local coordinates - * of the parent DisplayObjectContainer. + * @description スプライトのドラッグ先またはスプライトがドロップされた先の表示オブジェクトを指定します。 + * Specifies the display object over which the sprite is being dragged, + * or on which the sprite was dropped. * - * @member {number} + * @member {ISprite|null} + * @readonly * @public */ - get x (): number - { - return this._$transform._$rawMatrix()[4]; - } - set x (x: number) + get dropTarget (): ISprite | null { - const transform: Transform = this._$transform; - - if (!transform._$matrix) { - const matrix: Matrix = transform.matrix; - matrix.tx = x; - transform.matrix = matrix; - $poolMatrix(matrix); - } else { - transform._$matrix.tx = x; - this._$doChanged(); - $doUpdated(); - } + return $getDraggingDisplayObject(); } /** - * @description 親 DisplayObjectContainer のローカル座標を基準にした - * DisplayObject インスタンスの y 座標を示します。 - * Indicates the y coordinate - * of the DisplayObject instance relative to the local coordinates - * of the parent DisplayObjectContainer. + * @description 表示オブジェクトの高さを示します(ピクセル単位)。 + * Indicates the height of the display object, in pixels. * * @member {number} * @public */ - get y (): number + get height (): number { - return this._$transform._$rawMatrix()[5]; + return displayObjectGetHeightUseCase(this); } - set y (y: number) + set height (height: number) { - const transform: Transform = this._$transform; - - if (!transform._$matrix) { - const matrix = transform.matrix; - matrix.ty = y; - - transform.matrix = matrix; - $poolMatrix(matrix); - } else { - transform._$matrix.ty = y; - this._$doChanged(); - $doUpdated(); - } + displayObjectSetHeightUseCase(this, height); } /** - * @description targetCoordinateSpace オブジェクトの座標系を基準にして、 - * 表示オブジェクトの領域を定義する矩形を返します。 - * Returns a rectangle that defines the area - * of the display object relative to the coordinate system - * of the targetCoordinateSpace object. + * @description この表示オブジェクトが属するファイルの読み込み情報を含む LoaderInfo オブジェクトを返します。 + * Returns a LoaderInfo object containing information + * about loading the file to which this display object belongs. * - * @param {DisplayObject} [target=null] - * @return {Rectangle} + * @member {LoaderInfo} + * @default null + * @readonly + * @public */ - getBounds (target: DisplayObjectImpl | null = null): Rectangle + get loaderInfo (): LoaderInfo | null { - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const matrix: Matrix = this._$transform.concatenatedMatrix; - - // to global - const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix._$matrix); - - // pool - $poolMatrix(matrix); - $poolBoundsObject(baseBounds); - - // create bounds object - const targetBaseBounds: BoundsImpl = $getBoundsObject( - bounds.xMin, - bounds.xMax, - bounds.yMin, - bounds.yMax - ); - - // pool - $poolBoundsObject(bounds); - - if (!target) { - target = this; - } - - const targetMatrix: Matrix = target._$transform.concatenatedMatrix; - targetMatrix.invert(); - - const resultBounds: BoundsImpl = $boundsMatrix( - targetBaseBounds, targetMatrix._$matrix - ); - $poolBoundsObject(targetBaseBounds); - $poolMatrix(targetMatrix); - - const xMin: number = resultBounds.xMin; - const yMin: number = resultBounds.yMin; - const xMax: number = resultBounds.xMax; - const yMax: number = resultBounds.yMax; - - // pool - $poolBoundsObject(resultBounds); - - return new Rectangle( - xMin, yMin, - $Math.abs(xMax - xMin), - $Math.abs(yMax - yMin) - ); + return $loaderInfoMap.has(this) + ? $loaderInfoMap.get(this) as NonNullable + : null; } /** - * @description point オブジェクトをステージ(グローバル)座標から - * 表示オブジェクトの(ローカル)座標に変換します。 - * Converts the point object from the Stage (global) coordinates - * to the display object's (local) coordinates. + * @description 表示オブジェクトの ColorTransform を返します。 + * Returns the ColorTransform object of the display object. * - * @param {Point} point - * @return {Point} + * @member {ColorTransform} * @public */ - globalToLocal (point: Point): Point + get colorTransform (): ColorTransform { - const matrix: Matrix = this._$transform.concatenatedMatrix; - matrix.invert(); - - const newPoint: Point = new Point( - point.x * matrix.a + point.y * matrix.c + matrix.tx, - point.x * matrix.b + point.y * matrix.d + matrix.ty - ); - - $poolMatrix(matrix); - - return newPoint; + return displayObjectGetColorTransformUseCase(this); + } + set colorTransform (color_transform: ColorTransform) + { + this.$colorTransform = color_transform; } /** - * @description 表示オブジェクトの境界ボックスを評価して、 - * obj 表示オブジェクトの境界ボックスと重複または交差するかどうかを調べます。 - * Evaluates the bounding box of the display object to see - * if it overlaps or intersects with the bounding box of the obj display object. + * @description 表示オブジェクトの Matrix を返します。 + * Returns the Matrix of the display object. * - * @param {DisplayObject} object - * @returns {boolean} + * @member {Matrix} * @public */ - hitTestObject (object: DisplayObjectImpl): boolean + get matrix (): Matrix { - const baseBounds1: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const matrix1: Matrix = this._$transform.concatenatedMatrix; - const bounds1: BoundsImpl = $boundsMatrix(baseBounds1, matrix1._$matrix); - - // pool - $poolMatrix(matrix1); - $poolBoundsObject(baseBounds1); - - const baseBounds2: BoundsImpl = object._$getBounds(null); - const matrix2: Matrix = object._$transform.concatenatedMatrix; - const bounds2: BoundsImpl = $boundsMatrix(baseBounds2, matrix2._$matrix); - - // pool - $poolMatrix(matrix2); - $poolBoundsObject(baseBounds2); - - // calc - const sx: number = $Math.max(bounds1.xMin, bounds2.xMin); - const sy: number = $Math.max(bounds1.yMin, bounds2.yMin); - const ex: number = $Math.min(bounds1.xMax, bounds2.xMax); - const ey: number = $Math.min(bounds1.yMax, bounds2.yMax); - - // pool - $poolBoundsObject(bounds1); - $poolBoundsObject(bounds2); - - return ex - sx >= 0 && ey - sy >= 0; + return displayObjectGetMatrixUseCase(this); + } + set matrix (matrix: Matrix) + { + this.$matrix = matrix; } /** - * @description 表示オブジェクトを評価して、x および y パラメーターで指定された - * ポイントと重複または交差するかどうかを調べます。 - * Evaluates the display object to see if it overlaps - * or intersects with the point specified by the x and y parameters. + * @description 対象のDisplayObjectの基準点からの x 軸の位置をピクセルで示します。 + * Indicates the x coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. * - * @param {number} x - * @param {number} y - * @param {boolean} [shape_flag=false] - * @returns {boolean} + * @member {number} + * @default 0 + * @readonly * @public */ - hitTestPoint ( - x: number, y: number, - shape_flag: boolean = false - ): boolean { - - if (shape_flag) { - - let matrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - - let parent: ParentImpl | null = this._$parent; - while (parent) { - - matrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - matrix - ); - - parent = parent._$parent; - } - - $hitContext.setTransform(1, 0, 0, 1, 0, 0); - $hitContext.beginPath(); - - let result: boolean = false; - if ("_$hit" in this && typeof this._$hit === "function") { - result = this._$hit($hitContext, matrix, { "x": x, "y": y }, true); - } - - if (matrix !== $MATRIX_ARRAY_IDENTITY) { - $poolFloat32Array6(matrix); - } - - return result; - } - - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, this._$transform._$rawMatrix()); - $poolBoundsObject(baseBounds); - - const rectangle: Rectangle = new Rectangle( - bounds.xMin, bounds.yMin, - bounds.xMax - bounds.xMin, - bounds.yMax - bounds.yMin - ); - - // pool - $poolBoundsObject(bounds); - - const point: Point = this._$parent - ? this._$parent.globalToLocal(new Point(x, y)) - : new Point(x, y); - - return rectangle.containsPoint(point); + get mouseX (): number + { + return this.globalToLocal($pointer).x; } /** - * @description point オブジェクトを表示オブジェクトの(ローカル)座標から - * ステージ(グローバル)座標に変換します。 - * Converts the point object from the display object's (local) coordinates - * to the Stage (global) coordinates. + * @description 対象のDisplayObjectの基準点からの y 軸の位置をピクセルで示します。 + * Indicates the y coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. * - * - * @param {Point} point - * @returns {Point} + * @member {number} + * @default 0 + * @readonly * @public */ - localToGlobal (point: Point): Point + get mouseY (): number { - const matrix: Matrix = this - ._$transform - .concatenatedMatrix; - - const newPoint: Point = new Point( - point.x * matrix.a + point.y * matrix.c + matrix.tx, - point.x * matrix.b + point.y * matrix.d + matrix.ty - ); - - $poolMatrix(matrix); - - return newPoint; + return this.globalToLocal($pointer).y; } /** - * @description クラスのローカル変数空間から値を取得 - * Get a value from the local variable space of the class + * @description DisplayObject のルートである DisplayObjectContainer を返します。 + * Returns the DisplayObjectContainer object that contains this display object. * - * @param {*} key - * @return {*} - * @method + * @member {MovieClip | Sprite | null} + * @readonly * @public */ - getLocalVariable (key: any): any + get root (): IParent | null { - if (!this._$variables) { - return null; - } - - if (this._$variables.has(key)) { - return this._$variables.get(key); - } + return $rootMap.has(this) + ? $rootMap.get(this) as NonNullable> + : null; } /** - * @description クラスのローカル変数空間へ値を保存 - * Store values in the local variable space of the class + * @description DisplayObject インスタンスの元の位置からの回転角を度単位で示します。 + * Indicates the rotation of the DisplayObject instance, + * in degrees, from its original orientation. * - * @param {*} key - * @param {*} value - * @return {void} - * @method + * @member {number} * @public */ - setLocalVariable (key: any, value: any): void + get rotation (): number { - if (!this._$variables) { - this._$variables = $getMap(); - } - this._$variables.set(key, value); + return this.$rotation === null + ? displayObjectGetRotationUseCase(this) + : this.$rotation; } - - /** - * @description クラスのローカル変数空間に値があるかどうかを判断します。 - * Determines if there is a value in the local variable space of the class. - * - * @param {*} key - * @return {boolean} - * @method - * @public - */ - hasLocalVariable (key: any): boolean + set rotation (rotation: number) { - return this._$variables - ? this._$variables.has(key) - : false; + displayObjectSetRotationUseCase(this, rotation); } /** - * @description クラスのローカル変数空間の値を削除 - * Remove values from the local variable space of a class + * @description 現在有効な拡大 / 縮小グリッドです。 + * The current scaling grid that is in effect. * - * @param {*} key - * @return {void} - * @method + * @member {Rectangle} * @public */ - deleteLocalVariable (key: any): void + get scale9Grid (): Rectangle | null { - if (this._$variables && this._$variables.has(key)) { - this._$variables.delete(key); - if (!this._$variables.size) { - $poolMap(this._$variables); - this._$variables = null; - } + return this._$scale9Grid; + } + set scale9Grid (scale_9_grid: Rectangle | null) + { + if (this._$scale9Grid === scale_9_grid) { + return ; } + this._$scale9Grid = scale_9_grid; + displayObjectApplyChangesService(this); } /** - * @description グローバル変数空間から値を取得 - * Get a value from the global variable space + * @description 基準点から適用されるオブジェクトの水平スケール値を返却します。 + * Returns the horizontal scale value of the object applied from the reference point. * - * @param {*} key - * @return {*} - * @method + * @member {number} * @public */ - getGlobalVariable (key: any): any + get scaleX (): number { - if ($variables.has(key)) { - return $variables.get(key); - } - return null; + return this.$scaleX === null + ? displayObjectGetScaleXUseCase(this) + : this.$scaleX; + } + set scaleX (scale_x: number) + { + displayObjectSetScaleXUseCase(this, scale_x); } /** - * @description グローバル変数空間へ値を保存 - * Save values to global variable space + * @description 基準点から適用されるオブジェクトの垂直スケール(パーセンテージ)を示します。 + * IIndicates the vertical scale (percentage) + * of an object as applied from the registration point. * - * @param {*} key - * @param {*} value - * @return {void} - * @method + * @member {number} * @public */ - setGlobalVariable (key: any, value: any): void + get scaleY (): number { - $variables.set(key, value); + return this.$scaleY === null + ? displayObjectGetScaleYUseCase(this) + : this.$scaleY; + } + set scaleY (scale_y: number) + { + displayObjectSetScaleYUseCase(this, scale_y); } /** - * @description グローバル変数空間に値があるかどうかを判断します。 - * Determines if there is a value in the global variable space. + * @description 表示オブジェクトが可視かどうかを示します。 + * Whether or not the display object is visible. * - * @param {*} key - * @return {boolean} - * @method + * @member {boolean} * @public */ - hasGlobalVariable (key: any): boolean + get visible (): boolean { - return $variables.has(key); + return this._$visible; + } + set visible (visible: boolean) + { + if (this._$visible === visible) { + return ; + } + this._$visible = !!visible; + displayObjectApplyChangesService(this); } /** - * @description グローバル変数空間の値を削除 - * Remove values from global variable space. + * @description 表示オブジェクトの幅を示します(ピクセル単位)。 + * Indicates the width of the display object, in pixels. * - * @param {*} key - * @return {void} - * @method + * @member {number} * @public */ - deleteGlobalVariable (key: any): void + get width (): number { - if ($variables.has(key)) { - $variables.delete(key); - } + return displayObjectGetWidthUseCase(this); + } + set width (width: number) + { + displayObjectSetWidthUseCase(this, width); } /** - * @description グローバル変数空間に値を全てクリアします。 - * Clear all values in the global variable space. + * @description 親 DisplayObjectContainer のローカル座標を基準にした DisplayObject インスタンスの x 座標を示します。 + * Indicates the x coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. * - * @return {void} - * @method + * @member {number} * @public */ - clearGlobalVariable (): void + get x (): number { - return $variables.clear(); + return this.$x === null + ? displayObjectGetXUseCase(this) + : this.$x; } - - /** - * @return {object} - * @method - * @private - */ - _$getPlaceObject (): PlaceObjectImpl | null + set x (x: number) { - if (!this._$placeObject) { - - const placeId = this._$placeId; - if (placeId === -1) { - return null; - } - - const parent: ParentImpl | null = this._$parent; - if (!parent || !parent._$placeObjects) { - return null; - } - - const placeMap: Array> | null = parent._$placeMap; - if (!placeMap || !placeMap.length) { - return null; - } - - const frame: number = "currentFrame" in parent ? parent.currentFrame : 1; - const places: number[] | void = placeMap[frame]; - if (!places) { - return null; - } - - const currentPlaceId: number = places[placeId] | 0; - const placeObject: PlaceObjectImpl | void = parent._$placeObjects[currentPlaceId]; - if (!placeObject) { - return null; - } - - this._$changePlace = currentPlaceId !== this._$currentPlaceId; - this._$currentPlaceId = currentPlaceId; - this._$placeObject = placeObject; - - return placeObject; - } - - return this._$placeObject; + displayObjectSetXUseCase(this, x); } /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private + * @description 親 DisplayObjectContainer のローカル座標を基準にした DisplayObject インスタンスの y 座標を示します。 + * Indicates the y coordinate of the DisplayObject instance relative to the local coordinates of the parent DisplayObjectContainer. + * + * @member {number} + * @public */ - _$baseBuild ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): T { - - const loaderInfo: LoaderInfo | null = parent._$loaderInfo; - if (!loaderInfo || !loaderInfo._$data) { - throw new Error("the loaderInfo or data is nul."); - } - - // setup - this._$parent = parent; - this._$root = parent._$root; - this._$stage = parent._$stage; - this._$loaderInfo = loaderInfo; - - // bind tag data - this._$characterId = tag.characterId | 0; - this._$clipDepth = tag.clipDepth | 0; - this._$startFrame = tag.startFrame | 0; - this._$endFrame = tag.endFrame | 0; - this._$name = tag.name || ""; - - return loaderInfo._$data.characters[tag.characterId]; + get y (): number + { + return this.$y === null + ? displayObjectGetYUseCase(this) + : this.$y; + } + set y (y: number) + { + displayObjectSetYUseCase(this, y); } /** - * @return {boolean} - * @method - * @private + * @description 指定したDisplayObject の座標系を基準にして、表示オブジェクトの領域を定義する矩形を返します。 + * Returns a rectangle that defines the area of the display object relative to the coordinate system of the targetDisplayObject. + * + * @param {DisplayObject} [target_display_object=null] + * @return {Rectangle} */ - _$isUpdated (): boolean + getBounds (target_display_object: D | null = null): Rectangle { - return this._$updated; + return displayObjectGetBoundsUseCase(this as unknown as D, target_display_object); } /** - * @return {void} - * @method - * @private + * @description point オブジェクトをステージ(グローバル)座標から表示オブジェクトの(ローカル)座標に変換します。 + * Converts the point object from the Stage (global) coordinates to the display object's (local) coordinates. + * + * @param {Point} point + * @return {Point} + * @public */ - _$updateState (): void + globalToLocal (point: Point): Point { - this._$isNext = true; - - const parent: ParentImpl | null = this._$parent; - if (parent) { - parent._$updateState(); - } + return displayObjectGlobalToLocalService(this, point); } /** - * @return {void} - * @method - * @private + * @description DisplayObject の描画範囲を評価して、重複または交差するかどうかを調べます。 + * Evaluates a DisplayObject's drawing range to see if it overlaps or intersects. + * + * @param {DisplayObject} target_display_object + * @returns {boolean} + * @public */ - _$doChanged (): void + hitTestObject (target_display_object: D): boolean { - this._$posted = false; - this._$isNext = true; - this._$updated = true; - - const parent: ParentImpl | null = this._$parent; - if (parent) { - if (!parent._$updated) { - parent._$doChanged(); - } - } + return displayObjectHitTestObjectUseCase(this as unknown as DisplayObject, target_display_object); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {array} filters - * @param {number} width - * @param {number} height - * @param {WebGLTexture} [target_texture = null] - * @return {object} - * @method - * @private + * @description 表示オブジェクトを評価して、x および y パラメーターで指定されたポイントと重複または交差するかどうかを調べます。 + * Evaluates the display object to see if it overlaps or intersects with the point specified by the x and y parameters. + * + * @param {number} x + * @param {number} y + * @param {boolean} [shape_flag=false] + * @returns {boolean} + * @public */ - _$drawFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array, - filters: FilterArrayImpl, - width: number, - height: number, - target_texture: WebGLTexture | null = null - ): CachePositionImpl { - - const cacheKeys: any[] = $getArray(this._$instanceId, "f"); - let position: CachePositionImpl | void = $cacheStore.get(cacheKeys); - - const updated: boolean = this._$isFilterUpdated(matrix, filters, true); - - if (position && !updated) { - context.cachePosition = position; - return position; - } - - // cache clear - if (position) { - $cacheStore.set(cacheKeys, null); - } - - const manager: FrameBufferManager = context.frameBuffer; - const targetTexture: WebGLTexture = target_texture - ? target_texture - : context.getTextureFromRect( - context.cachePosition as NonNullable - ); - - const texture: WebGLTexture = this._$applyFilter( - context, filters, targetTexture, - matrix, width, height - ); - manager.textureManager.release(targetTexture); - - const bounds: BoundsImpl = this._$getLayerBounds(matrix); - position = manager.createCachePosition( - $Math.ceil($Math.abs(bounds.xMax - bounds.xMin)), - $Math.ceil($Math.abs(bounds.yMax - bounds.yMin)) - ); - $poolBoundsObject(bounds); - - position.filterState = true; - position.matrix = `${matrix[0]}_${matrix[1]}_${matrix[2]}_${matrix[3]}_0_0`; - position.offsetX = texture.offsetX; - position.offsetY = texture.offsetY; - - // 関数先でtextureがreleaseされる - context.drawTextureFromRect(texture, position); - - $cacheStore.set(cacheKeys, position); - $poolArray(cacheKeys); - - return position; + hitTestPoint ( + x: number, y: number, + shape_flag: boolean = false + ): boolean { + return displayObjectHitTestPointUseCase(this, x, y, shape_flag); } /** - * @param {Float32Array} multi_matrix - * @returns {object} - * @private + * @description point オブジェクトを表示オブジェクトの(ローカル)座標からステージ(グローバル)座標に変換します。 + * Converts the point object from the display object's (local) coordinates to the Stage (global) coordinates. + * + * @param {Point} point + * @returns {Point} + * @public */ - _$getLayerBounds (multi_matrix: Float32Array): BoundsImpl + localToGlobal (point: Point): Point { - const baseBounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds() as BoundsImpl - : $getBoundsObject(); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multi_matrix); - $poolBoundsObject(baseBounds); - - const filters: FilterArrayImpl = this._$filters || this.filters; - if (!filters.length) { - return bounds; - } - - let filterBounds: BoundsImpl = $getBoundsObject( - 0, - $Math.abs(bounds.xMax - bounds.xMin), - 0, - $Math.abs(bounds.yMax - bounds.yMin) - ); - $poolBoundsObject(bounds); - - let xScale: number = +$Math.sqrt( - multi_matrix[0] * multi_matrix[0] - + multi_matrix[1] * multi_matrix[1] - ); - let yScale: number = +$Math.sqrt( - multi_matrix[2] * multi_matrix[2] - + multi_matrix[3] * multi_matrix[3] - ); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - for (let idx: number = 0; idx < filters.length; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - - return filterBounds; + return displayObjectLocalToGlobalService(this, point); } /** - * @return {void} + * @description クラスのローカル変数空間から値を取得 + * Get a value from the local variable space of the class + * + * @param {*} key + * @return {*} * @method - * @private + * @public */ - _$executeAddedEvent (): void + getLocalVariable (key: any): any { - if (!this._$parent) { - return ; - } - - // add event - if (!this._$added) { - - // added event - if (this.willTrigger(Next2DEvent.ADDED)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.ADDED, true)); - } - - // update - this._$added = true; + if (!this._$variables) { + return null; } - if (!this._$addedStage && this._$stage !== null) { - - if (this.willTrigger(Next2DEvent.ADDED_TO_STAGE)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.ADDED_TO_STAGE)); - } - - // update - this._$addedStage = true; + if (this._$variables.has(key)) { + return this._$variables.get(key); } } /** + * @description クラスのローカル変数空間へ値を保存 + * Store values in the local variable space of the class + * + * @param {*} key + * @param {*} value * @return {void} * @method - * @private + * @public */ - _$prepareActions (): void + setLocalVariable (key: any, value: any): void { - this._$nextFrame(); + if (!this._$variables) { + this._$variables = new Map(); + } + this._$variables.set(key, value); } /** + * @description クラスのローカル変数空間に値があるかどうかを判断します。 + * Determines if there is a value in the local variable space of the class. + * + * @param {*} key * @return {boolean} * @method - * @private + * @public */ - _$nextFrame (): boolean + hasLocalVariable (key: any): boolean { - // added event - this._$executeAddedEvent(); - - this._$isNext = false; - - if (!this._$posted && $rendererWorker) { - // @ts-ignore - this._$postProperty(); - } - - return false; + return this._$variables + ? this._$variables.has(key) + : false; } /** - * @param {array} [filters=null] - * @return {boolean} - * @private + * @description クラスのローカル変数空間の値を削除 + * Remove values from the local variable space of a class + * + * @param {*} key + * @return {void} + * @method + * @public */ - _$canApply (filters: FilterArrayImpl | null = null): boolean + deleteLocalVariable (key: any): void { - if (filters) { - for (let idx: number = 0; idx < filters.length; ++idx) { - if (filters[idx]._$canApply()) { - return true; - } + if (this._$variables && this._$variables.has(key)) { + this._$variables.delete(key); + if (!this._$variables.size) { + this._$variables = null; } } - return false; } /** - * @param {Float32Array} matrix - * @param {array} [filters=null] - * @param {boolean} [can_apply=false] - * @param {number} [position_x=0] - * @param {number} [position_y=0] - * @return {boolean} - * @private + * @description グローバル変数空間から値を取得 + * Get a value from the global variable space + * + * @param {*} key + * @return {*} + * @method + * @public */ - _$isFilterUpdated ( - matrix: Float32Array, - filters: FilterArrayImpl | null = null, - can_apply: boolean = false - ): boolean { - - // cache flag - if (this._$isUpdated()) { - return true; - } - - // check filter data - if (can_apply && filters) { - - for (let idx: number = 0; idx < filters.length; ++idx) { - - if (!filters[idx]._$isUpdated()) { - continue; - } - - return true; - } - - } - - // check status - const cache: CachePositionImpl = $cacheStore.get([this._$instanceId, "f"]); - if (!cache) { - return true; - } - - if (cache.filterState !== can_apply) { - return true; - } - - if (cache.matrix !== `${matrix[0]}_${matrix[1]}_${matrix[2]}_${matrix[3]}`) { - return true; + getGlobalVariable (key: any): any + { + if ($variables.has(key)) { + return $variables.get(key); } - - return false; + return null; } /** - * @param {CanvasToWebGLContext} context - * @param {array} filters - * @param {WebGLTexture} target_texture - * @param {Float32Array} matrix - * @param {number} width - * @param {number} height - * @return {WebGLTexture} - * @private + * @description グローバル変数空間へ値を保存 + * Save values to global variable space + * + * @param {*} key + * @param {*} value + * @return {void} + * @method + * @public */ - _$applyFilter ( - context: CanvasToWebGLContext, - filters: FilterArrayImpl, - target_texture: WebGLTexture, - matrix: Float32Array, - width: number, - height: number - ): WebGLTexture { - - const xScale: number = +$Math.sqrt( - matrix[0] * matrix[0] - + matrix[1] * matrix[1] - ); - const yScale: number = +$Math.sqrt( - matrix[2] * matrix[2] - + matrix[3] * matrix[3] - ); - - const radianX: number = $Math.atan2(matrix[1], matrix[0]); - const radianY: number = $Math.atan2(-matrix[2], matrix[3]); - - const parentMatrix: Float32Array = $getFloat32Array6( - $Math.cos(radianX), $Math.sin(radianX), - -$Math.sin(radianY), $Math.cos(radianY), - width / 2, height / 2 - ); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - -target_texture.width / 2, - -target_texture.height / 2 - ); - - const multiMatrix: Float32Array = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - context._$bind(attachment); - - context.reset(); - context.setTransform( - multiMatrix[0], multiMatrix[1], - multiMatrix[2], multiMatrix[3], - multiMatrix[4], multiMatrix[5] - ); - $poolFloat32Array6(multiMatrix); - - context.drawImage(target_texture, - 0, 0, target_texture.width, target_texture.height - ); - - // init - context._$offsetX = 0; - context._$offsetY = 0; - - const filterMatrix: Float32Array = $getFloat32Array6( - xScale, 0, 0, yScale, 0, 0 - ); - - let texture: WebGLTexture | null = null; - for (let idx: number = 0; idx < filters.length; ++idx) { - texture = filters[idx]._$applyFilter(context, filterMatrix); - } - - $poolFloat32Array6(filterMatrix); - - if (!texture) { - return target_texture; - } - - const offsetX: number = context._$offsetX; - const offsetY: number = context._$offsetY; - - // reset - context._$offsetX = 0; - context._$offsetY = 0; - - // set offset - texture.offsetX = offsetX; - texture.offsetY = offsetY; - - context._$bind(currentAttachment); - manager.releaseAttachment(attachment, false); - - return texture; + setGlobalVariable (key: any, value: any): void + { + $variables.set(key, value); } /** - * @param {Float32Array} matrix + * @description グローバル変数空間に値があるかどうかを判断します。 + * Determines if there is a value in the global variable space. + * + * @param {*} key * @return {boolean} * @method - * @private + * @public */ - _$shouldClip (matrix: Float32Array): boolean + hasGlobalVariable (key: any): boolean { - const bounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds(matrix) as BoundsImpl - : $getBoundsObject(); - - const width = $Math.abs(bounds.xMax - bounds.xMin); - const height = $Math.abs(bounds.yMax - bounds.yMin); - $poolBoundsObject(bounds); - - // size 0 - return !(!width || !height); + return $variables.has(key); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {boolean} + * @description グローバル変数空間の値を削除 + * Remove values from global variable space. + * + * @param {*} key + * @return {void} * @method - * @private + * @public */ - _$startClip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): boolean { - - context.drawInstacedArray(); - - // マスクの描画反映を限定 - const bounds: BoundsImpl = "_$getBounds" in this && typeof this._$getBounds === "function" - ? this._$getBounds(matrix) as BoundsImpl - : $getBoundsObject(); - - const result = context._$startClip(bounds); - $poolBoundsObject(bounds); - - if (!result) { - return false; - } - - // start clip - context._$enterClip(); - - // mask start - context._$beginClipDef(); - - let containerClip = false; - if ("_$children" in this) { - containerClip = true; - context._$updateContainerClipFlag(true); - } - - // @ts-ignore - this._$clip(context, matrix); - this._$updated = false; - - // container clip - if (containerClip) { - - // update flag - context._$updateContainerClipFlag(false); - - // execute clip - context._$drawContainerClip(); + deleteGlobalVariable (key: any): void + { + if ($variables.has(key)) { + $variables.delete(key); } - - // mask end - context._$endClipDef(); - - return true; } /** + * @description グローバル変数空間に値を全てクリアします。 + * Clear all values in the global variable space. + * * @return {void} * @method - * @private + * @public */ - _$removeWorkerInstance (): void + clearGlobalVariable (): void { - if ($rendererWorker) { - $rendererWorker.postMessage({ - "command": "remove", - "instanceId": this._$instanceId - }); - } + return $variables.clear(); } /** - * @param {Float32Array} buffer - * @param {number} [index = 0] + * @description 親子関係を解除します。 + * Removes the parent-child relationship. + * * @return {void} * @method - * @private + * @public */ - _$registerProperty (buffer: Float32Array, index: number = 0): void + remove (): void { - // visible - buffer[index++] = +this._$visible; - - // depth - buffer[index++] = this._$placeId; - - // clip depth - buffer[index++] = this._$clipDepth; - - // isMask - buffer[index++] = +this._$isMask; - - const mask: DisplayObjectImpl | null = this._$mask; - buffer[index++] = +(mask !== null); - if (mask) { - - // mask id - buffer[index++] = mask._$instanceId; - - let maskMatrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - let parent: ParentImpl | null = mask._$parent; - while (parent) { - - maskMatrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - maskMatrix - ); - - parent = parent._$parent; - } - - // mask matrix - buffer[index++] = maskMatrix[0]; - buffer[index++] = maskMatrix[1]; - buffer[index++] = maskMatrix[2]; - buffer[index++] = maskMatrix[3]; - buffer[index++] = maskMatrix[4]; - buffer[index++] = maskMatrix[5]; - } else { - index += 7; - } - - if (this._$visible) { - const transform: Transform = this._$transform; - - // matrix - const matrix: Float32Array = transform._$rawMatrix(); - buffer[index++] = matrix[0]; - buffer[index++] = matrix[1]; - buffer[index++] = matrix[2]; - buffer[index++] = matrix[3]; - buffer[index++] = matrix[4]; - buffer[index++] = matrix[5]; - - // colorTransform - const colorTransform = transform._$rawColorTransform(); - buffer[index++] = colorTransform[0]; - buffer[index++] = colorTransform[1]; - buffer[index++] = colorTransform[2]; - buffer[index++] = colorTransform[3]; - buffer[index++] = colorTransform[4]; - buffer[index++] = colorTransform[5]; - buffer[index++] = colorTransform[6]; - buffer[index++] = colorTransform[7]; - - } else { - index += 6; // matrix - index += 8; // colorTransform - } - - // blend mode - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - buffer[index++] = $blendToNumber(blendMode); - - // scale9Grid - const scale9Grid: Rectangle | null = this._$scale9Grid; - buffer[index++] = +(scale9Grid !== null); - - if (scale9Grid) { - buffer[index++] = scale9Grid.x; - buffer[index++] = scale9Grid.y; - buffer[index++] = scale9Grid.width; - buffer[index++] = scale9Grid.height; - } else { - index += 4; - } - - // filter + displayObjectRemoveService(this); } /** - * @return {object} + * @description 指定の LoaderInfo を DisplayObject に同期します。 + * Synchronizes the specified LoaderInfo with the DisplayObject. + * + * @param {LoaderInfo} loader_info + * @return {void} * @method - * @private + * @protected */ - _$createMessage (): PropertyMessageMapImpl + $syncLoaderInfo (loader_info: LoaderInfo): void { - const message: PropertyMessageImpl = { - "command": "setProperty", - "buffer": new Float32Array(), - "instanceId": this._$instanceId, - "parentId": this._$parent ? this._$parent._$instanceId : -1, - "visible": this._$visible - }; - - if (this._$placeId > -1) { - message.depth = this._$placeId; - } - - if (this._$clipDepth) { - message.clipDepth = this._$clipDepth; - } - - if (this._$isMask) { - message.isMask = this._$isMask; - } - - const mask: DisplayObjectImpl | null = this._$mask; - if (mask) { - message.maskId = mask._$instanceId; - - let maskMatrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - let parent: ParentImpl | null = mask._$parent; - while (parent) { - - maskMatrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - maskMatrix - ); - - parent = parent._$parent; - } - - message.maskMatrix = maskMatrix; - } - - if (this._$visible) { - - const transform: Transform = this._$transform; - - const matrix: Float32Array = transform._$rawMatrix(); - if (matrix[0] !== 1) { - message.a = matrix[0]; - } - - if (matrix[1] !== 0) { - message.b = matrix[1]; - } - - if (matrix[2] !== 0) { - message.c = matrix[2]; - } - - if (matrix[3] !== 1) { - message.d = matrix[3]; - } - - if (matrix[4] !== 0) { - message.tx = matrix[4]; - } - - if (matrix[5] !== 0) { - message.ty = matrix[5]; - } - - const colorTransform = transform._$rawColorTransform(); - if (colorTransform[0] !== 1) { - message.f0 = colorTransform[0]; - } - - if (colorTransform[1] !== 1) { - message.f1 = colorTransform[1]; - } - - if (colorTransform[2] !== 1) { - message.f2 = colorTransform[2]; - } - - if (colorTransform[3] !== 1) { - message.f3 = colorTransform[3]; - } - - if (colorTransform[4] !== 0) { - message.f4 = colorTransform[4]; - } - - if (colorTransform[5] !== 0) { - message.f5 = colorTransform[5]; - } - - if (colorTransform[6] !== 0) { - message.f6 = colorTransform[6]; - } - - if (colorTransform[7] !== 0) { - message.f7 = colorTransform[7]; - } - - const filters: FilterArrayImpl | null = this._$filters || this.filters; - if (filters && filters.length) { - const parameters: any[] = $getArray(); - for (let idx: number = 0; idx < filters.length; ++idx) { - parameters.push(filters[idx]._$toArray()); - } - - message.filters = parameters; - } - - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - if (blendMode !== "normal") { - message.blendMode = blendMode; - } - - const scale9Grid: Rectangle | null = this._$scale9Grid; - if (scale9Grid && this._$isUpdated()) { - - const baseMatrix: Matrix = this - ._$parent - ._$transform - .concatenatedMatrix; - - message.matrixBase = baseMatrix._$matrix.slice(); - $poolMatrix(baseMatrix); - - message.grid = { - "x": scale9Grid.x, - "y": scale9Grid.y, - "w": scale9Grid.width, - "h": scale9Grid.height - }; - } - } - - return message; + $loaderInfoMap.set(this, loader_info); } -} +} \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.test.ts new file mode 100644 index 00000000..2a0bafa6 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.test.ts @@ -0,0 +1,33 @@ +import { execute } from "./DisplayObjectApplyChangesService"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectApplyChangesService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + + displayObject.changed = false; + expect(displayObject.changed).toBe(false); + + execute(displayObject); + expect(displayObject.changed).toBe(true); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + const parent = new DisplayObject(); + displayObject.parent = parent; + + parent.changed = false; + displayObject.changed = false; + expect(parent.changed).toBe(false); + expect(displayObject.changed).toBe(false); + + execute(displayObject); + expect(parent.changed).toBe(true); + expect(displayObject.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.ts b/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.ts new file mode 100644 index 00000000..3d1a493e --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectApplyChangesService.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "../../DisplayObject"; + +/** + * @description DisplayObjectの更新フラグを立てる + * Set the update flag of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + display_object.changed = true; + + const parent = display_object.parent as unknown as D; + if (parent && !parent.changed) { + execute(parent); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectBaseBuildService.ts b/packages/display/src/DisplayObject/service/DisplayObjectBaseBuildService.ts new file mode 100644 index 00000000..9e1e38d5 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectBaseBuildService.ts @@ -0,0 +1,49 @@ +import type { IDictionaryTag } from "../../interface/IDictionaryTag"; +import type { Loader } from "../../Loader"; +import type { MovieClip } from "../../MovieClip"; +import type { DisplayObject } from "../../DisplayObject"; +import { + $loaderInfoMap, + $rootMap +} from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectの基本情報を設定します。 + * Sets the basic information of the DisplayObject. + * + * @param {DisplayObject} display_object + * @param {object} tag + * @param {MovieClip} parent + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object: D, + dictionary_id: number, + tag: IDictionaryTag, + parent: MovieClip | Loader, + placeId: number = -1 +): void => { + + const loaderInfo = parent.loaderInfo; + if (!loaderInfo) { + throw new Error("the loaderInfo or data is null."); + } + + // set parent data + display_object.parent = parent as MovieClip; + $rootMap.set(display_object, parent.root); + $loaderInfoMap.set(display_object, loaderInfo); + + // bind tag data + display_object.dictionaryId = dictionary_id; + display_object.characterId = tag.characterId; + display_object.clipDepth = tag.clipDepth; + display_object.startFrame = tag.startFrame; + display_object.endFrame = tag.endFrame; + display_object.name = tag.name || ""; + + // first frame placeId + display_object.placeId = placeId; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.test.ts new file mode 100644 index 00000000..37f272f0 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./DisplayObjectBlendToNumberService"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectBlendToNumberService.js test", () => +{ + it("execute test case", () => + { + expect(execute("copy")).toBe(0); + expect(execute("add")).toBe(1); + expect(execute("alpha")).toBe(2); + expect(execute("darken")).toBe(3); + expect(execute("difference")).toBe(4); + expect(execute("erase")).toBe(5); + expect(execute("hardlight")).toBe(6); + expect(execute("invert")).toBe(7); + expect(execute("layer")).toBe(8); + expect(execute("lighten")).toBe(9); + expect(execute("multiply")).toBe(10); + expect(execute("normal")).toBe(11); + expect(execute("overlay")).toBe(12); + expect(execute("screen")).toBe(13); + expect(execute("subtract")).toBe(14); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.ts b/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.ts new file mode 100644 index 00000000..1cdcc310 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectBlendToNumberService.ts @@ -0,0 +1,65 @@ +import type { IBlendMode } from "../../interface/IBlendMode"; + +/** + * @description ブレンドモードを数値に変換します。 + * Converts the blend mode to a number. + * + * @param {IBlendMode} blend_mode + * @return {number} + * @method + * @protected + */ +export const execute = (blend_mode: IBlendMode): number => +{ + switch (blend_mode) { + + case "copy": + return 0; + + case "add": + return 1; + + case "alpha": + return 2; + + case "darken": + return 3; + + case "difference": + return 4; + + case "erase": + return 5; + + case "hardlight": + return 6; + + case "invert": + return 7; + + case "layer": + return 8; + + case "lighten": + return 9; + + case "multiply": + return 10; + + case "normal": + return 11; + + case "overlay": + return 12; + + case "screen": + return 13; + + case "subtract": + return 14; + + default: + return 11; // normal + + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.test.ts new file mode 100644 index 00000000..30720148 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.test.ts @@ -0,0 +1,238 @@ +import { execute } from "./DisplayObjectBuildFilterService"; +import type { ISurfaceFilter } from "../../interface/ISurfaceFilter"; +import { describe, expect, it } from "vitest"; +import type { + BevelFilter, + BlurFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; + +describe("DisplayObjectBuildFilterService.js test", () => +{ + it("execute test case1", () => + { + const surfaceFilterList = []; + const filters = execute(surfaceFilterList); + expect(filters.length).toBe(0); + }); + + it("execute BevelFilter test case", () => + { + const surfaceFilterList = [{ + "class": "BevelFilter", + "params": [null, 10, 90, 0x99ffff, 0.5, 0xffffff, 0.2, 10, 20, 2, 2, "full", true] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as BevelFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.distance).toBe(10); + expect(filter.angle).toBe(90); + expect(filter.highlightColor).toBe(0x99ffff); + expect(filter.highlightAlpha).toBe(0.5); + expect(filter.shadowColor).toBe(0xffffff); + expect(filter.shadowAlpha).toBe(0.2); + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.strength).toBe(2); + expect(filter.quality).toBe(2); + expect(filter.type).toBe("full"); + expect(filter.knockout).toBe(true); + }); + + it("execute BlurFilter test case", () => + { + const surfaceFilterList = [{ + "class": "BlurFilter", + "params": [null, 10, 20, 2] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as BlurFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.quality).toBe(2); + }); + + it("execute ColorMatrixFilter test case", () => + { + const surfaceFilterList = [{ + "class": "ColorMatrixFilter", + "params": [null, [0.1, 0, 0, 0, 0, 0, 0.2, 0, 0, 0, 0, 0, 0.3, 0, 0, 0, 0, 0, 0.4, 0]] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as ColorMatrixFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.matrix[0]).toBe(0.1); + expect(filter.matrix[1]).toBe(0); + expect(filter.matrix[2]).toBe(0); + expect(filter.matrix[3]).toBe(0); + expect(filter.matrix[4]).toBe(0); + expect(filter.matrix[5]).toBe(0); + expect(filter.matrix[6]).toBe(0.2); + expect(filter.matrix[7]).toBe(0); + expect(filter.matrix[8]).toBe(0); + expect(filter.matrix[9]).toBe(0); + expect(filter.matrix[10]).toBe(0); + expect(filter.matrix[11]).toBe(0); + expect(filter.matrix[12]).toBe(0.3); + expect(filter.matrix[13]).toBe(0); + expect(filter.matrix[14]).toBe(0); + expect(filter.matrix[15]).toBe(0); + expect(filter.matrix[16]).toBe(0); + expect(filter.matrix[17]).toBe(0); + expect(filter.matrix[18]).toBe(0.4); + expect(filter.matrix[19]).toBe(0); + }); + + it("execute ConvolutionFilter test case", () => + { + const surfaceFilterList = [{ + "class": "ConvolutionFilter", + "params": [null] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as ConvolutionFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.matrixX).toBe(0); + expect(filter.matrixY).toBe(0); + expect(filter.matrix).toBe(null); + expect(filter.divisor).toBe(1); + expect(filter.bias).toBe(0); + expect(filter.preserveAlpha).toBe(true); + expect(filter.clamp).toBe(true); + expect(filter.color).toBe(0); + expect(filter.alpha).toBe(0); + }); + + it("execute DisplacementMapFilter test case", () => + { + const surfaceFilterList = [{ + "class": "DisplacementMapFilter", + "params": [null] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as DisplacementMapFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.bitmapBuffer).toBe(null); + expect(filter.bitmapWidth).toBe(0); + expect(filter.bitmapHeight).toBe(0); + expect(filter.mapPointX).toBe(0); + expect(filter.mapPointY).toBe(0); + expect(filter.componentX).toBe(0); + expect(filter.componentY).toBe(0); + expect(filter.scaleX).toBe(0); + expect(filter.scaleY).toBe(0); + expect(filter.mode).toBe("wrap"); + expect(filter.color).toBe(0); + expect(filter.alpha).toBe(0); + }); + + it("execute DropShadowFilter test case", () => + { + const surfaceFilterList = [{ + "class": "DropShadowFilter", + "params": [null, 5, 90, 0x99ffff, 0.5, 10, 20, 2, 2, true, true, true] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as DropShadowFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.distance).toBe(5); + expect(filter.angle).toBe(90); + expect(filter.color).toBe(0x99ffff); + expect(filter.alpha).toBe(0.5); + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.strength).toBe(2); + expect(filter.quality).toBe(2); + expect(filter.inner).toBe(true); + expect(filter.knockout).toBe(true); + expect(filter.hideObject).toBe(true); + }); + + it("execute GlowFilter test case", () => + { + const surfaceFilterList = [{ + "class": "GlowFilter", + "params": [null, 0x99ffff, 0.5, 10, 20, 2, 2, true, true] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as GlowFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.color).toBe(0x99ffff); + expect(filter.alpha).toBe(0.5); + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.strength).toBe(2); + expect(filter.quality).toBe(2); + expect(filter.inner).toBe(true); + expect(filter.knockout).toBe(true); + }); + + it("execute GradientBevelFilter test case", () => + { + const surfaceFilterList = [{ + "class": "GradientBevelFilter", + "params": [null, 10, 90, [0x99ffff], [0.5], [1], 10, 20, 2, 2, "full", true] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as GradientBevelFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.distance).toBe(10); + expect(filter.angle).toBe(90); + expect((filter.colors as number[])[0]).toBe(0x99ffff); + expect((filter.alphas as number[])[0]).toBe(0.5); + expect((filter.ratios as number[])[0]).toBe(1); + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.strength).toBe(2); + expect(filter.quality).toBe(2); + expect(filter.type).toBe("full"); + expect(filter.knockout).toBe(true); + }); + + it("execute GradientGlowFilter test case", () => + { + const surfaceFilterList = [{ + "class": "GradientGlowFilter", + "params": [null, 10, 90, [0x99ffff], [0.5], [1], 10, 20, 2, 2, "full", true] + }] as ISurfaceFilter[]; + + const filters = execute(surfaceFilterList) as GradientGlowFilter[]; + expect(filters.length).toBe(1); + + const filter = filters[0]; + expect(filter.distance).toBe(10); + expect(filter.angle).toBe(90); + expect((filter.colors as number[])[0]).toBe(0x99ffff); + expect((filter.alphas as number[])[0]).toBe(0.5); + expect((filter.ratios as number[])[0]).toBe(1); + expect(filter.blurX).toBe(10); + expect(filter.blurY).toBe(20); + expect(filter.strength).toBe(2); + expect(filter.quality).toBe(2); + expect(filter.type).toBe("full"); + expect(filter.knockout).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.ts b/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.ts new file mode 100644 index 00000000..c2222fae --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectBuildFilterService.ts @@ -0,0 +1,99 @@ +import type { IFilterArray } from "../../interface/IFilterArray"; +import type { ISurfaceFilter } from "../../interface/ISurfaceFilter"; +import { + BevelFilter, + BlurFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; +import { $getArray } from "../../DisplayObjectUtil"; + +/** + * @description PlaceObjectのSurfaceFilterListを元にフィルターを構築 + * Build filters based on the SurfaceFilterList of PlaceObject + * + * @param {array} surface_filter_list + * @return {array} + * @method + * @protected + */ +export const execute = (surface_filter_list: ISurfaceFilter[]): IFilterArray => +{ + const filters: IFilterArray = $getArray(); + for (let idx: number = 0; idx < surface_filter_list.length; ++idx) { + + const surfaceFilter = surface_filter_list[idx]; + if (!surfaceFilter) { + continue; + } + + if (surfaceFilter.params[0] === null) { + surfaceFilter.params.shift(); + } + + switch (surfaceFilter.class) { + + case "BevelFilter": + filters.push( + new BevelFilter(...surfaceFilter.params) + ); + break; + + case "BlurFilter": + filters.push( + new BlurFilter(...surfaceFilter.params) + ); + break; + + case "ColorMatrixFilter": + filters.push( + new ColorMatrixFilter(...surfaceFilter.params) + ); + break; + + case "ConvolutionFilter": + filters.push( + new ConvolutionFilter(...surfaceFilter.params) + ); + break; + + case "DisplacementMapFilter": + filters.push( + new DisplacementMapFilter(...surfaceFilter.params) + ); + break; + + case "DropShadowFilter": + filters.push( + new DropShadowFilter(...surfaceFilter.params) + ); + break; + + case "GlowFilter": + filters.push( + new GlowFilter(...surfaceFilter.params) + ); + break; + + case "GradientBevelFilter": + filters.push( + new GradientBevelFilter(...surfaceFilter.params) + ); + break; + + case "GradientGlowFilter": + filters.push( + new GradientGlowFilter(...surfaceFilter.params) + ); + break; + + } + } + + return filters; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.test.ts new file mode 100644 index 00000000..ee5d9ce6 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./DisplayObjectCalcBoundsMatrixService"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectCalcBoundsMatrixService.js test", () => +{ + it("execute test case1", () => + { + const bounds = execute(-10, -20, 100, 200, new Float32Array([1, 0, 0, 1, 0, 0])); + expect(bounds[0]).toBe(-10); + expect(bounds[1]).toBe(-20); + expect(bounds[2]).toBe(100); + expect(bounds[3]).toBe(200); + + }); + + it("execute test case2", () => + { + const bounds = execute(-10, -20, 100, 200, new Float32Array([1.2, 0.34, -0.023, 0.3, 20, 40])); + expect(bounds[0]).toBe(3.3999996185302734); + expect(bounds[1]).toBe(30.600000381469727); + expect(bounds[2]).toBe(140.4600067138672); + expect(bounds[3]).toBe(134); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.ts b/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.ts new file mode 100644 index 00000000..85669f86 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectCalcBoundsMatrixService.ts @@ -0,0 +1,46 @@ +import { $getBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectのmatrixを考慮した描画範囲を計算 + * Calculate the drawing range considering the matrix of DisplayObject + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @param {Float32Array} matrix + * @return {array} + * @method + * @protected + */ +export const execute = ( + x_min: number, + y_min: number, + x_max: number, + y_max: number, + matrix: Float32Array +): Float32Array => { + + const m0 = matrix[0]; + const m1 = matrix[1]; + const m2 = matrix[2]; + const m3 = matrix[3]; + const m4 = matrix[4]; + const m5 = matrix[5]; + + const x0 = x_max * m0 + y_max * m2 + m4; + const x1 = x_max * m0 + y_min * m2 + m4; + const x2 = x_min * m0 + y_max * m2 + m4; + const x3 = x_min * m0 + y_min * m2 + m4; + const y0 = x_max * m1 + y_max * m3 + m5; + const y1 = x_max * m1 + y_min * m3 + m5; + const y2 = x_min * m1 + y_max * m3 + m5; + const y3 = x_min * m1 + y_min * m3 + m5; + + return $getBoundsArray( + Math.min(x0, x1, x2, x3), + Math.min(y0, y1, y2, y3), + Math.max(x0, x1, x2, x3), + Math.max(y0, y1, y2, y3) + ); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.test.ts new file mode 100644 index 00000000..627a1bb2 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.test.ts @@ -0,0 +1,14 @@ +import { execute } from "./DisplayObjectDispatchAddedEventService"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectDispatchAddedEventService.js test", () => +{ + it("execute test case", () => + { + const displayObject = new DisplayObject(); + expect(displayObject.$added).toBe(false); + execute(displayObject); + expect(displayObject.$added).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.ts new file mode 100644 index 00000000..6fc93304 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedEventService.ts @@ -0,0 +1,23 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Event } from "@next2d/events"; + +/** + * @description DisplayObjectのADDEDイベントを実行 + * Execute the ADDED event of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + if (display_object.$added) { + return ; + } + + display_object.$added = true; + if (display_object.willTrigger(Event.ADDED)) { + display_object.dispatchEvent(new Event(Event.ADDED, true)); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.test.ts new file mode 100644 index 00000000..a50f6ed6 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.test.ts @@ -0,0 +1,19 @@ +import { execute } from "./DisplayObjectDispatchAddedToStageEventService"; +import { DisplayObject } from "../../DisplayObject"; +import { $stageAssignedMap } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectDispatchAddedToStageEventService.js test", () => +{ + it("execute test case", () => + { + const displayObject = new DisplayObject(); + $stageAssignedMap.add(displayObject.instanceId); + + expect(displayObject.$addedToStage).toBe(false); + execute(displayObject); + + $stageAssignedMap.delete(displayObject.instanceId); + expect(displayObject.$addedToStage).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.ts new file mode 100644 index 00000000..8e3e8cc8 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.ts @@ -0,0 +1,26 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Event } from "@next2d/events"; +import { $stageAssignedMap } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectのADDED_TO_STAGEイベントを実行 + * Execute the ADDED_TO_STAGE event of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + if (display_object.$addedToStage + || !$stageAssignedMap.has(display_object.instanceId) + ) { + return ; + } + + display_object.$addedToStage = true; + if (display_object.willTrigger(Event.ADDED_TO_STAGE)) { + display_object.dispatchEvent(new Event(Event.ADDED_TO_STAGE)); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedEventService.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedEventService.ts new file mode 100644 index 00000000..e8d43945 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedEventService.ts @@ -0,0 +1,23 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Event } from "@next2d/events"; + +/** + * @description DisplayObjectのREMOVEDイベントを実行 + * Execute the REMOVED event of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + if (!display_object.$added) { + return ; + } + + display_object.$added = false; + if (display_object.willTrigger(Event.REMOVED)) { + display_object.dispatchEvent(new Event(Event.REMOVED, true)); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService.ts b/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService.ts new file mode 100644 index 00000000..826bc448 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService.ts @@ -0,0 +1,34 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Event } from "@next2d/events"; +import { $stageAssignedMap } from "../../DisplayObjectUtil"; +import { $cacheStore } from "@next2d/cache"; + +/** + * @description DisplayObjectのREMOVED_FROM_STAGEイベントを実行 + * Execute the REMOVED_FROM_STAGE event of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + if (!display_object.$addedToStage + || !$stageAssignedMap.has(display_object.instanceId) + ) { + return ; + } + + display_object.$addedToStage = false; + if (display_object.willTrigger(Event.REMOVED_FROM_STAGE)) { + display_object.dispatchEvent(new Event(Event.REMOVED_FROM_STAGE)); + } + + // キャッシュストアから削除 + if (display_object.uniqueKey + && $cacheStore.has(display_object.uniqueKey) + ) { + $cacheStore.removeTimer(display_object.uniqueKey); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectGenerateHashService.ts b/packages/display/src/DisplayObject/service/DisplayObjectGenerateHashService.ts new file mode 100644 index 00000000..25238fb1 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectGenerateHashService.ts @@ -0,0 +1,28 @@ +/** + * @description 描画で利用するFloat32Arrayからハッシュ値を生成します。 + * Generates a hash value from the Float32Array used for drawing. + * + * @param {Float32Array} buffer + * @return {number} + * @method + * @protected + */ +export const execute = (buffer: Float32Array): number => +{ + let hash = 2166136261; // FNV-1aオフセット basis + for (let idx = 0; idx < buffer.length; ++idx) { + + let num = buffer[idx] | 0; // 整数として扱う + + // 32bit整数の各バイトを処理 + for (let i = 0; i < 4; i++) { + const byte = num & 0xff; + hash ^= byte; + hash = Math.imul(hash, 16777619); // FNV-1a の FNV prime + num >>>= 8; + } + } + + // 32bitの符号なし整数にキャストし、24bitの範囲に収める + return (hash >>> 0) % 16777216; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.test.ts new file mode 100644 index 00000000..e4dac9a6 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.test.ts @@ -0,0 +1,20 @@ +import { execute } from "./DisplayObjectGetPlaceObjectService"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetPlaceObjectService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + + const mockPlaceObject = { + "matrix": [1, 2, 3, 4, 5, 6] + }; + displayObject.placeObject = mockPlaceObject; + expect(displayObject.placeObject).toBe(mockPlaceObject); + + execute(displayObject); + expect(displayObject.placeObject).toBe(mockPlaceObject); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.ts b/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.ts new file mode 100644 index 00000000..e7a86a76 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectGetPlaceObjectService.ts @@ -0,0 +1,54 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { IMovieClipCharacter } from "../../interface/IMovieClipCharacter"; +import type { IPlaceObject } from "../../interface/IPlaceObject"; +import type { MovieClip } from "../../MovieClip"; + +/** + * @description DisplayObjectのPlaceObjectを返却、存在しない場合はnullを返却 + * Return the PlaceObject of the DisplayObject, or return null if it does not exist. + * + * @param {DisplayObject} display_object + * @return {object} + * @method + * @protected + */ +export const execute = (display_object: D): IPlaceObject | null => +{ + // キャッシュされたPlaceObjectがあれば返却 + if (display_object.placeObject) { + return display_object.placeObject; + } + + const placeId = display_object.placeId; + if (placeId === -1) { + return null; + } + + const parent = display_object.parent; + if (!parent) { + return null; + } + + const loaderInfo = parent.loaderInfo; + if (!loaderInfo || !loaderInfo.data) { + return null; + } + + const character = loaderInfo.data.characters[parent.characterId] as IMovieClipCharacter; + const frame = parent.isTimelineEnabled ? (parent as MovieClip).currentFrame : 1; + const places = character.placeMap[frame]; + if (!places || !(placeId in places)) { + return null; + } + + const id = places[placeId]; + const placeObject: IPlaceObject | void = character.placeObjects[id]; + if (!placeObject) { + return null; + } + + // キャッシュ + display_object.placeObject = placeObject; + + return placeObject; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.test.ts new file mode 100644 index 00000000..f13d1cde --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.test.ts @@ -0,0 +1,29 @@ +import { execute } from "./DisplayObjectGlobalToLocalService"; +import { DisplayObject } from "../../DisplayObject"; +import { Matrix, Point } from "@next2d/geom"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGlobalToLocalService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + const point = execute(displayObject, new Point(10, 10)); + expect(point.x).toBe(10); + expect(point.y).toBe(10); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(1.2, 0.2, -0.3, 1.4, 120, 210); + + const parent = new DisplayObject(); + displayObject.parent = parent; + parent.$matrix = new Matrix(0.8, 0.122, -0.2213, 1.2, 20, 10); + + const point = execute(displayObject, new Point(10, 10)); + expect(point.x).toBe(-142.32785326242447); + expect(point.y).toBe(-128.78454852104187); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.ts b/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.ts new file mode 100644 index 00000000..ce894901 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectGlobalToLocalService.ts @@ -0,0 +1,27 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Point, Matrix } from "@next2d/geom"; + +/** + * @description point オブジェクトを表示オブジェクトの(ローカル)座標からステージ(グローバル)座標に変換します。 + * Converts the point object from the display object's (local) coordinates to the Stage (global) coordinates. + * + * @param {DisplayObject} display_object + * @param {Point} point + * @return {Point} + * @method + * @protected + */ +export const execute = (display_object: D, point: Point): Point => +{ + const matrix = display_object.concatenatedMatrix; + matrix.invert(); + + const newPoint = new Point( + point.x * matrix.a + point.y * matrix.c + matrix.tx, + point.x * matrix.b + point.y * matrix.d + matrix.ty + ); + + Matrix.release(matrix.rawData); + + return newPoint; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.test.ts b/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.test.ts new file mode 100644 index 00000000..ef6e9388 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.test.ts @@ -0,0 +1,29 @@ +import { execute } from "./DisplayObjectLocalToGlobalService"; +import { DisplayObject } from "../../DisplayObject"; +import { Matrix, Point } from "@next2d/geom"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectLocalToGlobalService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + const point = execute(displayObject, new Point(10, 10)); + expect(point.x).toBe(10); + expect(point.y).toBe(10); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(1.2, 0.2, -0.3, 1.4, 120, 210); + + const parent = new DisplayObject(); + displayObject.parent = parent; + parent.$matrix = new Matrix(0.8, 0.122, -0.2213, 1.2, 20, 10); + + const point = execute(displayObject, new Point(10, 10)); + expect(point.x).toBe(73.18620109558105); + expect(point.y).toBe(296.93801552057266); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.ts b/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.ts new file mode 100644 index 00000000..c56056f0 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectLocalToGlobalService.ts @@ -0,0 +1,26 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Point, Matrix } from "@next2d/geom"; + +/** + * @description point オブジェクトを表示オブジェクトの(ローカル)座標からステージ(グローバル)座標に変換します。 + * Converts the point object from the display object's (local) coordinates to the Stage (global) coordinates. + * + * @param {DisplayObject} display_object + * @param {Point} point + * @return {Point} + * @method + * @protected + */ +export const execute = (display_object: D, point: Point): Point => +{ + const matrix = display_object.concatenatedMatrix; + + const newPoint = new Point( + point.x * matrix.a + point.y * matrix.c + matrix.tx, + point.x * matrix.b + point.y * matrix.d + matrix.ty + ); + + Matrix.release(matrix.rawData); + + return newPoint; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts b/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts new file mode 100644 index 00000000..44c13af2 --- /dev/null +++ b/packages/display/src/DisplayObject/service/DisplayObjectRemoveService.ts @@ -0,0 +1,19 @@ +import type { DisplayObject } from "../../DisplayObject"; + +/** + * @description 親子関係を解除する。 + * Break the parent-child relationship. + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + const parent = display_object.parent; + if (!parent) { + return ; + } + parent.removeChild(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.test.ts new file mode 100644 index 00000000..4026f316 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.test.ts @@ -0,0 +1,53 @@ +import { execute } from "./DisplayObjectConcatenatedMatrixUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectConcatenatedMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + + const matrix = execute(displayObject); + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(1, 2, 3, 4, 5, 6); + + const matrix = execute(displayObject); + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(2); + expect(matrix.c).toBe(3); + expect(matrix.d).toBe(4); + expect(matrix.tx).toBe(5); + expect(matrix.ty).toBe(6); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(1, 2, 3, 4, 5, 6); + + const parent = new DisplayObject(); + displayObject.parent = parent; + parent.$matrix = new Matrix(2, 1.12, 1.3, 3, 120, 600); + + const matrix = execute(displayObject); + expect(matrix.a).toBe(4.599999904632568); + expect(matrix.b).toBe(7.119999885559082); + expect(matrix.c).toBe(11.199999809265137); + expect(matrix.d).toBe(15.359999656677246); + expect(matrix.tx).toBe(137.8000030517578); + expect(matrix.ty).toBe(623.5999755859375); + }); + +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.ts new file mode 100644 index 00000000..17f74bd1 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectConcatenatedMatrixUseCase.ts @@ -0,0 +1,35 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; +import { $MATRIX_ARRAY_IDENTITY } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectの連結Matrixを返却 + * Returns the concatenated Matrix of the DisplayObject. + * + * @param {DisplayObject} display_object + * @return {Matrix} + * @method + * @protected + */ +export const execute = (display_object: D): Matrix => +{ + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object); + + let matrix = rawMatrix ? rawMatrix : $MATRIX_ARRAY_IDENTITY; + let parent = display_object.parent; + while (parent) { + + const rawMatrix = displayObjectGetRawMatrixUseCase(parent); + if (rawMatrix) { + matrix = Matrix.multiply(rawMatrix, matrix); + } + + parent = parent.parent; + } + + return new Matrix( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.test.ts new file mode 100644 index 00000000..b320dd8d --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./DisplayObjectGetAlphaUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { ColorTransform } from "@next2d/geom"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetAlphaUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(1); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$alpha = 0.5; + expect(execute(displayObject)).toBe(0.5); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.$colorTransform = new ColorTransform(1, 1, 1, 0.2, 0, 0, 0, 0); + expect(execute(displayObject)).toBe(0.20000000298023224); + }); + + it("execute test case4", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + colorTransform: [1, 1, 1, 0.6, 0, 0, 0, 0], + typedColorTransform: new Float32Array([1, 1, 1, 0.6, 0, 0, 0, 0]) + }; + expect(execute(displayObject)).toBe(0.6000000238418579); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.ts new file mode 100644 index 00000000..3d8de876 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetAlphaUseCase.ts @@ -0,0 +1,30 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../usecase/DisplayObjectGetRawColorTransformUseCase"; + +/** + * @description DisplayObjectのalphaを返却 + * Returns the alpha of the DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + if (display_object.$alpha !== null) { + return display_object.$alpha; + } + + if (display_object.$colorTransform) { + const rawData = display_object.$colorTransform.rawData; + return rawData[3] + rawData[7] / 255; + } + + const colorTransform = displayObjectGetRawColorTransformUseCase(display_object); + if (colorTransform) { + return colorTransform[3] + colorTransform[7] / 255; + } + + return 1; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.test.ts new file mode 100644 index 00000000..fde3a2c2 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.test.ts @@ -0,0 +1,26 @@ +import { execute } from "./DisplayObjectGetBlendModeUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetBlendModeUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe("normal"); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$blendMode = "add"; + expect(execute(displayObject)).toBe("add"); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { blendMode: "alpha" }; + expect(execute(displayObject)).toBe("alpha"); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.ts new file mode 100644 index 00000000..ce0cb7cf --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBlendModeUseCase.ts @@ -0,0 +1,24 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { IBlendMode } from "../../interface/IBlendMode"; +import { execute as displayObjectGetPlaceObjectService } from "../service/DisplayObjectGetPlaceObjectService"; + +/** + * @description DisplayObjectのブレンドモードを返却 + * Returns the blend mode of the DisplayObject + * + * @param {DisplayObject} display_object + * @return {IBlendMode} + * @method + * @protected + */ +export const execute = (display_object: D): IBlendMode => +{ + if (display_object.$blendMode) { + return display_object.$blendMode; + } + + const placeObject = displayObjectGetPlaceObjectService(display_object); + return placeObject && placeObject.blendMode + ? placeObject.blendMode + : "normal"; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts new file mode 100644 index 00000000..ac8d637a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./DisplayObjectGetBoundsUseCase"; +import type { DisplayObject } from "../../DisplayObject"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetBoundsUseCase.js test", () => +{ + it("execute test case1", () => + { + const sprite = new DisplayObjectContainer(); + + const container = new DisplayObjectContainer(); + container.x = 100; + container.y = 100; + sprite.addChild(container); + + const contents = new Shape(); + contents.graphics.drawCircle(0, 0, 100); + container.addChild(contents); + + const bounds1 = execute(contents, container as unknown as DisplayObject); + const bounds2 = execute(contents, sprite as unknown as DisplayObject); + + expect(bounds1.x).toBe(-100); + expect(bounds1.y).toBe(-100); + expect(bounds1.width).toBe(200); + expect(bounds1.height).toBe(200); + expect(bounds2.x).toBe(0); + expect(bounds2.y).toBe(0); + expect(bounds2.width).toBe(200); + expect(bounds2.height).toBe(200); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts new file mode 100644 index 00000000..25eecf99 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetBoundsUseCase.ts @@ -0,0 +1,69 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { Rectangle, Matrix } from "@next2d/geom"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectのmatrixを考慮した描画範囲を計算 + * Calculate the drawing range considering the matrix of DisplayObject + * + * @param {DisplayObject} display_object + * @param {DisplayObject} [target_display_object=null] + * @return {Rectangle} + * @method + * @protected + */ +export const execute = ( + display_object: D, + target_display_object: D | null = null +): Rectangle => { + + const matrix = display_object.concatenatedMatrix; + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + + // to global + const bounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix.rawData + ); + + // pool + $poolBoundsArray(rawBounds); + Matrix.release(matrix.rawData); + + if (!target_display_object) { + const rectangle = new Rectangle( + bounds[0], bounds[1], + Math.abs(bounds[2] - bounds[0]), + Math.abs(bounds[3] - bounds[1]) + ); + + $poolBoundsArray(bounds); + return rectangle; + } + + const targetMatrix: Matrix = target_display_object.concatenatedMatrix; + targetMatrix.invert(); + + const targetBounds = displayObjectCalcBoundsMatrixService( + bounds[0], bounds[1], + bounds[2], bounds[3], + targetMatrix.rawData + ); + + // pool + $poolBoundsArray(bounds); + Matrix.release(targetMatrix.rawData); + + const rectangle = new Rectangle( + targetBounds[0], targetBounds[1], + Math.abs(targetBounds[2] - targetBounds[0]), + Math.abs(targetBounds[3] - targetBounds[1]) + ); + + $poolBoundsArray(targetBounds); + + return rectangle; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetCalcBoundsUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetCalcBoundsUseCase.ts new file mode 100644 index 00000000..1aa300c5 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetCalcBoundsUseCase.ts @@ -0,0 +1,49 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { Video } from "@next2d/media"; +import type { TextField } from "@next2d/text"; +import { $getBoundsArray } from "../../DisplayObjectUtil"; +import { execute as shapeCalcBoundsMatrixUseCase } from "../../Shape/usecase/ShapeCalcBoundsMatrixUseCase"; +import { execute as videoCalcBoundsMatrixUseCase } from "../../Video/usecase/VideoCalcBoundsMatrixUseCase"; +import { execute as textFieldCalcBoundsMatrixUseCase } from "../../TextField/usecase/TextFieldCalcBoundsMatrixUseCase"; +import { execute as displayObjectContainerCalcBoundsMatrixUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase"; + +/** + * @description DisplayObject のローカルバウンディングボックスを取得します。 + * Get the local bounding box of the DisplayObject. + * + * @param {DisplayObject} display_object + * @return {number[]} + * @method + * @protected + */ +export const execute = (display_object: D): Float32Array => +{ + switch (true) { + + case display_object.isContainerEnabled: + return displayObjectContainerCalcBoundsMatrixUseCase( + display_object as unknown as DisplayObjectContainer + ); + + case display_object.isShape: + return shapeCalcBoundsMatrixUseCase( + display_object as unknown as Shape + ); + + case display_object.isText: + return textFieldCalcBoundsMatrixUseCase( + display_object as unknown as TextField + ); + + case display_object.isVideo: + return videoCalcBoundsMatrixUseCase( + display_object as unknown as Video + ); + + default: + return $getBoundsArray(0, 0, 0, 0); + + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetColorTransformUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetColorTransformUseCase.ts new file mode 100644 index 00000000..def30626 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetColorTransformUseCase.ts @@ -0,0 +1,23 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { ColorTransform } from "@next2d/geom"; +import { execute as displayObjectGetRawColorTransformUseCase } from "./DisplayObjectGetRawColorTransformUseCase"; + +/** + * @description DisplayObject の ColorTransform を取得します。 + * Get the ColorTransform of DisplayObject. + * + * @param {D} display_object + * @return {ColorTransform} + * @method + * @protected + */ +export const execute = (display_object: D): ColorTransform => +{ + const colorTransform = displayObjectGetRawColorTransformUseCase(display_object); + return colorTransform + ? new ColorTransform( + colorTransform[0], colorTransform[1], colorTransform[2], colorTransform[3], + colorTransform[4], colorTransform[5], colorTransform[6], colorTransform[7] + ) + : new ColorTransform(); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.test.ts new file mode 100644 index 00000000..f524544a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.test.ts @@ -0,0 +1,29 @@ +import { execute } from "./DisplayObjectGetFiltersUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { BlurFilter } from "@next2d/filters"; + +describe("DisplayObjectGetFiltersUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(null); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$filters = [new BlurFilter(), new BlurFilter()]; + const filters = execute(displayObject); + expect(filters?.length).toBe(2); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.filters = [new BlurFilter()]; + const filters = execute(displayObject); + expect(filters?.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.ts new file mode 100644 index 00000000..482a6295 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetFiltersUseCase.ts @@ -0,0 +1,35 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { IFilterArray } from "../../interface/IFilterArray"; +import { execute as displayObjectGetPlaceObjectService } from "../service/DisplayObjectGetPlaceObjectService"; +import { execute as displayObjectBuildFilterService } from "../service/DisplayObjectBuildFilterService"; + +/** + * @description DisplayObjectのフィルタを返却、存在しない場合はnullを返却 + * Return the filter of the DisplayObject, or return null if it does not exist. + * + * @param {DisplayObject} display_object + * @return {IFilterArray | null} + * @method + * @protected + */ +export const execute = (display_object: D): IFilterArray | null => +{ + if (display_object.$filters) { + return display_object.$filters; + } + + const placeObject = displayObjectGetPlaceObjectService(display_object); + if (placeObject && placeObject.surfaceFilterList) { + + // build filter + if (!placeObject.filters) { + placeObject.filters = displayObjectBuildFilterService( + placeObject.surfaceFilterList + ); + } + + return placeObject.filters; + } + + return null; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.test.ts new file mode 100644 index 00000000..767d1eb2 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.test.ts @@ -0,0 +1,105 @@ +import { execute } from "./DisplayObjectGetHeightUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Video } from "@next2d/media"; +import { TextField } from "@next2d/text"; +import { Matrix } from "@next2d/geom"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetHeightUseCase.js test", () => +{ + it("execute test shape case1", () => + { + const shape = new Shape(); + shape.graphics.xMin = 10; + shape.graphics.xMax = 100; + shape.graphics.yMin = 20; + shape.graphics.yMax = 200; + expect(execute(shape)).toBe(180); + }); + + it("execute test shape case2", () => + { + const shape = new Shape(); + shape.$matrix = new Matrix(); + shape.$matrix.scale(2, 3); + + shape.graphics.xMin = 10; + shape.graphics.xMax = 100; + shape.graphics.yMin = 20; + shape.graphics.yMax = 200; + + expect(execute(shape)).toBe(540); + }); + + it("execute test video case1", () => + { + const video = new Video(100, 300); + expect(execute(video)).toBe(300); + }); + + it("execute test video case2", () => + { + const video = new Video(100, 300); + video.$matrix = new Matrix(); + video.$matrix.scale(2, 3); + + expect(execute(video)).toBe(900); + }); + + it("execute test text case1", () => + { + const textField = new TextField(); + expect(execute(textField)).toBe(100); + }); + + it("execute test text case2", () => + { + const textField = new TextField(); + textField.$matrix = new Matrix(); + textField.$matrix.scale(2, 3); + + expect(execute(textField)).toBe(300); + }); + + it("execute test container case1", () => + { + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 120; + shape.graphics.xMax = 220; + shape.graphics.yMin = 120; + shape.graphics.yMax = 220; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + expect(execute(container)).toBe(300); + }); + + it("execute test container case2", () => + { + const container = new DisplayObjectContainer(); + container.$matrix = new Matrix(); + container.$matrix.scale(2, 3); + + const shape = new Shape(); + shape.graphics.xMin = -20; + shape.graphics.xMax = 120; + shape.graphics.yMin = -20; + shape.graphics.yMax = 20; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + expect(execute(container)).toBe(960); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.ts new file mode 100644 index 00000000..f3f4df54 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetHeightUseCase.ts @@ -0,0 +1,32 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; +import { execute as displayObjectGetCalcBoundsUseCase } from "./DisplayObjectGetCalcBoundsUseCase"; + +/** + * @description DisplayObjectの高さを返却 + * DisplayObject height returned + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const bounds = displayObjectGetCalcBoundsUseCase(display_object); + + const height: number = Math.abs(bounds[3] - bounds[1]); + $poolBoundsArray(bounds); + + switch (true) { + + case height === 0: + case height === Infinity: + case height === -Infinity: + return 0; + + default: + return Math.round(height * 100) / 100; + + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetMatrixUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetMatrixUseCase.ts new file mode 100644 index 00000000..2ee22946 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetMatrixUseCase.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "./DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObject の Matrix を取得します。 + * Get the Matrix of DisplayObject. + * + * @param {D} display_object + * @return {Matrix} + * @method + * @protected + */ +export const execute = (display_object: D): Matrix => +{ + const matrix = displayObjectGetRawMatrixUseCase(display_object); + return matrix + ? new Matrix(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]) + : new Matrix(); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawBoundsUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawBoundsUseCase.ts new file mode 100644 index 00000000..880e1035 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawBoundsUseCase.ts @@ -0,0 +1,49 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { Video } from "@next2d/media"; +import type { TextField } from "@next2d/text"; +import { execute as displayObjectContainerRawBoundsMatrixUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase"; +import { execute as shapeGetRawBoundsService } from "../../Shape/service/ShapeGetRawBoundsService"; +import { execute as textFieldGetRawBoundsService } from "../../TextField/service/TextFieldGetRawBoundsService"; +import { execute as videoGetRawBoundsService } from "../../Video/service/VideoGetRawBoundsService"; +import { $getBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description matrixを含まないバウンディングボックスを返却 + * Return bounding box without matrix + * + * @param {DisplayObject} display_object + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (display_object: D): Float32Array => +{ + switch (true) { + + case display_object.isContainerEnabled: + return displayObjectContainerRawBoundsMatrixUseCase( + display_object as unknown as DisplayObjectContainer + ); + + case display_object.isShape: + return shapeGetRawBoundsService( + display_object as unknown as Shape + ); + + case display_object.isText: + return textFieldGetRawBoundsService( + display_object as unknown as TextField + ); + + case display_object.isVideo: + return videoGetRawBoundsService( + display_object as unknown as Video + ); + + default: + return $getBoundsArray(0, 0, 0, 0); + + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.test.ts new file mode 100644 index 00000000..4994fec7 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.test.ts @@ -0,0 +1,55 @@ +import { execute } from "./DisplayObjectGetRawColorTransformUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { ColorTransform } from "@next2d/geom"; + +describe("DisplayObjectGetRawColorTransformUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + const colorTransform = execute(displayObject); + expect(colorTransform).toBeNull(); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$colorTransform = new ColorTransform(8, 7, 6, 5, 4, 3, 2, 1); + const colorTransform = execute(displayObject); + if (!colorTransform) { + throw new Error("colorTransform is null"); + } + + expect(colorTransform[0]).toBe(8); + expect(colorTransform[1]).toBe(7); + expect(colorTransform[2]).toBe(6); + expect(colorTransform[3]).toBe(5); + expect(colorTransform[4]).toBe(4); + expect(colorTransform[5]).toBe(3); + expect(colorTransform[6]).toBe(2); + expect(colorTransform[7]).toBe(1); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + colorTransform: [1, 2, 3, 4, 5, 6, 7, 8] + }; + + const colorTransform = execute(displayObject); + if (!colorTransform) { + throw new Error("colorTransform is null"); + } + + expect(colorTransform[0]).toBe(1); + expect(colorTransform[1]).toBe(2); + expect(colorTransform[2]).toBe(3); + expect(colorTransform[3]).toBe(4); + expect(colorTransform[4]).toBe(5); + expect(colorTransform[5]).toBe(6); + expect(colorTransform[6]).toBe(7); + expect(colorTransform[7]).toBe(8); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.ts new file mode 100644 index 00000000..23555998 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase.ts @@ -0,0 +1,37 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { IPlaceObject } from "../../interface/IPlaceObject"; +import { execute as displayObjectGetPlaceObjectService } from "../service/DisplayObjectGetPlaceObjectService"; +import { $getFloat32Array8 } from "../../DisplayObjectUtil"; + +/** + * @description 現在のフレームのColorTransformを返却、存在しない場合はnullを返却 + * Returns the ColorTransform of the current frame, or null if it does not exist. + * + * @param {DisplayObject} display_object + * @return {Float32Array | null} + * @method + * @protected + */ +export const execute = (display_object: D): Float32Array | null => +{ + if (display_object.$colorTransform) { + return display_object.$colorTransform.rawData; + } + + const placeObject: IPlaceObject | null = displayObjectGetPlaceObjectService(display_object); + if (!placeObject || !placeObject.colorTransform) { + return null; + } + + if (!placeObject.typedColorTransform) { + const colorTransform: number[] = placeObject.colorTransform; + placeObject.typedColorTransform = $getFloat32Array8( + colorTransform[0], colorTransform[1], + colorTransform[2], colorTransform[3], + colorTransform[4], colorTransform[5], + colorTransform[6], colorTransform[7] + ); + } + + return placeObject.typedColorTransform; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.test.ts new file mode 100644 index 00000000..53f6f723 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.test.ts @@ -0,0 +1,51 @@ +import { execute } from "./DisplayObjectGetRawMatrixUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetRawMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + const matrix = execute(displayObject); + expect(matrix).toBeNull(); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(10, 20, 30, 40, 50, 60); + const matrix = execute(displayObject); + if (!matrix) { + throw new Error("matrix is null"); + } + + expect(matrix[0]).toBe(10); + expect(matrix[1]).toBe(20); + expect(matrix[2]).toBe(30); + expect(matrix[3]).toBe(40); + expect(matrix[4]).toBe(50); + expect(matrix[5]).toBe(60); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [1, 2, 3, 4, 5, 6] + }; + + const matrix = execute(displayObject); + if (!matrix) { + throw new Error("matrix is null"); + } + + expect(matrix[0]).toBe(1); + expect(matrix[1]).toBe(2); + expect(matrix[2]).toBe(3); + expect(matrix[3]).toBe(4); + expect(matrix[4]).toBe(5); + expect(matrix[5]).toBe(6); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.ts new file mode 100644 index 00000000..e70cbeea --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase.ts @@ -0,0 +1,36 @@ +import type { IPlaceObject } from "../../interface/IPlaceObject"; +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetPlaceObjectService } from "../service/DisplayObjectGetPlaceObjectService"; +import { $getFloat32Array6 } from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectの内部Float32Arrayデータを返却、存在しない場合は固定のFloat32Arrayデータを返却 + * Return the internal Float32Array data of the DisplayObject, + * or return a fixed Float32Array data if it does not exist. + * + * @param {DisplayObject} display_object + * @return {Float32Array | null} + * @method + * @protected + */ +export const execute = (display_object: D): Float32Array | null => +{ + if (display_object.$matrix) { + return display_object.$matrix.rawData; + } + + const placeObject: IPlaceObject | null = displayObjectGetPlaceObjectService(display_object); + if (!placeObject || !placeObject.matrix) { + return null; + } + + if (!placeObject.typedMatrix) { + const matrix: number[] = placeObject.matrix; + placeObject.typedMatrix = $getFloat32Array6( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); + } + + return placeObject.typedMatrix; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.test.ts new file mode 100644 index 00000000..15ad114d --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./DisplayObjectGetRotationUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetRotationUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(); + displayObject.$matrix.rotate(50 / 180 * Math.PI); + expect(execute(displayObject)).toBe(49.99999868188683); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [ + 0.7071067690849304, + 0.7071067690849304, + -0.7071067690849304, + 0.7071067690849304, + 0, + 0 + ] + }; + expect(execute(displayObject)).toBe(45); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.ts new file mode 100644 index 00000000..1d930e0b --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetRotationUseCase.ts @@ -0,0 +1,28 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description ラジアンから度に変換するための定数 + * Constant for converting radians to degrees + * + * @type {number} + */ +const $Rad2Deg: number = 180 / Math.PI; + +/** + * @description DisplayObjectの回転角度を返却 + * Returns the rotation angle of the DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const matrix = display_object.$matrix + ? display_object.$matrix.rawData + : displayObjectGetRawMatrixUseCase(display_object); + + return matrix ? Math.atan2(matrix[1], matrix[0]) * $Rad2Deg : 0; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.test.ts new file mode 100644 index 00000000..72e0bcc2 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./DisplayObjectGetScaleXUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetScaleXUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(1); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(); + displayObject.$matrix.scale(3, 1); + expect(execute(displayObject)).toBe(3); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [ + 0.7071067690849304, + 0.7071067690849304, + -0.7071067690849304, + 0.7071067690849304, + 0, + 0 + ] + }; + expect(execute(displayObject)).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.ts new file mode 100644 index 00000000..843eb85a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleXUseCase.ts @@ -0,0 +1,39 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのx軸方向の拡大率を返却 + * Returns the scaling factor in the x-axis direction of the DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const matrix = display_object.$matrix + ? display_object.$matrix.rawData + : displayObjectGetRawMatrixUseCase(display_object); + + if (!matrix) { + return 1; + } + + let xScale = Math.sqrt( + matrix[0] * matrix[0] + + matrix[1] * matrix[1] + ); + + if (!Number.isInteger(xScale)) { + const value: string = xScale.toString(); + const index: number = value.indexOf("e"); + if (index !== -1) { + xScale = +value.slice(0, index); + } + + xScale = Math.round(xScale * 10000) / 10000; + } + + return 0 > matrix[0] ? xScale * -1 : xScale; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.test.ts new file mode 100644 index 00000000..beb75325 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./DisplayObjectGetScaleYUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetScaleYUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(1); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(); + displayObject.$matrix.scale(1, 3); + expect(execute(displayObject)).toBe(3); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [ + 0.7071067690849304, + 0.7071067690849304, + -0.7071067690849304, + 0.7071067690849304, + 0, + 0 + ] + }; + expect(execute(displayObject)).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.ts new file mode 100644 index 00000000..2bc801f6 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetScaleYUseCase.ts @@ -0,0 +1,38 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのy軸方向の拡大率を返却 + * Returns the scaling factor in the y-axis direction of the DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const matrix = display_object.$matrix + ? display_object.$matrix.rawData + : displayObjectGetRawMatrixUseCase(display_object); + + if (!matrix) { + return 1; + } + + let yScale = Math.sqrt( + matrix[2] * matrix[2] + + matrix[3] * matrix[3] + ); + + if (!Number.isInteger(yScale)) { + const value: string = yScale.toString(); + const index: number = value.indexOf("e"); + if (index !== -1) { + yScale = +value.slice(0, index); + } + yScale = Math.round(yScale * 10000) / 10000; + } + + return 0 > matrix[0] ? yScale * -1 : yScale; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.test.ts new file mode 100644 index 00000000..acf17dc6 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.test.ts @@ -0,0 +1,105 @@ +import { execute } from "./DisplayObjectGetWidthUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Video } from "@next2d/media"; +import { TextField } from "@next2d/text"; +import { Matrix } from "@next2d/geom"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectGetWidthUseCase.js test", () => +{ + it("execute test shape case1", () => + { + const shape = new Shape(); + shape.graphics.xMin = 10; + shape.graphics.xMax = 100; + shape.graphics.yMin = 20; + shape.graphics.yMax = 200; + expect(execute(shape)).toBe(90); + }); + + it("execute test shape case2", () => + { + const shape = new Shape(); + shape.$matrix = new Matrix(); + shape.$matrix.scale(2, 3); + + shape.graphics.xMin = 10; + shape.graphics.xMax = 100; + shape.graphics.yMin = 20; + shape.graphics.yMax = 200; + + expect(execute(shape)).toBe(180); + }); + + it("execute test video case1", () => + { + const video = new Video(100, 300); + expect(execute(video)).toBe(100); + }); + + it("execute test video case2", () => + { + const video = new Video(100, 300); + video.$matrix = new Matrix(); + video.$matrix.scale(2, 3); + + expect(execute(video)).toBe(200); + }); + + it("execute test text case1", () => + { + const textField = new TextField(); + expect(execute(textField)).toBe(100); + }); + + it("execute test text case2", () => + { + const textField = new TextField(); + textField.$matrix = new Matrix(); + textField.$matrix.scale(2, 3); + + expect(execute(textField)).toBe(200); + }); + + it("execute test container case1", () => + { + const container = new DisplayObjectContainer(); + const shape = new Shape(); + shape.graphics.xMin = 120; + shape.graphics.xMax = 220; + shape.graphics.yMin = 120; + shape.graphics.yMax = 220; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + expect(execute(container)).toBe(220); + }); + + it("execute test container case2", () => + { + const container = new DisplayObjectContainer(); + container.$matrix = new Matrix(); + container.$matrix.scale(2, 3); + + const shape = new Shape(); + shape.graphics.xMin = -20; + shape.graphics.xMax = 120; + shape.graphics.yMin = -20; + shape.graphics.yMax = 20; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + expect(execute(container)).toBe(280); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.ts new file mode 100644 index 00000000..07a2bf72 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetWidthUseCase.ts @@ -0,0 +1,32 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; +import { execute as displayObjectGetCalcBoundsUseCase } from "./DisplayObjectGetCalcBoundsUseCase"; + +/** + * @description DisplayObjectの幅を返却 + * DisplayObject width returned + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const bounds = displayObjectGetCalcBoundsUseCase(display_object); + + const width: number = Math.abs(bounds[2] - bounds[0]); + $poolBoundsArray(bounds); + + switch (true) { + + case width === 0: + case width === Infinity: + case width === -Infinity: + return 0; + + default: + return Math.round(width * 100) / 100; + + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.test.ts new file mode 100644 index 00000000..c2efba78 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./DisplayObjectGetXUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetXUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(); + displayObject.$matrix.translate(120, 0); + expect(execute(displayObject)).toBe(120); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [1, 0, 0, 1, 320, 0] + }; + expect(execute(displayObject)).toBe(320); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.ts new file mode 100644 index 00000000..ac2b1a37 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetXUseCase.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのx座標を返却 + * Return x-coordinate of DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const matrix = display_object.$matrix + ? display_object.$matrix.rawData + : displayObjectGetRawMatrixUseCase(display_object); + + return matrix ? matrix[4] : 0; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.test.ts new file mode 100644 index 00000000..7bd0e052 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./DisplayObjectGetYUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("DisplayObjectGetYUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + expect(execute(displayObject)).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$matrix = new Matrix(); + displayObject.$matrix.translate(120, 180); + expect(execute(displayObject)).toBe(180); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.placeObject = { + matrix: [1, 0, 0, 1, 320, 400] + }; + expect(execute(displayObject)).toBe(400); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.ts new file mode 100644 index 00000000..097ad022 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectGetYUseCase.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのy座標を返却 + * Return y-coordinate of DisplayObject + * + * @param {DisplayObject} display_object + * @return {number} + * @method + * @protected + */ +export const execute = (display_object: D): number => +{ + const matrix = display_object.$matrix + ? display_object.$matrix.rawData + : displayObjectGetRawMatrixUseCase(display_object); + + return matrix ? matrix[5] : 0; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts new file mode 100644 index 00000000..6231f8c4 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.test.ts @@ -0,0 +1,55 @@ +import { execute } from "./DisplayObjectHitTestObjectUseCase"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectHitTestObjectUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + + const shape1 = container.addChild(new Shape()); + shape1 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape1.x = 32; + shape1.y = 32; + + const shape2 = container.addChild(new Shape()); + shape2 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape2.x = 65; + shape2.y = 65; + expect(execute(shape1, shape2)).toBe(false); + }); + + it("execute test case2", () => + { + const container = new DisplayObjectContainer(); + + const shape1 = container.addChild(new Shape()); + shape1 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape1.x = 32; + shape1.y = 32; + + const shape2 = container.addChild(new Shape()); + shape2 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape2.x = 65; + shape2.y = 65; + expect(execute(shape1, shape2)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts new file mode 100644 index 00000000..bbabb70a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestObjectUseCase.ts @@ -0,0 +1,55 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { Matrix } from "@next2d/geom"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description + * @param {DisplayObject} display_object + * @param {DisplayObject} target_display_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object: D, + target_display_object: D +): boolean => { + + const rawBounds1 = displayObjectGetRawBoundsUseCase(display_object); + const matrix1 = display_object.concatenatedMatrix; + const bounds1 = displayObjectCalcBoundsMatrixService( + rawBounds1[0], rawBounds1[1], + rawBounds1[2], rawBounds1[3], + matrix1.rawData + ); + + // pool + $poolBoundsArray(rawBounds1); + Matrix.release(matrix1.rawData); + + const rawBounds2 = displayObjectGetRawBoundsUseCase(target_display_object); + const matrix2 = target_display_object.concatenatedMatrix; + const bounds2 = displayObjectCalcBoundsMatrixService( + rawBounds2[0], rawBounds2[1], + rawBounds2[2], rawBounds2[3], + matrix2.rawData + ); + + // pool + $poolBoundsArray(rawBounds2); + Matrix.release(matrix2.rawData); + + // calc + const sx = Math.max(bounds1[0], bounds2[0]); + const sy = Math.max(bounds1[1], bounds2[1]); + const ex = Math.min(bounds1[2], bounds2[2]); + const ey = Math.min(bounds1[3], bounds2[3]); + + // pool + $poolBoundsArray(bounds1); + $poolBoundsArray(bounds2); + + return ex - sx >= 0 && ey - sy >= 0; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.test.ts new file mode 100644 index 00000000..1f33e375 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.test.ts @@ -0,0 +1,38 @@ +import { execute } from "./DisplayObjectHitTestPointUseCase"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectHitTestPointUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + + const shape1 = container.addChild(new Shape()); + shape1 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape1.x = 0; + shape1.y = 0; + expect(execute(shape1, 0, 0)).toBe(true); + expect(execute(shape1, 33, 33)).toBe(false); + + const shape2 = container.addChild(new Shape()); + shape2 + .graphics + .beginFill() + .drawRect(0, 0, 32, 32); + + shape2.x = 32; + shape2.y = 32; + expect(execute(shape2, 60, 60)).toBe(true); + expect(execute(shape2, 10, 10)).toBe(false); + + + expect(execute(container, 10, 10)).toBe(true); + expect(execute(container, 65, 65)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts new file mode 100644 index 00000000..d03c534f --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectHitTestPointUseCase.ts @@ -0,0 +1,135 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { TextField } from "@next2d/text"; +import type { Video } from "@next2d/media"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../service/DisplayObjectCalcBoundsMatrixService"; +import { execute as displayObjectGetRawMatrixUseCase } from "./DisplayObjectGetRawMatrixUseCase"; +import { execute as displayObjectContainerMouseHitUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase"; +import { execute as shapeHitTestUseCase } from "../../Shape/usecase/ShapeHitTestUseCase"; +import { execute as textFieldHitTestUseCase } from "../../TextField/usecase/TextFieldHitTestUseCase"; +import { execute as videoHitTestUseCase } from "../../Video/usecase/VideoHitTestUseCase"; +import { + Rectangle, + Point +} from "@next2d/geom"; +import { + $MATRIX_ARRAY_IDENTITY, + $poolBoundsArray, + $colorContext +} from "../../DisplayObjectUtil"; + +/** + * @type {IPlayerHitObject} + * @private + */ +const $hitObject: IPlayerHitObject = { + "x": 0, + "y": 0, + "pointer": "", + "hit": null +}; + +/** + * @description 指定された DisplayObject が指定された座標にヒットしているかどうかを返します + * Returns whether the specified DisplayObject hits the specified coordinates + * + * @param {DisplayObject} display_object + * @param {number} x + * @param {number} y + * @param {boolean} [shape_flag=false] + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object: D, + x: number, + y: number, + shape_flag: boolean = false +): boolean => { + + if (shape_flag) { + + const parent = display_object.parent; + + const matrix = parent + ? parent.concatenatedMatrix.rawData + : $MATRIX_ARRAY_IDENTITY; + + $colorContext.setTransform(1, 0, 0, 1, 0, 0); + $colorContext.beginPath(); + + $hitObject.x = x; + $hitObject.y = y; + $hitObject.hit = null; + + switch (true) { + + case display_object.isContainerEnabled: + return displayObjectContainerMouseHitUseCase( + display_object as unknown as DisplayObjectContainer, + $colorContext, + matrix, + $hitObject + ); + + case display_object.isShape: + return shapeHitTestUseCase( + display_object as unknown as Shape, + $colorContext, + matrix, + $hitObject + ); + + case display_object.isText: + return textFieldHitTestUseCase( + display_object as unknown as TextField, + $colorContext, + matrix, + $hitObject + ); + + case display_object.isVideo: + return videoHitTestUseCase( + display_object as unknown as Video, + $colorContext, + matrix, + $hitObject + ); + + default: + return false; + + } + } + + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + const martix = displayObjectGetRawMatrixUseCase(display_object); + const bounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + martix ? martix : $MATRIX_ARRAY_IDENTITY + ); + + // pool + $poolBoundsArray(rawBounds); + + const rectangle = new Rectangle( + bounds[0], bounds[1], + Math.abs(bounds[2] - bounds[0]), + Math.abs(bounds[3] - bounds[1]) + ); + + // pool + $poolBoundsArray(bounds); + + const parent = display_object.parent; + const point = parent + ? parent.globalToLocal(new Point(x, y)) + : new Point(x, y); + + return rectangle.containsPoint(point); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectIsMaskReflectedInDisplayUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectIsMaskReflectedInDisplayUseCase.ts new file mode 100644 index 00000000..3a31cb40 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectIsMaskReflectedInDisplayUseCase.ts @@ -0,0 +1,93 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { TextField } from "@next2d/text"; +import type { Video } from "@next2d/media"; +import { execute as shapeCalcBoundsMatrixUseCase } from "../../Shape/usecase/ShapeCalcBoundsMatrixUseCase"; +import { execute as textFieldCalcBoundsMatrixUseCase } from "../../TextField/usecase/TextFieldCalcBoundsMatrixUseCase"; +import { execute as videoCalcBoundsMatrixUseCase } from "../../Video/usecase/VideoCalcBoundsMatrixUseCase"; +import { execute as displayObjectContainerCalcBoundsMatrixUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase"; + +/** + * @description DisplayObjectのマスク描画範囲を計算して、マスク描画が実行可能かどうかを返します。 + * Calculate the mask drawing area of DisplayObject and return whether the mask drawing is executable. + * + * @param {DisplayObject} display_object + * @param {Float32Array} matrix + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object: D, + matrix: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): Float32Array | null => { + + let bounds: Float32Array | null = null; + switch (true) { + + case display_object.isContainerEnabled: + bounds = displayObjectContainerCalcBoundsMatrixUseCase( + display_object as unknown as DisplayObjectContainer, matrix + ); + break; + + case display_object.isShape: + bounds = shapeCalcBoundsMatrixUseCase( + display_object as unknown as Shape, matrix + ); + break; + + case display_object.isText: + bounds = textFieldCalcBoundsMatrixUseCase( + display_object as unknown as TextField, matrix + ); + break; + + case display_object.isVideo: + bounds = videoCalcBoundsMatrixUseCase( + display_object as unknown as Video, matrix + ); + break; + + default: + break; + + } + + if (!bounds) { + return null; + } + + const xMin = bounds[0]; + const xMax = bounds[2]; + const width = Math.abs(xMax - xMin); + if (!width) { + return null; + } + + const yMin = bounds[1]; + const yMax = bounds[3]; + const height = Math.abs(yMax - yMin); + if (!height) { + return null; + } + + if (point_x > xMin + width + || point_y > yMin + height + || xMin > renderer_width + || yMin > renderer_height + ) { + return null; + } + + return bounds; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.test.ts new file mode 100644 index 00000000..2c31c7f3 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.test.ts @@ -0,0 +1,50 @@ +import { execute } from "./DisplayObjectSetAlphaUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetAlphaUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$alpha).toBe(null); + expect(displayObject.$colorTransform).toBe(null); + + execute(displayObject, 0.5); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$alpha).toBe(0.5); + + const rawData = displayObject.$colorTransform?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(1); + expect(rawData[2]).toBe(1); + expect(rawData[3]).toBe(0.5); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + expect(rawData[6]).toBe(0); + expect(rawData[7]).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$alpha = 0.2; + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$alpha).toBe(0.2); + + execute(displayObject, 0.2); + + expect(displayObject.changed).toBe(false); + expect(displayObject.$alpha).toBe(0.2); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.ts new file mode 100644 index 00000000..e92f42fc --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetAlphaUseCase.ts @@ -0,0 +1,38 @@ +import { ColorTransform } from "@next2d/geom"; +import type { DisplayObject } from "../../DisplayObject"; +import { $clamp } from "../../DisplayObjectUtil"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../usecase/DisplayObjectGetRawColorTransformUseCase"; + +/** + * @description DisplayObjectのalphaを設定 + * Set the alpha of the DisplayObject + * + * @param {DisplayObject} display_object + * @param {number} alpha + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, alpha: number): void => +{ + alpha = $clamp(alpha, 0, 1); + if (display_object.$alpha === alpha) { + return ; + } + + display_object.$alpha = alpha; + + let colorTransform = display_object.$colorTransform; + if (!colorTransform) { + const rawData = displayObjectGetRawColorTransformUseCase(display_object); + colorTransform = rawData + ? new ColorTransform(...rawData) + : new ColorTransform(); + } + + colorTransform.alphaMultiplier = alpha; + colorTransform.alphaOffset = 0; + display_object.$colorTransform = colorTransform; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.test.ts new file mode 100644 index 00000000..4a55732c --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.test.ts @@ -0,0 +1,35 @@ +import { execute } from "./DisplayObjectSetBlendModeUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetBlendModeUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$blendMode).toBe(null); + + execute(displayObject, "copy"); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$blendMode).toBe("copy"); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$blendMode = "darken"; + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$blendMode).toBe("darken"); + + execute(displayObject, "darken"); + + expect(displayObject.changed).toBe(false); + expect(displayObject.$blendMode).toBe("darken"); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.ts new file mode 100644 index 00000000..af5869ff --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetBlendModeUseCase.ts @@ -0,0 +1,22 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import type { IBlendMode } from "../../interface/IBlendMode"; + +/** + * @description DisplayObjectのブレンドモードをセット + * Set the blend mode of the DisplayObject + * + * @param {DisplayObject} display_object + * @param {IBlendMode} blend_mode + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, blend_mode: IBlendMode): void => +{ + if (display_object.$blendMode === blend_mode) { + return ; + } + display_object.$blendMode = blend_mode; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.test.ts new file mode 100644 index 00000000..47056dcd --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./DisplayObjectSetFiltersUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; +import { BlurFilter } from "@next2d/filters"; + +describe("DisplayObjectSetFiltersUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$filters).toBe(null); + + const filters = [new BlurFilter()]; + execute(displayObject, filters); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$filters).toBe(filters); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.ts new file mode 100644 index 00000000..1ccd124f --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetFiltersUseCase.ts @@ -0,0 +1,19 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { IFilterArray } from "../../interface/IFilterArray"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; + +/** + * @description DisplayObjectにフィルタを設定 + * Set a filter to the DisplayObject. + * + * @param {DisplayObject} display_object + * @param {IFilterArray | null} filters + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, filters: IFilterArray | null): void => +{ + display_object.$filters = filters; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.test.ts new file mode 100644 index 00000000..078ae7ec --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.test.ts @@ -0,0 +1,101 @@ +import { execute } from "./DisplayObjectSetHeightUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Video } from "@next2d/media"; +import { TextField } from "@next2d/text"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetHeightUseCase.js test", () => +{ + it("execute test case1", () => + { + const shape = new Shape(); + shape.graphics.yMax = 100; + shape.graphics.yMin = 200; + shape.changed = false; + + expect(shape.changed).toBe(false); + expect(shape.$scaleY).toBe(null); + expect(shape.$matrix).toBe(null); + + execute(shape, 50); + + expect(shape.changed).toBe(true); + expect(shape.$scaleY).toBe(0.5); + + const rawData = shape.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(0.5); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const video = new Video(100, 200); + video.changed = false; + + expect(video.changed).toBe(false); + expect(video.$scaleY).toBe(null); + expect(video.$matrix).toBe(null); + + execute(video, 50); + + expect(video.changed).toBe(true); + expect(video.$scaleY).toBe(0.25); + + const rawData = video.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(0.25); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case3", () => + { + const container = new DisplayObjectContainer(); + container.changed = false; + expect(container.changed).toBe(false); + + const shape = new Shape(); + shape.graphics.xMin = 120; + shape.graphics.xMax = 220; + shape.graphics.yMin = 120; + shape.graphics.yMax = 220; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + execute(container, 100); + + expect(container.changed).toBe(true); + + const rawData = container.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(0.33329999446868896); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.ts new file mode 100644 index 00000000..2c1bab8a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetHeightUseCase.ts @@ -0,0 +1,39 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectSetScaleYUseCase } from "./DisplayObjectSetScaleYUseCase"; + +/** + * @description DisplayObjectの高さを設定します + * Sets the height of the DisplayObject + * + * @param {DisplayObject} display_object + * @param {number} height + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, height: number): void => +{ + height = +height; + if (isNaN(height) || 0 > height) { + return ; + } + + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + + let rawHeight = Math.abs(rawBounds[3] - rawBounds[1]); + switch (true) { + + case rawHeight === 0: + case rawHeight === Infinity: + case rawHeight === -Infinity: + return ; + + default: + rawHeight = Math.round(rawHeight * 100) / 100; + break; + + } + + displayObjectSetScaleYUseCase(display_object, height / rawHeight); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.test.ts new file mode 100644 index 00000000..3f16606a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.test.ts @@ -0,0 +1,48 @@ +import { execute } from "./DisplayObjectSetRotationUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetRotationUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$rotation).toBe(null); + expect(displayObject.$matrix).toBe(null); + + execute(displayObject, 12); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$rotation).toBe(12); + + const rawData = displayObject.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(0.9781476259231567); + expect(rawData[1]).toBe(0.2079116851091385); + expect(rawData[2]).toBe(-0.2079116851091385); + expect(rawData[3]).toBe(0.9781476259231567); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$rotation = 32; + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$rotation).toBe(32); + + execute(displayObject, 32); + + expect(displayObject.changed).toBe(false); + expect(displayObject.$rotation).toBe(32); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.ts new file mode 100644 index 00000000..77216371 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetRotationUseCase.ts @@ -0,0 +1,71 @@ +import { Matrix } from "@next2d/geom"; +import type { DisplayObject } from "../../DisplayObject"; +import { $clamp } from "../../DisplayObjectUtil"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @type {number} + * @private + */ +const $Deg2Rad: number = Math.PI / 180; + +/** + * @description DisplayObjectの回転値を設定 + * Set DisplayObject rotation value + * + * @param {DisplayObject} display_object + * @param {number} rotation + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, rotation: number): void => +{ + rotation = $clamp(rotation % 360, 0 - 360, 360, 0); + if (display_object.$rotation === rotation) { + return ; + } + + let matrix = display_object.$matrix; + if (!matrix) { + const rawData = displayObjectGetRawMatrixUseCase(display_object); + display_object.$matrix = matrix = rawData + ? new Matrix(...rawData) + : new Matrix(); + } + + const scaleX = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b); + const scaleY = Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d); + if (rotation === 0) { + matrix.a = scaleX; + matrix.b = 0; + matrix.c = 0; + matrix.d = scaleY; + } else { + + let radianX = Math.atan2(matrix.b, matrix.a); + let radianY = Math.atan2(-matrix.c, matrix.d); + + const radian = rotation * $Deg2Rad; + radianY = radianY + radian - radianX; + radianX = radian; + + matrix.b = scaleX * Math.sin(radianX); + if (matrix.b === 1 || matrix.b === -1) { + matrix.a = 0; + } else { + matrix.a = scaleX * Math.cos(radianX); + } + + matrix.c = -scaleY * Math.sin(radianY); + if (matrix.c === 1 || matrix.c === -1) { + matrix.d = 0; + } else { + matrix.d = scaleY * Math.cos(radianY); + } + } + + display_object.$rotation = rotation; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.test.ts new file mode 100644 index 00000000..d27e850e --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.test.ts @@ -0,0 +1,76 @@ +import { execute } from "./DisplayObjectSetScaleXUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetScaleXUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleX).toBe(null); + expect(displayObject.$matrix).toBe(null); + + execute(displayObject, 1.5); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleX).toBe(1.5); + + const rawData = displayObject.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1.5); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$scaleX = 2.2; + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleX).toBe(2.2); + + execute(displayObject, 2.2); + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleX).toBe(2.2); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleX).toBe(null); + + execute(displayObject, 2.2001231231005124151); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleX).toBe(2.2001); + }); + + it("execute test case4", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleX).toBe(null); + + execute(displayObject, "a" as unknown as number); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleX).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.ts new file mode 100644 index 00000000..6d7656b3 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleXUseCase.ts @@ -0,0 +1,60 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { $clamp } from "../../DisplayObjectUtil"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのx軸方向の拡大率を設定 + * Set the x-axis scaling factor of the DisplayObject. + * + * @param {DisplayObject} display_object + * @param {number} scale_x + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, scale_x: number): void => +{ + scale_x = $clamp(scale_x, -Number.MAX_VALUE, Number.MAX_VALUE, 1); + + if (!Number.isInteger(scale_x)) { + const value: string = scale_x.toString(); + const index: number = value.indexOf("e"); + if (index !== -1) { + scale_x = +value.slice(0, index); + } + scale_x = Math.round(scale_x * 10000) / 10000; + } + + if (display_object.$scaleX === scale_x) { + return ; + } + + let matrix = display_object.$matrix; + if (!matrix) { + const rawData = displayObjectGetRawMatrixUseCase(display_object); + display_object.$matrix = matrix = rawData + ? new Matrix(...rawData) + : new Matrix(); + } + + if (matrix.b === 0 || isNaN(matrix.b)) { + + matrix.a = scale_x; + + } else { + + let radianX = Math.atan2(matrix.b, matrix.a); + if (radianX === -Math.PI) { + radianX = 0; + } + + matrix.b = scale_x * Math.sin(radianX); + matrix.a = scale_x * Math.cos(radianX); + + } + + display_object.$scaleX = scale_x; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.test.ts new file mode 100644 index 00000000..28b05715 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.test.ts @@ -0,0 +1,76 @@ +import { execute } from "./DisplayObjectSetScaleYUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetScaleYUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleY).toBe(null); + expect(displayObject.$matrix).toBe(null); + + execute(displayObject, 1.5); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleY).toBe(1.5); + + const rawData = displayObject.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1.5); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.$scaleY = 2.2; + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleY).toBe(2.2); + + execute(displayObject, 2.2); + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleY).toBe(2.2); + }); + + it("execute test case3", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleY).toBe(null); + + execute(displayObject, 2.2001231231005124151); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleY).toBe(2.2001); + }); + + it("execute test case4", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$scaleY).toBe(null); + + execute(displayObject, "a" as unknown as number); + + expect(displayObject.changed).toBe(true); + expect(displayObject.$scaleY).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.ts new file mode 100644 index 00000000..3a212de9 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetScaleYUseCase.ts @@ -0,0 +1,59 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { $clamp } from "../../DisplayObjectUtil"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのy軸方向の拡大率を設定 + * Set the y-axis scaling factor of the DisplayObject. + * + * @param {DisplayObject} display_object + * @param {number} scale_y + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, scale_y: number): void => +{ + scale_y = $clamp(scale_y, -Number.MAX_VALUE, Number.MAX_VALUE, 1); + + if (!Number.isInteger(scale_y)) { + const value: string = scale_y.toString(); + const index: number = value.indexOf("e"); + if (index !== -1) { + scale_y = +value.slice(0, index); + } + scale_y = Math.round(scale_y * 10000) / 10000; + } + + if (display_object.$scaleY === scale_y) { + return ; + } + + let matrix = display_object.$matrix; + if (!matrix) { + const rawData = displayObjectGetRawMatrixUseCase(display_object); + display_object.$matrix = matrix = rawData + ? new Matrix(...rawData) + : new Matrix(); + } + + if (matrix.c === 0 || isNaN(matrix.c)) { + + matrix.d = scale_y; + + } else { + + let radianY = Math.atan2(-matrix.c, matrix.d); + if (radianY === -Math.PI) { + radianY = 0; + } + matrix.c = -scale_y * Math.sin(radianY); + matrix.d = scale_y * Math.cos(radianY); + + } + + display_object.$scaleY = scale_y; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.test.ts new file mode 100644 index 00000000..ec0a9343 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.test.ts @@ -0,0 +1,101 @@ +import { execute } from "./DisplayObjectSetWidthUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Video } from "@next2d/media"; +import { TextField } from "@next2d/text"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetWidthUseCase.js test", () => +{ + it("execute test case1", () => + { + const shape = new Shape(); + shape.graphics.xMax = 100; + shape.graphics.xMin = 200; + shape.changed = false; + + expect(shape.changed).toBe(false); + expect(shape.$scaleX).toBe(null); + expect(shape.$matrix).toBe(null); + + execute(shape, 50); + + expect(shape.changed).toBe(true); + expect(shape.$scaleX).toBe(0.5); + + const rawData = shape.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(0.5); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const video = new Video(100, 200); + video.changed = false; + + expect(video.changed).toBe(false); + expect(video.$scaleX).toBe(null); + expect(video.$matrix).toBe(null); + + execute(video, 50); + + expect(video.changed).toBe(true); + expect(video.$scaleX).toBe(0.5); + + const rawData = video.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(0.5); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); + + it("execute test case3", () => + { + const container = new DisplayObjectContainer(); + container.changed = false; + expect(container.changed).toBe(false); + + const shape = new Shape(); + shape.graphics.xMin = 120; + shape.graphics.xMax = 220; + shape.graphics.yMin = 120; + shape.graphics.yMax = 220; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + execute(container, 100); + + expect(container.changed).toBe(true); + + const rawData = container.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(0.4544999897480011); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.ts new file mode 100644 index 00000000..9ef264a6 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetWidthUseCase.ts @@ -0,0 +1,39 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { execute as displayObjectGetRawBoundsUseCase } from "./DisplayObjectGetRawBoundsUseCase"; +import { execute as displayObjectSetScaleXUseCase } from "./DisplayObjectSetScaleXUseCase"; + +/** + * @description DisplayObjectの幅を設定します + * Sets the width of the DisplayObject + * + * @param {DisplayObject} display_object + * @param {number} width + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, width: number): void => +{ + width = +width; + if (isNaN(width) || 0 > width) { + return ; + } + + const rawBounds = displayObjectGetRawBoundsUseCase(display_object); + + let rawWidth = Math.abs(rawBounds[2] - rawBounds[0]); + switch (true) { + + case rawWidth === 0: + case rawWidth === Infinity: + case rawWidth === -Infinity: + return ; + + default: + rawWidth = Math.round(rawWidth * 100) / 100; + break; + + } + + displayObjectSetScaleXUseCase(display_object, width / rawWidth); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.test.ts new file mode 100644 index 00000000..dfdb1893 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.test.ts @@ -0,0 +1,41 @@ +import { execute } from "./DisplayObjectSetXUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetXUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$matrix).toBe(null); + + execute(displayObject, 150); + + expect(displayObject.changed).toBe(true); + + const rawData = displayObject.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(150); + expect(rawData[5]).toBe(0); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + execute(displayObject, 0); + expect(displayObject.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.ts new file mode 100644 index 00000000..c321ae50 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetXUseCase.ts @@ -0,0 +1,32 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのx座標を設定 + * Set x-coordinate of DisplayObject + * + * @param {DisplayObject} display_object + * @param {number} x + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, x: number): void => +{ + let matrix = display_object.$matrix; + if (!matrix) { + const rawData = displayObjectGetRawMatrixUseCase(display_object); + display_object.$matrix = matrix = rawData + ? new Matrix(...rawData) + : new Matrix(); + } + + if (display_object.$x === x) { + return; + } + + display_object.$x = matrix.tx = x; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.test.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.test.ts new file mode 100644 index 00000000..a31f154a --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.test.ts @@ -0,0 +1,41 @@ +import { execute } from "./DisplayObjectSetYUseCase"; +import { DisplayObject } from "../../DisplayObject"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectSetYUseCase.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + expect(displayObject.$matrix).toBe(null); + + execute(displayObject, 150); + + expect(displayObject.changed).toBe(true); + + const rawData = displayObject.$matrix?.rawData; + if (!rawData) { + throw new Error("rawData is null"); + } + + expect(rawData[0]).toBe(1); + expect(rawData[1]).toBe(0); + expect(rawData[2]).toBe(0); + expect(rawData[3]).toBe(1); + expect(rawData[4]).toBe(0); + expect(rawData[5]).toBe(150); + }); + + it("execute test case2", () => + { + const displayObject = new DisplayObject(); + displayObject.changed = false; + + expect(displayObject.changed).toBe(false); + execute(displayObject, 0); + expect(displayObject.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.ts b/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.ts new file mode 100644 index 00000000..54dd2030 --- /dev/null +++ b/packages/display/src/DisplayObject/usecase/DisplayObjectSetYUseCase.ts @@ -0,0 +1,32 @@ +import type { DisplayObject } from "../../DisplayObject"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectApplyChangesService } from "../service/DisplayObjectApplyChangesService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description DisplayObjectのy座標を設定 + * Set y-coordinate of DisplayObject + * + * @param {DisplayObject} display_object + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D, y: number): void => +{ + let matrix = display_object.$matrix; + if (!matrix) { + const rawData = displayObjectGetRawMatrixUseCase(display_object); + display_object.$matrix = matrix = rawData + ? new Matrix(...rawData) + : new Matrix(); + } + + if (display_object.$y === y) { + return; + } + + display_object.$y = matrix.ty = y; + displayObjectApplyChangesService(display_object); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer.ts b/packages/display/src/DisplayObjectContainer.ts index 92a2d815..0d72aa7c 100644 --- a/packages/display/src/DisplayObjectContainer.ts +++ b/packages/display/src/DisplayObjectContainer.ts @@ -1,78 +1,32 @@ +import type { DisplayObject } from "./DisplayObject"; +import type { IDisplayObject } from "./interface/IDisplayObject"; +import type { IParent } from "./interface/IParent"; +import { execute as displayObjectApplyChangesService } from "./DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as displayObjectContainerAddChildUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase"; +import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase"; +import { execute as displayObjectContainerRemoveChildAtUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase"; +import { execute as displayObjectContainerGetChildAtService } from "./DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService"; +import { execute as displayObjectContainerContainsService } from "./DisplayObjectContainer/service/DisplayObjectContainerContainsService"; +import { execute as displayObjectContainerGetChildByNameService } from "./DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService"; +import { execute as displayObjectContainerRemoveChildrenUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase"; +import { execute as displayObjectContainerSetChildIndexUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase"; +import { execute as displayObjectContainerSwapChildrenUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase"; +import { execute as displayObjectContainerSwapChildrenAtUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase"; +import { $getArray } from "./DisplayObjectUtil"; import { InteractiveObject } from "./InteractiveObject"; -import { Event as Next2DEvent } from "@next2d/events"; -import type { LoaderInfo } from "./LoaderInfo"; -import type { Player } from "@next2d/core"; -import type { Sound } from "@next2d/media"; -import type { Transform } from "@next2d/geom"; -import type { - DictionaryTagImpl, - PlaceObjectImpl, - DisplayObjectImpl, - BoundsImpl, - FilterArrayImpl, - LoopConfigImpl, - ParentImpl, - PreObjectImpl, - AttachmentImpl, - BlendModeImpl, - PlayerHitObjectImpl, - PropertyMessageMapImpl, - PropertyContainerMessageImpl, - Character -} from "@next2d/interface"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $createInstance, - $currentPlayer, - $getRenderBufferArray, - $getRenderMessageObject, - $hitContext, - $isTouch, - $MATRIX_HIT_ARRAY_IDENTITY, - $poolRenderMessageObject, - $rendererWorker -} from "@next2d/util"; -import { - $cacheStore, - $doUpdated, - $boundsMatrix, - $clamp, - $getArray, - $getBoundsObject, - $getFloat32Array6, - $getFloat32Array8, - $getMap, - $getPreObject, - $Math, - $COLOR_ARRAY_IDENTITY, - $MATRIX_ARRAY_IDENTITY, - $multiplicationColor, - $multiplicationMatrix, - $Number, - $poolArray, - $poolBoundsObject, - $poolFloat32Array6, - $poolFloat32Array8, - $poolMap, - $poolPreObject, - $devicePixelRatio -} from "@next2d/share"; /** - * DisplayObjectContainer クラスは、表示リストで表示オブジェクトコンテナとして機能するすべてのオブジェクトの基本クラスです。 - * このクラス自体は、画面上でのコンテンツの描画のための API を含みません。 - * そのため、DisplayObject クラスのカスタムサブクラスを作成する場合は、 - * Sprite、または MovieClip など、画面上にコンテンツを描画する API を持つサブクラスの 1 つを拡張する必要があります。 + * @description DisplayObjectContainer クラスは、表示リストで表示オブジェクトコンテナとして機能するすべてのオブジェクトの基本クラスです。 + * このクラス自体は、画面上でのコンテンツの描画のための API を含みません。 + * そのため、DisplayObject クラスのカスタムサブクラスを作成する場合は、 + * Sprite、または MovieClip など、画面上にコンテンツを描画する API を持つサブクラスの 1 つを拡張する必要があります。 * - * The DisplayObjectContainer class is the base class for all objects that can serve - * as display object containers on the display list. - * This class itself does not contain any API for drawing content on the screen. - * Therefore, if you want to create a custom subclass of the DisplayObject class, - * you need to extend one of its subclasses that has an API for drawing content on the screen, - * such as Sprite or MovieClip. + * The DisplayObjectContainer class is the base class for all objects that can serve + * as display object containers on the display list. + * This class itself does not contain any API for drawing content on the screen. + * Therefore, if you want to create a custom subclass of the DisplayObject class, + * you need to extend one of its subclasses that has an API for drawing content on the screen, + * such as Sprite or MovieClip. * * @class * @memberOf next2d.display @@ -80,15 +34,54 @@ import { */ export class DisplayObjectContainer extends InteractiveObject { - protected _$placeMap: Array> | null; - protected _$placeObjects: PlaceObjectImpl[] | null; - protected _$controller: Array> | null; - protected _$dictionary: DictionaryTagImpl[] | null; - protected readonly _$children: DisplayObjectImpl[]; - protected _$needsChildren: boolean; - protected _$mouseChildren: boolean; - protected _$wait: boolean; - protected readonly _$names: Map>; + /** + * @description 描画対象となるDisplayObjectの配列です。 + * An array of DisplayObjects to be drawn. + * + * @type {array} + * @readonly + * @private + */ + protected readonly _$children: IDisplayObject[]; + + /** + * @description セットされてるDisplayObjectがマスクとして使用されます。 + * The DisplayObject set is used as a mask. + * + * @type {IDisplayObject|null} + * @default null + * @private + */ + private _$mask: IDisplayObject | null; + + /** + * @description オブジェクトの子がマウスまたはユーザー入力デバイスに対応しているかどうかを判断します。 + * Determine if the object's children are compatible with mouse or user input devices. + * + * @type {boolean} + * @default true + * @public + */ + public mouseChildren: boolean; + + /** + * @description このコンテナ何にセットされているコンテナだけの配列 + * An array of containers set in this container + * + * @type {IParent[] | null} + * @public + */ + public $container: IParent[] | null; + + /** + * @description コンテナの機能を所持しているかを返却 + * Returns whether the display object has container functionality. + * + * @type {boolean} + * @readonly + * @public + */ + public readonly isContainerEnabled: boolean; /** * @constructor @@ -98,93 +91,29 @@ export class DisplayObjectContainer extends InteractiveObject { super(); - /** - * @type {array} - * @default null - * @private - */ - this._$placeMap = null; + // public + this.isContainerEnabled = true; + this.mouseChildren = true; - /** - * @type {array} - * @default null - * @private - */ - this._$placeObjects = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$controller = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$dictionary = null; - - /** - * @type {array} - * @private - */ + // private + this._$mask = null; this._$children = $getArray(); - /** - * @type {boolean} - * @default true - * @private - */ - this._$needsChildren = true; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$mouseChildren = true; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$wait = true; - - /** - * @type {Map} - * @private - */ - this._$names = $getMap(); - - return new Proxy(this, { - "get": (object: this, name: string): any => - { - if (object._$names.size && object._$names.has(name)) { - return object._$names.get(name); - } - // @ts-ignore - return object[name]; - } - }); + // protected + this.$container = null; } /** - * @description オブジェクトの子がマウスまたはユーザー入力デバイスに対応しているかどうかを判断します。 - * Determine if the object's children are compatible with mouse or user input devices. + * @description コンテナのアクティブな子要素を返却 + * Returns the active child elements of the container. * - * @member {boolean} - * @public + * @return {array} + * @method + * @protected */ - get mouseChildren (): boolean + get children (): IDisplayObject[] { - return this._$mouseChildren; - } - set mouseChildren (mouse_children: boolean) - { - this._$mouseChildren = !!mouse_children; + return this._$children; } /** @@ -197,124 +126,85 @@ export class DisplayObjectContainer extends InteractiveObject */ get numChildren (): number { - return this._$needsChildren - ? this._$getChildren().length - : this._$children.length; + return this.children.length; } /** - * @description この DisplayObjectContainer インスタンスに子 DisplayObject インスタンスを追加します。 - * Adds a child DisplayObject instance to this DisplayObjectContainer instance. + * @description 呼び出し元の表示オブジェクトは、指定された mask オブジェクトによってマスクされます。 + * The calling display object is masked by the specified mask object. * - * @param {DisplayObject} child - * @return {DisplayObject} - * @method + * @member {DisplayObject|null} * @public */ - addChild (child: DisplayObjectImpl): DisplayObjectImpl + get mask (): IDisplayObject | null + { + return this._$mask; + } + set mask (mask: IDisplayObject | null) { - if (child._$parent) { - child._$parent._$remove(child, - !(child._$parent._$instanceId === this._$instanceId) - ); + if (mask === this._$mask) { + return ; } - this._$getChildren().push(child); + // 初期化 + if (this._$mask) { + this._$mask.isMask = false; + this._$mask = null; + } - if (child._$name) { - this._$names.set(child._$name, child); + if (mask) { + mask.isMask = true; + this._$mask = mask; } - return this._$addChild(child); + displayObjectApplyChangesService(this); + } + + /** + * @description この DisplayObjectContainer インスタンスに子 DisplayObject インスタンスを追加します。 + * Adds a child DisplayObject instance to this DisplayObjectContainer instance. + * + * @param {DisplayObject} display_object + * @return {DisplayObject} + * @method + * @public + */ + addChild (display_object: D): D + { + return displayObjectContainerAddChildUseCase(this, display_object); } /** * @description この DisplayObjectContainer インスタンスに子 DisplayObject インスタンスを追加します。 * Adds a child DisplayObject instance to this DisplayObjectContainer instance. * - * @param {DisplayObject} child + * @param {DisplayObject} display_object * @param {number} index * @return {DisplayObject} * @method * @public */ - addChildAt ( - child: DisplayObjectImpl, + addChildAt ( + display_object: D, index: number - ): DisplayObjectImpl { - - if (child._$parent) { - child._$parent._$remove(child, - !(child._$parent._$instanceId === this._$instanceId) - ); - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - const length: number = children.length; - if (0 > index || index > length) { - throw new RangeError(`RangeError: addChildAt: index error: ${index}`); - } - - if (length && length > index) { - - children.splice(index, 0, child); - - for (let idx: number = 0; idx < index; ++idx) { - const instance: DisplayObjectImpl = children[idx]; - if (instance._$name) { - this._$names.set(instance._$name, instance); - } - } - - } else { - - children.push(child); - if (child._$name) { - this._$names.set(child._$name, child); - } - - } - - return this._$addChild(child); + ): D { + return displayObjectContainerAddChildUseCase(this, display_object, index); } /** - * @description 指定された表示オブジェクトが、DisplayObjectContainer インスタンスの子であるか - * インスタンス自体であるかを指定します。 - * Determines whether the specified display object is a child - * of the DisplayObjectContainer instance or the instance itself. + * @description 指定された DisplayObject が、DisplayObjectContainer インスタンスの子孫であるか + * もしくは、インスタンス自体であるかを指定します。 + * Whether the specified DisplayObject is a descendant of a DisplayObjectContainer instance. + * or the instance itself. * - * @param {DisplayObject} child + * @param {DisplayObject} display_object * @return {boolean} * @method * @public */ - contains (child: DisplayObjectImpl): boolean + contains (display_object: D): boolean { - if (this._$instanceId === child._$instanceId) { - return true; - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - - if (instance._$instanceId === child._$instanceId) { - return true; - } - - if (instance instanceof DisplayObjectContainer) { - - if (instance.contains(child)) { - return true; - } - - } - - } - - return false; + return displayObjectContainerContainsService(this, display_object); } /** @@ -326,15 +216,9 @@ export class DisplayObjectContainer extends InteractiveObject * @method * @public */ - getChildAt (index: number): DisplayObjectImpl + getChildAt (index: number): D | null { - const children: DisplayObjectImpl[] = this._$getChildren(); - - if (0 > index || index > children.length) { - throw new RangeError(`RangeError: getChildAt: index error: ${index}`); - } - - return index in children ? children[index] : null; + return displayObjectContainerGetChildAtService(this, index); } /** @@ -346,48 +230,23 @@ export class DisplayObjectContainer extends InteractiveObject * @method * @public */ - getChildByName (name: string): DisplayObjectImpl | null + getChildByName (name: string): D | null { - if (!name) { - return null; - } - - // fixed logic - const children: DisplayObjectImpl[] = this._$getChildren(); - for (let idx: number = 0; idx < children.length; ++idx) { - - const child: DisplayObjectImpl = children[idx]; - if (child.name !== name) { - continue; - } - - return child; - } - - return null; + return displayObjectContainerGetChildByNameService(this, name); } /** * @description 子 DisplayObject インスタンスのインデックス位置を返します。 * Returns the index position of a child DisplayObject instance. * - * @param {DisplayObject} child + * @param {DisplayObject} display_object * @return {number} * @method * @public */ - getChildIndex (child: DisplayObjectImpl): number + getChildIndex (display_object: D): number { - if (child._$parent !== this) { - throw new Error("ArgumentError: getChildIndex: not child"); - } - - const index: number = this._$getChildren().indexOf(child); - if (index === -1) { - throw new Error("ArgumentError: getChildIndex: not found."); - } - - return index; + return this.children.indexOf(display_object); } /** @@ -396,17 +255,14 @@ export class DisplayObjectContainer extends InteractiveObject * Removes the specified child DisplayObject instance from the * child list of the DisplayObjectContainer instance. * - * @param {DisplayObject} child - * @return {DisplayObject} + * @param {DisplayObject} display_object + * @return {void} * @method * @public */ - removeChild (child: DisplayObjectImpl): DisplayObjectImpl + removeChild (display_object: D): void { - if (child._$parent !== this) { - throw new Error("ArgumentError: removeChild: not child"); - } - return this._$remove(child); + displayObjectContainerRemoveChildUseCase(this, display_object); } /** @@ -415,103 +271,61 @@ export class DisplayObjectContainer extends InteractiveObject * in the child list of the DisplayObjectContainer. * * @param {number} index - * @return {DisplayObject} + * @return {void} * @method * @public */ - removeChildAt (index: number): DisplayObjectImpl + removeChildAt (index: number): void { - return this._$remove(this.getChildAt(index)); + displayObjectContainerRemoveChildAtUseCase(this, index); } /** - * @description DisplayObjectContainer インスタンスの子リストから - * すべての child DisplayObject インスタンスを削除します。 - * Removes all child DisplayObject instances from - * the child list of the DisplayObjectContainer instance. + * @description 配列で指定されたインデックスの子をコンテナから削除します + * Removes children of the index specified in the array from the container * - * @param {number} [begin_index=0] - * @param {number} [end_index=0x7fffffff] + * @param {number[]} indexes * @return {void} * @method * @public */ - removeChildren ( - begin_index: number = 0, - end_index: number = 0x7fffffff - ): void { - - const children: DisplayObjectImpl[] = this._$getChildren(); - if (!children.length) { - return ; - } - - begin_index = $clamp(begin_index, 0, 0x7ffffffe, 0) - 1; - end_index = $clamp(end_index, 1, 0x7ffffff, 0x7ffffff); - - for (let idx: number = $Math.min(end_index, children.length - 1); idx > begin_index; --idx) { - this._$remove(children[idx]); - } + removeChildren (...indexes: number[]): void + { + displayObjectContainerRemoveChildrenUseCase(this, indexes); } /** * @description 表示オブジェクトコンテナの既存の子の位置を変更します。 * Changes the position of an existing child in the display object container. * - * @param {DisplayObject} child + * @param {DisplayObject} display_object * @param {number} index * @return {void} * @method * @public */ - setChildIndex ( - child: DisplayObjectImpl, + setChildIndex ( + display_object: D, index: number ): void { - - const currentIndex: number = this.getChildIndex(child); - if (currentIndex === index) { - return ; - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - children.splice(currentIndex, 1); - children.splice(index, 0, child); - - if ($rendererWorker) { - this._$postChildrenIds(); - } - - this._$doChanged(); + displayObjectContainerSetChildIndexUseCase(this, display_object, index); } /** * @description 指定された 2 つの子オブジェクトの z 順序(重ね順)を入れ替えます。 * Swaps the z-order (front-to-back order) of the two specified child objects. * - * @param {DisplayObject} child1 - * @param {DisplayObject} child2 + * @param {DisplayObject} display_object1 + * @param {DisplayObject} display_object2 * @return {void} * @method * @public */ - swapChildren ( - child1: DisplayObjectImpl, - child2: DisplayObjectImpl + swapChildren ( + display_object1: D, + display_object2: D ): void { - - const children: DisplayObjectImpl[] = this._$getChildren(); - const index1: number = this.getChildIndex(child1); - const index2: number = this.getChildIndex(child2); - - children[index1] = child2; - children[index2] = child1; - - if ($rendererWorker) { - this._$postChildrenIds(); - } - - this._$doChanged(); + displayObjectContainerSwapChildrenUseCase(this, display_object1, display_object2); } /** @@ -527,1748 +341,6 @@ export class DisplayObjectContainer extends InteractiveObject */ swapChildrenAt (index1: number, index2: number): void { - this.swapChildren( - this.getChildAt(index1), - this.getChildAt(index2) - ); - } - - /** - * @param {array} [matrix=null] - * @return {object} - * @private - */ - _$getBounds (matrix: Float32Array | null = null): BoundsImpl - { - let multiMatrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - if (matrix) { - - multiMatrix = matrix; - - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - } - - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - // size zero - if (!children.length) { - - const bounds: BoundsImpl = $getBoundsObject( - multiMatrix[4], -multiMatrix[4], - multiMatrix[5], -multiMatrix[5] - ); - - if (matrix && multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return bounds; - } - - // data init - const no = $Number.MAX_VALUE; - let xMin = no; - let xMax = -no; - let yMin = no; - let yMax = -no; - for (let idx: number = 0; idx < children.length; ++idx) { - - const bounds: BoundsImpl = children[idx]._$getBounds(multiMatrix); - - xMin = $Math.min(xMin, bounds.xMin); - xMax = $Math.max(xMax, bounds.xMax); - yMin = $Math.min(yMin, bounds.yMin); - yMax = $Math.max(yMax, bounds.yMax); - - $poolBoundsObject(bounds); - - } - - if (matrix && multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - // end - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - /** - * @param {Float32Array} multi_matrix - * @return {object} - * @private - */ - _$getLayerBounds (multi_matrix: Float32Array): BoundsImpl - { - - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - // size zero - if (!children.length) { - return $getBoundsObject(0, 0, 0, 0); - } - - // data init - const no: number = $Number.MAX_VALUE; - let xMin: number = no; - let xMax: number = -no; - let yMin: number = no; - let yMax: number = -no; - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance = children[idx]; - - let multiMatrix = multi_matrix; - const rawMatrix: Float32Array = instance._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(multi_matrix, rawMatrix); - } - - const bounds: BoundsImpl = instance._$getLayerBounds(multiMatrix); - - xMin = $Math.min(xMin, bounds.xMin); - xMax = $Math.max(xMax, bounds.xMax); - yMin = $Math.min(yMin, bounds.yMin); - yMax = $Math.max(yMax, bounds.yMax); - - $poolBoundsObject(bounds); - - if (multiMatrix !== multi_matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - const filters: FilterArrayImpl = this._$filters || this.filters; - if (!filters.length) { - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - let filterBounds: BoundsImpl = $getBoundsObject( - 0, xMax - xMin, - 0, yMax - yMin - ); - - let xScale: number = +$Math.sqrt( - multi_matrix[0] * multi_matrix[0] - + multi_matrix[1] * multi_matrix[1] - ); - let yScale: number = +$Math.sqrt( - multi_matrix[2] * multi_matrix[2] - + multi_matrix[3] * multi_matrix[3] - ); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - for (let idx: number = 0; idx < filters.length; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - - xMax += filterBounds.xMax - (xMax - xMin); - yMax += filterBounds.yMax - (yMax - yMin); - xMin += filterBounds.xMin; - yMin += filterBounds.yMin; - - $poolBoundsObject(filterBounds); - - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - /** - * @return {array} - * @private - */ - _$getChildren (): DisplayObjectImpl[] - { - if (this._$needsChildren) { - - // set flag - this._$needsChildren = false; - - const currentChildren: DisplayObjectImpl[] = this._$children; - if (!this._$controller) { - return currentChildren; - } - - const frame: number = "_$currentFrame" in this ? this._$currentFrame as number : 1; - const controller: number[] = this._$controller[frame]; - - // first build - if (!currentChildren.length) { - - if (controller) { - - for (let idx: number = 0; idx < controller.length; ++idx) { - - const dictionaryId = controller[idx]; - if (typeof dictionaryId !== "number") { - continue; - } - - const instance: DisplayObjectImpl = this._$createInstance(dictionaryId); - instance._$placeId = idx; - - const loopConfig: LoopConfigImpl | null = instance.loopConfig; - if (loopConfig) { - instance._$currentFrame = instance - ._$getLoopFrame(loopConfig); - } - - currentChildren.push(instance); - if (instance._$name) { - this._$names.set(instance._$name, instance); - } - } - } - - return currentChildren; - } - - const useWorker: boolean = !!$rendererWorker && !!this._$stage; - - const skipIds: Map = $getMap(); - const poolInstances: Map> = $getMap(); - - let depth: number = 0; - const children: DisplayObjectImpl[] = $getArray(); - for (let idx: number = 0; idx < currentChildren.length; ++idx) { - - const instance: DisplayObjectImpl = currentChildren[idx]; - - const parent: ParentImpl = instance._$parent; - if (!parent || parent._$instanceId !== this._$instanceId) { - continue; - } - - const instanceId: number = instance._$instanceId; - const startFrame: number = instance._$startFrame; - const endFrame: number = instance._$endFrame; - if (startFrame === 1 && endFrame === 0 - || startFrame <= frame && endFrame > frame - ) { - - // reset - instance._$isNext = true; - instance._$placeObject = null; - instance._$filters = null; - instance._$blendMode = null; - - if (instance._$id === -1) { - - children.push(instance); - if (instance._$name) { - this._$names.set(instance._$name, instance); - } - - continue; - } - - const id: number = controller[depth]; - if (instance._$id === id) { - - instance._$placeId = depth; - children.push(instance); - - if (instance._$name) { - this._$names.set(instance._$name, instance); - } - - if (poolInstances.has(id)) { - poolInstances.delete(id); - } - - skipIds.set(id, true); - depth++; - - if (useWorker) { - instance._$postProperty(); - } - - continue; - } - - poolInstances.set(instance._$id, instance); - - continue; - } - - if (useWorker) { - instance._$removeWorkerInstance(); - } - - $cacheStore.setRemoveTimer(instanceId); - if (instance._$loaderInfo && instance._$characterId) { - $cacheStore.setRemoveTimer( - `${instance._$loaderInfo._$id}@${instance._$characterId}` - ); - } - if (instance._$graphics) { - $cacheStore.setRemoveTimer(instance._$graphics._$uniqueKey); - } - - // remove event - if (instance.willTrigger(Next2DEvent.REMOVED)) { - instance.dispatchEvent( - new Next2DEvent(Next2DEvent.REMOVED, true) - ); - } - if (instance.willTrigger(Next2DEvent.REMOVED_FROM_STAGE)) { - instance.dispatchEvent( - new Next2DEvent(Next2DEvent.REMOVED_FROM_STAGE, true) - ); - } - - // reset - instance._$added = false; - instance._$addedStage = false; - instance._$active = false; - instance._$updated = true; - instance._$filters = null; - instance._$blendMode = null; - instance._$isNext = true; - instance._$placeObject = null; - instance._$created = false; - instance._$posted = false; - - if (instance instanceof DisplayObjectContainer) { - instance._$executeRemovedFromStage(); - instance._$removeParentAndStage(); - } - - } - - if (controller) { - - for (let idx: number = 0; idx < controller.length; ++idx) { - - const dictionaryId: number = controller[idx]; - if (typeof dictionaryId !== "number" || skipIds.has(dictionaryId)) { - continue; - } - - const instance: DisplayObjectImpl = poolInstances.has(dictionaryId) - ? poolInstances.get(dictionaryId) - : this._$createInstance(dictionaryId); - - instance._$placeId = idx; - - const loopConfig: LoopConfigImpl | null = instance.loopConfig; - if (loopConfig) { - instance._$currentFrame = instance - ._$getLoopFrame(loopConfig); - } - - children.push(instance); - if (instance._$name) { - this._$names.set(instance._$name, instance); - } - - } - } - - // object pool - $poolMap(skipIds); - $poolMap(poolInstances); - - // update - currentChildren.length = 0; - currentChildren.push(...children); - $poolArray(children); - } - - return this._$children; - } - - /** - * @return void - * @private - */ - _$clearChildren (): void - { - this._$doChanged(); - $doUpdated(); - - // reset - this._$names.clear(); - - // clear - this._$needsChildren = true; - } - - /** - * @param {DisplayObject} child - * @returns {DisplayObject} - * @private - */ - _$addChild (child: DisplayObjectImpl): DisplayObjectImpl - { - // init - child._$parent = this; - if (!child._$stage || !child._$root) { - child._$stage = this._$stage; - child._$root = this._$root; - } - - // setup - if (child instanceof DisplayObjectContainer) { - child._$setParentAndStage(); - child._$wait = true; - } - - // added event - if (!child._$added) { - if (child.willTrigger(Next2DEvent.ADDED)) { - child.dispatchEvent( - new Next2DEvent(Next2DEvent.ADDED, true) - ); - } - child._$added = true; - } - - if (this._$stage !== null && !child._$addedStage) { - - if (child.willTrigger(Next2DEvent.ADDED_TO_STAGE)) { - child.dispatchEvent( - new Next2DEvent(Next2DEvent.ADDED_TO_STAGE) - ); - } - - child._$addedStage = true; - - // set params - if (child instanceof DisplayObjectContainer) { - child._$executeAddedToStage(); - } - - if ($rendererWorker) { - child._$createWorkerInstance(); - this._$postChildrenIds(); - } - } - - this._$doChanged(); - child._$active = true; - child._$updated = true; - child._$isNext = true; - - return child; - } - - /** - * @return {void} - * @method - * @private - */ - _$setParentAndStage (): void - { - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - - instance._$root = this._$root; - instance._$stage = this._$stage; - - if (instance instanceof DisplayObjectContainer) { - instance._$setParentAndStage(); - instance._$wait = true; - } - - } - } - - /** - * @return {void} - * @method - * @private - */ - _$executeAddedToStage (): void - { - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - const childrenIds: number[] = $getArray(); - - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - if (!instance) { - continue; - } - - childrenIds.push(instance._$instanceId); - if (!instance._$addedStage) { - - if ($rendererWorker) { - instance._$createWorkerInstance(); - } - - if (instance.willTrigger(Next2DEvent.ADDED_TO_STAGE)) { - instance.dispatchEvent( - new Next2DEvent(Next2DEvent.ADDED_TO_STAGE) - ); - } - - instance._$addedStage = true; - } - - if (instance instanceof DisplayObjectContainer) { - instance._$executeAddedToStage(); - } - - } - - if ($rendererWorker) { - this._$postChildrenIds(childrenIds); - } - - $poolArray(childrenIds); - } - - /** - * @param {DisplayObject} child - * @param {boolean} do_event - * @return {DisplayObject} - * @private - */ - _$remove ( - child: DisplayObjectImpl, - do_event: boolean = true - ): DisplayObjectImpl { - - child._$transform._$transform(); - - // remove all broadcast events - if (child.hasEventListener(Next2DEvent.ENTER_FRAME)) { - child.removeAllEventListener(Next2DEvent.ENTER_FRAME); - } - - if (child.hasEventListener(Next2DEvent.EXIT_FRAME)) { - child.removeAllEventListener(Next2DEvent.EXIT_FRAME); - } - - if (child.hasEventListener(Next2DEvent.FRAME_CONSTRUCTED)) { - child.removeAllEventListener(Next2DEvent.FRAME_CONSTRUCTED); - } - - if (child.hasEventListener(Next2DEvent.RENDER)) { - child.removeAllEventListener(Next2DEvent.RENDER); - } - - if (child.hasEventListener(Next2DEvent.ACTIVATE)) { - child.removeAllEventListener(Next2DEvent.ACTIVATE); - } - - if (child.hasEventListener(Next2DEvent.DEACTIVATE)) { - child.removeAllEventListener(Next2DEvent.DEACTIVATE); - } - - if (child.hasEventListener("keyDown")) { - child.removeAllEventListener("keyDown"); - } - - if (child.hasEventListener("keyUp")) { - child.removeAllEventListener("keyUp"); - } - - // remove - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - const depth: number = this.getChildIndex(child); - children.splice(depth, 1); - - this._$names.delete(child.name); - if (do_event) { - - // event - if (child.willTrigger(Next2DEvent.REMOVED)) { - child.dispatchEvent( - new Next2DEvent(Next2DEvent.REMOVED, true) - ); - } - - // remove stage event - if (this._$stage !== null) { - - // worker側のDisplayObjectも削除 - if ($rendererWorker) { - child._$removeWorkerInstance(); - this._$postChildrenIds(); - } - - if (child.willTrigger(Next2DEvent.REMOVED_FROM_STAGE)) { - child.dispatchEvent( - new Next2DEvent(Next2DEvent.REMOVED_FROM_STAGE) - ); - } - - if (child instanceof DisplayObjectContainer) { - child._$executeRemovedFromStage(); - } - } - - $cacheStore.setRemoveTimer(child._$instanceId); - if (child._$loaderInfo && child._$characterId) { - $cacheStore.setRemoveTimer( - `${child._$loaderInfo._$id}@${child._$characterId}` - ); - } - if (child._$graphics) { - $cacheStore.setRemoveTimer(child._$graphics._$uniqueKey); - } - - // reset params - if (child instanceof DisplayObjectContainer) { - child._$removeParentAndStage(); - } - - // reset - child._$stage = null; - child._$parent = null; - child._$root = null; - child._$active = false; - child._$wait = true; - child._$updated = true; - child._$added = false; - child._$addedStage = false; - child._$created = false; - child._$posted = false; - this._$doChanged(); - - } - - return child; - } - - /** - * @return {void} - * @method - * @private - */ - _$executeRemovedFromStage (): void - { - const children: DisplayObjectImpl[] = this._$getChildren().slice(0); - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - if (!instance) { - continue; - } - - if (instance._$addedStage) { - - // workerのDisplayObjectを削除 - if ($rendererWorker) { - instance._$removeWorkerInstance(); - } - - if (instance.willTrigger(Next2DEvent.REMOVED_FROM_STAGE)) { - instance.dispatchEvent( - new Next2DEvent(Next2DEvent.REMOVED_FROM_STAGE) - ); - } - - instance._$created = false; - instance._$posted = false; - instance._$addedStage = false; - } - - if (instance instanceof DisplayObjectContainer) { - instance._$executeRemovedFromStage(); - } - - } - } - - /** - * @return {void} - * @method - * @private - */ - _$removeParentAndStage (): void - { - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - - $cacheStore.setRemoveTimer(instance._$instanceId); - if (instance._$loaderInfo && instance._$characterId) { - $cacheStore.setRemoveTimer( - `${instance._$loaderInfo._$id}@${instance._$characterId}` - ); - } - if (instance._$graphics) { - $cacheStore.setRemoveTimer(instance._$graphics._$uniqueKey); - } - - if (instance instanceof DisplayObjectContainer) { - instance._$removeParentAndStage(); - } - - instance._$stage = null; - instance._$root = null; - instance._$addedStage = false; - } - - if ("_$sounds" in this) { - const soundsMap: Map = this._$sounds as Map; - if (soundsMap.size) { - for (const sounds of soundsMap.values()) { - for (let idx: number = 0; idx < sounds.length; ++idx) { - const sound: Sound = sounds[idx]; - sound.stop(); - } - } - } - } - - this._$needsChildren = true; - } - - /** - * @return {void} - * @method - * @private - */ - _$prepareActions (): void - { - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - for (let idx: number = children.length - 1; idx > -1; --idx) { - children[idx]._$prepareActions(); - } - - // added event - this._$executeAddedEvent(); - } - - /** - * @return {boolean} - * @method - * @private - */ - _$nextFrame (): boolean - { - let isNext: boolean = false; - - const children: DisplayObjectImpl[] = this._$getChildren(); - for (let idx: number = children.length - 1; idx > -1; --idx) { - - const child: DisplayObjectImpl = children[idx]; - if (!child._$isNext) { - continue; - } - - if (isNext) { - - child._$nextFrame(); - - } else { - - isNext = child._$nextFrame(); - - } - } - - // added event - this._$executeAddedEvent(); - - this._$isNext = isNext; - - if (!this._$posted && $rendererWorker) { - this._$postProperty(); - } - - return this._$isNext; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - let multiMatrix: Float32Array = matrix; - - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - - // mask instance - if (instance._$isMask) { - continue; - } - - instance._$clip(context, multiMatrix); - instance._$updated = false; - - } - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {object} - * @private - */ - _$preDraw ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): PreObjectImpl | null { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix !== $MATRIX_HIT_ARRAY_IDENTITY - && rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - // size zero - if (!multiMatrix[0] && !multiMatrix[1] - || !multiMatrix[2] && !multiMatrix[3] - ) { - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - // return object - const object: PreObjectImpl = $getPreObject(); - - // setup - object.matrix = multiMatrix; - - // check - const filters: FilterArrayImpl = this._$filters || this.filters; - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - if (filters.length > 0 || blendMode !== "normal") { - - // check size - const baseBounds: BoundsImpl = this._$getBounds(null); - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - $poolBoundsObject(baseBounds); - - const xMax: number = +bounds.xMax; - const xMin: number = +bounds.xMin; - const yMax: number = +bounds.yMax; - const yMin: number = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - if (0 >= width || 0 >= height) { - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - object.canApply = this._$canApply(filters); - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (object.canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - const currentAttachment: AttachmentImpl | null = context - .frameBuffer - .currentAttachment; - - if (!currentAttachment - || !currentAttachment.texture - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - // move size - let tx: number = multiMatrix[4] - xMin; - let ty: number = multiMatrix[5] - yMin; - - // start layer - context._$startLayer( - $getBoundsObject(xMin, xMax, yMin, yMax) - ); - - // check cache - const updated: boolean = this._$isFilterUpdated( - multiMatrix, filters, object.canApply - ); - - const layerBounds: BoundsImpl = this._$getLayerBounds(multiMatrix); - - const layerWidth: number = $Math.ceil($Math.abs(layerBounds.xMax - layerBounds.xMin)); - const layerHeight: number = $Math.ceil($Math.abs(layerBounds.yMax - layerBounds.yMin)); - $poolBoundsObject(layerBounds); - - const sw = layerWidth - filterBounds.xMax + filterBounds.xMin; - const sh = layerHeight - filterBounds.yMax + filterBounds.yMin; - - tx += sw; - ty += sh; - - object.sw = sw; - object.sh = sh; - if (updated) { - context._$saveAttachment( - $Math.ceil(width + sw), - $Math.ceil(height + sh), - true - ); - } - - // setup - object.isLayer = true; - object.isUpdated = updated; - object.filters = filters; - object.blendMode = blendMode; - object.color = $getFloat32Array8(); - object.matrix = $getFloat32Array6( - multiMatrix[0], multiMatrix[1], - multiMatrix[2], multiMatrix[3], - tx, ty - ); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - $poolBoundsObject(filterBounds); - } - - return object; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {object} object - * @return {void} - * @method - * @private - */ - _$postDraw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array, - object: PreObjectImpl - ): void { - - context.drawInstacedArray(); - - // cache - const cacheKeys: any[] = $getArray(this._$instanceId, "f"); - - const manager: FrameBufferManager = context.frameBuffer; - const multiMatrix: Float32Array = object.matrix as NonNullable; - - let offsetX: number = 0; - let offsetY: number = 0; - let texture: WebGLTexture | null = $cacheStore.get(cacheKeys); - - if (!texture || object.isUpdated) { - - // remove - if (texture) { - $cacheStore.set(cacheKeys, null); - } - - texture = manager - .getTextureFromCurrentAttachment(); - - const filters: FilterArrayImpl | null = object.filters; - let filterState = false; - if (filters && filters.length) { - - for (let idx: number = 0; idx < filters.length; ++idx) { - texture = filters[idx] - ._$applyFilter(context, matrix); - } - - // update - filterState = true; - offsetX = context._$offsetX; - offsetY = context._$offsetY; - - // reset - context._$offsetX = 0; - context._$offsetY = 0; - } - - texture.filterState = filterState; - texture.matrix = `${multiMatrix[0]}_` - + `${multiMatrix[1]}_` - + `${multiMatrix[2]}_` - + `${multiMatrix[3]}`; - - texture.offsetX = offsetX; - texture.offsetY = offsetY; - - $cacheStore.set(cacheKeys, texture); - - context._$restoreAttachment(); - } - - if (texture.offsetX) { - offsetX = texture.offsetX; - } - - if (texture.offsetY) { - offsetY = texture.offsetY; - } - - // set - context.reset(); - context.globalAlpha = $clamp( - color_transform[3] + color_transform[7] / 255, 0, 1 - ); - context.globalCompositeOperation = object.blendMode; - - const bounds: BoundsImpl = context.getCurrentPosition(); - - context.setTransform( - 1, 0, 0, 1, - bounds.xMin - offsetX - object.sw, - bounds.yMin - offsetY - object.sh - ); - - context.drawImage(texture, - 0, 0, texture.width, texture.height, - color_transform - ); - - // end blend - context._$endLayer(); - - // object pool - $poolFloat32Array6(object.matrix as NonNullable); - $poolPreObject(object); - - // reset - context.cachePosition = null; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - // not draw - if (!this._$visible) { - return ; - } - - const transform: Transform = this._$transform; - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = transform._$rawColorTransform(); - if (rawColor !== $COLOR_ARRAY_IDENTITY - && rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - // not draw - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - return ; - } - - // not draw - const children: DisplayObjectImpl[] = this._$getChildren(); - const length: number = children.length; - if (!length) { - return ; - } - - // pre data - const preObject: PreObjectImpl | null = this._$preDraw(context, matrix); - if (!preObject) { - return ; - } - - // use cache - if (preObject.isLayer && !preObject.isUpdated) { - this._$postDraw(context, matrix, multiColor, preObject); - return ; - } - - const preMatrix: Float32Array = preObject.matrix as NonNullable; - - const preColorTransform: Float32Array = preObject.isLayer && preObject.color - ? preObject.color - : multiColor; - - // init clip params - let shouldClip: boolean = true; - let clipDepth: number = 0; - - const player: Player = $currentPlayer(); - - // draw children - const isLayer: boolean = context.isLayer; - const isUpdate: boolean = this._$isUpdated(); - for (let idx: number = 0; idx < length; ++idx) { - - const instance = children[idx]; - - if (isUpdate) { - instance._$placeObject = null; - } - - // mask instance - if (instance._$isMask) { - continue; - } - - // not layer mode - const blendMode: BlendModeImpl = instance._$blendMode || instance.blendMode; - if ((blendMode === "alpha" || blendMode === "erase") - && !isLayer - ) { - continue; - } - - // mask end - if (clipDepth - && (instance._$placeId > clipDepth || instance._$clipDepth > 0) - ) { - - context.restore(); - - if (shouldClip) { - context._$leaveClip(); - } - - // clear - clipDepth = 0; - shouldClip = true; - } - - // mask size 0 - if (!shouldClip) { - continue; - } - - // mask start - if (instance._$clipDepth > 0) { - - clipDepth = instance._$clipDepth; - shouldClip = instance._$shouldClip(preMatrix); - - if (shouldClip) { - context.save(); - shouldClip = instance._$startClip(context, preMatrix); - } - - continue; - } - - // mask start - const maskInstance: DisplayObjectImpl | null = instance._$mask; - if (maskInstance) { - - maskInstance._$updated = false; - - let maskMatrix: Float32Array; - - if (this === maskInstance._$parent) { - - maskMatrix = preMatrix; - - } else { - - maskMatrix = $MATRIX_ARRAY_IDENTITY; - - let parent: ParentImpl | null = maskInstance._$parent; - while (parent) { - - maskMatrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - maskMatrix - ); - - parent = parent._$parent; - } - - const mScale: number = player.scaleX; - const playerMatrix: Float32Array = $getFloat32Array6( - mScale, 0, 0, mScale, 0, 0 - ); - - maskMatrix = $multiplicationMatrix( - playerMatrix, maskMatrix - ); - $poolFloat32Array6(playerMatrix); - - if (context.isLayer) { - const currentPosition: BoundsImpl = context.getCurrentPosition(); - maskMatrix[4] -= currentPosition.xMin; - maskMatrix[5] -= currentPosition.yMin; - } - - } - - if (!maskInstance._$shouldClip(maskMatrix)) { - continue; - } - - const result: boolean = maskInstance._$startClip(context, maskMatrix); - - context.save(); - - if (!result) { // fixed - context.restore(); - continue; - } - - } - - instance._$draw(context, preMatrix, preColorTransform); - instance._$updated = false; - - // mask end - if (maskInstance) { - context.restore(); - context._$leaveClip(); - } - } - - // end mask - if (clipDepth) { - context.restore(); - - if (shouldClip) { - context._$leaveClip(); - } - } - - // filter and blend - if (preObject.isLayer) { - return this._$postDraw(context, matrix, multiColor, preObject); - } - - if (preObject.matrix !== matrix) { - $poolFloat32Array6(preObject.matrix as NonNullable); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - - $poolPreObject(preObject); - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @param {boolean} [mouse_children=true] - * @return {boolean} - * @method - * @private - */ - _$mouseHit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl, - mouse_children: boolean = true - ): boolean { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix !== $MATRIX_ARRAY_IDENTITY) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - - // mask set - const clips: DisplayObjectImpl[] = $getArray(); - const targets: DisplayObjectImpl[] = $getArray(); - const clipIndexes: Map = $getMap(); - - let clipDepth: number = 0; - let clipIdx: number = 0; - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - - if (!instance._$visible && !instance._$hitObject) { - continue; - } - - if (instance._$clipDepth) { - clipIdx = clips.length; - clipDepth = instance._$clipDepth; - clips.push(instance); - continue; - } - - // clip end - if (clipDepth && instance._$placeId > clipDepth) { - clipIdx = 0; - clipDepth = 0; - } - - // clip check on - if (clipIdx) { - clipIndexes.set(instance._$instanceId, clipIdx); - } - - targets.push(instance); - - } - - // setup - const mouseChildren: boolean = this._$mouseChildren && mouse_children; - - let hit: boolean = false; - const isRoot = this._$root === this; - - while (targets.length) { - - const instance: DisplayObjectImpl = targets.pop(); - if (instance._$isMask) { - continue; - } - - if (isRoot && !(instance instanceof InteractiveObject)) { - continue; - } - - // mask target - if (clipIndexes.has(instance._$instanceId)) { - - const index: number | void = clipIndexes.get(instance._$instanceId); - if (!index) { - continue; - } - - const clip: DisplayObjectImpl = clips[index]; - if (!clip._$hit(context, multiMatrix, options, true)) { - continue; - } - - } - - // mask hit test - const maskInstance: DisplayObjectImpl | null = instance._$mask; - if (maskInstance) { - - if (this === maskInstance._$parent) { - - if (!maskInstance._$hit(context, multiMatrix, options, true)) { - continue; - } - - } else { - - let maskMatrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - - let parent: ParentImpl | null = maskInstance._$parent; - while (parent) { - - maskMatrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - maskMatrix - ); - - parent = parent._$parent; - } - - if (!maskInstance._$hit(context, maskMatrix, options, true)) { - continue; - } - - } - - } - - if (instance._$mouseHit(context, multiMatrix, options, mouseChildren) - || instance._$hitArea - && instance - ._$hitArea - ._$mouseHit(context, multiMatrix, options, mouseChildren) - ) { - - if (instance._$root === instance) { - return true; - } - - if (!mouseChildren) { - return true; - } - - hit = true; - if (instance instanceof InteractiveObject) { - - if (!instance.mouseEnabled && !instance._$hitObject) { - continue; - } - - if (!$isTouch && !options.pointer) { - - if ("_$text" in instance - && "type" in instance - && instance.type === "input" - ) { - options.pointer = "text"; - } - - if ("buttonMode" in instance - && "useHandCursor" in instance - && instance.buttonMode - && instance.useHandCursor - ) { - options.pointer = "pointer"; - } - - } - - if (!options.hit) { - - options.hit = !instance.mouseEnabled && instance._$hitObject - ? instance._$hitObject - : instance; - - } - - return true; - } - - } - - } - - // pool - $poolArray(clips); - $poolArray(targets); - $poolMap(clipIndexes); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - // not found - return hit; - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @param {boolean} [is_clip=false] - * @return {boolean} - * @method - * @private - */ - _$hit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl, - is_clip: boolean = false - ): boolean { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix !== $MATRIX_ARRAY_IDENTITY) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const children: DisplayObjectImpl[] = this._$getChildren(); - for (let idx: number = children.length; idx > -1; --idx) { - - const instance: DisplayObjectImpl = children[idx]; - if (instance._$isMask) { - continue; - } - - if (instance._$hit(context, multiMatrix, options, is_clip)) { - return true; - } - - } - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return false; - } - - /** - * @param {number} index - * @return {DisplayObject} - * @method - * @private - */ - _$createInstance (index: number): DisplayObjectImpl - { - if (!this._$dictionary) { - throw new Error("the dictionary is null."); - } - - // build - const tag: DictionaryTagImpl = this._$dictionary[index]; - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo || !loaderInfo._$data) { - throw new Error("the loaderInfo or data is null."); - } - - const character: Character = loaderInfo._$data.characters[tag.characterId]; - - // symbol class - const instance: DisplayObjectImpl = $createInstance(character.extends); - instance._$build(tag, this); - instance._$id = index; - - return instance; - } - - /** - * @param {number} x - * @param {number} y - * @return {boolean} - * @method - * @private - */ - _$outCheck (x: number, y: number): boolean - { - let matrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - let parent: ParentImpl | null = this._$parent; - while (parent) { - - matrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - matrix - ); - - parent = parent._$parent; - } - - $hitContext.setTransform(1, 0, 0, 1, 0, 0); - $hitContext.beginPath(); - - const options: PlayerHitObjectImpl = { - "x": x, - "y": y, - "pointer": "", - "hit": null - }; - - return this._$mouseHit($hitContext, matrix, options); - } - - /** - * @return {void} - * @method - * @private - */ - _$createWorkerInstance (): void - { - if (this._$created || !$rendererWorker) { - return ; - } - - this._$created = true; - this._$posted = true; - this._$updated = false; - - let index: number = 0; - const buffer: Float32Array = $getRenderBufferArray(); - buffer[index++] = this._$instanceId; - buffer[index++] = this._$parent ? this._$parent._$instanceId : -1; - - this._$registerProperty(buffer, index); - - const message: PropertyMessageMapImpl = $getRenderMessageObject(); - message.command = "createDisplayObjectContainer"; - message.buffer = buffer; - - const options: ArrayBuffer[] = $getArray(buffer.buffer); - $rendererWorker.postMessage(message, options); - - $poolRenderMessageObject(message); - $poolArray(options); - - this._$postChildrenIds(); - } - - /** - * @return {void} - * @method - * @private - */ - _$postProperty (): void - { - if (!$rendererWorker) { - return ; - } - - this._$postChildrenIds(); - - const options: ArrayBuffer[] = $getArray(); - const message: PropertyMessageMapImpl = this._$createMessage(); - - $rendererWorker - .postMessage(message, options); - - $poolArray(options); - - this._$posted = true; - this._$updated = false; - } - - /** - * @param {array} [childrenIds=null] - * @return {void} - * @method - * @private - */ - _$postChildrenIds (childrenIds: number[] | null = null): void - { - if (!$rendererWorker || !this._$created) { - return ; - } - - let poolIds = false; - if (!childrenIds) { - - const children: DisplayObjectImpl[] = this._$getChildren(); - - childrenIds = $getArray(); - for (let idx: number = 0; idx < children.length; ++idx) { - childrenIds.push(children[idx]._$instanceId); - } - - poolIds = true; - } - - const buffer: Int32Array = new Int32Array(childrenIds.length + 1); - buffer[0] = this._$instanceId; - buffer.set(childrenIds, 1); - - const message: PropertyMessageMapImpl = $getRenderMessageObject(); - message.command = "setChildren"; - message.buffer = buffer; - - const options: ArrayBuffer[] = $getArray(buffer.buffer); - $rendererWorker.postMessage(message, options); - - $poolRenderMessageObject(message); - $poolArray(options); - - if (poolIds) { - $poolArray(childrenIds); - } + displayObjectContainerSwapChildrenAtUseCase(this, index1, index2); } -} +} \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.test.ts new file mode 100644 index 00000000..3ed8f49b --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.test.ts @@ -0,0 +1,60 @@ +import { execute } from "./DisplayObjectContainerAddedToStageService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { $stageAssignedMap } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerAddedToStageService.js test", () => +{ + it("execute test case1 assign stage", () => + { + const container = new DisplayObjectContainer(); + + const shape = container.addChild(new Shape()); + $stageAssignedMap.add(shape.instanceId); + + const textField = container.addChild(new TextField()); + $stageAssignedMap.add(textField.instanceId); + + const video = container.addChild(new Video(100, 300)); + $stageAssignedMap.add(video.instanceId); + + expect(container.children.length).toBe(3); + + expect(shape.$addedToStage).toBe(false); + expect(textField.$addedToStage).toBe(false); + expect(video.$addedToStage).toBe(false); + + execute(container); + + expect(shape.$addedToStage).toBe(true); + expect(textField.$addedToStage).toBe(true); + expect(video.$addedToStage).toBe(true); + + $stageAssignedMap.delete(shape.instanceId); + $stageAssignedMap.delete(textField.instanceId); + $stageAssignedMap.delete(video.instanceId); + }); + + it("execute test case2 not assign stage", () => + { + const container = new DisplayObjectContainer(); + + const shape = container.addChild(new Shape()); + const textField = container.addChild(new TextField()); + const video = container.addChild(new Video(100, 300)); + expect(container.children.length).toBe(3); + + expect(shape.$addedToStage).toBe(false); + expect(textField.$addedToStage).toBe(false); + expect(video.$addedToStage).toBe(false); + + execute(container); + + expect(shape.$addedToStage).toBe(false); + expect(textField.$addedToStage).toBe(false); + expect(video.$addedToStage).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.ts new file mode 100644 index 00000000..1bcb7584 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAddedToStageService.ts @@ -0,0 +1,33 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectDispatchAddedToStageEventService } from "../../DisplayObject/service/DisplayObjectDispatchAddedToStageEventService"; + +/** + * @description ステージに追加された DisplayObjectContainer の子要素のADDED_TO_STAGEイベントを発行 + * Issue ADDED_TO_STAGE events for child elements of DisplayObjectContainer added to the stage + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C +): void => { + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + displayObjectDispatchAddedToStageEventService(child); + + if (!child.isContainerEnabled) { + continue; + } + + execute(child as unknown as DisplayObjectContainer); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.test.ts new file mode 100644 index 00000000..9ec16bc6 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.test.ts @@ -0,0 +1,44 @@ +import { execute } from "./DisplayObjectContainerAssignStageAndRootService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { $rootMap, $stageAssignedMap } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerAssignStageAndRootService.js test", () => +{ + it("execute test case1 assign stage", () => + { + const container = new DisplayObjectContainer(); + + const shape = container.addChild(new Shape()); + const textField = container.addChild(new TextField()); + const video = container.addChild(new Video(100, 300)); + + expect(container.children.length).toBe(3); + + expect($rootMap.has(shape)).toBe(false); + expect($stageAssignedMap.has(shape.instanceId)).toBe(false); + expect($rootMap.has(textField)).toBe(false); + expect($stageAssignedMap.has(textField.instanceId)).toBe(false); + expect($rootMap.has(video)).toBe(false); + expect($stageAssignedMap.has(video.instanceId)).toBe(false); + + execute(container); + + expect($rootMap.has(shape)).toBe(true); + expect($stageAssignedMap.has(shape.instanceId)).toBe(true); + expect($rootMap.has(textField)).toBe(true); + expect($stageAssignedMap.has(textField.instanceId)).toBe(true); + expect($rootMap.has(video)).toBe(true); + expect($stageAssignedMap.has(video.instanceId)).toBe(true); + + $rootMap.delete(shape); + $rootMap.delete(textField); + $rootMap.delete(video); + $stageAssignedMap.delete(shape.instanceId); + $stageAssignedMap.delete(textField.instanceId); + $stageAssignedMap.delete(video.instanceId); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.ts new file mode 100644 index 00000000..5b4bba09 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerAssignStageAndRootService.ts @@ -0,0 +1,38 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { + $rootMap, + $stageAssignedMap +} from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectContainer の子要素に root と stage を設定 + * Set root and stage for child elements of DisplayObjectContainer + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C +): void => { + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + // set root and stage + $rootMap.set(child, display_object_container.root); + $stageAssignedMap.add(child.instanceId); + + if (!child.isContainerEnabled) { + continue; + } + + execute(child as DisplayObjectContainer); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts new file mode 100644 index 00000000..f1dbe03e --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./DisplayObjectContainerContainsService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerContainsService.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape = container.addChild(new Shape()); + + const sprite1 = container.addChild(new DisplayObjectContainer()); + const textField = sprite1.addChild(new TextField()); + + const sprite2 = sprite1.addChild(new DisplayObjectContainer()); + const video = sprite2.addChild(new Video(100, 300)); + + expect(execute(container, shape)).toBe(true); + expect(execute(container, textField)).toBe(true); + expect(execute(container, video)).toBe(true); + expect(execute(container, container)).toBe(true); + expect(execute(container, sprite1)).toBe(true); + expect(execute(container, sprite2)).toBe(true); + + expect(execute(sprite1, shape)).toBe(false); + expect(execute(sprite2, textField)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts new file mode 100644 index 00000000..3705f779 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerContainsService.ts @@ -0,0 +1,45 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; + +/** + * @description DisplayObjectContainer とその子孫が指定の DisplayObject を含むかどうか + * Whether DisplayObjectContainer and its descendants contain the specified DisplayObject + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + display_object: D +): boolean => { + + if (display_object_container.instanceId === display_object.instanceId) { + return true; + } + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + if (child.instanceId === display_object.instanceId) { + return true; + } + + if (!child.isContainerEnabled) { + continue; + } + + if ((child as DisplayObjectContainer).contains(display_object)) { + return true; + } + } + + return false; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.test.ts new file mode 100644 index 00000000..82174a75 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.test.ts @@ -0,0 +1,39 @@ +import { execute } from "./DisplayObjectContainerGetChildAtService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerGetChildAtService.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + + const shape = container.addChild(new Shape()); + const textField = container.addChild(new TextField()); + const video = container.addChild(new Video(100, 300)); + expect(container.children.length).toBe(3); + + const child0 = execute(container, 0); + if (!child0) { + throw new Error("child is null"); + } + expect(child0.instanceId).toBe(shape.instanceId); + + const child1 = execute(container, 1); + if (!child1) { + throw new Error("child is null"); + } + expect(child1.instanceId).toBe(textField.instanceId); + + const child2 = execute(container, 2); + if (!child2) { + throw new Error("child is null"); + } + expect(child2.instanceId).toBe(video.instanceId); + + expect(execute(container, 3)).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.ts new file mode 100644 index 00000000..1912438c --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildAtService.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; + +/** + * @description DisplayObjectContainer の指定indexの子要素を取得 + * Get the child element of DisplayObjectContainer at the specified index + * + * @param {DisplayObjectContainer} display_object_container + * @param {number} index + * @return {DisplayObject | null} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + index: number +): D | null => { + const children = display_object_container.children; + return index in children ? children[index] : null; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts new file mode 100644 index 00000000..e978cf97 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./DisplayObjectContainerGetChildByNameService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerGetChildByNameService.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape = container.addChild(new Shape()); + shape.name = "shape"; + + const textField = container.addChild(new TextField()); + textField.name = "textField"; + + const video = container.addChild(new Video(100, 300)); + video.name = "video"; + + expect(execute(container, "")).toBe(null); + expect(execute(container, "shape")).toBe(shape); + expect(execute(container, "textField")).toBe(textField); + expect(execute(container, "video")).toBe(video); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts new file mode 100644 index 00000000..bc445a2a --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerGetChildByNameService.ts @@ -0,0 +1,35 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; + +/** + * @description 指定した名前の子要素を取得します + * Gets the child element with the specified name. + * + * @param {DisplayObjectContainer} display_object_container + * @param {string} name + * @return {DisplayObject | null} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + name: string +): D | null => { + + if (!name) { + return null; + } + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child.name || child.name !== name) { + continue; + } + + return child; + } + + return null; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.test.ts new file mode 100644 index 00000000..19480075 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./DisplayObjectContainerRemovedToStageService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { $stageAssignedMap } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemovedToStageService.js test", () => +{ + it("execute test case1 assign stage", () => + { + const container = new DisplayObjectContainer(); + + const shape = container.addChild(new Shape()); + $stageAssignedMap.add(shape.instanceId); + + const textField = container.addChild(new TextField()); + $stageAssignedMap.add(textField.instanceId); + + const video = container.addChild(new Video(100, 300)); + $stageAssignedMap.add(video.instanceId); + + expect(container.children.length).toBe(3); + + shape.$addedToStage = true; + textField.$addedToStage = true; + video.$addedToStage = true; + expect(shape.$addedToStage).toBe(true); + expect(textField.$addedToStage).toBe(true); + expect(video.$addedToStage).toBe(true); + + execute(container); + + expect(shape.$addedToStage).toBe(false); + expect(textField.$addedToStage).toBe(false); + expect(video.$addedToStage).toBe(false); + + $stageAssignedMap.delete(shape.instanceId); + $stageAssignedMap.delete(textField.instanceId); + $stageAssignedMap.delete(video.instanceId); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.ts new file mode 100644 index 00000000..9f54e2e7 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService.ts @@ -0,0 +1,34 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectDispatchRemovedToStageEventService } from "../../DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService"; + +/** + * @description ステージに追加された DisplayObjectContainer の子要素のREMOVED_FROM_STAGEイベントを発行 + * Issue REMOVED_FROM_STAGE events for child elements of DisplayObjectContainer added to the stage + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C +): void => { + + const children = display_object_container.children as D[]; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + displayObjectDispatchRemovedToStageEventService(child); + + if (!child.isContainerEnabled) { + continue; + } + + execute(child as unknown as DisplayObjectContainer); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.test.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.test.ts new file mode 100644 index 00000000..5c3e2ab8 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.test.ts @@ -0,0 +1,46 @@ +import { execute } from "./DisplayObjectContainerUnAssignStageAndRootService"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { $rootMap, $stageAssignedMap } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerUnAssignStageAndRootService.js test", () => +{ + it("execute test case1 assign stage", () => + { + const container = new DisplayObjectContainer(); + $stageAssignedMap.clear(); + + const shape = container.addChild(new Shape()); + $rootMap.set(shape, null); + $stageAssignedMap.add(shape.instanceId); + + const textField = container.addChild(new TextField()); + $rootMap.set(textField, null); + $stageAssignedMap.add(textField.instanceId); + + const video = container.addChild(new Video(100, 300)); + $rootMap.set(video, null); + $stageAssignedMap.add(video.instanceId); + + expect(container.children.length).toBe(3); + + expect($rootMap.has(shape)).toBe(true); + expect($stageAssignedMap.has(shape.instanceId)).toBe(true); + expect($rootMap.has(textField)).toBe(true); + expect($stageAssignedMap.has(textField.instanceId)).toBe(true); + expect($rootMap.has(video)).toBe(true); + expect($stageAssignedMap.has(video.instanceId)).toBe(true); + + execute(container); + + expect($rootMap.has(shape)).toBe(false); + expect($stageAssignedMap.has(shape.instanceId)).toBe(false); + expect($rootMap.has(textField)).toBe(false); + expect($stageAssignedMap.has(textField.instanceId)).toBe(false); + expect($rootMap.has(video)).toBe(false); + expect($stageAssignedMap.has(video.instanceId)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.ts b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.ts new file mode 100644 index 00000000..9709ff37 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/service/DisplayObjectContainerUnAssignStageAndRootService.ts @@ -0,0 +1,43 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Video } from "@next2d/media"; +import { + $rootMap, + $stageAssignedMap +} from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectContainer の子要素に設定した root と stage を解除 + * Remove the root and stage set for the child elements of DisplayObjectContainer + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C +): void => { + + const children = display_object_container.children; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx]; + if (!child) { + continue; + } + + // set root and stage + $rootMap.delete(child); + $stageAssignedMap.delete(child.instanceId); + + if (child.isVideo) { + (child as unknown as Video).pause(); + } + + if (!child.isContainerEnabled) { + continue; + } + + execute(child as DisplayObjectContainer); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.test.ts new file mode 100644 index 00000000..f19931d3 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.test.ts @@ -0,0 +1,79 @@ +import { execute } from "./DisplayObjectContainerAddChildUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerAddChildUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape1 = new Shape(); + const shape2 = new Shape(); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape1.$added).toBe(false); + expect(shape1.parent).toBe(null); + expect(container.children.length).toBe(0); + execute(container, shape1); + expect(shape1.$added).toBe(true); + expect((shape1.parent as NonNullable).instanceId).toBe(container.instanceId); + expect(container.changed).toBe(true); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape2.$added).toBe(false); + expect(shape2.parent).toBe(null); + expect(container.children.length).toBe(1); + execute(container, shape2, container.numChildren); + expect(shape2.$added).toBe(true); + expect((shape2.parent as NonNullable).instanceId).toBe(container.instanceId); + expect(container.changed).toBe(true); + + expect(container.children.length).toBe(2); + + expect((container.children[0] as NonNullable).instanceId).toBe(shape1.instanceId); + expect((container.children[1] as NonNullable).instanceId).toBe(shape2.instanceId); + }); + + it("execute test case2", () => + { + const container = new DisplayObjectContainer(); + const shape1 = new Shape(); + const shape2 = new Shape(); + const shape3 = new Shape(); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape1.$added).toBe(false); + expect(shape1.parent).toBe(null); + expect(container.children.length).toBe(0); + execute(container, shape1, 0); + expect(container.children.length).toBe(1); + expect(shape1.$added).toBe(true); + expect((shape1.parent as NonNullable).instanceId).toBe(container.instanceId); + expect(container.changed).toBe(true); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape2.$added).toBe(false); + expect(shape2.parent).toBe(null); + execute(container, shape2, 0); + expect(container.children.length).toBe(2); + expect(shape2.$added).toBe(true); + expect((shape2.parent as NonNullable).instanceId).toBe(container.instanceId); + expect(container.changed).toBe(true); + + execute(container, shape3, 0); + expect(container.children.length).toBe(3); + + expect((container.children[0] as NonNullable).instanceId).toBe(shape3.instanceId); + expect((container.children[1] as NonNullable).instanceId).toBe(shape2.instanceId); + expect((container.children[2] as NonNullable).instanceId).toBe(shape1.instanceId); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.ts new file mode 100644 index 00000000..5920fe88 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAddChildUseCase.ts @@ -0,0 +1,78 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectContainerAssignStageAndRootService } from "../service/DisplayObjectContainerAssignStageAndRootService"; +import { execute as displayObjectContainerAddedToStageService } from "../service/DisplayObjectContainerAddedToStageService"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as displayObjectDispatchAddedEventService } from "../../DisplayObject/service/DisplayObjectDispatchAddedEventService"; +import { execute as displayObjectDispatchAddedToStageEventService } from "../../DisplayObject/service/DisplayObjectDispatchAddedToStageEventService"; +import { + $rootMap, + $stageAssignedMap +} from "../../DisplayObjectUtil"; + +/** + * @description 指定のDisplayObjectContainerに子要素を追加する + * Add a child element to the specified DisplayObjectContainer + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object + * @return {DisplayObject} + * @method + * @public + */ +export const execute =

( + display_object_container: P, + display_object: D, + index: number = -1 +): D => { + + const parent = display_object.parent; + if (parent) { + parent.removeChild(display_object); + } + + // added display object + const children = display_object_container.children; + if (0 > index) { + children.push(display_object); + } else { + children.splice(index, 0, display_object); + } + + // cache clear + display_object_container.$container = null; + + // Set parent-child relationship + display_object.parent = display_object_container; + + // 親が Stage に追加されている場合は、マップデータに情報を追加 + if ($stageAssignedMap.has(display_object_container.instanceId)) { + + $rootMap.set(display_object, display_object_container.root); + $stageAssignedMap.add(display_object.instanceId); + + // If container functionality is available, set stage and root for small elements + if (display_object.isContainerEnabled) { + displayObjectContainerAssignStageAndRootService( + display_object as unknown as DisplayObjectContainer + ); + } + } + + // Dispatch added event + displayObjectDispatchAddedEventService(display_object); + + if ($stageAssignedMap.has(display_object_container.instanceId)) { + displayObjectDispatchAddedToStageEventService(display_object); + + if (display_object.isContainerEnabled) { + displayObjectContainerAddedToStageService( + display_object as unknown as DisplayObjectContainer + ); + } + } + + displayObjectApplyChangesService(display_object); + + return display_object; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.test.ts new file mode 100644 index 00000000..85f0e53e --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.test.ts @@ -0,0 +1,25 @@ +import { execute } from "./DisplayObjectContainerAdvanceFrameUseCase"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerAdvanceFrameUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const movieClip = container.addChild(new MovieClip()); + + movieClip.totalFrames = 10; + movieClip.$canAction = false; + movieClip.$hasTimelineHeadMoved = false; + + expect(movieClip.currentFrame).toBe(1); + expect(movieClip.$hasTimelineHeadMoved).toBe(false); + + execute(container); + + expect(movieClip.currentFrame).toBe(2); + expect(movieClip.$hasTimelineHeadMoved).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.ts new file mode 100644 index 00000000..7ddf1303 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase.ts @@ -0,0 +1,51 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as movieClipAdvanceFrameUseCase } from "../../MovieClip/usecase/MovieClipAdvanceFrameUseCase"; + +/** + * @description DisplayObjectContainer 内のMovieClipのフレームを1フレーム進める + * Advance the frame of the MovieClip in the DisplayObjectContainer by 1 frame + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C +): void => { + + const container = display_object_container.$container; + if (container) { + for (let idx = 0; container.length > idx; ++idx) { + + const child = container[idx]; + + if (child.isTimelineEnabled) { + movieClipAdvanceFrameUseCase(child); + } + + execute(child); + } + } else { + const caches = []; + const children = display_object_container.children; + for (let idx = 0; children.length > idx; ++idx) { + + const child = children[idx]; + if (!child.isContainerEnabled) { + continue; + } + + if (child.isTimelineEnabled) { + movieClipAdvanceFrameUseCase(child); + } + + execute(child); + + caches.push(child); + } + + // cache + display_object_container.$container = caches; + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.test.ts new file mode 100644 index 00000000..66e886ba --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.test.ts @@ -0,0 +1,39 @@ +import { execute } from "./DisplayObjectContainerCalcBoundsMatrixUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Matrix } from "@next2d/geom"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerCalcBoundsMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.$matrix = new Matrix(); + container.$matrix.scale(2, 3); + + const shape = new Shape(); + shape.$matrix = new Matrix(); + shape.$matrix.scale(1.1, 1.2); + + shape.graphics.xMin = -120; + shape.graphics.xMax = 20; + shape.graphics.yMin = -120; + shape.graphics.yMax = 20; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + const bounds = execute(container); + expect(bounds[0]).toBe(-264); + expect(bounds[1]).toBe(-432.0000305175781); + expect(bounds[2]).toBe(200); + expect(bounds[3]).toBe(900); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.ts new file mode 100644 index 00000000..b645f749 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerCalcBoundsMatrixUseCase.ts @@ -0,0 +1,88 @@ +import type { Shape } from "../../Shape"; +import type { Video } from "@next2d/media"; +import type { TextField } from "@next2d/text"; +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as shapeCalcBoundsMatrixUseCase } from "../../Shape/usecase/ShapeCalcBoundsMatrixUseCase"; +import { execute as videoCalcBoundsMatrixUseCase } from "../../Video/usecase/VideoCalcBoundsMatrixUseCase"; +import { execute as textFieldCalcBoundsMatrixUseCase } from "../../TextField/usecase/TextFieldCalcBoundsMatrixUseCase"; +import { + $getBoundsArray, + $poolBoundsArray +} from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectContainerのバウンディングボックスを計算 + * Calculate the bounding box of the DisplayObjectContainer + * + * @param {DisplayObjectContainer} display_object_container + * @return {Float32Array} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + matrix: Float32Array | null = null +): Float32Array => { + + const children = display_object_container.children; + if (!children.length) { + return $getBoundsArray(0, 0, 0, 0); + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object_container); + const tMatrix = rawMatrix + ? matrix + ? Matrix.multiply(matrix, rawMatrix) + : rawMatrix + : matrix; + + const no = Number.MAX_VALUE; + let xMin = no; + let xMax = -no; + let yMin = no; + let yMax = -no; + for (let idx = 0; idx < children.length; idx++) { + + const child = children[idx] as DisplayObject; + if (!child.visible) { + continue; + } + + let bounds: Float32Array | null = null; + switch (true) { + + case child.isContainerEnabled: + bounds = execute(child as DisplayObjectContainer, tMatrix); + break; + + case child.isShape: + bounds = shapeCalcBoundsMatrixUseCase(child as Shape, tMatrix); + break; + + case child.isText: + bounds = textFieldCalcBoundsMatrixUseCase(child as TextField, tMatrix); + break; + + case child.isVideo: + bounds = videoCalcBoundsMatrixUseCase(child as Video, tMatrix); + break; + + } + + if (!bounds) { + continue; + } + + xMin = Math.min(xMin, bounds[0]); + yMin = Math.min(yMin, bounds[1]); + xMax = Math.max(xMax, bounds[2]); + yMax = Math.max(yMax, bounds[3]); + + $poolBoundsArray(bounds); + } + + return $getBoundsArray(xMin, yMin, xMax, yMax); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateClipQueueUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateClipQueueUseCase.ts new file mode 100644 index 00000000..457f9958 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateClipQueueUseCase.ts @@ -0,0 +1,65 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Matrix } from "@next2d/geom"; +import { renderQueue } from "@next2d/render-queue"; +import { $RENDERER_CONTAINER_TYPE } from "../../DisplayObjectUtil"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as shapeGenerateClipQueueUseCase } from "../../Shape/usecase/ShapeGenerateClipQueueUseCase"; + +/** + * @description renderer workerに渡すDisplayObjectContainerのマスク描画データを生成 + * Generate mask drawing data of DisplayObjectContainer to pass to renderer + * + * @param {DisplayObjectContainer} display_object_container + * @param {number[]} render_queue + * @param {Float32Array} matrix + * @return {void} + * @method + * @protected + */ +export const execute =

( + display_object_container: P, + matrix: Float32Array +): void => { + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object_container); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + const children = display_object_container.children; + renderQueue.push($RENDERER_CONTAINER_TYPE, children.length); + + for (let idx = 0; idx < children.length; idx++) { + + const child = children[idx]; + if (!child) { + continue; + } + + // mask instance + if (child.isMask) { + continue; + } + + switch (true) { + + case child.isContainerEnabled: + execute(child, tMatrix); + break; + + case child.isShape: + shapeGenerateClipQueueUseCase(child, tMatrix); + break; + + // text, videoはマスク対象外 + default: + break; + + } + } + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateRenderQueueUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateRenderQueueUseCase.ts new file mode 100644 index 00000000..ca63bd25 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateRenderQueueUseCase.ts @@ -0,0 +1,282 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { TextField } from "@next2d/text"; +import type { Video } from "@next2d/media"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as shapeGenerateRenderQueueUseCase } from "../../Shape/usecase/ShapeGenerateRenderQueueUseCase"; +import { execute as shapeGenerateClipQueueUseCase } from "../../Shape/usecase/ShapeGenerateClipQueueUseCase"; +import { execute as textFieldGenerateRenderQueueUseCase } from "../../TextField/usecase/TextFieldGenerateRenderQueueUseCase"; +import { execute as videoGenerateRenderQueueUseCase } from "../../Video/usecase/VideoGenerateRenderQueueUseCase"; +import { execute as displayObjectIsMaskReflectedInDisplayUseCase } from "../../DisplayObject/usecase/DisplayObjectIsMaskReflectedInDisplayUseCase"; +import { execute as displayObjectContainerGenerateClipQueueUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerGenerateClipQueueUseCase"; +import { renderQueue } from "@next2d/render-queue"; +import { + $clamp, + $RENDERER_CONTAINER_TYPE +} from "../../DisplayObjectUtil"; +import { + ColorTransform, + Matrix +} from "@next2d/geom"; + +/** + * @description renderer workerに渡すコンテナの描画データを生成 + * Generate rendering data of the container to be passed to the renderer worker + * + * @param {DisplayObjectContainer} display_object_container + * @param {ImageBitmap[]} image_bitmaps + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {void} + * @method + * @private + */ +export const execute =

( + display_object_container: P, + image_bitmaps: ImageBitmap[], + matrix: Float32Array, + color_transform: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): void => { + + if (!display_object_container.visible) { + renderQueue.push(0); + return ; + } + + // transformed ColorTransform(tColorTransform) + const rawColor = displayObjectGetRawColorTransformUseCase(display_object_container); + const tColorTransform = rawColor + ? ColorTransform.multiply(color_transform, rawColor) + : color_transform; + + const alpha: number = $clamp(tColorTransform[3] + tColorTransform[7] / 255, 0, 1, 0); + if (!alpha) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + renderQueue.push(0); + return ; + } + + const children = display_object_container.children; + if (!children.length) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + renderQueue.push(0); + return ; + } + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object_container); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + // size zero + if (!tMatrix[0] && !tMatrix[1] + || !tMatrix[2] && !tMatrix[3] + ) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + renderQueue.push(0); + return ; + } + + renderQueue.push(1, $RENDERER_CONTAINER_TYPE); + + // mask + const maskDisplayObject = display_object_container.mask; + if (maskDisplayObject) { + + const bounds = displayObjectIsMaskReflectedInDisplayUseCase( + maskDisplayObject, + tMatrix, + renderer_width, + renderer_height, + point_x, + point_y + ); + + if (!bounds) { + renderQueue.push(0); + } else { + + // マスクの描画範囲 + renderQueue.push(1, bounds[0], bounds[1], bounds[2], bounds[3]); + + switch (true) { + + case maskDisplayObject.isContainerEnabled: // 0x00 + displayObjectContainerGenerateClipQueueUseCase( + maskDisplayObject as DisplayObjectContainer, + tMatrix + ); + break; + + case maskDisplayObject.isShape: // 0x01 + shapeGenerateClipQueueUseCase( + maskDisplayObject as Shape, + tMatrix + ); + break; + + default: + break; + } + } + + maskDisplayObject.changed = false; + } else { + renderQueue.push(0); + } + + renderQueue.push(children.length); + + let clipDepth = 0; + let canRenderMask = true; + for (let idx = 0; idx < children.length; ++idx) { + + const child = children[idx] as DisplayObject; + if (child.isMask) { + continue; + } + + renderQueue.push(child.placeId, child.clipDepth); + if (clipDepth && child.placeId > clipDepth) { + clipDepth = 0; + canRenderMask = true; + } + + if (!canRenderMask) { + child.changed = false; + continue; + } + + if (child.clipDepth) { + clipDepth = child.clipDepth; + + // マスクの描画開始判定 + const bounds = displayObjectIsMaskReflectedInDisplayUseCase( + child, + tMatrix, + renderer_width, + renderer_height, + point_x, + point_y + ); + + canRenderMask = bounds ? true : false; + renderQueue.push(+canRenderMask); + + if (!bounds) { + child.changed = false; + continue; + } + + renderQueue.push(bounds[0], bounds[1], bounds[2], bounds[3]); + switch (true) { + + case child.isContainerEnabled: // 0x00 + displayObjectContainerGenerateClipQueueUseCase( + child as DisplayObjectContainer, + tMatrix + ); + break; + + case child.isShape: // 0x01 + shapeGenerateClipQueueUseCase( + child as Shape, + tMatrix + ); + break; + + default: + break; + } + + child.changed = false; + continue; + } + + switch (true) { + + case child.isContainerEnabled: // 0x00 + execute( + child as DisplayObjectContainer, + image_bitmaps, + tMatrix, + tColorTransform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case child.isShape: // 0x01 + shapeGenerateRenderQueueUseCase( + child as Shape, + tMatrix, + tColorTransform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case child.isText: // 0x02 + textFieldGenerateRenderQueueUseCase( + child as TextField, + tMatrix, + tColorTransform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case child.isVideo: // 0x03 + videoGenerateRenderQueueUseCase( + child as Video, + image_bitmaps, + tMatrix, + tColorTransform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + default: + break; + + } + + child.changed = false; + } + + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts new file mode 100644 index 00000000..44280114 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase.ts @@ -0,0 +1,329 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { DisplayObject } from "../../DisplayObject"; +import type { Shape } from "../../Shape"; +import type { Sprite } from "../../Sprite"; +import type { InteractiveObject } from "../../InteractiveObject"; +import type { TextField } from "@next2d/text"; +import type { Video } from "@next2d/media"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as shapeHitTestUseCase } from "../../Shape/usecase/ShapeHitTestUseCase"; +import { execute as textFieldHitTestUseCase } from "../../TextField/usecase/TextFieldHitTestUseCase"; +import { execute as videoHitTestUseCase } from "../../Video/usecase/VideoHitTestUseCase"; +import { + $getArray, + $poolArray, + $getMap, + $poolMap +} from "../../DisplayObjectUtil"; + +/** + * @description コンテナ内のヒット判定 + * Hit judgment in the container + * + * @param {DisplayObjectContainer} display_object_container + * @param {CanvasRenderingContext2D} hit_context + * @param {Float32Array} matrix + * @param {IPlayerHitObject} hit_object + * @param {boolean} [mouse_children=true] + * @return {boolean} + * @method + * @protected + */ +export const execute =

( + display_object_container: P, + hit_context: CanvasRenderingContext2D, + matrix: Float32Array, + hit_object: IPlayerHitObject, + mouse_children: boolean = true +): boolean => { + + const children = display_object_container.children as D[]; + if (!children.length) { + return false; + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object_container); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + // mask set + const clips: D[] = $getArray(); + const targets: D[] = $getArray(); + const clipIndexes: Map = $getMap(); + + let clipDepth = 0; + let clipIdx = 0; + for (let idx = 0; idx < children.length; ++idx) { + + const instance = children[idx]; + if (!instance) { + continue; + } + + if (instance.isMask) { + continue; + } + + if (instance.clipDepth) { + clipIdx = clips.length; + clipDepth = instance.clipDepth; + clips.push(instance); + continue; + } + + // fixed logic + if (!instance.visible) { + continue; + } + + // clip end + if (clipDepth && instance.placeId > clipDepth) { + clipIdx = 0; + clipDepth = 0; + } + + // clip check on + if (clipIdx) { + clipIndexes.set(instance.instanceId, clipIdx); + } + + targets.push(instance); + } + + const mouseChildren = display_object_container.mouseChildren && mouse_children; + + // fixed logic + if (display_object_container.isSprite + && (display_object_container as unknown as Sprite).hitArea + ) { + const hitTest = execute( + (display_object_container as unknown as Sprite).hitArea as unknown as DisplayObjectContainer, + hit_context, tMatrix, hit_object, mouseChildren + ); + + if (hitTest + && mouseChildren + && display_object_container.mouseEnabled + ) { + if ((display_object_container as unknown as Sprite).buttonMode + && (display_object_container as unknown as Sprite).useHandCursor + ) { + hit_object.pointer = "pointer"; + } + + if (!hit_object.hit) { + hit_object.hit = display_object_container; + } + } + + return hitTest; + } + + let hit = false; + const maskDisplayObject = display_object_container.mask as D; + if (maskDisplayObject) { + let hitTest = false; + switch (true) { + + case maskDisplayObject.isContainerEnabled: + hitTest = execute( + maskDisplayObject as unknown as DisplayObjectContainer, + hit_context, tMatrix, hit_object, mouseChildren + ); + break; + + case maskDisplayObject.isShape: + hitTest = shapeHitTestUseCase( + maskDisplayObject as unknown as Shape, + hit_context, tMatrix, hit_object + ); + break; + + case maskDisplayObject.isText: + hitTest = textFieldHitTestUseCase( + maskDisplayObject as unknown as TextField, + hit_context, tMatrix, hit_object + ); + break; + + case maskDisplayObject.isVideo: + hitTest = videoHitTestUseCase( + maskDisplayObject as unknown as Video, + hit_context, tMatrix, hit_object + ); + break; + + default: + break; + + } + + if (!hitTest) { + return false; + } + } + + while (targets.length) { + + const instance = targets.pop(); + if (!instance) { + continue; + } + + if (instance.isMask) { + continue; + } + + // mask target + if (clipIndexes.has(instance.instanceId)) { + + const index = clipIndexes.get(instance.instanceId) as number; + if (!index) { + continue; + } + + const clip = clips[index]; + if (!clip) { + continue; + } + + let hitTest = false; + switch (true) { + + case clip.isContainerEnabled: + hitTest = execute( + clip as unknown as DisplayObjectContainer, + hit_context, tMatrix, hit_object, mouseChildren + ); + break; + + case clip.isShape: + hitTest = shapeHitTestUseCase( + clip as unknown as Shape, + hit_context, tMatrix, hit_object + ); + break; + + case clip.isText: + hitTest = textFieldHitTestUseCase( + clip as unknown as TextField, + hit_context, tMatrix, hit_object + ); + break; + + case clip.isVideo: + hitTest = videoHitTestUseCase( + clip as unknown as Video, + hit_context, tMatrix, hit_object + ); + break; + + default: + break; + + } + + if (!hitTest) { + continue; + } + } + + let hitTest = false; + switch (true) { + + case instance.isContainerEnabled: + hitTest = execute( + instance as unknown as DisplayObjectContainer, + hit_context, tMatrix, hit_object, mouseChildren + ); + break; + + case instance.isShape: + hitTest = shapeHitTestUseCase( + instance as unknown as Shape, + hit_context, tMatrix, hit_object + ); + break; + + case instance.isText: + hitTest = textFieldHitTestUseCase( + instance as unknown as TextField, + hit_context, tMatrix, hit_object + ); + break; + + case instance.isVideo: + hitTest = videoHitTestUseCase( + instance as unknown as Video, + hit_context, tMatrix, hit_object + ); + break; + + default: + break; + + } + + if (!hitTest) { + continue; + } + + hit = true; + if (!mouseChildren) { + break; + } + + if (!instance.isInteractive) { + continue; + } + + if (!(instance as unknown as InteractiveObject).mouseEnabled) { + continue; + } + + if (hit_object.pointer === "auto") { + + switch (true) { + + case instance.isText: + if ((instance as unknown as TextField).type === "input") { + hit_object.pointer = "text"; + } + break; + + case instance.isSprite: + if ((instance as unknown as Sprite).buttonMode + && (instance as unknown as Sprite).useHandCursor + ) { + hit_object.pointer = "pointer"; + } + break; + + default: + hit_object.pointer = "pointer"; + break; + + } + } + + if (!hit_object.hit) { + hit_object.hit = instance; + } + + break; + } + + // pool + $poolArray(clips); + $poolArray(targets); + $poolMap(clipIndexes); + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + return hit; +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerPrepareUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerPrepareUseCase.ts new file mode 100644 index 00000000..4dc927c3 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerPrepareUseCase.ts @@ -0,0 +1,43 @@ +import type { MovieClip } from "../../MovieClip"; +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as movieClipPrepareActionUseCase } from "../../MovieClip/usecase/MovieClipPrepareActionUseCase"; +import { execute as movieClipPrepareSoundUseCase } from "../../MovieClip/usecase/MovieClipPrepareSoundUseCase"; + +/** + * @description 子孫のサウンド・アクションを準備する + * Prepare sound and action of descendants + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = (display_object_container: C): void => +{ + const children = display_object_container.children; + for (let idx = children.length - 1; idx > -1; --idx) { + + const displayObject = children[idx] as DisplayObject; + if (!displayObject) { + continue; + } + + if (!displayObject.isContainerEnabled) { + continue; + } + + execute(displayObject as C); + + // タイムラインが有効な場合 + if (!displayObject.isTimelineEnabled) { + continue; + } + + // サウンドを登録 + movieClipPrepareSoundUseCase(displayObject as MovieClip); + + // アクションを登録 + movieClipPrepareActionUseCase(displayObject as MovieClip); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.test.ts new file mode 100644 index 00000000..148412a5 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.test.ts @@ -0,0 +1,39 @@ +import { execute } from "./DisplayObjectContainerRawBoundsMatrixUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { Matrix } from "@next2d/geom"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRawBoundsMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.$matrix = new Matrix(); + container.$matrix.scale(2, 3); + + const shape = new Shape(); + shape.$matrix = new Matrix(); + shape.$matrix.scale(1.2, 1.3); + + shape.graphics.xMin = -120; + shape.graphics.xMax = 20; + shape.graphics.yMin = -120; + shape.graphics.yMax = 20; + container.addChild(shape); + + const textField = new TextField(); + container.addChild(textField); + + const video = new Video(100, 300); + container.addChild(video); + + const bounds = execute(container); + expect(bounds[0]).toBe(-288); + expect(bounds[1]).toBe(-467.9999694824219); + expect(bounds[2]).toBe(200); + expect(bounds[3]).toBe(900); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.ts new file mode 100644 index 00000000..8530b673 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRawBoundsMatrixUseCase.ts @@ -0,0 +1,89 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { Video } from "@next2d/media"; +import type { TextField } from "@next2d/text"; +import { Matrix } from "@next2d/geom"; +import { execute as shapeCalcBoundsMatrixUseCase } from "../../Shape/usecase/ShapeCalcBoundsMatrixUseCase"; +import { execute as videoCalcBoundsMatrixUseCase } from "../../Video/usecase/VideoCalcBoundsMatrixUseCase"; +import { execute as textFieldCalcBoundsMatrixUseCase } from "../../TextField/usecase/TextFieldCalcBoundsMatrixUseCase"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { + $getBoundsArray, + $poolBoundsArray +} from "../../DisplayObjectUtil"; + +/** + * @description DisplayObjectContainerのmatrixを含まないバウンディングボックスを返却 + * Returns a bounding box that does not include the matrix of the DisplayObjectContainer + * + * @param {DisplayObjectContainer} display_object_container + * @param {Float32Array} [matrix=null] + * @return {Float32Array} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + matrix: Float32Array | null = null +): Float32Array => { + + const children = display_object_container.children; + if (!children.length) { + return $getBoundsArray(0, 0, 0, 0); + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(display_object_container); + + let tMatrix = matrix; + if (rawMatrix) { + tMatrix = matrix ? Matrix.multiply(matrix, rawMatrix) : rawMatrix; + } + + const no = Number.MAX_VALUE; + let xMin = no; + let xMax = -no; + let yMin = no; + let yMax = -no; + for (let idx = 0; idx < children.length; idx++) { + + const child = children[idx] as DisplayObject; + if (!child.visible) { + continue; + } + + let bounds: Float32Array | null = null; + switch (true) { + + case child.isContainerEnabled: + bounds = execute(child as DisplayObjectContainer, tMatrix); + break; + + case child.isShape: + bounds = shapeCalcBoundsMatrixUseCase(child as Shape, tMatrix); + break; + + case child.isText: + bounds = textFieldCalcBoundsMatrixUseCase(child as TextField, tMatrix); + break; + + case child.isVideo: + bounds = videoCalcBoundsMatrixUseCase(child as Video, tMatrix); + break; + + } + + if (!bounds) { + continue; + } + + xMin = Math.min(xMin, bounds[0]); + yMin = Math.min(yMin, bounds[1]); + xMax = Math.max(xMax, bounds[2]); + yMax = Math.max(yMax, bounds[3]); + + $poolBoundsArray(bounds); + } + + return $getBoundsArray(xMin, yMin, xMax, yMax); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts new file mode 100644 index 00000000..db2691df --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./DisplayObjectContainerRemoveChildAtUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemoveChildAtUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.addChild(new Shape()); + container.addChild(new Shape()); + container.addChild(new Shape()); + + const shape4 = container.addChild(new Shape()); + const shape5 = container.addChild(new Shape()); + expect(container.children.length).toBe(5); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape4.parent).toBe(container); + execute(container, 3); + expect(shape4.parent).toBe(null); + expect(container.children.length).toBe(4); + + expect(shape5.parent).toBe(container); + execute(container, container.numChildren - 1); + expect(container.children.length).toBe(3); + expect(shape5.parent).toBe(null); + + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts new file mode 100644 index 00000000..1b679807 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildAtUseCase.ts @@ -0,0 +1,31 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectContainerGetChildAtService } from "../service/DisplayObjectContainerGetChildAtService"; +import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainerRemoveChildUseCase"; + +/** + * @description 指定されたインデックスから子を削除する。 + * Remove a child from the specified index. + * + * @param {DisplayObjectContainer} display_object_container + * @param {number} index + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + index: number +): void => { + + const displayObject = displayObjectContainerGetChildAtService( + display_object_container, index + ); + + if (!displayObject) { + return ; + } + + displayObjectContainerRemoveChildUseCase( + display_object_container, displayObject + ); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts new file mode 100644 index 00000000..30d19b8d --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.test.ts @@ -0,0 +1,38 @@ +import { execute } from "./DisplayObjectContainerRemoveChildUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemoveChildUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape1.$added).toBe(true); + expect(shape1.parent?.instanceId).toBe(container.instanceId); + + expect(shape2.$added).toBe(true); + expect(shape2.parent?.instanceId).toBe(container.instanceId); + + expect(container.children.length).toBe(2); + execute(container, shape1); + + expect(container.children.length).toBe(1); + expect(shape1.$added).toBe(false); + expect(shape1.parent).toBe(null); + + execute(container, shape2); + + expect(container.children.length).toBe(0); + expect(shape2.$added).toBe(false); + expect(shape2.parent).toBe(null); + + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.ts new file mode 100644 index 00000000..aa7c6129 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildUseCase.ts @@ -0,0 +1,98 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Video } from "@next2d/media"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as displayObjectContainerUnAssignStageAndRootService } from "../service/DisplayObjectContainerUnAssignStageAndRootService"; +import { execute as displayObjectDispatchRemovedEventService } from "../../DisplayObject/service/DisplayObjectDispatchRemovedEventService"; +import { execute as displayObjectDispatchRemovedToStageEventService } from "../../DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService"; +import { execute as displayObjectContainerRemovedToStageService } from "../service/DisplayObjectContainerRemovedToStageService"; +import { + Event, + KeyboardEvent +} from "@next2d/events"; +import { + $rootMap, + $stageAssignedMap +} from "../../DisplayObjectUtil"; + +/** + * @description 指定の DisplayObject を DisplayObjectContainer から削除します。 + * Deletes the specified DisplayObject from the DisplayObjectContainer. + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute =

( + display_object_container: P, + display_object: D +): void => { + + const parent = display_object.parent; + if (parent + && (parent as unknown as DisplayObjectContainer).instanceId !== display_object_container.instanceId + ) { + return ; + } + + const children = display_object_container.children; + const depth = children.indexOf(display_object); + if (depth > -1) { + children.splice(depth, 1); + } + + // cache clear + display_object_container.$container = null; + + // remove all broadcast events + if (display_object.hasEventListener(Event.ENTER_FRAME)) { + display_object.removeAllEventListener(Event.ENTER_FRAME); + } + if (display_object.hasEventListener(KeyboardEvent.KEY_DOWN)) { + display_object.removeAllEventListener(KeyboardEvent.KEY_DOWN); + } + if (display_object.hasEventListener(KeyboardEvent.KEY_UP)) { + display_object.removeAllEventListener(KeyboardEvent.KEY_UP); + } + + // dispatch removed event + displayObjectDispatchRemovedEventService(display_object); + + // ステージに登録されている場合はステージからの削除イベントを実行 + if ($stageAssignedMap.has(display_object_container.instanceId)) { + + displayObjectDispatchRemovedToStageEventService(display_object); + + if (display_object.isContainerEnabled) { + displayObjectContainerRemovedToStageService( + display_object as unknown as DisplayObjectContainer + ); + } + } + + // remove root and stage + if ($stageAssignedMap.has(display_object_container.instanceId)) { + + $rootMap.delete(display_object); + $stageAssignedMap.delete(display_object.instanceId); + + if (display_object.isVideo) { + (display_object as unknown as Video).pause(); + } + + // コンテナであれば子孫の DisplayObject に対しても Stage と Root を解除 + if (display_object.isContainerEnabled) { + displayObjectContainerUnAssignStageAndRootService( + display_object as unknown as DisplayObjectContainer + ); + } + } + + // remove parent-child relationship + display_object.parent = null; + + // apply changes + displayObjectApplyChangesService(display_object_container); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts new file mode 100644 index 00000000..2fbddcbf --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./DisplayObjectContainerRemoveChildrenUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerRemoveChildrenUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + container.addChild(new Shape()); + container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + const shape4 = container.addChild(new Shape()); + container.addChild(new Shape()); + expect(container.children.length).toBe(5); + + container.changed = false; + expect(container.changed).toBe(false); + + expect(shape3.parent).toBe(container); + expect(shape4.parent).toBe(container); + execute(container, [2, 3]); + expect(shape3.parent).toBe(null); + expect(shape4.parent).toBe(null); + expect(container.children.length).toBe(3); + + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts new file mode 100644 index 00000000..a854b72b --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerRemoveChildrenUseCase.ts @@ -0,0 +1,37 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectContainerRemoveChildUseCase } from "./DisplayObjectContainerRemoveChildUseCase"; + +/** + * @description 配列で指定されたインデックスの子をコンテナから削除します + * Removes children of the index specified in the array from the container + * + * @param {DisplayObjectContainer} display_object_container + * @param {number[]} indexes + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + indexes: number[] +): void => { + + const children = display_object_container.children.slice(); + if (!children.length) { + return ; + } + + for (let idx = 0; idx < indexes.length; idx++) { + + const index = indexes[idx]; + + const child = children[index]; + if (!child) { + continue; + } + + displayObjectContainerRemoveChildUseCase( + display_object_container, child + ); + } +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.test.ts new file mode 100644 index 00000000..38b673c4 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.test.ts @@ -0,0 +1,57 @@ +import { execute } from "./DisplayObjectContainerSetChildIndexUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerSetChildIndexUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, shape1, 2); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape2); + expect(container.children[2]).toBe(shape1); + expect(container.children[3]).toBe(shape3); + expect(container.changed).toBe(true); + }); + + it("execute test case2", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, shape0, 3); + + expect(container.children[0]).toBe(shape1); + expect(container.children[1]).toBe(shape2); + expect(container.children[2]).toBe(shape3); + expect(container.children[3]).toBe(shape0); + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.ts new file mode 100644 index 00000000..51f2ae26 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSetChildIndexUseCase.ts @@ -0,0 +1,32 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; + +/** + * @description 指定されたインデックスに子を移動します + * Moves the child to the specified index + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object + * @param {number} index + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + display_object: D, + index: number +): void => { + + const currentIndex = display_object_container.getChildIndex(display_object); + if (currentIndex === -1 || currentIndex === index) { + return ; + } + + const children = display_object_container.children; + children.splice(currentIndex, 1); + children.splice(index, 0, display_object); + + displayObjectApplyChangesService(display_object_container); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.test.ts new file mode 100644 index 00000000..1b9cdbe2 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.test.ts @@ -0,0 +1,57 @@ +import { execute } from "./DisplayObjectContainerSwapChildrenAtUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerSwapChildrenAtUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, 1, 2); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape2); + expect(container.children[2]).toBe(shape1); + expect(container.children[3]).toBe(shape3); + expect(container.changed).toBe(true); + }); + + it("execute test case2", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, 0, 3); + + expect(container.children[0]).toBe(shape3); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape0); + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.ts new file mode 100644 index 00000000..7e2258bd --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenAtUseCase.ts @@ -0,0 +1,34 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; + +/** + * @description 子リスト内の指定されたインデックス位置に該当する 2 つの子オブジェクトの z 順序(重ね順)を入れ替えます。 + * Swaps the z-order (front-to-back order) of the child objects at + * the two specified index positions in the child list. + * @param {DisplayObjectContainer} display_object_container + * @param {number} index1 + * @param {number} index2 + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + index1: number, + index2: number +): void => { + + const children = display_object_container.children; + + const displayObject1 = children[index1]; + const displayObject2 = children[index2]; + + if (!displayObject1 || !displayObject2) { + return ; + } + + children[index1] = displayObject2; + children[index2] = displayObject1; + + displayObjectApplyChangesService(display_object_container); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.test.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.test.ts new file mode 100644 index 00000000..9f8b1de8 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.test.ts @@ -0,0 +1,57 @@ +import { execute } from "./DisplayObjectContainerSwapChildrenUseCase"; +import { Shape } from "../../Shape"; +import { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { describe, expect, it } from "vitest"; + +describe("DisplayObjectContainerSwapChildrenUseCase.js test", () => +{ + it("execute test case1", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, shape1, shape2); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape2); + expect(container.children[2]).toBe(shape1); + expect(container.children[3]).toBe(shape3); + expect(container.changed).toBe(true); + }); + + it("execute test case2", () => + { + const container = new DisplayObjectContainer(); + const shape0 = container.addChild(new Shape()); + const shape1 = container.addChild(new Shape()); + const shape2 = container.addChild(new Shape()); + const shape3 = container.addChild(new Shape()); + + expect(container.children[0]).toBe(shape0); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape3); + + container.changed = false; + expect(container.changed).toBe(false); + + execute(container, shape0, shape3); + + expect(container.children[0]).toBe(shape3); + expect(container.children[1]).toBe(shape1); + expect(container.children[2]).toBe(shape2); + expect(container.children[3]).toBe(shape0); + expect(container.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.ts b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.ts new file mode 100644 index 00000000..2fe02415 --- /dev/null +++ b/packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerSwapChildrenUseCase.ts @@ -0,0 +1,34 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; + +/** + * @description 指定された2つの子の表示順を入れ替えます + * Swaps the display order of the two specified children + * + * @param {DisplayObjectContainer} display_object_container + * @param {DisplayObject} display_object1 + * @param {DisplayObject} display_object2 + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object_container: C, + display_object1: D, + display_object2: D +): void => { + + const index1 = display_object_container.getChildIndex(display_object1); + const index2 = display_object_container.getChildIndex(display_object2); + + if (index1 === -1 || index2 === -1) { + return ; + } + + const children = display_object_container.children; + children[index1] = display_object2; + children[index2] = display_object1; + + displayObjectApplyChangesService(display_object_container); +}; \ No newline at end of file diff --git a/packages/display/src/DisplayObjectUtil.ts b/packages/display/src/DisplayObjectUtil.ts new file mode 100644 index 00000000..a8017ae3 --- /dev/null +++ b/packages/display/src/DisplayObjectUtil.ts @@ -0,0 +1,574 @@ +import type { IAjaxOption } from "./interface/IAjaxOption"; +import type { ISprite } from "./interface/ISprite"; +import type { IDisplayObject } from "./interface/IDisplayObject"; +import type { IURLRequestHeader } from "./interface/IURLRequestHeader"; +import type { LoaderInfo } from "./LoaderInfo"; +import type { Graphics } from "./Graphics"; +import type { MovieClip } from "./MovieClip"; +import { Point } from "@next2d/geom"; + +/** + * @description 現在のマウスの座標情報 + * Current mouse coordinate information + * + * @type {Point} + * @private + */ +export const $pointer: Point = new Point(); + +/** + * @type {number} + * @const + */ +export const $RENDERER_CONTAINER_TYPE: number = 0x00; + +/** + * @type {number} + * @const + */ +export const $RENDERER_SHAPE_TYPE: number = 0x01; + +/** + * @type {number} + * @const + */ +export const $RENDERER_TEXT_TYPE: number = 0x02; + +/** + * @type {number} + * @const + */ +export const $RENDERER_VIDEO_TYPE: number = 0x03; + +/** + * @typs {Float32Array} + * @const + */ +export const $MATRIX_ARRAY_IDENTITY = new Float32Array([1, 0, 0, 1, 0, 0]); + +/** + * @type {number} + */ +let instanceId: number = 0; + +/** + * @description インスタンスユニークIDを返却 + * Returns the instance unique ID + * + * @return {number} + * @method + * @public + */ +export const $getInstanceId = (): number => +{ + return instanceId++; +}; + +/** + * @description 使用済みになったArrayのプール配列 + * Pool array of used Array. + * + * @type {array[]} + * @const + * @private + */ +const $arrays: any[] = []; + +/** + * @description 使用済みになったBoundsのArrayのプール配列 + * Pool array of used Bounds Array. + * + * @type {Float32Array[]} + * @const + * @private + */ +const $boundsArrays: Float32Array[] = []; + +/** + * @description 使用済みになったFloat32Arrayをプール、サイズは6固定 + * Pool used Float32Array, size fixed at 6. + * + * @type {Float32Array[]} + * @const + * @private + */ +const $float32Array6: Float32Array[] = []; + +/** + * @description 使用済みになったFloat32Arrayをプール、サイズは8固定 + * Pool used Float32Array, size fixed at 8. + * + * @type {Float32Array[]} + * @const + * @private + */ +const $float32Array8: Float32Array[] = []; + +/** + * @description プールされたFloat32Arrayがあればプールから。なければ新規作成して返却、サイズは6固定 + * If there is a pooled Float32Array, it is taken from the pool. + * If not, create a new one and return it. Size is fixed at 6. + * + * @param {number} [f0=0] + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getFloat32Array6 = ( + f0: number = 0, f1: number = 0, + f2: number = 0, f3: number = 0, + f4: number = 0, f5: number = 0 +): Float32Array => { + + const array: Float32Array = $float32Array6.pop() || new Float32Array(6); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + + return array; +}; + +/** + * @description 使用済みになったFloat32Arrayをプール + * Pool used Float32Array. + * + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolFloat32Array6 = (array: Float32Array): void => +{ + $float32Array6.push(array); +}; + +/** + * @description プールされたFloat32Arrayがあればプールから。なければ新規作成して返却、サイズは8固定 + * If there is a pooled Float32Array, it is taken from the pool. + * If not, create a new one and return it. Size is fixed at 8. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @param {number} [f6=0] + * @param {number} [f7=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getFloat32Array8 = ( + f0: number = 1, f1: number = 1, + f2: number = 1, f3: number = 1, + f4: number = 0, f5: number = 0, + f6: number = 0, f7: number = 0 +): Float32Array => { + + const array: Float32Array = $float32Array8.pop() || new Float32Array(8); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + array[6] = f6; + array[7] = f7; + + return array; +}; + +/** + * @description 使用済みになったFloat32Arrayをプール + * Pool used Float32Array. + * + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolFloat32Array8 = (array: Float32Array): void => +{ + $float32Array8.push(array); +}; + +/** + * @type {Map} + * @private + */ +const $maps: Map[] = []; + +/** + * @description プールされたMapがあればプールから、なければ新規作成して返却 + * If there is a pooled Map, return it from the pool, + * otherwise create a new one and return it. + * + * @return {Map} + * @method + * @protected + */ +export const $getMap = (): Map => +{ + return $maps.pop() || new Map(); +}; + +/** + * @description 使用済みになったMapをプール + * Pool used Map. + * + * @param {Map} map + * @return {void} + * @method + * @protected + */ +export const $poolMap = (map: Map): void => +{ + map.clear(); + $maps.push(map); +}; + +/** + * @description プールされたArrayがあればプールから、なければ新規作成して返却 + * If there is a pooled Array, return it from the pool, + * otherwise create a new one and return it. + * + * @param {array} args + * @return {array} + * @method + * @protected + */ +export const $getArray = (...args: any[]): any[] => +{ + const array: any[] = $arrays.pop() || []; + if (args.length) { + array.push(...args); + } + return array; +}; + +/** + * @description 使用済みになったArrayをプール + * Pool used Array. + * + * @param {array} array + * @return {void} + * @method + * @protected + */ +export const $poolArray = (array: any[]): void => +{ + if (array.length) { + array.length = 0; + } + + $arrays.push(array); +}; + +/** + * @description プールされたArrayがあればプールから、なければ新規作成して返却 + * If there is a pooled Array, return it from the pool, + * otherwise create a new one and return it. + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @return {number[]} + * @method + * @protected + */ +export const $getBoundsArray = ( + x_min: number, y_min: number, + x_max: number, y_max: number +): Float32Array => { + + const array = $boundsArrays.length + ? $boundsArrays.pop() as unknown as Float32Array + : new Float32Array(4); + + array[0] = x_min; + array[1] = y_min; + array[2] = x_max; + array[3] = y_max; + + return array; +}; + +/** + * @description 使用済みになったBoundsのArrayをプール + * Pool used Bounds Array. + * + * @param {Float32Array} array + * @method + * @protected + */ +export const $poolBoundsArray = (array: Float32Array): void => +{ + $boundsArrays.push(array); +}; + +/** + * @param {object} option + * @return {void} + * @method + * @public + */ +export const $ajax = (option: IAjaxOption): void => +{ + // get or post + let postData: string | null = null; + switch (option.method.toUpperCase()) { + + case "GET": + if (option.data) { + const urls = option.url.split("?"); + + urls[1] = urls.length === 1 + ? option.data.toString() + : `${urls[1]}&${option.data.toString()}`; + + option.url = urls.join("?"); + } + break; + + case "PUT": + case "POST": + if (option.data) { + postData = option.data.toString(); + } + break; + + default: + break; + + } + + // start + const xmlHttpRequest = new XMLHttpRequest(); + + // init + xmlHttpRequest.open(option.method, option.url, true); + + // set mimeType + xmlHttpRequest.responseType = option.format; + + // use cookie + xmlHttpRequest.withCredentials = option.withCredentials; + + // add event + if (option.event) { + const keys: string[] = Object.keys(option.event); + for (let idx = 0; idx < keys.length; ++idx) { + + const name: string = keys[idx]; + + // @ts-ignore + xmlHttpRequest.addEventListener(name, option.event[name]); + } + } + + // set request header + for (let idx: number = 0; idx < option.headers.length; ++idx) { + const header = option.headers[idx]; + if (!header) { + continue; + } + xmlHttpRequest.setRequestHeader(header.name, header.value); + } + + xmlHttpRequest.send(postData); +}; + +/** + * @description ヘッダー文字列を配列に変換 + * Convert header string to array + * + * @param {string} header + * @return {array} + * @method + * @public + */ +export const $headerStringToArray = (header: string): IURLRequestHeader[] => +{ + const results = $getArray(); + if (header) { + + const headers = header.trim().split("\n"); + for (let idx = 0; idx < headers.length; ++idx) { + + const values = headers[idx].split(":"); + + results.push({ + "name": values[0].trim(), + "value": values[1].trim() + }); + + } + + } + return results; +}; + +/** + * @description 親子関係のマップデータ + * Parent-child relationship map data + * + * @type {Map} + * @protected + */ +export const $loaderInfoMap: WeakMap, LoaderInfo> = new WeakMap(); + +/** + * @description 子孫のrootへのマップデータ + * Map data to the root of descendants + * + * @type {Map} + * @protected + */ +export const $rootMap: WeakMap, IDisplayObject> = new WeakMap(); + +/** + * @description Stageに追加したかどうかのマップデータ + * Map data of whether it was added to the Stage + * + * @type {Map} + * @protected + */ +export const $stageAssignedMap: Set> = new Set(); + +/** + * @description GraphicsとDisplayObjectのマップデータ + * Map data of Graphics and DisplayObject + * + * @type {Map} + * @protected + */ +export const $graphicMap: WeakMap> = new WeakMap(); + +const canvas = document.createElement("canvas"); +canvas.width = canvas.height = 1; + +/** + * @type {CanvasRenderingContext2D} + * @protected + */ +export const $colorContext: CanvasRenderingContext2D = canvas.getContext("2d") as CanvasRenderingContext2D; + +/** + * @description カラー文字列を数値に変換 + * Convert color string to number + * @param {string} value + * @return {number} + * @method + * @protected + */ +export const $convertColorStringToNumber = (value: string): number => +{ + if (!$colorContext) { + return 0; + } + + $colorContext.fillStyle = value; + return +`0x${$colorContext.fillStyle.slice(1)}`; +}; + +/** + * @description 値が最小値と最大値の間に収まるように調整します。 + * Adjust the value so that it falls between the minimum and maximum values. + * + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @protected + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +/** + * @description フレームアクションの発生したMovieClipを格納する配列 + * Array to store MovieClip where frame actions occurred + * + * @type {array} + * @protected + */ +export const $actions: MovieClip[] = []; + +/** + * @description フレームで実行するサウンドを格納する配列 + * Array to store sounds to be played on the frame + * + * @type {array} + * @protected + */ +export const $sounds: MovieClip[] = []; + +/** + * @description グローバル変数を格納するMap + * Map to store global variables + * + * @type {Map} + * @protected + */ +export const $variables: Map = new Map(); + +/** + * @description ドラッグ中のDisplayObject + * DisplayObject being dragged + * + * @type {ISprite} + * @private + */ +let $draggingDisplayObject: ISprite | null = null; + +/** + * @description ドラッグ中のDisplayObjectを設定 + * Set the DisplayObject being dragged + * + * @param {ISprite} display_object + * @return {void} + * @method + * @protected + */ +export const $setDraggingDisplayObject = (display_object: ISprite | null): void => +{ + $draggingDisplayObject = display_object; +}; + +/** + * @description ドラッグ中のDisplayObjectを返却 + * Returns the DisplayObject being dragged + * + * @return {ISprite} + * @method + * @protected + */ +export const $getDraggingDisplayObject = (): ISprite | null => +{ + return $draggingDisplayObject; +}; \ No newline at end of file diff --git a/packages/display/src/FrameLabel.test.ts b/packages/display/src/FrameLabel.test.ts new file mode 100644 index 00000000..77a7e247 --- /dev/null +++ b/packages/display/src/FrameLabel.test.ts @@ -0,0 +1,12 @@ +import { FrameLabel } from "./FrameLabel"; +import { describe, expect, it } from "vitest"; + +describe("FrameLabel.js property test", function() +{ + it("property test", function() + { + const frameLabel = new FrameLabel("test", 10); + expect(frameLabel.name).toBe("test"); + expect(frameLabel.frame).toBe(10); + }); +}); diff --git a/packages/display/src/FrameLabel.ts b/packages/display/src/FrameLabel.ts index 4400db43..73ac63fe 100644 --- a/packages/display/src/FrameLabel.ts +++ b/packages/display/src/FrameLabel.ts @@ -1,25 +1,16 @@ import { EventDispatcher } from "@next2d/events"; /** - * FrameLabel オブジェクトには、フレーム番号および対応するラベル名を指定するプロパティがあります。 - * MovieClip クラスには、currentLabels プロパティがあります。 - * これは、現在のシーンの FrameLabel オブジェクトの配列です。 - * MovieClip インスタンスがシーンを使用していない場合、配列には MovieClip インスタンス全体のすべてのフレームラベルが含まれます。 + * @description FrameLabel オブジェクトには、フレーム番号および対応するラベル名を指定するプロパティがあります。 + * MovieClip クラスには、currentLabels プロパティがあります。 + * これは、現在のシーンの FrameLabel オブジェクトの配列です。 + * MovieClip インスタンスがシーンを使用していない場合、配列には MovieClip インスタンス全体のすべてのフレームラベルが含まれます。 * - * The FrameLabel object contains properties that specify a frame number and the corresponding label name. - * The MovieClip class includes a currentLabels property, - * which is an Array of FrameLabel objects for the current scene. - * If the MovieClip instance does not use scenes, - * the Array includes all frame labels from the entire MovieClip instance. - * - * @example Example usage of FrameLabel. - * // static BlendMode - * const {FrameLabel} = next2d.display; - * const frameLabel = new FrameLabel(); - * frameLabel.addEventListener(Event.FRAME_LABEL, (event) => - * { - * // more... - * } + * The FrameLabel object contains properties that specify a frame number and the corresponding label name. + * The MovieClip class includes a currentLabels property, + * which is an Array of FrameLabel objects for the current scene. + * If the MovieClip instance does not use scenes, + * the Array includes all frame labels from the entire MovieClip instance. * * @class * @memberOf next2d.display @@ -27,114 +18,38 @@ import { EventDispatcher } from "@next2d/events"; */ export class FrameLabel extends EventDispatcher { - private readonly _$name: string; - private readonly _$frame: number; - - /** - * @param {string} name - * @param {number} frame - * - * @constructor - * @public - */ - constructor (name: string, frame: number) - { - super(); - - /** - * @type {string} - * @private - */ - this._$name = `${name}`; - - /** - * @type {number} - * @private - */ - this._$frame = frame | 0; - } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class FrameLabel] - * @method - * @static - */ - static toString (): string - { - return "[class FrameLabel]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.FrameLabel - * @const - * @static - */ - static get namespace (): string - { - return "next2d.display.FrameLabel"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description ラベルの名前。 + * The name of the label. * - * @return {string} - * @default [object FrameLabel] - * @method + * @type {string} + * @readonly * @public */ - toString (): string - { - return "[object FrameLabel]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.FrameLabel - * @const - * @public - */ - get namespace (): string - { - return "next2d.display.FrameLabel"; - } + public readonly name: string; /** * @description ラベルを含むフレームの番号。 * The frame number containing the label. * - * @return {number} - * @method + * @type {number} * @readonly * @public */ - get frame (): number - { - return this._$frame; - } + public readonly frame: number; /** - * @description ラベルの名前。 - * The name of the label. + * @param {string} name + * @param {number} frame * - * @return {string} - * @method - * @readonly + * @constructor * @public */ - get name (): string + constructor (name: string, frame: number) { - return this._$name; + super(); + + this.name = `${name}`; + this.frame = frame | 0; } -} +} \ No newline at end of file diff --git a/packages/display/src/Graphics.ts b/packages/display/src/Graphics.ts index 58e69c3d..3cec9508 100644 --- a/packages/display/src/Graphics.ts +++ b/packages/display/src/Graphics.ts @@ -1,447 +1,290 @@ +import type { ICapsStyle } from "./interface/ICapsStyle"; +import type { IJointStyle } from "./interface/IJointStyle"; +import type { IGradientType } from "./interface/IGradientType"; +import type { ISpreadMethod } from "./interface/ISpreadMethod"; +import type { IInterpolationMethod } from "./interface/IInterpolationMethod"; +import type { BitmapData } from "./BitmapData"; +import type { Matrix } from "@next2d/geom"; import { GraphicsBitmapFill } from "./GraphicsBitmapFill"; import { GraphicsGradientFill } from "./GraphicsGradientFill"; -import type { BitmapData } from "./BitmapData"; -import type { Player } from "@next2d/core"; -import type { Matrix, Rectangle } from "@next2d/geom"; -import type { - GraphicsParentImpl, - CapsStyleImpl, - JointStyleImpl, - FilterArrayImpl, - BlendModeImpl, - BoundsImpl, - DisplayObjectImpl, - AttachmentImpl, - PlayerHitObjectImpl, - ColorStopImpl, - SpreadMethodImpl, - GradientTypeImpl, - InterpolationMethodImpl, - CachePositionImpl, - ShapeModeImpl, - GridImpl -} from "@next2d/interface"; -import type { - CanvasToWebGLContext, - FrameBufferManager, - CanvasGradientToWebGL -} from "@next2d/webgl"; -import { $currentPlayer } from "@next2d/util"; +import { execute as graphicsCalcBoundsUseCase } from "./Graphics/usecase/GraphicsCalcBoundsUseCase"; +import { execute as graphicsMargePathService } from "./Graphics/service/GraphicsMargePathService"; +import { execute as graphicsDrawEllipseService } from "./Graphics/service/GraphicsDrawEllipseService"; +import { execute as graphicsDrawRectService } from "./Graphics/service/GraphicsDrawRectService"; +import { execute as graphicsDrawRoundRectService } from "./Graphics/service/GraphicsDrawRoundRectService"; +import { execute as graphicsToNumberArrayService } from "./Graphics/service/GraphicsToNumberArrayService"; import { - $cacheStore, - $doUpdated, - $Math, - $Number, $getArray, $poolArray, - $toColorInt, - $intToRGBA, $clamp, - $boundsMatrix, - $poolBoundsObject, - $Infinity, - $getFloat32Array6, - $multiplicationMatrix, - $poolFloat32Array6, - $getBoundsObject, - $Float32Array, - $getFloat32Array4, - $getInt32Array4, - $linearGradientXY -} from "@next2d/share"; + $convertColorStringToNumber +} from "./DisplayObjectUtil"; /** - * Graphics クラスには、ベクターシェイプの作成に使用できる一連のメソッドがあります。 - * 描画をサポートする表示オブジェクトには、Sprite および Shape オブジェクトがあります。 - * これらの各クラスには、Graphics オブジェクトである graphics プロパティがあります。 - * 以下は、簡単に使用できるように用意されているヘルパー関数の一例です。 - * drawRect()、drawRoundRect()、drawCircle()、および drawEllipse()。 + * @description Graphics クラスには、ベクターシェイプの作成に使用できる一連のメソッドがあります。 + * 描画をサポートする表示オブジェクトには、Sprite および Shape オブジェクトがあります。 + * これらの各クラスには、Graphics オブジェクトである graphics プロパティがあります。 + * 以下は、簡単に使用できるように用意されているヘルパー関数の一例です。 + * drawRect()、drawRoundRect()、drawCircle()、および drawEllipse()。 * - * The Graphics class contains a set of methods that you can use to create a vector shape. - * Display objects that support drawing include Sprite and Shape objects. - * Each of these classes includes a graphics property that is a Graphics object. - * The following are among those helper functions provided for ease of use: - * drawRect(), drawRoundRect(), drawCircle(), and drawEllipse(). + * The Graphics class contains a set of methods that you can use to create a vector shape. + * Display objects that support drawing include Sprite and Shape objects. + * Each of these classes includes a graphics property that is a Graphics object. + * The following are among those helper functions provided for ease of use: + * drawRect(), drawRoundRect(), drawCircle(), and drawEllipse(). * * @class * @memberOf next2d.display */ export class Graphics { - private readonly _$displayObject: GraphicsParentImpl | null; - public _$maxAlpha: number; - private _$pointerX: number; - private _$pointerY: number; - public _$canDraw: boolean; - private _$fillType: number; - private _$fillGradient: GraphicsGradientFill | null; - private _$fillBitmap: GraphicsBitmapFill | null; - private _$fillStyleR: number; - private _$fillStyleG: number; - private _$fillStyleB: number; - private _$fillStyleA: number; - private _$doFill: boolean; - private _$lineType: number; - private _$lineGradient: GraphicsGradientFill | null; - private _$caps: CapsStyleImpl; - private _$joints: JointStyleImpl; - private _$miterLimit: number; - private _$lineWidth: number; - private _$lineStyleR: number; - private _$lineStyleG: number; - private _$lineStyleB: number; - private _$lineStyleA: number; - private _$doLine: boolean; - public _$xMin: number; - public _$xMax: number; - public _$yMin: number; - public _$yMax: number; - public _$buffer: Float32Array | null; - public _$recode: any[] | null; - private _$fills: any[] | null; - private _$lines: any[] | null; - private _$uniqueKey: string; - private _$cacheKeys: string[]; - private readonly _$cacheParams: number[]; - public _$bitmapId: number; - public _$mode: ShapeModeImpl; - public _$posted: boolean; - /** - * @param {DisplayObject} src + * @description グラフィックの確認フラグ + * Graphic confirmation flag * - * @constructor + * @type {boolean} + * @default false * @public */ - constructor (src: GraphicsParentImpl | null = null) - { - /** - * @type {DisplayObject} - * @default null - * @private - */ - this._$displayObject = src; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxAlpha = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$pointerX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$pointerY = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$canDraw = false; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillType = 0; - - /** - * @type {GraphicsGradientFill} - * @default null - * @private - */ - this._$fillGradient = null; - - /** - * @type {GraphicsGradientFill} - * @default null - * @private - */ - this._$fillBitmap = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillStyleR = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillStyleG = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillStyleB = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillStyleA = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$doFill = false; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$lineType = 0; - - /** - * @type {GraphicsGradientFill} - * @default 0 - * @private - */ - this._$lineGradient = null; - - /** - * @type {string} - * @default none - * @private - */ - this._$caps = "none"; - - /** - * @type {string} - * @default round - * @private - */ - this._$joints = "round"; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$miterLimit = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$lineWidth = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$lineStyleR = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$lineStyleG = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$lineStyleB = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$lineStyleA = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$doLine = false; - - /** - * @type {number} - * @default Number.MAX_VALUE - * @private - */ - this._$xMin = $Number.MAX_VALUE; - - /** - * @type {number} - * @default -Number.MAX_VALUE - * @private - */ - this._$xMax = -$Number.MAX_VALUE; - - /** - * @type {number} - * @default Number.MAX_VALUE - * @private - */ - this._$yMin = $Number.MAX_VALUE; - - /** - * @type {number} - * @default -Number.MAX_VALUE - * @private - */ - this._$yMax = -$Number.MAX_VALUE; - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$buffer = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$recode = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$fills = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$lines = null; + public isConfirmed: boolean; - /** - * @type {string} - * @default "" - * @private - */ - this._$uniqueKey = ""; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bitmapId = 0; - - /** - * @type {string} - * @default "shape" - * @private - */ - this._$mode = "shape"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$posted = false; - } + /** + * @description グラフィックの最小x座標 + * Minimum x coordinate of graphic + * + * @type {number} + * @default Number.MAX_VALUE + * @public + */ + public xMin: number; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description グラフィックの最小y座標 + * Minimum y coordinate of graphic * - * @return {string} - * @default [class Graphics] - * @method - * @static + * @type {number} + * @default Number.MAX_VALUE + * @public */ - static toString (): string - { - return "[class Graphics]"; - } + public yMin: number; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description グラフィックの最大x座標 + * Maximum x coordinate of graphic * - * @return {string} - * @default next2d.display.Bitmap - * @const - * @static + * @type {number} + * @default -Number.MAX_VALUE + * @public */ - static get namespace (): string - { - return "next2d.display.Graphics"; - } + public xMax: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description グラフィックの最大y座標 + * Maximum y coordinate of graphic * - * @return {string} - * @default [object Graphics] - * @method + * @type {number} + * @default -Number.MAX_VALUE * @public */ - toString (): string - { - return "[object Graphics]"; - } + public yMax: number; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description グラフィックのコマンド配列 + * Graphic command array * - * @return {string} - * @default next2d.display.Graphics - * @const + * @type {array} + * @default null + * @public + */ + public $recodes: any[] | null; + + /** + * @type {Float32Array} + * @default null + * @private + */ + private _$buffer: Float32Array | null; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$maxAlpha: number; + + /** + * @type {boolean} + * @default false + * @private + */ + private _$isBeginning: boolean; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$positionX: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$positionY: number; + + /** + * @type {boolean} + * @default false + * @private + */ + private _$hasFillEnabled: boolean; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$fillType: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$fillColor: number; + + /** + * @type {GraphicsGradientFill} + * @default null + * @private + */ + private _$fillGradient: GraphicsGradientFill | null; + + /** + * @type {GraphicsBitmapFill} + * @default null + * @private + */ + private _$fillBitmap: GraphicsBitmapFill | null; + + /** + * @type {array} + * @default null + * @private + */ + private _$fills: any[] | null; + + /** + * @type {boolean} + * @default false + * @private + */ + private _$hasLineEnabled: boolean; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$lineType: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$lineColor: number; + + /** + * @type {GraphicsGradientFill} + * @default null + * @private + */ + private _$lineGradient: GraphicsGradientFill | null; + + /** + * @type {string} + * @default "round" + * @private + */ + private _$joints: IJointStyle; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$miterLimit: number; + + /** + * @type {string} + * @default "none" + * @private + */ + private _$caps: ICapsStyle; + + /** + * @type {number} + * @default 1 + * @private + */ + private _$lineWidth: number; + + /** + * @type {array} + * @default null + * @private + */ + private _$lines: any[] | null; + + /** + * @constructor * @public */ - get namespace (): string + constructor () { - return "next2d.display.Graphics"; + this.isConfirmed = false; + this.xMin = Number.MAX_VALUE; + this.yMin = Number.MAX_VALUE; + this.xMax = -Number.MAX_VALUE; + this.yMax = -Number.MAX_VALUE; + + // private + this.$recodes = null; + this._$buffer = null; + this._$positionX = 0; + this._$positionY = 0; + this._$maxAlpha = 0; + this._$isBeginning = false; + + // fill + this._$fills = null; + this._$hasFillEnabled = false; + this._$fillType = 0; + this._$fillGradient = null; + this._$fillBitmap = null; + this._$fillColor = 0; + + // stroke + this._$caps = "none"; + this._$lineWidth = 1; + this._$lines = null; + this._$hasLineEnabled = false; + this._$lineType = 0; + this._$lineGradient = null; + this._$joints = "round"; + this._$miterLimit = 0; + this._$lineColor = 0; } /** + * @description 描画コマンド、MoveToの識別番号 + * Drawing command, MoveTo identification number + * * @return {number} - * @default 0 * @const * @static - * @private */ static get MOVE_TO (): number { @@ -449,11 +292,12 @@ export class Graphics } /** + * @description 描画コマンド、2次ベジェ曲線の識別番号 + * Drawing command, 2nd Bezier curve identification number + * * @return {number} - * @default 1 * @const * @static - * @private */ static get CURVE_TO (): number { @@ -461,11 +305,12 @@ export class Graphics } /** + * @description 描画コマンド、直線の識別番号 + * Drawing command, straight line identification number + * * @return {number} - * @default 2 * @const * @static - * @private */ static get LINE_TO (): number { @@ -473,11 +318,12 @@ export class Graphics } /** + * @description 描画コマンド、3次ベジェ曲線の識別番号 + * Drawing command, 3rd Bezier curve identification number + * * @return {number} - * @default 3 * @const * @static - * @private */ static get CUBIC (): number { @@ -485,11 +331,12 @@ export class Graphics } /** + * @description 描画コマンド、円弧の識別番号 + * Drawing command, arc identification number + * * @return {number} - * @default 4 * @const * @static - * @private */ static get ARC (): number { @@ -497,11 +344,12 @@ export class Graphics } /** + * @description 描画コマンド、塗りの識別番号 + * Drawing command, fill identification number + * * @return {number} - * @default 5 * @const * @static - * @private */ static get FILL_STYLE (): number { @@ -509,11 +357,12 @@ export class Graphics } /** + * @description 描画コマンド、線の識別番号 + * Drawing command, line identification number + * * @return {number} - * @default 6 * @const * @static - * @private */ static get STROKE_STYLE (): number { @@ -521,11 +370,12 @@ export class Graphics } /** + * @description 描画コマンド、塗りの終了の識別番号 + * Drawing command, fill end identification number + * * @return {number} - * @default 7 * @const * @static - * @private */ static get END_FILL (): number { @@ -533,11 +383,12 @@ export class Graphics } /** + * @description 描画コマンド、線の終了の識別番号 + * Drawing command, line end identification number + * * @return {number} - * @default 8 * @const * @static - * @private */ static get END_STROKE (): number { @@ -545,11 +396,12 @@ export class Graphics } /** + * @description 描画コマンド、描画開始の識別番号 + * Drawing command, drawing start identification number + * * @return {number} - * @default 9 * @const * @static - * @private */ static get BEGIN_PATH (): number { @@ -557,11 +409,12 @@ export class Graphics } /** + * @description 描画コマンド、塗りのグラデーション開始の識別番号 + * Drawing command, gradient fill start identification number + * * @return {number} - * @default 10 * @const * @static - * @private */ static get GRADIENT_FILL (): number { @@ -569,11 +422,12 @@ export class Graphics } /** + * @description 描画コマンド、線のグラデーション開始の識別番号 + * Drawing command, gradient line start identification number + * * @return {number} - * @default 11 * @const * @static - * @private */ static get GRADIENT_STROKE (): number { @@ -581,11 +435,12 @@ export class Graphics } /** + * @description 描画コマンド、描画結合の識別番号 + * Drawing command, drawing join identification number + * * @return {number} - * @default 12 * @const * @static - * @private */ static get CLOSE_PATH (): number { @@ -593,11 +448,12 @@ export class Graphics } /** + * @description 描画コマンド、Bitmapの塗りの識別番号 + * Drawing command, Bitmap fill identification number + * * @return {number} - * @default 13 * @const * @static - * @private */ static get BITMAP_FILL (): number { @@ -605,17 +461,37 @@ export class Graphics } /** + * @description 描画コマンド、Bitmapの線の識別番号 + * Drawing command, Bitmap line identification number + * * @return {number} - * @default 14 * @const * @static - * @private */ static get BITMAP_STROKE (): number { return 14; } + /** + * @description 描画コマンドが実行可能かを返却 + * Returns whether the drawing command can be executed + * + * @type {boolean} + * @readonly + * @public + */ + get isDrawable (): boolean + { + if (!this._$isBeginning || !this._$maxAlpha) { + return false; + } + + const width = Math.abs(this.xMax - this.xMin); + const height = Math.abs(this.yMax - this.yMin); + return width > 0 && height > 0; + } + /** * @description 描画領域をビットマップイメージで塗りつぶします。 * Fills a drawing area with a bitmap image. @@ -636,7 +512,7 @@ export class Graphics ): Graphics { // end fill - if (this._$doFill) { + if (this._$hasFillEnabled) { this.endFill(); } @@ -645,9 +521,9 @@ export class Graphics } // start - this._$maxAlpha = 1; - this._$doFill = true; - this._$canDraw = true; + this._$maxAlpha = 1; + this._$hasFillEnabled = true; + this._$isBeginning = true; // beginPath this._$fills.push(Graphics.BEGIN_PATH); @@ -678,7 +554,7 @@ export class Graphics ): Graphics { // end fill - if (this._$doFill) { + if (this._$hasFillEnabled) { this.endFill(); } @@ -687,25 +563,28 @@ export class Graphics } // valid - color = $clamp($toColorInt(color), 0, 0xffffff, 0); + if (typeof color === "string") { + color = $convertColorStringToNumber(color); + } + + color = $clamp(color, 0, 0xffffff, 0); alpha = $clamp(alpha, 0, 1, 1); // setup - this._$maxAlpha = $Math.max(this._$maxAlpha, alpha); - this._$doFill = true; - this._$canDraw = true; + this._$maxAlpha = Math.max(this._$maxAlpha, alpha); + this._$hasFillEnabled = true; + this._$isBeginning = true; // beginPath this._$fills.push(Graphics.BEGIN_PATH); - // add Fill Style - const object = $intToRGBA(color, alpha); + this._$fillType = Graphics.FILL_STYLE; - this._$fillType = Graphics.FILL_STYLE; - this._$fillStyleR = object.R; - this._$fillStyleG = object.G; - this._$fillStyleB = object.B; - this._$fillStyleA = object.A; + // Color Int 32bit(RGBA) + const red = color >>> 16 & 0xff; + const green = color >>> 8 & 0xff; + const blue = color & 0xff; + this._$fillColor = red << 24 | green << 16 | blue << 8 | alpha * 255; return this; } @@ -729,17 +608,17 @@ export class Graphics * @public */ beginGradientFill ( - type: GradientTypeImpl, + type: IGradientType, colors: number[] | string[], alphas: number[], ratios: number[], matrix: Matrix | null = null, - spread_method: SpreadMethodImpl = "pad", - interpolation_method: InterpolationMethodImpl = "rgb", + spread_method: ISpreadMethod = "pad", + interpolation_method: IInterpolationMethod = "rgb", focal_point_ratio: number = 0 ): Graphics { - if (this._$doFill) { + if (this._$hasFillEnabled) { this.endFill(); } @@ -749,10 +628,10 @@ export class Graphics // setup for (let idx: number = 0; idx < alphas.length; ++idx) { - this._$maxAlpha = $Math.max(this._$maxAlpha, alphas[idx]); + this._$maxAlpha = Math.max(this._$maxAlpha, alphas[idx]); } - this._$doFill = true; - this._$canDraw = true; + this._$hasFillEnabled = true; + this._$isBeginning = true; // beginPath this._$fills.push(Graphics.BEGIN_PATH); @@ -780,46 +659,37 @@ export class Graphics clear (): Graphics { // param clear - this._$maxAlpha = 0; - this._$pointerX = 0; - this._$pointerY = 0; - this._$canDraw = false; - this._$bitmapId = 0; - this._$mode = "shape"; - this._$posted = false; + this._$maxAlpha = 0; + this._$isBeginning = false; + this._$positionX = 0; + this._$positionY = 0; // fill - this._$fillType = 0; - this._$fillGradient = null; - this._$fillBitmap = null; - this._$fillStyleR = 0; - this._$fillStyleG = 0; - this._$fillStyleB = 0; - this._$fillStyleA = 0; - this._$doFill = false; + this._$fillType = 0; + this._$fillGradient = null; + this._$fillBitmap = null; + this._$fillColor = 0; + this._$hasFillEnabled = false; // stroke - this._$lineType = 0; - this._$lineGradient = null; - this._$caps = "none"; - this._$joints = "round"; - this._$miterLimit = 0; - this._$lineWidth = 1; - this._$lineStyleR = 0; - this._$lineStyleG = 0; - this._$lineStyleB = 0; - this._$lineStyleA = 0; - this._$doLine = false; + this._$caps = "none"; + this._$lineWidth = 1; + this._$lineType = 0; + this._$lineGradient = null; + this._$joints = "round"; + this._$miterLimit = 0; + this._$lineColor = 0; + this._$hasLineEnabled = false; // bounds size - this._$xMin = $Number.MAX_VALUE; - this._$xMax = -$Number.MAX_VALUE; - this._$yMin = $Number.MAX_VALUE; - this._$yMax = -$Number.MAX_VALUE; + this.xMin = Number.MAX_VALUE; + this.xMax = -Number.MAX_VALUE; + this.yMin = Number.MAX_VALUE; + this.yMax = -Number.MAX_VALUE; // init array - if (this._$recode) { - $poolArray(this._$recode); + if (this.$recodes) { + $poolArray(this.$recodes); } if (this._$fills) { $poolArray(this._$fills); @@ -828,18 +698,10 @@ export class Graphics $poolArray(this._$lines); } - this._$buffer = null; - this._$recode = null; - this._$fills = null; - this._$lines = null; - - // cache clear - this._$cacheKeys.length = 0; - this._$uniqueKey = ""; - this._$cacheParams.fill(0); - - // restart - this._$restart(); + this._$buffer = null; + this.$recodes = null; + this._$fills = null; + this._$lines = null; return this; } @@ -879,40 +741,34 @@ export class Graphics } // fill - this._$doFill = graphics._$doFill; - this._$fillType = graphics._$fillType; - this._$fillStyleR = graphics._$fillStyleR; - this._$fillStyleG = graphics._$fillStyleG; - this._$fillStyleB = graphics._$fillStyleB; - this._$fillStyleA = graphics._$fillStyleA; + this._$hasFillEnabled = graphics._$hasFillEnabled; + this._$fillType = graphics._$fillType; + this._$fillColor = graphics._$fillColor; if (graphics._$lineGradient) { this._$lineGradient = graphics._$lineGradient.clone(); } // stroke - this._$doLine = graphics._$doLine; - this._$lineType = graphics._$lineType; - this._$caps = graphics._$caps; - this._$joints = graphics._$joints; - this._$miterLimit = graphics._$miterLimit; - this._$lineWidth = graphics._$lineWidth; - this._$lineStyleR = graphics._$lineStyleR; - this._$lineStyleG = graphics._$lineStyleG; - this._$lineStyleB = graphics._$lineStyleB; - this._$lineStyleA = graphics._$lineStyleA; + this._$hasLineEnabled = graphics._$hasLineEnabled; + this._$lineType = graphics._$lineType; + this._$caps = graphics._$caps; + this._$joints = graphics._$joints; + this._$miterLimit = graphics._$miterLimit; + this._$lineWidth = graphics._$lineWidth; + this._$lineColor = graphics._$lineColor; // bounds - this._$xMin = graphics._$xMin; - this._$xMax = graphics._$xMax; - this._$yMin = graphics._$yMin; - this._$yMax = graphics._$yMax; + this.xMin = graphics.xMin; + this.xMax = graphics.xMax; + this.yMin = graphics.yMin; + this.yMax = graphics.yMax; // params - this._$maxAlpha = graphics._$maxAlpha; - this._$pointerX = graphics._$pointerX; - this._$pointerY = graphics._$pointerY; - this._$canDraw = graphics._$canDraw; + this._$maxAlpha = graphics._$maxAlpha; + this._$positionX = graphics._$positionX; + this._$positionY = graphics._$positionY; + this._$isBeginning = graphics._$isBeginning; // path params if (graphics._$fills) { @@ -921,9 +777,11 @@ export class Graphics if (graphics._$lines) { this._$lines = graphics._$lines.slice(0); } - if (graphics._$recode) { - this._$recode = graphics._$recode.slice(0); + if (graphics.$recodes) { + this.$recodes = graphics.$recodes.slice(0); } + + this._$buffer = null; } /** @@ -949,7 +807,9 @@ export class Graphics anchor_x = +anchor_x || 0; anchor_y = +anchor_y || 0; - if (this._$pointerX === anchor_x && this._$pointerY === anchor_y) { + if (this._$positionX === anchor_x + && this._$positionY === anchor_y + ) { return this; } @@ -958,23 +818,29 @@ export class Graphics control_x2 = +control_x2 || 0; control_y2 = +control_y2 || 0; - // set bounds - this._$setBounds(control_x1, control_y1); - this._$setBounds(control_x2, control_y2); - this._$setBounds(anchor_x, anchor_y); + // calc bounds + graphicsCalcBoundsUseCase( + this, this._$hasLineEnabled, + this._$positionX, this._$positionY, + this._$lineWidth, this._$caps, + control_x1, control_y1, + control_x2, control_y2, + anchor_x, anchor_y + ); - this._$margePath($getArray( + // marge path + graphicsMargePathService( + this, + this._$hasFillEnabled, this._$hasLineEnabled, + this._$fills, this._$lines, Graphics.CUBIC, control_x1, control_y1, control_x2, control_y2, anchor_x, anchor_y - )); - - this._$pointerX = anchor_x; - this._$pointerY = anchor_y; + ); - // restart - this._$restart(); + this._$positionX = anchor_x; + this._$positionY = anchor_y; return this; } @@ -1002,27 +868,36 @@ export class Graphics anchor_x = +anchor_x || 0; anchor_y = +anchor_y || 0; - if (this._$pointerX === anchor_x && this._$pointerY === anchor_y) { + if (this._$positionX === anchor_x + && this._$positionY === anchor_y + ) { return this; } control_x = +control_x || 0; control_y = +control_y || 0; - this._$setBounds(control_x, control_y); - this._$setBounds(anchor_x, anchor_y); + // calc bounds + graphicsCalcBoundsUseCase( + this, this._$hasLineEnabled, + this._$positionX, this._$positionY, + this._$lineWidth, this._$caps, + control_x, control_y, + anchor_x, anchor_y + ); - this._$margePath($getArray( + // marge path + graphicsMargePathService( + this, + this._$hasFillEnabled, this._$hasLineEnabled, + this._$fills, this._$lines, Graphics.CURVE_TO, control_x, control_y, anchor_x, anchor_y - )); - - this._$pointerX = anchor_x; - this._$pointerY = anchor_y; + ); - // restart - this._$restart(); + this._$positionX = anchor_x; + this._$positionY = anchor_y; return this; } @@ -1043,21 +918,28 @@ export class Graphics x = +x || 0; y = +y || 0; radius = +radius || 0; - radius = $Math.round(radius); - - this._$setBounds(x - radius, y - radius); - this._$setBounds(x + radius, y + radius); + radius = Math.round(radius); + + // calc bounds + graphicsCalcBoundsUseCase( + this, this._$hasLineEnabled, + this._$positionX, this._$positionY, + this._$lineWidth, this._$caps, + x - radius, y - radius, + x + radius, y + radius + ); - this._$margePath($getArray( + // marge path + graphicsMargePathService( + this, + this._$hasFillEnabled, this._$hasLineEnabled, + this._$fills, this._$lines, Graphics.MOVE_TO, x + radius, y, Graphics.ARC, x, y, radius - )); - - this._$pointerX = x; - this._$pointerY = y; + ); - // restart - this._$restart(); + this._$positionX = x; + this._$positionY = y; return this; } @@ -1076,30 +958,7 @@ export class Graphics */ drawEllipse (x: number, y: number, width: number, height: number): Graphics { - x = +x || 0; - y = +y || 0; - width = +width || 0; - height = +height || 0; - - width = $Math.round(width); - height = $Math.round(height); - - const hw = width / 2; // half width - const hh = height / 2; // half height - const x0 = x + hw; - const y0 = y + hh; - const x1 = x + width; - const y1 = y + height; - const c = 4 / 3 * ($Math.SQRT2 - 1); - const cw = c * hw; - const ch = c * hh; - - return this - .moveTo(x0, y) - .cubicCurveTo(x0 + cw, y, x1, y0 - ch, x1, y0) - .cubicCurveTo(x1, y0 + ch, x0 + cw, y1, x0, y1) - .cubicCurveTo(x0 - cw, y1, x, y0 + ch, x, y0) - .cubicCurveTo(x, y0 - ch, x0 - cw, y, x0, y ); + return graphicsDrawEllipseService(this, x, y, width, height); } /** @@ -1116,22 +975,7 @@ export class Graphics */ drawRect (x: number, y: number, width: number, height: number): Graphics { - // valid - x = +x || 0; - y = +y || 0; - - width = +width || 0; - height = +height || 0; - - const xMax = $Math.round(x + width); - const yMax = $Math.round(y + height); - - return this - .moveTo(x, y) - .lineTo(x, yMax) - .lineTo(xMax, yMax) - .lineTo(xMax, y) - .lineTo(x, y); + return graphicsDrawRectService(this, x, y, width, height); } /** @@ -1153,45 +997,7 @@ export class Graphics width: number, height: number, ellipse_width: number, ellipse_height: number = NaN ): Graphics { - - x = +x || 0; - y = +y || 0; - - width = +width || 0; - height = +height || 0; - - ellipse_width = +ellipse_width || 0; - ellipse_height = +ellipse_height || ellipse_width; - - width = $Math.round(width); - height = $Math.round(height); - ellipse_width = $Math.round(ellipse_width); - ellipse_height = $Math.round(ellipse_height); - - const hew = ellipse_width / 2; - const heh = ellipse_height / 2; - const c = 4 / 3 * ($Math.SQRT2 - 1); - const cw = c * hew; - const ch = c * heh; - - const dx0 = x + hew; - const dx1 = x + width; - const dx2 = dx1 - hew; - - const dy0 = y + heh; - const dy1 = y + height; - const dy2 = dy1 - heh; - - return this - .moveTo(dx0, y) - .lineTo(dx2, y) - .cubicCurveTo(dx2 + cw, y, dx1, dy0 - ch, dx1, dy0) - .lineTo(dx1, dy2) - .cubicCurveTo(dx1, dy2 + ch, dx2 + cw, dy1, dx2, dy1) - .lineTo(dx0, dy1) - .cubicCurveTo(dx0 - cw, dy1, x, dy2 + ch, x, dy2) - .lineTo(x, dy0) - .cubicCurveTo(x, dy0 - ch, dx0 - cw, y, dx0, y); + return graphicsDrawRoundRectService(this, x, y, width, height, ellipse_width, ellipse_height); } /** @@ -1207,76 +1013,68 @@ export class Graphics */ endFill (): Graphics { - if (this._$doFill && this._$fills && this._$fills.length > 7) { + if (!this._$hasFillEnabled || !this._$fills || 8 > this._$fills.length) { + return this; + } - if (!this._$recode) { - this._$recode = $getArray(); - } + if (!this.$recodes) { + this.$recodes = $getArray(); + } - if (this._$fills[2] !== this._$fills[this._$fills.length - 2] - || this._$fills[3] !== this._$fills[this._$fills.length - 1] - ) { - this._$fills.push( - Graphics.LINE_TO, - this._$fills[2], - this._$fills[3] - ); - } - this._$recode.push(...this._$fills); + if (this._$fills[2] !== this._$fills[this._$fills.length - 2] + || this._$fills[3] !== this._$fills[this._$fills.length - 1] + ) { + this._$fills.push( + Graphics.LINE_TO, + this._$fills[2], + this._$fills[3] + ); + } + this.$recodes.push(...this._$fills); + + $poolArray(this._$fills); + this._$fills = null; - // fill - switch (this._$fillType) { + // fill + switch (this._$fillType) { + + case Graphics.FILL_STYLE: + this.$recodes.push( + this._$fillType, + this._$fillColor >>> 24 & 0xff, + this._$fillColor >>> 16 & 0xff, + this._$fillColor >>> 8 & 0xff, + this._$fillColor & 0xff, + Graphics.END_FILL + ); + break; - case Graphics.FILL_STYLE: - this._$recode.push( + case Graphics.GRADIENT_FILL: + if (this._$fillGradient) { + this.$recodes.push( this._$fillType, - this._$fillStyleR, - this._$fillStyleG, - this._$fillStyleB, - this._$fillStyleA, - Graphics.END_FILL + ...this._$fillGradient.toArray() ); - break; - - case Graphics.GRADIENT_FILL: - if (this._$fillGradient) { - this._$recode.push( - this._$fillType, - ...this._$fillGradient.toArray() - ); - } - break; - - case Graphics.BITMAP_FILL: - if (this._$fillBitmap) { - this._$recode.push( - this._$fillType, - ...this._$fillBitmap.toArray() - ); - } - break; - - } + } + break; - } + case Graphics.BITMAP_FILL: + if (this._$fillBitmap) { + this.$recodes.push( + this._$fillType, + ...this._$fillBitmap.toArray() + ); + } + break; - if (this._$fills) { - $poolArray(this._$fills); - this._$fills = null; } // reset - this._$fillType = 0; - this._$fillGradient = null; - this._$fillBitmap = null; - this._$fillStyleR = 0; - this._$fillStyleG = 0; - this._$fillStyleB = 0; - this._$fillStyleA = 0; - this._$doFill = false; - - // restart - this._$restart(); + this._$fillType = 0; + this._$fillColor = 0; + this._$fillGradient = null; + this._$fillBitmap = null; + this._$hasFillEnabled = false; return this; } @@ -1293,80 +1091,75 @@ export class Graphics */ endLine (): Graphics { - if (this._$doLine && this._$lines) { + if (!this._$hasLineEnabled || !this._$lines) { + return this; + } - if (!this._$recode) { - this._$recode = $getArray(); - } + if (!this.$recodes) { + this.$recodes = $getArray(); + } - this._$recode.push(...this._$lines); + this.$recodes.push(...this._$lines); - // clear - $poolArray(this._$lines); - this._$lines = null; + // clear + $poolArray(this._$lines); + this._$lines = null; - // fill - switch (this._$lineType) { + // fill + switch (this._$lineType) { + + case Graphics.STROKE_STYLE: + this.$recodes.push( + this._$lineType, + this._$lineWidth, + this._$caps, + this._$joints, + this._$miterLimit, + this._$lineColor >>> 24 & 0xff, + this._$lineColor >>> 16 & 0xff, + this._$lineColor >>> 8 & 0xff, + this._$lineColor & 0xff, + Graphics.END_STROKE + ); + break; - case Graphics.STROKE_STYLE: - this._$recode.push( + case Graphics.GRADIENT_STROKE: + if (this._$lineGradient) { + this.$recodes.push( this._$lineType, this._$lineWidth, this._$caps, this._$joints, this._$miterLimit, - this._$lineStyleR, - this._$lineStyleG, - this._$lineStyleB, - this._$lineStyleA, - Graphics.END_STROKE + ...this._$lineGradient.toArray() ); - break; - - case Graphics.GRADIENT_STROKE: - if (this._$lineGradient) { - this._$recode.push( - this._$lineType, - this._$lineWidth, - this._$caps, - this._$joints, - this._$miterLimit, - ...this._$lineGradient.toArray() - ); - } - break; - - case Graphics.BITMAP_STROKE: - if (this._$fillBitmap) { - this._$recode.push( - this._$lineType, - this._$lineWidth, - this._$caps, - this._$joints, - this._$miterLimit, - ...this._$fillBitmap.toArray() - ); - } - break; + } + break; + + case Graphics.BITMAP_STROKE: + if (this._$fillBitmap) { + this.$recodes.push( + this._$lineType, + this._$lineWidth, + this._$caps, + this._$joints, + this._$miterLimit, + ...this._$fillBitmap.toArray() + ); + } + break; - } } // reset - this._$lineType = 0; - this._$lineWidth = 0; - this._$lineGradient = null; - this._$lineStyleR = 0; - this._$lineStyleG = 0; - this._$lineStyleB = 0; - this._$lineStyleA = 0; - this._$caps = "none"; - this._$joints = "round"; - this._$miterLimit = 0; - this._$doLine = false; - - // restart - this._$restart(); + this._$lineType = 0; + this._$lineWidth = 0; + this._$lineGradient = null; + this._$lineColor = 0; + this._$caps = "none"; + this._$joints = "round"; + this._$miterLimit = 0; + this._$hasLineEnabled = false; return this; } @@ -1390,9 +1183,8 @@ export class Graphics smooth: boolean = false ): Graphics { - // end fill - if (this._$doLine) { - this.endLine(); + if (!this._$hasLineEnabled) { + return this; } if (!this._$lines) { @@ -1400,9 +1192,9 @@ export class Graphics } // start - this._$maxAlpha = 1; - this._$doLine = true; - this._$canDraw = true; + this._$maxAlpha = 1; + this._$hasLineEnabled = true; + this._$isBeginning = true; // beginPath this._$lines.push(Graphics.BEGIN_PATH); @@ -1432,15 +1224,15 @@ export class Graphics * @public */ lineGradientStyle ( - type: GradientTypeImpl, + type: IGradientType, colors: number[], alphas: number[], ratios: number[], matrix: Matrix | null = null, - spread_method: SpreadMethodImpl = "pad", - interpolation_method: InterpolationMethodImpl = "rgb", + spread_method: ISpreadMethod = "pad", + interpolation_method: IInterpolationMethod = "rgb", focal_point_ratio: number = 0 ): Graphics { - if (!this._$doLine) { + if (!this._$hasLineEnabled) { return this; } @@ -1450,7 +1242,7 @@ export class Graphics // setup for (let idx: number = 0; idx < alphas.length; ++idx) { - this._$maxAlpha = $Math.max(this._$maxAlpha, alphas[idx]); + this._$maxAlpha = Math.max(this._$maxAlpha, alphas[idx]); } // beginPath @@ -1487,12 +1279,12 @@ export class Graphics thickness: number = 1, color: string | number = 0, alpha: number = 1, - caps: CapsStyleImpl = "round", - joints: JointStyleImpl = "round", + caps: ICapsStyle = "round", + joints: IJointStyle = "round", miter_limit: number = 3 ): Graphics { - if (this._$doLine) { + if (this._$hasLineEnabled) { this.endLine(); } @@ -1500,40 +1292,44 @@ export class Graphics this._$lines = $getArray(); } - color = $clamp($toColorInt(color), 0, 0xffffff, 0); - alpha = $clamp(+alpha, 0, 1, 1); - // setup - this._$maxAlpha = $Math.max(this._$maxAlpha, alpha); - this._$doLine = true; - this._$canDraw = true; + this._$maxAlpha = Math.max(this._$maxAlpha, alpha); + this._$hasLineEnabled = true; + this._$isBeginning = true; // beginPath - if (this._$pointerX || this._$pointerY) { + if (this._$positionX || this._$positionY) { this._$lines.push( Graphics.BEGIN_PATH, Graphics.MOVE_TO, - this._$pointerX, - this._$pointerY + this._$positionX, + this._$positionY ); } else { this._$lines.push(Graphics.BEGIN_PATH); } - // add Fill Style - const object = $intToRGBA(color, alpha); - // color - this._$lineType = Graphics.STROKE_STYLE; - this._$lineStyleR = object.R; - this._$lineStyleG = object.G; - this._$lineStyleB = object.B; - this._$lineStyleA = object.A; + this._$lineType = Graphics.STROKE_STYLE; + + // valid + if (typeof color === "string") { + color = $convertColorStringToNumber(color); + } + + color = $clamp(color, 0, 0xffffff, 0); + alpha = $clamp(alpha, 0, 1, 1); + + // Color Int 32bit(RGBA) + const red = color >>> 16 & 0xff; + const green = color >>> 8 & 0xff; + const blue = color & 0xff; + this._$lineColor = red << 24 | green << 16 | blue << 8 | alpha * 255; // param - this._$lineWidth = thickness; - this._$caps = `${caps}`; - this._$joints = `${joints}`; + this._$lineWidth = thickness; + this._$caps = `${caps}`; + this._$joints = `${joints}`; // set miter limit if (this._$joints === "miter") { @@ -1560,19 +1356,29 @@ export class Graphics x = +x || 0; y = +y || 0; - if (this._$pointerX === x && this._$pointerY === y) { + if (this._$positionX === x && this._$positionY === y) { return this; } - this._$setBounds(x, y); - - this._$margePath($getArray(Graphics.LINE_TO, x, y)); + // calc bounds + graphicsCalcBoundsUseCase( + this, this._$hasLineEnabled, + this._$positionX, this._$positionY, + this._$lineWidth, this._$caps, + x, y + ); - this._$pointerX = x; - this._$pointerY = y; + // marge path + graphicsMargePathService( + this, + this._$hasFillEnabled, this._$hasLineEnabled, + this._$fills, this._$lines, + Graphics.LINE_TO, + x, y + ); - // restart - this._$restart(); + this._$positionX = x; + this._$positionY = y; return this; } @@ -1592,13 +1398,19 @@ export class Graphics x = +x || 0; y = +y || 0; - this._$pointerX = x; - this._$pointerY = y; + this._$positionX = x; + this._$positionY = y; - this._$setBounds(x, y); + // calc bounds + graphicsCalcBoundsUseCase( + this, this._$hasLineEnabled, + this._$positionX, this._$positionY, + this._$lineWidth, this._$caps, + x, y + ); let duplication = false; - if (this._$doFill && this._$fills) { + if (this._$hasFillEnabled && this._$fills) { const isMove = this._$fills[this._$fills.length - 3] === Graphics.MOVE_TO; if (isMove) { duplication = true; @@ -1607,7 +1419,7 @@ export class Graphics } } - if (this._$doLine && this._$lines) { + if (this._$hasLineEnabled && this._$lines) { const isMove = this._$lines[this._$lines.length - 3] === Graphics.MOVE_TO; if (isMove) { duplication = true; @@ -1616,1688 +1428,58 @@ export class Graphics } } + // marge path if (!duplication) { - this._$margePath($getArray(Graphics.MOVE_TO, x, y)); - } - - // restart - this._$restart(); - - return this; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - // size - const baseBounds: BoundsImpl = this._$getBounds(); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil($Math.abs(bounds.xMax - bounds.xMin)); - const height: number = $Math.ceil($Math.abs(bounds.yMax - bounds.yMin)); - $poolBoundsObject(bounds); - - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - context.reset(); - context.setTransform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - this._$doDraw(context, null, true); - - context.clip(); - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {string} [blend_mode=BlendMode.NORMAL] - * @param {array} [filters=null] - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array, - blend_mode: BlendModeImpl = "normal", - filters: FilterArrayImpl | null = null - ): void { - - if (!this._$maxAlpha) { - return ; - } - - const alpha: number = $clamp( - color_transform[3] + color_transform[7] / 255, 0, 1 - ); - - const displayObject: DisplayObjectImpl = this._$displayObject; - - // set grid data - let hasGrid: boolean = displayObject._$scale9Grid !== null; - - // 9スライスを有効にしたオブジェクトが回転・傾斜成分を含む場合は - // 9スライスは無効になる - const rawMatrix: Float32Array = displayObject._$transform._$rawMatrix(); - if (hasGrid) { - hasGrid = hasGrid - && $Math.abs(rawMatrix[1]) < 0.001 - && $Math.abs(rawMatrix[2]) < 0.0001; - } - - // size - const baseBounds: BoundsImpl = this._$getBounds(); - const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix); - const xMax: number = bounds.xMax; - const xMin: number = bounds.xMin; - const yMax: number = bounds.yMax; - const yMin: number = bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - matrix[0] * matrix[0] - + matrix[1] * matrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - matrix[2] * matrix[2] - + matrix[3] * matrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const canApply: boolean = filters !== null - && filters.length > 0 - && displayObject._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - - $poolBoundsObject(filterBounds); - - // get cache - if (this._$uniqueKey === "") { - if (!hasGrid - && displayObject._$loaderInfo - && displayObject._$characterId - ) { - this._$uniqueKey = `${displayObject._$loaderInfo._$id}@${this._$bitmapId || displayObject._$characterId}`; - } else { - this._$uniqueKey = this._$createCacheKey(); - } - } - - const player: Player = $currentPlayer(); - - if (this._$mode === "bitmap") { - - if (!this._$cacheKeys.length) { - this._$cacheKeys = $cacheStore.generateKeys(this._$uniqueKey); - } - - } else { - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - - const keys: number[] = $getArray(); - keys[0] = xScale; - keys[1] = yScale; - - this._$cacheKeys = $cacheStore.generateKeys( - this._$uniqueKey, keys, color_transform - ); - - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - } - - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - if (currentAttachment && currentAttachment.mask) { - context.stopStencil(); - } - - let width: number = 0; - let height: number = 0; - if (this._$mode === "shape") { - - width = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin) * xScale); - height = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin) * yScale); - - // resize - const textureScale: number = context._$getTextureScale(width, height); - if (textureScale < 1) { - width *= textureScale; - height *= textureScale; - } - - } else { - width = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin)); - height = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin)); - } - - // create cache position - context.cachePosition = manager.createCachePosition(width, height); - context.bindRenderBuffer(context.cachePosition); - - // reset - context.reset(); - - if (this._$mode === "shape") { - context.setTransform( - xScale, 0, 0, yScale, - -baseBounds.xMin * xScale, - -baseBounds.yMin * yScale - ); - } else { - context.setTransform( - 1, 0, 0, 1, - -baseBounds.xMin, - -baseBounds.yMin - ); - } - - if (hasGrid) { - - const mScale: number = player.scaleX; - - const baseMatrix: Float32Array = $getFloat32Array6( - mScale, 0, 0, mScale, 0, 0 - ); - - const pMatrix: Float32Array = $multiplicationMatrix( - baseMatrix, rawMatrix - ); - - $poolFloat32Array6(baseMatrix); - - const aMatrixBase: Float32Array = displayObject - ._$parent - ._$transform - .concatenatedMatrix - ._$matrix; - - const aMatrix: Float32Array = $getFloat32Array6( - aMatrixBase[0], aMatrixBase[1], aMatrixBase[2], aMatrixBase[3], - aMatrixBase[4] * mScale - xMin, - aMatrixBase[5] * mScale - yMin - ); - $poolFloat32Array6(aMatrixBase); - - const apMatrix: Float32Array = $multiplicationMatrix( - aMatrix, pMatrix - ); - const aOffsetX: number = apMatrix[4] - (matrix[4] - xMin); - const aOffsetY: number = apMatrix[5] - (matrix[5] - yMin); - $poolFloat32Array6(apMatrix); - - const parentBounds: BoundsImpl = $boundsMatrix(baseBounds, pMatrix); - const parentXMax: number = +parentBounds.xMax; - const parentXMin: number = +parentBounds.xMin; - const parentYMax: number = +parentBounds.yMax; - const parentYMin: number = +parentBounds.yMin; - const parentWidth: number = $Math.ceil($Math.abs(parentXMax - parentXMin)); - const parentHeight: number = $Math.ceil($Math.abs(parentYMax - parentYMin)); - - $poolBoundsObject(parentBounds); - - const scale9Grid: Rectangle = displayObject._$scale9Grid as NonNullable; - const grid: GridImpl = { - "x": scale9Grid.x, - "y": scale9Grid.y, - "w": scale9Grid.width, - "h": scale9Grid.height - }; - - context.grid.enable( - parentXMin, parentYMin, parentWidth, parentHeight, - baseBounds, grid, mScale, - pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5], - aMatrix[0], aMatrix[1], aMatrix[2], aMatrix[3], aMatrix[4] - aOffsetX, aMatrix[5] - aOffsetY - ); - - $poolFloat32Array6(pMatrix); - $poolFloat32Array6(aMatrix); - } - - // execute - this._$doDraw(context, color_transform, false); - - if (hasGrid) { - context.grid.disable(); - } - - manager.transferTexture(context.cachePosition); - - // set cache - $cacheStore.set(this._$cacheKeys, context.cachePosition); - - // end draw and reset current buffer - context._$bind(currentAttachment); - } - - let offsetX: number = 0; - let offsetY: number = 0; - if (canApply) { - - const bitmapTexture: WebGLTexture | null = this._$createBitmapTexture( - context, context.cachePosition, - xScale, yScale, width, height - ); - - const position: CachePositionImpl = displayObject._$drawFilter( - context, matrix, filters, - width, height, bitmapTexture - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - } - - if (!canApply && this._$mode === "bitmap") { - - context.setTransform( - matrix[0], matrix[1], - matrix[2], matrix[3], - baseBounds.xMin * matrix[0] + baseBounds.yMin * matrix[2] + matrix[4], - baseBounds.xMin * matrix[1] + baseBounds.yMin * matrix[3] + matrix[5] - ); - - } else { - - const radianX: number = $Math.atan2(matrix[1], matrix[0]); - const radianY: number = $Math.atan2(-matrix[2], matrix[3]); - if (!canApply && (radianX || radianY)) { - - const tx: number = baseBounds.xMin * xScale; - const ty: number = baseBounds.yMin * yScale; - - const cosX: number = $Math.cos(radianX); - const sinX: number = $Math.sin(radianX); - const cosY: number = $Math.cos(radianY); - const sinY: number = $Math.sin(radianY); - - context.setTransform( - cosX, sinX, -sinY, cosY, - tx * cosX - ty * sinY + matrix[4], - tx * sinX + ty * cosY + matrix[5] - ); - - } else { - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = this._$mode === "shape"; - context.globalCompositeOperation = blend_mode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; - } - - // pool - $poolBoundsObject(baseBounds); - } - - /** - * @return {WebGLTexture | null} - * @method - * @private - */ - _$createBitmapTexture ( - context: CanvasToWebGLContext, - position: CachePositionImpl, - x_scale: number, - y_scale: number, - width: number, - height: number - ): WebGLTexture | null { - - if (this._$mode !== "bitmap") { - return null; - } - - context.drawInstacedArray(); - - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - - context._$bind(attachment); - - context.reset(); - - const parentMatrix: Float32Array = $getFloat32Array6( - x_scale, 0, 0, y_scale, - width / 2, height / 2 - ); - - const texture: WebGLTexture = context.getTextureFromRect(position); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - -texture.width / 2, - -texture.height / 2 - ); - - const scaleMatrix = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - context.setTransform( - scaleMatrix[0], scaleMatrix[1], - scaleMatrix[2], scaleMatrix[3], - scaleMatrix[4], scaleMatrix[5] - ); - - context.drawImage(texture, 0, 0, texture.width, texture.height); - - const bitmapTexture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - context._$bind(currentAttachment); - - manager.releaseAttachment(attachment); - manager.textureManager.release(texture); - - return bitmapTexture; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} [color_transform=null] - * @param {boolean} [is_clip=false] - * @return {void} - * @method - * @private - */ - _$doDraw ( - context: CanvasToWebGLContext, - color_transform: Float32Array | null = null, - is_clip: boolean = false - ): void { - - // draw - context.reset(); - context.beginPath(); - this._$runCommand(context, color_transform, is_clip); - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @param {boolean} [is_clip=false] - * @return {boolean} - * @method - * @private - */ - _$hit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl, - is_clip: boolean = false - ): boolean { - - context.beginPath(); - context.setTransform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - return this._$runCommand(context, null, is_clip, options); - } - - /** - * @return {object} - * @method - * @private - */ - _$getBounds (): BoundsImpl - { - const displayObject: DisplayObjectImpl = this._$displayObject; - if (displayObject && displayObject._$bounds) { - return $getBoundsObject( - displayObject._$bounds.xMin, displayObject._$bounds.xMax, - displayObject._$bounds.yMin, displayObject._$bounds.yMax + graphicsMargePathService( + this, + this._$hasFillEnabled, this._$hasLineEnabled, + this._$fills, this._$lines, + Graphics.MOVE_TO, + x, y ); } - return $getBoundsObject( - this._$xMin, this._$xMax, - this._$yMin, this._$yMax - ); - } - - /** - * @return {void} - * @method - * @private - */ - _$restart (): void - { - if (this._$displayObject) { - - // reset - this._$displayObject._$posted = false; - - if (!this._$displayObject._$isUpdated()) { - - this._$displayObject._$doChanged(); - $doUpdated(); - - $cacheStore.removeCache(this._$displayObject._$instanceId); - - if (this._$displayObject._$characterId) { - $cacheStore.removeCache(this._$displayObject._$characterId); - } - } - } - } - - /** - * @param {number} [x=0] - * @param {number} [y=0] - * @return {void} - * @method - * @private - */ - _$setBounds (x: number = 0, y: number = 0): void - { - this._$setFillBounds(x, y); - if (this._$doLine) { - this._$setLineBounds(x, y); - } - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @private - */ - _$setFillBounds (x: number = 0, y: number = 0): void - { - this._$xMin = $Math.min(this._$xMin, x); - this._$xMax = $Math.max(this._$xMax, x); - this._$yMin = $Math.min(this._$yMin, y); - this._$yMax = $Math.max(this._$yMax, y); - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @private - */ - _$setLineBounds (x: number = 0, y: number = 0): void - { - this._$xMin = $Math.min(this._$xMin, $Math.min(x, this._$pointerX)); - this._$xMax = $Math.max(this._$xMax, $Math.max(x, this._$pointerX)); - this._$yMin = $Math.min(this._$yMin, $Math.min(y, this._$pointerY)); - this._$yMax = $Math.max(this._$yMax, $Math.max(y, this._$pointerY)); - - // correction - const half: number = this._$lineWidth / 2; - const radian90: number = 0.5 * $Math.PI; - const radian1: number = $Math.atan2(y - this._$pointerY, x - this._$pointerX); // to end point - const radian2: number = $Math.atan2(this._$pointerY - y, this._$pointerX - x); // to start point - const radian3: number = radian1 + radian90; - const radian4: number = radian1 - radian90; - const radian5: number = radian2 + radian90; - const radian6: number = radian2 - radian90; - - // init - let x1: number = x + half; - let x2: number = -half + x; - let x3: number = this._$pointerX + half; - let x4: number = -half + this._$pointerX; - let y1: number = y + half; - let y2: number = -half + y; - let y3: number = this._$pointerY + half; - let y4: number = -half + this._$pointerY; - - this._$xMin = $Math.min(this._$xMin, $Math.min(x1, $Math.min(x2, $Math.min(x3, x4)))); - this._$xMax = $Math.max(this._$xMax, $Math.max(x1, $Math.max(x2, $Math.max(x3, x4)))); - this._$yMin = $Math.min(this._$yMin, $Math.min(y1, $Math.min(y2, $Math.min(y3, y4)))); - this._$yMax = $Math.max(this._$yMax, $Math.max(y1, $Math.max(y2, $Math.max(y3, y4)))); - - // pointer x - if ($Math.abs(radian3) % radian90 !== 0) { - x1 = x + $Math.cos(radian3) * half; - } - - if ($Math.abs(radian4) % radian90 !== 0) { - x2 = x + $Math.cos(radian4) * half; - } - - if ($Math.abs(radian5) % radian90 !== 0) { - x3 = this._$pointerX + $Math.cos(radian5) * half; - } - - if ($Math.abs(radian6) % radian90 !== 0) { - x4 = this._$pointerX + $Math.cos(radian6) * half; - } - - // pointer y - if (radian3 && $Math.abs(radian3) % $Math.PI !== 0) { - y1 = y + $Math.sin(radian3) * half; - } - - if (radian4 && $Math.abs(radian4) % $Math.PI !== 0) { - y2 = y + $Math.sin(radian4) * half; - } - - if (radian5 && $Math.abs(radian5) % $Math.PI !== 0) { - y3 = this._$pointerY + $Math.sin(radian5) * half; - } - - if (radian6 && $Math.abs(radian6) % $Math.PI !== 0) { - y4 = this._$pointerY + $Math.sin(radian6) * half; - } - - this._$xMin = $Math.min(this._$xMin, $Math.min(x1, $Math.min(x2, $Math.min(x3, x4)))); - this._$xMax = $Math.max(this._$xMax, $Math.max(x1, $Math.max(x2, $Math.max(x3, x4)))); - this._$yMin = $Math.min(this._$yMin, $Math.min(y1, $Math.min(y2, $Math.min(y3, y4)))); - this._$yMax = $Math.max(this._$yMax, $Math.max(y1, $Math.max(y2, $Math.max(y3, y4)))); - - // case - switch (this._$caps) { - - case "round": - - if ($Math.abs(radian1) % radian90 !== 0) { - const rx1: number = x + $Math.cos(radian1) * half; - this._$xMin = $Math.min(this._$xMin, rx1); - this._$xMax = $Math.max(this._$xMax, rx1); - } - - if (radian1 && $Math.abs(radian1) % $Math.PI !== 0) { - const ry1: number = y + $Math.sin(radian1) * half; - this._$yMin = $Math.min(this._$yMin, ry1); - this._$yMax = $Math.max(this._$yMax, ry1); - } - - if ($Math.abs(radian2) % radian90 !== 0) { - const rx2: number = this._$pointerX + $Math.cos(radian2) * half; - this._$xMin = $Math.min(this._$xMin, rx2); - this._$xMax = $Math.max(this._$xMax, rx2); - } - - if (radian2 && $Math.abs(radian2) % $Math.PI !== 0) { - const ry2: number = this._$pointerY + $Math.sin(radian2) * half; - this._$yMin = $Math.min(this._$yMin, ry2); - this._$yMax = $Math.max(this._$yMax, ry2); - } - - break; - - case "square": - - if ($Math.abs(radian1) % radian90 !== 0) { - const r1cos: number = $Math.cos(radian1) * half; - const rx1: number = x1 + r1cos; - const rx2: number = x2 + r1cos; - this._$xMin = $Math.min(this._$xMin, $Math.min(rx1, rx2)); - this._$xMax = $Math.max(this._$xMax, $Math.max(rx1, rx2)); - } - - if ($Math.abs(radian2) % radian90 !== 0) { - const r2cos: number = $Math.cos(radian2) * half; - const rx3: number = x3 + r2cos; - const rx4: number = x4 + r2cos; - this._$xMin = $Math.min(this._$xMin, $Math.min(rx3, rx4)); - this._$xMax = $Math.max(this._$xMax, $Math.max(rx3, rx4)); - } - - if (radian1 && $Math.abs(radian1) % $Math.PI !== 0) { - const r1sin: number = $Math.sin(radian1) * half; - const ry1: number = y1 + r1sin; - const ry2: number = y2 + r1sin; - this._$yMin = $Math.min(this._$yMin, $Math.min(ry1, ry2)); - this._$yMax = $Math.max(this._$yMax, $Math.max(ry1, ry2)); - } - - if (radian2 && $Math.abs(radian2) % $Math.PI !== 0) { - const r2sin: number = $Math.sin(radian2) * half; - const ry3: number = y3 + r2sin; - const ry4: number = y4 + r2sin; - this._$yMin = $Math.min(this._$yMin, $Math.min(ry3, ry4)); - this._$yMax = $Math.max(this._$yMax, $Math.max(ry3, ry4)); - } - - break; - - default: - break; - - } - } - - /** - * @param {array} data - * @method - * @private - */ - _$margePath (data: any[]): void - { - if (this._$doFill && this._$fills) { - this._$fills.push(...data); - } - - if (this._$doLine && this._$lines) { - this._$lines.push(...data); - } - - $poolArray(data); + return this; } /** - * @return {string} + * @description この Graphics オブジェクトに描画されているパス情報をFloat32Arrayで返却 + * Returns the path information drawn to this Graphics object in Float32Array. + * + * @member {Float32Array} * @method - * @private + * @public */ - _$createCacheKey (): string + get buffer (): Float32Array { - if (this._$doLine) { - this.endLine(); + if (this.isConfirmed && this._$buffer) { + return this._$buffer; } // fixed logic - if (this._$doFill) { - this.endFill(); - } - - if (!this._$recode) { - return ""; - } - - const recodes: Float32Array = this._$getRecodes(); - - let hash = 0; - for (let idx: number = 0; idx < recodes.length; idx++) { - - const chr: number = recodes[idx]; - - hash = (hash << 5) - hash + chr; - hash |= 0; - } - - return `${hash}`; - } - - /** - * @return {Float32Array} - * @method - * @private - */ - _$getRecodes (): Float32Array - { - // fixed logic - if (this._$doLine) { + if (this._$hasLineEnabled) { this.endLine(); } // fixed logic - if (this._$doFill) { + if (this._$hasFillEnabled) { this.endFill(); } - if (!this._$recode) { - this._$recode = $getArray(); - } - - if (!this._$buffer) { - - const array: number[] = $getArray(); - - const recode: any[] = this._$recode; - for (let idx: number = 0; idx < recode.length;) { - - const type: number = recode[idx++]; - array.push(type); - switch (type) { - - case Graphics.BEGIN_PATH: - case Graphics.END_FILL: - case Graphics.END_STROKE: - case Graphics.CLOSE_PATH: - break; - - case Graphics.MOVE_TO: - case Graphics.LINE_TO: - array.push(recode[idx++], recode[idx++]); - break; - - case Graphics.CURVE_TO: - case Graphics.FILL_STYLE: - array.push( - recode[idx++], recode[idx++], - recode[idx++], recode[idx++] - ); - break; - - case Graphics.CUBIC: - array.push( - recode[idx++], recode[idx++], - recode[idx++], recode[idx++], - recode[idx++], recode[idx++] - ); - break; - - case Graphics.STROKE_STYLE: - { - array.push(recode[idx++]); - - const lineCap = recode[idx++]; - switch (lineCap) { - - case "none": - array.push(0); - break; - - case "round": - array.push(1); - break; - - case "square": - array.push(2); - break; - - } - - const lineJoin = recode[idx++]; - switch (lineJoin) { - - case "bevel": - array.push(0); - break; - - case "miter": - array.push(1); - break; - - case "round": - array.push(2); - break; - - } - - array.push( - recode[idx++], // MITER LIMIT - recode[idx++], recode[idx++], - recode[idx++], recode[idx++] - ); - } - break; - - case Graphics.ARC: - array.push(recode[idx++], recode[idx++], recode[idx++]); - break; - - case Graphics.GRADIENT_FILL: - { - const type: GradientTypeImpl = recode[idx++]; - const stops: ColorStopImpl[] = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const spread: SpreadMethodImpl = recode[idx++]; - const interpolation: InterpolationMethodImpl = recode[idx++]; - const focal: number = recode[idx++]; - - array.push(type === "linear" ? 0 : 1); - - array.push(stops.length); - for (let idx: number = 0; idx < stops.length; ++idx) { - const color: ColorStopImpl = stops[idx]; - array.push( - color.ratio, - color.R, - color.G, - color.B, - color.A - ); - } - - array.push( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - switch (spread) { - - case "reflect": - array.push(0); - break; - - case "repeat": - array.push(1); - break; - - default: - array.push(2); - break; - - } - - array.push( - interpolation === "linearRGB" ? 0 : 1 - ); - - array.push(focal); - } - break; - - case Graphics.GRADIENT_STROKE: - { - array.push(recode[idx++]); - - const lineCap: CapsStyleImpl = recode[idx++]; - switch (lineCap) { - - case "none": - array.push(0); - break; - - case "round": - array.push(1); - break; - - case "square": - array.push(2); - break; - - } - - const lineJoin: JointStyleImpl = recode[idx++]; - switch (lineJoin) { - - case "bevel": - array.push(0); - break; - - case "miter": - array.push(1); - break; - - case "round": - array.push(2); - break; - - } - - // miterLimit - array.push(recode[idx++]); - - const type: GradientTypeImpl = recode[idx++]; - const stops: ColorStopImpl[] = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const spread: SpreadMethodImpl = recode[idx++]; - const interpolation: InterpolationMethodImpl = recode[idx++]; - const focal: number = recode[idx++]; - - array.push(type === "linear" ? 0 : 1); - - array.push(stops.length); - for (let idx: number = 0; idx < stops.length; ++idx) { - const color: ColorStopImpl = stops[idx]; - array.push( - color.ratio, - color.R, - color.G, - color.B, - color.A - ); - } - - array.push( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - switch (spread) { - - case "reflect": - array.push(0); - break; - - case "repeat": - array.push(1); - break; - - default: - array.push(2); - break; - - } - - array.push( - interpolation === "linearRGB" ? 0 : 1 - ); - - array.push(focal); - } - break; - - case Graphics.BITMAP_FILL: - { - const bitmapData: BitmapData = recode[idx++]; - - let buffer: Uint8Array; - if (bitmapData.image !== null || bitmapData.canvas !== null) { - - const canvas: HTMLCanvasElement = $cacheStore.getCanvas(); - - const width: number = bitmapData.width; - const height: number = bitmapData.height; - canvas.width = width; - canvas.height = height; - - const context: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!context) { - throw new Error("the context is null."); - } - - // @ts-ignore - context.drawImage(bitmapData.image || bitmapData.canvas, 0, 0); - - buffer = new Uint8Array( - context.getImageData(0, 0, width, height).data - ); - - $cacheStore.destroy(context); - - } else if (bitmapData._$buffer !== null) { - buffer = bitmapData._$buffer; - } else { - break; - } - - array.push( - bitmapData.width, - bitmapData.height, - this._$xMax - this._$xMin, - this._$yMax - this._$yMin, - buffer.length - ); - - for (let idx: number = 0; idx < buffer.length; ++idx) { - array.push(buffer[idx]); - } - - const matrix: Float32Array = recode[idx++]; - if (matrix) { - array.push( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } else { - array.push(1, 0, 0, 1, 0, 0); - } - - const repeat: boolean = recode[idx++]; - array.push(repeat ? 1 : 0); - - const smooth: boolean = recode[idx++]; - array.push(smooth ? 1 : 0); - } - break; - - case Graphics.BITMAP_STROKE: - { - - array.push(recode[idx++]); - - const lineCap: CapsStyleImpl = recode[idx++]; - switch (lineCap) { - - case "none": - array.push(0); - break; - - case "round": - array.push(1); - break; + const array = graphicsToNumberArrayService(this.$recodes); + this._$buffer = new Float32Array(array); + $poolArray(array); - case "square": - array.push(2); - break; + // レコードの確定フラグを更新 + this.isConfirmed = true; - } - - const lineJoin: JointStyleImpl = recode[idx++]; - switch (lineJoin) { - - case "bevel": - array.push(0); - break; - - case "miter": - array.push(1); - break; - - case "round": - array.push(2); - break; - - } - - // MITER LIMIT - array.push(recode[idx++]); - - const bitmapData: BitmapData = recode[idx++]; - - let buffer: Uint8Array; - if (bitmapData.image !== null || bitmapData.canvas !== null) { - - const canvas: HTMLCanvasElement = $cacheStore.getCanvas(); - - const width: number = bitmapData.width; - const height: number = bitmapData.height; - canvas.width = width; - canvas.height = height; - - const context: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!context) { - throw new Error("the context is null."); - } - - // @ts-ignore - context.drawImage(bitmapData.image || bitmapData.canvas, 0, 0); - - buffer = new Uint8Array( - context.getImageData(0, 0, width, height).data - ); - - $cacheStore.destroy(context); - - } else if (bitmapData._$buffer !== null) { - buffer = bitmapData._$buffer; - } else { - break; - } - - array.push( - bitmapData.width, - bitmapData.height, - this._$xMax - this._$xMin, - this._$yMax - this._$yMin, - buffer.length - ); - - for (let idx: number = 0; idx < buffer.length; ++idx) { - array.push(buffer[idx]); - } - - const matrix: Float32Array = recode[idx++]; - if (matrix) { - array.push( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } else { - array.push(1, 0, 0, 1, 0, 0); - } - - const repeat: boolean = recode[idx++]; - array.push(repeat ? 1 : 0); - - const smooth: boolean = recode[idx++]; - array.push(smooth ? 1 : 0); - } - break; - - default: - break; - - } - } - - this._$buffer = new $Float32Array(array); - } - - return this._$buffer.slice(); + return this._$buffer; } - - /** - * @param {CanvasToWebGLContext|CanvasRenderingContext2D} context - * @param {Float32Array} [color_transform=null] - * @param {boolean} [is_clip=false] - * @param {object} [options=null] - * @return {boolean} - * @method - * @private - */ - _$runCommand ( - context: CanvasToWebGLContext | CanvasRenderingContext2D, - color_transform: Float32Array | null = null, - is_clip: boolean = false, - options: PlayerHitObjectImpl | null = null - ): boolean { - - // fixed logic - if (this._$doLine) { - this.endLine(); - } - - // fixed logic - if (this._$doFill) { - this.endFill(); - } - - if (!this._$recode) { - return false; - } - - const recode: any[] = this._$recode; - const length: number = recode.length; - for (let idx: number = 0; idx < length; ) { - switch (recode[idx++]) { - - case Graphics.BEGIN_PATH: - context.beginPath(); - break; - - case Graphics.MOVE_TO: - context.moveTo(recode[idx++], recode[idx++]); - break; - - case Graphics.LINE_TO: - context.lineTo(recode[idx++], recode[idx++]); - break; - - case Graphics.CURVE_TO: - context.quadraticCurveTo( - recode[idx++], recode[idx++], - recode[idx++], recode[idx++] - ); - break; - - case Graphics.FILL_STYLE: - { - if (is_clip || options) { - idx += 4; - continue; - } - - const color: Float32Array = $getFloat32Array4(); - color[0] = recode[idx++] / 255; - color[1] = recode[idx++] / 255; - color[2] = recode[idx++] / 255; - color[3] = recode[idx++] / 255; - - if (color_transform !== null) { - if (color_transform[7] !== 0) { - color[3] = $Math.max(0, $Math.min( - color[3] * color_transform[7], 255) - ) / 255; - } - } - - context.fillStyle = color; - } - break; - - case Graphics.END_FILL: - - if (options && "isPointInPath" in context - && context.isPointInPath(options.x, options.y) - ) { - return true; - } - - if (!is_clip && !options) { - context.fill(); - } - - break; - - case Graphics.STROKE_STYLE: - { - if (is_clip || options) { - idx += 8; - continue; - } - - context.lineWidth = recode[idx++]; - context.lineCap = recode[idx++]; - context.lineJoin = recode[idx++]; - context.miterLimit = recode[idx++]; - - const color = $getFloat32Array4(); - - color[0] = recode[idx++] / 255; - color[1] = recode[idx++] / 255; - color[2] = recode[idx++] / 255; - color[3] = recode[idx++] / 255; - - if (color_transform !== null) { - if (color_transform[7] !== 0) { - color[3] = $Math.max(0, $Math.min( - color[3] + color_transform[7], 255) - ) / 255; - } - } - - context.strokeStyle = color; - } - break; - - case Graphics.END_STROKE: - - if (options && "isPointInStroke" in context - && context.isPointInStroke(options.x, options.y) - ) { - return true; - } - - if (!is_clip && !options) { - context.stroke(); - } - - break; - - case Graphics.CLOSE_PATH: - context.closePath(); - break; - - case Graphics.CUBIC: - context.bezierCurveTo( - recode[idx++], recode[idx++], - recode[idx++], recode[idx++], - recode[idx++], recode[idx++] - ); - break; - - case Graphics.ARC: - context.arc( - recode[idx++], recode[idx++], recode[idx++], - 0, 2 * $Math.PI - ); - break; - - case Graphics.GRADIENT_FILL: - { - if (options && "isPointInPath" in context - && context.isPointInPath(options.x, options.y) - ) { - return true; - } - - if (is_clip || options - || context instanceof CanvasRenderingContext2D // fixed logic - ) { - idx += 6; - continue; - } - - const type: GradientTypeImpl = recode[idx++]; - const stops: ColorStopImpl[] = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const spread: SpreadMethodImpl = recode[idx++]; - const interpolation: InterpolationMethodImpl = recode[idx++]; - const focal: number = recode[idx++]; - - let css: CanvasGradientToWebGL; - if (type === "linear") { - - const xy: Float32Array = $linearGradientXY(matrix); - css = context.createLinearGradient( - xy[0], xy[1], xy[2], xy[3], - interpolation, spread - ); - - } else { - - context.save(); - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - css = context.createRadialGradient( - 0, 0, 0, 0, 0, 819.2, - interpolation, spread, focal - ); - - } - - for (let idx: number = 0; idx < stops.length; ++idx) { - - const color: ColorStopImpl = stops[idx]; - - let alpha: number = color.A; - if (color_transform) { - if (color_transform[7] !== 0) { - alpha = $Math.max(0, $Math.min(color.A + color_transform[7], 255)) | 0; - } - } - - css.addColorStop(color.ratio, $getInt32Array4( - color.R, color.G, color.B, alpha - )); - - } - - context.fillStyle = css; - context.fill(); - - if (type === "radial") { - context.restore(); - } - } - break; - - case Graphics.GRADIENT_STROKE: - { - if (options && "isPointInStroke" in context - && context.isPointInStroke(options.x, options.y) - ) { - return true; - } - - if (is_clip || options - || context instanceof CanvasRenderingContext2D // fixed logic - ) { - idx += 12; - continue; - } - - context.lineWidth = recode[idx++]; - context.lineCap = recode[idx++]; - context.lineJoin = recode[idx++]; - context.miterLimit = recode[idx++]; - - const type: GradientTypeImpl = recode[idx++]; - const stops: ColorStopImpl[] = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const spread: SpreadMethodImpl = recode[idx++]; - const interpolation: InterpolationMethodImpl = recode[idx++]; - const focal: number = recode[idx++]; - - let css: CanvasGradientToWebGL; - if (type === "linear") { - - const xy: Float32Array = $linearGradientXY(matrix); - css = context.createLinearGradient( - xy[0], xy[1], xy[2], xy[3], - interpolation, spread - ); - - } else { - - context.save(); - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - css = context.createRadialGradient( - 0, 0, 0, 0, 0, 819.2, - interpolation, spread, focal - ); - - } - - for (let idx = 0; idx < stops.length; ++idx) { - - const color: ColorStopImpl = stops[idx]; - - let alpha: number = color.A; - if (color_transform) { - if (color_transform[7] !== 0) { - alpha = $Math.max(0, $Math.min(color.A + color_transform[7], 255)) | 0; - } - } - - css.addColorStop(color.ratio, $getInt32Array4( - color.R, color.G, color.B, alpha - )); - - } - - context.strokeStyle = css; - context.stroke(); - - if (type === "radial") { - context.restore(); - } - - } - break; - - case Graphics.BITMAP_FILL: - { - if (options && "isPointInPath" in context - && context.isPointInPath(options.x, options.y) - ) { - return true; - } - - if (is_clip || options - || context instanceof CanvasRenderingContext2D // fixed logic - ) { - idx += 6; - continue; - } - - context.save(); - - const bitmapData: BitmapData = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const repeat: boolean = recode[idx++]; - const smooth: boolean = recode[idx++]; - - if (matrix) { - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } - - const texture: WebGLTexture | null = bitmapData.getTexture(); - if (!texture || !color_transform) { - break; - } - - context.imageSmoothingEnabled = smooth; - if (this._$bitmapId - || bitmapData.width === this._$xMax - this._$xMin - && bitmapData.height === this._$yMax - this._$yMin - ) { - - context.drawBitmap(texture); - - } else { - - context.fillStyle = context.createPattern( - texture, repeat, color_transform - ); - - context.fill(); - } - - // restore - context.restore(); - - context.imageSmoothingEnabled = false; - } - break; - - case Graphics.BITMAP_STROKE: - { - if (options && "isPointInStroke" in context - && context.isPointInStroke(options.x, options.y) - ) { - return true; - } - - if (is_clip || options - || context instanceof CanvasRenderingContext2D // fixed logic - ) { - idx += 9; - continue; - } - - context.save(); - - context.lineWidth = recode[idx++]; - context.lineCap = recode[idx++]; - context.lineJoin = recode[idx++]; - context.miterLimit = recode[idx++]; - - const bitmapData: BitmapData = recode[idx++]; - const matrix: Float32Array = recode[idx++]; - const repeat: boolean = recode[idx++]; - const smooth: boolean = recode[idx++]; - - if (matrix) { - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } - - const texture: WebGLTexture | null = bitmapData.getTexture(); - if (!texture || !color_transform) { - break; - } - - context.strokeStyle = context.createPattern( - texture, repeat, color_transform - ); - - context.imageSmoothingEnabled = smooth; - context.stroke(); - - // restore - context.restore(); - context.imageSmoothingEnabled = false; - - } - break; - - default: - break; - - } - } - - return false; + set buffer (buffer: Float32Array) + { + this._$buffer = buffer; + this.isConfirmed = true; + this._$isBeginning = true; + this._$maxAlpha = 1; } -} +} \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.test.ts b/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.test.ts new file mode 100644 index 00000000..4699fa34 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.test.ts @@ -0,0 +1,30 @@ +import { Graphics } from "../../Graphics"; +import { execute } from "./GraphicsCalcFillBoundsService"; +import { describe, expect, it } from "vitest"; + +describe("GraphicsCalcFillBoundsService.js test", () => +{ + it("execute test case1", () => + { + const graphics = new Graphics(); + + expect(graphics.xMin).toBe(Number.MAX_VALUE); + expect(graphics.yMin).toBe(Number.MAX_VALUE); + expect(graphics.xMax).toBe(-Number.MAX_VALUE); + expect(graphics.yMax).toBe(-Number.MAX_VALUE); + + execute(graphics, 10, 20); + + expect(graphics.xMin).toBe(10); + expect(graphics.yMin).toBe(20); + expect(graphics.xMax).toBe(10); + expect(graphics.yMax).toBe(20); + + execute(graphics, 0, 0); + + expect(graphics.xMin).toBe(0); + expect(graphics.yMin).toBe(0); + expect(graphics.xMax).toBe(10); + expect(graphics.yMax).toBe(20); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.ts b/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.ts new file mode 100644 index 00000000..70a30981 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsCalcFillBoundsService.ts @@ -0,0 +1,20 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 塗りの描画範囲を計算 + * Calculate the fill drawing range + * + * @param {Graphics} graphics + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = (graphics: Graphics, x: number = 0, y: number = 0): void => +{ + graphics.xMin = Math.min(graphics.xMin, x); + graphics.xMax = Math.max(graphics.xMax, x); + graphics.yMin = Math.min(graphics.yMin, y); + graphics.yMax = Math.max(graphics.yMax, y); +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsCalcLineBoundsService.ts b/packages/display/src/Graphics/service/GraphicsCalcLineBoundsService.ts new file mode 100644 index 00000000..b848d0a6 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsCalcLineBoundsService.ts @@ -0,0 +1,119 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 線の描画範囲を計算 + * Calculate the stroke drawing range + * + * @param {Graphics} graphics + * @param {number} x + * @param {number} y + * @param {number} position_x + * @param {number} position_y + * @param {number} line_width + * @param {string} caps + * @return {void} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + x: number = 0, y: number = 0, + position_x: number = 0, position_y: number = 0, + line_width: number = 0, caps: string = "none" +): void => { + + graphics.xMin = Math.min(graphics.xMin, Math.min(x, position_x)); + graphics.xMax = Math.max(graphics.xMax, Math.max(x, position_x)); + graphics.yMin = Math.min(graphics.yMin, Math.min(y, position_y)); + graphics.yMax = Math.max(graphics.yMax, Math.max(y, position_y)); + + // 始点 - 終点のベクトル + const dx = position_x - x; + const dy = position_y - y; + + // ベクトルの長さ + const length = Math.sqrt(dx * dx + dy * dy); + if (!length) { + return ; + } + + // 単位ベクトル + const ux = dx / length; + const uy = dy / length; + const vx = -uy; + const vy = ux; + + // 線の太さの半分(半径) + const r = line_width / 2; + + const xMin = Math.min( + x + vx * r, + x - vx * r, + position_x + vx * r, + position_x - vx * r + ); + + const xMax = Math.max( + x + vx * r, + x - vx * r, + position_x + vx * r, + position_x - vx * r + ); + + const yMin = Math.min( + y + vy * r, + y - vy * r, + position_y + vy * r, + position_y - vy * r + ); + + const yMax = Math.max( + y + vy * r, + y - vy * r, + position_y + vy * r, + position_y - vy * r + ); + + graphics.xMin = Math.min(graphics.xMin, xMin); + graphics.xMax = Math.max(graphics.xMax, xMax); + graphics.yMin = Math.min(graphics.yMin, yMin); + graphics.yMax = Math.max(graphics.yMax, yMax); + + // case + if (caps === "round" || caps === "square") { + // 矩形キャップの中心点(ux, uyは逆ベクトルなのでマイナスで反転) + const cxn = x + r * -ux; + const cyn = y + r * -uy; + + const xMin = Math.min( + cxn + r * ux + r * vx, + cxn - r * ux + r * vx, + cxn + r * ux - r * vx, + cxn - r * ux - r * vx + ); + + const xMax = Math.max( + cxn + r * ux + r * vx, + cxn - r * ux + r * vx, + cxn + r * ux - r * vx, + cxn - r * ux - r * vx + ); + const yMin = Math.min( + cyn + r * uy + r * vy, + cyn + r * uy - r * vy, + cyn - r * uy - r * vy, + cyn - r * uy + r * vy + ); + const yMax = Math.max( + cyn + r * uy + r * vy, + cyn + r * uy - r * vy, + cyn - r * uy - r * vy, + cyn - r * uy + r * vy + ); + + graphics.xMin = Math.min(graphics.xMin, xMin); + graphics.xMax = Math.max(graphics.xMax, xMax); + graphics.yMin = Math.min(graphics.yMin, yMin); + graphics.yMax = Math.max(graphics.yMax, yMax); + } +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawEllipseService.test.ts b/packages/display/src/Graphics/service/GraphicsDrawEllipseService.test.ts new file mode 100644 index 00000000..26b32fac --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawEllipseService.test.ts @@ -0,0 +1,65 @@ +import { Graphics } from "../../Graphics"; +import { execute } from "./GraphicsDrawEllipseService"; +import { describe, expect, it } from "vitest"; + +describe("GraphicsDrawEllipseService.js test", () => +{ + it("execute test case1", () => + { + const graphics = new Graphics(); + + expect(graphics.xMin).toBe(Number.MAX_VALUE); + expect(graphics.yMin).toBe(Number.MAX_VALUE); + expect(graphics.xMax).toBe(-Number.MAX_VALUE); + expect(graphics.yMax).toBe(-Number.MAX_VALUE); + + graphics.beginFill(); + execute(graphics, 10, 20, 60, 100); + + expect(graphics.xMin).toBe(10); + expect(graphics.yMin).toBe(20); + expect(graphics.xMax).toBe(70); + expect(graphics.yMax).toBe(120); + + const buffer = graphics.buffer; + expect(buffer.length).toBe(38); + expect(buffer[0]).toBe(9); + expect(buffer[1]).toBe(0); + expect(buffer[2]).toBe(40); + expect(buffer[3]).toBe(20); + expect(buffer[4]).toBe(3); + expect(buffer[5]).toBe(56.56854248046875); + expect(buffer[6]).toBe(20); + expect(buffer[7]).toBe(70); + expect(buffer[8]).toBe(42.38576126098633); + expect(buffer[9]).toBe(70); + expect(buffer[10]).toBe(70); + expect(buffer[11]).toBe(3); + expect(buffer[12]).toBe(70); + expect(buffer[13]).toBe(97.6142349243164); + expect(buffer[14]).toBe(56.56854248046875); + expect(buffer[15]).toBe(120); + expect(buffer[16]).toBe(40); + expect(buffer[17]).toBe(120); + expect(buffer[18]).toBe(3); + expect(buffer[19]).toBe(23.43145751953125); + expect(buffer[20]).toBe(120); + expect(buffer[21]).toBe(10); + expect(buffer[22]).toBe(97.6142349243164); + expect(buffer[23]).toBe(10); + expect(buffer[24]).toBe(70); + expect(buffer[25]).toBe(3); + expect(buffer[26]).toBe(10); + expect(buffer[27]).toBe(42.38576126098633); + expect(buffer[28]).toBe(23.43145751953125); + expect(buffer[29]).toBe(20); + expect(buffer[30]).toBe(40); + expect(buffer[31]).toBe(20); + expect(buffer[32]).toBe(5); + expect(buffer[33]).toBe(0); + expect(buffer[34]).toBe(0); + expect(buffer[35]).toBe(0); + expect(buffer[36]).toBe(255); + expect(buffer[37]).toBe(7); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawEllipseService.ts b/packages/display/src/Graphics/service/GraphicsDrawEllipseService.ts new file mode 100644 index 00000000..a274a11c --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawEllipseService.ts @@ -0,0 +1,46 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 楕円を描画データを登録 + * Register ellipse drawing data + * + * @param {Graphics} graphics + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {Graphics} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + x: number, y: number, + width: number, height: number +): Graphics => { + + x = +x || 0; + y = +y || 0; + width = +width || 0; + height = +height || 0; + + width = Math.round(width); + height = Math.round(height); + + const hw = width / 2; // half width + const hh = height / 2; // half height + const x0 = x + hw; + const y0 = y + hh; + const x1 = x + width; + const y1 = y + height; + const c = 4 / 3 * (Math.SQRT2 - 1); + const cw = c * hw; + const ch = c * hh; + + return graphics + .moveTo(x0, y) + .cubicCurveTo(x0 + cw, y, x1, y0 - ch, x1, y0) + .cubicCurveTo(x1, y0 + ch, x0 + cw, y1, x0, y1) + .cubicCurveTo(x0 - cw, y1, x, y0 + ch, x, y0) + .cubicCurveTo(x, y0 - ch, x0 - cw, y, x0, y ); +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawRectService.test.ts b/packages/display/src/Graphics/service/GraphicsDrawRectService.test.ts new file mode 100644 index 00000000..9c169f49 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawRectService.test.ts @@ -0,0 +1,50 @@ +import { Graphics } from "../../Graphics"; +import { execute } from "./GraphicsDrawRectService"; +import { describe, expect, it } from "vitest"; + +describe("GraphicsDrawRectService.js test", () => +{ + it("execute test case1", () => + { + const graphics = new Graphics(); + + expect(graphics.xMin).toBe(Number.MAX_VALUE); + expect(graphics.yMin).toBe(Number.MAX_VALUE); + expect(graphics.xMax).toBe(-Number.MAX_VALUE); + expect(graphics.yMax).toBe(-Number.MAX_VALUE); + + graphics.beginFill(); + execute(graphics, 10, 20, 60, 100); + + expect(graphics.xMin).toBe(10); + expect(graphics.yMin).toBe(20); + expect(graphics.xMax).toBe(70); + expect(graphics.yMax).toBe(120); + + const buffer = graphics.buffer; + expect(buffer.length).toBe(22); + + expect(buffer[0]).toBe(9); + expect(buffer[1]).toBe(0); + expect(buffer[2]).toBe(10); + expect(buffer[3]).toBe(20); + expect(buffer[4]).toBe(2); + expect(buffer[5]).toBe(10); + expect(buffer[6]).toBe(120); + expect(buffer[7]).toBe(2); + expect(buffer[8]).toBe(70); + expect(buffer[9]).toBe(120); + expect(buffer[10]).toBe(2); + expect(buffer[11]).toBe(70); + expect(buffer[12]).toBe(20); + expect(buffer[13]).toBe(2); + expect(buffer[14]).toBe(10); + expect(buffer[15]).toBe(20); + expect(buffer[16]).toBe(5); + expect(buffer[17]).toBe(0); + expect(buffer[18]).toBe(0); + expect(buffer[19]).toBe(0); + expect(buffer[20]).toBe(255); + expect(buffer[21]).toBe(7); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawRectService.ts b/packages/display/src/Graphics/service/GraphicsDrawRectService.ts new file mode 100644 index 00000000..d9d9e7dd --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawRectService.ts @@ -0,0 +1,38 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 矩形を描画データを登録 + * Register rectangle drawing data + * + * @param {Graphics} graphics + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {Graphics} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + x: number, y: number, + width: number, height: number +): Graphics => { + + // valid + x = +x || 0; + y = +y || 0; + + width = +width || 0; + height = +height || 0; + + const xMax = Math.round(x + width); + const yMax = Math.round(y + height); + + return graphics + .moveTo(x, y) + .lineTo(x, yMax) + .lineTo(xMax, yMax) + .lineTo(xMax, y) + .lineTo(x, y); +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.test.ts b/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.test.ts new file mode 100644 index 00000000..52af9b1f --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.test.ts @@ -0,0 +1,78 @@ +import { Graphics } from "../../Graphics"; +import { execute } from "./GraphicsDrawRoundRectService"; +import { describe, expect, it } from "vitest"; + +describe("GraphicsDrawRoundRectService.js test", () => +{ + it("execute test case1", () => + { + const graphics = new Graphics(); + + expect(graphics.xMin).toBe(Number.MAX_VALUE); + expect(graphics.yMin).toBe(Number.MAX_VALUE); + expect(graphics.xMax).toBe(-Number.MAX_VALUE); + expect(graphics.yMax).toBe(-Number.MAX_VALUE); + + graphics.beginFill(); + execute(graphics, 10, 20, 60, 100, 10, 20); + + expect(graphics.xMin).toBe(10); + expect(graphics.yMin).toBe(20); + expect(graphics.xMax).toBe(70); + expect(graphics.yMax).toBe(120); + + const buffer = graphics.buffer; + expect(buffer.length).toBe(50); + + expect(buffer[0]).toBe(9); + expect(buffer[1]).toBe(0); + expect(buffer[2]).toBe(15); + expect(buffer[3]).toBe(20); + expect(buffer[4]).toBe(2); + expect(buffer[5]).toBe(65); + expect(buffer[6]).toBe(20); + expect(buffer[7]).toBe(3); + expect(buffer[8]).toBe(67.76142120361328); + expect(buffer[9]).toBe(20); + expect(buffer[10]).toBe(70); + expect(buffer[11]).toBe(24.47715187072754); + expect(buffer[12]).toBe(70); + expect(buffer[13]).toBe(30); + expect(buffer[14]).toBe(2); + expect(buffer[15]).toBe(70); + expect(buffer[16]).toBe(110); + expect(buffer[17]).toBe(3); + expect(buffer[18]).toBe(70); + expect(buffer[19]).toBe(115.5228500366211); + expect(buffer[20]).toBe(67.76142120361328); + expect(buffer[21]).toBe(120); + expect(buffer[22]).toBe(65); + expect(buffer[23]).toBe(120); + expect(buffer[24]).toBe(2); + expect(buffer[25]).toBe(15); + expect(buffer[26]).toBe(120); + expect(buffer[27]).toBe(3); + expect(buffer[28]).toBe(12.23857593536377); + expect(buffer[29]).toBe(120); + expect(buffer[30]).toBe(10); + expect(buffer[31]).toBe(115.5228500366211); + expect(buffer[32]).toBe(10); + expect(buffer[33]).toBe(110); + expect(buffer[34]).toBe(2); + expect(buffer[35]).toBe(10); + expect(buffer[36]).toBe(30); + expect(buffer[37]).toBe(3); + expect(buffer[38]).toBe(10); + expect(buffer[39]).toBe(24.47715187072754); + expect(buffer[40]).toBe(12.23857593536377); + expect(buffer[41]).toBe(20); + expect(buffer[42]).toBe(15); + expect(buffer[43]).toBe(20); + expect(buffer[44]).toBe(5); + expect(buffer[45]).toBe(0); + expect(buffer[46]).toBe(0); + expect(buffer[47]).toBe(0); + expect(buffer[48]).toBe(255); + expect(buffer[49]).toBe(7); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.ts b/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.ts new file mode 100644 index 00000000..e2891eaa --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsDrawRoundRectService.ts @@ -0,0 +1,63 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 角丸矩形を描画データを登録 + * Register rounded rectangle drawing data + * + * @param {Graphics} graphics + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @param {number} ellipse_width + * @param {number} [ellipse_height=NaN] + * @return {Graphics} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + x: number, y: number, + width: number, height: number, + ellipse_width: number, ellipse_height: number = NaN +): Graphics => { + + x = +x || 0; + y = +y || 0; + + width = +width || 0; + height = +height || 0; + + ellipse_width = +ellipse_width || 0; + ellipse_height = +ellipse_height || ellipse_width; + + width = Math.round(width); + height = Math.round(height); + ellipse_width = Math.round(ellipse_width); + ellipse_height = Math.round(ellipse_height); + + const hew = ellipse_width / 2; + const heh = ellipse_height / 2; + const c = 4 / 3 * (Math.SQRT2 - 1); + const cw = c * hew; + const ch = c * heh; + + const dx0 = x + hew; + const dx1 = x + width; + const dx2 = dx1 - hew; + + const dy0 = y + heh; + const dy1 = y + height; + const dy2 = dy1 - heh; + + return graphics + .moveTo(dx0, y) + .lineTo(dx2, y) + .cubicCurveTo(dx2 + cw, y, dx1, dy0 - ch, dx1, dy0) + .lineTo(dx1, dy2) + .cubicCurveTo(dx1, dy2 + ch, dx2 + cw, dy1, dx2, dy1) + .lineTo(dx0, dy1) + .cubicCurveTo(dx0 - cw, dy1, x, dy2 + ch, x, dy2) + .lineTo(x, dy0) + .cubicCurveTo(x, dy0 - ch, dx0 - cw, y, dx0, y); +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsHitTestService.ts b/packages/display/src/Graphics/service/GraphicsHitTestService.ts new file mode 100644 index 00000000..af1c94d6 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsHitTestService.ts @@ -0,0 +1,109 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import { Graphics } from "../../Graphics"; + +/** + * @description 指定のパスが描画のヒット範囲か判定 + * Determines if the specified path is within the hit range for drawing + * + * @param {CanvasRenderingContext2D} context + * @param {array} recodes + * @param {IPlayerHitObject} hit_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + context: CanvasRenderingContext2D, + recodes: Float32Array, + hit_object: IPlayerHitObject +): boolean => { + + for (let idx = 0; idx < recodes.length; ) { + + switch (recodes[idx++]) { + + case Graphics.BEGIN_PATH: + context.beginPath(); + break; + + case Graphics.MOVE_TO: + context.moveTo(recodes[idx++], recodes[idx++]); + break; + + case Graphics.LINE_TO: + context.lineTo(recodes[idx++], recodes[idx++]); + break; + + case Graphics.CURVE_TO: + context.quadraticCurveTo( + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++] + ); + break; + + case Graphics.FILL_STYLE: + idx += 4; + continue; + + case Graphics.END_FILL: + if (context.isPointInPath(hit_object.x, hit_object.y)) { + return true; + } + break; + + case Graphics.STROKE_STYLE: + idx += 8; + continue; + + case Graphics.END_STROKE: + break; + + case Graphics.CLOSE_PATH: + context.closePath(); + break; + + case Graphics.CUBIC: + context.bezierCurveTo( + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++] + ); + break; + + case Graphics.ARC: + context.arc( + recodes[idx++], recodes[idx++], recodes[idx++], + 0, 2 * Math.PI + ); + break; + + case Graphics.GRADIENT_FILL: + if (context.isPointInPath(hit_object.x, hit_object.y)) { + return true; + } + idx += 6; + continue; + + case Graphics.GRADIENT_STROKE: + idx += 12; + continue; + + case Graphics.BITMAP_FILL: + if (context.isPointInPath(hit_object.x, hit_object.y)) { + return true; + } + idx += 6; + continue; + + case Graphics.BITMAP_STROKE: + idx += 9; + continue; + + default: + break; + + } + } + + return false; +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsMargePathService.ts b/packages/display/src/Graphics/service/GraphicsMargePathService.ts new file mode 100644 index 00000000..7389a6fc --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsMargePathService.ts @@ -0,0 +1,35 @@ +import type { Graphics } from "../../Graphics"; + +/** + * @description 追加のパスを描画データに追加 + * Add additional paths to the drawing data + * + * @param {boolean} has_fill_enabled + * @param {boolean} has_line_enabled + * @param {array | null} fills + * @param {array | null} lines + * @param {array} args + * @return {void} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + has_fill_enabled: boolean, + has_line_enabled: boolean, + fills: any[] | null, + lines: any[] | null, + ...args: any[] +): void => { + + // 確定フラグを解除 + graphics.isConfirmed = false; + + if (has_fill_enabled && fills) { + fills.push(...args); + } + + if (has_line_enabled && lines) { + lines.push(...args); + } +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/service/GraphicsToNumberArrayService.ts b/packages/display/src/Graphics/service/GraphicsToNumberArrayService.ts new file mode 100644 index 00000000..75f8b322 --- /dev/null +++ b/packages/display/src/Graphics/service/GraphicsToNumberArrayService.ts @@ -0,0 +1,368 @@ +import type { IGradientType } from "../../interface/IGradientType"; +import type { IColorStop } from "../../interface/IColorStop"; +import type { ISpreadMethod } from "../../interface/ISpreadMethod"; +import type { IInterpolationMethod } from "../../interface/IInterpolationMethod"; +import type { ICapsStyle } from "../../interface/ICapsStyle"; +import type { IJointStyle } from "../../interface/IJointStyle"; +import type { Matrix } from "@next2d/geom"; +import { $getArray } from "../../DisplayObjectUtil"; +import { Graphics } from "../../Graphics"; +import { BitmapData } from "../../BitmapData"; + +/** + * @description Graphicsのrecodesを解析して数値配列を生成します。 + * Parses Graphics recodes and generates a numerical array. + * + * @param {array} recodes + * @return {any[]} + * @method + * @public + */ +export const execute = (recodes : any[] | null): any[] => +{ + if (!recodes) { + return []; + } + + const array: number[] = $getArray(); + for (let idx = 0; idx < recodes.length;) { + + const type = recodes[idx++]; + array.push(type); + + switch (type) { + + case Graphics.BEGIN_PATH: + case Graphics.END_FILL: + case Graphics.END_STROKE: + case Graphics.CLOSE_PATH: + break; + + case Graphics.MOVE_TO: + case Graphics.LINE_TO: + array.push(recodes[idx++], recodes[idx++]); + break; + + case Graphics.CURVE_TO: + case Graphics.FILL_STYLE: + array.push( + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++] + ); + break; + + case Graphics.CUBIC: + array.push( + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++] + ); + break; + + case Graphics.STROKE_STYLE: + { + array.push(recodes[idx++]); + + const lineCap = recodes[idx++]; + switch (lineCap) { + + case "none": + array.push(0); + break; + + case "round": + array.push(1); + break; + + case "square": + array.push(2); + break; + + } + + const lineJoin = recodes[idx++]; + switch (lineJoin) { + + case "bevel": + array.push(0); + break; + + case "miter": + array.push(1); + break; + + case "round": + array.push(2); + break; + + } + + array.push( + recodes[idx++], // MITER LIMIT + recodes[idx++], recodes[idx++], + recodes[idx++], recodes[idx++] + ); + } + break; + + case Graphics.ARC: + array.push(recodes[idx++], recodes[idx++], recodes[idx++]); + break; + + case Graphics.GRADIENT_FILL: + { + const type: IGradientType = recodes[idx++]; + const stops: IColorStop[] = recodes[idx++]; + const matrix: Float32Array = recodes[idx++]; + const spread: ISpreadMethod = recodes[idx++]; + const interpolation: IInterpolationMethod = recodes[idx++]; + const focal: number = recodes[idx++]; + + array.push(type === "linear" ? 0 : 1); + + array.push(stops.length); + + // 昇順に並び替え + const sortStops = stops.sort((a, b) => a.ratio - b.ratio); + + for (let idx: number = 0; idx < sortStops.length; ++idx) { + const color = sortStops[idx]; + array.push( + color.ratio, + color.R, + color.G, + color.B, + color.A + ); + } + + array.push(...matrix); + + switch (spread) { + + case "reflect": + array.push(0); + break; + + case "repeat": + array.push(1); + break; + + default: + array.push(2); + break; + + } + + array.push( + interpolation === "linearRGB" ? 0 : 1 + ); + + array.push(focal); + } + break; + + case Graphics.GRADIENT_STROKE: + { + array.push(recodes[idx++]); + + const lineCap: ICapsStyle = recodes[idx++]; + switch (lineCap) { + + case "none": + array.push(0); + break; + + case "round": + array.push(1); + break; + + case "square": + array.push(2); + break; + + } + + const lineJoin: IJointStyle = recodes[idx++]; + switch (lineJoin) { + + case "bevel": + array.push(0); + break; + + case "miter": + array.push(1); + break; + + case "round": + array.push(2); + break; + + } + + // miterLimit + array.push(recodes[idx++]); + + const type: IGradientType = recodes[idx++]; + const stops: IColorStop[] = recodes[idx++]; + const matrix: Float32Array = recodes[idx++]; + const spread: ISpreadMethod = recodes[idx++]; + const interpolation: IInterpolationMethod = recodes[idx++]; + const focal: number = recodes[idx++]; + + array.push(type === "linear" ? 0 : 1); + + array.push(stops.length); + for (let idx: number = 0; idx < stops.length; ++idx) { + const color = stops[idx]; + array.push( + color.ratio, + color.R, + color.G, + color.B, + color.A + ); + } + + array.push(...matrix); + + switch (spread) { + + case "reflect": + array.push(0); + break; + + case "repeat": + array.push(1); + break; + + default: + array.push(2); + break; + + } + + array.push( + interpolation === "linearRGB" ? 0 : 1 + ); + + array.push(focal); + } + break; + + case Graphics.BITMAP_FILL: + { + const bitmapData: BitmapData = recodes[idx++]; + const buffer = bitmapData.buffer; + + if (!buffer) { + idx += 3; + break; + } + + array.push( + bitmapData.width, + bitmapData.height, + buffer.length + ); + + for (let idx = 0; idx < buffer.length; idx += 4096) { + array.push(...buffer.subarray(idx, idx + 4096)); + } + + const matrix: Matrix = recodes[idx++]; + if (matrix) { + array.push(...matrix.rawData); + } else { + array.push(1, 0, 0, 1, 0, 0); + } + + const repeat: boolean = recodes[idx++]; + array.push(repeat ? 1 : 0); + + const smooth: boolean = recodes[idx++]; + array.push(smooth ? 1 : 0); + } + break; + + case Graphics.BITMAP_STROKE: + { + array.push(recodes[idx++]); + + const lineCap: ICapsStyle = recodes[idx++]; + switch (lineCap) { + + case "none": + array.push(0); + break; + + case "round": + array.push(1); + break; + + case "square": + array.push(2); + break; + + } + + const lineJoin: IJointStyle = recodes[idx++]; + switch (lineJoin) { + + case "bevel": + array.push(0); + break; + + case "miter": + array.push(1); + break; + + case "round": + array.push(2); + break; + + } + + // MITER LIMIT + array.push(recodes[idx++]); + + const bitmapData: BitmapData = recodes[idx++]; + const buffer = bitmapData.buffer; + if (!buffer) { + idx += 3; + break; + } + + array.push( + bitmapData.width, + bitmapData.height, + buffer.length + ); + + for (let idx = 0; idx < buffer.length; idx += 4096) { + array.push(...buffer.subarray(idx, idx + 4096)); + } + + const matrix: Float32Array = recodes[idx++]; + if (matrix) { + array.push(...matrix); + } else { + array.push(1, 0, 0, 1, 0, 0); + } + + const repeat: boolean = recodes[idx++]; + array.push(repeat ? 1 : 0); + + const smooth: boolean = recodes[idx++]; + array.push(smooth ? 1 : 0); + } + break; + + default: + break; + + } + } + + return array; +}; \ No newline at end of file diff --git a/packages/display/src/Graphics/usecase/GraphicsCalcBoundsUseCase.ts b/packages/display/src/Graphics/usecase/GraphicsCalcBoundsUseCase.ts new file mode 100644 index 00000000..1339aa3c --- /dev/null +++ b/packages/display/src/Graphics/usecase/GraphicsCalcBoundsUseCase.ts @@ -0,0 +1,47 @@ +import type { Graphics } from "../../Graphics"; +import { execute as graphicsCalcFillBoundsService } from "../service/GraphicsCalcFillBoundsService"; +import { execute as graphicsCalcLineBoundsService } from "../service/GraphicsCalcLineBoundsService"; + +/** + * @description 描画範囲のバウンディングボックスを計算 + * Calculate the bounding box of the drawing range + * + * @param {Graphics} graphics + * @param {boolean} has_line_enabled + * @param {number} position_x + * @param {number} position_y + * @param {number} line_width + * @param {string} caps + * @param {number[]} args + * @return {void} + * @method + * @protected + */ +export const execute = ( + graphics: Graphics, + has_line_enabled: boolean, + position_x: number = 0, position_y: number = 0, + line_width: number = 0, caps: string = "none", + ...args: number[] +): void => { + + for (let idx = 0; idx < args.length;) { + const x = args[idx++]; + const y = args[idx++]; + + graphicsCalcFillBoundsService(graphics, x, y); + + if (has_line_enabled) { + graphicsCalcLineBoundsService( + graphics, x, y, + position_x, position_y, + line_width, caps + ); + graphicsCalcLineBoundsService( + graphics, position_x, position_y, + x, y, + line_width, caps + ); + } + } +}; \ No newline at end of file diff --git a/packages/display/src/GraphicsBitmapFill.ts b/packages/display/src/GraphicsBitmapFill.ts index b0969972..90550e6b 100644 --- a/packages/display/src/GraphicsBitmapFill.ts +++ b/packages/display/src/GraphicsBitmapFill.ts @@ -1,16 +1,15 @@ import type { BitmapData } from "./BitmapData"; import type { Matrix } from "@next2d/geom"; -import { $getArray } from "@next2d/share"; +import { $getArray } from "./DisplayObjectUtil"; /** - * ビットマップ塗りを定義します。ビットマップは、スムージング、繰り返し、 - * またはタイリング表示して領域を塗りつぶしたり、変換マトリックスを使用して操作できます。 + * @description ビットマップ塗りを定義します。ビットマップは、スムージング、繰り返し、 + * またはタイリング表示して領域を塗りつぶしたり、変換マトリックスを使用して操作できます。 * - * Defines a bitmap fill. The bitmap can be smoothed, - * repeated or tiled to fill the area; or manipulated using a transformation matrix. + * Defines a bitmap fill. The bitmap can be smoothed, + * repeated or tiled to fill the area; or manipulated using a transformation matrix. * * @class - * @memberOf next2d.display * @private */ export class GraphicsBitmapFill @@ -106,7 +105,7 @@ export class GraphicsBitmapFill * @method * @public */ - toArray () + toArray (): any[] { return $getArray( this._$bitmapData, diff --git a/packages/display/src/GraphicsGradientFill.ts b/packages/display/src/GraphicsGradientFill.ts index 38243361..ab5db408 100644 --- a/packages/display/src/GraphicsGradientFill.ts +++ b/packages/display/src/GraphicsGradientFill.ts @@ -1,37 +1,32 @@ import type { Matrix } from "@next2d/geom"; -import type { - ColorStopImpl, - GradientTypeImpl, - SpreadMethodImpl, - InterpolationMethodImpl -} from "@next2d/interface"; +import type { IGradientType } from "./interface/IGradientType"; +import type { ISpreadMethod } from "./interface/ISpreadMethod"; +import type { IInterpolationMethod } from "./interface/IInterpolationMethod"; +import type { IColorStop } from "./interface/IColorStop"; import { - $colorStringToInt, $getArray, - $intToRGBA, - $Math, - $MATRIX_ARRAY_IDENTITY -} from "@next2d/share"; + $MATRIX_ARRAY_IDENTITY, + $convertColorStringToNumber +} from "./DisplayObjectUtil"; /** - * グラデーション塗りを定義します。 - * Defines a gradient fill. + * @description グラデーション塗りを定義します。 + * Defines a gradient fill. * * @class - * @memberOf next2d.display * @private */ export class GraphicsGradientFill { - private readonly _$type: GradientTypeImpl; + private readonly _$type: IGradientType; private readonly _$colors: number[] | string[]; private readonly _$alphas: number[]; private readonly _$ratios: number[]; private readonly _$matrix: Matrix | null; - private readonly _$spreadMethod: SpreadMethodImpl; - private readonly _$interpolationMethod: InterpolationMethodImpl; + private readonly _$spreadMethod: ISpreadMethod; + private readonly _$interpolationMethod: IInterpolationMethod; private readonly _$focalPointRatio: number; - private readonly _$colorStops: ColorStopImpl[]; + private readonly _$colorStops: IColorStop[]; /** * @param {string} [type=GradientType.LINEAR] @@ -47,13 +42,13 @@ export class GraphicsGradientFill * @private */ constructor ( - type: GradientTypeImpl, + type: IGradientType, colors: number[] | string[], alphas: number[], ratios: number[], matrix: Matrix | null = null, - spread_method: SpreadMethodImpl = "pad", - interpolation_method: InterpolationMethodImpl = "rgb", + spread_method: ISpreadMethod = "pad", + interpolation_method: IInterpolationMethod = "rgb", focal_point_ratio: number = 0 ) { @@ -98,7 +93,7 @@ export class GraphicsGradientFill * @description Matrix クラスで定義される変換マトリックスです。 * A transformation matrix as defined by the Matrix class. * - * @type {Matrix} + * @type {Matrix | null} * @default null * @private */ @@ -147,8 +142,7 @@ export class GraphicsGradientFill * @description 分配された色の情報を統合して配列で返却 * Integrate the distributed color information and return it in an array. * - * @member {array} - * @default null + * @member {array} * @readonly * @public */ @@ -156,8 +150,8 @@ export class GraphicsGradientFill { if (!this._$colorStops.length) { - const length: number = $Math.min( - $Math.min(this._$alphas.length, this._$colors.length), + const length: number = Math.min( + Math.min(this._$alphas.length, this._$colors.length), this._$ratios.length ); @@ -166,17 +160,15 @@ export class GraphicsGradientFill const value: number | string = this._$colors[idx]; const color = typeof value === "string" - ? $colorStringToInt(value) + ? $convertColorStringToNumber(value) : value; - const object = $intToRGBA(color, this._$alphas[idx]); - this._$colorStops[idx] = { "ratio": this._$ratios[idx] / 255, - "R": object.R, - "G": object.G, - "B": object.B, - "A": object.A + "R": color >>> 16 & 0xff, + "G": color >>> 8 & 0xff, + "B": color & 0xff, + "A": this._$alphas[idx] * 255 }; } @@ -186,20 +178,20 @@ export class GraphicsGradientFill } /** - * @description このクラスのもつパラメーターをArrayで返却する - * Return the parameters of this class as an Array. + * @description このクラスのもつパラメーターを配列で返却 + * Return the parameters of this class as an array. * * @return {array} * @method * @public */ - toArray (): any[] + toArray (): Array { return $getArray( this._$type, this.colorStops, this._$matrix - ? this._$matrix._$matrix + ? this._$matrix.rawData : $MATRIX_ARRAY_IDENTITY, this._$spreadMethod, this._$interpolationMethod, @@ -209,9 +201,7 @@ export class GraphicsGradientFill /** * @description 新しい GraphicsGradientFill オブジェクトとして、クローンを返します。 - * 含まれるオブジェクトはまったく同じコピーになります。 * Returns a clone as a new GraphicsGradientFill object. - * The contained object will be an exact copy. * * @return {GraphicsGradientFill} * @method diff --git a/packages/display/src/InteractiveObject.ts b/packages/display/src/InteractiveObject.ts index 4fe499e9..9508c81a 100644 --- a/packages/display/src/InteractiveObject.ts +++ b/packages/display/src/InteractiveObject.ts @@ -1,11 +1,11 @@ import { DisplayObject } from "./DisplayObject"; /** - * InteractiveObject クラスは、マウス、キーボードまたは他のユーザー入力デバイスを使用して - * ユーザーが操作できるすべての表示オブジェクトの抽象基本クラスです。 + * @description InteractiveObject クラスは、マウス、キーボードまたは他のユーザー入力デバイスを使用して + * ユーザーが操作できるすべての表示オブジェクトの抽象基本クラスです。 * - * The InteractiveObject class is the abstract base class for all display objects - * with which the user can interact, using the mouse, keyboard, or other user input device. + * The InteractiveObject class is the abstract base class for all display objects + * with which the user can interact, using the mouse, keyboard, or other user input device. * * @class * @memberOf next2d.display @@ -13,40 +13,35 @@ import { DisplayObject } from "./DisplayObject"; */ export class InteractiveObject extends DisplayObject { - protected _$mouseEnabled: boolean; - /** - * @constructor + * @description このオブジェクトでマウスまたはその他のユーザー入力メッセージを受け取るかどうかを指定します。 + * Specifies whether this object receives mouse, or other user input, messages. + * + * @member {boolean} + * @default true * @public */ - constructor() - { - super(); - - /** - * @type {boolean} - * @default true - * @private - */ - this._$mouseEnabled = true; - } + public mouseEnabled: boolean; /** - * @description このオブジェクトでマウスまたはその他のユーザー入力メッセージを - * 受け取るかどうかを指定します。 - * Specifies whether this object receives mouse, - * or other user input, messages. + * @description InteractiveObject の機能を所持しているかを返却 + * Returns whether InteractiveObject functions are possessed. * - * @member {boolean} - * @default true + * @type {boolean} + * @readonly * @public */ - get mouseEnabled (): boolean - { - return this._$mouseEnabled; - } - set mouseEnabled (mouse_enabled: boolean) + public readonly isInteractive: boolean; + + /** + * @constructor + * @public + */ + constructor() { - this._$mouseEnabled = !!mouse_enabled; + super(); + + this.isInteractive = true; + this.mouseEnabled = true; } -} +} \ No newline at end of file diff --git a/packages/display/src/Loader.ts b/packages/display/src/Loader.ts index 4e32d8f9..39be07da 100644 --- a/packages/display/src/Loader.ts +++ b/packages/display/src/Loader.ts @@ -1,115 +1,50 @@ -import { DisplayObjectContainer } from "./DisplayObjectContainer"; +import type { IParent } from "./interface/IParent"; +import type { Sprite } from "./Sprite"; +import type { IAnimationToolData } from "./interface/IAnimationToolData"; +import type { IAnimationToolDataZlib } from "./interface/IAnimationToolDataZlib"; +import type { MovieClip } from "./MovieClip"; +import type { URLRequest } from "@next2d/net"; import { LoaderInfo } from "./LoaderInfo"; -import { MovieClip } from "./MovieClip"; -import { URLRequest } from "@next2d/net"; -import { $getMap } from "@next2d/share"; -import { - IOErrorEvent, - Event, - ProgressEvent as Next2DProgressEvent, - HTTPStatusEvent -} from "@next2d/events"; -import type { Player } from "@next2d/core"; -import type { - NoCodeDataZlibImpl, - NoCodeDataImpl, - ParentImpl, - MovieClipCharacterImpl -} from "@next2d/interface"; -import { - $ajax, - $headerToArray, - $unzipQueues, - $updateUnzipWorkerStatus, - $getUnzipWorker, - $currentPlayer, - $useUnzipWorker -} from "@next2d/util"; +import { execute as loaderLoadJsonUseCase } from "./Loader/usecase/LoaderLoadJsonUseCase"; +import { execute as loaderLoadUseCase } from "./Loader/usecase/LoaderLoadUseCase"; /** - * Loader クラスは、JSON ファイルまたはイメージ(JPEG、PNG、または GIF)ファイルを読み込むために使用します。 - * 読み込みを開始するには load() メソッドを使用します。 - * 読み込まれた表示オブジェクトは Loader オブジェクトの子として追加されます。 + * @description Loader クラスは、JSON ファイルを読み込むために使用します。 + * 外部からの読み込みを開始するには load() メソッドを使用し、ローカルのJSONを読み込むには loadJSON() メソッドを使用します。 * - * The Loader class is used to load JSON files or image (JPEG, PNG, or GIF) files. - * Use the load() method to initiate loading. - * The loaded display object is added as a child of the Loader object. + * The Loader class is used to load JSON files. + * To start loading from an external source, use the load() method, and to load local JSON, use the loadJSON() method. * * @class * @memberOf next2d.display - * @extends DisplayObjectContainer */ -export class Loader extends DisplayObjectContainer +export class Loader { /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {LoaderInfo} - * @private - */ - this._$loaderInfo = new LoaderInfo(); - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Loader] - * @method - * @static - */ - static toString (): string - { - return "[class Loader]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description 読み込まれているオブジェクトに対応する LoaderInfo オブジェクトを返します。 + * Returns a LoaderInfo object corresponding to the object being loaded. * - * @return {string} - * @default next2d.display.Loader - * @const - * @static + * @type {LoaderInfo} + * @readonly + * @public */ - static get namespace (): string - { - return "next2d.display.Loader"; - } + public readonly contentLoaderInfo: LoaderInfo; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object Loader] - * @method + * @type {null} + * @readonly * @public */ - toString (): string - { - return "[object Loader]"; - } + public readonly root: null; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.Loader - * @const + * @constructor * @public */ - get namespace (): string + constructor () { - return "next2d.display.Loader"; + this.root = null; + this.contentLoaderInfo = new LoaderInfo(); } /** @@ -120,22 +55,24 @@ export class Loader extends DisplayObjectContainer * @readonly * @public */ - get content (): ParentImpl | null + get content (): IParent | null { - return this._$loaderInfo ? this._$loaderInfo.content : null; + return this.contentLoaderInfo.content; } /** - * @description 読み込まれているオブジェクトに対応する LoaderInfo オブジェクトを返します。 - * Returns a LoaderInfo object corresponding to the object being loaded. + * @description この表示オブジェクトが属するファイルの読み込み情報を含む LoaderInfo オブジェクトを返します。 + * Returns a LoaderInfo object containing information + * about loading the file to which this display object belongs. * - * @member {LoaderInfo} + * @member {LoaderInfo} + * @default null * @readonly * @public */ - get contentLoaderInfo (): LoaderInfo + get loaderInfo (): LoaderInfo { - return this._$loaderInfo as NonNullable; + return this.contentLoaderInfo; } /** @@ -146,297 +83,33 @@ export class Loader extends DisplayObjectContainer * are loaded into the content property in the same way with loadImage. * * @param {URLRequest} request - * @returns {void} + * @returns {Promise} * @method * @public */ - load (request: URLRequest): void + async load (request: URLRequest): Promise { - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo) { - return ; + if (request.responseDataFormat !== "json") { + throw new Error("The only format that can be loaded by this function is `json` format."); } - loaderInfo.url = request.url; - loaderInfo.format = request.responseDataFormat; + this.contentLoaderInfo.url = request.url; + this.contentLoaderInfo.format = request.responseDataFormat; - $ajax({ - "format": request.responseDataFormat, - "url": request.url, - "method": request.method, - "data": request.data, - "headers": request.headers, - "withCredentials": request.withCredentials, - "event": { - "loadstart": (event: ProgressEvent) => - { - this._$loadstart(event); - }, - "progress": (event: ProgressEvent) => - { - this._$progress(event); - }, - "loadend": (event: ProgressEvent) => - { - this._$loadend(event); - } - } - }); + await loaderLoadUseCase(this, request); } /** - * @description NoCodeToolのJSONを直接読み込む - * Read JSON directly from NoCodeTool + * @description AnimationToolで書き出したJSONオブジェクトを直接読み込む + * Load the JSON object exported with AnimationTool directly. * * @param {object} json * @return {void} * @method * @public */ - loadJSON (json: any): void + async loadJSON (json: IAnimationToolData | IAnimationToolDataZlib): Promise { - if (json.type === "zlib") { - - if ($useUnzipWorker()) { - - $unzipQueues.push(json); - - return ; - } - - $updateUnzipWorkerStatus(true); - - const unzipWorker: Worker = $getUnzipWorker(); - - const buffer: Uint8Array = new Uint8Array(json.buffer); - unzipWorker.onmessage = (event: MessageEvent) => - { - this._$unzipHandler(event); - }; - unzipWorker.postMessage(buffer, [buffer.buffer]); - - } else { - - this._$build(json); - - } - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$loadend (event: ProgressEvent): void - { - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo) { - return ; - } - - // set - loaderInfo.bytesLoaded = event.loaded; - loaderInfo.bytesTotal = event.total; - - // progress event - if (loaderInfo.willTrigger(Next2DProgressEvent.PROGRESS)) { - loaderInfo.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, - false, false, event.loaded, event.total - )); - } - - const target: any = event.target; - - // http status event - if (loaderInfo.willTrigger(HTTPStatusEvent.HTTP_STATUS)) { - - const responseHeaders = $headerToArray( - target.getAllResponseHeaders() - ); - - loaderInfo.dispatchEvent(new HTTPStatusEvent( - HTTPStatusEvent.HTTP_STATUS, false, false, - target.status, target.responseURL, - responseHeaders - )); - } - - if (199 < target.status && 400 > target.status) { - - if (loaderInfo.format === "json") { - - this.loadJSON(target.response); - - } else { - - if (loaderInfo.willTrigger(IOErrorEvent.IO_ERROR)) { - loaderInfo.dispatchEvent(new IOErrorEvent( - IOErrorEvent.IO_ERROR, false, false, - "LoaderInfo format is `json`" - )); - } - - } - - } else { - - if (loaderInfo.willTrigger(IOErrorEvent.IO_ERROR)) { - loaderInfo.dispatchEvent(new IOErrorEvent( - IOErrorEvent.IO_ERROR, false, false, - target.statusText - )); - } - - } - - } - - /** - * @param {MessageEvent} event - * @return {void} - * @method - * @private - */ - _$unzipHandler (event: MessageEvent): void - { - this._$build(event.data); - - if ($unzipQueues.length) { - - const object: NoCodeDataZlibImpl | void = $unzipQueues.pop(); - if (!object) { - return ; - } - - const buffer: Uint8Array = new Uint8Array(object.buffer); - - const unzipWorker = $getUnzipWorker(); - - unzipWorker.onmessage = (event: MessageEvent) => - { - this._$unzipHandler(event); - }; - unzipWorker.postMessage(buffer, [buffer.buffer]); - - } else { - - $updateUnzipWorkerStatus(false); - - } - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$loadstart (event: ProgressEvent): void - { - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo) { - return ; - } - - loaderInfo.bytesLoaded = event.loaded; - loaderInfo.bytesTotal = event.total; - - if (loaderInfo.willTrigger(Event.OPEN)) { - loaderInfo.dispatchEvent(new Event(Event.OPEN)); - } - - if (loaderInfo.willTrigger(Next2DProgressEvent.PROGRESS)) { - loaderInfo.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, - false, false, event.loaded, event.total - )); - } - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$progress (event: ProgressEvent): void - { - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo) { - return ; - } - - // set - loaderInfo.bytesLoaded = event.loaded; - loaderInfo.bytesTotal = event.total; - - // progress event - if (loaderInfo.willTrigger(Next2DProgressEvent.PROGRESS)) { - loaderInfo.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, - false, false, event.loaded, event.total - )); - } - } - - /** - * @param {object} object - * @return {void} - * @method - * @private - */ - _$build (object: NoCodeDataImpl): void - { - const loaderInfo: LoaderInfo | null = this._$loaderInfo; - if (!loaderInfo) { - return ; - } - - const symbols: Map = $getMap(); - if (object.symbols.length) { - for (let idx: number = 0; idx < object.symbols.length; ++idx) { - - const values: any[] = object.symbols[idx]; - - symbols.set(values[0], values[1]); - } - } - - loaderInfo._$data = { - "stage": object.stage, - "characters": object.characters, - "symbols": symbols - }; - - // setup - loaderInfo._$content = new MovieClip(); - - // build root - const root: MovieClipCharacterImpl = object.characters[0]; - loaderInfo._$content._$build({ - "characterId": 0, - "clipDepth": 0, - "depth": 0, - "endFrame": root.controller.length, - "startFrame": 1 - }, this); - - // fixed logic - loaderInfo._$content._$parent = null; - this.addChild(loaderInfo._$content); - - // fixed logic - loaderInfo._$content._$added = false; - loaderInfo._$content._$addedStage = false; - - // to event - const player: Player = $currentPlayer(); - player._$loaders.push(loaderInfo); - - if (player._$loadStatus === 1) { // LOAD_START - player._$loadStatus = 2; // LOAD_END - } + await loaderLoadJsonUseCase(this, json); } } diff --git a/packages/display/src/Loader/service/LoaderBuildService.test.ts b/packages/display/src/Loader/service/LoaderBuildService.test.ts new file mode 100644 index 00000000..7b19a644 --- /dev/null +++ b/packages/display/src/Loader/service/LoaderBuildService.test.ts @@ -0,0 +1,44 @@ +import type { IAnimationToolData } from "../../interface/IAnimationToolData"; +import { Loader } from "../../Loader"; +import { Event } from "@next2d/events"; +import { execute } from "./LoaderBuildService"; +import { describe, expect, it, vi } from "vitest"; + +describe("LoaderBuildService.js test", () => +{ + it("execute test case1", async () => + { + const loader = new Loader(); + + let openState = ""; + loader + .contentLoaderInfo + .addEventListener(Event.COMPLETE, (event: Event): void => + { + openState = event.type; + }); + + expect(openState).toBe(""); + + // mock save data + const object: IAnimationToolData = { + "type": "json", + "stage": { + "width": 240, + "height": 240, + "fps": 60, + "bgColor": "#ffffff" + }, + "characters": [ + { + "controller": [1,2,3] + } + ], + "symbols": [] + }; + + await execute(loader, object); + + expect(openState).toBe(Event.COMPLETE); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Loader/service/LoaderBuildService.ts b/packages/display/src/Loader/service/LoaderBuildService.ts new file mode 100644 index 00000000..39c4cc09 --- /dev/null +++ b/packages/display/src/Loader/service/LoaderBuildService.ts @@ -0,0 +1,58 @@ +import type { IAnimationToolData } from "../../interface/IAnimationToolData"; +import type { Loader } from "../../Loader"; +import type { IMovieClipCharacter } from "../../interface/IMovieClipCharacter"; +import { Event } from "@next2d/events"; +import { MovieClip } from "../../MovieClip"; +import { execute as displayObjectBaseBuildService } from "../../DisplayObject/service/DisplayObjectBaseBuildService"; + +/** + * @description 読み込んだJSONオブジェクトからrootのMovieClipを構築 + * Build the root MovieClip from the loaded JSON object + * + * @param {Loader} loader + * @param {object} object + * @return {Promise} + * @method + * @protected + */ +export const execute = async (loader: Loader, object: IAnimationToolData): Promise => +{ + const symbols: Map = new Map(); + if (object.symbols.length) { + for (let idx: number = 0; idx < object.symbols.length; ++idx) { + + const values: any[] = object.symbols[idx]; + + symbols.set(values[0], values[1]); + } + } + + const loaderInfo = loader.contentLoaderInfo; + loaderInfo.data = { + "stage": object.stage, + "characters": object.characters, + "symbols": symbols + }; + + // build root content + const movieClip = new MovieClip(); + + const character = object.characters[0] as IMovieClipCharacter; + displayObjectBaseBuildService(movieClip, -1, { + "characterId": 0, + "name": "main", + "clipDepth": 0, + "depth": 0, + "endFrame": character.controller.length, + "startFrame": 1 + }, loader); + movieClip.$sync(character, loaderInfo); + + movieClip.parent = null; + loaderInfo.content = movieClip; + + // dispatch complete event + if (loaderInfo.willTrigger(Event.COMPLETE)) { + loaderInfo.dispatchEvent(new Event(Event.COMPLETE)); + } +}; \ No newline at end of file diff --git a/packages/display/src/Loader/service/LoaderLoadStartEventService.test.ts b/packages/display/src/Loader/service/LoaderLoadStartEventService.test.ts new file mode 100644 index 00000000..7da49040 --- /dev/null +++ b/packages/display/src/Loader/service/LoaderLoadStartEventService.test.ts @@ -0,0 +1,74 @@ +import { Loader } from "../../Loader"; +import { execute } from "./LoaderLoadStartEventService"; +import { + Event, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundLoadStartEventService.js test", () => +{ + it("execute test case1", () => + { + const loader = new Loader(); + + let openState = ""; + loader + .contentLoaderInfo + .addEventListener(Event.OPEN, (event: Event): void => + { + openState = event.type; + }); + + expect(openState).toBe(""); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(loader.contentLoaderInfo, new MockEvent()); + + expect(openState).toBe(Event.OPEN); + }); + + it("execute test case2", () => + { + const loader = new Loader(); + + let openState = ""; + let loaded = 0; + let total = 0; + loader + .contentLoaderInfo + .addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(loader.contentLoaderInfo, new MockEvent()); + + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + expect(loaded).toBe(1); + expect(total).toBe(10); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Loader/service/LoaderLoadStartEventService.ts b/packages/display/src/Loader/service/LoaderLoadStartEventService.ts new file mode 100644 index 00000000..1232c48a --- /dev/null +++ b/packages/display/src/Loader/service/LoaderLoadStartEventService.ts @@ -0,0 +1,29 @@ +import type { LoaderInfo } from "../../LoaderInfo"; +import { + Event, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; + +/** + * @description 外部JSONの読み込みが開始イベントの実行関数 + * Execution function of the external JSON loading start event + * + * @param {LoaderInfo} loader_info + * @param {ProgressEvent} event + * @return {void} + * @method + * @public + */ +export const execute = (loader_info: LoaderInfo, event: ProgressEvent): void => +{ + if (loader_info.willTrigger(Event.OPEN)) { + loader_info.dispatchEvent(new Event(Event.OPEN)); + } + + if (loader_info.willTrigger(Next2DProgressEvent.PROGRESS)) { + loader_info.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } +}; \ No newline at end of file diff --git a/packages/display/src/Loader/service/LoaderProgressEventService.test.ts b/packages/display/src/Loader/service/LoaderProgressEventService.test.ts new file mode 100644 index 00000000..679d2b2b --- /dev/null +++ b/packages/display/src/Loader/service/LoaderProgressEventService.test.ts @@ -0,0 +1,44 @@ +import { Loader } from "../../Loader"; +import { execute } from "./LoaderProgressEventService"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("LoaderProgressEventService.js test", () => +{ + it("execute test case1", () => + { + const loader = new Loader(); + + let openState = ""; + let loaded = 0; + let total = 0; + loader + .contentLoaderInfo + .addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(loader.contentLoaderInfo, new MockEvent()); + + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + expect(loaded).toBe(1); + expect(total).toBe(10); + + }); +}); \ No newline at end of file diff --git a/packages/display/src/Loader/service/LoaderProgressEventService.ts b/packages/display/src/Loader/service/LoaderProgressEventService.ts new file mode 100644 index 00000000..949315a7 --- /dev/null +++ b/packages/display/src/Loader/service/LoaderProgressEventService.ts @@ -0,0 +1,22 @@ +import type { LoaderInfo } from "../../LoaderInfo"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; + +/** + * @description 外部JSONのローディング中のイベント実行関数 + * Execution function of the external JSON loading event + * + * @param {Sound} loader_info + * @param {ProgressEvent} event + * @return {void} + * @method + * @public + */ +export const execute = (loader_info: LoaderInfo, event: ProgressEvent): void => +{ + if (loader_info.willTrigger(Next2DProgressEvent.PROGRESS)) { + loader_info.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } +}; \ No newline at end of file diff --git a/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.test.ts b/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.test.ts new file mode 100644 index 00000000..eba3ff2c --- /dev/null +++ b/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.test.ts @@ -0,0 +1,108 @@ +import { Loader } from "../../Loader"; +import { execute } from "./LoaderLoadEndEventUseCase"; +import { + Event, + IOErrorEvent, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("LoaderLoadEndEventUseCase.js test", () => +{ + it("execute test case1", async () => + { + const loader = new Loader(); + + let openState = ""; + let loaded = 0; + let total = 0; + loader + .contentLoaderInfo + .addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + let completeState = ""; + loader + .contentLoaderInfo + .addEventListener(Event.COMPLETE, (event: Event): void => + { + completeState = event.type; + }); + + expect(completeState).toBe(""); + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + const object = { + "type": "json", + "stage": { + "width": 240, + "height": 240, + "fps": 60, + "bgColor": "#ffffff" + }, + "characters": [ + { + "controller": [1,2,3] + } + ], + "symbols": [] + }; + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "target": { + "status": 200, + "statusText": "OK", + "response": object + }, + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + await execute(loader, new MockEvent()); + + expect(completeState).toBe(Event.COMPLETE); + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + }); + + it("execute test case2", () => + { + const loader = new Loader(); + + let openState = ""; + loader + .contentLoaderInfo + .addEventListener(IOErrorEvent.IO_ERROR, (event: IOErrorEvent): void => + { + openState = event.type; + }); + + expect(openState).toBe(""); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "target": { + "status": 404, + "statusText": "Not Found" + }, + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(loader, new MockEvent()); + + expect(openState).toBe(IOErrorEvent.IO_ERROR); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.ts b/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.ts new file mode 100644 index 00000000..85457587 --- /dev/null +++ b/packages/display/src/Loader/usecase/LoaderLoadEndEventUseCase.ts @@ -0,0 +1,63 @@ +import { Loader } from "../../Loader"; +import { $headerStringToArray } from "../../DisplayObjectUtil"; +import { execute as loaderLoadJsonUseCase } from "./LoaderLoadJsonUseCase"; +import { + Event, + ProgressEvent as Next2DProgressEvent, + IOErrorEvent, + HTTPStatusEvent +} from "@next2d/events"; + +/** + * @description 外部JSONのローディング完了イベントの実行関数 + * Execution function for external JSON loading completion event + * + * @param {Loader} loader + * @param {ProgressEvent} event + * @return {Promise} + * @method + * @public + */ +export const execute = async (loader: Loader, event: ProgressEvent): Promise => +{ + const target = event.target as XMLHttpRequest; + if (!target) { + return ; + } + + const loaderInfo = loader.contentLoaderInfo; + if (loaderInfo.willTrigger(Next2DProgressEvent.PROGRESS)) { + loaderInfo.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } + + // http status event + if (loaderInfo.willTrigger(HTTPStatusEvent.HTTP_STATUS)) { + + const responseHeaders = $headerStringToArray( + target.getAllResponseHeaders() + ); + + loaderInfo.dispatchEvent(new HTTPStatusEvent( + HTTPStatusEvent.HTTP_STATUS, false, + target.status, target.responseURL, + responseHeaders + )); + } + + if (199 < target.status && 400 > target.status) { + await loaderLoadJsonUseCase(loader, target.response); + + if (loaderInfo.willTrigger(Event.COMPLETE)) { + loaderInfo.dispatchEvent(new Event(Event.COMPLETE)); + } + } else { + if (loaderInfo.willTrigger(IOErrorEvent.IO_ERROR)) { + loaderInfo.dispatchEvent(new IOErrorEvent( + IOErrorEvent.IO_ERROR, false, target.statusText + )); + } + } +}; \ No newline at end of file diff --git a/packages/display/src/Loader/usecase/LoaderLoadJsonUseCase.ts b/packages/display/src/Loader/usecase/LoaderLoadJsonUseCase.ts new file mode 100644 index 00000000..43521544 --- /dev/null +++ b/packages/display/src/Loader/usecase/LoaderLoadJsonUseCase.ts @@ -0,0 +1,42 @@ +import type { IAnimationToolData } from "../../interface/IAnimationToolData"; +import type { IAnimationToolDataZlib } from "../../interface/IAnimationToolDataZlib"; +import type { Loader } from "../../Loader"; +import { execute as loaderBuildService } from "../service/LoaderBuildService"; + +// @ts-ignore +import ZlibInflateWorker from "../worker/ZlibInflateWorker?worker&inline"; + +/** + * @type {Worker} + * @private + */ +const worker: Worker = new ZlibInflateWorker(); + +/** + * @description JSONオブジェクトがzlib圧縮されている場合はworkerで解凍、無圧縮ならビルド処理を実行 + * If the JSON object is zlib compressed, decompress it with a worker, otherwise execute the build process + * + * @param {Loader} loader + * @param {object} object + * @return {Promise} + * @method + * @public + */ +export const execute = async (loader: Loader, object: IAnimationToolData | IAnimationToolDataZlib): Promise => +{ + if (object.type === "zlib") { + await new Promise((resolve): void => + { + worker.onmessage = (event: MessageEvent): void => + { + loaderBuildService(loader, event.data as IAnimationToolData); + resolve(); + }; + + const buffer: Uint8Array = new Uint8Array(object.buffer); + worker.postMessage(buffer, [buffer.buffer]); + }); + } else { + loaderBuildService(loader, object); + } +}; \ No newline at end of file diff --git a/packages/display/src/Loader/usecase/LoaderLoadUseCase.ts b/packages/display/src/Loader/usecase/LoaderLoadUseCase.ts new file mode 100644 index 00000000..ce9ab257 --- /dev/null +++ b/packages/display/src/Loader/usecase/LoaderLoadUseCase.ts @@ -0,0 +1,47 @@ +import type { URLRequest } from "@next2d/net"; +import type { Loader } from "../../Loader"; +import { $ajax } from "../../DisplayObjectUtil"; +import { execute as loaderLoadstartEventService } from "../service/LoaderLoadStartEventService"; +import { execute as loaderProgressEventService } from "../service/LoaderProgressEventService"; +import { execute as loaderLoadEndEventUseCase } from "../usecase/LoaderLoadEndEventUseCase"; + +/** + * @description 外部JSONのローディングの実行関数 + * Execution function of external JSON loading + * + * @see Loader.load + * @param {Loader} loader + * @param {URLRequest} request + * @return {Promise} + * @method + * @protected + */ +export const execute = async (loader: Loader, request: URLRequest): Promise => +{ + await new Promise((resolve): void => + { + $ajax({ + "format": request.responseDataFormat, + "url": request.url, + "method": request.method, + "data": request.data, + "headers": request.headers, + "withCredentials": request.withCredentials, + "event": { + "loadstart": (event: ProgressEvent): void => + { + loaderLoadstartEventService(loader.contentLoaderInfo, event); + }, + "progress": (event: ProgressEvent): void => + { + loaderProgressEventService(loader.contentLoaderInfo, event); + }, + "loadend": async (event: ProgressEvent): Promise => + { + await loaderLoadEndEventUseCase(loader, event); + resolve(); + } + } + }); + }); +}; \ No newline at end of file diff --git a/packages/display/src/Loader/worker/ZlibInflateWorker.ts b/packages/display/src/Loader/worker/ZlibInflateWorker.ts new file mode 100644 index 00000000..6653e1d6 --- /dev/null +++ b/packages/display/src/Loader/worker/ZlibInflateWorker.ts @@ -0,0 +1,26 @@ +"use strict"; + +import { decompressSync } from "fflate"; + +/** + * @description zilbの圧縮されたデータを解凍します。 + * Unzips zlib-compressed data. + * + * @param {MessageEvent} event + * @return {void} + * @method + * @public + */ +self.addEventListener("message", (event: MessageEvent): void => +{ + const buffer = decompressSync(event.data); + + let json = ""; + for (let idx: number = 0; idx < buffer.length; idx += 4096) { + json += String.fromCharCode(...buffer.subarray(idx, idx + 4096)); + } + + self.postMessage(JSON.parse(decodeURIComponent(json))); +}); + +export default {}; \ No newline at end of file diff --git a/packages/display/src/LoaderInfo.ts b/packages/display/src/LoaderInfo.ts index c7d39bb9..4d359c2f 100644 --- a/packages/display/src/LoaderInfo.ts +++ b/packages/display/src/LoaderInfo.ts @@ -1,20 +1,17 @@ +import type { MovieClip } from "./MovieClip"; +import type { Sprite } from "./Sprite"; +import type { IParent } from "./interface/IParent"; +import type { IURLLoaderDataFormat } from "./interface/IURLLoaderDataFormat"; +import type { ILoaderInfoData } from "./interface/ILoaderInfoData"; import { EventDispatcher } from "@next2d/events"; -import { $getLoaderInfoId } from "@next2d/util"; -import type { - URLLoaderDataFormatImpl, - ParentImpl, - LoaderInfoDataImpl -} from "@next2d/interface"; +import { $getInstanceId } from "./DisplayObjectUtil"; /** - * LoaderInfo クラスは、読み込まれる JSON ファイルやイメージファイル(JPEG、GIF、PNG ファイルなど)に関する情報を提供します。 - * LoaderInfo オブジェクトは、すべての表示オブジェクトで使用できます。 - * 提供される情報には、読み込みの進行状況、読み込む側と読み込まれたコンテンツの URL、メディアの総バイト数、メディアの規格高さと幅などが含まれます。 + * @description LoaderInfo クラスは、読み込まれる JSON ファイルに関する情報を提供します。 + * LoaderInfo オブジェクトは、すべての表示オブジェクトで使用できます。 * - * The LoaderInfo class provides information about a loaded JSON file or a loaded image file (JPEG, GIF, or PNG). - * LoaderInfo objects are available for any display object. - * The information provided includes load progress, the URLs of the loader and loaded content, - * the number of bytes total for the media, and the nominal height and width of the media. + * The LoaderInfo class provides information about the JSON file to be loaded. + * The LoaderInfo object can be used with all display objects. * * @class * @memberOf next2d.display @@ -22,209 +19,67 @@ import type { */ export class LoaderInfo extends EventDispatcher { - public readonly _$id: number; - public _$content: ParentImpl | null; - public _$data: LoaderInfoDataImpl | null; - private _$bytesLoaded: number; - private _$bytesTotal: number; - private _$url: string; - private _$format: URLLoaderDataFormatImpl; - - /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {number} - * @private - */ - this._$id = $getLoaderInfoId(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesLoaded = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesTotal = 0; - - /** - * @type {string} - * @default "" - * @private - */ - this._$url = ""; - - /** - * @type {DisplayObject} - * @default null - * @private - */ - this._$content = null; - - /** - * @type {object} - * @default null - * @private - */ - this._$data = null; - - /** - * @type {string} - * @default "json" - * @private - */ - this._$format = "json"; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class LoaderInfo] - * @method - * @static - */ - static toString (): string - { - return "[class LoaderInfo]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.LoaderInfo - * @const - * @static - */ - static get namespace (): string - { - return "next2d.display.LoaderInfo"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object LoaderInfo] - * @method - * @public - */ - toString (): string - { - return "[object LoaderInfo]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.LoaderInfo - * @const - * @public - */ - get namespace (): string - { - return "next2d.display.LoaderInfo"; - } - /** - * @description そのメディアのロード済みのバイト数です。 - * The uint of bytes that are loaded for the media. + * @description LoaderInfoのユニークIDを返却 + * Returns the unique ID of LoaderInfo * - * @member {number} - * @default 0 + * @type {number} + * @readonly * @public */ - get bytesLoaded (): number - { - return this._$bytesLoaded; - } - set bytesLoaded (bytes_loaded: number) - { - this._$bytesLoaded = bytes_loaded | 0; - } + public readonly id: number; /** - * @description メディアファイル全体のバイト数です。 - * The number of bytes in the entire media file. + * @description LoaderInfo オブジェクトに関係したロードされたオブジェクトです。 + * The loaded object associated with this LoaderInfo object. * - * @member {number} - * @default 0 + * @type {MovieClip | Sprite | null} * @public */ - get bytesTotal (): number - { - return this._$bytesTotal; - } - set bytesTotal (bytes_total: number) - { - this._$bytesTotal = bytes_total | 0; - } + public content: IParent | null; /** - * @description LoaderInfo オブジェクトに関係したロードされたオブジェクトです。 - * The loaded object associated with this LoaderInfo object. + * @description Loaderで読み込まれたデータオブジェクトを返却 + * Returns the data object loaded by the Loader * - * @member {DisplayObject} + * @type {object} * @public */ - get content (): ParentImpl - { - return this._$content; - } - set content (content: ParentImpl) - { - this._$content = content; - } + public data: ILoaderInfoData | null; /** * @description 読み込まれるメディアの URL です。 * The URL of the media being loaded. * - * @member {string} + * @type {string} * @default "" * @readonly * @public */ - get url (): string - { - return this._$url; - } - set url (url: string) - { - this._$url = url; - } + public url: string; /** * @description 読み込まれるメディアの データフォーマット です。 * The data format of the media being loaded. * - * @member {string} + * @type {string} * @default "json" * @public */ - get format (): URLLoaderDataFormatImpl - { - return this._$format; - } - set format (format: URLLoaderDataFormatImpl) + public format: IURLLoaderDataFormat; + + /** + * @constructor + * @public + */ + constructor () { - this._$format = format; + super(); + + this.id = $getInstanceId(); + this.url = ""; + this.content = null; + this.data = null; + this.format = "json"; } -} +} \ No newline at end of file diff --git a/packages/display/src/LoopConfig.ts b/packages/display/src/LoopConfig.ts index 2bbd56e4..e6d2ee49 100644 --- a/packages/display/src/LoopConfig.ts +++ b/packages/display/src/LoopConfig.ts @@ -1,187 +1,72 @@ -import { $clamp } from "@next2d/share"; - /** - * LoopConfig クラスで、MovieClipのフレームヘッダーの移動方法を指定できます。 - * 一度限りの再生や逆再生、フレーム固定などのアニメーションのループバリエーションを設定できます。 + * @description LoopConfig クラスで、MovieClipのフレームヘッダーの移動方法を指定できます。 + * 一度限りの再生や逆再生、フレーム固定などのアニメーションのループバリエーションを設定できます。 * - * The LoopConfig class allows you to specify how the frame headers of a MovieClip are moved. - * You can set up looping variations of the animation, such as one-time playback, - * reverse playback, or fixed frame. + * The LoopConfig class allows you to specify how the frame headers of a MovieClip are moved. + * You can set up looping variations of the animation, such as one-time playback, + * reverse playback, or fixed frame. * * @class * @memberOf next2d.display */ export class LoopConfig { - private _$type: number; - private _$start: number; - private _$end: number; - private _$frame: number; - - /** - * @param {number} [type=0] - * @param {number} [start=1] - * @param {number} [end=0] - * - * @constructor - * @public - */ - constructor (type: number = 0, start: number = 1, end: number = 0) - { - /** - * @type {number} - * @default 0 - * @private - */ - this._$type = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$start = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$end = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$frame = 1; - - // setup - this.type = type; - this.start = start; - this.end = end; - } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class LoopConfig] - * @method - * @static - */ - static toString (): string - { - return "[class LoopConfig]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.LoopConfig - * @const - * @static - */ - static get namespace (): string - { - return "next2d.display.LoopConfig"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object LoopConfig] - * @method - * @public - */ - toString (): string - { - return "[object LoopConfig]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description LoopTypeクラスの固定値を利用して、ループのタイプを設定できます。 + * You can set the type of loop by using a fixed value in the LoopType class. * - * @return {string} - * @default next2d.display.LoopConfig - * @const + * @return {number} + * @default 0 * @public */ - get namespace (): string - { - return "next2d.display.LoopConfig"; - } + public type: 0 | 1 | 2 | 3 | 4; /** - * @description ループ設定の適用を開始するフレームの値、自動で設定されます。 - * The value of the frame at which to start applying the loop setting, set automatically. + * @description フレーム移動の開始値を設定します。逆再生時はここで設定した値が終了フレームとなります。 + * Sets the start value for frame shift. For reverse playback, + * the value set here is the end frame. * * @return {number} * @default 1 - * @readonly * @public */ - get frame (): number - { - return this._$frame; - } + public start: number; /** - * @description LoopTypeクラスの固定値を利用して、ループのタイプを設定できます。 - * You can set the type of loop by using a fixed value in the LoopType class. + * @description フレーム移動の終了値を設定します。逆再生時はここで設定した値が開始フレームとなります。 + * Sets the end value of frame shift. For reverse playback, + * the value set here is the start frame. * * @return {number} * @default 0 * @public */ - get type (): number - { - return this._$type; - } - set type (type: number) - { - this._$type = $clamp(type | 0, 0, 4); - } + public end: number; /** - * @description フレーム移動の開始値を設定します。逆再生時はここで設定した値が終了フレームとなります。 - * Sets the start value for frame shift. For reverse playback, - * the value set here is the end frame. + * @description ループ設定の適用を開始するフレームの値、自動で設定されます。 + * The value of the frame at which to start applying the loop setting, set automatically. * * @return {number} * @default 1 + * @readonly * @public */ - get start (): number - { - return this._$start; - } - set start (start: number) - { - this._$start = $clamp(start | 0, 1, 0xffffff); - } + public frame: number; /** - * @description フレーム移動の終了値を設定します。逆再生時はここで設定した値が開始フレームとなります。 - * Sets the end value of frame shift. For reverse playback, - * the value set here is the start frame. + * @param {number} [type=0] + * @param {number} [start=1] + * @param {number} [end=0] * - * @return {number} - * @default 0 + * @constructor * @public */ - get end (): number + constructor (type: 0 | 1 | 2 | 3 | 4 = 0, start: number = 1, end: number = 0) { - return this._$end; - } - set end (end: number) - { - this._$end = $clamp(end | 0, 0, 0xffffff); + this.type = type; + this.start = start; + this.end = end; + this.frame = 1; } -} +} \ No newline at end of file diff --git a/packages/display/src/LoopType.ts b/packages/display/src/LoopType.ts index 10177367..2bb006d8 100644 --- a/packages/display/src/LoopType.ts +++ b/packages/display/src/LoopType.ts @@ -1,82 +1,25 @@ /** - * LoopType クラスは、MovieClipのフレームヘッダーの移動方法を指定する定数値の列挙です。 - * これらの定数は、LoopConfigで利用されます。 + * @description LoopType クラスは、MovieClipのフレームヘッダーの移動方法を指定する定数値の列挙です。 + * これらの定数は、LoopConfigで利用されます。 * - * The LoopType class is an enumeration of constant values - * that specify how to move the frame header of a MovieClip, - * These constants are used by LoopConfig. + * The LoopType class is an enumeration of constant values + * that specify how to move the frame header of a MovieClip, + * These constants are used by LoopConfig. * * @class * @memberOf next2d.display */ export class LoopType { - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class LoopType] - * @method - * @static - */ - static toString () - { - return "[class LoopType]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.LoopType - * @const - * @static - */ - static get namespace () - { - return "next2d.display.LoopType"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object LoopType] - * @method - * @public - */ - toString () - { - return "[object LoopType]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.LoopType - * @const - * @public - */ - get namespace () - { - return "next2d.display.LoopType"; - } - /** * @description ループ設定でリピート再生を使用することを指定します。 * Specifies that repeat playback should be used in the loop settings. * - * @return {number} - * @default 0 + * @return {number} * @const * @static */ - static get REPEAT () + static get REPEAT (): number { return 0; } @@ -85,12 +28,11 @@ export class LoopType * @description ループ設定で再生ヘッダーが指定した最終フレームに到達するとフレームを固定する設定を指定します。 * Specifies the setting to fix frames when the playback header reaches the specified final frame in the loop settings. * - * @return {number} - * @default 1 + * @return {number} * @const * @static */ - static get NO_REPEAT () + static get NO_REPEAT (): number { return 1; } @@ -99,12 +41,11 @@ export class LoopType * @description ループ設定でフレームを固定する設定を指定します。 * Specifies the setting for fixing frames in the loop setting. * - * @return {number} - * @default 2 + * @return {number} * @const * @static */ - static get FIXED () + static get FIXED (): number { return 2; } @@ -114,12 +55,11 @@ export class LoopType * Specifies the setting where the playback header plays backwards in the loop setting * and fixes the frame when the specified start frame is reached. * - * @return {number} - * @default 3 + * @return {number} * @const * @static */ - static get NO_REPEAT_REVERSAL () + static get NO_REPEAT_REVERSAL (): number { return 3; } @@ -128,13 +68,12 @@ export class LoopType * @description ループ設定でリピート逆再生を使用することを指定します。 * Specifies the use of repeat reverse playback in the loop settings. * - * @return {number} - * @default 4 + * @return {number} * @const * @static */ - static get REPEAT_REVERSAL () + static get REPEAT_REVERSAL (): number { return 4; } -} +} \ No newline at end of file diff --git a/packages/display/src/MovieClip.ts b/packages/display/src/MovieClip.ts index 7d24b4c3..c1692252 100644 --- a/packages/display/src/MovieClip.ts +++ b/packages/display/src/MovieClip.ts @@ -1,45 +1,32 @@ +import type { IDisplayObject } from "./interface/IDisplayObject"; +import type { ICharacter } from "./interface/ICharacter"; +import type { LoaderInfo } from "./LoaderInfo"; +import type { IMovieClipCharacter } from "./interface/IMovieClipCharacter"; import { Sprite } from "./Sprite"; import { FrameLabel } from "./FrameLabel"; -import { Event } from "@next2d/events"; import { Sound } from "@next2d/media"; -import type { SoundTransform } from "@next2d/media"; -import type { Player } from "@next2d/core"; -import type { - PlaceObjectImpl, - LoopConfigImpl, - DisplayObjectImpl, - ParentImpl, - MovieClipCharacterImpl, - MovieClipSoundObjectImpl, - MovieClipActionObjectImpl, - MovieClipLabelObjectImpl, - DictionaryTagImpl, - Character -} from "@next2d/interface"; -import { - $setCurrentLoaderInfo, - $currentPlayer, - $rendererWorker -} from "@next2d/util"; -import { - $Array, - $getArray, - $getMap, - $isNaN, - $Math -} from "@next2d/share"; +import { execute as movieClipGetChildrenService } from "./MovieClip/service/MovieClipGetChildrenService"; +import { execute as movieClipAddFrameLabelService } from "./MovieClip/service/MovieClipAddFrameLabelService"; +import { execute as movieClipCurrentFrameLabelService } from "./MovieClip/service/MovieClipCurrentFrameLabelService"; +import { execute as movieClipCurrentLabelsService } from "./MovieClip/service/MovieClipCurrentLabelsService"; +import { execute as displayObjectApplyChangesService } from "./DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as movieClipGotoAndPlayUseCase } from "./MovieClip/usecase/MovieClipGotoAndPlayUseCase"; +import { execute as movieClipGotoAndStopUseCase } from "./MovieClip/usecase/MovieClipGotoAndStopUseCase"; +import { execute as movieClipNextFrameUseCase } from "./MovieClip/usecase/MovieClipNextFrameUseCase"; +import { execute as movieClipPrevFrameUseCase } from "./MovieClip/usecase/MovieClipPrevFrameUseCase"; +import { execute as movieClipBuildFromCharacterUseCase } from "./MovieClip/usecase/MovieClipBuildFromCharacterUseCase"; /** - * MovieClip クラスは、Sprite、DisplayObjectContainer、InteractiveObject、DisplayObject - * および EventDispatcher クラスを継承します。 - * MovieClip オブジェクトには、Sprite オブジェクトとは違ってタイムラインがあります。 - * タイムラインの再生ヘッドが停止されても、その MovieClip オブジェクトの子 MovieClip オブジェクトの再生ヘッドは停止しません。 + * @description MovieClip クラスは、Sprite、DisplayObjectContainer、InteractiveObject、DisplayObject + * および EventDispatcher クラスを継承します。 + * MovieClip オブジェクトには、Sprite オブジェクトとは違ってタイムラインがあります。 + * タイムラインの再生ヘッドが停止されても、その MovieClip オブジェクトの子 MovieClip オブジェクトの再生ヘッドは停止しません。 * - * The MovieClip class inherits from the following classes: Sprite, DisplayObjectContainer, - * InteractiveObject, DisplayObject, and EventDispatcher. - * Unlike the Sprite object, a MovieClip object has a timeline. - * When the playback head of the timeline is stopped, - * the playback head of the child MovieClip object of that MovieClip object will not be stopped. + * The MovieClip class inherits from the following classes: Sprite, DisplayObjectContainer, + * InteractiveObject, DisplayObject, and EventDispatcher. + * Unlike the Sprite object, a MovieClip object has a timeline. + * When the playback head of the timeline is stopped, + * the playback head of the child MovieClip object of that MovieClip object will not be stopped. * * @class * @memberOf next2d.display @@ -47,153 +34,149 @@ import { */ export class MovieClip extends Sprite { - private _$labels: Map | null; - public _$currentFrame: number; - public _$stopFlag: boolean; - public _$canAction: boolean; - public _$canSound: boolean; - public _$actionProcess: boolean; - public _$actions: Map; - public _$frameCache: Map; - public _$sounds: Map; - public _$actionOffset: number; - public _$actionLimit: number; - public _$totalFrames: number; - public _$isPlaying: boolean; - public _$loopConfig: LoopConfigImpl | null; - private _$tweenFrame: number; - /** - * @constructor - * @public + * @description フレーム毎のラベルマップ + * Label map per frame + * + * @type {Map} + * @default null + * @private */ - constructor() - { - super(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$stopFlag = false; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$canAction = true; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$canSound = true; + public $labels: Map | null; - /** - * @type {boolean} - * @default false - * @private - */ - this._$actionProcess = false; - - /** - * @type {Map} - * @private - */ - this._$actions = $getMap(); - - /** - * @type {Map} - * @private - */ - this._$frameCache = $getMap(); - - /** - * @type {Map} - * @default null - * @private - */ - this._$labels = null; + /** + * @description フレーム毎のアクションマップ + * Action map per frame + * + * @type {Map} + * @default null + * @private + */ + public $actions: Map | null; - /** - * @type {Map} - * @private - */ - this._$sounds = $getMap(); + /** + * @description フレーム毎のサウンドマップ + * Sound map per frame + * + * @type {Map} + * @default null + * @private + */ + public $sounds: Map | null; - /** - * @type {number} - * @default 0 - * @private - */ - this._$actionOffset = 0; + /** + * @description アクション実行フラグ + * Action execution flag + * + * @type {boolean} + * @default true + * @private + */ + public $canAction: boolean; - /** - * @type {number} - * @default 0 - * @private - */ - this._$actionLimit = 0; + /** + * @description 待機フラグ + * Wait flag + * + * @type {boolean} + * @default false + * @private + */ + public $wait: boolean; - /** - * @type {number} - * @default 1 - * @private - */ - this._$currentFrame = 1; + /** + * @description MovieClip インスタンス内のフレーム総数です。 + * The total number of frames in the MovieClip instance. + * + * @member {number} + * @default 1 + * @readonly + * @public + */ + public totalFrames: number; - /** - * @type {number} - * @default 1 - * @private - */ - this._$totalFrames = 1; + /** + * @description MovieClipのタイムライン内の再生ヘッドが置かれているフレームの番号を示します。 + * Specifies the number of the frame in which the playhead is located + * + * @member {number} + * @default 1 + * @readonly + * @public + */ + public currentFrame: number; - /** - * @type {boolean} - * @default false - * @private - */ - this._$isPlaying = false; + /** + * @description 停止フラグ + * Stop flag + * + * @type {boolean} + * @default false + * @private + */ + private _$stopFlag: boolean; - /** - * @type {LoopConfig} - * @default null - * @private - */ - this._$loopConfig = null; + /** + * @description サウンド実行フラグ + * Sound execution flag + * + * @type {boolean} + * @default true + * @private + */ + public $canSound: boolean; - /** - * @type {number} - * @default 0 - * @private - */ - this._$tweenFrame = 0; - } + /** + * @description タイムラインヘッドが移動したかどうか + * Whether the timeline head has moved + * + * @type {boolean} + * @default true + * @private + */ + public $hasTimelineHeadMoved: boolean; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description MovieClipの機能を所持しているかを返却 + * Returns whether the display object has MovieClip functionality. * - * @return {string} - * @default [class MovieClip] - * @method - * @static + * @type {boolean} + * @readonly + * @public */ - static toString (): string + public readonly isTimelineEnabled: boolean; + + /** + * @constructor + * @public + */ + constructor() { - return "[class MovieClip]"; + super(); + + // public + this.currentFrame = 1; + this.totalFrames = 1; + this.isTimelineEnabled = true; + + // protected + this.$actions = null; + this.$labels = null; + this.$sounds = null; + this.$canAction = true; + this.$wait = false; + this.$canSound = true; + this.$hasTimelineHeadMoved = true; + + // private + this._$stopFlag = false; } /** * @description 指定されたクラスの空間名を返します。 * Returns the space name of the specified class. * - * @return {string} - * @default next2d.display.MovieClip + * @return {string} * @const * @static */ @@ -202,26 +185,11 @@ export class MovieClip extends Sprite return "next2d.display.MovieClip"; } - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object MovieClip] - * @method - * @public - */ - toString (): string - { - return "[object MovieClip]"; - } - /** * @description 指定されたオブジェクトの空間名を返します。 * Returns the space name of the specified object. * - * @return {string} - * @default next2d.display.MovieClip + * @return {string} * @const * @public */ @@ -231,18 +199,22 @@ export class MovieClip extends Sprite } /** - * @description MovieClip インスタンスのタイムライン内の再生ヘッドが置かれているフレームの番号を示します。 - * Specifies the number of the frame in which the playhead is located - * in the timeline of the MovieClip instance. + * @description コンテナのアクティブな子要素を返却 + * Returns the active child elements of the container. * - * @member {number} - * @default 1 - * @readonly - * @public + * @return {array} + * @method + * @protected */ - get currentFrame (): number + get children (): IDisplayObject { - return this._$currentFrame; + if (!this.$hasTimelineHeadMoved || this.characterId === -1) { + return this._$children; + } + + this.$hasTimelineHeadMoved = false; + + return movieClipGetChildrenService(this, this._$children); } /** @@ -255,31 +227,20 @@ export class MovieClip extends Sprite */ get currentFrameLabel (): FrameLabel | null { - if (!this._$labels) { - return null; - } - - const frame = this._$currentFrame; - if (!this._$labels.has(frame)) { - return null; - } - - return this._$labels.get(frame) || null; + return movieClipCurrentFrameLabelService(this); } /** * @description 現在のシーンの FrameLabel オブジェクトの配列を返します。 * Returns an array of FrameLabel objects from the current scene. * - * @member {array|null} + * @member {FrameLabel[]|null} * @readonly * @public */ get currentLabels (): FrameLabel[] | null { - return !this._$labels || !this._$labels.size - ? null - : $Array.from(this._$labels.values()); + return movieClipCurrentLabelsService(this); } /** @@ -293,21 +254,7 @@ export class MovieClip extends Sprite */ get isPlaying (): boolean { - return this._$isPlaying; - } - - /** - * @description MovieClip インスタンス内のフレーム総数です。 - * The total number of frames in the MovieClip instance. - * - * @member {number} - * @default 1 - * @readonly - * @public - */ - get totalFrames (): number - { - return this._$totalFrames; + return !this._$stopFlag; } /** @@ -318,51 +265,50 @@ export class MovieClip extends Sprite * @default null * @public */ - get loopConfig (): LoopConfigImpl | null - { - if (this._$loopConfig) { - return this._$loopConfig; - } - - const place: PlaceObjectImpl | null = this._$placeObject || this._$getPlaceObject(); - if (!place || !place.loop) { - return null; - } - - if (this._$tweenFrame) { - this._$changePlace = this._$tweenFrame !== this._$parent._$currentFrame; - this._$tweenFrame = 0; - } - - if (place.loop.tweenFrame) { - this._$tweenFrame = place.loop.tweenFrame; - } - - return place.loop; - } - set loopConfig (loop_config: LoopConfigImpl | null) - { - this._$loopConfig = loop_config; - if (loop_config) { - loop_config.frame = this._$startFrame; - this._$loopConfig = loop_config; - this._$currentFrame = this._$getLoopFrame(loop_config); - } - } + // get loopConfig (): LoopConfigImpl | null + // { + // if (this._$loopConfig) { + // return this._$loopConfig; + // } + + // const place: PlaceObjectImpl | null = this._$placeObject || this._$getPlaceObject(); + // if (!place || !place.loop) { + // return null; + // } + + // if (this._$tweenFrame) { + // this._$changePlace = this._$tweenFrame !== this._$parent._$currentFrame; + // this._$tweenFrame = 0; + // } + + // if (place.loop.tweenFrame) { + // this._$tweenFrame = place.loop.tweenFrame; + // } + + // return place.loop; + // } + // set loopConfig (loop_config: LoopConfigImpl | null) + // { + // this._$loopConfig = loop_config; + // if (loop_config) { + // loop_config.frame = this._$startFrame; + // this._$loopConfig = loop_config; + // this._$currentFrame = this._$getLoopFrame(loop_config); + // } + // } /** * @description 指定されたフレームで SWF ファイルの再生を開始します。 * Starts playing the SWF file at the specified frame. * - * @param {number|string} frame + * @param {string|number} frame * @return {void} * @method * @public */ gotoAndPlay (frame: string | number): void { - this.play(); - this._$goToFrame(frame); + movieClipGotoAndPlayUseCase(this, frame); } /** @@ -377,8 +323,7 @@ export class MovieClip extends Sprite */ gotoAndStop (frame: string | number): void { - this.stop(); - this._$goToFrame(frame); + movieClipGotoAndStopUseCase(this, frame); } /** @@ -391,10 +336,7 @@ export class MovieClip extends Sprite */ nextFrame (): void { - this.stop(); - if (this._$totalFrames > this._$currentFrame) { - this._$goToFrame(this._$currentFrame + 1); - } + movieClipNextFrameUseCase(this); } /** @@ -407,9 +349,8 @@ export class MovieClip extends Sprite */ play (): void { - this._$stopFlag = false; - this._$isPlaying = true; - this._$updateState(); + this._$stopFlag = false; + displayObjectApplyChangesService(this); } /** @@ -422,11 +363,7 @@ export class MovieClip extends Sprite */ prevFrame (): void { - const frame: number = this._$currentFrame - 1; - if (frame) { - this.stop(); - this._$goToFrame(frame); - } + movieClipPrevFrameUseCase(this); } /** @@ -439,806 +376,37 @@ export class MovieClip extends Sprite */ stop (): void { - this._$stopFlag = true; - this._$isPlaying = false; + this._$stopFlag = true; } /** * @description タイムラインに対して動的にLabelを追加できます。 * Labels can be added dynamically to the timeline. * - * @example Example1 usage of addFrameLabel. - * // case 1 - * const {MovieClip, FrameLabel} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.addFrameLabel(new FrameLabel(1, "start")); - * * @param {FrameLabel} frame_label * @return {void} * @public */ addFrameLabel (frame_label: FrameLabel): void { - if (!this._$labels) { - this._$labels = $getMap(); - } - - this._$labels.set(frame_label.frame, frame_label); + movieClipAddFrameLabelService(this, frame_label); } /** - * @description 指定のフレームのアクションを追加できます - * You can add an action for a given frame. - * - * @example Example1 usage of addFrameScript. - * // case 1 - * const {MovieClip} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.addFrameScript(1 , function () - * { - * this.stop(); - * }); - * - * @example Example3 usage of addFrameScript. - * // case 2 - * const {MovieClip} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.addFrameScript(1, method_1, 2, method_2, 10, method_10); + * @description character 情報を元に DisplayObject を構築 + * Build DisplayObject based on character * + * @param {ICharacter} character + * @param {LoaderInfo} [loader_info=null] * @return {void} * @method - * @public - */ - addFrameScript (...args: any[]): void - { - for (let idx = 0; idx < args.length; idx += 2) { - - const value: string | number = args[idx]; - - let frame: number = +value; - if ($isNaN(frame)) { - frame = this._$getFrameForLabel(`${value}`); - } - - const script: Function = args[idx + 1]; - if (script && frame && this._$totalFrames >= frame) { - this._$addAction(frame, script); - } - - // end action add - if (frame === this._$currentFrame) { - - // set action position - const player: Player = $currentPlayer(); - player._$actionOffset = player._$actions.length; - - // execute action stack - this._$canAction = true; - this._$setAction(); - - // adjustment - if (player._$actionOffset !== player._$actions.length) { - - // marge - const actions: MovieClip[] = player._$actions.splice(0, player._$actionOffset); - player._$actions.push( - ...player._$actions, - ...actions - ); - - // reset - player._$actionOffset = 0; - } - - } - } - } - - /** - * @param {string} name - * @return {number} - * @private - */ - _$getFrameForLabel (name: string): number - { - if (!this._$labels) { - return 0; - } - - for (const [frame, frameLabel] of this._$labels) { - if (frameLabel.name === name) { - return frame; - } - } - - return 0; - } - - /** - * @param {number} frame - * @param {function} script - * @return {void} - * @method - * @private - */ - _$addAction (frame: number, script: Function): void - { - if (frame) { - if (!this._$actions.has(frame)) { - this._$actions.set(frame, $getArray()); - } - - const actions: Function[] | void = this._$actions.get(frame); - if (actions) { - actions.push(script); - } - - } - } - - /** - * @return {void} - * @private - */ - _$setAction (): void - { - // added event - this._$executeAddedEvent(); - - if (this._$canAction) { - - const frame: number = this._$currentFrame; - - // frame label event - if (this._$labels && this._$labels.has(frame)) { - - const frameLabel: FrameLabel | void = this._$labels.get(frame); - - if (frameLabel && frameLabel.willTrigger(Event.FRAME_LABEL)) { - frameLabel.dispatchEvent(new Event(Event.FRAME_LABEL)); - } - } - - // add action queue - if (this._$actions.size && this._$actions.has(frame)) { - - const player: Player = $currentPlayer(); - const index = player._$actions.indexOf(this); - if (index === -1) { - player._$actions.push(this); - } - - } - } - } - - /** - * @param {number|string} value - * @return {void} - * @private - */ - _$goToFrame (value: string | number): void - { - let frame: number = +value; - if ($isNaN(frame)) { - frame = this._$getFrameForLabel(`${value}`); - } - - if (frame < 1) { - frame = 1; - } - - // over - if (frame > this._$totalFrames) { - this._$currentFrame = this._$totalFrames; - this._$clearChildren(); - - // flag off - this._$canAction = false; - this._$wait = false; - - return ; - } - - const player: Player = $currentPlayer(); - switch (true) { - - case frame !== this._$currentFrame: - { - // flag off - this._$wait = false; - - const currentFrame: number = this._$currentFrame; - - if (this._$actionProcess) { - this._$frameCache.set("nextFrame", frame); - this._$frameCache.set("stopFlag", this._$stopFlag); - this._$frameCache.set("isPlaying", this._$isPlaying); - } - - // setup - this._$currentFrame = frame; - this._$clearChildren(); - - // set action position - player._$actionOffset = player._$actions.length; - const position: number = player._$actionOffset - ? player._$actions.indexOf(this) - : -1; - - this._$canAction = true; - this._$prepareActions(); - - // adjustment - if (player._$actionOffset - && player._$actionOffset !== player._$actions.length - ) { - - // marge - const actions: MovieClip[] = player._$actions.splice(0, player._$actionOffset); - player._$actions.push( - ...player._$actions, - ...actions - ); - - // reset - player._$actionOffset = 0; - } - - if (!this._$actionProcess && (position > -1 || !player._$actionOffset)) { - - while (player._$actions.length) { - - if (player._$actions.length === position) { - break; - } - - // target object - const mc: MovieClip | void = player._$actions.pop(); - if (!mc) { - continue; - } - - mc._$canAction = false; - mc._$actionOffset = 0; - mc._$actionLimit = 0; - - if (mc._$actionProcess && mc._$frameCache.size) { - - mc._$currentFrame = mc._$frameCache.get("nextFrame"); - mc._$clearChildren(); - - mc._$stopFlag = mc._$frameCache.get("stopFlag"); - mc._$isPlaying = mc._$frameCache.get("isPlaying"); - mc._$frameCache.clear(); - } - - const frame: number = mc._$currentFrame; - if (!mc._$actions.has(frame)) { - continue; - } - - const actions: Function[] | void = mc._$actions.get(frame); - if (!actions) { - continue; - } - - for (let idx: number = 0; idx < actions.length; ++idx) { - - try { - - $setCurrentLoaderInfo(mc._$loaderInfo); - actions[idx].apply(mc); - - } catch (e) { - - mc.stop(); - - } - } - } - } - - if (this._$actionProcess) { - this._$currentFrame = currentFrame; - this._$clearChildren(); - } - } - break; - - case !this._$actionProcess && player._$actions.indexOf(this) > -1: - { - if (!this._$actionLimit) { - break; - } - - // flag off - this._$wait = false; - - const myActions: MovieClip[] = player._$actions.splice( - this._$actionOffset, this._$actionLimit - ); - - while (myActions.length) { - - const mc: MovieClip | void = myActions.pop(); - if (!mc) { - continue; - } - - // target reset - mc._$canAction = false; - mc._$actionOffset = 0; - mc._$actionLimit = 0; - - const frame = mc._$currentFrame; - if (!mc._$actions.has(frame)) { - continue; - } - - const actions: Function[] | void = mc._$actions.get(frame); - if (!actions) { - continue; - } - - for (let idx: number = 0; idx < actions.length; ++idx) { - - try { - - $setCurrentLoaderInfo(mc._$loaderInfo); - // @ts-ignore - actions[idx].apply(mc); - - } catch (e) { - - mc.stop(); - - } - - } - - } - } - break; - - default: - break; - - } - - $setCurrentLoaderInfo(null); - - // set sound - if (this._$canSound && this._$sounds.size - && this._$sounds.has(this._$currentFrame) - && !player._$sounds.has(this._$instanceId) - ) { - player._$sounds.set(this._$instanceId, this); - } - } - - /** - * @return {void} - * @method - * @private - */ - _$prepareActions (): void - { - // draw flag - this._$wait = false; - - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - for (let idx: number = children.length - 1; idx > -1; --idx) { - children[idx]._$prepareActions(); - } - - this._$setAction(); - } - - /** - * @return {boolean} - * @method - * @private - */ - _$nextFrame (): boolean - { - let isNext: boolean = this._$needsChildren; - switch (true) { - - case this._$wait: - isNext = true; - this._$wait = false; - break; - - case this._$stopFlag: - case this._$totalFrames === 1: - break; - - default: - { - isNext = true; - - // action on - this._$canAction = true; - - // sound on - this._$canSound = true; - - const loopConfig: LoopConfigImpl | null = this.loopConfig; - if (!loopConfig) { - - // next frame point - ++this._$currentFrame; - if (this._$currentFrame > this._$totalFrames) { - this._$currentFrame = 1; - } - - } else { - - const totalFrames: number = loopConfig.end - ? loopConfig.end - : this._$totalFrames; - - switch (loopConfig.type) { - - case 0: // REPEAT - if (this._$changePlace) { - - this._$currentFrame = loopConfig.start; - - } else { - - ++this._$currentFrame; - if (this._$currentFrame > totalFrames) { - this._$currentFrame = loopConfig.start; - } - - } - break; - - case 1: // NO_REPEAT - if (this._$changePlace) { - - this._$currentFrame = loopConfig.start; - - } else { - - ++this._$currentFrame; - if (this._$currentFrame > totalFrames) { - - this._$currentFrame = totalFrames; - isNext = false; - - // action on - this._$canAction = false; - - // sound on - this._$canSound = false; - } - - } - break; - - case 2: // FIXED - if (this._$changePlace) { - - this._$currentFrame = loopConfig.start; - - } else { - - isNext = false; - - // action on - this._$canAction = false; - - // sound on - this._$canSound = false; - - } - break; - - case 3: // NO_REPEAT_REVERSAL - if (this._$changePlace) { - - this._$currentFrame = totalFrames; - - } else { - - --this._$currentFrame; - if (loopConfig.start > this._$currentFrame) { - this._$currentFrame = loopConfig.start; - - isNext = false; - - // action on - this._$canAction = false; - - // sound on - this._$canSound = false; - } - - } - break; - - case 4: // REPEAT_REVERSAL - if (this._$changePlace) { - - this._$currentFrame = totalFrames; - - } else { - - --this._$currentFrame; - if (loopConfig.start > this._$currentFrame) { - this._$currentFrame = totalFrames; - } - - } - break; - - } - } - - // clear - if (isNext) { - this._$clearChildren(); - } - - // set sound - if (this._$canSound && this._$sounds.size - && this._$sounds.has(this._$currentFrame) - ) { - const player: Player = $currentPlayer(); - if (!player._$sounds.has(this._$instanceId)) { - player._$sounds.set(this._$instanceId, this); - } - } - - } - break; - - } - - const children: DisplayObjectImpl[] = this._$needsChildren - ? this._$getChildren() - : this._$children; - - for (let idx: number = children.length - 1; idx > -1; --idx) { - - const child: DisplayObjectImpl = children[idx]; - - if (!child._$isNext) { - continue; - } - - if (isNext) { - - child._$nextFrame(); - - } else { - - isNext = child._$nextFrame(); - - } - - } - - // frame action - this._$setAction(); - - this._$isNext = isNext; - - if (!this._$posted && $rendererWorker) { - this._$postProperty(); - } - - return this._$isNext; - } - - /** - * @param {LoopConfig} loop_config - * @return {number} - * @method - * @private - */ - _$getLoopFrame (loop_config: LoopConfigImpl): number - { - const parent: ParentImpl = this._$parent; - const length: number = parent._$currentFrame - loop_config.frame; - - let frame: number = 1; - switch (loop_config.type) { - - case 0: // REPEAT - { - const totalFrame = loop_config.end - ? loop_config.end - : this._$totalFrames; - - frame = loop_config.start; - for (let idx = 0; idx < length; ++idx) { - - ++frame; - - if (frame > totalFrame) { - frame = loop_config.start; - } - - } - } - break; - - case 1: // NO_REPEAT - { - const totalFrame = loop_config.end - ? loop_config.end - : this._$totalFrames; - - frame = $Math.min(totalFrame, loop_config.start + length); - } - break; - - case 2: // FIXED - frame = loop_config.start; - break; - - case 3: // NO_REPEAT_REVERSAL - - frame = loop_config.end - ? loop_config.end - : this._$totalFrames; - - frame = $Math.max(loop_config.start, frame - length); - break; - - case 4: // REPEAT_REVERSAL - { - const totalFrame: number = loop_config.end - ? loop_config.end - : this._$totalFrames; - - frame = totalFrame; - for (let idx: number = 0; idx < length; ++idx) { - - --frame; - - if (loop_config.start > frame) { - frame = totalFrame; - } - - } - } - break; - - } - - return frame; - } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$buildCharacter (character: MovieClipCharacterImpl): void - { - if (character.sounds) { - for (let idx: number = 0; idx < character.sounds.length; ++idx) { - - const object: MovieClipSoundObjectImpl = character.sounds[idx]; - - const sounds: Sound[] = $getArray(); - for (let idx: number = 0; idx < object.sound.length; ++idx) { - - const sound: Sound = new Sound(); - sound._$build(object.sound[idx], this); - - sounds.push(sound); - } - - this._$sounds.set(object.frame, sounds); - } - } - - if (character.actions) { - for (let idx: number = 0; idx < character.actions.length; ++idx) { - - const object: MovieClipActionObjectImpl = character.actions[idx]; - if (!object.script) { - object.script = Function(object.action); - } - - this._$addAction(object.frame, object.script); - } - } - - if (character.labels) { - for (let idx: number = 0; idx < character.labels.length; ++idx) { - - const label: MovieClipLabelObjectImpl = character.labels[idx]; - - this.addFrameLabel(new FrameLabel(label.name, label.frame)); - - } - } - - this._$totalFrames = character.totalFrame || 1; - } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$sync (character: Character): void - { - super._$sync(character); - this._$buildCharacter(character); - } - - /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private - */ - _$build ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): MovieClipCharacterImpl { - - const character: MovieClipCharacterImpl = super._$build(tag, parent); - - this._$buildCharacter(character); - - return character; - } - - /** - * @return {void} - * @method - * @private + * @protected */ - _$soundPlay (): void + $sync (character: ICharacter, loader_info: LoaderInfo | null = null): void { - if (!this._$sounds.has(this._$currentFrame)) { - return ; + if (loader_info) { + super.$syncLoaderInfo(loader_info); } - - const sounds: Sound[] = this._$sounds.get(this._$currentFrame) as NonNullable; - if (sounds.length) { - - // set SoundTransform - let soundTransform: SoundTransform | null = this._$soundTransform; - - let parent = this._$parent; - while (parent) { - - if (parent._$soundTransform) { - soundTransform = parent._$soundTransform; - } - - parent = parent._$parent; - } - - // play sound - for (let idx: number = 0; idx < sounds.length; ++idx) { - - const sound = sounds[idx]; - - if (soundTransform) { - sound.loopCount = soundTransform.loop ? 0xffffff : 0; - sound.volume = soundTransform.volume; - } - sound.play(); - } - } - - this._$canSound = false; + movieClipBuildFromCharacterUseCase(this, character as IMovieClipCharacter); } -} +} \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddActionsService.test.ts b/packages/display/src/MovieClip/service/MovieClipAddActionsService.test.ts new file mode 100644 index 00000000..7e59bdb9 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddActionsService.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./MovieClipAddActionsService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipAddActionsService.js test", () => +{ + it("execute test case1", () => + { + const actions = new Map(); + + const objects = [ + { + "frame": 1, + "action": "console.log('test1')" + }, + { + "frame": 5, + "action": "console.log('test2')" + } + ]; + + expect(actions.size).toBe(0); + execute(actions, objects); + expect(actions.size).toBe(2); + expect(actions.has(1)).toBe(true); + expect(actions.has(2)).toBe(false); + expect(actions.has(3)).toBe(false); + expect(actions.has(4)).toBe(false); + expect(actions.has(5)).toBe(true); + + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddActionsService.ts b/packages/display/src/MovieClip/service/MovieClipAddActionsService.ts new file mode 100644 index 00000000..56292e95 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddActionsService.ts @@ -0,0 +1,37 @@ +import type { IMovieClipActionObject } from "../../interface/IMovieClipActionObject"; +import { $getArray } from "../../DisplayObjectUtil"; + +/** + * @description 指定フレームに関数を追加 + * Add a function to the specified frame + * + * @param {Map} actions + * @param {array} objects + * @return {void} + * @method + * @public + */ +export const execute = ( + actions: Map, + objects: IMovieClipActionObject[] +): void => { + + for (let idx = 0; idx < objects.length; ++idx) { + + const object = objects[idx]; + if (!object) { + continue; + } + + if (!object.script) { + object.script = Function(object.action); + } + + const frame = object.frame; + if (!actions.has(frame)) { + actions.set(frame, $getArray()); + } + + actions.get(frame)?.push(object.script); + } +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.test.ts b/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.test.ts new file mode 100644 index 00000000..abba0644 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.test.ts @@ -0,0 +1,17 @@ +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { execute } from "./MovieClipAddFrameLabelService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipAddFrameLabelService.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + const frameLabel = new FrameLabel("label", 1); + + expect(movieClip.$labels).toBe(null); + execute(movieClip, frameLabel); + expect(movieClip.$labels?.size).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.ts b/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.ts new file mode 100644 index 00000000..5f4ec154 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddFrameLabelService.ts @@ -0,0 +1,20 @@ +import type { FrameLabel } from "../../FrameLabel"; +import type { MovieClip } from "../../MovieClip"; + +/** + * @description ラベル情報をマップに追加 + * Add label information to map + * + * @param {MovieClip} movei_clip + * @param {FrameLabel} frame_label + * @return {void} + * @method + * @protected + */ +export const execute = (movei_clip: MovieClip, frame_label: FrameLabel): void => +{ + if (!movei_clip.$labels) { + movei_clip.$labels = new Map(); + } + movei_clip.$labels.set(frame_label.frame, frame_label); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddLabelsService.test.ts b/packages/display/src/MovieClip/service/MovieClipAddLabelsService.test.ts new file mode 100644 index 00000000..26912c47 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddLabelsService.test.ts @@ -0,0 +1,31 @@ +import { FrameLabel } from "../../FrameLabel"; +import { execute } from "./MovieClipAddLabelsService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipAddLabelsService.js test", () => +{ + it("execute test case1", () => + { + const labelMap = new Map(); + + const labels = [ + { + "frame": 1, + "name": "start" + }, + { + "frame": 5, + "name": "end" + } + ]; + + expect(labelMap.size).toBe(0); + execute(labelMap, labels); + expect(labelMap.size).toBe(2); + expect(labelMap.has(1)).toBe(true); + expect(labelMap.has(2)).toBe(false); + expect(labelMap.has(3)).toBe(false); + expect(labelMap.has(4)).toBe(false); + expect(labelMap.has(5)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipAddLabelsService.ts b/packages/display/src/MovieClip/service/MovieClipAddLabelsService.ts new file mode 100644 index 00000000..7bb6f64e --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipAddLabelsService.ts @@ -0,0 +1,27 @@ +import type { IMovieClipLabelObject } from "../../interface/IMovieClipLabelObject"; +import { FrameLabel } from "../../FrameLabel"; + +/** + * @description ラベル情報をマップに追加 + * Add label information to map + * + * @param {Map} label_map + * @param {array} labels + * @return {void} + * @method + * @protected + */ +export const execute = ( + label_map: Map, + labels: IMovieClipLabelObject[] +): void => { + + for (let idx: number = 0; idx < labels.length; ++idx) { + const label = labels[idx]; + if (!label) { + continue; + } + + label_map.set(label.frame, new FrameLabel(label.name, label.frame)); + } +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipBuildSoundsService.ts b/packages/display/src/MovieClip/service/MovieClipBuildSoundsService.ts new file mode 100644 index 00000000..7fe756df --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipBuildSoundsService.ts @@ -0,0 +1,69 @@ +import type { MovieClip } from "../../MovieClip"; +import type { IMovieClipSoundObject } from "../../interface/IMovieClipSoundObject"; +import type { ISoundCharacter } from "../../interface/ISoundCharacter"; +import { $getArray } from "../../DisplayObjectUtil"; +import { + Sound, + SoundMixer +} from "@next2d/media"; + +/** + * @description 指定tagの情報を元に、MovieClipにサウンドを追加 + * Add sound to MovieClip based on specified tag information + * + * @param {MovieClip} movie_clip + * @param {Map} sound_map + * @param {array} sound_objects + * @return {Promise} + * @method + * @public + */ +export const execute = async ( + movie_clip: MovieClip, + sound_map: Map, + sound_objects: IMovieClipSoundObject[] +): Promise => { + + const loaderInfo = movie_clip.loaderInfo; + if (!loaderInfo) { + return ; + } + + const data = loaderInfo.data; + if (!data) { + return ; + } + + const characters = data.characters; + for (let idx = 0; idx < sound_objects.length; ++idx) { + + const object = sound_objects[idx]; + if (!object) { + continue; + } + + const sounds: Sound[] = $getArray(); + for (let idx = 0; idx < object.sound.length; ++idx) { + + const tag = object.sound[idx]; + if (!tag) { + continue; + } + + const character = characters[tag.characterId] as unknown as ISoundCharacter; + if (!character) { + continue; + } + + const sound: Sound = new Sound(); + sound.loopCount = tag.loopCount | 0; + sound.volume = Math.min(SoundMixer.volume, tag.volume); + + await sound.$build(character); + + sounds.push(sound); + } + + sound_map.set(object.frame, sounds); + } +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.test.ts b/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.test.ts new file mode 100644 index 00000000..f6cf8b7f --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.test.ts @@ -0,0 +1,36 @@ +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { execute } from "./MovieClipCurrentFrameLabelService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipCurrentFrameLabelService.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + const frameLabel = new FrameLabel("label", 1); + + movieClip.$labels = new Map(); + movieClip.$labels.set(frameLabel.frame, frameLabel); + expect(movieClip.$labels.size).toBe(1); + + expect(execute(movieClip)).toBe(frameLabel); + }); + + it("execute test case2", () => + { + expect(execute(new MovieClip())).toBe(null); + }); + + it("execute test case3", () => + { + const movieClip = new MovieClip(); + const frameLabel = new FrameLabel("label", 2); + + movieClip.$labels = new Map(); + movieClip.$labels.set(frameLabel.frame, frameLabel); + expect(movieClip.$labels.size).toBe(1); + + expect(execute(movieClip)).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.ts b/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.ts new file mode 100644 index 00000000..a8452204 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipCurrentFrameLabelService.ts @@ -0,0 +1,26 @@ + +import type { FrameLabel } from "../../FrameLabel"; +import type { MovieClip } from "../../MovieClip"; + +/** + * @description 現在のフレームラベルがあれば返却、なければnullを返却 + * Returns the current frame label if it exists, otherwise returns null. + * + * @param {MovieClip} movei_clip + * @return {FrameLabel | null} + * @method + * @protected + */ +export const execute = (movei_clip: MovieClip): FrameLabel | null => +{ + if (!movei_clip.$labels) { + return null; + } + + const frame = movei_clip.currentFrame; + if (!movei_clip.$labels.has(frame)) { + return null; + } + + return movei_clip.$labels.get(frame) || null; +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.test.ts b/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.test.ts new file mode 100644 index 00000000..d902cd43 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.test.ts @@ -0,0 +1,33 @@ +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { execute } from "./MovieClipCurrentLabelsService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipCurrentLabelsService.js test", () => +{ + it("execute test case1", () => + { + expect(execute(new MovieClip())).toBe(null); + }); + + it("execute test case2", () => + { + const movieClip = new MovieClip(); + + movieClip.$labels = new Map(); + + const frameLabel1 = new FrameLabel("label", 2); + const frameLabel2 = new FrameLabel("label", 1); + movieClip.$labels.set(2, frameLabel1); + movieClip.$labels.set(1, frameLabel2); + + const labels = execute(movieClip); + if (!labels) { + throw new Error("labels is null"); + } + + expect(labels?.length).toBe(2); + expect(labels[0]).toBe(frameLabel1); + expect(labels[1]).toBe(frameLabel2); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.ts b/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.ts new file mode 100644 index 00000000..0bdc02a6 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipCurrentLabelsService.ts @@ -0,0 +1,19 @@ + +import type { FrameLabel } from "../../FrameLabel"; +import type { MovieClip } from "../../MovieClip"; + +/** + * @description 指定のMovieClipのフレームラベルを全て返却、なければnullを返却 + * Returns all frame labels of the specified MovieClip, otherwise returns null. + * + * @param {MovieClip} movei_clip + * @return {FrameLabel[] | null} + * @method + * @protected + */ +export const execute = (movei_clip: MovieClip): FrameLabel[] | null => +{ + return !movei_clip.$labels || !movei_clip.$labels.size + ? null + : Array.from(movei_clip.$labels.values()); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.test.ts b/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.test.ts new file mode 100644 index 00000000..27b2150d --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.test.ts @@ -0,0 +1,30 @@ +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { Event } from "@next2d/events"; +import { execute } from "./MovieClipDispatchFrameLabelEventService"; +import { describe, expect, it, vi } from "vitest"; + +describe("MovieClipDispatchFrameLabelEventService.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + const frameLabel = new FrameLabel("label", 1); + + movieClip.$labels = new Map(); + movieClip.$labels.set(1, frameLabel); + + frameLabel.willTrigger = vi.fn().mockReturnValue(true); + + let eventEnd = false; + frameLabel.dispatchEvent = vi.fn((event: Event): boolean => { + expect(event.type).toBe(Event.FRAME_LABEL); + eventEnd = true; + return eventEnd; + }); + + expect(eventEnd).toBe(false); + execute(movieClip, movieClip.$labels); + expect(eventEnd).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.ts b/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.ts new file mode 100644 index 00000000..3c874228 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipDispatchFrameLabelEventService.ts @@ -0,0 +1,30 @@ +import type { MovieClip } from "../../MovieClip"; +import type { FrameLabel } from "../../FrameLabel"; +import { Event } from "@next2d/events"; + +/** + * @description MovieClipのフレームラベルイベントを実行 + * Execute the frame label event of MovieClip + * + * @param {MovieClip} movie_clip + * @param {Map} [labels=null] + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, labels: Map | null = null): void => +{ + if (!labels || !labels.size) { + return ; + } + + const frame = movie_clip.currentFrame; + if (!labels.has(frame)) { + return ; + } + + const frameLabel = labels.get(frame) as FrameLabel; + if (frameLabel.willTrigger(Event.FRAME_LABEL)) { + frameLabel.dispatchEvent(new Event(Event.FRAME_LABEL)); + } +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipGetChildrenService.ts b/packages/display/src/MovieClip/service/MovieClipGetChildrenService.ts new file mode 100644 index 00000000..9721a783 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipGetChildrenService.ts @@ -0,0 +1,191 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { IDisplayObject } from "../../interface/IDisplayObject"; +import type { IMovieClipCharacter } from "../../interface/IMovieClipCharacter"; +import type { MovieClip } from "../../MovieClip"; +import type { Video } from "@next2d/media"; +import { execute as movieClipBuildDictionaryCharacterUseCase } from "../usecase/MovieClipBuildDictionaryCharacterUseCase"; +import { execute as displayObjectContainerRemovedToStageService } from "../../DisplayObjectContainer/service/DisplayObjectContainerRemovedToStageService"; +import { $cacheStore } from "@next2d/cache"; +import { Event } from "@next2d/events"; + +/** + * @description MovieClip の子要素で次のフレームに移動するDisplayObjectの格納マップ + * Storage map of DisplayObjects that move to the next frame in the child elements of MovieClip + * + * @type {Map>} + * @private + */ +const $stayDisplayObjectMap: Map> = new Map(); + +/** + * @description MovieClipの現在のフレームに設置されている子要素の取得 + * Get the child elements placed on the current frame of MovieClip + * + * @param {MovieClip} movie_clip + * @param {DisplayObject[]} children + * @return {DisplayObject[]} + * @method + * @public + */ +export const execute = ( + movie_clip: MovieClip, + children: D[] +): D[] => { + + const loaderInfo = movie_clip.loaderInfo; + if (!loaderInfo || !loaderInfo.data) { + return children; + } + + const character = loaderInfo.data.characters[movie_clip.characterId] as IMovieClipCharacter; + if (!character) { + return children; + } + + const frame = movie_clip.currentFrame; + + const controller: number[] = character.controller[frame]; + if (!controller) { + return children; + } + + const caches = []; + const dictionary = character.dictionary; + if (!children.length) { + + for (let idx = 0; idx < controller.length; ++idx) { + + if (!(idx in controller)) { + continue; + } + + const dictionaryId = controller[idx]; + if (typeof dictionaryId !== "number") { + continue; + } + + const tag = dictionary[dictionaryId]; + if (!tag) { + continue; + } + + const character = loaderInfo.data.characters[tag.characterId]; + if (!character) { + continue; + } + + const displayObject = movieClipBuildDictionaryCharacterUseCase( + dictionaryId, tag, character, movie_clip, idx + ); + + if (!displayObject) { + continue; + } + + children.push(displayObject as D); + + if (displayObject.isContainerEnabled) { + caches.push(displayObject as unknown as DisplayObjectContainer); + } + } + + movie_clip.$container = caches; + + return children; + } + + for (let idx = 0; idx < children.length; ++idx) { + + const displayObject = children[idx]; + if (!displayObject) { + continue; + } + + const startFrame = displayObject.startFrame; + const endFrame = displayObject.endFrame; + if (startFrame === 1 && endFrame === 0 + || startFrame <= frame && endFrame > frame + ) { + $stayDisplayObjectMap.set(displayObject.dictionaryId, displayObject); + continue; + } + + // remove + if (displayObject.uniqueKey && $cacheStore.has(displayObject.uniqueKey)) { + $cacheStore.removeTimer(displayObject.uniqueKey); + } + + if (displayObject.willTrigger(Event.REMOVED)) { + displayObject.dispatchEvent( + new Event(Event.REMOVED, true) + ); + } + + if (displayObject.willTrigger(Event.REMOVED_FROM_STAGE)) { + displayObject.dispatchEvent( + new Event(Event.REMOVED_FROM_STAGE, true) + ); + } + + if (displayObject.isVideo) { + (displayObject as unknown as Video).pause(); + } + + if (displayObject.isContainerEnabled) { + displayObjectContainerRemovedToStageService( + displayObject as unknown as DisplayObjectContainer + ); + } + } + + children.length = 0; + for (let idx = 0; idx < controller.length; ++idx) { + + if (!(idx in controller)) { + continue; + } + + const dictionaryId = controller[idx]; + if (typeof dictionaryId !== "number") { + continue; + } + + if ($stayDisplayObjectMap.has(dictionaryId)) { + const displayObject = $stayDisplayObjectMap.get(dictionaryId) as D; + displayObject.placeId = idx; + displayObject.placeObject = null; + children.push(displayObject); + continue; + } + + const tag = dictionary[dictionaryId]; + if (!tag) { + continue; + } + + const character = loaderInfo.data.characters[tag.characterId]; + if (!character) { + continue; + } + + const displayObject = movieClipBuildDictionaryCharacterUseCase( + dictionaryId, tag, character, movie_clip, idx + ); + if (!displayObject) { + continue; + } + + children.push(displayObject as D); + + if (displayObject.isContainerEnabled) { + caches.push(displayObject as unknown as DisplayObjectContainer); + } + } + + movie_clip.$container = caches; + + $stayDisplayObjectMap.clear(); + + return children; +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.test.ts b/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.test.ts new file mode 100644 index 00000000..e3a7baa6 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.test.ts @@ -0,0 +1,28 @@ +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { execute } from "./MovieClipGetFrameForLabelService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipGetFrameForLabelService.js test", () => +{ + it("execute test case1", () => + { + expect(execute(new MovieClip(), "")).toBe(0); + }); + + it("execute test case2", () => + { + const movieClip = new MovieClip(); + + movieClip.$labels = new Map(); + + const frameLabel1 = new FrameLabel("labelA", 2); + const frameLabel2 = new FrameLabel("labelB", 1); + movieClip.$labels.set(2, frameLabel1); + movieClip.$labels.set(1, frameLabel2); + + expect(execute(movieClip, "labelA")).toBe(2); + expect(execute(movieClip, "labelB")).toBe(1); + expect(execute(movieClip, "labelC")).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.ts b/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.ts new file mode 100644 index 00000000..0e6180d7 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipGetFrameForLabelService.ts @@ -0,0 +1,27 @@ +import type { MovieClip } from "../../MovieClip"; + +/** + * @description ラベル名に対応するフレーム番号を取得します。 + * Get the frame number corresponding to the label name. + * + * @param {MovieClip} movie_clip + * @param {string} name + * @return {number} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, name: string): number => +{ + if (!movie_clip.$labels || !movie_clip.$labels.size) { + return 0; + } + + for (const [frame, frameLabel] of movie_clip.$labels) { + if (frameLabel.name !== name) { + continue; + } + return frame; + } + + return 0; +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipSetActionService.test.ts b/packages/display/src/MovieClip/service/MovieClipSetActionService.test.ts new file mode 100644 index 00000000..c3097a59 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipSetActionService.test.ts @@ -0,0 +1,33 @@ +import { MovieClip } from "../../MovieClip"; +import { $actions } from "../../DisplayObjectUtil"; +import { execute } from "./MovieClipSetActionService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipSetActionService.js test", () => +{ + it("execute test case1", () => + { + const actions = new Map(); + actions.set(1, [() => {}]); + + const movieClip = new MovieClip(); + + $actions.length = 0; + expect($actions.length).toBe(0); + execute(movieClip, actions); + expect($actions.length).toBe(1); + }); + + it("execute test case2", () => + { + const actions = new Map(); + actions.set(2, [() => {}]); + + const movieClip = new MovieClip(); + + $actions.length = 0; + expect($actions.length).toBe(0); + execute(movieClip, actions); + expect($actions.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipSetActionService.ts b/packages/display/src/MovieClip/service/MovieClipSetActionService.ts new file mode 100644 index 00000000..73cc1c94 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipSetActionService.ts @@ -0,0 +1,26 @@ +import type { MovieClip } from "../../MovieClip"; +import { $actions } from "../../DisplayObjectUtil"; + +/** + * @description MovieClipのアクションを配列にセット + * Set the action of MovieClip to an array + * + * @param {MovieClip} movie_clip + * @param {Map} [actions=null] + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, actions: Map | null = null): void => +{ + if (!actions || !actions.size) { + return ; + } + + const frame = movie_clip.currentFrame; + if (!actions.has(frame)) { + return ; + } + + $actions.push(movie_clip); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipSetSoundsService.test.ts b/packages/display/src/MovieClip/service/MovieClipSetSoundsService.test.ts new file mode 100644 index 00000000..a5289e21 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipSetSoundsService.test.ts @@ -0,0 +1,34 @@ +import { MovieClip } from "../../MovieClip"; +import { $sounds } from "../../DisplayObjectUtil"; +import { Sound } from "@next2d/media"; +import { execute } from "./MovieClipSetSoundsService"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipSetSoundsService.js test", () => +{ + it("execute test case1", () => + { + const sounds = new Map(); + sounds.set(1, [new Sound(), new Sound()]); + + const movieClip = new MovieClip(); + + $sounds.length = 0; + expect($sounds.length).toBe(0); + execute(movieClip, sounds); + expect($sounds.length).toBe(1); + }); + + it("execute test case2", () => + { + const sounds = new Map(); + sounds.set(2, [new Sound(), new Sound()]); + + const movieClip = new MovieClip(); + + $sounds.length = 0; + expect($sounds.length).toBe(0); + execute(movieClip, sounds); + expect($sounds.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/service/MovieClipSetSoundsService.ts b/packages/display/src/MovieClip/service/MovieClipSetSoundsService.ts new file mode 100644 index 00000000..d86b1df5 --- /dev/null +++ b/packages/display/src/MovieClip/service/MovieClipSetSoundsService.ts @@ -0,0 +1,27 @@ +import type { MovieClip } from "../../MovieClip"; +import type { Sound } from "@next2d/media"; +import { $sounds } from "../../DisplayObjectUtil"; + +/** + * @description MovieClipのサウンドを配列にセット + * Set the sound of MovieClip to an array + * + * @param {MovieClip} movie_clip + * @param {Map} [sounds=null] + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, sounds: Map | null = null): void => +{ + if (!sounds || !sounds.size) { + return ; + } + + const frame = movie_clip.currentFrame; + if (!sounds.has(frame)) { + return ; + } + + $sounds.push(movie_clip); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.test.ts new file mode 100644 index 00000000..9d93d903 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.test.ts @@ -0,0 +1,57 @@ +import { execute } from "./MovieClipAdvanceFrameUseCase"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipAdvanceFrameUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 10; + movieClip.$wait = true; + + expect(movieClip.changed).toBe(false); + expect(movieClip.$wait).toBe(true); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip); + + expect(movieClip.changed).toBe(true); + expect(movieClip.$wait).toBe(false); + expect(movieClip.currentFrame).toBe(1); + }); + + it("execute test case2", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 10; + + expect(movieClip.changed).toBe(false); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip); + + expect(movieClip.changed).toBe(true); + expect(movieClip.currentFrame).toBe(2); + }); + + it("execute test case3", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.currentFrame = movieClip.totalFrames = 2; + + expect(movieClip.changed).toBe(false); + expect(movieClip.currentFrame).toBe(2); + + execute(movieClip); + + expect(movieClip.changed).toBe(true); + expect(movieClip.currentFrame).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.ts new file mode 100644 index 00000000..adba4137 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipAdvanceFrameUseCase.ts @@ -0,0 +1,44 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as movieClipPrepareActionUseCase } from "./MovieClipPrepareActionUseCase"; +import { execute as movieClipPrepareSoundUseCase } from "./MovieClipPrepareSoundUseCase"; + +/** + * @description フレームを進める + * Advance the frame + * + * @param {MovieClip} movie_clip + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip): void => +{ + if (movie_clip.totalFrames === 1 || !movie_clip.isPlaying) { + return ; + } + + // 待機でなければフレームを進める + if (!movie_clip.$wait) { + ++movie_clip.currentFrame; + if (movie_clip.currentFrame > movie_clip.totalFrames) { + movie_clip.currentFrame = 1; + } + } + + // フラグをリセット + movie_clip.$wait = false; + movie_clip.$canSound = true; + movie_clip.$canAction = true; + movie_clip.$hasTimelineHeadMoved = true; + movie_clip.$container = null; + + // サウンドがあればセット + movieClipPrepareSoundUseCase(movie_clip); + + // アクションがあればセット + movieClipPrepareActionUseCase(movie_clip); + + // 表示オブジェクトの変更を適用 + displayObjectApplyChangesService(movie_clip); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipBuildDictionaryCharacterUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipBuildDictionaryCharacterUseCase.ts new file mode 100644 index 00000000..26e54081 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipBuildDictionaryCharacterUseCase.ts @@ -0,0 +1,72 @@ +import type { IDictionaryTag } from "../../interface/IDictionaryTag"; +import type { ICharacter } from "../../interface/ICharacter"; +import type { IMovieClipCharacter } from "../../interface/IMovieClipCharacter"; +import type { IShapeCharacter } from "../../interface/IShapeCharacter"; +import type { ITextFieldCharacter } from "../../interface/ITextFieldCharacter"; +import type { IVideoCharacter } from "../../interface/IVideoCharacter"; +import type { DisplayObject } from "../../DisplayObject"; +import { MovieClip } from "../../MovieClip"; +import { Shape } from "../../Shape"; +import { TextField } from "@next2d/text"; +import { Video } from "@next2d/media"; +import { execute as displayObjectBaseBuildService } from "../../DisplayObject/service/DisplayObjectBaseBuildService"; + +/** + * @description cahracterを元にDisplayObjectを構築 + * Build DisplayObject based on character + * + * @param {number} dictionary_id + * @param {object} tag + * @param {object} character + * @param {MovieClip} parent + * @return {DisplayObject} + * @method + * @protected + */ +export const execute = ( + dictionary_id: number, + tag: IDictionaryTag, + character: ICharacter, + parent: MovieClip, + placeId: number = -1 +): D => { + + switch (character.extends) { + + case MovieClip.namespace: + { + const movieClip = new MovieClip(); + displayObjectBaseBuildService(movieClip, dictionary_id, tag, parent, placeId); + movieClip.$sync(character as IMovieClipCharacter); + return movieClip as unknown as D; + } + + case Shape.namespace: + { + const shape = new Shape(); + displayObjectBaseBuildService(shape, dictionary_id, tag, parent, placeId); + shape.$sync(character as IShapeCharacter); + return shape as unknown as D; + } + + case TextField.namespace: + { + const textField = new TextField(); + displayObjectBaseBuildService(textField, dictionary_id, tag, parent, placeId); + textField.$sync(character as ITextFieldCharacter); + return textField as unknown as D; + } + + case Video.namespace: + { + const video = new Video(); + displayObjectBaseBuildService(video, dictionary_id, tag, parent, placeId); + video.$sync(character as IVideoCharacter); + return video as unknown as D; + } + + default: + throw new Error(`Character extends not found: ${character.extends}`); + + } +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipBuildFromCharacterUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipBuildFromCharacterUseCase.ts new file mode 100644 index 00000000..c0178892 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipBuildFromCharacterUseCase.ts @@ -0,0 +1,45 @@ +import type { IMovieClipCharacter } from "../../interface/IMovieClipCharacter"; +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipAddActionsService } from "../service/MovieClipAddActionsService"; +import { execute as movieClipAddLabelsService } from "../service/MovieClipAddLabelsService"; +import { execute as movieClipBuildSoundsService } from "../service/MovieClipBuildSoundsService"; + +/** + * @description MovieClipのキャラクター情報を元に、MovieClipを構築 + * Build MovieClip based on character information of MovieClip + * @param {MovieClip} movie_clip + * @param {object} character + * @return {void} + * @method + * @protected + */ +export const execute = ( + movie_clip: MovieClip, + character: IMovieClipCharacter +): void => { + + if (character.actions) { + if (!movie_clip.$actions) { + movie_clip.$actions = new Map(); + } + movieClipAddActionsService(movie_clip.$actions, character.actions); + } + + if (character.sounds) { + if (!movie_clip.$sounds) { + movie_clip.$sounds = new Map(); + } + movieClipBuildSoundsService( + movie_clip, movie_clip.$sounds, character.sounds + ); + } + + if (character.labels) { + if (!movie_clip.$labels) { + movie_clip.$labels = new Map(); + } + movieClipAddLabelsService(movie_clip.$labels, character.labels); + } + + movie_clip.totalFrames = character.totalFrame || 1; +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.test.ts new file mode 100644 index 00000000..86a6c4cb --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.test.ts @@ -0,0 +1,54 @@ +import { execute } from "./MovieClipGoToFrameUseCase"; +import { MovieClip } from "../../MovieClip"; +import { FrameLabel } from "../../FrameLabel"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipGoToFrameUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + movieClip.totalFrames = 3; + + movieClip.$wait = false; + movieClip.$canSound = false; + movieClip.$canAction = false; + movieClip.$hasTimelineHeadMoved = false; + + expect(movieClip.currentFrame).toBe(1); + expect(movieClip.$wait).toBe(false); + expect(movieClip.$canSound).toBe(false); + expect(movieClip.$hasTimelineHeadMoved).toBe(false); + + execute(movieClip, 2); + + expect(movieClip.currentFrame).toBe(2); + expect(movieClip.$wait).toBe(true); + expect(movieClip.$canSound).toBe(true); + expect(movieClip.$hasTimelineHeadMoved).toBe(true); + }); + + it("execute test case1", () => + { + const movieClip = new MovieClip(); + movieClip.addFrameLabel(new FrameLabel("test", 4)); + movieClip.totalFrames = 3; + + movieClip.$wait = false; + movieClip.$canSound = false; + movieClip.$canAction = false; + movieClip.$hasTimelineHeadMoved = false; + + expect(movieClip.currentFrame).toBe(1); + expect(movieClip.$wait).toBe(false); + expect(movieClip.$canSound).toBe(false); + expect(movieClip.$hasTimelineHeadMoved).toBe(false); + + execute(movieClip, "test"); + + expect(movieClip.currentFrame).toBe(3); + expect(movieClip.$wait).toBe(true); + expect(movieClip.$canSound).toBe(true); + expect(movieClip.$hasTimelineHeadMoved).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.ts new file mode 100644 index 00000000..499bf175 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGoToFrameUseCase.ts @@ -0,0 +1,75 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipGetFrameForLabelService } from "../service/MovieClipGetFrameForLabelService"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; +import { execute as movieClipPrepareActionUseCase } from "./MovieClipPrepareActionUseCase"; + +/** + * @description 指定フレームに移動する + * Move to the specified frame + * + * @param {MovieClip} movie_clip + * @param {string | number} value + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, value: string | number): void => +{ + if (movie_clip.totalFrames === 1) { + return ; + } + + let frame = +value; + if (isNaN(frame)) { + frame = movieClipGetFrameForLabelService(movie_clip, `${value}`); + } + + if (1 > frame) { + frame = 1; + } + + // 移動先が現在のフレームと同じ場合は何もしない + if (movie_clip.currentFrame === frame) { + return ; + } + + // 最大フレーム数を超えた場合 + if (frame > movie_clip.totalFrames) { + + if (movie_clip.currentFrame === movie_clip.totalFrames) { + return ; + } + + // フラグを更新 + movie_clip.$wait = true; + movie_clip.$canSound = true; + movie_clip.$canAction = true; + movie_clip.$hasTimelineHeadMoved = true; + + // fixed logic + movie_clip.currentFrame = movie_clip.totalFrames; + + // アクション準備 + movieClipPrepareActionUseCase(movie_clip); + + // 更新 + displayObjectApplyChangesService(movie_clip); + + return ; + } + + // フレームをセット + movie_clip.currentFrame = frame; + + // フラグを更新 + movie_clip.$wait = true; + movie_clip.$canSound = true; + movie_clip.$canAction = true; + movie_clip.$hasTimelineHeadMoved = true; + + // 更新 + displayObjectApplyChangesService(movie_clip); + + // アクション準備 + movieClipPrepareActionUseCase(movie_clip); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.test.ts new file mode 100644 index 00000000..4a1d36d3 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.test.ts @@ -0,0 +1,25 @@ +import { execute } from "./MovieClipGotoAndPlayUseCase"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipGotoAndPlayUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + movieClip.stop(); + + movieClip.changed = false; + movieClip.totalFrames = 3; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(false); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip, 3); + + expect(movieClip.changed).toBe(true); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(3); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.ts new file mode 100644 index 00000000..1a18b994 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGotoAndPlayUseCase.ts @@ -0,0 +1,18 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipGoToFrameUseCase } from "./MovieClipGoToFrameUseCase"; + +/** + * @description 指定フレームに移動して再生する + * Move to the specified frame and play + * + * @param {MovieClip} movie_clip + * @param {string | number} frame + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, frame: string | number): void => +{ + movie_clip.play(); + movieClipGoToFrameUseCase(movie_clip, frame); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.test.ts new file mode 100644 index 00000000..a592c9e8 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./MovieClipGotoAndStopUseCase"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipGotoAndStopUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 3; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip, 3); + + expect(movieClip.changed).toBe(true); + expect(movieClip.isPlaying).toBe(false); + expect(movieClip.currentFrame).toBe(3); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.ts new file mode 100644 index 00000000..65f3d05d --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipGotoAndStopUseCase.ts @@ -0,0 +1,19 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipGoToFrameUseCase } from "./MovieClipGoToFrameUseCase"; + +/** + * @description 指定フレームに移動して停止する + * Move to the specified frame and stop + * + * @param {MovieClip} movie_clip + * @param {string | number} frame + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip, frame: string | number): void => +{ + movie_clip.stop(); + movieClipGoToFrameUseCase(movie_clip, frame); + movie_clip.$wait = false; +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.test.ts new file mode 100644 index 00000000..d85e48c1 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.test.ts @@ -0,0 +1,42 @@ +import { execute } from "./MovieClipNextFrameUseCase"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipNextFrameUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 3; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip); + + expect(movieClip.changed).toBe(true); + expect(movieClip.isPlaying).toBe(false); + expect(movieClip.currentFrame).toBe(2); + }); + + it("execute test case2", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 1; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip); + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.ts new file mode 100644 index 00000000..dd6cc1f7 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipNextFrameUseCase.ts @@ -0,0 +1,21 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipGoToFrameUseCase } from "./MovieClipGoToFrameUseCase"; + +/** + * @description 次のフレームに移動して停止する + * Move to the next frame and stop + * + * @param {MovieClip} movie_clip + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip): void => +{ + if (movie_clip.totalFrames <= movie_clip.currentFrame) { + return ; + } + + movie_clip.stop(); + movieClipGoToFrameUseCase(movie_clip, movie_clip.currentFrame + 1); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.test.ts new file mode 100644 index 00000000..45dcd4f4 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.test.ts @@ -0,0 +1,26 @@ +import { execute } from "./MovieClipPrepareActionUseCase"; +import { MovieClip } from "../../MovieClip"; +import { $actions } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipPrepareActionUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + movieClip.$actions = new Map(); + movieClip.$actions.set(1, [() => {}]); + + movieClip.$canAction = true; + $actions.length = 0; + + expect(movieClip.$canAction).toBe(true); + expect($actions.length).toBe(0); + + execute(movieClip); + + expect(movieClip.$canAction).toBe(false); + expect($actions.length).toBe(1); + $actions.length = 0; + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.ts new file mode 100644 index 00000000..e4e6bc38 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrepareActionUseCase.ts @@ -0,0 +1,27 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipDispatchFrameLabelEventService } from "../service/MovieClipDispatchFrameLabelEventService"; +import { execute as movieClipSetActionService } from "../service/MovieClipSetActionService"; + +/** + * @description MovieClipのFrameLabel Eventを発火して、アクションを登録 + * Fire the FrameLabel Event of MovieClip and register the action + * + * @param {MovieClip} movie_clip + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip): void => +{ + if (!movie_clip.$canAction) { + return ; + } + + movie_clip.$canAction = false; + + // dispatch frame label event + movieClipDispatchFrameLabelEventService(movie_clip, movie_clip.$labels); + + // set frame action + movieClipSetActionService(movie_clip, movie_clip.$actions); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.test.ts new file mode 100644 index 00000000..9c348b3f --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./MovieClipPrepareSoundUseCase"; +import { MovieClip } from "../../MovieClip"; +import { $sounds } from "../../DisplayObjectUtil"; +import { describe, expect, it } from "vitest"; +import { Sound } from "@next2d/media"; + +describe("MovieClipPrepareSoundUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + movieClip.$sounds = new Map(); + movieClip.$sounds.set(1, [new Sound()]); + + movieClip.$canSound = true; + $sounds.length = 0; + + expect(movieClip.$canSound).toBe(true); + expect($sounds.length).toBe(0); + + execute(movieClip); + + expect(movieClip.$canSound).toBe(false); + expect($sounds.length).toBe(1); + $sounds.length = 0; + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.ts new file mode 100644 index 00000000..85d31b5a --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrepareSoundUseCase.ts @@ -0,0 +1,23 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipSetSoundsService } from "../service/MovieClipSetSoundsService"; + +/** + * @description MovieClipの現在のフレームのサウンドを登録 + * Register the sound of the current frame of MovieClip + * + * @param {MovieClip} movie_clip + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip): void => +{ + if (!movie_clip.$canSound) { + return ; + } + + movie_clip.$canSound = false; + + // set frame sound + movieClipSetSoundsService(movie_clip, movie_clip.$sounds); +}; \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.test.ts b/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.test.ts new file mode 100644 index 00000000..325d3b82 --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./MovieClipPrevFrameUseCase"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("MovieClipPrevFrameUseCase.js test", () => +{ + it("execute test case1", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.currentFrame = 3; + movieClip.totalFrames = 3; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(3); + + execute(movieClip); + + expect(movieClip.changed).toBe(true); + expect(movieClip.isPlaying).toBe(false); + expect(movieClip.currentFrame).toBe(2); + }); + + it("execute test case2", () => + { + const movieClip = new MovieClip(); + + movieClip.changed = false; + movieClip.totalFrames = 3; + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + + execute(movieClip); + + expect(movieClip.changed).toBe(false); + expect(movieClip.isPlaying).toBe(true); + expect(movieClip.currentFrame).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.ts b/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.ts new file mode 100644 index 00000000..b8908ace --- /dev/null +++ b/packages/display/src/MovieClip/usecase/MovieClipPrevFrameUseCase.ts @@ -0,0 +1,21 @@ +import type { MovieClip } from "../../MovieClip"; +import { execute as movieClipGoToFrameUseCase } from "./MovieClipGoToFrameUseCase"; + +/** + * @description ムービークリップの再生を一つ前のフレームに移動させる + * Moves the playback of a movie clip to the previous frame. + * + * @param {MovieClip} movie_clip + * @return {void} + * @method + * @protected + */ +export const execute = (movie_clip: MovieClip): void => +{ + if (2 > movie_clip.currentFrame) { + return ; + } + + movie_clip.stop(); + movieClipGoToFrameUseCase(movie_clip, movie_clip.currentFrame - 1); +}; \ No newline at end of file diff --git a/packages/display/src/Shape.ts b/packages/display/src/Shape.ts index 4cea3ec1..461e8497 100644 --- a/packages/display/src/Shape.ts +++ b/packages/display/src/Shape.ts @@ -1,53 +1,21 @@ +import type { ICharacter } from "./interface/ICharacter"; +import type { LoaderInfo } from "./LoaderInfo"; +import type { IShapeCharacter } from "./interface/IShapeCharacter"; import { DisplayObject } from "./DisplayObject"; import { Graphics } from "./Graphics"; import { BitmapData } from "./BitmapData"; -import { Rectangle } from "@next2d/geom"; -import { Event } from "@next2d/events"; -import type { LoaderInfo } from "./LoaderInfo"; -import type { CanvasToWebGLContext } from "@next2d/webgl"; -import type { - BoundsImpl, - ShapeCharacterImpl, - CapsStyleImpl, - JointStyleImpl, - ParentImpl, - DictionaryTagImpl, - FilterArrayImpl, - BlendModeImpl, - PlayerHitObjectImpl, - PropertyMessageMapImpl, - PropertyShapeMessageImpl, - Character, - PropertyMessageImpl -} from "@next2d/interface"; +import { execute as shapeClearBitmapBufferService } from "./Shape/usecase/ShapeClearBitmapBufferUseCase"; +import { execute as shapeSetBitmapBufferUseCase } from "./Shape/usecase/ShapeSetBitmapBufferUseCase"; +import { execute as shapeLoadSrcUseCase } from "./Shape/usecase/ShapeLoadSrcUseCase"; +import { execute as shapeBuildFromCharacterUseCase } from "./Shape/usecase/ShapeBuildFromCharacterUseCase"; import { - $MATRIX_HIT_ARRAY_IDENTITY, - $getRenderBufferArray, - $getRenderMessageObject, - $poolRenderMessageObject, - $rendererWorker -} from "@next2d/util"; -import { - $getArray, - $poolArray, - $getFloat32Array6, - $getBoundsObject, - $poolBoundsObject, - $multiplicationMatrix, - $boundsMatrix, - $poolFloat32Array6, - $multiplicationColor, - $clamp, - $poolFloat32Array8, - $Math, - $COLOR_ARRAY_IDENTITY -} from "@next2d/share"; + $graphicMap, + $getArray +} from "./DisplayObjectUtil"; /** - * Shape クラスには、Graphics クラスからメソッドにアクセスできる graphics プロパティがあります。 - * - * The Shape class includes a graphics property, - * which lets you access methods from the Graphics class. + * @description Shape クラスは、ベクターグラフィックスを表示するための表示オブジェクトです。 + * The Shape class is a display object for displaying vector graphics. * * @class * @memberOf next2d.display @@ -55,52 +23,95 @@ import { */ export class Shape extends DisplayObject { + /** + * @type {Graphics} + * @default null + * @private + */ private _$graphics: Graphics | null; + + /** + * @type {string} + * @default "" + * @private + */ private _$src: string; /** - * @constructor + * @description Shapeの機能を所持しているかを返却 + * Returns whether the display object has Shape functionality. + * + * @type {boolean} + * @readonly * @public */ - constructor () - { - super(); + public readonly isShape: boolean; - /** - * @type {Graphics} - * @default null - * @private - */ - this._$graphics = null; + /** + * @description ビルドされたキャッシュキー + * Built cache key + * + * @type {number} + * @default 0 + * @public + */ + public cacheKey: number; - /** - * @type {string} - * @default "" - * @private - */ - this._$src = ""; - } + /** + * @description キャッシュのビルドに利用されるパラメータ + * Parameters used to build the cache + * + * @type {number[]} + * @public + */ + public readonly cacheParams: number[]; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description ビットマップ描画の判定フラグ + * Bitmap drawing judgment flag * - * @return {string} - * @default [class Shape] - * @method - * @static + * @type {boolean} + * @default false + * @protected */ - static toString (): string + public isBitmap: boolean; + + /** + * @description 画像RGBAのUint8Array + * Image RGBA Uint8Array + * + * @type {Uint8Array|null} + * @default null + * @protected + */ + public $bitmapBuffer: Uint8Array | null; + + /** + * @constructor + * @public + */ + constructor () { - return "[class Shape]"; + super(); + + this.isShape = true; + this.cacheKey = 0; + this.cacheParams = $getArray(0, 0, 0); + + // private + this._$graphics = null; + this._$src = ""; + + // bitmap + this.$bitmapBuffer = null; + this.isBitmap = false; } /** * @description 指定されたクラスの空間名を返します。 * Returns the space name of the specified class. * - * @return {string} - * @default next2d.display.Shape + * @return {string} * @const * @static */ @@ -109,26 +120,11 @@ export class Shape extends DisplayObject return "next2d.display.Shape"; } - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object Shape] - * @method - * @public - */ - toString (): string - { - return "[object Shape]"; - } - /** * @description 指定されたオブジェクトの空間名を返します。 * Returns the space name of the specified object. * - * @return {string} - * @default next2d.display.Shape + * @return {string} * @const * @public */ @@ -138,9 +134,8 @@ export class Shape extends DisplayObject } /** - * @description ベクターの描画コマンドが発生するこのスプライトに属する Graphics オブジェクトを指定します。 - * Specifies the Graphics object that belongs to this sprite - * where vector drawing commands can occur. + * @description この Shape オブジェクトに描画されるベクターの描画コマンドを保持する Graphics オブジェクトです。 + * The Graphics object that belongs to this Shape object, where vector drawing commands can occur. * * @member {Graphics} * @readonly @@ -149,7 +144,8 @@ export class Shape extends DisplayObject get graphics (): Graphics { if (!this._$graphics) { - this._$graphics = new Graphics(this); + this._$graphics = new Graphics(); + $graphicMap.set(this._$graphics, this); } return this._$graphics; } @@ -168,587 +164,80 @@ export class Shape extends DisplayObject } set src (src: string) { - if (!src) { + if (this._$src === src) { return ; } - const image: HTMLImageElement = new Image(); - image.addEventListener("load", () => - { - const width: number = image.width; - const height: number = image.height; + this._$src = src; - const bitmapData: BitmapData = new BitmapData(width, height); - bitmapData.image = image; - - this - .graphics - .beginBitmapFill(bitmapData) - .drawRect(0, 0, width, height); - - if (this.hasEventListener(Event.LOAD)) { - this.dispatchEvent(new Event(Event.LOAD)); - } - }); - - this._$src = image.src = src; - this.graphics._$mode = "bitmap"; + shapeLoadSrcUseCase(this, src); } /** - * @param {object} character - * @param {LoaderInfo} loaderInfo - * @return {void} - * @method - * @private - */ - _$buildCharacter ( - character: Character, - loaderInfo: LoaderInfo - ): void { - - const graphics: Graphics = this.graphics; - - if (!loaderInfo._$data) { - throw new Error("the loaderInfo data is null."); - } - - if (character.recodes) { - - switch (true) { - - case character.bitmapId > 0: - { - const bitmap: Character = loaderInfo - ._$data - .characters[character.bitmapId]; - - if (!bitmap.buffer) { - throw new Error("the bitmap buffer is null."); - } - - const width: number = $Math.abs(bitmap.bounds.xMax - bitmap.bounds.xMin); - const height: number = $Math.abs(bitmap.bounds.yMax - bitmap.bounds.yMin); - - const bitmapData: BitmapData = new BitmapData(width, height); - if (!bitmap._$buffer) { - bitmap._$buffer = new Uint8Array(bitmap.buffer); - $poolArray(bitmap.buffer); - bitmap.buffer = null; - } - bitmapData.buffer = bitmap._$buffer.slice(); - - // setup - graphics._$recode = $getArray(); - - if (width === character.bounds.xMax - character.bounds.xMin - && height === character.bounds.yMax - character.bounds.yMin - ) { - graphics._$bitmapId = character.bitmapId; - graphics._$mode = "bitmap"; - } - - // clone - const recodes: any[] = character.recodes; - if (recodes[recodes.length - 1] === Graphics.END_FILL) { - - const length: number = recodes.length - 6; - for (let idx: number = 0; idx < length; ++idx) { - graphics._$recode.push(recodes[idx]); - } - - // add Bitmap Fill - graphics._$recode.push( - Graphics.BITMAP_FILL, - bitmapData, - null, - "repeat", - false - ); - - } else { - - const width: number = recodes[recodes.length - 9]; - const caps: CapsStyleImpl = recodes[recodes.length - 8]; - const joints: JointStyleImpl = recodes[recodes.length - 7]; - const miterLimit: number = recodes[recodes.length - 6]; - - const length: number = recodes.length - 10; - for (let idx: number = 0; idx < length; ++idx) { - graphics._$recode.push(recodes[idx]); - } - - graphics._$recode.push( - Graphics.BITMAP_STROKE, - width, - caps, - joints, - miterLimit, - bitmapData, - $getFloat32Array6( - 1, 0, 0, 1, character.bounds.xMin, character.bounds.yMin - ), - "repeat", - false - ); - } - } - break; - - case character.inBitmap: - { - // setup - graphics._$recode = $getArray(); - - const recodes: any[] = character.recodes; - for (let idx: number = 0; idx < recodes.length; ++idx) { - - const value: BitmapData = recodes[idx]; - graphics._$recode[idx] = value; - - if (typeof value !== "object") { - continue; - } - - if (!value.buffer) { - continue; - } - - const bitmapData: BitmapData = new BitmapData( - value.width, value.height - ); - bitmapData.buffer = new Uint8Array(value.buffer); - graphics._$recode[idx++] = bitmapData; - - const matrix: number[] = recodes[idx]; - graphics._$recode[idx] = $getFloat32Array6( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - if (value.width === character.bounds.xMax - character.bounds.xMin - && value.height === character.bounds.yMax - character.bounds.yMin - ) { - graphics._$bitmapId = character.bitmapId; - graphics._$mode = "bitmap"; - } - } - } - break; - - default: - graphics._$recode = character.recodes.slice(0); - break; - - } - - } else { - - graphics._$mode = "bitmap"; - - const width: number = $Math.abs(character.bounds.xMax - character.bounds.xMin); - const height: number = $Math.abs(character.bounds.yMax - character.bounds.yMin); - - const bitmapData: BitmapData = new BitmapData(width, height); - if (!character._$buffer) { - if (!character.buffer) { - throw new Error("the bitmap buffer is null."); - } - character._$buffer = new Uint8Array(character.buffer); - $poolArray(character.buffer); - character.buffer = null; - } - bitmapData.buffer = character._$buffer.slice(0); - - graphics - .beginBitmapFill(bitmapData, null, false) - .drawRect(0, 0, width, height); - - } - - graphics._$maxAlpha = 1; - graphics._$canDraw = true; - - graphics._$xMin = character.bounds.xMin; - graphics._$xMax = character.bounds.xMax; - graphics._$yMin = character.bounds.yMin; - graphics._$yMax = character.bounds.yMax; - - // 9-scale - if (character.grid) { - this._$scale9Grid = new Rectangle( - character.grid.x, character.grid.y, - character.grid.w, character.grid.h - ); - } - - if ($rendererWorker && this._$stage) { - this._$createWorkerInstance(); - } - } - - /** - * @param {object} character - * @return {void} - * @method - * @protected + * @description ビットマップデータを返します + * Returns the bitmap data. + * + * @return {BitmapData} + * @readonly + * @public */ - _$sync (character: ShapeCharacterImpl): void + get bitmapData (): BitmapData { - if (this._$loaderInfo) { - this._$buildCharacter(character, this._$loaderInfo); + const bitmapData = new BitmapData(this.width, this.height); + if (this.$bitmapBuffer) { + bitmapData.buffer = this.$bitmapBuffer; } + return bitmapData; } /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private - */ - _$build ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): ShapeCharacterImpl { - - const character: ShapeCharacterImpl = this - ._$baseBuild(tag, parent); - - this._$buildCharacter(character, parent._$loaderInfo); - - return character; - } - - /** - * @param {Float32Array} [matrix=null] - * @returns {object} - * @method - * @private - */ - _$getBounds ( - matrix: Float32Array | null = null - ): BoundsImpl { - - if (!this._$graphics) { - return $getBoundsObject(0, 0, 0, 0); - } - - const baseBounds: BoundsImpl = this._$graphics._$getBounds(); - if (!matrix) { - return baseBounds; - } - - let multiMatrix: Float32Array = matrix; - - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - $poolBoundsObject(baseBounds); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return bounds; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform + * @description ビットマップデータを解放します + * Releases bitmap data. + * * @return {void} * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible) { - return ; - } - - if (!this._$graphics || !this._$graphics._$canDraw) { - return ; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$transform._$rawColorTransform(); - if (rawColor !== $COLOR_ARRAY_IDENTITY - && rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - return ; - } - - const filters: FilterArrayImpl | null = this._$filters || this.filters; - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix !== $MATRIX_HIT_ARRAY_IDENTITY - && rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - this - ._$graphics - ._$draw(context, multiMatrix, multiColor, blendMode, filters); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @returns {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - if (!this._$graphics) { - return ; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - this._$graphics._$clip(context, multiMatrix); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @return {boolean} - * @method - * @private - */ - _$mouseHit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl - ): boolean { - - if (!this._$visible) { - return false; - } - - return this._$hit(context, matrix, options); - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @param {boolean} [is_clip=false] - * @return {boolean} - * @method - * @private + * @public */ - _$hit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl, - is_clip: boolean = false - ): boolean { - - let hit: boolean = false; - - const graphics: Graphics | null = this._$graphics; - if (graphics - && graphics._$canDraw - && graphics._$getBounds() - ) { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - hit = graphics._$hit( - context, - multiMatrix, - options, - is_clip - ); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - } - - return hit; + clearBitmapBuffer (): void + { + shapeClearBitmapBufferService(this); } /** - * @return {void} + * @description RGBAの画像データを設定します + * Sets the RGBA image data. + * + * @param {number} width + * @param {number} height + * @param {Uint8Array} buffer * @method - * @private + * @public */ - _$createWorkerInstance (): void + setBitmapBuffer (width: number, height: number, buffer: Uint8Array): void { - if (this._$created || !$rendererWorker) { - return ; - } - - // update flag - this._$created = true; - this._$posted = true; - this._$updated = false; - - const buffer: Float32Array = $getRenderBufferArray(); - - let index: number = 0; - buffer[index++] = this._$instanceId; - buffer[index++] = this._$parent ? this._$parent._$instanceId : -1; - buffer[index++] = 0; // maxAlpha - buffer[index++] = 0; // canDraw - - // graphics - const graphics: Graphics | null = this._$graphics; - if (graphics - && !graphics._$posted - && graphics._$maxAlpha > 0 - && graphics._$canDraw - ) { - - graphics._$posted = true; - - const message: PropertyMessageImpl = $getRenderMessageObject(); - const recodes: Float32Array = graphics._$getRecodes(); - - message.command = `shapeRecodes@${this._$instanceId}`; - message.buffer = recodes; - - const options: ArrayBuffer[] = $getArray(recodes.buffer); - $rendererWorker.postMessage(message, options); - - $poolRenderMessageObject(message); - $poolArray(options); - - buffer[2] = graphics._$maxAlpha; - buffer[3] = +graphics._$canDraw; - } - - // bounds - const bounds: BoundsImpl = this._$getBounds(); - buffer[index++] = bounds.xMin; - buffer[index++] = bounds.yMin; - buffer[index++] = bounds.xMax; - buffer[index++] = bounds.yMax; - - // characterId - buffer[index++] = this._$characterId > -1 ? this._$characterId : -1; - - // loaderInfoId - buffer[index++] = this._$loaderInfo ? this._$loaderInfo._$id : -1; - - // property - this._$registerProperty(buffer, index); - - const message: PropertyMessageMapImpl = $getRenderMessageObject(); - message.command = "createShape"; - message.buffer = buffer; - - const options: ArrayBuffer[] = $getArray(buffer.buffer); - $rendererWorker.postMessage(message, options); - - $poolRenderMessageObject(message); - $poolArray(options); + shapeSetBitmapBufferUseCase( + this, + width, + height, + buffer + ); } /** + * @description character 情報を元に DisplayObject を構築 + * Build DisplayObject based on character + * + * @param {ICharacter} character + * @param {LoaderInfo} [loader_info=null] * @return {void} * @method - * @private + * @protected */ - _$postProperty (): void + $sync (character: ICharacter, loader_info: LoaderInfo | null = null): void { - if (!this._$created || !$rendererWorker) { - return ; + if (loader_info) { + super.$syncLoaderInfo(loader_info); } - - const message: PropertyMessageMapImpl = this._$createMessage(); - - const graphics: Graphics | null = this._$graphics; - if (graphics && !graphics._$posted) { - - message.maxAlpha = graphics._$maxAlpha; - message.canDraw = graphics._$canDraw; - - const recodes: Float32Array = graphics._$getRecodes(); - message.recodes = recodes; - - const options: ArrayBuffer[] = $getArray(recodes.buffer); - - const bounds: BoundsImpl = this._$getBounds(); - message.xMin = bounds.xMin; - message.yMin = bounds.yMin; - message.xMax = bounds.xMax; - message.yMax = bounds.yMax; - - $rendererWorker - .postMessage(message, options); - - $poolArray(options); - - } else { - - $rendererWorker - .postMessage(message); - - } - - this._$posted = true; - this._$updated = false; + shapeBuildFromCharacterUseCase(this, character as IShapeCharacter); } -} +} \ No newline at end of file diff --git a/packages/display/src/Shape/service/ShapeGetRawBoundsService.test.ts b/packages/display/src/Shape/service/ShapeGetRawBoundsService.test.ts new file mode 100644 index 00000000..dd331d8b --- /dev/null +++ b/packages/display/src/Shape/service/ShapeGetRawBoundsService.test.ts @@ -0,0 +1,21 @@ +import { execute } from "./ShapeGetRawBoundsService"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; + +describe("ShapeRawBoundsService.js test", () => +{ + it("execute test case", () => + { + const shape = new Shape(); + shape.graphics.xMin = 1; + shape.graphics.yMin = 2; + shape.graphics.xMax = 3; + shape.graphics.yMax = 4; + const bounds = execute(shape); + + expect(bounds[0]).toBe(1); + expect(bounds[1]).toBe(2); + expect(bounds[2]).toBe(3); + expect(bounds[3]).toBe(4); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Shape/service/ShapeGetRawBoundsService.ts b/packages/display/src/Shape/service/ShapeGetRawBoundsService.ts new file mode 100644 index 00000000..e204ee4e --- /dev/null +++ b/packages/display/src/Shape/service/ShapeGetRawBoundsService.ts @@ -0,0 +1,21 @@ +import type { Shape } from "../../Shape"; +import { $getBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description Shapeのローカルバウンディングボックスを取得します。 + * Get the local bounding box of the Shape. + * + * @param {Shape} shape + * @return {Float32Array} + * @protected + */ +export const execute = (shape: Shape): Float32Array => +{ + const graphics = shape.graphics; + return $getBoundsArray( + graphics.xMin, + graphics.yMin, + graphics.xMax, + graphics.yMax + ); +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeBuildFromCharacterUseCase.ts b/packages/display/src/Shape/usecase/ShapeBuildFromCharacterUseCase.ts new file mode 100644 index 00000000..fce45561 --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeBuildFromCharacterUseCase.ts @@ -0,0 +1,182 @@ +import type { Shape } from "../../Shape"; +import type { IShapeCharacter } from "../../interface/IShapeCharacter"; +import type { LoaderInfo } from "../../LoaderInfo"; +import { execute as graphicsToNumberArrayService } from "../../Graphics/service/GraphicsToNumberArrayService"; +import { $poolArray } from "../../DisplayObjectUtil"; +import { BitmapData } from "../../BitmapData"; +import { Graphics } from "../../Graphics"; +import { Rectangle } from "@next2d/geom"; + +/** + * @description characterを元にShapeを構築 + * Build Shape based on character + * + * @param {Shape} shape + * @param {object} character + * @return {void} + * @method + * @protected + */ +export const execute = (shape: Shape, character: IShapeCharacter): void => +{ + const graphics = shape.graphics; + + const width = Math.ceil(Math.abs(character.bounds.xMax - character.bounds.xMin)); + const height = Math.ceil(Math.abs(character.bounds.yMax - character.bounds.yMin)); + + switch (true) { + + case character.bitmapId > 0: + { + const loaderInfo = shape.loaderInfo as NonNullable; + const bitmap = loaderInfo.data?.characters[character.bitmapId] as IShapeCharacter; + if (!bitmap) { + break; + } + + shape.isBitmap = true; + + if (!bitmap.imageBuffer) { + bitmap.imageBuffer = new Uint8Array(bitmap.buffer as number[]); + bitmap.buffer = null; + } + + const bitmapWidth = Math.ceil(Math.abs(bitmap.bounds.xMax - bitmap.bounds.xMin)); + const bitmapHeight = Math.ceil(Math.abs(bitmap.bounds.yMax - bitmap.bounds.yMin)); + + if (character.grid) { + + // 9slice image + if (character.recodes) { + const recodes = character.recodes.slice(0); + recodes.splice(-6, 6); + + const bitmapData = new BitmapData(bitmapWidth, bitmapHeight); + bitmapData.buffer = bitmap.imageBuffer; + recodes.push( + Graphics.BITMAP_FILL, + bitmapData, + null, + true, + false, + 9 + ); + + const numberArray = graphicsToNumberArrayService(recodes); + graphics.buffer = new Float32Array(numberArray); + + $poolArray(recodes); + $poolArray(numberArray); + } + + } else { + + // draw image + if (width === bitmapWidth && height === bitmapHeight) { + + shape.setBitmapBuffer( + Math.ceil(Math.abs(bitmap.bounds.xMax - bitmap.bounds.xMin)), + Math.ceil(Math.abs(bitmap.bounds.yMax - bitmap.bounds.yMin)), + bitmap.imageBuffer + ); + + } else { + + const bitmapData = new BitmapData(bitmapWidth, bitmapHeight); + bitmapData.buffer = bitmap.imageBuffer; + + if (character.recodes) { + const type = character.recodes[character.recodes.length - 10]; + if (type === Graphics.BITMAP_STROKE) { + + const recodes = character.recodes.slice(0); + recodes.splice(-5, 5); + recodes.push( + bitmapData, null, true, false + ); + + const numberArray = graphicsToNumberArrayService(recodes); + graphics.buffer = new Float32Array(numberArray); + + $poolArray(recodes); + $poolArray(numberArray); + + } else { + + graphics + .beginBitmapFill(bitmapData) + .drawRect(0, 0, width, height); + + } + } + } + } + } + break; + + case character.inBitmap: // to swf only + { + shape.isBitmap = true; + + const recodes = character.recodes as any[]; + const bitmap = recodes[recodes.length - 4]; + if (width === bitmap.width && height === bitmap.height) { + shape.setBitmapBuffer( + bitmap.width, + bitmap.height, + bitmap.buffer + ); + } else { + const bitmapData = new BitmapData(bitmap.width, bitmap.height); + bitmapData.buffer = bitmap.buffer; + graphics + .beginBitmapFill(bitmapData) + .drawRect(0, 0, width, height); + } + } + break; + + case "buffer" in character: // bitmap + if (!character.imageBuffer) { + character.imageBuffer = new Uint8Array(character.buffer as number[]); + character.buffer = null; + } + + shape.setBitmapBuffer( + Math.ceil(Math.abs(graphics.xMax - graphics.xMin)), + Math.ceil(Math.abs(graphics.yMax - graphics.yMin)), + character.imageBuffer + ); + break; + + default: // normal shape + if (character.recodes) { + character.recodeBuffer = new Float32Array( + graphicsToNumberArrayService(character.recodes) + ); + $poolArray(character.recodes); + character.recodes = null; + } + + if (character.recodeBuffer) { + graphics.buffer = character.recodeBuffer; + } + + break; + } + + // fixed logic + graphics.xMin = character.bounds.xMin; + graphics.xMax = character.bounds.xMax; + graphics.yMin = character.bounds.yMin; + graphics.yMax = character.bounds.yMax; + + if (character.grid) { + shape.scale9Grid = new Rectangle( + character.grid.x, + character.grid.y, + character.grid.w, + character.grid.h + ); + } +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.test.ts b/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.test.ts new file mode 100644 index 00000000..3b8816de --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.test.ts @@ -0,0 +1,54 @@ +import { execute } from "./ShapeCalcBoundsMatrixUseCase"; +import { Shape } from "../../Shape"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("ShapeCalcBoundsMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const shape = new Shape(); + shape.graphics.xMin = 1; + shape.graphics.yMin = 2; + shape.graphics.xMax = 3; + shape.graphics.yMax = 4; + + const bounds = execute(shape); + expect(bounds[0]).toBe(1); + expect(bounds[1]).toBe(2); + expect(bounds[2]).toBe(3); + expect(bounds[3]).toBe(4); + }); + + it("execute test case2", () => + { + const shape = new Shape(); + shape.$matrix = new Matrix(1.3, 0.5, 0.2, 1.2, 110, 220); + shape.graphics.xMin = 1; + shape.graphics.yMin = 2; + shape.graphics.xMax = 3; + shape.graphics.yMax = 4; + + const bounds = execute(shape); + expect(bounds[0]).toBe(111.69999694824219); + expect(bounds[1]).toBe(222.89999389648438); + expect(bounds[2]).toBe(114.69999694824219); + expect(bounds[3]).toBe(226.3000030517578); + }); + + it("execute test case3", () => + { + const shape = new Shape(); + shape.$matrix = new Matrix(0.9, 0.25, -0.2, 1.5, 10, 20); + shape.graphics.xMin = 1; + shape.graphics.yMin = 2; + shape.graphics.xMax = 3; + shape.graphics.yMax = 4; + + const bounds = execute(shape, new Float32Array([1.3, 0.5, 0.2, 1.2, 110, 220])); + expect(bounds[0]).toBe(128.3000030517578); + expect(bounds[1]).toBe(253.14999389648438); + expect(bounds[2]).toBe(130.82000732421875); + expect(bounds[3]).toBe(258.04998779296875); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.ts b/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.ts new file mode 100644 index 00000000..d8691b7e --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeCalcBoundsMatrixUseCase.ts @@ -0,0 +1,47 @@ +import type { Shape } from "../../Shape"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as shapeGetRawBoundsService } from "../service/ShapeGetRawBoundsService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description Shapeの描画範囲を計算します。 + * Calculate the drawing area of Shape. + * + * @param {Shape} shape + * @param {Float32Array | null} [matrix=null] + * @return {number[]} + * @method + * @protected + */ +export const execute = (shape: Shape, matrix: Float32Array | null = null): Float32Array => +{ + const rawBounds = shapeGetRawBoundsService(shape); + + const rawMatrix = displayObjectGetRawMatrixUseCase(shape); + if (!rawMatrix) { + if (matrix) { + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix + ); + $poolBoundsArray(rawBounds); + + return calcBounds; + } + + return rawBounds; + } + + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix ? Matrix.multiply(matrix, rawMatrix) : rawMatrix + ); + + $poolBoundsArray(rawBounds); + + return calcBounds; +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeClearBitmapBufferUseCase.ts b/packages/display/src/Shape/usecase/ShapeClearBitmapBufferUseCase.ts new file mode 100644 index 00000000..8cf3f87c --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeClearBitmapBufferUseCase.ts @@ -0,0 +1,30 @@ +import type { Shape } from "../../Shape"; +import { $cacheStore } from "@next2d/cache"; +import { execute as displayObjectApplyChangesService } from "../../DisplayObject/service/DisplayObjectApplyChangesService"; + +/** + * @description BitmapBufferの設定をクリア + * Clear the BitmapBuffer settings + * + * @param {Shape} shape + * @return {void} + * @method + * @protected + */ +export const execute = (shape: Shape): void => +{ + shape.isBitmap = false; + shape.$bitmapBuffer = null; + + // graphics clear + shape.graphics.clear(); + + // cache clear + if (shape.uniqueKey !== "" && $cacheStore.has(shape.uniqueKey)) { + $cacheStore.removeById(shape.uniqueKey); + $cacheStore.$removeIds.push(+shape.uniqueKey); + } + + // apply changes + displayObjectApplyChangesService(shape); +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeGenerateClipQueueUseCase.ts b/packages/display/src/Shape/usecase/ShapeGenerateClipQueueUseCase.ts new file mode 100644 index 00000000..35bd4b4e --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeGenerateClipQueueUseCase.ts @@ -0,0 +1,43 @@ +import type { Shape } from "../../Shape"; +import { Matrix } from "@next2d/geom"; +import { renderQueue } from "@next2d/render-queue"; +import { $RENDERER_SHAPE_TYPE } from "../../DisplayObjectUtil"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description renderer workerに渡すShapeのマスク描画データを生成 + * Generate mask drawing data of Shape to pass to renderer worker + * + * @param {Shape} shape + * @param {Float32Array} matrix + * @return {void} + * @method + * @protected + */ +export const execute = ( + shape: Shape, + matrix: Float32Array +): void => { + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(shape); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + const hasGrid: boolean = rawMatrix && shape.scale9Grid + ? Math.abs(rawMatrix[1]) < 0.001 && Math.abs(rawMatrix[2]) < 0.0001 + : false; + + const buffer = shape.graphics.buffer; + renderQueue.push( + $RENDERER_SHAPE_TYPE, + tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], tMatrix[4], tMatrix[5], + +hasGrid, buffer.length + ); + renderQueue.set(buffer); +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeGenerateRenderQueueUseCase.ts b/packages/display/src/Shape/usecase/ShapeGenerateRenderQueueUseCase.ts new file mode 100644 index 00000000..460bdfe2 --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeGenerateRenderQueueUseCase.ts @@ -0,0 +1,372 @@ +import type { Shape } from "../../Shape"; +import type { Rectangle } from"@next2d/geom"; +import type { MovieClip } from "../../MovieClip"; +import { renderQueue } from"@next2d/render-queue"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as displayObjectGenerateHashService } from "../../DisplayObject/service/DisplayObjectGenerateHashService"; +import { execute as displayObjectBlendToNumberService } from "../../DisplayObject/service/DisplayObjectBlendToNumberService"; +import { stage } from "../../Stage"; +import { $cacheStore } from "@next2d/cache"; +import { + $clamp, + $RENDERER_SHAPE_TYPE, + $getArray, + $poolArray, + $poolBoundsArray, + $MATRIX_ARRAY_IDENTITY, + $getFloat32Array6, + $poolFloat32Array6, + $getBoundsArray +} from "../../DisplayObjectUtil"; +import { + ColorTransform, + Matrix +} from "@next2d/geom"; + +/** + * @description renderer workerに渡すShapeの描画データを生成 + * Generate drawing data of Shape to pass to renderer + * + * @param {Shape} shape + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {void} + * @method + * @protected + */ +export const execute = ( + shape: Shape, + matrix: Float32Array, + color_transform: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): void => { + + if (!shape.visible) { + renderQueue.push(0); + return ; + } + + const graphics = shape.graphics; + const isDrawable = graphics.isDrawable; + if (!isDrawable && !shape.isBitmap) { + renderQueue.push(0); + return ; + } + + // transformed ColorTransform(tColorTransform) + const rawColor = displayObjectGetRawColorTransformUseCase(shape); + const tColorTransform = rawColor + ? ColorTransform.multiply(color_transform, rawColor) + : color_transform; + + const alpha = $clamp(tColorTransform[3] + tColorTransform[7] / 255, 0, 1, 0); + if (!alpha) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + renderQueue.push(0); + return ; + } + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(shape); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + const bounds = displayObjectCalcBoundsMatrixService( + graphics.xMin, graphics.yMin, + graphics.xMax, graphics.yMax, + tMatrix + ); + + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + $poolBoundsArray(bounds); + + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + switch (true) { + + case width === 0: + case height === 0: + case width === -Infinity: + case height === -Infinity: + case width === Infinity: + case height === Infinity: + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + $poolBoundsArray(bounds); + renderQueue.push(0); + return; + + default: + break; + + } + + if (point_x > xMin + width + || point_y > yMin + height + || xMin > renderer_width + || yMin > renderer_height + ) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + $poolBoundsArray(bounds); + renderQueue.push(0); + return; + } + + const isGridEnabled: boolean = rawMatrix && shape.scale9Grid + ? Math.abs(rawMatrix[1]) < 0.001 && Math.abs(rawMatrix[2]) < 0.0001 + : false; + + if (!shape.uniqueKey) { + if (shape.characterId && shape.loaderInfo) { + + const values = $getArray( + shape.loaderInfo.id, + shape.characterId + ); + + shape.uniqueKey = `${displayObjectGenerateHashService(new Float32Array(values))}`; + $poolArray(values); + + } else { + + shape.uniqueKey = shape.isBitmap + ? `${shape.instanceId}` + : `${displayObjectGenerateHashService(graphics.buffer)}`; + + } + } + + const xScale = Math.round(Math.sqrt( + tMatrix[0] * tMatrix[0] + + tMatrix[1] * tMatrix[1] + ) * 10000) / 10000; + + const yScale = Math.round(Math.sqrt( + tMatrix[2] * tMatrix[2] + + tMatrix[3] * tMatrix[3] + ) * 10000) / 10000; + + if (!shape.isBitmap + && !shape.cacheKey + || shape.cacheParams[0] !== xScale + || shape.cacheParams[1] !== yScale + || shape.cacheParams[2] !== tColorTransform[7] + ) { + shape.cacheKey = $cacheStore.generateKeys(xScale, yScale, tColorTransform[7]); + shape.cacheParams[0] = xScale; + shape.cacheParams[1] = yScale; + shape.cacheParams[2] = tColorTransform[7]; + } + + const cacheKey = shape.isBitmap + ? 0 + : shape.cacheKey; + + // rennder on + renderQueue.push( + 1, $RENDERER_SHAPE_TYPE, + tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], tMatrix[4], tMatrix[5], + tColorTransform[0], tColorTransform[1], tColorTransform[2], tColorTransform[3], + tColorTransform[4], tColorTransform[5], tColorTransform[6], tColorTransform[7], + xMin, yMin, xMax, yMax, + graphics.xMin, graphics.yMin, + graphics.xMax, graphics.yMax, + +isGridEnabled, +isDrawable, +shape.isBitmap, + +shape.uniqueKey, cacheKey + ); + + if (shape.$cache && !shape.$cache.has(shape.uniqueKey)) { + shape.$cache = null; + } + + const cache = shape.$cache + ? shape.$cache.get(`${cacheKey}`) + : $cacheStore.get(shape.uniqueKey, `${cacheKey}`); + + if (!cache) { + + renderQueue.push(0); + + if (isGridEnabled) { + + const scale = stage.rendererScale; + + const stageMatrix = $getFloat32Array6( + scale, 0, 0, scale, 0, 0 + ); + + const pMatrix = Matrix.multiply( + stageMatrix, + rawMatrix ? rawMatrix : $MATRIX_ARRAY_IDENTITY + ); + $poolFloat32Array6(stageMatrix); + + const rawData = (shape.parent as MovieClip).concatenatedMatrix.rawData; + const aMatrix = $getFloat32Array6( + rawData[0], rawData[1], rawData[2], rawData[3], + rawData[4] * scale - xMin, + rawData[5] * scale - yMin + ); + Matrix.release(rawData); + + const apMatrix = Matrix.multiply(aMatrix, pMatrix); + const aOffsetX = apMatrix[4] - (tMatrix[4] - xMin); + const aOffsetY = apMatrix[5] - (tMatrix[5] - yMin); + $poolFloat32Array6(apMatrix); + + const parentBounds = displayObjectCalcBoundsMatrixService( + graphics.xMin, graphics.yMin, + graphics.xMax, graphics.yMax, + pMatrix + ); + + const parentXMin = parentBounds[0]; + const parentYMin = parentBounds[1]; + const parentXMax = parentBounds[2]; + const parentYMax = parentBounds[3]; + $poolBoundsArray(parentBounds); + + const parentWidth = Math.ceil(Math.abs(parentXMax - parentXMin)); + const parentHeight = Math.ceil(Math.abs(parentYMax - parentYMin)); + + const scale9Grid = shape.scale9Grid as Rectangle; + + const actualWidth = Math.abs(graphics.xMax - graphics.xMin); + const actualHeight = Math.abs(graphics.yMax - graphics.yMin); + + // 等倍サイズでの正規化 + const minXST = scale9Grid.width > 0 ? (scale9Grid.x - graphics.xMin) / actualWidth : 0.00001; + const minYST = scale9Grid.height > 0 ? (scale9Grid.y - graphics.yMin) / actualHeight : 0.00001; + const maxXST = scale9Grid.width > 0 ? (scale9Grid.x + scale9Grid.width - graphics.xMin) / actualWidth : 0.99999; + const maxYST = scale9Grid.height > 0 ? (scale9Grid.y + scale9Grid.height - graphics.yMin) / actualHeight : 0.99999; + + // 現在サイズでの正規化 + const sameWidth = Math.ceil(actualWidth * scale); + const sameHeight = Math.ceil(actualHeight * scale); + let minXPQ = sameWidth * minXST / parentWidth; + let minYPQ = sameHeight * minYST / parentHeight; + let maxXPQ = (parentWidth - sameWidth * (1 - maxXST)) / parentWidth; + let maxYPQ = (parentHeight - sameHeight * (1 - maxYST)) / parentHeight; + + if (minXPQ >= maxXPQ) { + const m = minXST / (minXST + (1 - maxXST)); + minXPQ = Math.max(m - 0.00001, 0); + maxXPQ = Math.min(m + 0.00001, 1); + } + + if (minYPQ >= maxYPQ) { + const m = minYST / (minYST + (1 - maxYST)); + minYPQ = Math.max(m - 0.00001, 0); + maxYPQ = Math.min(m + 0.00001, 1); + } + + renderQueue.push( + pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5], + aMatrix[0], aMatrix[1], aMatrix[2], aMatrix[3], aMatrix[4] - aOffsetX, aMatrix[5] - aOffsetY, + parentXMin, parentYMin, parentWidth, parentHeight, + minXST, minYST, minXPQ, minYPQ, + maxXST, maxYST, maxXPQ, maxYPQ + ); + + $poolFloat32Array6(aMatrix); + $poolFloat32Array6(pMatrix); + } + + const buffer = isDrawable || isGridEnabled + ? graphics.buffer + : shape.$bitmapBuffer as Uint8Array; + + renderQueue.push(buffer.length); + renderQueue.set(buffer); + + $cacheStore.set(shape.uniqueKey, `${cacheKey}`, true); + + if (shape.$cache) { + shape.$cache = null; + } + + } else { + if (!shape.$cache) { + shape.$cache = $cacheStore.getById(shape.uniqueKey); + shape.$cache.set(shape.uniqueKey, true); + } + renderQueue.push(1); + } + + renderQueue.push( + displayObjectBlendToNumberService(shape.blendMode) + ); + + if (shape.filters?.length) { + + let updated = false; + const params = []; + const bounds = $getBoundsArray(0, 0, 0, 0); + for (let idx = 0; idx < shape.filters.length; idx++) { + + const filter = shape.filters[idx]; + if (!filter || !filter.canApplyFilter()) { + continue; + } + + // フィルターが更新されたかをチェック + if (filter.$updated) { + updated = true; + } + filter.$updated = false; + + filter.getBounds(bounds); + + const buffer = filter.toNumberArray(); + + for (let idx = 0; idx < buffer.length; idx += 4096) { + params.push(...buffer.subarray(idx, idx + 4096)); + } + } + + const useFilfer = params.length > 0; + if (useFilfer) { + renderQueue.push( + +useFilfer, +updated, + bounds[0], bounds[1], bounds[2], bounds[3], + params.length + ); + renderQueue.set(new Float32Array(params)); + } + + $poolBoundsArray(bounds); + } else { + renderQueue.push(0); + } + + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeHitTestUseCase.ts b/packages/display/src/Shape/usecase/ShapeHitTestUseCase.ts new file mode 100644 index 00000000..0d6c917d --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeHitTestUseCase.ts @@ -0,0 +1,54 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { Shape } from "../../Shape"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as graphicsHitTestService } from "../../Graphics/service/GraphicsHitTestService"; + +/** + * @description Shape のヒット判定 + * Hit judgment of Shape + * + * @param {Shape} shape + * @param {CanvasRenderingContext2D} hit_context + * @param {Float32Array} matrix + * @param {IPlayerHitObject} hit_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + shape: Shape, + hit_context: CanvasRenderingContext2D, + matrix: Float32Array, + hit_object: IPlayerHitObject +): boolean => { + + const graphics = shape.graphics; + + const width = graphics.xMax - graphics.xMin; + const height = graphics.yMax - graphics.yMin; + if (width <= 0 || height <= 0 ) { + return false; + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(shape); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + hit_context.beginPath(); + hit_context.setTransform( + tMatrix[0], tMatrix[1], tMatrix[2], + tMatrix[3], tMatrix[4], tMatrix[5] + ); + + const hit = graphicsHitTestService( + hit_context, graphics.buffer, hit_object + ); + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + return hit; +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeLoadSrcUseCase.ts b/packages/display/src/Shape/usecase/ShapeLoadSrcUseCase.ts new file mode 100644 index 00000000..72d9d418 --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeLoadSrcUseCase.ts @@ -0,0 +1,45 @@ +import type { Shape } from "../../Shape"; +import { $cacheStore } from "@next2d/cache"; +import { Event } from "@next2d/events"; +import { execute as shapeSetBitmapBufferUseCase } from "./ShapeSetBitmapBufferUseCase"; + +/** + * @description 指定のURLを画像を読み込み、Shapeに設定 + * Load the specified URL image and set it to Shape + * + * @param {Shape} shape + * @param {string} src + * @return {void} + * @method + * @protected + */ +export const execute = (shape: Shape, src: string): void => +{ + const image = new Image(); + image.crossOrigin = "anonymous"; + image.addEventListener("load", (): void => + { + const width = image.width; + const height = image.height; + + const canvas = $cacheStore.getCanvas(); + canvas.width = width; + canvas.height = height; + + const context = canvas.getContext("2d", { "willReadFrequently": true }) as CanvasRenderingContext2D; + context.drawImage(image, 0, 0, width, height); + + shapeSetBitmapBufferUseCase( + shape, width, height, + new Uint8Array(context.getImageData(0, 0, width, height).data) + ); + + $cacheStore.destroy(context); + + if (shape.hasEventListener(Event.COMPLETE)) { + shape.dispatchEvent(new Event(Event.COMPLETE)); + } + }); + + image.src = src; +}; \ No newline at end of file diff --git a/packages/display/src/Shape/usecase/ShapeSetBitmapBufferUseCase.ts b/packages/display/src/Shape/usecase/ShapeSetBitmapBufferUseCase.ts new file mode 100644 index 00000000..3356c937 --- /dev/null +++ b/packages/display/src/Shape/usecase/ShapeSetBitmapBufferUseCase.ts @@ -0,0 +1,32 @@ +import type { Shape } from "../../Shape"; +import { execute as shapeClearBitmapBufferService } from "./ShapeClearBitmapBufferUseCase"; + +/** + * @description BitmapBufferを設定 + * Set BitmapBuffer + * + * @param {Shape} shape + * @param {number} width + * @param {number} height + * @param {Uint8Array} buffer + * @return {void} + * @method + * @protected + */ +export const execute = ( + shape: Shape, + width: number, + height: number, + buffer: Uint8Array +): void => { + + shapeClearBitmapBufferService(shape); + + shape.isBitmap = true; + shape.$bitmapBuffer = buffer; + + shape.graphics.xMin = 0; + shape.graphics.yMin = 0; + shape.graphics.xMax = width; + shape.graphics.yMax = height; +}; \ No newline at end of file diff --git a/packages/display/src/Sprite.ts b/packages/display/src/Sprite.ts index d6675702..a07e9f99 100644 --- a/packages/display/src/Sprite.ts +++ b/packages/display/src/Sprite.ts @@ -1,30 +1,16 @@ +import type { ISprite } from "./interface/ISprite"; +import type { Rectangle } from "@next2d/geom"; +import type { SoundTransform } from "@next2d/media"; import { DisplayObjectContainer } from "./DisplayObjectContainer"; -import { SoundTransform } from "@next2d/media"; -import type { - Rectangle, - Point -} from "@next2d/geom"; -import type { - SpriteImpl, - DropTargetImpl, - DictionaryTagImpl, - ParentImpl, - MovieClipCharacterImpl -} from "@next2d/interface"; -import { - $currentMousePoint, - $dragRules, - $dropTarget, - $rendererWorker, - $setDropTarget -} from "@next2d/util"; +import { execute as spriteStartDragService } from "./Sprite/service/SpriteStartDragService"; +import { execute as spriteStopDragService } from "./Sprite/service/SpriteStopDragService"; /** - * Sprite クラスは、表示リストの基本的要素です。 - * グラフィックを表示でき、子を持つこともできる表示リストノードです。 + * @description Sprite クラスは、表示リストの基本的要素です。 + * グラフィックを表示でき、子を持つこともできる表示リストノードです。 * - * The Sprite class is a basic display list building block: - * a display list node that can display graphics and can also contain children. + * The Sprite class is a basic display list building block: + * a display list node that can display graphics and can also contain children. * * @class * @memberOf next2d.display @@ -32,171 +18,123 @@ import { */ export class Sprite extends DisplayObjectContainer { - protected _$buttonMode: boolean; - protected _$hitArea: SpriteImpl | null; - protected _$soundTransform: SoundTransform | null; - protected _$useHandCursor: boolean; - /** - * @constructor + * @description このスプライトのボタンモードを指定します。 + * Specifies the button mode of this sprite. + * + * @member {boolean} + * @default false * @public */ - constructor () - { - super(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$buttonMode = false; - - /** - * @type {Sprite|null} - * @default null - * @private - */ - this._$hitArea = null; - - /** - * @type {SoundTransform} - * @default null - * @private - */ - this._$soundTransform = null; + public buttonMode: boolean; - /** - * @type {boolean} - * @default true - * @private - */ - this._$useHandCursor = true; - } + /** + * @description buttonMode プロパティが true に設定されたスプライト上にポインターが移動したときに、 + * 指差しハンドポインター(ハンドカーソル)を表示するかどうかを示すブール値です。 + * A Boolean value that indicates whether the pointing hand (hand cursor) + * appears when the pointer rolls over a sprite + * in which the buttonMode property is set to true. + * + * @member {boolean} + * @default true + * @public + */ + public useHandCursor: boolean; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description Spriteの機能を所持しているかを返却 + * Returns whether Sprite functions are possessed. * - * @return {string} - * @default [class Sprite] - * @method - * @static + * @type {boolean} + * @readonly + * @public */ - static toString (): string - { - return "[class Sprite]"; - } + public readonly isSprite: boolean; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description スプライトのヒット領域となる別のスプライトを指定します。 + * Designates another sprite to serve as the hit area for a sprite. * - * @return {string} - * @default next2d.display.Sprite - * @const - * @static + * @type {Sprite|null} + * @private */ - static get namespace (): string - { - return "next2d.display.Sprite"; - } + public hitArea: ISprite | null; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @type {SoundTransform|null} + * @private + */ + private _$soundTransform: SoundTransform | null; + + /** + * @description ドラッグ時のオフセットX + * Offset X during drag * - * @return {string} - * @default [object Sprite] - * @method + * @type {number} * @public */ - toString (): string - { - return "[object Sprite]"; - } + public $offsetX: number = 0; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description ドラッグ時のオフセットY + * Offset Y during drag * - * @return {string} - * @default next2d.display.Sprite - * @const + * @type {number} * @public */ - get namespace (): string - { - return "next2d.display.Sprite"; - } + public $offsetY: number = 0; /** - * @description このスプライトのボタンモードを指定します。 - * Specifies the button mode of this sprite. + * @description 中心をロックするかどうか + * Whether to lock the center * - * @member {boolean} - * @default false + * @type {boolean} * @public */ - get buttonMode (): boolean - { - return this._$buttonMode; - } - set buttonMode (button_mode: boolean) - { - this._$buttonMode = !!button_mode; - } + public $lockCenter: boolean = false; /** - * @description スプライトのドラッグ先またはスプライトがドロップされた先の表示オブジェクトを指定します。 - * Specifies the display object over which the sprite is being dragged, - * or on which the sprite was dropped. + * @description バウンドされた矩形 + * Bounded rectangle * - * @member {DisplayObject|null} - * @readonly + * @type {Rectangle|null} * @public */ - get dropTarget (): DropTargetImpl - { - return $dropTarget; - } + public $boundedRect: Rectangle | null = null; /** - * @description スプライトのヒット領域となる別のスプライトを指定します。 - * Designates another sprite to serve as the hit area for a sprite. - * - * @member {Sprite|null} + * @constructor * @public */ - get hitArea (): SpriteImpl | null - { - return this._$hitArea; - } - set hitArea (hit_area: SpriteImpl | null) + constructor () { - // reset - if (this._$hitArea) { - this._$hitArea._$hitObject = null; - } + super(); + + // public + this.isSprite = true; + this.buttonMode = false; + this.useHandCursor = true; + this.hitArea = null; - this._$hitArea = hit_area; - if (hit_area) { - hit_area._$hitObject = this; - } + // drag rules + this.$offsetX = 0; + this.$offsetY = 0; + this.$lockCenter = false; + this.$boundedRect = null; + + // private + this._$soundTransform = null; } /** * @description このスプライト内のサウンドを制御します。 * Controls sound within this sprite. * - * @member {SoundTransform} + * @member {SoundTransform | null} * @public */ - get soundTransform (): SoundTransform + get soundTransform (): SoundTransform | null { - if (!this._$soundTransform) { - this._$soundTransform = new SoundTransform(); - } return this._$soundTransform; } set soundTransform (sound_transform: SoundTransform | null) @@ -204,26 +142,6 @@ export class Sprite extends DisplayObjectContainer this._$soundTransform = sound_transform; } - /** - * @description buttonMode プロパティが true に設定されたスプライト上にポインターが移動したときに、 - * 指差しハンドポインター(ハンドカーソル)を表示するかどうかを示すブール値です。 - * A Boolean value that indicates whether the pointing hand (hand cursor) - * appears when the pointer rolls over a sprite - * in which the buttonMode property is set to true. - * - * @member {boolean} - * @default true - * @public - */ - get useHandCursor (): boolean - { - return this._$useHandCursor; - } - set useHandCursor (use_hand_cursor: boolean) - { - this._$useHandCursor = use_hand_cursor; - } - /** * @description 指定されたスプライトをユーザーがドラッグできるようにします。 * Lets the user drag the specified sprite. @@ -238,21 +156,7 @@ export class Sprite extends DisplayObjectContainer lock_center: boolean = false, bounds: Rectangle | null = null ): void { - - let x: number = 0; - let y: number = 0; - - if (!lock_center) { - const point: Point = this._$dragMousePoint(); - x = this.x - point.x; - y = this.y - point.y; - } - - $setDropTarget(this); - $dragRules.lock = lock_center; - $dragRules.position.x = x; - $dragRules.position.y = y; - $dragRules.bounds = bounds; + spriteStartDragService(this, lock_center, bounds); } /** @@ -265,68 +169,6 @@ export class Sprite extends DisplayObjectContainer */ stopDrag () { - // reset - $setDropTarget(null); - $dragRules.lock = false; - $dragRules.position.x = 0; - $dragRules.position.y = 0; - $dragRules.bounds = null; - } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$sync (character: MovieClipCharacterImpl): void - { - if ($rendererWorker && this._$stage) { - this._$createWorkerInstance(); - } - - this._$controller = character.controller; - this._$dictionary = character.dictionary; - this._$placeMap = character.placeMap; - this._$placeObjects = character.placeObjects; - } - - /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private - */ - _$build ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): MovieClipCharacterImpl { - - const character: MovieClipCharacterImpl = this - ._$baseBuild(tag, parent); - - if ($rendererWorker && this._$stage) { - this._$createWorkerInstance(); - } - - this._$controller = character.controller; - this._$dictionary = character.dictionary; - this._$placeMap = character.placeMap; - this._$placeObjects = character.placeObjects; - - return character; - } - - /** - * @return {Point} - * @method - * @private - */ - _$dragMousePoint (): Point - { - return this._$parent - ? this._$parent.globalToLocal($currentMousePoint()) - : this.globalToLocal($currentMousePoint()); + spriteStopDragService(this); } -} +} \ No newline at end of file diff --git a/packages/display/src/Sprite/service/SpriteStartDragService.ts b/packages/display/src/Sprite/service/SpriteStartDragService.ts new file mode 100644 index 00000000..15aeb274 --- /dev/null +++ b/packages/display/src/Sprite/service/SpriteStartDragService.ts @@ -0,0 +1,35 @@ +import type { Sprite } from "../../Sprite"; +import type { Rectangle } from "@next2d/geom"; +import { + $pointer, + $setDraggingDisplayObject +} from "../../DisplayObjectUtil"; + +/** + * @description ドラッグを開始します。 + * Starts dragging. + * + * @param {Sprite} sprite + * @param {boolean} [lock_center=false] + * @param {Rectangle|null} [bounds=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + sprite: D, + lock_center: boolean = false, + bounds: Rectangle | null = null +): void => { + + const point = sprite.parent + ? sprite.parent.globalToLocal($pointer) + : sprite.globalToLocal($pointer); + + sprite.$lockCenter = lock_center; + sprite.$offsetX = sprite.x - point.x; + sprite.$offsetY = sprite.y - point.y; + sprite.$boundedRect = bounds; + + $setDraggingDisplayObject(sprite); +}; \ No newline at end of file diff --git a/packages/display/src/Sprite/service/SpriteStopDragService.ts b/packages/display/src/Sprite/service/SpriteStopDragService.ts new file mode 100644 index 00000000..8586d1b0 --- /dev/null +++ b/packages/display/src/Sprite/service/SpriteStopDragService.ts @@ -0,0 +1,21 @@ +import type { Sprite } from "../../Sprite"; +import { $setDraggingDisplayObject } from "../../DisplayObjectUtil"; + +/** + * @description startDrag() メソッドを終了して、ドラッグ変数をリセットします。 + * Ends the startDrag() method and resets the drag variables. + * + * @param {Sprite} sprite + * @return {void} + * @method + * @protected + */ +export const execute = (sprite: D): void => +{ + sprite.$lockCenter = false; + sprite.$offsetX = 0; + sprite.$offsetY = 0; + sprite.$boundedRect = null; + + $setDraggingDisplayObject(null); +}; \ No newline at end of file diff --git a/packages/display/src/Stage.ts b/packages/display/src/Stage.ts index 90e92599..6174e2ea 100644 --- a/packages/display/src/Stage.ts +++ b/packages/display/src/Stage.ts @@ -1,16 +1,20 @@ +import type { DisplayObject } from "./DisplayObject"; +import type { IPlayerHitObject } from "./interface/IPlayerHitObject"; +import type { Point } from "@next2d/geom"; import { DisplayObjectContainer } from "./DisplayObjectContainer"; -import type { Player } from "@next2d/core"; -import type { DisplayObjectImpl } from "@next2d/interface"; +import { execute as stageReadyUseCase } from "./Stage/usecase/StageReadyUseCase"; +import { execute as stageGenerateRenderQueueUseCase } from "./Stage/usecase/StageGenerateRenderQueueUseCase"; +import { execute as stageTickerUseCase } from "./Stage/usecase/StageTickerUseCase"; +import { execute as displayObjectContainerMouseHitUseCase } from "./DisplayObjectContainer/usecase/DisplayObjectContainerMouseHitUseCase"; import { - $clamp, - $toColorInt, - $uintToRGBA, - $devicePixelRatio -} from "@next2d/share"; + $pointer, + $rootMap, + $stageAssignedMap +} from "./DisplayObjectUtil"; /** - * Stage クラスはメイン描画領域を表します。 - * The Stage class represents the main drawing area. + * @description Stage クラスはメイン描画領域を表します。 + * The Stage class represents the main drawing area. * * @class * @memberOf next2d.display @@ -18,295 +22,237 @@ import { */ export class Stage extends DisplayObjectContainer { - public _$player: Player | null; - public _$invalidate: boolean; - private _$color: number; - public _$frameRate: number; - /** - * @constructor - * @public + * @description 初期起動の準備完了したかどうか + * Whether the initial startup is ready + * + * @type {boolean} + * @default false + * @private */ - constructor () - { - super(); - - /** - * @type {Player} - * @default null - * @private - */ - this._$player = null; - - /** - * @type {Stage} - * @private - */ - this._$root = this; - - /** - * @type {Stage} - * @private - */ - this._$stage = this; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$invalidate = true; - - /** - * @type {number} - * @default 0xffffffff - * @private - */ - this._$color = 0xffffffff; - - /** - * @type {number} - * @default 60 - * @private - */ - this._$frameRate = 60; - } + private _$ready: boolean; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description ステージ幅 + * Stage width * - * @return {string} - * @default [class Stage] - * @method - * @static + * @type {number} + * @public */ - static toString (): string - { - return "[class Stage]"; - } + public stageWidth: number; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description ステージ高さ + * Stage height * - * @return {string} - * @default next2d.display.Stage - * @const - * @static + * @type {number} + * @public */ - static get namespace (): string - { - return "next2d.display.Stage"; - } + public stageHeight: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description フレームレート + * Frame rate * - * @return {string} - * @default [object Stage] - * @method + * @type {number} * @public */ - toString (): string - { - return "[object Stage]"; - } + public frameRate: number; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description devicePixelRatioを含んだcanvasの描画領域の拡大率 + * The magnification of the drawing area of the canvas including devicePixelRatio * - * @return {string} - * @default next2d.display.Stage - * @const + * @member {number} + * @default 1 * @public */ - get namespace (): string - { - return "next2d.display.Stage"; - } + public rendererScale: number; /** - * @description 背景色です。 - * background color. + * @description devicePixelRatioを含んだcanvasの描画領域の幅 + * The width of the drawing area of the canvas including devicePixelRatio * * @member {number} + * @default 0 * @public */ - get color (): number - { - return this._$color; - } - set color (color: number) - { - this._$color = $clamp($toColorInt(color), 0, 0xffffff, 0xffffff); - const player: Player | null = this._$player; - if (player && player.context) { - const rgba = $uintToRGBA(this._$color); - player - .context - ._$setColor( - rgba.R / 255, - rgba.G / 255, - rgba.B / 255, - rgba.A / 255 - ); - } - } + public rendererWidth: number; /** - * @description ステージのフレームレートを取得または設定します。 - * Gets and sets the frame rate of the stage. + * @description devicePixelRatioを含んだcanvasの描画領域の高さ + * The height of the drawing area of the canvas including devicePixelRatio * * @member {number} + * @default 0 * @public */ - get frameRate () - { - return this._$frameRate; - } - set frameRate (frame_rate) - { - this._$frameRate = $clamp(+frame_rate, 1, 60, 60); - if (this._$player && !this._$player._$stopFlag) { - this._$player.stop(); - this._$player.play(); - } - } + public rendererHeight: number; /** - * @description Player オブジェクトを返します。 - * Returns a Player object. + * @description 背景色 + * Background color * - * @member {Player} - * @readonly + * @type {number} * @public */ - get player (): Player | null - { - return this._$player; - } + private _$backgroundColor: number; /** - * @description 現在のCanvasの高さをピクセル単位で指定します。 - * Specifies the height of the current Canvas in pixels. - * - * @member {number} - * @readonly + * @constructor * @public */ - get canvasHeight (): number + constructor () { - return this._$player - ? this._$player._$height / $devicePixelRatio - : 0; + super(); + + this.stageWidth = 0; + this.stageHeight = 0; + this.frameRate = 1; + + this.rendererScale = 1; + this.rendererWidth = 0; + this.rendererHeight = 0; + + // private + this._$ready = false; + this._$backgroundColor = -1; } /** - * @description 現在のCanvasの幅をピクセル単位で指定します。 - * Specifies the width of the current Canvas in pixels. + * @description 背景色 + * Background color * * @member {number} - * @readonly * @public */ - get canvasWidth (): number + get backgroundColor (): number { - return this._$player - ? this._$player._$width / $devicePixelRatio - : 0; + return this._$backgroundColor; + } + set backgroundColor (color: string) + { + this._$backgroundColor = color === "transparent" + ? -1 + : parseInt(color.replace("#", ""), 16); } /** - * @description 現在のStageの高さをピクセル単位で指定します。 - * Specifies the height of the current Stage in pixels. + * @description ポインターの最終座標 + * The final coordinates of the pointer * - * @member {number} + * @type {Point} * @readonly * @public */ - get currentStageHeight (): number + get pointer (): Point { - return this._$player - ? this._$player.height * this._$player._$scale - : 0; + return $pointer; } /** - * @description 現在のStageの幅をピクセル単位で指定します。 - * Specifies the width of the current Stage in pixels. + * @description 初期起動の準備完了したかどうか + * Whether the initial startup is ready * - * @member {number} - * @readonly + * @type {boolean} + * @writeonly * @public */ - get currentStageWidth (): number + set ready (ready: boolean) { - return this._$player - ? this._$player.width * this._$player._$scale - : 0; + if (!ready || this._$ready) { + return ; + } + + this._$ready = ready; + + // Stage の起動準備完了のUseCase + stageReadyUseCase(this); } /** - * @description 初期設定したステージの高さをピクセル単位で指定します。 - * Specifies the height of the initially set stage in pixels. + * @description Stage に追加した DisplayObject は rootとして rootMap に追加 + * DisplayObject added to Stage is added to rootMap as root * - * @member {number} - * @readonly + * @param {DisplayObject} display_object + * @return {DisplayObject} + * @method * @public */ - get stageHeight (): number + addChild(display_object: T): T { - return this._$player ? this._$player.height : 0; + $rootMap.set(display_object, display_object); + $stageAssignedMap.add(display_object.instanceId); + + return super.addChild(display_object); } /** - * @description 初期設定したステージの幅をピクセル単位で指定します。 - * Specifies the width of the initially set stage in pixels. + * @description Stage に追加した DisplayObject の定期処理、描画処理を実行 + * Execute regular processing and drawing processing of DisplayObject added to Stage * - * @member {number} - * @readonly - * @public + * @return {void} + * @method + * @protected */ - get stageWidth (): number + $ticker (): void { - return this._$player ? this._$player.width : 0; + stageTickerUseCase(); } /** - * @description 表示リストをレンダリングする必要のある次の機会に、 - * 表示オブジェクトに警告するようランタイムに通知します。 - * (例えば、再生ヘッドを新しいフレームに進める場合などです。) - * Calling the invalidate() method signals runtimes - * to alert display objects on the next opportunity - * it has to render the display list. - * (for example, when the playhead advances to a new frame) + * @description renderer workerに渡す描画データを生成 + * Generate drawing data to pass to the renderer worker * + * @param {ImageBitmap[]} image_bitmaps + * @param {Float32Array} matrix * @return {void} * @method - * @public + * @protected */ - invalidate (): void - { - this._$invalidate = true; + $generateRenderQueue ( + display_object: D, + image_bitmaps: ImageBitmap[], + matrix: Float32Array, + color_transform: Float32Array + ): void { + stageGenerateRenderQueueUseCase( + display_object, + image_bitmaps, + matrix, + color_transform, + this.rendererWidth, + this.rendererHeight, + -matrix[4], + -matrix[5] + ); } /** - * @param {DisplayObject} child - * @return {DisplayObject} + * @description タップポイントの当たり判定 + * Hit test of tap point + * + * @param {CanvasRenderingContext2D} hit_context + * @param {Float32Array} matrix + * @param {IPlayerHitObject} hit_object + * @return {void} * @method - * @private + * @protected */ - _$addChild (child: DisplayObjectImpl): DisplayObjectImpl - { - child._$stage = this; - child._$root = child; - - // worker flag updated - this._$created = true; - - return super._$addChild(child); + $mouseHit ( + hit_context: CanvasRenderingContext2D, + matrix: Float32Array, + hit_object: IPlayerHitObject + ): void { + displayObjectContainerMouseHitUseCase( + this, hit_context, matrix, hit_object, true + ); } } + +/** + * @type {Stage} + * @public + */ +export const stage: Stage = new Stage(); +$stageAssignedMap.add(stage.instanceId); \ No newline at end of file diff --git a/packages/display/src/Stage/service/StageExecuteFrameActionsService.test.ts b/packages/display/src/Stage/service/StageExecuteFrameActionsService.test.ts new file mode 100644 index 00000000..8a81e494 --- /dev/null +++ b/packages/display/src/Stage/service/StageExecuteFrameActionsService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./StageExecuteFrameActionsService"; +import { $actions } from "../../DisplayObjectUtil"; +import { MovieClip } from "../../MovieClip"; +import { describe, expect, it } from "vitest"; + +describe("StageExecuteFrameActionsService.js test", () => +{ + it("execute test case", () => + { + const movieClip = new MovieClip(); + $actions.push(movieClip); + + let result = false; + movieClip.$actions = new Map(); + movieClip.$actions.set(1, [() => { result = true; }]); + + expect($actions.length).toBe(1); + expect(result).toBe(false); + + execute(); + expect($actions.length).toBe(0); + expect(result).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Stage/service/StageExecuteFrameActionsService.ts b/packages/display/src/Stage/service/StageExecuteFrameActionsService.ts new file mode 100644 index 00000000..566ad139 --- /dev/null +++ b/packages/display/src/Stage/service/StageExecuteFrameActionsService.ts @@ -0,0 +1,40 @@ +import type { MovieClip } from "../../MovieClip"; +import { $actions } from "../../DisplayObjectUtil"; + +/** + * @description フレームアクションがなくなるまで実行 + * Execute until there are no frame actions + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + while ($actions.length) { + + const movieClip = $actions.pop() as MovieClip; + const actionMap = movieClip.$actions; + if (!actionMap) { + continue; + } + + const frame = movieClip.currentFrame; + if (!actionMap.has(frame)) { + continue; + } + + const actions = actionMap.get(frame) as Function[]; + if (!actions) { + continue; + } + + for (let idx = 0; idx < actions.length; idx++) { + try { + actions[idx].apply(movieClip); + } catch (error) { + console.error(error); + } + } + } +}; \ No newline at end of file diff --git a/packages/display/src/Stage/service/StageExecuteFrameSoundsService.ts b/packages/display/src/Stage/service/StageExecuteFrameSoundsService.ts new file mode 100644 index 00000000..cb030768 --- /dev/null +++ b/packages/display/src/Stage/service/StageExecuteFrameSoundsService.ts @@ -0,0 +1,46 @@ +import type { Sound } from "@next2d/media"; +import type { MovieClip } from "../../MovieClip"; +import { $sounds } from "../../DisplayObjectUtil"; + +/** + * @description フレームにセットされたサウンドを再生 + * Play the sound set in the frame + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + while ($sounds.length) { + + const movieClip = $sounds.pop() as MovieClip; + const soundsMap = movieClip.$sounds; + if (!soundsMap) { + continue; + } + + const frame = movieClip.currentFrame; + if (!soundsMap.has(frame)) { + continue; + } + + const sounds = soundsMap.get(frame) as Sound[]; + const soundTransform = movieClip.soundTransform; + + for (let idx = 0; idx < sounds.length; idx++) { + + const sound = sounds[idx]; + if (!sound) { + continue; + } + + if (soundTransform) { + sound.volume = soundTransform.volume; + sound.loopCount = soundTransform.loopCount; + } + + sound.play(); + } + } +}; \ No newline at end of file diff --git a/packages/display/src/Stage/usecase/StageGenerateRenderQueueUseCase.ts b/packages/display/src/Stage/usecase/StageGenerateRenderQueueUseCase.ts new file mode 100644 index 00000000..ea6317c4 --- /dev/null +++ b/packages/display/src/Stage/usecase/StageGenerateRenderQueueUseCase.ts @@ -0,0 +1,100 @@ +import type { DisplayObject } from "../../DisplayObject"; +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import type { Shape } from "../../Shape"; +import type { Video } from "@next2d/media"; +import type { TextField } from "@next2d/text"; +import { renderQueue } from "@next2d/render-queue"; +import { stage } from "../../Stage"; +import { execute as displayObjectContainerGenerateRenderQueueUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerGenerateRenderQueueUseCase"; +import { execute as shapeGenerateRenderQueueUseCase } from "../../Shape/usecase/ShapeGenerateRenderQueueUseCase"; +import { execute as videoGenerateRenderQueueUseCase } from "../../Video/usecase/VideoGenerateRenderQueueUseCase"; +import { execute as textFieldGenerateRenderQueueUseCase } from "../../TextField/usecase/TextFieldGenerateRenderQueueUseCase"; + +/** + * @description renderer workerに渡す描画データを生成 + * Generate rendering data to be passed to the renderer worker + * + * @param {D} display_object + * @param {ImageBitmap[]} image_bitmaps + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {void} + * @method + * @protected + */ +export const execute = ( + display_object: D, + image_bitmaps: ImageBitmap[], + matrix: Float32Array, + color_transform: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): void => { + + renderQueue.push(stage.backgroundColor); + + switch (true) { + + case display_object.isContainerEnabled: // 0x00 + displayObjectContainerGenerateRenderQueueUseCase( + display_object as unknown as DisplayObjectContainer, + image_bitmaps, + matrix, + color_transform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case display_object.isShape: // 0x01 + shapeGenerateRenderQueueUseCase( + display_object as unknown as Shape, + matrix, + color_transform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case display_object.isText: // 0x02 + textFieldGenerateRenderQueueUseCase( + display_object as unknown as TextField, + matrix, + color_transform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + case display_object.isVideo: // 0x03 + videoGenerateRenderQueueUseCase( + display_object as unknown as Video, + image_bitmaps, + matrix, + color_transform, + renderer_width, + renderer_height, + point_x, + point_y + ); + break; + + default: + break; + + } + + display_object.changed = false; +}; \ No newline at end of file diff --git a/packages/display/src/Stage/usecase/StageReadyUseCase.ts b/packages/display/src/Stage/usecase/StageReadyUseCase.ts new file mode 100644 index 00000000..c2c1c332 --- /dev/null +++ b/packages/display/src/Stage/usecase/StageReadyUseCase.ts @@ -0,0 +1,25 @@ +import type { DisplayObjectContainer } from "../../DisplayObjectContainer"; +import { execute as stageExecuteFrameActionsService } from "../../Stage/service/StageExecuteFrameActionsService"; +import { execute as stageExecuteFrameSoundsService } from "../../Stage/service/StageExecuteFrameSoundsService"; +import { execute as displayObjectContainerPrepareUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerPrepareUseCase"; + +/** + * @description Stageの起動準備完了時のユースーケース + * Use case when Stage is ready to start + * + * @param {DisplayObjectContainer} display_object_container + * @return {void} + * @method + * @protected + */ +export const execute = (display_object_container: DisplayObjectContainer): void => +{ + // 1フレーム目の実行準備 + displayObjectContainerPrepareUseCase(display_object_container); + + // サウンドの実行 + stageExecuteFrameSoundsService(); + + // フレームアクションを実行 + stageExecuteFrameActionsService(); +}; \ No newline at end of file diff --git a/packages/display/src/Stage/usecase/StageTickerUseCase.ts b/packages/display/src/Stage/usecase/StageTickerUseCase.ts new file mode 100644 index 00000000..bd8720ea --- /dev/null +++ b/packages/display/src/Stage/usecase/StageTickerUseCase.ts @@ -0,0 +1,24 @@ +import { stage } from "../../Stage"; +import { execute as displayObjectContainerAdvanceFrameUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerAdvanceFrameUseCase"; +import { execute as stageExecuteFrameActionsService } from "../service/StageExecuteFrameActionsService"; +import { execute as stageExecuteFrameSoundsService } from "../service/StageExecuteFrameSoundsService"; + +/** + * @description ステージに配置されたDisplayObjectの定期処理 + * Regular processing of DisplayObjects placed on the Stage + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + // フレーム移動処理 + displayObjectContainerAdvanceFrameUseCase(stage); + + // 各フレームのサウンドを再生 + stageExecuteFrameSoundsService(); + + // 各フレームのアクションを実行 + stageExecuteFrameActionsService(); +}; \ No newline at end of file diff --git a/packages/display/src/TextField.ts b/packages/display/src/TextField.ts deleted file mode 100644 index e747b1cc..00000000 --- a/packages/display/src/TextField.ts +++ /dev/null @@ -1,3721 +0,0 @@ -import { - InteractiveObject, - Shape -} from "@next2d/display"; -import { - FocusEvent, - Event as Next2DEvent -} from "@next2d/events"; -import { - Tween, - Job, - Easing -} from "@next2d/ui"; -import { - TextData, - TextFormat, - parsePlainText, - parseHtmlText -} from "@next2d/text"; -import { - Rectangle, - Matrix, - Point -} from "@next2d/geom"; -import type { Player } from "@next2d/core"; -import type { - BoundsImpl, - TextFieldTypeImpl, - TextFieldAutoSizeImpl, - RGBAImpl, - ParentImpl, - TextObjectImpl, - DictionaryTagImpl, - TextCharacterImpl, - AttachmentImpl, - FilterArrayImpl, - BlendModeImpl, - PlayerHitObjectImpl, - PropertyMessageMapImpl, - PropertyTextMessageImpl, - Character, - CachePositionImpl -} from "@next2d/interface"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $document, - $rendererWorker, - $getEventType, - $TOUCH_MOVE, - $MOUSE_MOVE, - $textArea, - $currentPlayer -} from "@next2d/util"; -import { - $cacheStore, - $doUpdated, - $clamp, - $getArray, - $intToRGBA, - $isNaN, - $Math, - $toColorInt, - $poolFloat32Array6, - $boundsMatrix, - $multiplicationMatrix, - $poolBoundsObject, - $multiplicationColor, - $Infinity, - $Number, - $poolArray, - $poolFloat32Array8, - $generateFontStyle, - $getBoundsObject, - $setTimeout, - $clearTimeout -} from "@next2d/share"; - -/** - * TextField クラスは、テキストの表示と入力用の表示オブジェクトを作成するために使用されます。 - * プロパティインスペクターを使用して、テキストフィールドにインスタンス名を付けることができます。 - * また、TextField クラスのメソッドとプロパティを使用して、JavaScript でテキストフィールドを操作できます。 - * - * The TextField class is used to create display objects for text display and input. - * You can give a text field an instance name in the Property inspector - * and use the methods and properties of the TextField class to manipulate it with JavaScript. - * - * @class - * @memberOf next2d.display - * @extends InteractiveObject - */ -export class TextField extends InteractiveObject -{ - private readonly _$bounds: BoundsImpl; - private readonly _$originBounds: BoundsImpl; - private _$textData: TextData | null; - private _$background: boolean; - private _$backgroundColor: number; - private _$border: boolean; - private _$borderColor: number; - private _$htmlText: string; - private _$multiline: boolean; - private _$text: string; - private _$wordWrap: boolean; - private _$scrollX: number; - private _$scrollY: number; - private _$maxChars: number; - private _$defaultTextFormat: TextFormat; - private _$rawHtmlText: string; - private _$textFormats: TextFormat[] | null; - private _$restrict: string; - private _$isHTML: boolean; - private _$autoSize: TextFieldAutoSizeImpl; - private _$autoFontSize: boolean; - private _$scrollEnabled: boolean; - private _$xScrollShape: Shape | null; - private _$yScrollShape: Shape | null; - private _$type: TextFieldTypeImpl; - private _$focus: boolean; - private _$focusVisible: boolean; - private _$timerId: number; - private _$focusIndex: number; - private _$selectIndex: number; - private _$copyText: string; - private _$thickness: number; - private _$thicknessColor: number; - private _$cacheKeys: string[]; - private readonly _$cacheParams: number[]; - private _$stopIndex: number; - private _$compositionStartIndex: number; - private _$compositionEndIndex: number; - - /** - * @constructor - * @public - */ - constructor() - { - super(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$background = false; - - /** - * @type {number} - * @default 0xffffff - * @private - */ - this._$backgroundColor = 0xffffff; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$border = false; - - /** - * @type {number} - * @default 0x000000 - * @private - */ - this._$borderColor = 0x000000; - - /** - * @type {string} - * @default "" - * @private - */ - this._$htmlText = ""; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$multiline = false; - - /** - * @type {string} - * @default "" - * @private - */ - this._$text = ""; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$wordWrap = false; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$scrollX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$scrollY = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxChars = 0; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$stopIndex = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$compositionStartIndex = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$compositionEndIndex = -1; - - // TextFormat - const textFormat: TextFormat = new TextFormat(); - textFormat._$setDefault(); - - /** - * @type {TextFormat} - * @private - */ - this._$defaultTextFormat = textFormat; - - /** - * @type {string} - * @default "" - * @private - */ - this._$rawHtmlText = ""; - - /** - * @type {object} - * @private - */ - this._$bounds = { - "xMin": 0 , - "xMax": 100, - "yMin": 0 , - "yMax": 100 - }; - - /** - * @type {object} - * @private - */ - this._$originBounds = { - "xMin": 0 , - "xMax": 100, - "yMin": 0 , - "yMax": 100 - }; - - /** - * @type {string} - * @default null - * @private - */ - this._$restrict = ""; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isHTML = false; - - /** - * @type {TextData} - * @private - */ - this._$textData = null; - - /** - * @type {string} - * @default TextFieldAutoSize.NONE - * @private - */ - this._$autoSize = "none"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$autoFontSize = false; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$focusVisible = false; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$timerId = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$focusIndex = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$selectIndex = -1; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$scrollEnabled = true; - - /** - * @type {Shape} - * @default null - * @private - */ - this._$xScrollShape = null; - - /** - * @type {Shape} - * @default null - * @private - */ - this._$yScrollShape = null; - - /** - * @type {string} - * @default null - * @private - */ - this._$type = "static"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$focus = false; - - /** - * @type {string} - * @default "" - * @private - */ - this._$copyText = ""; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$thickness = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$thicknessColor = 0; - - /** - * @type {array} - * @default null - * @private - */ - this._$textFormats = null; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class TextField] - * @method - * @static - */ - static toString (): string - { - return "[class TextField]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.display.TextField - * @const - * @static - */ - static get namespace (): string - { - return "next2d.display.TextField"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object TextField] - * @method - * @public - */ - toString (): string - { - return "[object TextField]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.display.TextField - * @const - * @public - */ - get namespace (): string - { - return "next2d.display.TextField"; - } - - /** - * @description テキストサイズの自動的な拡大 / 縮小および整列を制御します。 - * Controls automatic sizing and alignment of text size. - * - * @member {boolean} - * @default false - * @public - */ - get autoFontSize (): boolean - { - return this._$autoFontSize; - } - set autoFontSize (auto_font_size: boolean) - { - if (auto_font_size !== this._$autoFontSize) { - this._$autoFontSize = auto_font_size; - this._$reload(); - } - } - - /** - * @description テキストフィールドの自動的な拡大 / 縮小および整列を制御します。 - * Controls automatic sizing and alignment of text fields. - * - * @member {string} - * @default TextFieldAutoSize.NONE - * @public - */ - get autoSize (): TextFieldAutoSizeImpl - { - return this._$autoSize; - } - set autoSize (auto_size: TextFieldAutoSizeImpl) - { - if (auto_size !== this._$autoSize) { - this._$autoSize = auto_size; - this._$reload(); - } - } - - /** - * @description テキストフィールドに背景の塗りつぶしがあるかどうかを指定します。 - * Specifies whether the text field has a background fill. - * - * @member {boolean} - * @default false - * @public - */ - get background (): boolean - { - return this._$background; - } - set background (background: boolean) - { - if (background !== this._$background) { - this._$background = !!background; - this._$reset(); - } - } - - /** - * @description テキストフィールドの背景の色です。 - * The color of the text field background. - * - * @member {number} - * @default 0xffffff - * @public - */ - get backgroundColor (): number - { - return this._$backgroundColor; - } - set backgroundColor (background_color: string | number) - { - background_color = $clamp( - $toColorInt(background_color), 0, 0xffffff, 0xffffff - ); - - if (background_color !== this._$backgroundColor) { - this._$backgroundColor = background_color; - this._$reset(); - } - } - - /** - * @description テキストフィールドに境界線があるかどうかを指定します。 - * Specifies whether the text field has a border. - * - * @member {boolean} - * @default false - * @public - */ - get border (): boolean - { - return this._$border; - } - set border (border: boolean) - { - if (border !== this._$border) { - this._$border = !!border; - this._$reset(); - } - } - - /** - * @description テキストフィールドの境界線の色です。 - * The color of the text field border. - * - * @member {number} - * @default 0x000000 - * @public - */ - get borderColor (): number - { - return this._$borderColor; - } - set borderColor (border_color: string | number) - { - border_color = $clamp( - $toColorInt(border_color), 0, 0xffffff, 0 - ); - - if (border_color !== this._$borderColor) { - this._$borderColor = border_color; - this._$reset(); - } - } - - /** - * @description テキストの任意の表示終了位置の設定 - * Setting an arbitrary display end position for text. - * - * @member {number} - * @default -1 - * @public - */ - get stopIndex (): number - { - return this._$stopIndex; - } - set stopIndex (index: number) - { - index |= 0; - if (this._$stopIndex === index) { - return ; - } - - this._$stopIndex = index; - - const textData: TextData = this.getTextData(); - if (!textData.textTable.length) { - return ; - } - - let currentTextWidth: number = 2; - let targetIndex: number = 0; - for (let idx = 0; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - let countUp = false; - if (textObject.mode === "text") { - countUp = true; - currentTextWidth += textObject.w; - } - - if (targetIndex >= index) { - targetIndex = idx; - break; - } - - if (textObject.mode === "break") { - countUp = true; - // reset - this._$scrollX = 0; - currentTextWidth = 2; - } - - if (countUp) { - targetIndex++; - } - - } - - const textObject: TextObjectImpl = textData.textTable[targetIndex]; - - const line: number = textObject.line; - - let currentTextHeight: number = 0; - for (let idx = 0; idx <= line; ++idx) { - currentTextHeight += textData.heightTable[idx]; - } - - const height: number = this.height; - let viewTextHeight: number = 0; - for (let idx = line; idx > -1; --idx) { - const lineHeight: number = textData.heightTable[idx]; - if (height < viewTextHeight + lineHeight) { - break; - } - viewTextHeight += lineHeight; - } - - if (currentTextHeight > height) { - const scaleY: number = (this.textHeight - height) / height; - this._$scrollY = $Math.min((currentTextHeight - viewTextHeight) / scaleY, height); - } - - const width: number = this.width; - let viewTextWidth: number = 0; - for (let idx = targetIndex; idx > 0; --idx) { - const textObject: TextObjectImpl = textData.textTable[idx]; - if (textObject.mode !== "text") { - continue; - } - - if (width < viewTextWidth + textObject.w) { - break; - } - viewTextWidth += textObject.w; - } - - if (currentTextWidth > width) { - const scaleX: number = (this.textWidth - width) / width; - this._$scrollX = $Math.min((currentTextWidth - viewTextWidth) / scaleX, width + 0.5); - } - this._$doChanged(); - } - - /** - * @description テキストに適用するフォーマットを指定します。 - * Specifies the formatting to be applied to the text. - * - * @member {TextFormat} - * @public - */ - get defaultTextFormat (): TextFormat - { - return this._$defaultTextFormat._$clone(); - } - set defaultTextFormat (text_format: TextFormat) - { - text_format._$merge(this._$defaultTextFormat); - this._$defaultTextFormat = text_format; - this._$reset(); - } - - /** - * @description このオブジェクトでマウスまたはその他のユーザー入力メッセージを - * - * @member {boolean} - * @default false - * @public - */ - get focus (): boolean - { - return this._$focus; - } - set focus (focus: boolean) - { - if (this._$focus === focus) { - return ; - } - - if (this._$type !== "input") { - return ; - } - - this._$focus = !!focus; - - const name = this._$focus - ? FocusEvent.FOCUS_IN - : FocusEvent.FOCUS_OUT; - - if (this.willTrigger(name)) { - this.dispatchEvent(new FocusEvent(name)); - } - - $textArea.value = ""; - if (this._$focus) { - $textArea.focus(); - } else { - this._$focusIndex = -1; - this._$selectIndex = -1; - this._$focusVisible = false; - $clearTimeout(this._$timerId); - $textArea.blur(); - } - - this._$doChanged(); - $doUpdated(); - } - - /** - * @description テキストフィールドの内容を HTML で表します。 - * Contains the HTML representation of the text field contents. - * - * @member {string} - * @default "" - * @public - */ - get htmlText (): string - { - if (this._$htmlText) { - return this._$htmlText; - } - - const textData: TextData = this.getTextData(); - - let prevTextFormat: TextFormat = textData.textTable[0].textFormat; - - let htmlText = " width) { - - this._$doChanged(); - - this._$scrollX = scroll_x; - - this._$xScrollShape.width = width * width / this.textWidth; - const parent: ParentImpl = this._$parent; - if (parent) { - - // start animation - if (this._$xScrollShape.hasLocalVariable("job")) { - this._$xScrollShape.getLocalVariable("job").stop(); - } - - // view start - this._$xScrollShape.alpha = 0.9; - - // set position - this._$xScrollShape.x = this.x + 1 - + (width - 1 - this._$xScrollShape.width) - / (width - 1) - * (this._$scrollX - 1); - this._$xScrollShape.y = this.y + this.height - this._$xScrollShape.height - 0.5; - - // added sprite - parent.addChildAt( - this._$xScrollShape, - parent.getChildIndex(this) + 1 - ); - - const job: Job = Tween.add(this._$xScrollShape, - { "alpha" : 0.9 }, - { "alpha" : 0 }, - 0.5, 0.2, Easing.outQuad - ); - - job.addEventListener(Next2DEvent.COMPLETE, (event: Next2DEvent) => - { - const shape: Shape = event.target.target; - shape.deleteLocalVariable("job"); - if (shape.parent) { - shape.parent.removeChild(shape); - } - }); - job.start(); - - this._$xScrollShape.setLocalVariable("job", job); - } - - } - - if (this.willTrigger(Next2DEvent.SCROLL)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.SCROLL, true)); - } - } - } - /** - * @description テキストフィールドのスクロール垂直位置です。 - * The scroll vertical position of the text field. - * - * @member {number} - * @public - */ - get scrollY (): number - { - return this._$scrollY; - } - set scrollY (scroll_y: number) - { - if (!this._$scrollEnabled - || this._$autoSize !== "none" - || !this._$multiline && !this._$wordWrap - ) { - return ; - } - - // check x animation - if (this._$xScrollShape - && this._$xScrollShape.hasLocalVariable("job") - ) { - return ; - } - - scroll_y = $clamp(scroll_y, 0, this.height, 0); - if (this._$scrollY !== scroll_y) { - - const height: number = this.height; - if (this._$yScrollShape && this.textHeight > height) { - - this._$doChanged(); - - this._$scrollY = scroll_y; - - this._$yScrollShape.height = height * height / this.textHeight; - - const parent: ParentImpl = this._$parent; - if (parent) { - - // start animation - if (this._$yScrollShape.hasLocalVariable("job")) { - this._$yScrollShape.getLocalVariable("job").stop(); - } - - // view start - this._$yScrollShape.alpha = 0.9; - - // set position - this._$yScrollShape.x = this.x + this.width - this._$yScrollShape.width - 0.5; - this._$yScrollShape.y = this.y + 0.5 - + (height - 1 - this._$yScrollShape.height) - / (height - 1) - * (this._$scrollY - 1); - - // added sprite - parent.addChildAt( - this._$yScrollShape, - parent.getChildIndex(this) + 1 - ); - - const job: Job = Tween.add(this._$yScrollShape, - { "alpha" : 0.9 }, - { "alpha" : 0 }, - 0.5, 0.2, Easing.outQuad - ); - - job.addEventListener(Next2DEvent.COMPLETE, (event: Next2DEvent) => - { - const shape: Shape = event.target.target; - shape.deleteLocalVariable("job"); - if (shape.parent) { - shape.parent.removeChild(shape); - } - }); - job.start(); - - this._$yScrollShape.setLocalVariable("job", job); - } - } - - if (this.willTrigger(Next2DEvent.SCROLL)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.SCROLL, true)); - } - } - - } - - /** - * @description テキストフィールド内の現在のテキストであるストリングです。 - * A string that is the current text in the text field. - * - * @member {string} - * @default "" - * @public - */ - get text (): string - { - if (!this._$isHTML) { - return this._$text; - } - - if (this._$rawHtmlText) { - return this._$rawHtmlText; - } - - let text: string = ""; - - const textData: TextData = this.getTextData(); - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - switch (textObject.mode) { - - case "text": - text += textObject.text; - break; - - case "break": - text += "\r"; - break; - - default: - continue; - - } - } - - this._$rawHtmlText = text; - - return text; - } - set text (text: string | null) - { - if (text === null) { - this._$text = ""; - this._$htmlText = ""; - this._$rawHtmlText = ""; - this._$isHTML = false; - this._$reload(); - return ; - } - - text = `${text}`; - if (text !== this._$text) { - this._$text = text; - this._$htmlText = ""; - this._$rawHtmlText = ""; - this._$isHTML = false; - this._$reload(); - } - } - - /** - * @description テキストフィールドのテキストの色です(16 進数形式)。 - * The color of the text in a text field, in hexadecimal format. - * - * @member {number} - * @public - */ - get textColor (): number - { - return this._$defaultTextFormat.color || 0; - } - set textColor (text_color: number) - { - this._$defaultTextFormat.color = text_color; - this._$reload(); - } - - /** - * @description テキストの高さです(ピクセル単位)。 - * The height of the text in pixels. - * - * @member {number} - * @readonly - * @public - */ - get textHeight (): number - { - return this.getTextData().textHeight; - } - - /** - * @description テキストの幅です(ピクセル単位)。 - * The width of the text in pixels. - * - * @member {number} - * @readonly - * @public - */ - get textWidth () - { - return this.getTextData().textWidth; - } - - /** - * @description 輪郭のテキスト幅です。0(デフォルト値)で無効にできます。 - * The text width of the outline, which can be disabled with 0 (the default value). - * - * @member {number} - * @default 0 - * @public - */ - get thickness (): number - { - return this._$thickness; - } - set thickness (thickness: number) - { - thickness |= 0; - if (thickness !== this._$thickness) { - this._$thickness = thickness; - this._$reset(); - } - } - - /** - * @description 輪郭のテキストの色です(16 進数形式)。 - * The color of the outline text. (Hexadecimal format) - * - * @member {number} - * @default 0 - * @public - */ - get thicknessColor (): number - { - return this._$thicknessColor; - } - set thicknessColor (thickness_color: number) - { - thickness_color = $clamp( - $toColorInt(thickness_color), 0, 0xffffff, 0 - ); - if (thickness_color !== this._$thicknessColor) { - this._$thicknessColor = thickness_color; - this._$reset(); - } - } - - /** - * @description テキストフィールドのタイプです。 - * The type of the text field. - * - * @member {string} - * @default TextFieldType.STATIC - * @public - */ - get type (): TextFieldTypeImpl - { - return this._$type; - } - set type (type: TextFieldTypeImpl) - { - this._$type = type; - } - - /** - * @description テキストフィールドのテキストを折り返すかどうかを示すブール値です。 - * A Boolean value that indicates whether the text field has word wrap. - * - * @member {boolean} - * @default false - * @public - */ - get wordWrap (): boolean - { - return this._$wordWrap; - } - set wordWrap (word_wrap: boolean) - { - if (this._$wordWrap !== word_wrap) { - this._$wordWrap = !!word_wrap; - this._$reset(); - } - } - - /** - * @description 表示オブジェクトの幅を示します(ピクセル単位)。 - * Indicates the width of the display object, in pixels. - * - * @member {number} - * @public - */ - get width (): number - { - return super.width; - } - set width (width: number) - { - width = +width; - if (!$isNaN(width) && width > -1) { - - const bounds: BoundsImpl = this._$getBounds(null); - - const xMin: number = $Math.abs(bounds.xMin); - this._$originBounds.xMax = width + xMin; - this._$originBounds.xMin = xMin; - this._$bounds.xMax = this._$originBounds.xMax; - this._$bounds.xMin = this._$originBounds.xMin; - - super.width = width; - - this._$reload(); - } - } - - /** - * @description 表示オブジェクトの高さを示します(ピクセル単位)。 - * Indicates the height of the display object, in pixels. - * - * @member {number} - * @public - */ - get height (): number - { - return super.height; - } - set height (height: number) - { - height = +height; - if (!$isNaN(height) && height > -1) { - - const bounds: BoundsImpl = this._$getBounds(null); - - const yMin: number = $Math.abs(bounds.yMin); - this._$originBounds.yMax = height + yMin; - this._$bounds.yMax = this._$originBounds.yMax; - this._$bounds.yMin = this._$originBounds.yMin; - super.height = height; - - this._$reload(); - } - } - - /** - * @description 親 DisplayObjectContainer のローカル座標を基準にした - * DisplayObject インスタンスの x 座標を示します。 - * Indicates the x coordinate - * of the DisplayObject instance relative to the local coordinates - * of the parent DisplayObjectContainer. - * - * @member {number} - * @public - */ - get x (): number - { - const matrix: Matrix = this._$transform.matrix; - const bounds: BoundsImpl = this._$getBounds(null); - return matrix._$matrix[4] + bounds.xMin; - } - set x (x: number) - { - const bounds: BoundsImpl = this._$getBounds(null); - super.x = x - bounds.xMin; - } - - /** - * @description 親 DisplayObjectContainer のローカル座標を基準にした - * DisplayObject インスタンスの y 座標を示します。 - * Indicates the y coordinate - * of the DisplayObject instance relative to the local coordinates - * of the parent DisplayObjectContainer. - * - * @member {number} - * @public - */ - get y (): number - { - const matrix: Matrix = this._$transform.matrix; - const bounds: BoundsImpl = this._$getBounds(null); - return matrix._$matrix[5] + bounds.yMin; - } - set y (y: number) - { - const bounds: BoundsImpl = this._$getBounds(null); - super.y = y - bounds.yMin; - } - - /** - * @description newText パラメーターで指定されたストリングを、 - * テキストフィールドのテキストの最後に付加します。 - * Appends the string specified by the newText parameter - * to the end of the text of the text field. - * - * @param {string} new_text - * @return void - * @method - * @public - */ - appendText (new_text: string): void - { - const currentText: string = this.text; - this.text = currentText + `${new_text}`; - } - - /** - * @description lineIndex パラメーターで指定された行のテキストを返します。 - * Returns the text of the line specified by the lineIndex parameter. - * - * @param {number} line_index - * @return {string} - * @public - */ - getLineText (line_index: number): string - { - if (!this._$text && !this._$htmlText) { - return ""; - } - - line_index |= 0; - - let lineText: string = ""; - const textData: TextData = this.getTextData(); - for (let idx: number = 0; idx < textData.textTable.length; idx++) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - if (textObject.line > line_index) { - break; - } - - if (textObject.line !== line_index) { - continue; - } - - if (textObject.mode !== "text") { - continue; - } - - lineText += textObject.text; - } - - return lineText; - } - - /** - * @description beginIndex パラメーターと endIndex パラメーターで指定された文字範囲を、 - * newText パラメーターの内容に置き換えます。 - * Replaces the range of characters that the beginIndex - * and endIndex parameters specify with the contents of the newText parameter. - * - * @param {number} begin_index - * @param {number} end_index - * @param {string} new_text - * @return {void} - * @method - * @public - */ - replaceText ( - begin_index: number, - end_index: number, - new_text: string - ): void { - - begin_index |= 0; - end_index |= 0; - if (begin_index > -1 && end_index > -1 && end_index >= begin_index) { - - const text: string = this.text; - - if (begin_index >= text.length) { - - if (end_index >= text.length && end_index >= begin_index) { - this.text = text + `${new_text}`; - } - - } else { - - this.text = text.slice(0, begin_index) - + `${new_text}` - + text.slice(end_index, text.length); - - } - } - } - - /** - * @description text、htmlTextに登録したテキスト情報をTextDataクラスで返却 - * Return text information registered in text and htmlText with TextData class. - * - * @param {number} [sub_font_size = 0] - * @return {array} - * @method - * @public - */ - getTextData (sub_font_size: number = 0): TextData - { - if (this._$textData !== null) { - return this._$textData; - } - - if (!this._$isHTML) { - - this._$textData = parsePlainText( - this._$text, - this._$defaultTextFormat, - { - "width": this.width, - "multiline": this._$multiline, - "wordWrap": this._$wordWrap, - "subFontSize": sub_font_size, - "textFormats": this._$textFormats - } - ); - - } else { - - this._$textData = parseHtmlText( - this._$htmlText, - this._$defaultTextFormat, - { - "width": this.width, - "multiline": this._$multiline, - "wordWrap": this._$wordWrap, - "subFontSize": sub_font_size, - "textFormats": this._$textFormats - } - ); - - } - - return this._$textData; - } - - /** - * @return {void} - * @method - * @public - */ - selectAll (): void - { - const textData: TextData = this.getTextData(); - if (!textData.textTable.length) { - return ; - } - - this._$selectIndex = 1; - this._$focusIndex = textData.textTable.length; - } - - /** - * @return {void} - * @method - * @public - */ - copy (): void - { - if (this._$focusIndex === -1 || this._$selectIndex === -1) { - return ; - } - - let text: string = ""; - const minIndex: number = $Math.min(this._$focusIndex, this._$selectIndex); - const maxIndex: number = $Math.max(this._$focusIndex, this._$selectIndex) + 1; - - const textData: TextData = this.getTextData(); - for (let idx = minIndex; idx < maxIndex; ++idx) { - const textObject: TextObjectImpl = textData.textTable[idx]; - if (!textObject || textObject.mode === "wrap") { - continue; - } - - switch (textObject.mode) { - - case "text": - text += textObject.text; - break; - - case "break": - text += "\n"; - break; - - } - } - - this._$copyText = text; - } - - /** - * @return {void} - * @method - * @public - */ - paste (): void - { - if (!this._$copyText || this._$focusIndex === -1) { - return ; - } - - this.insertText(this._$copyText); - } - - /** - * @return {void} - * @method - * @public - */ - arrowUp (): void - { - if (this._$focusIndex === -1) { - return ; - } - - const textData: TextData = this.getTextData(); - if (!textData.textTable.length) { - return ; - } - - const index = textData.textTable.length === this._$focusIndex - ? this._$focusIndex - 1 - : this._$focusIndex; - - const textObject: TextObjectImpl = textData.textTable[index]; - if (!textObject.line) { - return ; - } - - const line: number = textObject.mode === "text" - ? textObject.line - : textObject.line - 1; - - let currentWidth: number = 2; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (this._$focusIndex === idx) { - if (textObject.mode === "text") { - currentWidth += textObject.w / 2; - } - break; - } - - if (textObject.line > line) { - break; - } - - if (textObject.line !== line || textObject.mode !== "text") { - continue; - } - - currentWidth += textObject.w; - } - - let textWidth: number = 2; - const targetLine: number = line - 1; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (textObject.line > targetLine) { - this._$focusIndex = textObject.mode === "text" ? idx - 1 : idx; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - return ; - } - - if (textObject.line !== targetLine || textObject.mode !== "text") { - continue; - } - - textWidth += textObject.w; - if (textWidth > currentWidth) { - this._$focusIndex = idx; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - return ; - } - } - } - - /** - * @return {void} - * @method - * @public - */ - arrowDown (): void - { - if (this._$focusIndex === -1) { - return ; - } - - const textData: TextData = this.getTextData(); - if (!textData.textTable.length) { - return ; - } - - const textObject: TextObjectImpl = textData.textTable[this._$focusIndex]; - const line: number = textObject.mode === "text" - ? textObject.line - : textObject.line - 1; - - if (line === textData.lineTable.length - 1) { - return ; - } - - let currentWidth: number = 2; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (this._$focusIndex === idx) { - if (textObject.mode === "text") { - currentWidth += textObject.w / 2; - } - break; - } - - if (textObject.line > line) { - break; - } - - if (textObject.line !== line || textObject.mode !== "text") { - continue; - } - - currentWidth += textObject.w; - } - - let textWidth: number = 2; - const targetLine: number = line + 1; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (textObject.line > targetLine) { - this._$focusIndex = textObject.mode === "text" ? idx - 1 : idx; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - return ; - } - - if (textObject.line !== targetLine || textObject.mode !== "text") { - continue; - } - - textWidth += textObject.w; - if (textWidth > currentWidth) { - this._$focusIndex = idx; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - return ; - } - } - - this._$focusIndex = textData.textTable.length; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - } - - /** - * @return {void} - * @method - * @public - */ - arrowLeft (): void - { - if (!this._$focusIndex) { - return ; - } - - const textData: TextData = this.getTextData(); - if (textData.textTable.length && this._$focusIndex < 2) { - this._$focusIndex = 1; - return ; - } - - this._$focusIndex--; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - } - - /** - * @return {void} - * @method - * @public - */ - arrowRight (): void - { - const textData: TextData = this.getTextData(); - if (textData.textTable.length === this._$focusIndex) { - return ; - } - - this._$focusIndex++; - this._$selectIndex = -1; - $clearTimeout(this._$timerId); - this._$blinking(); - } - - /** - * @return {void} - * @method - * @public - */ - deleteText (): void - { - if (this._$compositionStartIndex > -1) { - return ; - } - - let minIndex: number = 0; - let maxIndex: number = 0; - if (this._$selectIndex > -1) { - minIndex = $Math.min(this._$focusIndex, this._$selectIndex); - maxIndex = $Math.max(this._$focusIndex, this._$selectIndex) + 1; - this._$focusIndex = minIndex; - } else { - if (2 > this._$focusIndex) { - return ; - } - - this._$focusIndex--; - } - - const textData: TextData = this.getTextData(); - const textObject: TextObjectImpl = textData.textTable[this._$focusIndex]; - if (textObject && textObject.mode === "wrap") { - this._$focusIndex--; - } - - const textFormats: TextFormat[] = $getArray(); - - let newText: string = ""; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - if (this._$focusIndex === idx || minIndex <= idx && maxIndex > idx) { - continue; - } - - switch (textObject.mode) { - - case "break": - textFormats.push(textObject.textFormat); - newText += "\n"; - break; - - case "text": - textFormats.push(textObject.textFormat); - newText += textObject.text; - break; - - default: - continue; - - } - } - - if (textData.textTable.length === this._$focusIndex) { - textFormats.pop(); - newText = newText.slice(0, -1); - } - - this._$selectIndex = -1; - if (!newText) { - // reset - this.text = null; - - this._$scrollX = 0; - this._$scrollY = 0; - - this._$focusIndex = 0; - } else { - - const beforeTextWidth: number = this.textWidth; - const beforeTextHeight : number = this.textHeight; - - this._$textFormats = textFormats; - this.text = newText; - - if (this._$scrollX > 0) { - const textWidth: number = this.textWidth; - const width: number = this.width; - - switch (true) { - - case width > textWidth: - this._$scrollY = 0; - break; - - case beforeTextWidth !== textWidth: - this._$scrollY -= (beforeTextWidth - textWidth) - / (textWidth / width); - break; - - default: - break; - - } - } - - if (this._$scrollY > 0) { - const textHeight: number = this.textHeight; - const height: number = this.height; - - switch (true) { - - case height > textHeight: - this._$scrollY = 0; - break; - - case beforeTextHeight !== textHeight: - this._$scrollY -= (beforeTextHeight - textHeight) - / (textHeight / height); - break; - - default: - break; - - } - } - - // reset - this._$textFormats = null; - $poolArray(textFormats); - } - } - - /** - * @return {void} - * @method - * @public - */ - compositionStart (): void - { - this._$compositionStartIndex = this._$focusIndex; - } - - /** - * @param {string} texts - * @return {void} - * @method - * @public - */ - compositionUpdate (texts: string): void - { - if (this._$compositionEndIndex > -1) { - const cacheIndex: number = this._$compositionStartIndex; - this._$focusIndex = this._$compositionStartIndex; - this._$selectIndex = this._$compositionEndIndex - 1; - - this._$compositionStartIndex = -1; - this.deleteText(); - - // reset - this._$compositionStartIndex = cacheIndex; - this._$selectIndex = -1; - } - - let textData: TextData = this.getTextData(); - const textFormats: TextFormat[] = $getArray(); - - const length: number = texts.length; - let newText: string = ""; - if (!textData.textTable.length) { - newText = texts; - this._$focusIndex = 1; - this._$compositionStartIndex = 1; - } else { - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - if (this._$compositionStartIndex === idx) { - for (let idx: number = 0; idx < length; ++idx) { - textFormats.push(textObject.textFormat._$clone()); - newText += texts[idx]; - } - } - - switch (textObject.mode) { - - case "break": - textFormats.push(textObject.textFormat); - newText += "\n"; - break; - - case "text": - textFormats.push(textObject.textFormat); - newText += textObject.text; - break; - - default: - continue; - - } - } - - // last text - if (this._$compositionStartIndex === textData.textTable.length ) { - const textObject: TextObjectImpl = textData.textTable[this._$compositionStartIndex - 1]; - for (let idx: number = 0; idx < length; ++idx) { - textFormats.push(textObject.textFormat._$clone()); - newText += texts[idx]; - } - } - } - - // update - if (textFormats.length) { - this._$textFormats = textFormats; - } - this.text = newText; - - // reset - this._$textFormats = null; - $poolArray(textFormats); - - textData = this.getTextData(); - let index: number = this._$compositionStartIndex + length; - for (let idx: number = this._$compositionStartIndex; idx < index; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (!textObject) { - break; - } - - textObject.textFormat.underline = true; - if (textObject.mode === "wrap") { - if (idx === this._$compositionStartIndex) { - let subIndex = 1; - for (;;) { - const textObject: TextObjectImpl = textData.textTable[idx - subIndex]; - - if (!textObject) { - break; - } - - if (textObject.mode === "text") { - textObject.textFormat.underline = true; - break; - } - - subIndex++; - } - } - - if (idx > this._$compositionStartIndex) { - index++; - } - } - } - - this._$compositionEndIndex = this._$focusIndex = index; - - // move textarea element - const player: Player = $currentPlayer(); - - const lastIndex = $Math.min(textData.textTable.length - 1, this._$compositionEndIndex); - const textObject: TextObjectImpl = textData.textTable[lastIndex]; - if (textObject) { - const line: number = textObject.line; - - let offsetHeight: number = 0; - for (let idx = 0; idx < line; ++idx) { - offsetHeight += textData.heightTable[idx]; - } - - const verticalAlign: number = textData.ascentTable[line]; - - let offsetWidth: number = 0; - let targetIndex: number = this._$compositionEndIndex; - for (;;) { - - const textObject: TextObjectImpl = textData.textTable[targetIndex--]; - if (!textObject || textObject.line !== line) { - break; - } - - offsetWidth += textObject.w; - } - - const lineObject: TextObjectImpl = textData.lineTable[line]; - const offsetAlign: number = this._$getAlignOffset(lineObject, this.width); - - const point: Point = this.localToGlobal(new Point( - offsetWidth + offsetAlign + player.tx, - offsetHeight + verticalAlign + player.ty - )); - - const div: HTMLElement | null = $document - .getElementById(player.contentElementId); - - let left: number = point.x * player._$scale; - let top: number = point.y * player._$scale; - if (div) { - const rect: DOMRect = div.getBoundingClientRect(); - left += rect.left; - top += rect.top; - } - - $textArea.style.left = `${left}px`; - $textArea.style.top = `${top}px`; - } - } - - /** - * @return {void} - * @method - * @public - */ - compositionEnd (): void - { - if (this._$compositionEndIndex > -1) { - const textData: TextData = this.getTextData(); - for (let idx: number = this._$compositionStartIndex; idx < this._$compositionEndIndex; ++idx) { - const textObject: TextObjectImpl = textData.textTable[idx]; - textObject.textFormat.underline = false; - } - this._$focusIndex = this._$compositionEndIndex; - } - - $textArea.blur(); - $textArea.value = ""; - if (this._$focus) { - $textArea.focus(); - } - - this._$selectIndex = -1; - this._$compositionStartIndex = -1; - this._$compositionEndIndex = -1; - } - - /** - * @param {string} text - * @return {void} - * @method - * @public - */ - insertText (texts: string): void - { - if (this._$focusIndex === -1 - || this._$compositionStartIndex > -1 - ) { - return ; - } - - if (this._$selectIndex > -1) { - this.deleteText(); - } - - const textData: TextData = this.getTextData(); - const textFormats: TextFormat[] = $getArray(); - - let newText: string = ""; - for (let idx = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - if (this._$focusIndex === idx) { - for (let idx = 0; idx < texts.length; ++idx) { - textFormats.push(textObject.textFormat); - newText += texts[idx]; - } - } - - switch (textObject.mode) { - - case "break": - textFormats.push(textObject.textFormat); - newText += "\n"; - break; - - case "text": - textFormats.push(textObject.textFormat); - newText += textObject.text; - break; - - default: - continue; - - } - } - - if (textData.textTable.length === this._$focusIndex) { - let textFormat: TextFormat; - if (textData.textTable.length) { - const textObject: TextObjectImpl = textData.textTable[textData.textTable.length - 1]; - textFormat = textObject.textFormat._$clone(); - } else { - textFormat = this.defaultTextFormat; - this._$focusIndex++; - } - - for (let idx = 0; idx < texts.length; ++idx) { - textFormats.push(textFormat._$clone()); - newText += texts[idx]; - } - } - - // update - this._$textFormats = textFormats; - this.text = newText; - - // reset - this._$textFormats = null; - $poolArray(textFormats); - - this._$focusIndex += texts.length; - this._$selectIndex = -1; - - $textArea.value = ""; - } - - /** - * @return {void} - * @method - * @private - */ - _$reset (): void - { - this._$textData = null; - - this._$doChanged(); - $doUpdated(); - - // cache clear - $cacheStore.removeCache(this._$instanceId); - } - - /** - * @return {void} - * @method - * @private - */ - _$reload (): void - { - this._$reset(); - this.getTextData(); - - if (this._$autoSize === "none" && this._$autoFontSize) { - - let maxFontSize: number = 0; - const textData: TextData = this.getTextData(); - for (let idx = 0; idx < textData.textTable.length; ++idx) { - const textObject: TextObjectImpl = textData.textTable[idx]; - maxFontSize = $Math.max(maxFontSize, textObject.textFormat.size || 0); - } - - let subSize: number = 1; - if (this.width && this.textWidth) { - - while (maxFontSize > subSize - && this.textWidth + 4 > this.width - ) { - this._$reset(); - this.getTextData(subSize++); - } - - } - - if (this.height && this.textHeight) { - - while (maxFontSize > subSize - && this.textHeight + 4 > this.height - ) { - this._$reset(); - this.getTextData(subSize++); - } - - } - } - - this._$resize(); - } - - /** - * @return {void} - * @method - * @private - */ - _$blinking (): void - { - this._$focusVisible = !this._$focusVisible; - this._$doChanged(); - $doUpdated(); - - this._$timerId = +$setTimeout(() => - { - this._$blinking(); - }, 500); - - this._$timerId |= 0; - } - - /** - * @param {number} stage_x - * @param {number} stage_y - * @return {void} - * @method - * @private - */ - _$setIndex (stage_x: number, stage_y: number): void - { - if (this._$type !== "input") { - return ; - } - - const textData: TextData = this.getTextData(); - if (!textData.textTable.length) { - this._$focusIndex = 0; - this._$selectIndex = -1; - this.setBlinkingTimer(); - return ; - } - - const width: number = this.width; - const height: number = this.height; - - let tx: number = 0; - if (this._$scrollX > 0) { - tx += this._$scrollX * (this.textWidth - width) / width; - } - - let ty: number = 0; - if (this._$scrollY) { - ty += this._$scrollY * (this.textHeight - height) / height; - } - - const eventType: string = $getEventType(); - const point: Point = this.globalToLocal(new Point(stage_x, stage_y)); - const x: number = point.x + tx; - const y: number = point.y + ty; - - let w: number = 2; - let yMin: number = 2; - let yMax: number = yMin + textData.heightTable[0]; - for (let idx: number = 1; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - - switch (textObject.mode) { - - case "break": - case "wrap": - if (x > w && y > yMin - && yMax > y - && width > x - ) { - const index: number = idx; - switch (eventType) { - - case $TOUCH_MOVE: - case $MOUSE_MOVE: - if (this._$selectIndex !== index && this._$focusIndex === index) { - this._$selectIndex = index; - - if (this._$focusIndex !== index) { - this._$focusVisible = false; - $clearTimeout(this._$timerId); - - this._$doChanged(); - $doUpdated(); - } - } - break; - - default: - if (this._$focusIndex !== index || this._$selectIndex > -1) { - this._$focusIndex = index; - this._$selectIndex = -1; - this.setBlinkingTimer(); - } - break; - } - - return ; - } - - w = 2; - yMin += textData.heightTable[textObject.line - 1]; - yMax = yMin + textData.heightTable[textObject.line]; - break; - - case "text": - if (idx === textData.textTable.length - 1 - && x > w && y > yMin && yMax > y - && width > x - ) { - - const index: number = textData.textTable.length; - switch (eventType) { - - case $TOUCH_MOVE: - case $MOUSE_MOVE: - if (this._$selectIndex !== index) { - this._$selectIndex = index; - - if (this._$focusIndex !== index) { - this._$focusVisible = false; - $clearTimeout(this._$timerId); - - this._$doChanged(); - $doUpdated(); - } - } - break; - - default: - if (this._$focusIndex !== index || this._$selectIndex > -1) { - this._$focusIndex = index; - this._$selectIndex = -1; - this.setBlinkingTimer(); - } - break; - - } - - return ; - } - - if (x > w && y > yMin - && yMax > y - && w + textObject.w > x - ) { - - let index: number = idx; - switch (eventType) { - case $TOUCH_MOVE: - case $MOUSE_MOVE: - - if (this._$focusIndex > index) { // left - if (this._$focusIndex === index + 1) { - if (w + textObject.w / 2 < x) { - index = -1; - } - } else { - if (w + textObject.w / 2 < x) { - index += 1; - } - } - } else { // right - if (this._$focusIndex === index) { - if (w + textObject.w / 2 > x) { - index = -1; - } - } else { - if (w + textObject.w / 2 > x) { - index -= 1; - } - } - } - - if (this._$selectIndex !== index) { - this._$selectIndex = index; - - if (this._$selectIndex > -1) { - this._$focusVisible = false; - $clearTimeout(this._$timerId); - } - - this._$doChanged(); - $doUpdated(); - } - break; - - default: - - if (w + textObject.w / 2 < x) { - const textObject: TextObjectImpl = textData.textTable[index + 1]; - if (!textObject || textObject.mode === "text") { - index += 1; - } - } - - if (this._$focusIndex !== index || this._$selectIndex > -1) { - this._$focusIndex = index; - this._$selectIndex = -1; - this.setBlinkingTimer(); - } - break; - - } - return ; - } - - w += textObject.w; - break; - - default: - break; - - } - } - - switch (eventType) { - - case $TOUCH_MOVE: - case $MOUSE_MOVE: - // reset - this._$focusIndex = -1; - this._$selectIndex = -1; - break; - - default: - this._$focusIndex = textData.textTable.length; - this._$selectIndex = -1; - this.setBlinkingTimer(); - break; - - } - } - - /** - * @return {void} - * @method - * @private - */ - setBlinkingTimer (): void - { - this._$focusVisible = false; - - this._$doChanged(); - $doUpdated(); - - $clearTimeout(this._$timerId); - this._$timerId = +$setTimeout(() => - { - this._$blinking(); - }, 500); - this._$timerId |= 0; - } - - /** - * @return {void} - * @method - * @private - */ - _$resize (): void - { - // update bounds - if (this._$autoSize !== "none") { - - const tf: TextFormat = this._$defaultTextFormat; - const width: number = this.textWidth + 4 + tf._$widthMargin(); - - if (this._$wordWrap) { - - this._$bounds.xMax = this._$originBounds.xMax; - this._$bounds.xMin = this._$originBounds.xMin; - - } else { - - switch (this._$autoSize) { - - case "left": - this._$bounds.xMax = width + this._$bounds.xMin; - break; - - case "center": - this._$bounds.xMax = width + this._$bounds.xMin; - break; - - case "right": - this._$bounds.xMax = this._$originBounds.xMax - - (this._$originBounds.xMax - this._$originBounds.xMin - - (width - this._$originBounds.xMin)); - break; - - default: - break; - - } - - } - - // set height - this._$bounds.yMax = this.textHeight + this._$originBounds.yMin + 4; - - } else { - if (this._$scrollEnabled) { - - if (!this._$xScrollShape) { - - this._$xScrollShape = new Shape(); - - this - ._$xScrollShape - .graphics - .beginFill("#000", 0.3) - .drawRoundRect(0, 0, 3, 3, 3); - - this - ._$xScrollShape - .scale9Grid = new Rectangle(1.5, 1.5, 0.1, 0.1); - } - - if (!this._$yScrollShape) { - - this._$yScrollShape = new Shape(); - - this - ._$yScrollShape - .graphics - .beginFill("#000", 0.3) - .drawRoundRect(0, 0, 3, 3, 3); - - this - ._$yScrollShape - .scale9Grid = new Rectangle(1.5, 1.5, 0.1, 0.1); - } - } - } - } - - /** - * @param {object} text_object - * @param {number} width - * @return {number} - * @method - * @private - */ - _$getAlignOffset (text_object: TextObjectImpl, width: number): number - { - // default - const lineWidth: number = this - .getTextData() - .getLineWidth(text_object.line); - - const textFormat: TextFormat = text_object.textFormat; - - const leftMargin: number = textFormat.leftMargin || 0; - if (!this._$wordWrap && lineWidth > width) { - return $Math.max(0, leftMargin); - } - - const rightMargin: number = textFormat.rightMargin || 0; - if (textFormat.align === "center" // format CENTER - || this._$autoSize === "center" // autoSize CENTER - ) { - return $Math.max(0, width / 2 - leftMargin - rightMargin - lineWidth / 2 - 2); - } - - if (textFormat.align === "right" // format RIGHT - || this._$autoSize === "right" // autoSize RIGHT - ) { - return $Math.max(0, width - leftMargin - lineWidth - rightMargin - 4); - } - - // autoSize LEFT - // format LEFT - return $Math.max(0, leftMargin); - } - - /** - * @param {Float32Array} [matrix=null] - * @returns {object} - * @method - * @private - */ - _$getBounds (matrix: Float32Array | null = null): BoundsImpl - { - if (matrix) { - - let multiMatrix : Float32Array = matrix; - - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - return $boundsMatrix(this._$bounds, multiMatrix); - } - - return $getBoundsObject( - this._$bounds.xMin, this._$bounds.xMax, - this._$bounds.yMin, this._$bounds.yMax - ); - } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$buildCharacter (character: Character): void - { - const textFormat = this._$defaultTextFormat; - - textFormat.font = character.font; - textFormat.size = character.size | 0; - textFormat.align = character.align; - textFormat.color = character.color | 0; - textFormat.leading = character.leading; - textFormat.letterSpacing = character.letterSpacing; - textFormat.leftMargin = character.leftMargin; - textFormat.rightMargin = character.rightMargin; - - switch (character.fontType) { - - case 1: - textFormat.bold = true; - break; - - case 2: - textFormat.italic = true; - break; - - case 3: - textFormat.bold = true; - textFormat.italic = true; - break; - - } - - // setup - this._$type = character.inputType; - this._$multiline = !!character.multiline; - this._$wordWrap = !!character.wordWrap; - this._$border = !!character.border; - this._$scrollEnabled = !!character.scroll; - this._$thickness = character.thickness | 0; - this._$thicknessColor = character.thicknessColor | 0; - - // bounds - this._$bounds.xMin = character.originBounds.xMin; - this._$bounds.xMax = character.originBounds.xMax; - this._$bounds.yMin = character.originBounds.yMin; - this._$bounds.yMax = character.originBounds.yMax; - this._$originBounds.xMin = character.originBounds.xMin; - this._$originBounds.xMax = character.originBounds.xMax; - this._$originBounds.yMin = character.originBounds.yMin; - this._$originBounds.yMax = character.originBounds.yMax; - - switch (character.autoSize) { - - case 1: - this.autoSize = character.align; - break; - - case 2: - this.autoFontSize = true; - break; - - } - - this.text = character.text; - - if ($rendererWorker && this._$stage) { - this._$createWorkerInstance(); - } - } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$sync (character: TextCharacterImpl): void - { - this._$buildCharacter(character); - } - - /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private - */ - _$build ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): TextCharacterImpl { - - const character: TextCharacterImpl = this - ._$baseBuild(tag, parent); - - this._$buildCharacter(character); - - return character; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @returns {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - // size - const bounds: BoundsImpl = this._$getBounds(); - const xMax = bounds.xMax; - const xMin = bounds.xMin; - const yMax = bounds.yMax; - const yMin = bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - if (!width || !height) { - return; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - context.reset(); - context.setTransform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - context.clip(); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible) { - return ; - } - - if (this._$focusIndex === -1 - && !this._$background - && !this._$border - && !this.text - ) { - return; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$transform._$rawColorTransform(); - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0 ,1); - if (!alpha) { - return ; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const baseBounds: BoundsImpl = this._$getBounds(null); - baseBounds.xMin -= this._$thickness; - baseBounds.xMax += this._$thickness; - baseBounds.yMin -= this._$thickness; - baseBounds.yMax += this._$thickness; - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - const xMax = +bounds.xMax; - const xMin = +bounds.xMin; - const yMax = +bounds.yMax; - const yMin = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const filters: FilterArrayImpl = this._$filters || this.filters; - const canApply: boolean = filters !== null - && filters.length > 0 - && this._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - - $poolBoundsObject(filterBounds); - - // texture is small or renew - if (this._$isUpdated()) { - $cacheStore.removeCache(this._$instanceId); - context.cachePosition = null; - this._$cacheKeys.length = 0; - } - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - const keys: number[] = $getArray(xScale, yScale); - this._$cacheKeys = $cacheStore.generateKeys( - this._$instanceId, keys - ); - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - // resize - const lineWidth: number = $Math.min(1, $Math.max(xScale, yScale)); - const width: number = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin) * xScale); - const height: number = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin) * yScale); - - // new canvas - const canvas: HTMLCanvasElement = $cacheStore.getCanvas(); - canvas.width = width + lineWidth * 2; - canvas.height = height + lineWidth * 2; - - const ctx: CanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!ctx) { - throw new Error("the context is null."); - } - - // border and background - if (this._$background || this._$border) { - - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(width, 0); - ctx.lineTo(width, height); - ctx.lineTo(0, height); - ctx.lineTo(0, 0); - - if (this._$background) { - - const rgb: RGBAImpl = $intToRGBA(this._$backgroundColor); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + multiColor[7], 255) - ) / 255; - - ctx.fillStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - ctx.fill(); - } - - if (this._$border) { - - const rgb: RGBAImpl = $intToRGBA(this._$borderColor); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + multiColor[7], 255) - ) / 255; - - ctx.lineWidth = lineWidth; - ctx.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - ctx.stroke(); - - } - - } - - // mask start - ctx.save(); - ctx.beginPath(); - ctx.moveTo(2, 2); - ctx.lineTo(width - 2, 2); - ctx.lineTo(width - 2, height - 2); - ctx.lineTo(2, height - 2); - ctx.lineTo(2, 2); - ctx.clip(); - - let tx = 2; - if (this._$scrollX > 0) { - const scaleX: number = (this.textWidth - this.width) / this.width; - tx += -this._$scrollX * scaleX; - } - - let ty = 2; - if (this._$scrollY > 0) { - const scaleY: number = (this.textHeight - this.height) / this.height; - ty += -this._$scrollY * scaleY; - } - - ctx.setTransform(xScale, 0, 0, yScale, tx * xScale, ty * yScale); - - ctx.beginPath(); - this._$doDraw(ctx, multiColor, width / xScale, lineWidth); - ctx.restore(); - - const position: CachePositionImpl = manager - .createCachePosition(width, height); - - const texture: WebGLTexture = manager - .createTextureFromCanvas(ctx.canvas); - - context.drawTextureFromRect(texture, position); - - // set cache - context.cachePosition = position; - $cacheStore.set(this._$cacheKeys, position); - - // destroy cache - $cacheStore.destroy(ctx); - } - - let drawFilter: boolean = false; - let offsetX: number = 0; - let offsetY: number = 0; - if (filters && filters.length - && this._$canApply(filters) - ) { - - drawFilter = true; - - const position: CachePositionImpl = this._$drawFilter( - context, multiMatrix, filters, - width, height - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - } - - const radianX: number = $Math.atan2(multiMatrix[1], multiMatrix[0]); - const radianY: number = $Math.atan2(-multiMatrix[2], multiMatrix[3]); - if (!drawFilter && (radianX || radianY)) { - - const tx: number = baseBounds.xMin * xScale; - const ty: number = baseBounds.yMin * yScale; - - const cosX: number = $Math.cos(radianX); - const sinX: number = $Math.sin(radianX); - const cosY: number = $Math.cos(radianY); - const sinY: number = $Math.sin(radianY); - - context.setTransform( - cosX, sinX, -sinY, cosY, - tx * cosX - ty * sinY + multiMatrix[4], - tx * sinX + ty * cosY + multiMatrix[5] - ); - - } else { - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = true; - context.globalCompositeOperation = blendMode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; - } - - // get cache - $poolBoundsObject(baseBounds); - - // pool - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} color_transform - * @param {number} width - * @param {number} line_width - * @return {void} - * @method - * @private - */ - _$doDraw ( - context: CanvasRenderingContext2D, - color_transform: Float32Array, - width: number, - line_width: number - ): void { - - // init - const textData: TextData = this.getTextData(); - if (!textData.textTable.length - && this._$focusIndex > -1 - && this._$focusVisible - ) { - - const textFormat: TextFormat = this._$defaultTextFormat; - - const rgb: RGBAImpl = $intToRGBA(textFormat.color || 0); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + color_transform[7], 255) - ) / 255; - - context.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(0, 0 + (textFormat.size || 12)); - context.stroke(); - - return ; - } - - if (this._$selectIndex > -1 && this._$focusIndex > -1) { - - const range: number = textData.textTable.length - 1; - let minIndex: number = 0; - let maxIndex: number = 0; - if (this._$focusIndex <= this._$selectIndex) { - minIndex = $Math.min(this._$focusIndex, range); - maxIndex = $Math.min(this._$selectIndex, range); - } else { - minIndex = $Math.min(this._$selectIndex, range); - maxIndex = $Math.min(this._$focusIndex - 1, range); - } - - const textObject: TextObjectImpl = textData.textTable[minIndex]; - const lineObject: TextObjectImpl = textData.lineTable[textObject.line]; - const offsetAlign: number = this._$getAlignOffset(lineObject, width); - - let x: number = 0; - if (minIndex && textObject.mode === "text") { - let idx: number = minIndex; - while (idx) { - const textObject: TextObjectImpl = textData.textTable[--idx]; - if (textObject.mode !== "text") { - break; - } - x += textObject.w; - } - } - - context.fillStyle = "#b4d7ff"; - - let w: number = 0; - for (let idx: number = minIndex; idx <= maxIndex; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (textObject.mode === "text") { - - w += textObject.w; - - if (idx !== maxIndex) { - continue; - } - } - - let y: number = 0; - const line: number = textObject.mode === "text" - ? textObject.line - : textObject.line - 1; - - for (let idx: number = 0; idx < line; ++idx) { - y += textData.heightTable[idx]; - } - - context.beginPath(); - context.rect( - x, y, - w + offsetAlign, - textData.heightTable[line] - ); - context.fill(); - - x = 0; - w = 0; - } - } - - const tw: number = this.width; - let scrollX = 0; - if (this._$scrollX > 0) { - const scaleX: number = (this.textWidth - tw) / tw; - scrollX = this._$scrollX * scaleX; - } - const limitWidth: number = tw + scrollX; - - const th: number = this.height; - let scrollY = 0; - if (this._$scrollY > 0) { - const scaleY: number = (this.textHeight - th) / th; - scrollY = this._$scrollY * scaleY; - } - const limitHeight: number = th + scrollY; - - // setup - let offsetWidth: number = 0; - let offsetHeight: number = 0; - let offsetAlign: number = 0; - let verticalAlign: number = 0; - - let skip = false; - let currentIndex = -1; - for (let idx: number = 0; idx < textData.textTable.length; ++idx) { - - const textObject: TextObjectImpl = textData.textTable[idx]; - if (textObject.mode === "text" || textObject.mode === "break") { - currentIndex++; - if (this._$stopIndex > -1 && currentIndex > this._$stopIndex) { - break; - } - } - - if (skip && textObject.mode === "text") { - continue; - } - - const textFormat: TextFormat = textObject.textFormat; - - // check - if (this._$autoSize === "none") { - - if (offsetHeight > limitHeight) { - break; - } - - if (textObject.mode === "text") { - if (scrollX > offsetWidth + textObject.w - || offsetWidth > limitWidth - ) { - offsetWidth += textObject.w; - continue; - } - } - - } - - // color setting - const rgb: RGBAImpl = $intToRGBA(textFormat.color || 0); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + color_transform[7], 255) - ) / 255; - - context.fillStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - - // focus line - if (this._$focusVisible && this._$focusIndex === idx) { - - const x: number = offsetWidth + offsetAlign + 0.1; - let line: number = textObject.line; - - let h: number = textObject.y; - let y: number = textData.ascentTable[line]; - if (textObject.mode !== "text") { - h = textObject.mode === "break" - ? textObject.h - : textData.ascentTable[line - 1]; - if (line > 0 && !textData.ascentTable[line - 1]) { - line = textObject.line; - y = textData.ascentTable[line - 1]; - } else { - line = textObject.line - 1; - y = textData.ascentTable[line]; - } - } - - for (let idx: number = 0; idx < line; ++idx) { - y += textData.heightTable[idx]; - } - - context.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - context.beginPath(); - context.moveTo(x, y); - context.lineTo(x, y - h); - context.stroke(); - } - - if (this._$thickness) { - const rgb: RGBAImpl = $intToRGBA(this._$thicknessColor); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + color_transform[7], 255) - ) / 255; - context.lineWidth = this._$thickness; - context.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - } - - const line: number = textObject.line | 0; - switch (textObject.mode) { - - case "break": - case "wrap": - - // reset width - offsetWidth = 0; - if (line) { - offsetHeight += textData.heightTable[line - 1]; - } - - if (scrollY > offsetHeight + textData.heightTable[line]) { - skip = true; - continue; - } - - verticalAlign = textData.ascentTable[line]; - offsetAlign = this._$getAlignOffset(textObject, width); - - skip = false; - - break; - - case "text": - { - context.beginPath(); - context.font = $generateFontStyle( - textFormat.font || "", - textFormat.size || 0, - !!textFormat.italic, - !!textFormat.bold - ); - - const x: number = offsetWidth + offsetAlign; - const y: number = offsetHeight + verticalAlign; - if (textFormat.underline) { - - const rgb: RGBAImpl = $intToRGBA(textFormat.color || 0); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + color_transform[7], 255) - ) / 255; - - context.lineWidth = line_width; - context.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - - context.beginPath(); - context.moveTo(x, y + 2); - context.lineTo(x + textObject.w, y + 2); - context.stroke(); - - } - - if (this._$thickness) { - context.strokeText(textObject.text, x, y); - } - - context.fillText(textObject.text, x, y); - - offsetWidth += textObject.w; - } - break; - - // case "image": - - // if (!obj.loaded) { - // continue; - // } - - // context.beginPath(); - // context.drawImage(obj.image, - // obj.hspace, obj.y, - // obj.width, obj.height - // ); - - // break; - - } - } - - if (this._$focusVisible && this._$focusIndex >= textData.textTable.length) { - const textObject: TextObjectImpl = textData.textTable[this._$focusIndex - 1]; - if (textObject) { - const rgb: RGBAImpl = $intToRGBA(textObject.textFormat.color || 0); - const alpha: number = $Math.max(0, $Math.min( - rgb.A * 255 + color_transform[7], 255) - ) / 255; - - context.strokeStyle = `rgba(${rgb.R},${rgb.G},${rgb.B},${alpha})`; - - const x: number = offsetWidth + offsetAlign + 0.1; - const y: number = offsetHeight + verticalAlign; - - context.beginPath(); - if (textObject.mode === "text") { - context.moveTo(x, y - textObject.y); - context.lineTo(x, y); - } else { - context.moveTo(x, y + textObject.h); - context.lineTo(x, y); - } - context.stroke(); - } - } - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @return {boolean} - * @method - * @private - */ - _$mouseHit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl - ): boolean { - - if (!this._$visible) { - return false; - } - - return this._$hit(context, matrix, options); - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @return {boolean} - * @method - * @private - */ - _$hit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl - ): boolean { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const baseBounds: BoundsImpl = this._$getBounds(null); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - const xMax = +bounds.xMax; - const xMin = +bounds.xMin; - const yMax = +bounds.yMax; - const yMin = +bounds.yMin; - $poolBoundsObject(bounds); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - - context.setTransform(1, 0, 0, 1, xMin, yMin); - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return context.isPointInPath(options.x, options.y); - } - - /** - * @return {void} - * @method - * @private - */ - _$createWorkerInstance (): void - { - if (this._$created || !$rendererWorker) { - return ; - } - this._$created = true; - - const bounds: BoundsImpl = this._$getBounds(); - - const message: PropertyTextMessageImpl = { - "command": "createTextField", - "buffer": new Float32Array(), - "instanceId": this._$instanceId, - "parentId": this._$parent ? this._$parent._$instanceId : -1, - "xMin": bounds.xMin, - "yMin": bounds.yMin, - "xMax": bounds.xMax, - "yMax": bounds.yMax, - "limitWidth": this.width, - "limitHeight": this.height, - "textHeight": this.textHeight, - "autoSize": this._$autoSize, - "wordWrap": this._$wordWrap, - "border": this._$border, - "background": this._$background, - "thickness": this._$thickness - }; - - if (this._$border) { - message.borderColor = this._$borderColor; - } - - if (this._$background) { - message.backgroundColor = this._$backgroundColor; - } - - if (this._$thickness) { - message.thicknessColor = this._$backgroundColor; - } - - if (this._$characterId > -1) { - message.characterId = this._$characterId; - } - - if (this._$loaderInfo) { - message.loaderInfoId = this._$loaderInfo._$id; - } - - if (this._$scale9Grid) { - message.grid = { - "x": this._$scale9Grid.x, - "y": this._$scale9Grid.y, - "w": this._$scale9Grid.width, - "h": this._$scale9Grid.height - }; - } - - $rendererWorker.postMessage(message); - } - - /** - * @return {void} - * @method - * @private - */ - _$postProperty (): void - { - if (!$rendererWorker) { - return ; - } - - const message: PropertyMessageMapImpl = this._$createMessage(); - - const bounds: BoundsImpl = this._$getBounds(null); - message.xMin = bounds.xMin; - message.yMin = bounds.yMin; - message.xMax = bounds.xMax; - message.yMax = bounds.yMax; - $poolBoundsObject(bounds); - - if (this._$isUpdated()) { - - message.limitWidth = this.width; - message.limitHeight = this.height; - message.textHeight = this.textHeight; - message.autoSize = this._$autoSize; - message.wordWrap = this._$wordWrap; - - message.border = this._$border; - if (this._$border) { - message.borderColor = this._$borderColor; - } - - message.background = this._$background; - if (this._$background) { - message.backgroundColor = this._$backgroundColor; - } - - message.thickness = this._$thickness; - if (this._$thickness) { - message.thicknessColor = this._$backgroundColor; - } - } - - $rendererWorker.postMessage(message); - - this._$posted = true; - this._$updated = false; - } -} diff --git a/packages/display/src/TextField/service/TextFieldGetRawBoundsService.test.ts b/packages/display/src/TextField/service/TextFieldGetRawBoundsService.test.ts new file mode 100644 index 00000000..05a91107 --- /dev/null +++ b/packages/display/src/TextField/service/TextFieldGetRawBoundsService.test.ts @@ -0,0 +1,17 @@ +import { execute } from "./TextFieldGetRawBoundsService"; +import { TextField } from "@next2d/text"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldGetRawBoundsService.js test", () => +{ + it("execute test case", () => + { + const textField = new TextField(); + const bounds = execute(textField); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(100); + expect(bounds[3]).toBe(100); + }); +}); \ No newline at end of file diff --git a/packages/display/src/TextField/service/TextFieldGetRawBoundsService.ts b/packages/display/src/TextField/service/TextFieldGetRawBoundsService.ts new file mode 100644 index 00000000..9e7323dc --- /dev/null +++ b/packages/display/src/TextField/service/TextFieldGetRawBoundsService.ts @@ -0,0 +1,20 @@ +import type { TextField } from "@next2d/text"; +import { $getBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description TextFieldのローカルバウンディングボックスを取得します。 + * Get the local bounding box of the TextField. + * + * @param {TextField} text_field + * @return {Float32Array} + * @protected + */ +export const execute = (text_field: TextField): Float32Array => +{ + return $getBoundsArray( + text_field.xMin, + text_field.yMin, + text_field.xMax, + text_field.yMax + ); +}; \ No newline at end of file diff --git a/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.test.ts b/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.test.ts new file mode 100644 index 00000000..0347d614 --- /dev/null +++ b/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.test.ts @@ -0,0 +1,54 @@ +import { execute } from "./TextFieldCalcBoundsMatrixUseCase"; +import { TextField } from "@next2d/text"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("TextFieldCalcBoundsMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + const bounds = execute(textField); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(100); + expect(bounds[3]).toBe(100); + }); + + it("execute test case2", () => + { + const textField = new TextField(); + textField.$matrix = new Matrix(1.3, 0.5, 0.2, 1.2, 110, 220); + const bounds = execute(textField); + + expect(bounds[0]).toBe(110); + expect(bounds[1]).toBe(220); + expect(bounds[2]).toBe(260); + expect(bounds[3]).toBe(390); + + }); + + it("execute test case3", () => + { + const textField = new TextField(); + const bounds = execute(textField, new Float32Array([1.3, 0.5, 0.2, 1.2, 110, 220])); + + expect(bounds[0]).toBe(110); + expect(bounds[1]).toBe(220); + expect(bounds[2]).toBe(260); + expect(bounds[3]).toBe(390); + }); + + it("execute test case4", () => + { + const textField = new TextField(); + textField.$matrix = new Matrix(0.9, 0.25, -0.2, 1.5, 10, 20); + const bounds = execute(textField, new Float32Array([1.3, 0.5, 0.2, 1.2, 110, 220])); + + expect(bounds[0]).toBe(127); + expect(bounds[1]).toBe(249); + expect(bounds[2]).toBe(252.99998474121094); + expect(bounds[3]).toBe(494); + }); +}); \ No newline at end of file diff --git a/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.ts b/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.ts new file mode 100644 index 00000000..26e4efa9 --- /dev/null +++ b/packages/display/src/TextField/usecase/TextFieldCalcBoundsMatrixUseCase.ts @@ -0,0 +1,47 @@ +import type { TextField } from "@next2d/text"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as textFieldGetRawBoundsService } from "../service/TextFieldGetRawBoundsService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description TextFieldの描画範囲を計算します。 + * Calculate the drawing area of Shape. + * + * @param {TextField} text_field + * @param {Float32Array | null} [matrix=null] + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (text_field: TextField, matrix: Float32Array | null = null): Float32Array => +{ + const rawBounds = textFieldGetRawBoundsService(text_field); + + const rawMatrix = displayObjectGetRawMatrixUseCase(text_field); + if (!rawMatrix) { + if (matrix) { + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix + ); + $poolBoundsArray(rawBounds); + + return calcBounds; + } + + return rawBounds; + } + + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix ? Matrix.multiply(matrix, rawMatrix) : rawMatrix + ); + + $poolBoundsArray(rawBounds); + + return calcBounds; +}; \ No newline at end of file diff --git a/packages/display/src/TextField/usecase/TextFieldGenerateRenderQueueUseCase.ts b/packages/display/src/TextField/usecase/TextFieldGenerateRenderQueueUseCase.ts new file mode 100644 index 00000000..8fb40ca8 --- /dev/null +++ b/packages/display/src/TextField/usecase/TextFieldGenerateRenderQueueUseCase.ts @@ -0,0 +1,314 @@ +import type { TextField } from "@next2d/text"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as displayObjectBlendToNumberService } from "../../DisplayObject/service/DisplayObjectBlendToNumberService"; +import { execute as displayObjectGenerateHashService } from "../../DisplayObject/service/DisplayObjectGenerateHashService"; +import { $cacheStore } from "@next2d/cache"; +import { renderQueue } from "@next2d/render-queue"; +import { + $clamp, + $RENDERER_TEXT_TYPE, + $getArray, + $poolArray, + $poolBoundsArray, + $getBoundsArray +} from "../../DisplayObjectUtil"; +import { + ColorTransform, + Matrix +} from "@next2d/geom"; + +/** + * @type {TextEncoder} + * @private + */ +const $textEncoder: TextEncoder = new TextEncoder(); + +/** + * @description renderer workerに渡すTextFieldの描画データを生成 + * Generate drawing data of TextField to pass to renderer + * + * @param {TextField} text_field + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {void} + * @method + * @protected + */ +export const execute = ( + text_field: TextField, + matrix: Float32Array, + color_transform: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): void => { + + if (!text_field.visible) { + renderQueue.push(0); + return ; + } + + // transformed ColorTransform(tColorTransform) + const rawColor = displayObjectGetRawColorTransformUseCase(text_field); + const tColorTransform = rawColor + ? ColorTransform.multiply(color_transform, rawColor) + : color_transform; + + const alpha = $clamp(tColorTransform[3] + tColorTransform[7] / 255, 0, 1, 0); + if (!alpha) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + renderQueue.push(0); + return ; + } + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(text_field); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + const bounds = displayObjectCalcBoundsMatrixService( + text_field.xMin, text_field.yMin, + text_field.xMax, text_field.yMax, + tMatrix + ); + + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + $poolBoundsArray(bounds); + + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + switch (true) { + + case width === 0: + case height === 0: + case width === -Infinity: + case height === -Infinity: + case width === Infinity: + case height === Infinity: + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + $poolBoundsArray(bounds); + renderQueue.push(0); + return; + + default: + break; + + } + + if (point_x > xMin + width + || point_y > yMin + height + || xMin > renderer_width + || yMin > renderer_height + ) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + $poolBoundsArray(bounds); + renderQueue.push(0); + return; + } + + if (!text_field.uniqueKey) { + if (text_field.characterId && text_field.loaderInfo) { + + const values = $getArray( + text_field.loaderInfo.id, + text_field.characterId + ); + + text_field.uniqueKey = `${displayObjectGenerateHashService(new Float32Array(values))}`; + $poolArray(values); + + } else { + + text_field.uniqueKey = `${text_field.instanceId}`; + + } + } + + const xScale = Math.round(Math.sqrt( + tMatrix[0] * tMatrix[0] + + tMatrix[1] * tMatrix[1] + ) * 10000) / 10000; + + const yScale = Math.round(Math.sqrt( + tMatrix[2] * tMatrix[2] + + tMatrix[3] * tMatrix[3] + ) * 10000) / 10000; + + if (text_field.changed + && !text_field.cacheKey + || text_field.cacheParams[0] !== xScale + || text_field.cacheParams[1] !== yScale + || text_field.cacheParams[2] !== tColorTransform[7] + ) { + text_field.cacheKey = $cacheStore.generateKeys(xScale, yScale, tColorTransform[7]); + text_field.cacheParams[0] = xScale; + text_field.cacheParams[1] = yScale; + text_field.cacheParams[2] = tColorTransform[7]; + } + + const cacheKey = text_field.cacheKey; + + // rennder on + renderQueue.push( + 1, $RENDERER_TEXT_TYPE, + tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], tMatrix[4], tMatrix[5], + tColorTransform[0], tColorTransform[1], tColorTransform[2], tColorTransform[3], + tColorTransform[4], tColorTransform[5], tColorTransform[6], tColorTransform[7], + xMin, yMin, xMax, yMax, + text_field.xMin, text_field.yMin, + text_field.xMax, text_field.yMax, + +text_field.uniqueKey, cacheKey, +text_field.changed + ); + + if (text_field.$cache && !text_field.$cache.has(text_field.uniqueKey)) { + text_field.$cache = null; + } + + const cache = text_field.$cache + ? text_field.$cache.get(`${cacheKey}`) + : $cacheStore.get(text_field.uniqueKey, `${cacheKey}`); + + if (!cache || text_field.changed) { + + // cache none + renderQueue.push(0, +cache); + + const buffer = $textEncoder.encode(JSON.stringify(text_field.$textData)); + + renderQueue.push(buffer.length); + renderQueue.set(buffer); + + // text setting + switch (text_field.autoSize) { + + case "center": + renderQueue.push(0); + break; + + case "left": + renderQueue.push(1); + break; + + case "none": + renderQueue.push(2); + break; + + case "right": + renderQueue.push(3); + break; + + } + + renderQueue.push( + text_field.stopIndex, + text_field.scrollX, + text_field.scrollY, + text_field.textWidth, + text_field.textHeight, + Math.abs(text_field.xMax - text_field.xMin), + Math.abs(text_field.yMax - text_field.yMin), + text_field.focusIndex, + text_field.selectIndex, + +text_field.focusVisible, + text_field.thickness, + text_field.thicknessColor, + +text_field.wordWrap, + +text_field.border, + text_field.borderColor, + +text_field.background, + text_field.backgroundColor, + text_field.defaultTextFormat.color || 0, + text_field.defaultTextFormat.size || 0 + ); + + if (!cache) { + $cacheStore.set(text_field.uniqueKey, `${cacheKey}`, true); + } + + if (text_field.$cache) { + text_field.$cache = null; + } + } else { + if (!text_field.$cache) { + text_field.$cache = $cacheStore.getById(text_field.uniqueKey); + text_field.$cache.set(text_field.uniqueKey, true); + } + renderQueue.push(1); + } + + renderQueue.push( + displayObjectBlendToNumberService(text_field.blendMode) + ); + + if (text_field.filters?.length) { + + let updated = false; + const params = []; + const bounds = $getBoundsArray(0, 0, 0, 0); + for (let idx = 0; idx < text_field.filters.length; idx++) { + + const filter = text_field.filters[idx]; + if (!filter || !filter.canApplyFilter()) { + continue; + } + + // フィルターが更新されたかをチェック + if (filter.$updated) { + updated = true; + } + filter.$updated = false; + + filter.getBounds(bounds); + + const buffer = filter.toNumberArray(); + + for (let idx = 0; idx < buffer.length; idx += 4096) { + params.push(...buffer.subarray(idx, idx + 4096)); + } + } + + const useFilfer = params.length > 0; + if (useFilfer) { + renderQueue.push( + +useFilfer, +updated, + bounds[0], bounds[1], bounds[2], bounds[3], + params.length + ); + renderQueue.set(new Float32Array(params)); + } + + $poolBoundsArray(bounds); + } else { + renderQueue.push(0); + } + + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } +}; \ No newline at end of file diff --git a/packages/display/src/TextField/usecase/TextFieldHitTestUseCase.ts b/packages/display/src/TextField/usecase/TextFieldHitTestUseCase.ts new file mode 100644 index 00000000..986be8ea --- /dev/null +++ b/packages/display/src/TextField/usecase/TextFieldHitTestUseCase.ts @@ -0,0 +1,52 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { TextField } from "@next2d/text"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description TextField のヒット判定 + * Hit judgment of TextField + * + * @param {TextField} text_field + * @param {CanvasRenderingContext2D} hit_context + * @param {Float32Array} matrix + * @param {IPlayerHitObject} hit_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + text_field: TextField, + hit_context: CanvasRenderingContext2D, + matrix: Float32Array, + hit_object: IPlayerHitObject +): boolean => { + + const width = Math.ceil(Math.abs(text_field.xMax - text_field.xMin)); + const height = Math.ceil(Math.abs(text_field.yMax - text_field.yMin)); + if (width <= 0 || height <= 0 ) { + return false; + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(text_field); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + hit_context.setTransform( + tMatrix[0], tMatrix[1], tMatrix[2], + tMatrix[3], tMatrix[4], tMatrix[5] + ); + hit_context.beginPath(); + hit_context.moveTo(0, 0); + hit_context.lineTo(width, 0); + hit_context.lineTo(width, height); + hit_context.lineTo(0, height); + hit_context.lineTo(0, 0); + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + return hit_context.isPointInPath(hit_object.x, hit_object.y); +}; \ No newline at end of file diff --git a/packages/display/src/Video/service/VideoGetRawBoundsService.test.ts b/packages/display/src/Video/service/VideoGetRawBoundsService.test.ts new file mode 100644 index 00000000..b0c9f6cb --- /dev/null +++ b/packages/display/src/Video/service/VideoGetRawBoundsService.test.ts @@ -0,0 +1,17 @@ +import { execute } from "./VideoGetRawBoundsService"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; + +describe("VideoGetRawBoundsService.js test", () => +{ + it("execute test case", () => + { + const video = new Video(100, 200); + const bounds = execute(video); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(100); + expect(bounds[3]).toBe(200); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Video/service/VideoGetRawBoundsService.ts b/packages/display/src/Video/service/VideoGetRawBoundsService.ts new file mode 100644 index 00000000..c376aed6 --- /dev/null +++ b/packages/display/src/Video/service/VideoGetRawBoundsService.ts @@ -0,0 +1,16 @@ +import type { Video } from "@next2d/media"; +import { $getBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description Videoのローカルバウンディングボックスを取得します。 + * Get the local bounding box of the Video. + * + * @param {Video} video + * @return {number[]} + * @method + * @protected + */ +export const execute = (video: Video): Float32Array => +{ + return $getBoundsArray(0, 0, video.videoWidth, video.videoHeight); +}; \ No newline at end of file diff --git a/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.test.ts b/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.test.ts new file mode 100644 index 00000000..43e6fd6f --- /dev/null +++ b/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.test.ts @@ -0,0 +1,53 @@ +import { execute } from "./VideoCalcBoundsMatrixUseCase"; +import { Video } from "@next2d/media"; +import { describe, expect, it } from "vitest"; +import { Matrix } from "@next2d/geom"; + +describe("VideoCalcBoundsMatrixUseCase.js test", () => +{ + it("execute test case1", () => + { + const video = new Video(3, 4); + + const bounds = execute(video); + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(3); + expect(bounds[3]).toBe(4); + }); + + it("execute test case2", () => + { + const video = new Video(3, 4); + video.$matrix = new Matrix(1.3, 0.5, 0.2, 1.2, 110, 220); + + const bounds = execute(video); + expect(bounds[0]).toBe(110); + expect(bounds[1]).toBe(220); + expect(bounds[2]).toBe(114.69999694824219); + expect(bounds[3]).toBe(226.3000030517578); + + }); + + it("execute test case3", () => + { + const video = new Video(3, 4); + const bounds = execute(video, new Float32Array([1.3, 0.5, 0.2, 1.2, 110, 220])); + expect(bounds[0]).toBe(110); + expect(bounds[1]).toBe(220); + expect(bounds[2]).toBe(114.69999694824219); + expect(bounds[3]).toBe(226.3000030517578); + }); + + it("execute test case4", () => + { + const video = new Video(3, 4); + video.$matrix = new Matrix(0.9, 0.25, -0.2, 1.5, 10, 20); + + const bounds = execute(video, new Float32Array([1.3, 0.5, 0.2, 1.2, 110, 220])); + expect(bounds[0]).toBe(127); + expect(bounds[1]).toBe(249); + expect(bounds[2]).toBe(130.82000732421875); + expect(bounds[3]).toBe(258.04998779296875); + }); +}); \ No newline at end of file diff --git a/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.ts b/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.ts new file mode 100644 index 00000000..3fe0c083 --- /dev/null +++ b/packages/display/src/Video/usecase/VideoCalcBoundsMatrixUseCase.ts @@ -0,0 +1,47 @@ +import type { Video } from "@next2d/media"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as videoGetRawBoundsService } from "../service/VideoGetRawBoundsService"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { $poolBoundsArray } from "../../DisplayObjectUtil"; + +/** + * @description Shapeの描画範囲を計算します。 + * Calculate the drawing area of Shape. + * + * @param {Video} video + * @param {Float32Array | null} [matrix=null] + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (video: Video, matrix: Float32Array | null = null): Float32Array => +{ + const rawBounds = videoGetRawBoundsService(video); + + const rawMatrix = displayObjectGetRawMatrixUseCase(video); + if (!rawMatrix) { + if (matrix) { + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix + ); + $poolBoundsArray(rawBounds); + + return calcBounds; + } + + return rawBounds; + } + + const calcBounds = displayObjectCalcBoundsMatrixService( + rawBounds[0], rawBounds[1], + rawBounds[2], rawBounds[3], + matrix ? Matrix.multiply(matrix, rawMatrix) : rawMatrix + ); + + $poolBoundsArray(rawBounds); + + return calcBounds; +}; \ No newline at end of file diff --git a/packages/display/src/Video/usecase/VideoGenerateRenderQueueUseCase.ts b/packages/display/src/Video/usecase/VideoGenerateRenderQueueUseCase.ts new file mode 100644 index 00000000..549a9892 --- /dev/null +++ b/packages/display/src/Video/usecase/VideoGenerateRenderQueueUseCase.ts @@ -0,0 +1,263 @@ +import type { Video } from "@next2d/media"; +import { execute as displayObjectGetRawColorTransformUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawColorTransformUseCase"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; +import { execute as displayObjectCalcBoundsMatrixService } from "../../DisplayObject/service/DisplayObjectCalcBoundsMatrixService"; +import { execute as displayObjectBlendToNumberService } from "../../DisplayObject/service/DisplayObjectBlendToNumberService"; +import { execute as displayObjectGenerateHashService } from "../../DisplayObject/service/DisplayObjectGenerateHashService"; +import { $cacheStore } from "@next2d/cache"; +import { renderQueue } from "@next2d/render-queue"; +import { + $clamp, + $RENDERER_VIDEO_TYPE, + $getArray, + $poolBoundsArray, + $poolArray, + $getBoundsArray +} from "../../DisplayObjectUtil"; +import { + ColorTransform, + Matrix +} from "@next2d/geom"; + +/** + * @description renderer workerに渡すVideoの描画データを生成 + * Generate drawing data of Video to pass to renderer + * + * @param {Video} video + * @param {ImageBitmap[]} image_bitmaps + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {number} renderer_width + * @param {number} renderer_height + * @param {number} point_x + * @param {number} point_y + * @return {void} + * @method + * @protected + */ +export const execute = ( + video: Video, + image_bitmaps: ImageBitmap[], + matrix: Float32Array, + color_transform: Float32Array, + renderer_width: number, + renderer_height: number, + point_x: number, + point_y: number +): void => { + + if (!video.visible + || !video.$videoElement + || !video.$offscreenCanvas + || !video.loaded + ) { + renderQueue.push(0); + return ; + } + + // transformed ColorTransform(tColorTransform) + const rawColor = displayObjectGetRawColorTransformUseCase(video); + const tColorTransform = rawColor + ? ColorTransform.multiply(color_transform, rawColor) + : color_transform; + + const alpha = $clamp(tColorTransform[3] + tColorTransform[7] / 255, 0, 1, 0); + if (!alpha) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + renderQueue.push(0); + return ; + } + + // transformed matrix(tMatrix) + const rawMatrix = displayObjectGetRawMatrixUseCase(video); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + // draw text + const bounds = displayObjectCalcBoundsMatrixService( + 0, 0, video.videoWidth, video.videoHeight, + tMatrix + ); + + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + $poolBoundsArray(bounds); + + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + switch (true) { + + case width === 0: + case height === 0: + case width === -Infinity: + case height === -Infinity: + case width === Infinity: + case height === Infinity: + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + renderQueue.push(0); + return; + + default: + break; + + } + + if (point_x > xMin + width + || point_y > yMin + height + || xMin > renderer_width + || yMin > renderer_height + ) { + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + renderQueue.push(0); + return; + } + + if (!video.uniqueKey) { + if (video.characterId && video.loaderInfo) { + + const values = $getArray( + video.loaderInfo.id, + video.characterId + ); + + video.uniqueKey = `${displayObjectGenerateHashService(new Float32Array(values))}`; + $poolArray(values); + + } else { + + video.uniqueKey = `${video.instanceId}`; + + } + } + + // rennder on + renderQueue.push( + 1, $RENDERER_VIDEO_TYPE, + tMatrix[0], tMatrix[1], tMatrix[2], tMatrix[3], tMatrix[4], tMatrix[5], + tColorTransform[0], tColorTransform[1], tColorTransform[2], tColorTransform[3], + tColorTransform[4], tColorTransform[5], tColorTransform[6], tColorTransform[7], + xMin, yMin, xMax, yMax, + 0, 0, video.videoWidth, video.videoHeight, + +video.uniqueKey, +video.changed + ); + + if (video.$cache && !video.$cache.has(video.uniqueKey)) { + video.$cache = null; + } + + const cache = video.$cache + ? video.$cache.get("0") + : $cacheStore.get(video.uniqueKey, "0"); + + if (!cache || video.changed) { + + // cache, node + renderQueue.push(0, +cache); + + const context = video.$context; + if (context) { + + const x = video.videoWidth / 2; + const y = video.videoHeight / 2; + + // 反転して転送 + context.save(); + context.translate(x, y); + context.rotate(Math.PI); + context.scale(-1, 1); + context.drawImage( + video.$videoElement, -x, -y, + video.videoWidth, video.videoHeight + ); + context.restore(); + + image_bitmaps.push( + video.$offscreenCanvas.transferToImageBitmap() + ); + } + + if (!cache) { + $cacheStore.set(video.uniqueKey, "0", true); + } + + if (video.$cache) { + video.$cache = null; + } + } else { + if (!video.$cache) { + video.$cache = $cacheStore.getById(video.uniqueKey); + video.$cache.set(video.uniqueKey, true); + } + renderQueue.push(1); + } + + renderQueue.push( + displayObjectBlendToNumberService(video.blendMode) + ); + + if (video.filters?.length) { + + let updated = false; + const params = []; + const bounds = $getBoundsArray(0, 0, 0, 0); + for (let idx = 0; idx < video.filters.length; idx++) { + + const filter = video.filters[idx]; + if (!filter || !filter.canApplyFilter()) { + continue; + } + + // フィルターが更新されたかをチェック + if (filter.$updated) { + updated = true; + } + filter.$updated = false; + + filter.getBounds(bounds); + + const buffer = filter.toNumberArray(); + + for (let idx = 0; idx < buffer.length; idx += 4096) { + params.push(...buffer.subarray(idx, idx + 4096)); + } + } + + const useFilfer = params.length > 0; + if (useFilfer) { + renderQueue.push( + +useFilfer, +updated, + bounds[0], bounds[1], bounds[2], bounds[3], + params.length + ); + renderQueue.set(new Float32Array(params)); + } + + $poolBoundsArray(bounds); + } else { + renderQueue.push(0); + } + + if (tColorTransform !== color_transform) { + ColorTransform.release(tColorTransform); + } + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } +}; \ No newline at end of file diff --git a/packages/display/src/Video/usecase/VideoHitTestUseCase.ts b/packages/display/src/Video/usecase/VideoHitTestUseCase.ts new file mode 100644 index 00000000..7e3135f3 --- /dev/null +++ b/packages/display/src/Video/usecase/VideoHitTestUseCase.ts @@ -0,0 +1,53 @@ +import type { IPlayerHitObject } from "../../interface/IPlayerHitObject"; +import type { Video } from "@next2d/media"; +import { Matrix } from "@next2d/geom"; +import { execute as displayObjectGetRawMatrixUseCase } from "../../DisplayObject/usecase/DisplayObjectGetRawMatrixUseCase"; + +/** + * @description Video のヒット判定 + * Hit judgment of Video + * + * @param {Video} video + * @param {CanvasRenderingContext2D} hit_context + * @param {Float32Array} matrix + * @param {IPlayerHitObject} hit_object + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + video: Video, + hit_context: CanvasRenderingContext2D, + matrix: Float32Array, + hit_object: IPlayerHitObject +): boolean => { + + const width = video.videoWidth; + const height = video.videoHeight; + if (width <= 0 || height <= 0 ) { + return false; + } + + const rawMatrix = displayObjectGetRawMatrixUseCase(video); + const tMatrix = rawMatrix + ? Matrix.multiply(matrix, rawMatrix) + : matrix; + + hit_context.beginPath(); + hit_context.setTransform( + tMatrix[0], tMatrix[1], tMatrix[2], + tMatrix[3], tMatrix[4], tMatrix[5] + ); + hit_context.beginPath(); + hit_context.moveTo(0, 0); + hit_context.lineTo(width, 0); + hit_context.lineTo(width, height); + hit_context.lineTo(0, height); + hit_context.lineTo(0, 0); + + if (tMatrix !== matrix) { + Matrix.release(tMatrix); + } + + return hit_context.isPointInPath(hit_object.x, hit_object.y); +}; \ No newline at end of file diff --git a/packages/display/src/index.ts b/packages/display/src/index.ts index dd7e3996..82b4a261 100644 --- a/packages/display/src/index.ts +++ b/packages/display/src/index.ts @@ -4,8 +4,6 @@ export * from "./DisplayObject"; export * from "./DisplayObjectContainer"; export * from "./FrameLabel"; export * from "./Graphics"; -export * from "./GraphicsBitmapFill"; -export * from "./GraphicsGradientFill"; export * from "./InteractiveObject"; export * from "./Loader"; export * from "./LoaderInfo"; @@ -14,5 +12,4 @@ export * from "./LoopType"; export * from "./MovieClip"; export * from "./Shape"; export * from "./Sprite"; -export * from "./Stage"; -export * from "./TextField"; \ No newline at end of file +export * from "./Stage"; \ No newline at end of file diff --git a/packages/display/src/interface/IAjaxEvent.ts b/packages/display/src/interface/IAjaxEvent.ts new file mode 100644 index 00000000..c9a77edb --- /dev/null +++ b/packages/display/src/interface/IAjaxEvent.ts @@ -0,0 +1,5 @@ +export interface IAjaxEvent { + loadstart?: Function; + progress?: Function; + loadend?: Function; +} \ No newline at end of file diff --git a/packages/display/src/interface/IAjaxOption.ts b/packages/display/src/interface/IAjaxOption.ts new file mode 100644 index 00000000..3d25ea4b --- /dev/null +++ b/packages/display/src/interface/IAjaxOption.ts @@ -0,0 +1,14 @@ +import type { IURLRequestMethod } from "./IURLRequestMethod"; +import type { IURLLoaderDataFormat } from "./IURLLoaderDataFormat"; +import type { IAjaxEvent } from "./IAjaxEvent"; +import type { IURLRequestHeader } from "./IURLRequestHeader"; + +export interface IAjaxOption { + url: string; + format: IURLLoaderDataFormat; + method: IURLRequestMethod; + withCredentials: boolean; + headers: IURLRequestHeader[]; + data?: any; + event?: IAjaxEvent; +} \ No newline at end of file diff --git a/packages/display/src/interface/IAnimationToolData.ts b/packages/display/src/interface/IAnimationToolData.ts new file mode 100644 index 00000000..d9009686 --- /dev/null +++ b/packages/display/src/interface/IAnimationToolData.ts @@ -0,0 +1,9 @@ +import type { IStageData } from "./IStageData"; +import type { ICharacter } from "./ICharacter"; + +export interface IAnimationToolData { + type: "json"; + stage: IStageData; + characters: ICharacter[]; + symbols: Array>; +} \ No newline at end of file diff --git a/packages/display/src/interface/IAnimationToolDataZlib.ts b/packages/display/src/interface/IAnimationToolDataZlib.ts new file mode 100644 index 00000000..ca572309 --- /dev/null +++ b/packages/display/src/interface/IAnimationToolDataZlib.ts @@ -0,0 +1,4 @@ +export interface IAnimationToolDataZlib { + type: "zlib"; + buffer: number[]; +} \ No newline at end of file diff --git a/packages/display/src/interface/IBlendMode.ts b/packages/display/src/interface/IBlendMode.ts new file mode 100644 index 00000000..9480efe7 --- /dev/null +++ b/packages/display/src/interface/IBlendMode.ts @@ -0,0 +1,15 @@ +export type IBlendMode = "copy" + | "add" + | "alpha" + | "darken" + | "difference" + | "erase" + | "hardlight" + | "invert" + | "layer" + | "lighten" + | "multiply" + | "normal" + | "overlay" + | "screen" + | "subtract"; \ No newline at end of file diff --git a/packages/display/src/interface/IBounds.ts b/packages/display/src/interface/IBounds.ts new file mode 100644 index 00000000..b20c1ee3 --- /dev/null +++ b/packages/display/src/interface/IBounds.ts @@ -0,0 +1,6 @@ +export interface IBounds { + xMin: number; + xMax: number; + yMin: number; + yMax: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/ICapsStyle.ts b/packages/display/src/interface/ICapsStyle.ts new file mode 100644 index 00000000..87ddb794 --- /dev/null +++ b/packages/display/src/interface/ICapsStyle.ts @@ -0,0 +1 @@ +export type ICapsStyle = "none" | "round" | "square"; \ No newline at end of file diff --git a/packages/display/src/interface/ICharacter.ts b/packages/display/src/interface/ICharacter.ts new file mode 100644 index 00000000..1b583b65 --- /dev/null +++ b/packages/display/src/interface/ICharacter.ts @@ -0,0 +1,6 @@ +import type { IMovieClipCharacter } from "./IMovieClipCharacter"; +import type { IVideoCharacter } from "./IVideoCharacter"; +import type { IShapeCharacter } from "./IShapeCharacter"; +import type { ITextFieldCharacter } from "./ITextFieldCharacter"; + +export type ICharacter = IMovieClipCharacter | IVideoCharacter | IShapeCharacter | ITextFieldCharacter; \ No newline at end of file diff --git a/packages/display/src/interface/IColorStop.ts b/packages/display/src/interface/IColorStop.ts new file mode 100644 index 00000000..56b2e743 --- /dev/null +++ b/packages/display/src/interface/IColorStop.ts @@ -0,0 +1,7 @@ +export interface IColorStop { + ratio: number; + R: number; + G: number; + B: number; + A: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/IDictionaryTag.ts b/packages/display/src/interface/IDictionaryTag.ts new file mode 100644 index 00000000..31e8ba03 --- /dev/null +++ b/packages/display/src/interface/IDictionaryTag.ts @@ -0,0 +1,8 @@ +export interface IDictionaryTag { + characterId: number; + name: string; + startFrame: number; + endFrame: number; + depth: number; + clipDepth: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/IDisplayObject.ts b/packages/display/src/interface/IDisplayObject.ts new file mode 100644 index 00000000..05e71532 --- /dev/null +++ b/packages/display/src/interface/IDisplayObject.ts @@ -0,0 +1,3 @@ +import type { DisplayObject } from "../DisplayObject"; + +export type IDisplayObject = T; \ No newline at end of file diff --git a/packages/display/src/interface/IFilterArray.ts b/packages/display/src/interface/IFilterArray.ts new file mode 100644 index 00000000..1cc8fd6c --- /dev/null +++ b/packages/display/src/interface/IFilterArray.ts @@ -0,0 +1,23 @@ +import type { + BlurFilter, + BevelFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; + +export type IFilterArray = Array< + BlurFilter + | BevelFilter + | ColorMatrixFilter + | ConvolutionFilter + | DisplacementMapFilter + | DropShadowFilter + | GlowFilter + | GradientBevelFilter + | GradientGlowFilter +>; \ No newline at end of file diff --git a/packages/display/src/interface/IGradientType.ts b/packages/display/src/interface/IGradientType.ts new file mode 100644 index 00000000..1c97a3e9 --- /dev/null +++ b/packages/display/src/interface/IGradientType.ts @@ -0,0 +1 @@ +export type IGradientType = "linear" | "radial"; \ No newline at end of file diff --git a/packages/display/src/interface/IGrid.ts b/packages/display/src/interface/IGrid.ts new file mode 100644 index 00000000..f95d98fd --- /dev/null +++ b/packages/display/src/interface/IGrid.ts @@ -0,0 +1,6 @@ +export interface IGrid { + x: number; + y: number; + w: number; + h: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/IInterpolationMethod.ts b/packages/display/src/interface/IInterpolationMethod.ts new file mode 100644 index 00000000..a15b1bef --- /dev/null +++ b/packages/display/src/interface/IInterpolationMethod.ts @@ -0,0 +1 @@ +export type IInterpolationMethod = "rgb" | "linearRGB"; \ No newline at end of file diff --git a/packages/display/src/interface/IJointStyle.ts b/packages/display/src/interface/IJointStyle.ts new file mode 100644 index 00000000..7fa2abd9 --- /dev/null +++ b/packages/display/src/interface/IJointStyle.ts @@ -0,0 +1 @@ +export type IJointStyle = "bevel" | "miter" | "round"; \ No newline at end of file diff --git a/packages/display/src/interface/ILoaderInfoData.ts b/packages/display/src/interface/ILoaderInfoData.ts new file mode 100644 index 00000000..a9f63149 --- /dev/null +++ b/packages/display/src/interface/ILoaderInfoData.ts @@ -0,0 +1,8 @@ +import type { IStageData } from "./IStageData"; +import type { ICharacter } from "./ICharacter"; + +export interface ILoaderInfoData { + stage: IStageData; + characters: ICharacter[]; + symbols: Map; +} \ No newline at end of file diff --git a/packages/display/src/interface/ILoopConfig.ts b/packages/display/src/interface/ILoopConfig.ts new file mode 100644 index 00000000..835d87a4 --- /dev/null +++ b/packages/display/src/interface/ILoopConfig.ts @@ -0,0 +1,9 @@ +import type { ILoopType } from "./ILoopType"; + +export interface ILoopConfig { + type: ILoopType; + frame: number; + start: number; + end: number; + tweenFrame?: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/ILoopType.ts b/packages/display/src/interface/ILoopType.ts new file mode 100644 index 00000000..7ee3e8ad --- /dev/null +++ b/packages/display/src/interface/ILoopType.ts @@ -0,0 +1,5 @@ +export type ILoopType = 0 // REPEAT + | 1 // NO_REPEAT + | 2 // FIXED + | 3 // NO_REPEAT_REVERSAL + | 4 ;// REPEAT_REVERSAL \ No newline at end of file diff --git a/packages/display/src/interface/IMovieClipActionObject.ts b/packages/display/src/interface/IMovieClipActionObject.ts new file mode 100644 index 00000000..a7960988 --- /dev/null +++ b/packages/display/src/interface/IMovieClipActionObject.ts @@ -0,0 +1,5 @@ +export interface IMovieClipActionObject { + frame: number; + action: string; + script?: Function; +} \ No newline at end of file diff --git a/packages/display/src/interface/IMovieClipCharacter.ts b/packages/display/src/interface/IMovieClipCharacter.ts new file mode 100644 index 00000000..305a9f20 --- /dev/null +++ b/packages/display/src/interface/IMovieClipCharacter.ts @@ -0,0 +1,18 @@ +import type { IMovieClipSoundObject } from "./IMovieClipSoundObject"; +import type { IMovieClipActionObject } from "./IMovieClipActionObject"; +import type { IMovieClipLabelObject } from "./IMovieClipLabelObject"; +import type { IPlaceObject } from "./IPlaceObject"; +import type { IDictionaryTag } from "./IDictionaryTag"; + +export interface IMovieClipCharacter { + symbol?: string; + extends: string; + totalFrame: number; + controller: Array>; + dictionary: IDictionaryTag[]; + placeMap: Array>; + placeObjects: IPlaceObject[]; + labels?: IMovieClipLabelObject[]; + actions?: IMovieClipActionObject[]; + sounds?: IMovieClipSoundObject[]; +} \ No newline at end of file diff --git a/packages/display/src/interface/IMovieClipLabelObject.ts b/packages/display/src/interface/IMovieClipLabelObject.ts new file mode 100644 index 00000000..da1d6877 --- /dev/null +++ b/packages/display/src/interface/IMovieClipLabelObject.ts @@ -0,0 +1,4 @@ +export interface IMovieClipLabelObject { + name: string; + frame: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/IMovieClipSoundObject.ts b/packages/display/src/interface/IMovieClipSoundObject.ts new file mode 100644 index 00000000..ab1b961e --- /dev/null +++ b/packages/display/src/interface/IMovieClipSoundObject.ts @@ -0,0 +1,6 @@ +import type { ISoundTag } from "./ISoundTag"; + +export interface IMovieClipSoundObject { + frame: number; + sound: ISoundTag[]; +} \ No newline at end of file diff --git a/packages/display/src/interface/IParent.ts b/packages/display/src/interface/IParent.ts new file mode 100644 index 00000000..7e08d66a --- /dev/null +++ b/packages/display/src/interface/IParent.ts @@ -0,0 +1,3 @@ +import type { DisplayObjectContainer } from "../DisplayObjectContainer"; + +export type IParent = T; \ No newline at end of file diff --git a/packages/display/src/interface/IPlaceObject.ts b/packages/display/src/interface/IPlaceObject.ts new file mode 100644 index 00000000..2b0741a3 --- /dev/null +++ b/packages/display/src/interface/IPlaceObject.ts @@ -0,0 +1,15 @@ +import type { ILoopConfig } from "./ILoopConfig"; +import type { ISurfaceFilter } from "./ISurfaceFilter"; +import type { IFilterArray } from "./IFilterArray"; +import type { IBlendMode } from "./IBlendMode"; + +export interface IPlaceObject { + matrix?: number[]; + typedMatrix?: Float32Array; + colorTransform?: number[]; + typedColorTransform?: Float32Array; + blendMode?: IBlendMode; + surfaceFilterList?: ISurfaceFilter[]; + filters?: IFilterArray; + loop?: ILoopConfig; +} \ No newline at end of file diff --git a/packages/display/src/interface/IPlayerHitObject.ts b/packages/display/src/interface/IPlayerHitObject.ts new file mode 100644 index 00000000..67b691fe --- /dev/null +++ b/packages/display/src/interface/IPlayerHitObject.ts @@ -0,0 +1,8 @@ +import type { IDisplayObject } from "./IDisplayObject"; + +export interface IPlayerHitObject { + x: number; + y: number; + pointer: string; + hit: IDisplayObject | null; +} \ No newline at end of file diff --git a/packages/display/src/interface/IShapeCharacter.ts b/packages/display/src/interface/IShapeCharacter.ts new file mode 100644 index 00000000..9f52aca2 --- /dev/null +++ b/packages/display/src/interface/IShapeCharacter.ts @@ -0,0 +1,15 @@ +import type { IGrid } from "./IGrid"; +import type { IBounds } from "./IBounds"; + +export interface IShapeCharacter { + symbol?: string; + extends: string; + bounds: IBounds; + bitmapId: number; + inBitmap?: boolean; + grid?: IGrid | null; + recodes?: any[] | null; + buffer?: number[] | null; + imageBuffer?: Uint8Array; + recodeBuffer?: Float32Array; +} \ No newline at end of file diff --git a/packages/display/src/interface/ISoundCharacter.ts b/packages/display/src/interface/ISoundCharacter.ts new file mode 100644 index 00000000..b0363a15 --- /dev/null +++ b/packages/display/src/interface/ISoundCharacter.ts @@ -0,0 +1,4 @@ +export interface ISoundCharacter { + buffer: number[] | null; + audioBuffer: AudioBuffer | null; +} \ No newline at end of file diff --git a/packages/display/src/interface/ISoundTag.ts b/packages/display/src/interface/ISoundTag.ts new file mode 100644 index 00000000..51e955dd --- /dev/null +++ b/packages/display/src/interface/ISoundTag.ts @@ -0,0 +1,6 @@ +export interface ISoundTag { + characterId: number; + volume: number; + autoPlay: boolean; + loopCount: number; +} \ No newline at end of file diff --git a/packages/display/src/interface/ISpreadMethod.ts b/packages/display/src/interface/ISpreadMethod.ts new file mode 100644 index 00000000..dcae8d20 --- /dev/null +++ b/packages/display/src/interface/ISpreadMethod.ts @@ -0,0 +1 @@ +export type ISpreadMethod = "pad" | "reflect" | "repeat"; \ No newline at end of file diff --git a/packages/display/src/interface/ISprite.ts b/packages/display/src/interface/ISprite.ts new file mode 100644 index 00000000..eeb7bbf5 --- /dev/null +++ b/packages/display/src/interface/ISprite.ts @@ -0,0 +1,3 @@ +import type { Sprite } from "@next2d/display"; + +export type ISprite = T; \ No newline at end of file diff --git a/packages/display/src/interface/IStageData.ts b/packages/display/src/interface/IStageData.ts new file mode 100644 index 00000000..e6d87748 --- /dev/null +++ b/packages/display/src/interface/IStageData.ts @@ -0,0 +1,6 @@ +export interface IStageData { + width: number; + height: number; + fps: number + bgColor: string; +} \ No newline at end of file diff --git a/packages/display/src/interface/ISurfaceFilter.ts b/packages/display/src/interface/ISurfaceFilter.ts new file mode 100644 index 00000000..c761a9d3 --- /dev/null +++ b/packages/display/src/interface/ISurfaceFilter.ts @@ -0,0 +1,14 @@ +type ClassName = "BevelFilter" + | "BlurFilter" + | "ColorMatrixFilter" + | "ConvolutionFilter" + | "DisplacementMapFilter" + | "DropShadowFilter" + | "GlowFilter" + | "GradientBevelFilter" + | "GradientGlowFilter"; + +export interface ISurfaceFilter { + class: ClassName; + params: any[]; +} \ No newline at end of file diff --git a/packages/display/src/interface/ITextFieldCharacter.ts b/packages/display/src/interface/ITextFieldCharacter.ts new file mode 100644 index 00000000..f3c9837e --- /dev/null +++ b/packages/display/src/interface/ITextFieldCharacter.ts @@ -0,0 +1,27 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; +import type { ITextFieldType } from "./ITextFieldType"; +import type { IBounds } from "./IBounds"; + +export interface ITextFieldCharacter { + symbol?: string; + extends: string; + font: string; + size: number; + align: ITextFormatAlign; + color: number; + leading: number; + letterSpacing: number; + leftMargin: number; + rightMargin: number; + fontType: number; + autoSize: number; + inputType: ITextFieldType; + multiline: boolean; + wordWrap: boolean; + border: boolean; + scroll: boolean; + thickness: number; + thicknessColor: number; + bounds: IBounds; + text: string; +} \ No newline at end of file diff --git a/packages/display/src/interface/ITextFieldType.ts b/packages/display/src/interface/ITextFieldType.ts new file mode 100644 index 00000000..3f92a04b --- /dev/null +++ b/packages/display/src/interface/ITextFieldType.ts @@ -0,0 +1 @@ +export type ITextFieldType = "input" | "static"; \ No newline at end of file diff --git a/packages/display/src/interface/ITextFormatAlign.ts b/packages/display/src/interface/ITextFormatAlign.ts new file mode 100644 index 00000000..aa48dea1 --- /dev/null +++ b/packages/display/src/interface/ITextFormatAlign.ts @@ -0,0 +1 @@ +export type ITextFormatAlign = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/display/src/interface/IURLLoaderDataFormat.ts b/packages/display/src/interface/IURLLoaderDataFormat.ts new file mode 100644 index 00000000..1b80009d --- /dev/null +++ b/packages/display/src/interface/IURLLoaderDataFormat.ts @@ -0,0 +1 @@ +export type IURLLoaderDataFormat = "json" | "arraybuffer" | "text"; \ No newline at end of file diff --git a/packages/display/src/interface/IURLRequestHeader.ts b/packages/display/src/interface/IURLRequestHeader.ts new file mode 100644 index 00000000..7034cfb9 --- /dev/null +++ b/packages/display/src/interface/IURLRequestHeader.ts @@ -0,0 +1,4 @@ +export interface IURLRequestHeader { + name: string; + value: string; +} \ No newline at end of file diff --git a/packages/display/src/interface/IURLRequestMethod.ts b/packages/display/src/interface/IURLRequestMethod.ts new file mode 100644 index 00000000..202e485b --- /dev/null +++ b/packages/display/src/interface/IURLRequestMethod.ts @@ -0,0 +1 @@ +export type IURLRequestMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT"; \ No newline at end of file diff --git a/packages/display/src/interface/IVideoCharacter.ts b/packages/display/src/interface/IVideoCharacter.ts new file mode 100644 index 00000000..da241a86 --- /dev/null +++ b/packages/display/src/interface/IVideoCharacter.ts @@ -0,0 +1,12 @@ +import type { IBounds } from "./IBounds"; + +export interface IVideoCharacter { + symbol?: string; + extends: string; + buffer: number[] | null; + videoData: Uint8Array; + volume: number; + loop: boolean; + autoPlay: boolean; + bounds: IBounds; +} \ No newline at end of file diff --git a/packages/events/package.json b/packages/events/package.json index cb1551fb..277817d3 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/events", "version": "*", - "description": "Next2D Events Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Events Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -30,13 +22,5 @@ "repository": { "type": "git", "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/interface": "file:../interface", - "@next2d/display": "file:../display", - "@next2d/core": "file:../core", - "@next2d/util": "file:../util", - "@next2d/net": "file:../net", - "@next2d/share": "file:../share" } } diff --git a/packages/events/src/Event.test.ts b/packages/events/src/Event.test.ts new file mode 100644 index 00000000..6a2d1cf8 --- /dev/null +++ b/packages/events/src/Event.test.ts @@ -0,0 +1,66 @@ +import { Event } from "./Event"; +import { describe, expect, it } from "vitest"; + +describe("Event.js property test", () => +{ + + it("ADDED test", () => + { + expect(Event.ADDED).toBe("added"); + }); + + it("ADDED_TO_STAGE test", () => + { + expect(Event.ADDED_TO_STAGE).toBe("addedToStage"); + }); + + it("COMPLETE test", () => + { + expect(Event.COMPLETE).toBe("complete"); + }); + + it("ENDED test", () => + { + expect(Event.ENDED).toBe("ended"); + }); + + it("ENTER_FRAME test", () => + { + expect(Event.ENTER_FRAME).toBe("enterFrame"); + }); + + it("FRAME_LABEL test", () => + { + expect(Event.FRAME_LABEL).toBe("frameLabel"); + }); + + it("OPEN test", () => + { + expect(Event.OPEN).toBe("open"); + }); + + it("REMOVED test", () => + { + expect(Event.REMOVED).toBe("removed"); + }); + + it("REMOVED_FROM_STAGE test", () => + { + expect(Event.REMOVED_FROM_STAGE).toBe("removedFromStage"); + }); + + it("RESIZE test", () => + { + expect(Event.RESIZE).toBe("resize"); + }); + + it("SCROLL test", () => + { + expect(Event.SCROLL).toBe("scroll"); + }); + + it("INPUT test", () => + { + expect(Event.INPUT).toBe("input"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/Event.ts b/packages/events/src/Event.ts index ec98db91..b3f86a53 100644 --- a/packages/events/src/Event.ts +++ b/packages/events/src/Event.ts @@ -1,94 +1,150 @@ +import type { EventDispatcher } from "./EventDispatcher"; +import type { IEventDispatcher } from "./interface/IEventDispatcher"; import { EventPhase } from "./EventPhase"; -import type { EventDispatcherImpl } from "@next2d/interface"; /** - * Event クラスのメソッドは、イベントリスナー関数で使用してイベントオブジェクトの動作に影響を与えることができます。 - * 一部のイベントにはデフォルトの動作が関連付けられています。 - * 例えば、doubleClick イベントには、イベント時にマウスポインター位置の単語がハイライト表示されるというデフォルトの動作が関連付けられています。 - * イベントリスナーで preventDefault() メソッドを呼び出してこの動作をキャンセルできます。 - * また、stopPropagation() メソッドまたは stopImmediatePropagation() メソッドを呼び出すと、 - * 現在のイベントリスナーを、イベントを処理する最後のイベントリスナーにすることができます。 + * @description Event クラスのメソッドは、イベントリスナー関数で使用してイベントオブジェクトの動作に影響を与えることができます。 + * 一部のイベントにはデフォルトの動作が関連付けられています。 + * 例えば、doubleClick イベントには、イベント時にマウスポインター位置の単語がハイライト表示されるというデフォルトの動作が関連付けられています。 + * イベントリスナーで preventDefault() メソッドを呼び出してこの動作をキャンセルできます。 + * また、stopPropagation() メソッドまたは stopImmediatePropagation() メソッドを呼び出すと、 + * 現在のイベントリスナーを、イベントを処理する最後のイベントリスナーにすることができます。 * - * The methods of the Event class can be used in event listener functions to affect the behavior of the event object. - * Some events have an associated default behavior. For example, - * the doubleClick event has an associated default behavior that highlights the word under the mouse pointer at the time of the event. - * Your event listener can cancel this behavior by calling the preventDefault() method. - * You can also make the current event listener the last one to process - * an event by calling the stopPropagation() or stopImmediatePropagation() method. + * The methods of the Event class can be used in event listener functions to affect the behavior of the event object. + * Some events have an associated default behavior. For example, + * the doubleClick event has an associated default behavior that highlights the word under the mouse pointer at the time of the event. + * Your event listener can cancel this behavior by calling the preventDefault() method. + * You can also make the current event listener the last one to process + * an event by calling the stopPropagation() or stopImmediatePropagation() method. * * @class * @memberOf next2d.events */ export class Event { - private readonly _$type: string; - private readonly _$bubbles: boolean; - private readonly _$cancelable: boolean; - private _$target: EventDispatcherImpl | null; - private _$currentTarget: EventDispatcherImpl | null; - private _$listener: Function | null; - private _$eventPhase: number; + /** + * @description イベントのタイプです。 + * The type of event. + * + * @member {string} + * @readonly + * @public + */ + public readonly type: string; + + /** + * @description イベントがバブリングイベントかどうかを示します。 + * Indicates whether an event is a bubbling event. + * + * @member {boolean} + * @default false + * @readonly + * @public + */ + public readonly bubbles: boolean; + + /** + * @description 実行される関数 + * The function to execute. + * + * @type {Function | null} + * @default null + * @public + */ + public listener: Function | null; + + /** + * @description イベント登録を行なったオブジェクト + * The object that registered the event. + * + * @type {EventDispatcher | null} + * @default null + * @public + */ + public target: IEventDispatcher | null; + + /** + * @description イベントを発火したオブジェクト + * The object that fired the event. + * + * @type {EventDispatcher | null} + * @default null + * @public + */ + public currentTarget: IEventDispatcher | null; + + /** + * @description イベントフェーズ + * Event phase. + * + * @see {EventPhase} + * @type {number} + * @default EventPhase.AT_TARGET + * @public + */ + public eventPhase: number; + + /** + * @type {boolean} + * @private + */ public _$stopImmediatePropagation: boolean; + + /** + * @type {boolean} + * @private + */ public _$stopPropagation: boolean; /** * @param {string} type * @param {boolean} [bubbles=false] - * @param {boolean} [cancelable=false] * * @constructor * @public */ - constructor ( - type: string, - bubbles: boolean = false, - cancelable: boolean = false - ) { - + constructor (type: string, bubbles: boolean = false) + { /** * @type {string} * @private */ - this._$type = `${type}`; - - /** - * @type {boolean} - * @private - */ - this._$bubbles = bubbles; + this.type = `${type}`; /** * @type {boolean} + * @default false * @private */ - this._$cancelable = cancelable; + this.bubbles = bubbles; /** - * @type {EventDispatcher} + * @type {EventDispatcher | null} + * @default null * @private */ - this._$target = null; + this.target = null; /** - * @type {EventDispatcher} + * @type {EventDispatcher | null} * @default null * @private */ - this._$currentTarget = null; + this.currentTarget = null; /** - * @type {number} + * @type {number} * @default EventPhase.AT_TARGET * @private */ - this._$eventPhase = EventPhase.AT_TARGET; + this.eventPhase = EventPhase.AT_TARGET; /** * @type {function} * @default null * @private */ - this._$listener = null; + this.listener = null; /** * @type {boolean} @@ -106,82 +162,10 @@ export class Event } /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Event] - * @method - * @static - */ - static toString (): string - { - return "[class Event]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.Event - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.Event"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description Sprite または MovieClip を親に持つオブジェクトに追加されたときに発生します。 + * Occurs when the object is added to a parent object. * * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString("Event", "type", "bubbles", "cancelable", "eventPhase"); - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.Event - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.Event"; - } - - /** - * @description ACTIVATE 定数は、type プロパティ(activate イベントオブジェクト)の値を定義します。 - * The ACTIVATE constant defines the value - * of the type property of an activate event object. - * - * @return {string} - * @default activate - * @const - * @static - */ - static get ACTIVATE (): string - { - return "activate"; - } - - /** - * @description Event.ADDED 定数は、added イベントオブジェクトの type プロパティの値を定義します。 - * The Event.ADDED constant defines the value - * of the type property of an added event object. - * - * @return {string} - * @default added * @const * @static */ @@ -191,12 +175,10 @@ export class Event } /** - * @description Event.ADDED_TO_STAGE 定数は、type プロパティ(addedToStage イベントオブジェクト)の値を定義します。 - * The Event.ADDED_TO_STAGE constant defines the value - * of the type property of an addedToStage event object. + * @description Stage に追加されたときに発生します。 + * Occurs when the object is added to the Stage. * - * @return {string} - * @default addedToStage + * @return {string} * @const * @static */ @@ -206,27 +188,10 @@ export class Event } /** - * @description Event.CHANGE 定数は、type プロパティ(change イベントオブジェクト)の値を定義します。 - * The Event.CHANGE constant defines the value - * of the type property of a change event object. - * - * @return {string} - * @default change - * @const - * @static - */ - static get CHANGE (): string - { - return "change"; - } - - /** - * @description Event.COMPLETE 定数は、complete イベントオブジェクトの type プロパティの値を定義します。 - * The Event.COMPLETE constant defines the value - * of the type property of a complete event object. + * @description 読み込みや、処理完了時に発生します。 + * Occurs when loading or processing is complete. * - * @return {string} - * @default complete + * @return {string} * @const * @static */ @@ -236,27 +201,23 @@ export class Event } /** - * @description Event.DEACTIVATE 定数は、deactivate イベントオブジェクトの type プロパティの値を定義します。 - * The Event.DEACTIVATE constant defines the value - * of the type property of a deactivate event object. + * @description サウンドやビデオなどの再生処理の終了時に発生します。 + * Occurs when playback of a sound or video ends. * - * @return {string} - * @default deactivate + * @return {string} * @const * @static */ - static get DEACTIVATE (): string + static get ENDED (): string { - return "deactivate"; + return "ended"; } /** - * @description Event.ENTER_FRAME 定数は、enterFrame イベントオブジェクトの type プロパティの値を定義します。 - * The Event.ENTER_FRAME constant defines the value - * of the type property of an enterFrame event object. + * @description MovieClip のフレームが変更される度に発生します。 + * Occurs when the frame of a MovieClip changes. * - * @return {string} - * @default enterFrame + * @return {string} * @const * @static */ @@ -266,42 +227,10 @@ export class Event } /** - * @description Event.EXIT_FRAME 定数は、exitFrame イベントオブジェクトの type プロパティの値を定義します。 - * The Event.EXIT_FRAME constant defines the value - * of the type property of an exitFrame event object. + * @description MovieClip のフレームラベルのキーフレームに到達したときに発生します。 + * Occurs when a MovieClip reaches a keyframe that designates a frame label. * - * @return {string} - * @default exitFrame - * @const - * @static - */ - static get EXIT_FRAME (): string - { - return "exitFrame"; - } - - /** - * @description Event.FRAME_CONSTRUCTED 定数は、frameConstructed イベントオブジェクトの type プロパティの値を定義します。 - * The Event.FRAME_CONSTRUCTED constant defines the value - * of the type property of an frameConstructed event object. - * - * @return {string} - * @default frameConstructed - * @const - * @static - */ - static get FRAME_CONSTRUCTED (): string - { - return "frameConstructed"; - } - - /** - * @description Event.FRAME_LABEL 定数は、frameLabel イベントオブジェクトの type プロパティの値を定義します。 - * The Event.FRAME_LABEL constant defines the value - * of the type property of an frameLabel event object. - * - * @return {string} - * @default frameLabel + * @return {string} * @const * @static */ @@ -311,57 +240,36 @@ export class Event } /** - * @description Event.INIT 定数は、init イベントオブジェクトの type プロパティの値を定義します。 - * The Event.INIT constant defines the value - * of the type property of an init event object. - * - * @return {string} - * @default frameConstructed - * @const - * @static - */ - static get INIT (): string - { - return "init"; - } - - /** - * @description Event.LOAD 定数は、load イベントオブジェクトの type プロパティの値を定義します。 - * The Event.LOAD constant defines the value - * of the type property of an load event object. + * @description データの読み込み開始時に発生します。 + * Occurs when sound data loading starts. * - * @return {string} - * @default frameConstructed + * @return {string} * @const * @static */ - static get LOAD (): string + static get INPUT (): string { - return "load"; + return "input"; } /** - * @description Event.MOUSE_LEAVE 定数は、mouseLeave イベントオブジェクトの type プロパティの値を定義します。 - * The Event.MOUSE_LEAVE constant defines the value - * of the type property of a mouseLeave event object. + * @description データの読み込み開始時に発生します。 + * Occurs when sound data loading starts. * - * @return {string} - * @default mouseLeave + * @return {string} * @const * @static */ - static get MOUSE_LEAVE (): string + static get OPEN (): string { - return "mouseLeave"; + return "open"; } /** - * @description Event.REMOVED 定数は、removed プロパティ(paste イベントオブジェクト)の値を定義します。 - * The Event.REMOVED constant defines the value - * of the type property of a removed event object. + * @description Sprite または MovieClip を親に持つオブジェクトから削除されたときに発生します。 + * Occurs when the object is removed from a parent object. * - * @return {string} - * @default removed + * @return {string} * @const * @static */ @@ -371,12 +279,10 @@ export class Event } /** - * @description Event.REMOVED_FROM_STAGE 定数は、removedFromStage イベントオブジェクトの type プロパティの値を定義します。 - * The Event.REMOVED_FROM_STAGE constant defines the value - * of the type property of a removedFromStage event object. + * @description Stage から削除されたときに発生します。 + * Occurs when the object is removed from the Stage. * - * @return {string} - * @default removedFromStage + * @return {string} * @const * @static */ @@ -386,29 +292,10 @@ export class Event } /** - * @description Event.RENDER 定数は、render イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.RENDER constant defines the value - * of the type property of a render event object. + * @description 画面のサイズが変更されたときに発生します。 + * Occurs when the screen size changes. * * @return {string} - * @default render - * @const - * @static - */ - static get RENDER (): string - { - return "render"; - } - - /** - * @description Event.RESIZE 定数は、resize イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.RESIZE constant defines the value - * of the type property of a resize event object. - * - * @return {string} - * @default resize * @const * @static */ @@ -418,13 +305,10 @@ export class Event } /** - * @description Event.SCROLL 定数は、render イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.SCROLL constant defines the value - * of the type property of a render event object. + * @description スクロール位置が変更されたときに発生します。 + * Occurs when the scroll position changes. * * @return {string} - * @default scroll * @const * @static */ @@ -434,212 +318,8 @@ export class Event } /** - * @description Event.OPEN 定数は、render イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.OPEN constant defines the value - * of the type property of a render event object. - * - * @return {string} - * @default open - * @const - * @static - */ - static get OPEN (): string - { - return "open"; - } - - /** - * @description Event.STOP 定数は、render イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.STOP constant defines the value - * of the type property of a render event object. - * - * @return {string} - * @default stop - * @const - * @static - */ - static get STOP (): string - { - return "stop"; - } - - /** - * @description Event.SOUND_COMPLETE 定数は、soundComplete イベントオブジェクトの type プロパティの値を定義します。 - * The Event.SOUND_COMPLETE constant defines the value - * of the type property of a soundComplete event object. - * - * @return {string} - * @default render - * @const - * @static - */ - static get SOUND_COMPLETE (): string - { - return "soundComplete"; - } - - /** - * @description Event.UPDATE 定数は、render イベントオブジェクトの - * type プロパティの値を定義します。 - * The Event.STOP constant defines the value - * of the type property of a render event object. - * - * @return {string} - * @default update - * @const - * @static - */ - static get UPDATE (): string - { - return "update"; - } - - /** - * @description イベントがバブリングイベントかどうかを示します。 - * Indicates whether an event is a bubbling event. - * - * @member {boolean} - * @readonly - * @public - */ - get bubbles (): boolean - { - return this._$bubbles; - } - - /** - * @description イベントに関連付けられた動作を回避できるかどうかを示します。 - * Indicates whether the behavior associated - * with the event can be prevented. - * - * @member {boolean} - * @readonly - * @public - */ - get cancelable (): boolean - { - return this._$cancelable; - } - - /** - * @description イベントリスナーで Event オブジェクトをアクティブに処理しているオブジェクトです。 - * The object that is actively processing the Event object - * with an event listener. - * - * @member {EventDispatcher|null} - * @public - */ - get currentTarget (): EventDispatcherImpl - { - return this._$currentTarget as EventDispatcherImpl; - } - set currentTarget (current_target: EventDispatcherImpl) - { - this._$currentTarget = current_target; - } - - /** - * @description イベントフローの現在の段階です。 - * The current phase in the event flow. - * - * @member {number} - * @public - */ - get eventPhase (): number - { - return this._$eventPhase; - } - set eventPhase (event_phase: number) - { - this._$eventPhase = event_phase; - } - - /** - * @description 現在コールされている関数 - * Function currently being called. - * - * @member {function} - * @public - */ - get listener (): Function | null - { - return this._$listener; - } - set listener (listener: Function | null) - { - this._$listener = listener; - } - - /** - * @description イベントターゲットです。 - * The event target. - * - * @member {EventDispatcher|null} - * @public - */ - get target (): EventDispatcherImpl - { - return this._$target ? this._$target : this._$currentTarget; - } - set target (target: EventDispatcherImpl) - { - this._$target = target; - } - - /** - * @description イベントのタイプです。 - * The type of event. - * - * @member {string} - * @readonly - * @public - */ - get type (): string - { - return this._$type; - } - - /** - * @description カスタム ActionScript 3.0 Event クラスに - * toString() メソッドを実装するためのユーティリティ関数です。 - * A utility function for implementing the toString() method - * in custom ActionScript 3.0 Event classes. - * - * @return {string} - * @method - * @public - */ - formatToString (...args: string[]): string - { - let str = `[${args[0]}`; - - for (let idx:number = 1; idx < args.length; ++idx) { - - // eslint-disable-next-line prefer-rest-params - const name = args[idx]; - - str += ` ${name}=`; - - // @ts-ignore - const value = this[name]; - if (typeof value === "string") { - str += `"${value}"`; - } else { - str += `${value}`; - } - - } - - return `${str}]`; - } - - /** - * @description イベントフローの現在のノードおよび後続するノードで、 - * イベントリスナーが処理されないようにします。 - * Prevents processing of any event listeners in the current node - * and any subsequent nodes in the event flow. + * @description イベントフローの現在のノードおよび後続するノードで、イベントリスナーが処理されないようにします。 + * Prevents processing of any event listeners in the current node and any subsequent nodes in the event flow. * * @return {void} * @method @@ -651,10 +331,8 @@ export class Event } /** - * @description イベントフローの現在のノードに後続するノードで - * イベントリスナーが処理されないようにします。 - * Prevents processing of any event listeners in nodes subsequent - * to the current node in the event flow. + * @description イベントフローの現在のノードに後続するノードで、イベントリスナーが処理されないようにします。 + * Prevents processing of any event listeners in nodes subsequent to the current node in the event flow. * * @return {void} * @method diff --git a/packages/events/src/EventDispatcher.ts b/packages/events/src/EventDispatcher.ts index 75ec3724..d2cd4a1f 100644 --- a/packages/events/src/EventDispatcher.ts +++ b/packages/events/src/EventDispatcher.ts @@ -1,39 +1,22 @@ -import { Event } from "./Event"; -import { EventPhase } from "./EventPhase"; -import type { Player } from "@next2d/core"; -import type { EventListenerImpl } from "@next2d/interface"; -import type { DisplayObjectContainer } from "@next2d/display"; -import { - $setCurrentLoaderInfo, - $currentPlayer -} from "@next2d/util"; -import { - $getMap, - $poolMap, - $getArray, - $poolArray -} from "@next2d/share"; +import type { Event } from "./Event"; +import type { IEventListener } from "./interface/IEventListener"; +import { execute as eventDispatcherAddEventListenerService } from "./EventDispatcher/service/EventDispatcherAddEventListenerService"; +import { execute as eventDispatcherHasEventListenerService } from "./EventDispatcher/service/EventDispatcherHasEventListenerService"; +import { execute as eventDispatcherRemoveEventListenerService } from "./EventDispatcher/service/EventDispatcherRemoveEventListenerService"; +import { execute as eventDispatcherRemoveAllEventListenerService } from "./EventDispatcher/service/EventDispatcherRemoveAllEventListenerService"; +import { execute as eventDispatcherWillTriggerService } from "./EventDispatcher/service/EventDispatcherWillTriggerService"; +import { execute as eventDispatcherDispatchEventService } from "./EventDispatcher/service/EventDispatcherDispatchEventService"; /** - * EventDispatcher クラスは、イベントを送出するすべてのクラスの基本クラスです。 - * - * The EventDispatcher class is the base class for all classes that dispatch events. - * - * @example Example usage of EventDispatcher. - * // new ColorTransform - * const {EventDispatcher} = next2d.events; - * const eventDispatcher = new EventDispatcher(); - * eventDispatcher.addEventListener(Event.ENTER_FRAME, function (event) - * { - * // more... - * }); + * @description EventDispatcher クラスは、イベントを送出するすべてのクラスの基本クラスです。 + * The EventDispatcher class is the base class for all classes that dispatch events. * * @class * @memberOf next2d.events */ export class EventDispatcher { - public _$events: Map|null; + public _$events: Map | null; /** * @constructor @@ -49,61 +32,6 @@ export class EventDispatcher this._$events = null; } - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class EventDispatcher] - * @method - * @static - */ - static toString (): string - { - return "[class EventDispatcher]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.EventDispatcher - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.EventDispatcher"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return "[object EventDispatcher]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.EventDispatcher - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.EventDispatcher"; - } - /** * @description イベントリスナーオブジェクトを EventDispatcher オブジェクトに登録し、 * リスナーがイベントの通知を受け取るようにします。 @@ -112,127 +40,25 @@ export class EventDispatcher * * @param {string} type * @param {function} listener - * @param {boolean} [use_capture=false] - * @param {number} [priority=0] + * @param {boolean} [use_capture = false] + * @param {number} [priority = 0] * @return {void} * @method * @public */ addEventListener ( - type: string, listener: Function, + type: string, + listener: Function, use_capture: boolean = false, priority: number = 0 ): void { - - const player: Player = $currentPlayer(); - - let events: EventListenerImpl[]; - let isBroadcast: boolean = false; - - type = `${type}`; - switch (type) { - - // broadcast event - case Event.ENTER_FRAME: - case Event.EXIT_FRAME: - case Event.FRAME_CONSTRUCTED: - case Event.RENDER: - case Event.ACTIVATE: - case Event.DEACTIVATE: - case "keyDown": - case "keyUp": - - if (!player.broadcastEvents.size - || !player.broadcastEvents.has(type) - ) { - player.broadcastEvents.set(type, $getArray()); - } - - events = player.broadcastEvents.get(type) || $getArray(); - - isBroadcast = true; - - break; - - // normal event - default: - - // init - if (!this._$events) { - this._$events = $getMap(); - } - - if (!this._$events.size || !this._$events.has(type)) { - this._$events.set(type, $getArray()); - } - - events = this._$events.get(type) || $getArray(); - - break; - - } - - // duplicate check - let length: number = events.length; - for (let idx: number = 0; idx < length; ++idx) { - - const event: EventListenerImpl = events[idx]; - if (use_capture !== event.useCapture) { - continue; - } - - if (event.target !== this) { - continue; - } - - if (event.listener === listener) { - length = idx; - } - - } - - // add or overwrite - events[length] = { - "listener": listener, - "priority": priority, - "useCapture": use_capture, - "target": this - }; - - if (events.length > 1) { - - // sort(DESC) - events.sort(function (a: EventListenerImpl, b: EventListenerImpl) - { - switch (true) { - - case a.priority > b.priority: - return -1; - - case a.priority < b.priority: - return 1; - - default: - return 0; - - } - }); - + if (!this._$events) { + this._$events = new Map(); } - // set new event - if (isBroadcast) { - - player.broadcastEvents.set(type, events); - - } else { - - if (!this._$events) { - this._$events = $getMap(); - } - - this._$events.set(type, events); - } + eventDispatcherAddEventListenerService( + this, type, listener, use_capture, priority + ); } /** @@ -244,274 +70,9 @@ export class EventDispatcher * @method * @public */ - dispatchEvent (event: Event) + dispatchEvent (event: E): boolean { - switch (event.type) { - - case Event.ENTER_FRAME: - case Event.EXIT_FRAME: - case Event.FRAME_CONSTRUCTED: - case Event.RENDER: - case Event.ACTIVATE: - case Event.DEACTIVATE: - case "keyDown": - case "keyUp": - { - const player = $currentPlayer(); - - if (player && player.broadcastEvents.size - && player.broadcastEvents.has(event.type) - ) { - - const events: EventListenerImpl[] = player.broadcastEvents.get(event.type) as NonNullable; - for (let idx: number = 0; idx < events.length; ++idx) { - - const obj: EventListenerImpl = events[idx]; - if (obj.target !== this) { - continue; - } - - // start target - event.eventPhase = EventPhase.AT_TARGET; - - // event execute - event.currentTarget = obj.target; - - try { - - event.listener = obj.listener; - obj.listener.call(null, event); - - } catch (e) { - - console.error(e); - - return false; - - } - } - - return true; - } - } - break; - - default: - { - - let events: EventListenerImpl[] | null = null; - if (this._$events - && this._$events.size - && this._$events.has(event.type) - ) { - events = this._$events.get(event.type) as NonNullable; - if (events) { - events = events.slice(0); - } - } - - if (!events) { - events = $getArray(); - } - - // parent - const parentEvents = $getArray(); - if ("parent" in this) { - - let parent: DisplayObjectContainer | null = this.parent as NonNullable; - while (parent) { - - if (parent.hasEventListener(event.type)) { - - const events: EventListenerImpl[] | void = parent._$events - ? parent._$events.get(event.type) - : undefined; - - if (events) { - parentEvents.push(events); - } - } - - parent = parent.parent; - - } - - } - - event.target = this; - if (events.length || parentEvents.length) { - - // start capture - event.eventPhase = EventPhase.CAPTURING_PHASE; - - // stage => parent... end - if (parentEvents.length) { - - switch (true) { - - case event._$stopImmediatePropagation: - case event._$stopPropagation: - break; - - default: - - parentEvents.reverse(); - for (let idx: number = 0; idx < parentEvents.length; ++idx) { - - const targets: EventListenerImpl[] = parentEvents[idx]; - for (let idx: number = 0; idx < targets.length; ++idx) { - - const obj: EventListenerImpl = targets[idx]; - if (!obj.useCapture) { - continue; - } - - // event execute - event.currentTarget = obj.target; - $setCurrentLoaderInfo( - obj.target.loaderInfo - ); - - try { - - event.listener = obj.listener; - obj.listener.call(null, event); - - } catch (e) { - - console.error(e); - - return false; - } - - if (event._$stopImmediatePropagation) { - break; - } - - } - - if (event._$stopImmediatePropagation) { - break; - } - - } - parentEvents.reverse(); - - break; - } - - } - - // start target - event.eventPhase = EventPhase.AT_TARGET; - if (!event._$stopImmediatePropagation - && !event._$stopPropagation - ) { - - const length: number = events.length; - for (let idx: number = 0; idx < length; ++idx) { - - const obj: EventListenerImpl = events[idx]; - if (obj.useCapture) { - continue; - } - - // event execute - event.currentTarget = obj.target; - - $setCurrentLoaderInfo( - obj.target.loaderInfo - ); - - try { - - event.listener = obj.listener; - obj.listener.call(null, event); - - } catch (e) { - - console.error(e); - - return false; - } - - if (event._$stopImmediatePropagation) { - break; - } - - } - } - - // start bubbling - event.eventPhase = EventPhase.BUBBLING_PHASE; - switch (true) { - - case event._$stopImmediatePropagation: - case event._$stopPropagation: - case !event.bubbles: - break; - - default: - - // this => parent... => stage end - for (let idx: number = 0; idx < parentEvents.length; ++idx) { - - const targets: EventListenerImpl[] = parentEvents[idx]; - for (let idx: number = 0; idx < targets.length; ++idx) { - - const obj: EventListenerImpl = targets[idx]; - if (obj.useCapture) { - continue; - } - - // event execute - event.currentTarget = obj.target; - $setCurrentLoaderInfo( - obj.target.loaderInfo - ); - - try { - - event.listener = obj.listener; - obj.listener.call(null, event); - - } catch (e) { - - console.error(e); - - return false; - } - - if (event._$stopImmediatePropagation) { - break; - } - } - - if (event._$stopImmediatePropagation) { - break; - } - - } - - break; - - } - - $poolArray(events); - $poolArray(parentEvents); - - return true; - - } - - $poolArray(events); - $poolArray(parentEvents); - } - break; - - } - - return false; + return eventDispatcherDispatchEventService(this, event); } /** @@ -525,42 +86,7 @@ export class EventDispatcher */ hasEventListener (type: string): boolean { - type = `${type}`; - switch (type) { - - case Event.ENTER_FRAME: - case Event.EXIT_FRAME: - case Event.FRAME_CONSTRUCTED: - case Event.RENDER: - case Event.ACTIVATE: - case Event.DEACTIVATE: - case "keyDown": - case "keyUp": - { - const player = $currentPlayer(); - - if (player - && player.broadcastEvents.size - && player.broadcastEvents.has(type) - ) { - - const events: EventListenerImpl[] = player.broadcastEvents.get(type) || $getArray(); - - for (let idx: number = 0; idx < events.length; idx++) { - if (events[idx].target === this) { - return true; - } - } - } - return false; - } - - default: - return !!(this._$events - && this._$events.size - && this._$events.has(type)); - - } + return eventDispatcherHasEventListenerService(this, type); } /** @@ -568,147 +94,28 @@ export class EventDispatcher * Removes a listener from the EventDispatcher object. * * @param {string} type - * @param {function} [listener = null] + * @param {function} listener * @param {boolean} [use_capture = false] * @return {void} * @method * @public */ removeEventListener ( - type: string, listener: Function | null, + type: string, + listener: Function, use_capture: boolean = false ): void { - - if (!listener) { - return; - } - - type = `${type}`; - if (!this.hasEventListener(type)) { - return; - } - - const player: Player = $currentPlayer(); - - let events: EventListenerImpl[] | null = null; - - let isBroadcast: boolean = false; - - switch (type) { - - case Event.ENTER_FRAME: - case Event.EXIT_FRAME: - case Event.FRAME_CONSTRUCTED: - case Event.RENDER: - case Event.ACTIVATE: - case Event.DEACTIVATE: - case "keyDown": - case "keyUp": - - isBroadcast = true; - - if (player) { - events = player.broadcastEvents.get(type) || $getArray(); - } - - break; - - default: - if (this._$events - && this._$events.size - && this._$events.has(type) - ) { - events = this._$events.get(type) || $getArray(); - } - - break; - - } - - if (!events) { - return ; - } - - // remove listener - for (let idx: number = 0; idx < events.length; ++idx) { - - // event object - const obj: EventListenerImpl = events[idx]; - if (use_capture === obj.useCapture - && obj.listener === listener - ) { - events.splice(idx, 1); - break; - } - - } - - if (!events.length) { - - if (isBroadcast) { - - player.broadcastEvents.delete(type); - - } else { - - if (!this._$events) { - return ; - } - - this._$events.delete(type); - - if (!this._$events.size) { - $poolMap(this._$events); - this._$events = null; - } - - } - - return ; - } - - if (events.length > 1) { - - // event sort(DESC) - events.sort(function (a: EventListenerImpl, b: EventListenerImpl) - { - switch (true) { - - case a.priority > b.priority: - return -1; - - case a.priority < b.priority: - return 1; - - default: - return 0; - - } - }); - - } - - if (isBroadcast) { - - player.broadcastEvents.set(type, events); - - } else { - - if (!this._$events) { - this._$events = $getMap(); - } - - this._$events.set(type, events); - - } + eventDispatcherRemoveEventListenerService( + this, type, listener, use_capture + ); } /** * @description EventDispatcherオブジェクトから指定したタイプのリスナーを全て削除します。 * Removes all listeners of the specified type from the EventDispatcher object. * - * @param {string} type - * @param {boolean} [use_capture=false] + * @param {string} type + * @param {boolean} [use_capture=false] * @return {void} * @method * @public @@ -717,122 +124,9 @@ export class EventDispatcher type: string, use_capture: boolean = false ): void { - - type = `${type}`; - if (!this.hasEventListener(type)) { - return; - } - - const player = $currentPlayer(); - - let events: EventListenerImpl[] | null = null; - - let isBroadcast: boolean = false; - - switch (type) { - - case Event.ENTER_FRAME: - case Event.EXIT_FRAME: - case Event.FRAME_CONSTRUCTED: - case Event.RENDER: - case Event.ACTIVATE: - case Event.DEACTIVATE: - case "keyDown": - case "keyUp": - - isBroadcast = true; - - if (player) { - events = player.broadcastEvents.get(type) || $getArray(); - } - - break; - - default: - if (this._$events - && this._$events.size - && this._$events.has(type) - ) { - events = this._$events.get(type) || $getArray(); - } - - break; - - } - - if (!events) { - return ; - } - - // remove listener - const results: EventListenerImpl[] = $getArray(); - - for (let idx = 0; idx < events.length; ++idx) { - - // event object - const obj: EventListenerImpl = events[idx]; - if (use_capture !== obj.useCapture) { - results.push(obj); - } - - } - - if (!results.length) { - - if (isBroadcast) { - - player.broadcastEvents.delete(type); - - } else { - - if (!this._$events) { - return ; - } - - this._$events.delete(type); - - if (!this._$events.size) { - $poolMap(this._$events); - this._$events = null; - } - } - - return ; - } - - if (results.length > 1) { - - // event sort (DESC) - results.sort(function (a: EventListenerImpl, b: EventListenerImpl) - { - switch (true) { - - case a.priority > b.priority: - return -1; - - case a.priority < b.priority: - return 1; - - default: - return 0; - - } - }); - - } - - if (isBroadcast) { - - player.broadcastEvents.set(type, results); - - } else { - - if (!this._$events) { - this._$events = $getMap(); - } - - this._$events.set(type, results); - } + eventDispatcherRemoveAllEventListenerService( + this, type, use_capture + ); } /** @@ -843,30 +137,13 @@ export class EventDispatcher * with this EventDispatcher object or * any of its ancestors for the specified event type. * - * @param {string} type + * @param {string} type * @return {boolean} * @method * @public */ - willTrigger (type :string): boolean + willTrigger (type: string): boolean { - if (this.hasEventListener(type)) { - return true; - } - - if ("parent" in this) { - - let parent: DisplayObjectContainer | null = this.parent as NonNullable; - while (parent) { - - if (parent.hasEventListener(type)) { - return true; - } - - parent = parent.parent; - } - } - - return false; + return eventDispatcherWillTriggerService(this, type); } } \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.test.ts new file mode 100644 index 00000000..8206e1e5 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.test.ts @@ -0,0 +1,139 @@ +import { Event } from "../../Event"; +import { EventDispatcher } from "../../EventDispatcher"; +import { $broadcastEvents } from "../../EventUtil"; +import { describe, expect, it } from "vitest"; + +describe("EventDispatcher.js addEventListener test", () => +{ + // addEventListener + it("addEventListener test success case1", () => + { + const eventDispatcher = new EventDispatcher(); + + eventDispatcher.addEventListener("test", () => { return "OK" }); + eventDispatcher.addEventListener("test", () => { return "NG" }); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + expect(events[0].listener()).toBe("OK"); + expect(events[1].listener()).toBe("NG"); + }); + + it("addEventListener test success case2", () => + { + const eventDispatcher = new EventDispatcher(); + + eventDispatcher.addEventListener("test", () => { return "NG" }, false, 50); + eventDispatcher.addEventListener("test", () => { return "OK" }, false, 100); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + expect(events[0].listener()).toBe("OK"); + expect(events[1].listener()).toBe("NG"); + }); + + it("addEventListener test success case3", () => + { + const eventDispatcher = new EventDispatcher(); + + eventDispatcher.addEventListener("123", () => { return "NG" }, false, 50); + eventDispatcher.addEventListener("123", () => { return "OK" }, false, 100); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("123"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + expect(events[0].listener()).toBe("OK"); + expect(events[1].listener()).toBe("NG"); + }); + + it("addEventListener test duplicate case1", () => + { + const eventDispatcher = new EventDispatcher(); + + const a = () => { return "OK" }; + + eventDispatcher.addEventListener("test", a); + eventDispatcher.addEventListener("test", a); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(1); + expect(events[0].listener()).toBe("OK"); + }); + + it("addEventListener test duplicate case2", () => + { + const eventDispatcher = new EventDispatcher(); + + let name = ""; + eventDispatcher.addEventListener("test", () => { name = "ok" }); + eventDispatcher.addEventListener("test", () => { name = "ng" }); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + eventDispatcher.dispatchEvent(new Event("test")); + expect(name).toBe("ng"); + }); + + it("addEventListener test duplicate case3", () => + { + const eventDispatcher1 = new EventDispatcher(); + const eventDispatcher2 = new EventDispatcher(); + + let result = ""; + + $broadcastEvents.clear(); + expect($broadcastEvents.size).toBe(0); + eventDispatcher1.addEventListener(Event.ENTER_FRAME, () => { result += "A" }); + eventDispatcher2.addEventListener(Event.ENTER_FRAME, () => { result += "B" }); + + expect($broadcastEvents.size).toBe(1); + expect($broadcastEvents.get(Event.ENTER_FRAME)?.length).toBe(2); + + eventDispatcher1.dispatchEvent(new Event(Event.ENTER_FRAME)); + expect(result).toBe("AB"); + + result = ""; + expect(result).toBe(""); + eventDispatcher2.dispatchEvent(new Event(Event.ENTER_FRAME)); + expect(result).toBe("AB"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.ts new file mode 100644 index 00000000..9b171c09 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherAddEventListenerService.ts @@ -0,0 +1,124 @@ +import type { IEventListener } from "../../interface/IEventListener"; +import type { EventDispatcher } from "../../EventDispatcher"; +import { Event } from "../../Event"; +import { KeyboardEvent } from "../../KeyboardEvent"; +import { + $broadcastEvents, + $getArray +} from "../../EventUtil"; + +/** + * @description 指定イベントに関数を登録、既に登録されている場合は上書。 + * Register a function in the specified event, or overwrite if already registered. + * + * @param {EventDispatcher} scope + * @param {string} type + * @param {Function} listener + * @param {boolean} [use_capture = false] + * @param {number} [priority = 0] + * @method + * @protected + */ +export const execute = ( + scope: D, + type: string, + listener: Function, + use_capture: boolean = false, + priority: number = 0 +): void => { + + let listenerObjects: IEventListener[]; + switch (type) { + + // broadcast event + case Event.ENTER_FRAME: + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + + if (!$broadcastEvents.size + || !$broadcastEvents.has(type) + ) { + $broadcastEvents.set(type, $getArray()); + } + + listenerObjects = $broadcastEvents.get(type) as NonNullable; + + break; + + // normal event + default: + + if (!scope._$events) { + scope._$events = new Map(); + } + + if (!scope._$events.size || !scope._$events.has(type)) { + scope._$events.set(type, $getArray()); + } + + listenerObjects = scope._$events.get(type) as NonNullable; + + break; + + } + + // duplicate check + const length = listenerObjects.length; + let index = 0; + for ( ; index < length; ++index) { + + const object = listenerObjects[index]; + if (use_capture !== object.useCapture) { + continue; + } + + if (object.target !== scope) { + continue; + } + + if (object.listener !== listener) { + continue; + } + + break; + } + + // add or overwrite + if (length === index) { + // add + listenerObjects.push({ + "listener": listener, + "priority": priority, + "useCapture": use_capture, + "target": scope + }); + } else { + // overwrite + const object = listenerObjects[index]; + object.listener = listener; + object.priority = priority; + object.useCapture = use_capture; + object.target = scope; + } + + if (listenerObjects.length > 1) { + + // sort(DESC) + listenerObjects.sort((a, b): number => + { + switch (true) { + + case a.priority > b.priority: + return -1; + + case a.priority < b.priority: + return 1; + + default: + return 0; + + } + }); + + } +}; \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.test.ts new file mode 100644 index 00000000..52ca02f3 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.test.ts @@ -0,0 +1,317 @@ +import { Event } from "../../Event"; +import { EventDispatcher } from "../../EventDispatcher"; +import { EventPhase } from "../../EventPhase"; +import { describe, expect, it } from "vitest"; + +describe("EventDispatcher.js dispatchEvent test", () => +{ + it("dispatchEvent test case1", () => + { + const eventDispatcher = new EventDispatcher(); + + let result = ""; + const test1 = () => { result += "O" }; + const test2 = () => { result += "K" }; + const test3 = () => { result += "!" }; + + eventDispatcher.addEventListener("test", test1); + eventDispatcher.addEventListener("test", test2); + eventDispatcher.addEventListener("test", test3); + + eventDispatcher.dispatchEvent(new Event("test")); + + expect(result).toBe("OK!"); + }); + + it("dispatchEvent test case2", () => + { + const eventDispatcher = new EventDispatcher(); + + let result = ""; + const test1 = () => { result += "!" }; + const test2 = () => { result += "K" }; + const test3 = () => { result += "O" }; + + eventDispatcher.addEventListener("test", test2, false, 20); + eventDispatcher.addEventListener("test", test1, false, 10); + eventDispatcher.addEventListener("test", test3, false, 30); + + eventDispatcher.dispatchEvent(new Event("test")); + + expect(result).toBe("OK!"); + }); + + it("dispatchEvent test case3", () => + { + class Parent extends EventDispatcher {} + + const parent = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child = new Child(parent); + + let result = ""; + const test = () => { result = "capture" }; + + parent.addEventListener("test", test, true); + parent.dispatchEvent(new Event("test")); + expect(result).toBe(""); + + child.dispatchEvent(new Event("test")); + expect(result).toBe("capture"); + }); + + it("dispatchEvent test case4", () => + { + class Parent extends EventDispatcher {} + + const parent = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child = new Child(parent); + + let result = ""; + const test = () => { result = "parent" }; + + parent.addEventListener("test", test); + + child.dispatchEvent(new Event("test")); + expect(result).toBe(""); + + parent.dispatchEvent(new Event("test")); + expect(result).toBe("parent"); + }); + + it("dispatchEvent test case5", () => + { + class Parent extends EventDispatcher {} + + const parent = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child = new Child(parent); + + let result = ""; + const test1 = () => { result += "cap" }; + const test2 = () => { result += "ture" }; + + parent.addEventListener("test", test1, true); + child.addEventListener("test", test2); + + child.dispatchEvent(new Event("test")); + expect(result).toBe("capture"); + }); + + it("dispatchEvent test case6", () => + { + class Parent extends EventDispatcher {} + + const parent = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child = new Child(parent); + + let result = ""; + const test1 = () => { result += "cap" }; + const test2 = () => { result += "ture" }; + const test3 = () => { result += " and bubble" }; + + parent.addEventListener("test", test1, true); + parent.addEventListener("test", test3); + + child.addEventListener("test", test2); + child.dispatchEvent(new Event("test", true)); + + expect(result).toBe("capture and bubble"); + }); + + // stopImmediatePropagation + it("dispatchEvent stopImmediatePropagation test case1", () => + { + class Mock extends EventDispatcher + { + // eslint-disable-next-line no-use-before-define + private _$parent: Mock | null; + public name: string; + constructor (src: Mock | null = null) + { + super(); + + this._$parent = src; + + this.name = ""; + } + get parent (): Mock | null + { + return this._$parent; + } + + } + + const stage = new Mock(); + stage.name = "stage"; + + const sprite_a = new Mock(stage); + sprite_a.name = "A"; + + const sprite_b = new Mock(sprite_a); + sprite_b.name = "B"; + + const sprite_c = new Mock(sprite_b); + sprite_c.name = "C"; + + let result = ""; + const EventRemovedFunc = (event: Event): void => + { + result += event.currentTarget.name; + + // ターゲットフェーズに到達した + if (event.eventPhase === EventPhase.AT_TARGET) { + event.stopImmediatePropagation(); + } + }; + + // event + stage.addEventListener(Event.REMOVED, EventRemovedFunc, true); + stage.addEventListener(Event.REMOVED, EventRemovedFunc, false); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFunc, true); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFunc, false); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFunc, true); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFunc, false); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFunc, true); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFunc, false); + + sprite_c.dispatchEvent(new Event(Event.REMOVED, true)); + expect(result).toBe("stageABC"); + }); + + // stopPropagation + it("dispatchEvent stopImmediatePropagation test case2", () => + { + class Mock extends EventDispatcher + { + // eslint-disable-next-line no-use-before-define + private _$parent: Mock | null; + public name: string; + constructor (src: Mock | null = null) + { + super(); + + this._$parent = src; + + this.name = ""; + } + get parent (): Mock | null + { + return this._$parent; + } + + } + + const stage = new Mock(); + stage.name = "stage1"; + + const sprite_a = new Mock(stage); + sprite_a.name = "A"; + + const sprite_b = new Mock(sprite_a); + sprite_b.name = "B"; + + const sprite_c = new Mock(sprite_b); + sprite_c.name = "C"; + + let resultCaseA = ""; + const EventRemovedFuncA = (e: any) => + { + + resultCaseA += e.currentTarget.name; + + // ターゲットフェーズに到達した + if (e.eventPhase === EventPhase.AT_TARGET) { + // イベント通知の伝達を終了する + e.stopPropagation(); + } + }; + + let resultCaseB = ""; + const EventRemovedFuncB = (e: any) => + { + resultCaseB += e.currentTarget.name; + }; + + // A + stage.addEventListener(Event.REMOVED, EventRemovedFuncA, true); + stage.addEventListener(Event.REMOVED, EventRemovedFuncA, false); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncA, true); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncA, false); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncA, true); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncA, false); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncA, true); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncA, false); + + // B + stage.addEventListener(Event.REMOVED, EventRemovedFuncB, true); + stage.addEventListener(Event.REMOVED, EventRemovedFuncB, false); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncB, true); + sprite_a.addEventListener(Event.REMOVED, EventRemovedFuncB, false); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncB, true); + sprite_b.addEventListener(Event.REMOVED, EventRemovedFuncB, false); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncB, true); + sprite_c.addEventListener(Event.REMOVED, EventRemovedFuncB, false); + + // execute + sprite_c.dispatchEvent(new Event(Event.REMOVED, true)); + + expect(resultCaseA).toBe("stage1ABC"); + expect(resultCaseB).toBe("stage1ABC"); + + }); +}); \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.ts new file mode 100644 index 00000000..3b198a12 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherDispatchEventService.ts @@ -0,0 +1,274 @@ +import type { IEventListener } from "../../interface/IEventListener"; +import type { EventDispatcher } from "../../EventDispatcher"; +import { Event } from "../../Event"; +import { KeyboardEvent } from "../../KeyboardEvent"; +import { EventPhase } from "../../EventPhase"; +import { + $broadcastEvents, + $getArray, + $poolArray +} from "../../EventUtil"; + +/** + * @description 指定のイベントを実行します。 + * Executes the specified event. + * + * @param {EventDispatcher} scope + * @param {Event} event + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + scope: D, + event: E +): boolean => { + + switch (event.type) { + + case Event.ENTER_FRAME: + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + { + if (!$broadcastEvents.size + || !$broadcastEvents.has(event.type) + ) { + return false; + } + + const listenerObjects = $broadcastEvents.get(event.type) as NonNullable; + if (!listenerObjects.length) { + return false; + } + + for (let idx = 0; idx < listenerObjects.length; ++idx) { + + const object = listenerObjects[idx]; + + // start target + event.eventPhase = EventPhase.AT_TARGET; + + // event execute + event.target = event.currentTarget = object.target; + + try { + + event.listener = object.listener; + object.listener(event); + + } catch (e) { + + console.error(e); + + return false; + + } + } + } + return true; + + default: + { + event.target = scope; + + let currentEvents: IEventListener[] | null = null; + if (scope._$events + && scope._$events.size + && scope._$events.has(event.type) + ) { + const events = scope._$events.get(event.type); + if (events) { + currentEvents = events.slice(0); + } + } + + // parent + const parentEvents = $getArray(); + if ("parent" in scope) { + + let parent = scope.parent as D | null; + while (parent) { + + if (parent.hasEventListener(event.type)) { + + const events: IEventListener[] | null = parent._$events && parent._$events.has(event.type) + ? parent._$events.get(event.type) as NonNullable + : null; + + if (events) { + parentEvents.push(events); + } + } + + if (!("parent" in parent)) { + break; + } + + parent = parent.parent as D | null; + + } + + } + + if (!currentEvents && !parentEvents.length) { + if (currentEvents) { + $poolArray(currentEvents); + } + $poolArray(parentEvents); + return false; + } + + // stage => child... end + if (parentEvents.length) { + + // start capture + event.eventPhase = EventPhase.CAPTURING_PHASE; + + switch (true) { + + case event._$stopImmediatePropagation: + case event._$stopPropagation: + break; + + default: + for (let idx = parentEvents.length - 1; idx > -1; --idx) { + + const events: IEventListener[] = parentEvents[idx]; + for (let idx: number = 0; idx < events.length; ++idx) { + + const object: IEventListener = events[idx]; + if (!object.useCapture) { + continue; + } + + // event execute + event.currentTarget = object.target; + + try { + + event.listener = object.listener; + object.listener.call(null, event); + + } catch (e) { + + console.error(e); + + return false; + } + + if (event._$stopImmediatePropagation) { + break; + } + + } + + if (event._$stopImmediatePropagation) { + break; + } + + } + break; + } + + } + + if (currentEvents + && !event._$stopImmediatePropagation + && !event._$stopPropagation + ) { + + // start target + event.eventPhase = EventPhase.AT_TARGET; + + for (let idx: number = 0; idx < currentEvents.length; ++idx) { + + const object = currentEvents[idx]; + if (object.useCapture) { + continue; + } + + // event execute + event.currentTarget = object.target; + try { + + event.listener = object.listener; + object.listener.call(null, event); + + } catch (e) { + + console.error(e); + + return false; + } + + if (event._$stopImmediatePropagation) { + break; + } + + } + + $poolArray(currentEvents); + } + + if (parentEvents.length) { + + // start bubbling + event.eventPhase = EventPhase.BUBBLING_PHASE; + + switch (true) { + + case event._$stopImmediatePropagation: + case event._$stopPropagation: + case !event.bubbles: + break; + + default: + + // this => parent... => stage end + for (let idx: number = 0; idx < parentEvents.length; ++idx) { + + const events: IEventListener[] = parentEvents[idx]; + for (let idx: number = 0; idx < events.length; ++idx) { + + const object = events[idx]; + if (object.useCapture) { + continue; + } + + // event execute + event.currentTarget = object.target; + + try { + + event.listener = object.listener; + object.listener.call(null, event); + + } catch (e) { + + console.error(e); + + return false; + } + + if (event._$stopImmediatePropagation) { + break; + } + } + + if (event._$stopImmediatePropagation) { + break; + } + + } + + break; + + } + + $poolArray(parentEvents); + } + } + return true; + + } +}; \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.test.ts new file mode 100644 index 00000000..f73e07cd --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.test.ts @@ -0,0 +1,35 @@ +import { Event } from "../../Event"; +import { EventDispatcher } from "../../EventDispatcher"; +import { describe, expect, it } from "vitest"; + +describe("EventDispatcher.js hasEventListener test", () => +{ + it("hasEventListener test case1", () => + { + const eventDispatcher = new EventDispatcher(); + + eventDispatcher.addEventListener("test1", () => { return "OK" }); + eventDispatcher.addEventListener("test3", () => { return "NG" }); + + expect(eventDispatcher.hasEventListener("test1")).toBe(true); + expect(eventDispatcher.hasEventListener("test2")).toBe(false); + expect(eventDispatcher.hasEventListener("test3")).toBe(true); + expect(eventDispatcher.hasEventListener("test4")).toBe(false); + + }); + + // メソッドが所属する EventDispatcher インスタンスについてのみリスナーが登録されているか + it("hasEventListener test case2", () => + { + const eventDispatcher1 = new EventDispatcher(); + const eventDispatcher2 = new EventDispatcher(); + const eventDispatcher3 = new EventDispatcher(); + + eventDispatcher2.addEventListener(Event.ENTER_FRAME, () => { return "OK" }); + + expect(eventDispatcher1.hasEventListener(Event.ENTER_FRAME)).toBe(true); + expect(eventDispatcher2.hasEventListener(Event.ENTER_FRAME)).toBe(true); + expect(eventDispatcher3.hasEventListener(Event.ENTER_FRAME)).toBe(true); + + }); +}); \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.ts new file mode 100644 index 00000000..61fef763 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherHasEventListenerService.ts @@ -0,0 +1,34 @@ +import type { EventDispatcher } from "../../EventDispatcher"; +import { KeyboardEvent } from "../../KeyboardEvent"; +import { Event } from "../../Event"; +import { $broadcastEvents } from "../../EventUtil"; + +/** + * @description 指定イベントが登録されているかを返却。 + * Returns whether the specified event is registered. + * + * @param {EventDispatcher} scope + * @param {string} type + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + scope: D, + type: string +): boolean => { + + switch (type) { + + case Event.ENTER_FRAME: + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + return $broadcastEvents.size && $broadcastEvents.has(type) ? true : false; + + default: + return !!(scope._$events + && scope._$events.size + && scope._$events.has(type)); + + } +}; \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.test.ts new file mode 100644 index 00000000..c2c72e32 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.test.ts @@ -0,0 +1,28 @@ +import { Event } from "../../Event"; +import { EventDispatcher } from "../../EventDispatcher"; +import { describe, expect, it, vi } from "vitest"; + +describe("EventDispatcher.js removeAllEventListener test", () => +{ + it("removeAllEventListener test case1", () => + { + const eventDispatcher = new EventDispatcher(); + eventDispatcher.addEventListener("test", vi.fn()); + eventDispatcher.addEventListener("test", vi.fn()); + eventDispatcher.addEventListener("test", vi.fn()); + + expect(eventDispatcher.hasEventListener("test")).toBe(true); + eventDispatcher.removeAllEventListener("test"); + expect(eventDispatcher.hasEventListener("test")).toBe(false); + }); + + it("removeAllEventListener test case2", () => + { + const eventDispatcher = new EventDispatcher(); + eventDispatcher.addEventListener(Event.ENTER_FRAME, vi.fn()); + + expect(eventDispatcher.hasEventListener(Event.ENTER_FRAME)).toBe(true); + eventDispatcher.removeAllEventListener(Event.ENTER_FRAME); + expect(eventDispatcher.hasEventListener(Event.ENTER_FRAME)).toBe(false); + }); +}); diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.ts new file mode 100644 index 00000000..32adb5a1 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveAllEventListenerService.ts @@ -0,0 +1,118 @@ +import type { IEventListener } from "../../interface/IEventListener"; +import type { EventDispatcher } from "../../EventDispatcher"; +import { KeyboardEvent } from "../../KeyboardEvent"; +import { Event } from "../../Event"; +import { + $broadcastEvents, + $getArray, + $poolArray +} from "../../EventUtil"; + +/** + * @description イベントリスナーを全て削除。 + * Remove all event listeners. + * + * @param {EventDispatcher} scope + * @param {string} type + * @param {boolean} use_capture + * @return {void} + * @method + * @protected + */ +export const execute = ( + scope: D, + type: string, + use_capture: boolean = false +): void => { + + let listenerObjects: IEventListener[]; + switch (type) { + + case Event.ENTER_FRAME: + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + if (!$broadcastEvents.size + || !$broadcastEvents.has(type) + ) { + return ; + } + listenerObjects = $broadcastEvents.get(type) as NonNullable; + break; + + default: + if (!scope._$events + || !scope._$events.size + || !scope._$events.has(type) + ) { + return ; + } + listenerObjects = scope._$events.get(type) as NonNullable; + break; + + } + + if (!listenerObjects) { + return ; + } + + // remove listener + const results: IEventListener[] = $getArray(); + for (let idx = 0; idx < listenerObjects.length; ++idx) { + + // event object + const object = listenerObjects[idx]; + if (use_capture === object.useCapture) { + continue; + } + + results.push(object); + } + + if (!results.length) { + + if ($broadcastEvents.has(type)) { + $broadcastEvents.delete(type); + } + + if (scope._$events && scope._$events.has(type)) { + scope._$events.delete(type); + if (!scope._$events.size) { + scope._$events = null; + } + } + + $poolArray(results); + $poolArray(listenerObjects); + + return ; + } + + if (results.length > 1) { + + // event sort (DESC) + results.sort((a, b): number => + { + switch (true) { + + case a.priority > b.priority: + return -1; + + case a.priority < b.priority: + return 1; + + default: + return 0; + + } + }); + + } + + if ($broadcastEvents.has(type)) { + $broadcastEvents.set(type, results); + } else { + scope._$events?.set(type, results); + } + + $poolArray(listenerObjects); +}; \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.test.ts new file mode 100644 index 00000000..87c5e33e --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.test.ts @@ -0,0 +1,150 @@ +import { Event } from "../../Event"; +import { $broadcastEvents } from "../../EventUtil"; +import { EventDispatcher } from "../../EventDispatcher"; +import { IEventListener } from "../../interface/IEventListener"; +import { describe, expect, it } from "vitest"; + +describe("EventDispatcher.js removeEventListener test", () => +{ + it("removeEventListener test case1", () => + { + const eventDispatcher = new EventDispatcher(); + + const test1 = () => { return "OK1" }; + const test2 = () => { return "OK2" }; + const test3 = () => { return "OK3" }; + + eventDispatcher.addEventListener("test", test1, false, 10); + eventDispatcher.addEventListener("test", test2, false, 20); + eventDispatcher.addEventListener("test", test3, false, 30); + + eventDispatcher.removeEventListener("test", test2); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + expect(events[0].listener()).toBe("OK3"); + expect(events[1].listener()).toBe("OK1"); + }); + + it("removeEventListener test case2", () => + { + const eventDispatcher = new EventDispatcher(); + + const a = () => { return "ok" }; + const b = () => { return "no" }; + + eventDispatcher.addEventListener("test", a); + eventDispatcher.addEventListener("test", b); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + + eventDispatcher.removeEventListener("test", a); + + expect(eventDispatcher._$events.has("test")).toBe(true); + expect(events.length).toBe(1); + }); + + it("removeEventListener test case3", () => + { + const eventDispatcher = new EventDispatcher(); + + const a = () => { return "yes" }; + const b = () => { return "no" }; + + eventDispatcher.addEventListener("test", a, true); + eventDispatcher.addEventListener("test", b, false); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + + eventDispatcher.removeEventListener("test", b, true); + eventDispatcher.removeEventListener("test", a, true); + + expect(events.length).toBe(1); + expect(events[0].listener().toString()).toBe("no"); + }); + + it("removeEventListener test case4", () => + { + const eventDispatcher = new EventDispatcher(); + + const a = () => { return "ok" }; + const b = () => { return "no" }; + + eventDispatcher.addEventListener("test", a, true); + eventDispatcher.addEventListener("test", b, false); + + if (!eventDispatcher._$events) { + throw new Error("addEventListener test success case1"); + } + + const events = eventDispatcher._$events.get("test"); + if (!events) { + throw new Error("the events is none."); + } + + expect(events.length).toBe(2); + + eventDispatcher.removeEventListener("test", a, false); + eventDispatcher.removeEventListener("test", b, true); + + expect(events.length).toBe(2); + expect(events[0].listener().toString()).toBe("ok"); + expect(events[1].listener().toString()).toBe("no"); + }); + + it("removeEventListener test case5", () => + { + const eventDispatcher1 = new EventDispatcher(); + const eventDispatcher2 = new EventDispatcher(); + + const a = () => { return undefined }; + + $broadcastEvents.clear(); + expect($broadcastEvents.size).toBe(0); + expect($broadcastEvents.has(Event.ENTER_FRAME)).toBe(false); + + eventDispatcher1.addEventListener(Event.ENTER_FRAME, a); + eventDispatcher2.addEventListener(Event.ENTER_FRAME, a); + + expect($broadcastEvents.size).toBe(1); + expect($broadcastEvents.has(Event.ENTER_FRAME)).toBe(true); + + const events = $broadcastEvents.get(Event.ENTER_FRAME) as NonNullable; + expect(events.length).toBe(2); + + eventDispatcher1.removeEventListener(Event.ENTER_FRAME, a); + expect(events.length).toBe(1); + expect($broadcastEvents.has(Event.ENTER_FRAME)).toBe(true); + + eventDispatcher2.removeEventListener(Event.ENTER_FRAME, a); + expect(events.length).toBe(0); + expect($broadcastEvents.has(Event.ENTER_FRAME)).toBe(false); + }); + +}); \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.ts new file mode 100644 index 00000000..4efa4172 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherRemoveEventListenerService.ts @@ -0,0 +1,115 @@ +import type { IEventListener } from "../../interface/IEventListener"; +import type { EventDispatcher } from "../../EventDispatcher"; +import { Event } from "../../Event"; +import { KeyboardEvent } from "../../KeyboardEvent"; +import { + $broadcastEvents, + $poolArray +} from "../../EventUtil"; + +/** + * @description イベントリスナーを削除。 + * Remove the event listener. + * + * @param {EventDispatcher} scope + * @param {string} type + * @param {Function} listener + * @param {boolean} [use_capture = false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + scope: D, + type: string, + listener: Function, + use_capture: boolean = false +): void => { + + let listenerObjects: IEventListener[]; + switch (type) { + + case Event.ENTER_FRAME: + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + if (!$broadcastEvents.size + || !$broadcastEvents.has(type) + ) { + return ; + } + listenerObjects = $broadcastEvents.get(type) as NonNullable; + break; + + default: + if (!scope._$events + || !scope._$events.size + || !scope._$events.has(type) + ) { + return ; + } + listenerObjects = scope._$events.get(type) as NonNullable; + break; + + } + + if (!listenerObjects) { + return ; + } + + // remove listener + for (let idx = 0; idx < listenerObjects.length; ++idx) { + + // event object + const object = listenerObjects[idx]; + if (use_capture !== object.useCapture) { + continue ; + } + + if (object.listener !== listener) { + continue ; + } + + // delete if match + listenerObjects.splice(idx, 1); + + break; + } + + if (!listenerObjects.length) { + + if ($broadcastEvents.has(type)) { + $broadcastEvents.delete(type); + } + + if (scope._$events && scope._$events.has(type)) { + scope._$events.delete(type); + if (!scope._$events.size) { + scope._$events = null; + } + } + + $poolArray(listenerObjects); + return ; + } + + if (listenerObjects.length > 1) { + + // event sort(DESC) + listenerObjects.sort((a, b): number => + { + switch (true) { + + case a.priority > b.priority: + return -1; + + case a.priority < b.priority: + return 1; + + default: + return 0; + + } + }); + + } +}; \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.test.ts b/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.test.ts new file mode 100644 index 00000000..551943c8 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.test.ts @@ -0,0 +1,64 @@ +import { EventDispatcher } from "../../EventDispatcher"; +import { describe, expect, it, vi } from "vitest"; + +describe("EventDispatcher.js willTrigger test", () => +{ + it("willTrigger test success case1", () => + { + class Parent extends EventDispatcher {} + const parent2 = new Parent(); + + const parent1 = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child = new Child(parent1); + + parent1.addEventListener("test", vi.fn()); + + expect(parent2.willTrigger("test")).toBe(false); + expect(parent1.willTrigger("test")).toBe(true); + expect(child.willTrigger("test")).toBe(true); + }); + + it("willTrigger test success case2", () => + { + class Parent extends EventDispatcher {} + + const parent = new Parent(); + class Child extends EventDispatcher + { + private _$parent: Parent; + constructor (src: Parent) + { + super(); + + this._$parent = src; + } + get parent () + { + return this._$parent; + } + } + const child1 = new Child(parent); + const child2 = new Child(parent); + + parent.addEventListener("test", vi.fn()); + + expect(parent.willTrigger("test")).toBe(true); + expect(child1.willTrigger("test")).toBe(true); + expect(child2.willTrigger("test")).toBe(true); + + }); +}); \ No newline at end of file diff --git a/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.ts b/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.ts new file mode 100644 index 00000000..f899ae10 --- /dev/null +++ b/packages/events/src/EventDispatcher/service/EventDispatcherWillTriggerService.ts @@ -0,0 +1,40 @@ +import type { EventDispatcher } from "../../EventDispatcher"; + +/** + * @description 先祖も含めてイベントリスナーが登録されているかどうかを判定。 + * Determine whether an event listener is registered, including ancestors. + * + * @param {EventDispatcher} scope + * @param {string} type + * @return {boolean} + * @method + * @protected + */ +export const execute = ( + scope: D, + type: string +): boolean => { + + if (scope.hasEventListener(type)) { + return true; + } + + if ("parent" in scope) { + + let parent = scope.parent as D | null; + while (parent) { + + if (parent.hasEventListener(type)) { + return true; + } + + if (!("parent" in parent)) { + break; + } + + parent = parent.parent as D | null; + } + } + + return false; +}; \ No newline at end of file diff --git a/packages/events/src/EventPhase.test.ts b/packages/events/src/EventPhase.test.ts new file mode 100644 index 00000000..98d5336c --- /dev/null +++ b/packages/events/src/EventPhase.test.ts @@ -0,0 +1,20 @@ +import { EventPhase } from "./EventPhase"; +import { describe, expect, it } from "vitest"; + +describe("EventPhase.js property test", () => +{ + it("CAPTURING_PHASE test", () => + { + expect(EventPhase.CAPTURING_PHASE).toBe(1); + }); + + it("AT_TARGET test", () => + { + expect(EventPhase.AT_TARGET).toBe(2); + }); + + it("BUBBLING_PHASE test", () => + { + expect(EventPhase.BUBBLING_PHASE).toBe(3); + }); +}); \ No newline at end of file diff --git a/packages/events/src/EventPhase.ts b/packages/events/src/EventPhase.ts index a67b1c64..8e703c7c 100644 --- a/packages/events/src/EventPhase.ts +++ b/packages/events/src/EventPhase.ts @@ -1,69 +1,12 @@ /** - * EventPhase クラスは、Event クラスの eventPhase プロパティの値を提供します。 - * - * The EventPhase class provides values for the eventPhase property of the Event class. + * @description EventPhase クラスは、Event クラスの eventPhase プロパティの値を提供します。 + * The EventPhase class provides values for the eventPhase property of the Event class. * * @class * @memberOf next2d.events */ export class EventPhase { - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class EventPhase] - * @method - * @static - */ - static toString (): string - { - return "[class EventPhase]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.EventPhase - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.EventPhase"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object EventPhase] - * @method - * @public - */ - toString (): string - { - return "[object EventPhase]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.EventPhase - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.EventPhase"; - } - /** * @description ターゲット段階(イベントフローの 2 番目の段階)です。 * The target phase, which is the second phase of the event flow. diff --git a/packages/events/src/EventUtil.ts b/packages/events/src/EventUtil.ts new file mode 100644 index 00000000..9f64b66f --- /dev/null +++ b/packages/events/src/EventUtil.ts @@ -0,0 +1,76 @@ +import type { IEventListener } from "./interface/IEventListener"; + +/** + * @type {Map} + * @private + */ +export const $broadcastEvents: Map = new Map(); + +/** + * @type {array} + * @private + */ +const $array: any[] = []; + +/** + * @return {array} + * @method + * @private + */ +export const $getArray = (): any[] => +{ + return $array.length + ? $array.pop() + : []; +}; + +/** + * @return {void} + * @method + * @private + */ +export const $poolArray = (array: any[]): void => +{ + if (10 > $array.length) { + array.length = 0; + $array.push(array); + } +}; + +/** + * @private + */ +type IEvent = PointerEvent | KeyboardEvent | WheelEvent | Event | null; + +/** + * @type {IEvent} + * @private + */ +let $event: IEvent = null; + +/** + * @description アクティブなイベントオブジェクをセット + * Set the active event object + * + * @param {IEvent} event + * @return {void} + * @method + * @protected + */ +export const $setEvent = (event: IEvent): void => +{ + $event = event; +}; + +/** + * @description アクティブなイベントオブジェクを取得 + * Get the active event object + * + * @return {IEvent} + * @method + * @protected + */ +export const $getEvent = (): IEvent => +{ + return $event; +}; \ No newline at end of file diff --git a/packages/events/src/FocusEvent.test.ts b/packages/events/src/FocusEvent.test.ts new file mode 100644 index 00000000..21231dca --- /dev/null +++ b/packages/events/src/FocusEvent.test.ts @@ -0,0 +1,15 @@ +import { FocusEvent } from "./FocusEvent"; +import { describe, expect, it } from "vitest"; + +describe("FocusEvent.js property test", () => +{ + it("FOCUS_IN test", () => + { + expect(FocusEvent.FOCUS_IN).toBe("focusin"); + }); + + it("FOCUS_OUT test", () => + { + expect(FocusEvent.FOCUS_OUT).toBe("focusout"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/FocusEvent.ts b/packages/events/src/FocusEvent.ts index 85181b1b..5524038d 100644 --- a/packages/events/src/FocusEvent.ts +++ b/packages/events/src/FocusEvent.ts @@ -1,18 +1,14 @@ import { Event } from "./Event"; +import { $getEvent } from "./EventUtil"; /** - * FocusEvent オブジェクトは、ユーザーが表示リストの1つのオブジェクトから - * 別のオブジェクトにフォーカスを変更したときにオブジェクトによって送出されます。 - * 次の2種類のフォーカスイベントがあります。 + * @description FocusEvent オブジェクトは、ユーザーが表示リストの1つのオブジェクトから + * 別のオブジェクトにフォーカスを変更したときにオブジェクトによって送出されます。 + * 次の2種類のフォーカスイベントがあります。 * - * An object dispatches a FocusEvent object when the user changes - * the focus from one object in the display list to another. - * There are two types of focus events: - * - *

    - *
  • FocusEvent.FOCUS_IN
  • - *
  • FocusEvent.FOCUS_OUT
  • - *
+ * An object dispatches a FocusEvent object when the user changes + * the focus from one object in the display list to another. + * There are two types of focus events: * * @class * @memberOf next2d.events @@ -23,71 +19,42 @@ export class FocusEvent extends Event /** * @param {string} type * @param {boolean} [bubbles=true] - * @param {boolean} [cancelable=false] * * @constructor * @public */ - constructor (type: string, bubbles: boolean = true, cancelable: boolean = false) + constructor (type: string, bubbles: boolean = true) { - super(type, bubbles, cancelable); - } + super(type, bubbles); - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class FocusEvent] - * @method - * @static - */ - static toString (): string - { - return "[class FocusEvent]"; - } + return new Proxy(this, { + "get": (object: any, name: string) => + { + if (name in object) { + return object[name]; + } - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.FocusEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.FocusEvent"; - } + const event = $getEvent(); + if (!event) { + return undefined; + } - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString( - "FocusEvent", "type", "bubbles", "cancelable", "eventPhase" - ); - } + switch (event.type) { - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.FocusEvent - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.FocusEvent"; + case FocusEvent.FOCUS_IN: + case FocusEvent.FOCUS_OUT: + if (name in event) { + // @ts-ignore + return $event[name]; + } + return undefined; + + default: + return undefined; + + } + } + }); } /** @@ -95,13 +62,12 @@ export class FocusEvent extends Event * Defines the value of the type property of a focusIn event object. * * @return {string} - * @default focusIn * @const * @static */ static get FOCUS_IN (): string { - return "focusIn"; + return "focusin"; } /** @@ -109,12 +75,11 @@ export class FocusEvent extends Event * Defines the value of the type property of a focusOut event object. * * @return {string} - * @default focusOut * @const * @static */ static get FOCUS_OUT (): string { - return "focusOut"; + return "focusout"; } } \ No newline at end of file diff --git a/packages/events/src/HTTPStatusEvent.test.ts b/packages/events/src/HTTPStatusEvent.test.ts new file mode 100644 index 00000000..6ee29ea9 --- /dev/null +++ b/packages/events/src/HTTPStatusEvent.test.ts @@ -0,0 +1,10 @@ +import { HTTPStatusEvent } from "./HTTPStatusEvent"; +import { describe, expect, it } from "vitest"; + +describe("HTTPStatusEvent.js property test", () => +{ + it("HTTP_STATUS test", () => + { + expect(HTTPStatusEvent.HTTP_STATUS).toBe("httpStatus"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/HTTPStatusEvent.ts b/packages/events/src/HTTPStatusEvent.ts index 7ffafdd2..3a8650ac 100644 --- a/packages/events/src/HTTPStatusEvent.ts +++ b/packages/events/src/HTTPStatusEvent.ts @@ -1,10 +1,9 @@ +import type { IURLRequestHeader } from "./interface/IURLRequestHeader"; import { Event } from "./Event"; -import type { URLRequestHeader } from "@next2d/net"; /** - * ネットワーク要求が HTTP ステータスコードを返すと、アプリケーションによって HTTPStatusEvent オブジェクトが送出されます。 - * - * The application dispatches HTTPStatusEvent objects when a network request returns an HTTP status code. + * @description ネットワーク要求が HTTP ステータスコードを返すと、アプリケーションによって HTTPStatusEvent オブジェクトが送出されます。 + * The application dispatches HTTPStatusEvent objects when a network request returns an HTTP status code. * * @class * @memberOf next2d.events @@ -12,14 +11,40 @@ import type { URLRequestHeader } from "@next2d/net"; */ export class HTTPStatusEvent extends Event { - private readonly _$status: number; - private readonly _$responseHeaders: URLRequestHeader[]; - private readonly _$responseURL: string; + /** + * @description サーバーから返された HTTP ステータスコードです。 + * The HTTP status code returned by the server. + * + * @return {number} + * @readonly + * @public + */ + public readonly status: number; + + /** + * @description 返された応答ヘッダー(URLRequestHeader オブジェクトの配列)です。 + * The response headers that the response returned, + * as an array of URLRequestHeader objects. + * + * @return {array} + * @readonly + * @public + */ + public readonly responseHeaders: IURLRequestHeader[]; + + /** + * @description 応答の返送元の URL です。 + * The URL that the response was returned from. + * + * @return {string} + * @readonly + * @public + */ + public readonly responseURL: string; /** * @param {string} type * @param {boolean} [bubbles=false] - * @param {boolean} [cancelable=false] * @param {number} [status=0] * @param {string} [response_url=""] * @param {array} [response_headers=[]] @@ -28,92 +53,35 @@ export class HTTPStatusEvent extends Event * @public */ constructor ( - type: string, bubbles: boolean = false, cancelable: boolean = false, - status: number = 0, response_url: string = "", - response_headers: URLRequestHeader[] = [] + type: string, + bubbles: boolean = false, + status: number = 0, + response_url: string = "", + response_headers: IURLRequestHeader[] = [] ) { - super(type, bubbles, cancelable); + super(type, bubbles); /** * @type {number} * @default 0 * @private */ - this._$status = status | 0; + this.status = status | 0; /** * @type {array} * @default {array} * @private */ - this._$responseHeaders = response_headers; + this.responseHeaders = response_headers; /** * @type {string} * @default "" * @private */ - this._$responseURL = response_url; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class HTTPStatusEvent] - * @method - * @static - */ - static toString (): string - { - return "[class HTTPStatusEvent]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.HTTPStatusEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.HTTPStatusEvent"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString( - "HTTPStatusEvent", - "type", "bubbles", "cancelable", - "eventPhase", "status", "responseURL" - ); - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.HTTPStatusEvent - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.HTTPStatusEvent"; + this.responseURL = response_url; } /** @@ -123,7 +91,7 @@ export class HTTPStatusEvent extends Event * of the type property of a httpStatus event object. * * @return {string} - * @default httpStatus + * @default "httpStatus" * @const * @static */ @@ -131,44 +99,4 @@ export class HTTPStatusEvent extends Event { return "httpStatus"; } - - /** - * @description 返された応答ヘッダー(URLRequestHeader オブジェクトの配列)です。 - * The response headers that the response returned, - * as an array of URLRequestHeader objects. - * - * @return {array} - * @readonly - * @public - */ - get responseHeaders (): URLRequestHeader[] - { - return this._$responseHeaders; - } - - /** - * @description 応答の返送元の URL です。 - * The URL that the response was returned from. - * - * @return {string} - * @readonly - * @public - */ - get responseURL (): string - { - return this._$responseURL; - } - - /** - * @description サーバーから返された HTTP ステータスコードです。 - * The HTTP status code returned by the server. - * - * @return {number} - * @readonly - * @public - */ - get status (): number - { - return this._$status; - } } \ No newline at end of file diff --git a/packages/events/src/IOErrorEvent.test.ts b/packages/events/src/IOErrorEvent.test.ts new file mode 100644 index 00000000..2b994402 --- /dev/null +++ b/packages/events/src/IOErrorEvent.test.ts @@ -0,0 +1,10 @@ +import { IOErrorEvent } from "./IOErrorEvent"; +import { describe, expect, it } from "vitest"; + +describe("IOErrorEvent.js property test", () => +{ + it("IO_ERROR test", () => + { + expect(IOErrorEvent.IO_ERROR).toBe("ioError"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/IOErrorEvent.ts b/packages/events/src/IOErrorEvent.ts index feb2f110..e298722d 100644 --- a/packages/events/src/IOErrorEvent.ts +++ b/packages/events/src/IOErrorEvent.ts @@ -1,9 +1,8 @@ import { Event } from "./Event"; /** - * IOErrorEvent オブジェクトは、エラーが発生して入力操作または出力操作が失敗したときに送出されます。 - * - * An IOErrorEvent object is dispatched when an error causes input or output operations to fail. + * @description IOErrorEvent オブジェクトは、エラーが発生して入力操作または出力操作が失敗したときに送出されます。 + * An IOErrorEvent object is dispatched when an error causes input or output operations to fail. * * @class * @memberOf next2d.events @@ -11,12 +10,20 @@ import { Event } from "./Event"; */ export class IOErrorEvent extends Event { - private readonly _$text: string; + /** + * @description エラーテキストです。 + * error text. + * + * @return {string} + * @default "" + * @readonly + * @public + */ + public readonly text: string; /** * @param {string} type * @param {boolean} [bubbles=true] - * @param {boolean} [cancelable=false] * @param {string} [text=""] * * @constructor @@ -25,77 +32,12 @@ export class IOErrorEvent extends Event constructor ( type: string, bubbles: boolean = false, - cancelable: boolean = false, text: string = "" ) { - super(type, bubbles, cancelable); + super(type, bubbles); - /** - * @type {string} - * @default "" - * @private - */ - this._$text = `${text}`; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class IOErrorEvent] - * @method - * @static - */ - static toString (): string - { - return "[class IOErrorEvent]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.IOErrorEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.IOErrorEvent"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString( - "IOErrorEvent", - "type", "bubbles", "cancelable", - "eventPhase", "text" - ); - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.IOErrorEvent - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.IOErrorEvent"; + this.text = `${text}`; } /** @@ -103,7 +45,6 @@ export class IOErrorEvent extends Event * Defines the value of the type property of an ioError event object. * * @return {string} - * @default ioError * @const * @static */ @@ -111,18 +52,4 @@ export class IOErrorEvent extends Event { return "ioError"; } - - /** - * @description エラーテキストです。 - * error text. - * - * @return {string} - * @default "" - * @readonly - * @public - */ - get text (): string - { - return this._$text; - } } \ No newline at end of file diff --git a/packages/events/src/JobEvent.test.ts b/packages/events/src/JobEvent.test.ts new file mode 100644 index 00000000..db03dcfd --- /dev/null +++ b/packages/events/src/JobEvent.test.ts @@ -0,0 +1,15 @@ +import { JobEvent } from "./JobEvent"; +import { describe, expect, it } from "vitest"; + +describe("JobEvent.js property test", () => +{ + it("UPDATE test", () => + { + expect(JobEvent.UPDATE).toBe("jobupdate"); + }); + + it("STOP test", () => + { + expect(JobEvent.STOP).toBe("jobstop"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/JobEvent.ts b/packages/events/src/JobEvent.ts new file mode 100644 index 00000000..e0a278fd --- /dev/null +++ b/packages/events/src/JobEvent.ts @@ -0,0 +1,38 @@ +import { Event } from "./Event"; + +/** + * @description Tween処理に関するイベントを示します。 + * Indicates events related to Tween processing. + * + * @class + * @memberOf next2d.events + * @extends Event + */ +export class JobEvent extends Event +{ + /** + * @description Jobのプロパティが更新されたときに発生します。 + * Occurs when the Job property is updated. + * + * @return {string} + * @const + * @static + */ + static get UPDATE (): string + { + return "jobupdate"; + } + + /** + * @description TweenのJobが停止したときに発生します。 + * Occurs when the Tween Job is stopped. + * + * @return {string} + * @const + * @static + */ + static get STOP (): string + { + return "jobstop"; + } +} \ No newline at end of file diff --git a/packages/events/src/KeyboardEvent.test.ts b/packages/events/src/KeyboardEvent.test.ts new file mode 100644 index 00000000..53fddf0d --- /dev/null +++ b/packages/events/src/KeyboardEvent.test.ts @@ -0,0 +1,15 @@ +import { KeyboardEvent } from "./KeyboardEvent"; +import { describe, expect, it } from "vitest"; + +describe("KeyboardEvent.js property test", () => +{ + it("KEY_DOWN test", () => + { + expect(KeyboardEvent.KEY_DOWN).toBe("keydown"); + }); + + it("KEY_UP test", () => + { + expect(KeyboardEvent.KEY_UP).toBe("keyup"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/KeyboardEvent.ts b/packages/events/src/KeyboardEvent.ts new file mode 100644 index 00000000..2dd345df --- /dev/null +++ b/packages/events/src/KeyboardEvent.ts @@ -0,0 +1,80 @@ +import { $getEvent } from "./EventUtil"; +import { Event } from "./Event"; + +/** + * @description キーボードによるユーザーの操作を示します。 + * Indicates user operation via keyboard. + * + * @class + * @memberOf next2d.events + * @extends Event + */ +export class KeyboardEvent extends Event +{ + /** + * @param {string} type + * @param {boolean} [bubbles=true] + * + * @constructor + * @public + */ + constructor (type: string, bubbles: boolean = true) + { + super(type, bubbles); + + return new Proxy(this, { + "get": (object: any, name: string) => + { + if (name in object) { + return object[name]; + } + + const event = $getEvent(); + if (!event) { + return undefined; + } + + switch (event.type) { + + case KeyboardEvent.KEY_DOWN: + case KeyboardEvent.KEY_UP: + if (name in event) { + // @ts-ignore + return $event[name]; + } + return undefined; + + default: + return undefined; + + } + } + }); + } + + /** + * @description キーボードのキーが押される度に発生します。 + * Occurs each time a key is pressed on the keyboard. + * + * @return {string} + * @const + * @static + */ + static get KEY_DOWN (): string + { + return "keydown"; + } + + /** + * @description キーボードのキーが離されたときに発生します。 + * Occurs when a key on the keyboard is released. + * + * @return {string} + * @const + * @static + */ + static get KEY_UP (): string + { + return "keyup"; + } +} \ No newline at end of file diff --git a/packages/events/src/MouseEvent.ts b/packages/events/src/MouseEvent.ts deleted file mode 100644 index 1da46719..00000000 --- a/packages/events/src/MouseEvent.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Event } from "./Event"; -import { $getEvent } from "@next2d/util"; - -/** - * MouseEvent オブジェクトは、マウスイベントが発生するたびにイベントフローに送出されます。 - * 通常、マウスイベントは、マウスやトラックボールなど、ポインターを使用したユーザー入力デバイスによって生成されます。 - * - * A MouseEvent object is dispatched into the event flow whenever mouse events occur. - * A mouse event is usually generated by a user input device, - * such as a mouse or a trackball, that uses a pointer. - * - * @class - * @memberOf next2d.events - * @extends Event - */ -export class MouseEvent extends Event -{ - /** - * @param {string} type - * @param {boolean} [bubbles=true] - * @param {boolean} [cancelable=false] - * - * @constructor - * @public - */ - constructor (type: string, bubbles: boolean = true, cancelable: boolean = false) - { - - super(type, bubbles, cancelable); - - return new Proxy(this, { - "get": (object: any, name: string) => - { - if (name in object) { - return object[name]; - } - - const $event = $getEvent(); - if ($event && name in $event) { - // @ts-ignore - return $event[name]; - } - - return undefined; - } - }); - - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class MouseEvent] - * @method - * @static - */ - static toString (): string - { - return "[class MouseEvent]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.MouseEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.MouseEvent"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString( - "MouseEvent", - "type", "bubbles", "cancelable", "eventPhase", - "localX", "localY", "stageX", "stageY", - "ctrlKey", "altKey", "shiftKey", "buttonDown", - "delta", "commandKey", "controlKey", "clickCount" - ); - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.MouseEvent - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.MouseEvent"; - } - - /** - * @description click イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a click event object. - * - * @return {string} - * @default click - * @const - * @static - */ - static get CLICK (): string - { - return "click"; - } - - /** - * @description dblclick イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a dblclick event object. - * - * @return {string} - * @default dblclick - * @const - * @static - */ - static get DOUBLE_CLICK (): string - { - return "dblclick"; - } - - /** - * @description mouseDown イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseDown event object. - * - * @return {string} - * @default mouseDown - * @const - * @static - */ - static get MOUSE_DOWN (): string - { - return "mouseDown"; - } - - /** - * @description mouseMove イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseMove event object. - * - * @return {string} - * @default mouseMove - * @const - * @static - */ - static get MOUSE_MOVE (): string - { - return "mouseMove"; - } - - /** - * @description mouseOut イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseOut event object. - * - * @return {string} - * @default mouseOut - * @const - * @static - */ - static get MOUSE_OUT (): string - { - return "mouseOut"; - } - - /** - * @description mouseOver イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseOver event object. - * - * @return {string} - * @default mouseOver - * @const - * @static - */ - static get MOUSE_OVER (): string - { - return "mouseOver"; - } - - /** - * @description mouseUp イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseUp event object. - * - * @return {string} - * @default mouseUp - * @const - * @static - */ - static get MOUSE_UP (): string - { - return "mouseUp"; - } - - /** - * @description mouseWheel イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a mouseWheel event object. - * - * @return {string} - * @default mouseWheel - * @const - * @static - */ - static get MOUSE_WHEEL (): string - { - return "mouseWheel"; - } - - /** - * @description rollOut イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a rollOut event object. - * - * @return {string} - * @default rollOut - * @const - * @static - */ - static get ROLL_OUT (): string - { - return "rollOut"; - } - - /** - * @description rollOver イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a rollOver event object. - * - * @return {string} - * @default rollOver - * @const - * @static - */ - static get ROLL_OVER (): string - { - return "rollOver"; - } -} \ No newline at end of file diff --git a/packages/events/src/PointerEvent.test.ts b/packages/events/src/PointerEvent.test.ts new file mode 100644 index 00000000..d3055f93 --- /dev/null +++ b/packages/events/src/PointerEvent.test.ts @@ -0,0 +1,35 @@ +import { PointerEvent } from "./PointerEvent"; +import { describe, expect, it } from "vitest"; + +describe("PointerEvent.js property test", () => +{ + it("DOUBLE_CLICK test", () => + { + expect(PointerEvent.DOUBLE_CLICK).toBe("dblclick"); + }); + + it("POINTER_DOWN test", () => + { + expect(PointerEvent.POINTER_DOWN).toBe("pointerdown"); + }); + + it("POINTER_MOVE test", () => + { + expect(PointerEvent.POINTER_MOVE).toBe("pointermove"); + }); + + it("POINTER_OUT test", () => + { + expect(PointerEvent.POINTER_OUT).toBe("pointerout"); + }); + + it("POINTER_OVER test", () => + { + expect(PointerEvent.POINTER_OVER).toBe("pointerover"); + }); + + it("POINTER_UP test", () => + { + expect(PointerEvent.POINTER_UP).toBe("pointerup"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/PointerEvent.ts b/packages/events/src/PointerEvent.ts new file mode 100644 index 00000000..bc33fa45 --- /dev/null +++ b/packages/events/src/PointerEvent.ts @@ -0,0 +1,152 @@ +import { Event } from "./Event"; +import { $getEvent } from "./EventUtil"; + +/** + * @description ポインターは、入力機器(マウス、ペン、またはタッチ可能な面の上の接触点など)のハードウェアにとらわれない表現です。 + * ポインターは、画面などの接触面上の特定の座標(または座標の集合)をターゲットにすることができます。 + * A pointer is a hardware-agnostic representation of an input device (such as a mouse, pen, or point of contact on a touchable surface). + * A pointer can target a specific coordinate (or set of coordinates) on a screen or other contact surface. + * + * @class + * @memberOf next2d.events + * @extends Event + */ +export class PointerEvent extends Event +{ + /** + * @param {string} type + * @param {boolean} [bubbles=true] + * + * @constructor + * @public + */ + constructor (type: string, bubbles: boolean = true) + { + super(type, bubbles); + + return new Proxy(this, { + "get": (object: any, name: string) => + { + if (name in object) { + return object[name]; + } + + const event = $getEvent(); + if (!event) { + return undefined; + } + + switch (event.type) { + + case PointerEvent.POINTER_DOWN: + case PointerEvent.POINTER_MOVE: + case PointerEvent.POINTER_UP: + case PointerEvent.POINTER_LEAVE: + case PointerEvent.POINTER_OVER: + case PointerEvent.POINTER_OUT: + if (name in event) { + // @ts-ignore + return $event[name]; + } + return undefined; + + default: + return undefined; + + } + + } + }); + } + + /** + * @description ボタンが連続で押された時に発生します。 + * Occurs when a button is pressed continuously. + * + * @return {string} + * @const + * @static + */ + static get DOUBLE_CLICK (): string + { + return "dblclick"; + } + + /** + * @description ボタンが押されていない状態から 1 つ以上のボタンが押されている状態に遷移したときに発生します + * Occurs when one or more buttons are pressed from the state where no buttons are pressed. + * + * @return {string} + * @const + * @static + */ + static get POINTER_DOWN (): string + { + return "pointerdown"; + } + + /** + * @description ポインティングデバイスが要素のヒットテスト領域を出た時に発生します + * Occurs when the pointing device leaves the hit test area of an element + * + * @return {string} + * @const + * @static + */ + static get POINTER_LEAVE (): string + { + return "pointerleave"; + } + + /** + * @description ポインターの座標が変化し、かつタッチ操作によってポインターがキャンセルされていないときに発生します。 + * Occurs when the pointer coordinates change and the pointer is not canceled by a touch operation. + * + * @return {string} + * @const + * @static + */ + static get POINTER_MOVE (): string + { + return "pointermove"; + } + + /** + * @description ヒットテスト境界を出たに発生します。ホバーに対応していない端末では発生しません。 + * Occurs when the hit test boundary is exited. Does not occur on devices that do not support hover. + * + * @return {string} + * @const + * @static + */ + static get POINTER_OUT (): string + { + return "pointerout"; + } + + /** + * @description インティングデバイスが要素のヒットテスト境界内に移動したときに発生します。 + * Occurs when the pointing device moves into the hit test boundary of an element. + * + * @return {string} + * @const + * @static + */ + static get POINTER_OVER (): string + { + return "pointerover"; + } + + /** + * @description ポインターがアクティブではなくなったときに発生します。 + * Occurs when the pointer is no longer active. + * + * @return {string} + * @const + * @static + */ + static get POINTER_UP (): string + { + return "pointerup"; + } +} \ No newline at end of file diff --git a/packages/events/src/ProgressEvent.test.ts b/packages/events/src/ProgressEvent.test.ts new file mode 100644 index 00000000..d9a74eb0 --- /dev/null +++ b/packages/events/src/ProgressEvent.test.ts @@ -0,0 +1,9 @@ +import { ProgressEvent } from "./ProgressEvent"; +import { describe, expect, it } from "vitest"; + +describe("ProgressEvent.js property test", () => +{ + it("PROGRESS test", () => { + expect(ProgressEvent.PROGRESS).toBe("progress"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/ProgressEvent.ts b/packages/events/src/ProgressEvent.ts index b038254c..7b892f89 100644 --- a/packages/events/src/ProgressEvent.ts +++ b/packages/events/src/ProgressEvent.ts @@ -1,11 +1,10 @@ import { Event } from "./Event"; /** - * ProgressEvent オブジェクトは、ロード処理が開始されたとき、またはソケットがデータを受信したときに送出されます。 - * これらのイベントは通常、JSON ファイル、イメージまたはデータがアプリケーションにロードされるときに生成されます。 - * - * A ProgressEvent object is dispatched when a load operation has begun or a socket has received data. - * These events are usually generated when JSON files, images or data are loaded into an application. + * @description ProgressEvent オブジェクトは、ロード処理が開始されたとき、またはソケットがデータを受信したときに送出されます。 + * これらのイベントは通常、JSON ファイル、イメージまたはデータがアプリケーションにロードされるときに生成されます。 + * A ProgressEvent object is dispatched when a load operation has begun or a socket has received data. + * These events are usually generated when JSON files, images or data are loaded into an application. * * @class * @memberOf next2d.events @@ -13,98 +12,49 @@ import { Event } from "./Event"; */ export class ProgressEvent extends Event { - private readonly _$bytesLoaded: number; - private readonly _$bytesTotal: number; - /** - * @param {string} type - * @param {boolean} [bubbles=true] - * @param {boolean} [cancelable=false] - * @param {number} [bytes_loaded=0] - * @param {number} [bytes_total=0] + * @description リスナーがイベントを処理しているときに読み込まれたアイテム数またはバイト数です。 + * The number of items or bytes loaded when the listener processes the event. * - * @constructor + * @return {number} + * @default 0 + * @readonly * @public */ - constructor ( - type: string, bubbles: boolean = false, cancelable: boolean = false, - bytes_loaded: number = 0, bytes_total: number = 0 - ) { - - super(type, bubbles, cancelable); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesLoaded = bytes_loaded | 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesTotal = bytes_total | 0; - } + public readonly bytesLoaded: number; /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class ProgressEvent] - * @method - * @static - */ - static toString (): string - { - return "[class ProgressEvent]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.ProgressEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.ProgressEvent"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description 読み込みプロセスが成功した場合に読み込まれるアイテムまたはバイトの総数です。 + * The total number of items or bytes that will be loaded + * if the loading process succeeds. * - * @return {string} - * @method + * @return {number} + * @default 0 + * @readonly * @public */ - toString (): string - { - return this.formatToString( - "ProgressEvent", - "type", "bubbles", "cancelable", - "eventPhase", "bytesLoaded", "bytesTotal" - ); - } + public readonly bytesTotal: number; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @param {string} type + * @param {boolean} [bubbles=true] + * @param {number} [bytes_loaded=0] + * @param {number} [bytes_total=0] * - * @member {string} - * @default next2d.events.ProgressEvent - * @const + * @constructor * @public */ - get namespace (): string - { - return "next2d.events.ProgressEvent"; + constructor ( + type: string, + bubbles: boolean = false, + bytes_loaded: number = 0, + bytes_total: number = 0 + ) { + + super(type, bubbles); + + this.bytesLoaded = bytes_loaded | 0; + this.bytesTotal = bytes_total | 0; } /** @@ -112,7 +62,6 @@ export class ProgressEvent extends Event * Defines the value of the type property of a progress event object. * * @return {string} - * @default progress * @const * @static */ @@ -120,33 +69,4 @@ export class ProgressEvent extends Event { return "progress"; } - - /** - * @description リスナーがイベントを処理しているときに読み込まれたアイテム数またはバイト数です。 - * The number of items or bytes loaded when the listener processes the event. - * - * @return {number} - * @default 0 - * @readonly - * @public - */ - get bytesLoaded (): number - { - return this._$bytesLoaded; - } - - /** - * @description 読み込みプロセスが成功した場合に読み込まれるアイテムまたはバイトの総数です。 - * The total number of items or bytes that will be loaded - * if the loading process succeeds. - * - * @return {number} - * @default 0 - * @readonly - * @public - */ - get bytesTotal (): number - { - return this._$bytesTotal; - } } \ No newline at end of file diff --git a/packages/events/src/VideoEvent.test.ts b/packages/events/src/VideoEvent.test.ts new file mode 100644 index 00000000..ef7705c8 --- /dev/null +++ b/packages/events/src/VideoEvent.test.ts @@ -0,0 +1,25 @@ +import { VideoEvent } from "./VideoEvent"; +import { describe, expect, it } from "vitest"; + +describe("VideoEvent.js property test", () => +{ + it("PLAY test", () => + { + expect(VideoEvent.PLAY).toBe("play"); + }); + + it("PLAYING test", () => + { + expect(VideoEvent.PLAYING).toBe("playing"); + }); + + it("PAUSE test", () => + { + expect(VideoEvent.PAUSE).toBe("pause"); + }); + + it("SEEK test", () => + { + expect(VideoEvent.SEEK).toBe("seek"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/VideoEvent.ts b/packages/events/src/VideoEvent.ts index 2dd4b6a7..bad30099 100644 --- a/packages/events/src/VideoEvent.ts +++ b/packages/events/src/VideoEvent.ts @@ -1,9 +1,8 @@ import { Event } from "./Event"; /** - * ビデオを再生または停止すると、VideoEvent オブジェクトを送出します。 - * - * When a video is played or stopped, it sends out a VideoEvent object. + * @description ビデオを再生または停止すると、VideoEvent オブジェクトを送出します。 + * When a video is played or stopped, it sends out a VideoEvent object. * * @class * @memberOf next2d.events @@ -11,120 +10,11 @@ import { Event } from "./Event"; */ export class VideoEvent extends Event { - private readonly _$bytesLoaded: number; - private readonly _$bytesTotal: number; - - /** - * @param {string} type - * @param {boolean} [bubbles=true] - * @param {boolean} [cancelable=false] - * @param {number} [bytes_loaded=0] - * @param {number} [bytes_total=0] - * - * @constructor - * @public - */ - constructor ( - type: string, bubbles: boolean = false, cancelable: boolean = false, - bytes_loaded: number = 0, bytes_total: number = 0 - ) { - - super(type, bubbles, cancelable); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesLoaded = bytes_loaded | 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesTotal = bytes_total | 0; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class VideoEvent] - * @method - * @static - */ - static toString (): string - { - return "[class VideoEvent]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.events.VideoEvent - * @const - * @static - */ - static get namespace (): string - { - return "next2d.events.VideoEvent"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return this.formatToString( - "VideoEvent", - "type", "bubbles", "cancelable", - "eventPhase", "bytesLoaded", "bytesTotal" - ); - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.events.VideoEvent - * @const - * @public - */ - get namespace (): string - { - return "next2d.events.VideoEvent"; - } - - /** - * @description progress イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a progress event object. - * - * @return {string} - * @default progress - * @const - * @static - */ - static get PROGRESS (): string - { - return "progress"; - } - /** * @description play イベントオブジェクトの type プロパティ値を定義します。 * Defines the value of the type property of a play event object. * * @return {string} - * @default play * @const * @static */ @@ -138,27 +28,12 @@ export class VideoEvent extends Event * Defines the value of the type property of a playStart event object. * * @return {string} - * @default playStart - * @const - * @static - */ - static get PLAY_START (): string - { - return "playStart"; - } - - /** - * @description playEnd イベントオブジェクトの type プロパティ値を定義します。 - * Defines the value of the type property of a playEnd event object. - * - * @return {string} - * @default playEnd * @const * @static */ - static get PLAY_END (): string + static get PLAYING (): string { - return "playEnd"; + return "playing"; } /** @@ -166,7 +41,6 @@ export class VideoEvent extends Event * Defines the value of the type property of a pause event object. * * @return {string} - * @default pause * @const * @static */ @@ -180,7 +54,6 @@ export class VideoEvent extends Event * Defines the value of the type property of a seek event object. * * @return {string} - * @default seek * @const * @static */ @@ -188,33 +61,4 @@ export class VideoEvent extends Event { return "seek"; } - - /** - * @description リスナーがイベントを処理しているときに読み込まれたアイテム数またはバイト数です。 - * The number of items or bytes loaded when the listener processes the event. - * - * @return {number} - * @default 0 - * @readonly - * @public - */ - get bytesLoaded (): number - { - return this._$bytesLoaded; - } - - /** - * @description 読み込みプロセスが成功した場合に読み込まれるアイテムまたはバイトの総数です。 - * The total number of items or bytes that will be loaded - * if the loading process succeeds. - * - * @return {number} - * @default 0 - * @readonly - * @public - */ - get bytesTotal (): number - { - return this._$bytesTotal; - } } \ No newline at end of file diff --git a/packages/events/src/WheelEvent.test.ts b/packages/events/src/WheelEvent.test.ts new file mode 100644 index 00000000..e5e38bcd --- /dev/null +++ b/packages/events/src/WheelEvent.test.ts @@ -0,0 +1,9 @@ +import { WheelEvent } from "./WheelEvent"; +import { describe, expect, it } from "vitest"; + +describe("WheelEvent.js property test", () => +{ + it("WHEEL test", () => { + expect(WheelEvent.WHEEL).toBe("wheel"); + }); +}); \ No newline at end of file diff --git a/packages/events/src/WheelEvent.ts b/packages/events/src/WheelEvent.ts new file mode 100644 index 00000000..79d015bc --- /dev/null +++ b/packages/events/src/WheelEvent.ts @@ -0,0 +1,58 @@ +import { Event } from "./Event"; +import { $getEvent } from "./EventUtil"; + +/** + * @description ホイールイベントは、マウスホイールの回転を表します。 + * A WheelEvent represents events that occur due to the user moving a mouse wheel or similar input device. + * + * @class + * @memberOf next2d.events + * @extends Event + */ +export class WheelEvent extends Event +{ + /** + * @param {string} type + * @param {boolean} [bubbles=true] + * + * @constructor + * @public + */ + constructor (type: string, bubbles: boolean = true) + { + super(type, bubbles); + + return new Proxy(this, { + "get": (object: any, name: string) => + { + if (name in object) { + return object[name]; + } + + const event = $getEvent(); + if (!event) { + return undefined; + } + + if (event.type === WheelEvent.WHEEL) { + // @ts-ignore + return name in event ? $event[name] : undefined; + } + return undefined; + } + }); + } + + /** + * @description ポインティングデバイス(通常はマウス)のホイールボタンを回転させたときに発生します。 + * Occurs when the wheel button of a pointing device (usually a mouse) is rotated. + * + * @return {string} + * @const + * @static + */ + static get WHEEL (): string + { + return "wheel"; + } +} \ No newline at end of file diff --git a/packages/events/src/index.ts b/packages/events/src/index.ts index e438d7a3..82934e2e 100644 --- a/packages/events/src/index.ts +++ b/packages/events/src/index.ts @@ -4,6 +4,10 @@ export * from "./EventPhase"; export * from "./FocusEvent"; export * from "./HTTPStatusEvent"; export * from "./IOErrorEvent"; -export * from "./MouseEvent"; +export * from "./PointerEvent"; export * from "./ProgressEvent"; -export * from "./VideoEvent"; \ No newline at end of file +export * from "./VideoEvent"; +export * from "./JobEvent"; +export * from "./KeyboardEvent"; +export * from "./WheelEvent"; +export { $setEvent } from "./EventUtil"; \ No newline at end of file diff --git a/packages/events/src/interface/IEvent.ts b/packages/events/src/interface/IEvent.ts new file mode 100644 index 00000000..bd06302c --- /dev/null +++ b/packages/events/src/interface/IEvent.ts @@ -0,0 +1,3 @@ +import type { Event } from "../Event"; + +export type IEvent = T; \ No newline at end of file diff --git a/packages/events/src/interface/IEventDispatcher.ts b/packages/events/src/interface/IEventDispatcher.ts new file mode 100644 index 00000000..a4402210 --- /dev/null +++ b/packages/events/src/interface/IEventDispatcher.ts @@ -0,0 +1,3 @@ +import type { EventDispatcher } from "../EventDispatcher"; + +export type IEventDispatcher = T; \ No newline at end of file diff --git a/packages/events/src/interface/IEventListener.ts b/packages/events/src/interface/IEventListener.ts new file mode 100644 index 00000000..b5a0c309 --- /dev/null +++ b/packages/events/src/interface/IEventListener.ts @@ -0,0 +1,8 @@ +import type { IEventDispatcher } from "./IEventDispatcher"; + +export interface IEventListener { + listener: Function; + priority: number; + useCapture: boolean; + target: IEventDispatcher; +} \ No newline at end of file diff --git a/packages/events/src/interface/IURLRequestHeader.ts b/packages/events/src/interface/IURLRequestHeader.ts new file mode 100644 index 00000000..f7901811 --- /dev/null +++ b/packages/events/src/interface/IURLRequestHeader.ts @@ -0,0 +1,5 @@ +export interface IURLRequestHeader +{ + name: string; + value: string; +} \ No newline at end of file diff --git a/packages/filters/package.json b/packages/filters/package.json index b04f1282..93f354d4 100644 --- a/packages/filters/package.json +++ b/packages/filters/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/filters", "version": "*", - "description": "Next2D Filters Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Filters Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -30,9 +22,5 @@ "repository": { "type": "git", "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/share": "file:../share", - "@next2d/webgl": "file:../webgl" } } diff --git a/packages/filters/src/BevelFilter.test.ts b/packages/filters/src/BevelFilter.test.ts new file mode 100644 index 00000000..613b359d --- /dev/null +++ b/packages/filters/src/BevelFilter.test.ts @@ -0,0 +1,567 @@ +import { BevelFilter } from "./BevelFilter"; +import { describe, expect, it } from "vitest"; + +describe("BevelFilter.js default property test", () => +{ + it("test case", () => + { + const bevelFilter = new BevelFilter(); + expect(bevelFilter.angle).toBe(45); + expect(bevelFilter.blurX).toBe(4); + expect(bevelFilter.blurY).toBe(4); + expect(bevelFilter.distance).toBe(4); + expect(bevelFilter.highlightAlpha).toBe(1); + expect(bevelFilter.highlightColor).toBe(0xffffff); + expect(bevelFilter.knockout).toBe(false); + expect(bevelFilter.quality).toBe(1); + expect(bevelFilter.shadowAlpha).toBe(1); + expect(bevelFilter.shadowColor).toBe(0x000000); + expect(bevelFilter.strength).toBe(1); + expect(bevelFilter.type).toBe("inner"); + }); +}); + +describe("BevelFilter.js knockout test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.knockout = null; + expect(bevelFilter.knockout).toBe(false); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.knockout = undefined; + expect(bevelFilter.knockout).toBe(false); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + expect(bevelFilter.knockout).toBe(false); + + bevelFilter.knockout = true; + expect(bevelFilter.$updated).toBe(true); + expect(bevelFilter.knockout).toBe(true); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.knockout = ""; + expect(bevelFilter.knockout).toBe(false); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.knockout = 0; + expect(bevelFilter.knockout).toBe(false); + }); + + it("test case6", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.knockout = 1; + expect(bevelFilter.knockout).toBe(true); + }); +}); + +describe("BevelFilter.js angle test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.angle = null; + expect(bevelFilter.angle).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.angle = undefined; + expect(bevelFilter.angle).toBe(45); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.angle = true; + expect(bevelFilter.angle).toBe(1); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.angle = ""; + expect(bevelFilter.angle).toBe(0); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.angle = "abc"; + expect(bevelFilter.angle).toBe(45); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.angle = 0; + expect(bevelFilter.angle).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js blurX test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.blurX = null; + expect(bevelFilter.blurX).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.blurX = undefined; + expect(bevelFilter.blurX).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.blurX = true; + expect(bevelFilter.blurX).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.blurX = ""; + expect(bevelFilter.blurX).toBe(0); + }); + + it("test case5", () => + { + let bf = new BevelFilter(); + // @ts-ignore + bf.blurX = "abc"; + expect(bf.blurX).toBe(0); + }); + + it("test case6", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.blurX = 0; + expect(bevelFilter.blurX).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js distance test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.distance = null; + expect(bevelFilter.distance).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.distance = undefined; + expect(bevelFilter.distance).toBe(4); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.distance = true; + expect(bevelFilter.distance).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.distance = ""; + expect(bevelFilter.distance).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.distance = 0; + expect(bevelFilter.distance).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js highlightAlpha test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightAlpha = null; + expect(bevelFilter.highlightAlpha).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightAlpha = undefined; + expect(bevelFilter.highlightAlpha).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightAlpha = true; + expect(bevelFilter.highlightAlpha).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightAlpha = ""; + expect(bevelFilter.highlightAlpha).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.highlightAlpha = 0; + expect(bevelFilter.highlightAlpha).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js highlightColor test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.highlightColor = 0; + expect(bevelFilter.highlightColor).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + bevelFilter.highlightColor = 0xffffffff; + expect(bevelFilter.highlightColor).toBe(0xffffff); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + bevelFilter.highlightColor = -1; + expect(bevelFilter.highlightColor).toBe(0); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightColor = null; + expect(bevelFilter.highlightColor).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightColor = undefined; + expect(bevelFilter.highlightColor).toBe(0xffffff); + }); + + it("test case6", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.highlightColor = true; + expect(bevelFilter.highlightColor).toBe(1); + }); +}); + +describe("BevelFilter.js quality test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.quality = null; + expect(bevelFilter.quality).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.quality = undefined; + expect(bevelFilter.quality).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.quality = true; + expect(bevelFilter.quality).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.quality = ""; + expect(bevelFilter.quality).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.quality = 0; + expect(bevelFilter.quality).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js shadowAlpha test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowAlpha = null; + expect(bevelFilter.shadowAlpha).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowAlpha = undefined; + expect(bevelFilter.shadowAlpha).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowAlpha = true; + expect(bevelFilter.shadowAlpha).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowAlpha = ""; + expect(bevelFilter.shadowAlpha).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.shadowAlpha = 0; + expect(bevelFilter.shadowAlpha).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js shadowColor test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowColor = null; + expect(bevelFilter.shadowColor).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowColor = undefined; + expect(bevelFilter.shadowColor).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowColor = true; + expect(bevelFilter.shadowColor).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.shadowColor = ""; + expect(bevelFilter.shadowColor).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.shadowColor = 100; + expect(bevelFilter.shadowColor).toBe(100); + expect(bevelFilter.$updated).toBe(true); + }); + + it("test case6", () => + { + const bevelFilter = new BevelFilter(); + bevelFilter.shadowColor = 0xffffffff; + expect(bevelFilter.shadowColor).toBe(0xffffff); + }); + + it("test case7", () => + { + const bevelFilter = new BevelFilter(); + bevelFilter.shadowColor = -1; + expect(bevelFilter.shadowColor).toBe(0); + }); +}); + +describe("BevelFilter.js strength test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.strength = null; + expect(bevelFilter.strength).toBe(0); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.strength = undefined; + expect(bevelFilter.strength).toBe(0); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.strength = true; + expect(bevelFilter.strength).toBe(1); + }); + + it("test case4", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.strength = ""; + expect(bevelFilter.strength).toBe(0); + }); + + it("test case5", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.strength = 0; + expect(bevelFilter.strength).toBe(0); + expect(bevelFilter.$updated).toBe(true); + }); +}); + +describe("BevelFilter.js type test", () => +{ + it("test case1", () => + { + const bevelFilter = new BevelFilter(); + + bevelFilter.$updated = false; + expect(bevelFilter.$updated).toBe(false); + + bevelFilter.type = "full"; + expect(bevelFilter.type).toBe("full"); + expect(bevelFilter.$updated).toBe(true); + }); + + it("test case2", () => + { + const bevelFilter = new BevelFilter(); + bevelFilter.type = "outer"; + expect(bevelFilter.type).toBe("outer"); + }); + + it("test case3", () => + { + const bevelFilter = new BevelFilter(); + // @ts-ignore + bevelFilter.type = "abc"; + expect(bevelFilter.type).toBe("inner"); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BevelFilter.ts b/packages/filters/src/BevelFilter.ts index 525e2a6e..66a4637c 100644 --- a/packages/filters/src/BevelFilter.ts +++ b/packages/filters/src/BevelFilter.ts @@ -1,37 +1,26 @@ +import type { IBitmapFilterType } from "./interface/IBitmapFilterType"; +import type { IFilterQuality } from "./interface/IFilterQuality"; import { BitmapFilter } from "./BitmapFilter"; -import { BlurFilter } from "./BlurFilter"; -import type { BitmapFilterTypeImpl } from "./interface/BitmapFilterTypeImpl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; +import { execute as bevelFilterGetBoundsService } from "./BevelFilter/usecase/BevelFilterGetBoundsUseCase"; +import { execute as bevelFilterCanApplyFilterService } from "./BevelFilter/service/BevelFilterCanApplyFilterService"; +import { execute as bevelFilterToArrayService } from "./BevelFilter/service/BevelFilterToArrayService"; +import { execute as bevelFilterToNumberArrayService } from "./BevelFilter/service/BevelFilterToNumberArrayService"; import { - $Math, $clamp, - $toColorInt, - $getArray, - $Deg2Rad, - $intToR, - $intToG, - $intToB, - $getBoundsObject, - $devicePixelRatio -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * BevelFilter クラスを使用すると、表示オブジェクトにベベル効果を追加できます。 - * ボタンなどのオブジェクトにベベル効果を適用すると 3 次元的に表現されます。 - * 異なるハイライトカラー、シャドウカラー、ベベルのぼかし量、ベベルの角度、ベベルの配置、 - * ノックアウト効果を使用して、ベベルの外観をカスタマイズできます。 + * @description BevelFilter クラスを使用すると、表示オブジェクトにベベル効果を追加できます。 + * ボタンなどのオブジェクトにベベル効果を適用すると 3 次元的に表現されます。 + * 異なるハイライトカラー、シャドウカラー、ベベルのぼかし量、ベベルの角度、ベベルの配置、 + * ノックアウト効果を使用して、ベベルの外観をカスタマイズできます。 * - * The BevelFilter class lets you add a bevel effect to display objects. - * A bevel effect gives objects such as buttons a three-dimensional look. - * You can customize the look of the bevel with different highlight and shadow colors, - * the amount of blur on the bevel, the angle of the bevel, the placement of the bevel, - * and a knockout effect. + * The BevelFilter class lets you add a bevel effect to display objects. + * A bevel effect gives objects such as buttons a three-dimensional look. + * You can customize the look of the bevel with different highlight and shadow colors, + * the amount of blur on the bevel, the angle of the bevel, the placement of the bevel, + * and a knockout effect. * * @class * @memberOf next2d.filters @@ -39,28 +28,112 @@ import { */ export class BevelFilter extends BitmapFilter { - private _$blurFilter: BlurFilter; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public readonly $filterType: number = 0; + + /** + * @description BevelFilterの認識番号 + * The recognition number of the BevelFilter. + * + * @member {number} + * @public + */ + private _$blurX: number; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurY: number; + + /** + * @type {IFilterQuality} + * @default 1 + * @private + */ + private _$quality: IFilterQuality; + + /** + * @type {number} + * @default 4 + * @private + */ private _$distance: number; + + /** + * @type {number} + * @default 45 + * @private + */ private _$angle: number; + + /** + * @type {number} + * @default 0xffffff + * @private + */ private _$highlightColor: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$highlightAlpha: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$shadowColor: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$shadowAlpha: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$strength: number; - private _$type: BitmapFilterTypeImpl; + + /** + * @type {string} + * @default "inner" + * @private + */ + private _$type: IBitmapFilterType; + + /** + * @type {boolean} + * @default false + * @private + */ private _$knockout: boolean; /** * @param {number} [distance=4] * @param {number} [angle=45] - * @param {uint} [highlight_color=0xffffff] + * @param {number} [highlight_color=0xffffff] * @param {number} [highlight_alpha=1] - * @param {uint} [shadow_color=0x000000] + * @param {number} [shadow_color=0x000000] * @param {number} [shadow_alpha=1] * @param {number} [blur_x=4] * @param {number} [blur_y=4] * @param {number} [strength=1] - * @param {int} [quality=1] + * @param {number} [quality=1] * @param {string} [type=BitmapFilterType.INNER] * @param {boolean} [knockout=false] * @@ -68,87 +141,40 @@ export class BevelFilter extends BitmapFilter * @public */ constructor ( - distance: number = 4, angle: number = 45, - highlight_color: number = 0xffffff, highlight_alpha: number = 1, - shadow_color: number = 0, shadow_alpha: number = 1, - blur_x: number = 4, blur_y: number = 4, - strength: number = 1, quality: FilterQualityImpl = 1, - type: BitmapFilterTypeImpl = "inner", knockout: boolean = false + distance: number = 4, + angle: number = 45, + highlight_color: number = 0xffffff, + highlight_alpha: number = 1, + shadow_color: number = 0, + shadow_alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: IFilterQuality = 1, + type: IBitmapFilterType = "inner", + knockout: boolean = false ) { super(); - /** - * @type {BlurFilter} - * @default BlurFilter - * @private - */ - this._$blurFilter = new BlurFilter(blur_x, blur_y, quality); - - /** - * @type {number} - * @default 4 - * @private - */ - this._$distance = 4; - - /** - * @type {number} - * @default 45 - * @private - */ - this._$angle = 45; - - /** - * @type {number} - * @default 0xffffff - * @private - */ + // default + this._$distance = 4; + this._$angle = 45; this._$highlightColor = 0xffffff; - - /** - * @type {number} - * @default 1 - * @private - */ this._$highlightAlpha = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$shadowColor = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$shadowAlpha = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$strength = 1; - - /** - * @type {string} - * @default "inner" - * @private - */ - this._$type = "inner"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$knockout = false; + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + this._$shadowColor = 0; + this._$shadowAlpha = 1; + this._$strength = 1; + this._$type = "inner"; + this._$knockout = false; // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; this.distance = distance; this.angle = angle; this.highlightColor = highlight_color; @@ -160,62 +186,6 @@ export class BevelFilter extends BitmapFilter this.knockout = knockout; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class BevelFilter] - * @method - * @static - */ - static toString (): string - { - return "[class BevelFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.BevelFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.BevelFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object BevelFilter] - * @method - * @public - */ - toString (): string - { - return "[object BevelFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.BevelFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.BevelFilter"; - } - /** * @description シャドウの角度 * The angle of the shadow. @@ -230,11 +200,12 @@ export class BevelFilter extends BitmapFilter } set angle (angle: number) { - angle %= 360; - if (angle !== this._$angle) { - this._$angle = $clamp(angle, -360, 360, 45); - this._$doChanged(); + angle = $clamp(angle % 360, -360, 360, 45); + if (angle === this._$angle) { + return ; } + this._$angle = angle; + this.$updated = true; } /** @@ -247,11 +218,16 @@ export class BevelFilter extends BitmapFilter */ get blurX (): number { - return this._$blurFilter.blurX; + return this._$blurX; } set blurX (blur_x: number) { - this._$blurFilter.blurX = blur_x; + blur_x = $clamp(+blur_x, 0, 255, 0); + if (blur_x === this._$blurX) { + return ; + } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -264,11 +240,16 @@ export class BevelFilter extends BitmapFilter */ get blurY (): number { - return this._$blurFilter.blurY; + return this._$blurY; } set blurY (blur_y: number) { - this._$blurFilter.blurY = blur_y; + blur_y = $clamp(+blur_y, 0, 255, 0); + if (blur_y === this._$blurY) { + return ; + } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -286,10 +267,11 @@ export class BevelFilter extends BitmapFilter set distance (distance: number) { distance = $clamp(+distance, -255, 255, 4); - if (distance !== this._$distance) { - this._$distance = distance; - this._$doChanged(); + if (distance === this._$distance) { + return ; } + this._$distance = distance; + this.$updated = true; } /** @@ -307,10 +289,11 @@ export class BevelFilter extends BitmapFilter set highlightAlpha (highlight_alpha: number) { highlight_alpha = $clamp(+highlight_alpha, 0, 1, 0); - if (highlight_alpha !== this._$highlightAlpha) { - this._$highlightAlpha = highlight_alpha; - this._$doChanged(); + if (highlight_alpha === this._$highlightAlpha) { + return ; } + this._$highlightAlpha = highlight_alpha; + this.$updated = true; } /** @@ -325,15 +308,19 @@ export class BevelFilter extends BitmapFilter { return this._$highlightColor; } - set highlightColor (highlight_color: number) + set highlightColor (highlight_color: number | string) { highlight_color = $clamp( - $toColorInt(highlight_color), 0, 0xffffff, 0xffffff + typeof highlight_color === "string" + ? $convertColorStringToNumber(highlight_color) + : highlight_color + , 0, 0xffffff, 0xffffff ); - if (highlight_color !== this._$highlightColor) { - this._$highlightColor = highlight_color; - this._$doChanged(); + if (highlight_color === this._$highlightColor) { + return ; } + this._$highlightColor = highlight_color; + this.$updated = true; } /** @@ -350,10 +337,12 @@ export class BevelFilter extends BitmapFilter } set knockout (knockout: boolean) { - if (knockout !== this._$knockout) { - this._$knockout = !!knockout; - this._$doChanged(); + knockout = !!knockout; + if (knockout === this._$knockout) { + return ; } + this._$knockout = knockout; + this.$updated = true; } /** @@ -364,13 +353,18 @@ export class BevelFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { - return this._$blurFilter.quality; + return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - this._$blurFilter.quality = quality; + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; + } + this._$quality = quality; + this.$updated = true; } /** @@ -387,11 +381,12 @@ export class BevelFilter extends BitmapFilter } set shadowAlpha (shadow_alpha: number) { - shadow_alpha = $clamp(+shadow_alpha, 0, 1, 0); - if (shadow_alpha !== this._$shadowAlpha) { - this._$shadowAlpha = shadow_alpha; - this._$doChanged(); + shadow_alpha = $clamp(shadow_alpha, 0, 1, 0); + if (shadow_alpha === this._$shadowAlpha) { + return ; } + this._$shadowAlpha = shadow_alpha; + this.$updated = true; } /** @@ -406,16 +401,20 @@ export class BevelFilter extends BitmapFilter { return this._$shadowColor; } - set shadowColor (shadow_color: number) + set shadowColor (shadow_color: number | string) { shadow_color = $clamp( - $toColorInt(shadow_color), 0, 0xffffff, 0 + typeof shadow_color === "string" + ? $convertColorStringToNumber(shadow_color) + : shadow_color + , 0, 0xffffff, 0 ); - if (shadow_color !== this._$shadowColor) { - this._$shadowColor = shadow_color; - this._$doChanged(); + if (shadow_color === this._$shadowColor) { + return ; } + this._$shadowColor = shadow_color; + this.$updated = true; } /** @@ -433,10 +432,11 @@ export class BevelFilter extends BitmapFilter set strength (strength: number) { strength = $clamp(strength | 0, 0, 255, 0); - if (strength !== this._$strength) { - this._$strength = strength; - this._$doChanged(); + if (strength === this._$strength) { + return ; } + this._$strength = strength; + this.$updated = true; } /** @@ -447,17 +447,30 @@ export class BevelFilter extends BitmapFilter * @default BitmapFilterType.INNER * @public */ - get type (): BitmapFilterTypeImpl + get type (): IBitmapFilterType { return this._$type; } - set type (type: BitmapFilterTypeImpl) + set type (type: IBitmapFilterType) { - type = `${type}`; - if (type !== this._$type) { - this._$type = type; - this._$doChanged(); + type = `${type}`.toLowerCase() as IBitmapFilterType; + if (type === this._$type) { + return ; + } + + switch (type) { + + case "inner": + case "outer": + case "full": + break; + + default: + return ; } + + this._$type = type; + this.$updated = true; } /** @@ -472,211 +485,61 @@ export class BevelFilter extends BitmapFilter { return new BevelFilter( this._$distance, this._$angle, this._$highlightColor, this._$highlightAlpha, - this._$shadowColor, this._$shadowAlpha, this._$blurFilter.blurX, this._$blurFilter.blurY, - this._$strength, this._$blurFilter.quality, this._$type, this._$knockout + this._$shadowColor, this._$shadowAlpha, this._$blurX, this._$blurY, + this._$strength, this._$quality, this._$type, this._$knockout ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(0, - this._$distance, this._$angle, this._$highlightColor, this._$highlightAlpha, - this._$shadowColor, this._$shadowAlpha, this._$blurFilter.blurX, this._$blurFilter.blurY, - this._$strength, this._$blurFilter.quality, this._$type, this._$knockout - ); + return bevelFilterToArrayService(this); } /** - * @return {boolean} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$isUpdated (): boolean + toNumberArray (): Float32Array { - return this._$updated || this._$blurFilter._$isUpdated(); - } - - /** - * @param {object} bounds - * @param {number} [x_scale=null] - * @param {number} [y_scale=null] - * @return {object} - * @method - * @private - */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, - y_scale: number = 0 - ): BoundsImpl { - - let clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$canApply()) { - return clone; - } - - clone = this - ._$blurFilter - ._$generateFilterRect(clone, x_scale, y_scale); - - const radian: number = this._$angle * $Deg2Rad; - - let x: number = $Math.abs($Math.cos(radian) * this._$distance); - let y: number = $Math.abs($Math.sin(radian) * this._$distance); - - if (x_scale) { - x *= x_scale; - } - - if (y_scale) { - y *= y_scale; - } - - clone.xMin = $Math.min(clone.xMin, x); - if (x > 0) { - clone.xMax += x; - } - clone.yMin = $Math.min(clone.yMin, y); - if (y > 0) { - clone.yMax += y; - } - - return clone; + return bevelFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$strength > 0 - && this._$distance !== 0 - && this._$blurFilter._$canApply(); + return bevelFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {WebGLTexture} - * @private + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} + * @method + * @public */ - _$applyFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): WebGLTexture { - - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - if (!currentAttachment) { - throw new Error("the current attachment is null."); - } - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - const baseTexture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - if (!this._$canApply()) { - return baseTexture; - } - - const baseWidth: number = currentAttachment.width; - const baseHeight: number = currentAttachment.height; - const baseOffsetX: number = context._$offsetX; - const baseOffsetY: number = context._$offsetY; - - // matrix to scale - let xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - let yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - // pointer - const radian: number = this._$angle * $Deg2Rad; - const x: number = $Math.cos(radian) * this._$distance * xScale; - const y: number = $Math.sin(radian) * this._$distance * yScale; - - // highlight buffer - const highlightTextureBaseAttachment: AttachmentImpl = manager - .createTextureAttachment(baseWidth, baseHeight); - - context._$bind(highlightTextureBaseAttachment); - - context.reset(); - context.drawImage(baseTexture, 0, 0, baseWidth, baseHeight); - - context.globalCompositeOperation = "erase"; - context.drawImage(baseTexture, x * 2, y * 2, baseWidth, baseHeight); - - const highlightTextureBase = this - ._$blurFilter - ._$applyFilter(context, matrix, false); - - const blurWidth = highlightTextureBase.width; - const blurHeight = highlightTextureBase.height; - const bevelWidth = $Math.ceil(blurWidth + $Math.abs(x) * 2); - const bevelHeight = $Math.ceil(blurHeight + $Math.abs(y) * 2); - - // bevel filter buffer - const isInner: boolean = this._$type === "inner"; - const width: number = isInner ? baseWidth : bevelWidth; - const height: number = isInner ? baseHeight : bevelHeight; - - const absX: number = $Math.abs(x); - const absY: number = $Math.abs(y); - const blurOffsetX: number = (blurWidth - baseWidth) / 2; - const blurOffsetY: number = (blurHeight - baseHeight) / 2; - - const baseTextureX: number = isInner ? 0 : absX + blurOffsetX; - const baseTextureY: number = isInner ? 0 : absY + blurOffsetY; - const blurTextureX: number = isInner ? -blurOffsetX - x : absX - x; - const blurTextureY: number = isInner ? -blurOffsetY - y : absY - y; - - context._$bind(currentAttachment); - - manager.releaseAttachment(highlightTextureBaseAttachment, true); - - context._$applyBitmapFilter( - highlightTextureBase, width, height, - baseWidth, baseHeight, baseTextureX, baseTextureY, - blurWidth, blurHeight, blurTextureX, blurTextureY, - false, this._$type, this._$knockout, - this._$strength, null, null, null, - $intToR(this._$highlightColor, this._$highlightAlpha, true), - $intToG(this._$highlightColor, this._$highlightAlpha, true), - $intToB(this._$highlightColor, this._$highlightAlpha, true), - this._$highlightAlpha, - $intToR(this._$shadowColor, this._$shadowAlpha, true), - $intToG(this._$shadowColor, this._$shadowAlpha, true), - $intToB(this._$shadowColor, this._$shadowAlpha, true), - this._$shadowAlpha - ); - - context._$offsetX = baseOffsetX + baseTextureX; - context._$offsetY = baseOffsetY + baseTextureY; - - manager.releaseTexture(highlightTextureBase); - - return manager.getTextureFromCurrentAttachment(); + getBounds (bounds: Float32Array): Float32Array + { + return bevelFilterGetBoundsService(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.test.ts b/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..da934301 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.test.ts @@ -0,0 +1,36 @@ +import { BevelFilter } from "../../BevelFilter"; +import { execute } from "./BevelFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("BevelFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const bevelFilter = new BevelFilter(); + expect(execute(bevelFilter)).toBe(true); + + bevelFilter.blurX = 0; + expect(execute(bevelFilter)).toBe(false); + + bevelFilter.blurX = 1; + bevelFilter.blurY = 0; + expect(execute(bevelFilter)).toBe(false); + + bevelFilter.blurY = 1; + bevelFilter.quality = 0; + expect(execute(bevelFilter)).toBe(false); + + bevelFilter.quality = 1; + expect(execute(bevelFilter)).toBe(true); + + bevelFilter.strength = 0; + expect(execute(bevelFilter)).toBe(false); + + bevelFilter.strength = 1; + bevelFilter.distance = 0; + expect(execute(bevelFilter)).toBe(false); + + bevelFilter.distance = 1; + expect(execute(bevelFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.ts b/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.ts new file mode 100644 index 00000000..5bcb96b5 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterCanApplyFilterService.ts @@ -0,0 +1,19 @@ +import type { BevelFilter } from "../../BevelFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {BevelFilter} bevel_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (bevel_filter: BevelFilter): boolean => +{ + return bevel_filter.strength > 0 + && bevel_filter.distance !== 0 + && bevel_filter.blurX > 0 + && bevel_filter.blurY > 0 + && bevel_filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.test.ts b/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.test.ts new file mode 100644 index 00000000..5998cb78 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.test.ts @@ -0,0 +1,25 @@ +import { BevelFilter } from "../../BevelFilter"; +import { execute } from "./BevelFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("BevelFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new BevelFilter()); + expect(array.length).toBe(13); + expect(array[0]).toBe(0); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(16777215); + expect(array[4]).toBe(1); + expect(array[5]).toBe(0); + expect(array[6]).toBe(1); + expect(array[7]).toBe(4); + expect(array[8]).toBe(4); + expect(array[9]).toBe(1); + expect(array[10]).toBe(1); + expect(array[11]).toBe("inner"); + expect(array[12]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.ts b/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.ts new file mode 100644 index 00000000..2627a8e6 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterToArrayService.ts @@ -0,0 +1,29 @@ +import type { BevelFilter } from "../../BevelFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {BevelFilter} bevel_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (bevel_filter: BevelFilter): Array => +{ + return [ + bevel_filter.$filterType, + bevel_filter.distance, + bevel_filter.angle, + bevel_filter.highlightColor, + bevel_filter.highlightAlpha, + bevel_filter.shadowColor, + bevel_filter.shadowAlpha, + bevel_filter.blurX, + bevel_filter.blurY, + bevel_filter.strength, + bevel_filter.quality, + bevel_filter.type, + bevel_filter.knockout + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.test.ts b/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..e2660cc6 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.test.ts @@ -0,0 +1,69 @@ +import { BevelFilter } from "../../BevelFilter"; +import { execute } from "./BevelFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("BevelFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new BevelFilter( + 4, 45, 0xffffff, 1, 0, 1, 4, 4, 1, 1, "inner" + )); + expect(array.length).toBe(13); + expect(array[0]).toBe(0); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(16777215); + expect(array[4]).toBe(1); + expect(array[5]).toBe(0); + expect(array[6]).toBe(1); + expect(array[7]).toBe(4); + expect(array[8]).toBe(4); + expect(array[9]).toBe(1); + expect(array[10]).toBe(1); + expect(array[11]).toBe(1); + expect(array[12]).toBe(0); + }); + + it("test case2", () => + { + const array = execute(new BevelFilter( + 4, 45, 0xffffff, 1, 0, 1, 4, 4, 1, 1, "outer" + )); + expect(array.length).toBe(13); + expect(array[0]).toBe(0); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(16777215); + expect(array[4]).toBe(1); + expect(array[5]).toBe(0); + expect(array[6]).toBe(1); + expect(array[7]).toBe(4); + expect(array[8]).toBe(4); + expect(array[9]).toBe(1); + expect(array[10]).toBe(1); + expect(array[11]).toBe(2); + expect(array[12]).toBe(0); + }); + + it("test case3", () => + { + const array = execute(new BevelFilter( + 4, 45, 0xffffff, 1, 0, 1, 4, 4, 1, 1, "full" + )); + expect(array.length).toBe(13); + expect(array[0]).toBe(0); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(16777215); + expect(array[4]).toBe(1); + expect(array[5]).toBe(0); + expect(array[6]).toBe(1); + expect(array[7]).toBe(4); + expect(array[8]).toBe(4); + expect(array[9]).toBe(1); + expect(array[10]).toBe(1); + expect(array[11]).toBe(0); + expect(array[12]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.ts b/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.ts new file mode 100644 index 00000000..9a598a23 --- /dev/null +++ b/packages/filters/src/BevelFilter/service/BevelFilterToNumberArrayService.ts @@ -0,0 +1,30 @@ +import type { BevelFilter } from "../../BevelFilter"; +import { $typeToNumber } from "../../FilterUtil"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {BevelFilter} bevel_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (bevel_filter: BevelFilter): Float32Array => +{ + return new Float32Array([ + bevel_filter.$filterType, + bevel_filter.distance, + bevel_filter.angle, + bevel_filter.highlightColor, + bevel_filter.highlightAlpha, + bevel_filter.shadowColor, + bevel_filter.shadowAlpha, + bevel_filter.blurX, + bevel_filter.blurY, + bevel_filter.strength, + bevel_filter.quality, + $typeToNumber(bevel_filter.type), + +bevel_filter.knockout + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.test.ts b/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..542df262 --- /dev/null +++ b/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.test.ts @@ -0,0 +1,39 @@ +import { BevelFilter } from "../../BevelFilter"; +import { execute } from "./BevelFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("BevelFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BevelFilter(), bounds); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(0); + expect(bounds[3]).toBe(0); + }); + + it("test case2", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BevelFilter(4, 45, 0xffffff, 1, 0, 1, 4, 4, 1, 1, "outer"), bounds); + + expect(bounds[0]).toBe(-4.828427314758301); + expect(bounds[1]).toBe(-4.828427314758301); + expect(bounds[2]).toBe(4.828427314758301); + expect(bounds[3]).toBe(4.828427314758301); + }); + + it("test case3", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BevelFilter(4, 45, 0xffffff, 1, 0, 1, 10, 10, 1, 1, "full"), bounds); + + expect(bounds[0]).toBe(-7.828427314758301); + expect(bounds[1]).toBe(-7.828427314758301); + expect(bounds[2]).toBe(7.828427314758301); + expect(bounds[3]).toBe(7.828427314758301); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.ts b/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..ed7b2d4a --- /dev/null +++ b/packages/filters/src/BevelFilter/usecase/BevelFilterGetBoundsUseCase.ts @@ -0,0 +1,37 @@ +import type { BevelFilter } from "../../BevelFilter"; +import { $Deg2Rad } from "../../FilterUtil"; +import { execute as bevelFilterCanApplyFilterService } from "../service/BevelFilterCanApplyFilterService"; +import { execute as blurFilterGetBoundsUseCase } from "../../BlurFilter/usecase/BlurFilterGetBoundsUseCase"; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {BevelFilter} bevel_filter + * @param {Float32Array} bounds + * @return {Float32Array} + */ +export const execute = (bevel_filter: BevelFilter, bounds: Float32Array): Float32Array => +{ + if (!bevelFilterCanApplyFilterService(bevel_filter)) { + return bounds; + } + + if (bevel_filter.type === "inner") { + return bounds; + } + + blurFilterGetBoundsUseCase(bevel_filter, bounds); + + const radian = bevel_filter.angle * $Deg2Rad; + const distance = bevel_filter.distance; + const x = Math.abs(Math.cos(radian) * distance); + const y = Math.abs(Math.sin(radian) * distance); + + bounds[0] -= x; + bounds[2] += x; + bounds[1] -= y; + bounds[3] += y; + + return bounds; +}; \ No newline at end of file diff --git a/packages/filters/src/BitmapFilter.ts b/packages/filters/src/BitmapFilter.ts index 21e64e7e..8a8d45c3 100644 --- a/packages/filters/src/BitmapFilter.ts +++ b/packages/filters/src/BitmapFilter.ts @@ -1,112 +1,63 @@ -import { $doUpdated } from "@next2d/share"; - /** - * BitmapFilter クラスは、すべてのイメージフィルター効果の基本クラスです。 - * BevelFilter、BlurFilter、ColorMatrixFilter、ConvolutionFilter、DisplacementMapFilter、DropShadowFilter、GlowFilter、GradientBevelFilter、 - * および GradientGlowFilter クラスはすべて、BitmapFilter クラスを継承します。 - * このフィルター効果は、あらゆる表示オブジェクトに適用できます。 + * @description BitmapFilter クラスは、すべてのイメージフィルター効果の基本クラスです。 + * BevelFilter、BlurFilter、ColorMatrixFilter、ConvolutionFilter、DisplacementMapFilter、DropShadowFilter、GlowFilter、GradientBevelFilter、 + * および GradientGlowFilter クラスはすべて、BitmapFilter クラスを継承します。 + * このフィルター効果は、あらゆる表示オブジェクトに適用できます。 * - * The BitmapFilter class is the base class for all image filter effects. - * The BevelFilter, BlurFilter, ColorMatrixFilter, ConvolutionFilter, DisplacementMapFilter, DropShadowFilter, GlowFilter, GradientBevelFilter, - * and GradientGlowFilter classes all extend the BitmapFilter class. - * You can apply these filter effects to any display object. - * You can neither directly instantiate nor extend BitmapFilter. + * The BitmapFilter class is the base class for all image filter effects. + * The BevelFilter, BlurFilter, ColorMatrixFilter, ConvolutionFilter, DisplacementMapFilter, DropShadowFilter, GlowFilter, GradientBevelFilter, + * and GradientGlowFilter classes all extend the BitmapFilter class. + * You can apply these filter effects to any display object. + * You can neither directly instantiate nor extend BitmapFilter. * * @class * @memberOf next2d.filters */ export class BitmapFilter { - protected _$updated: boolean; - - /** - * @constructor - * @public - */ - constructor() - { - /** - * @type {boolean} - * @default true - * @private - */ - this._$updated = true; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class BitmapFilter] - * @method - * @static - */ - static toString (): string - { - return "[class BitmapFilter]"; - } - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description フィルターが更新されたかどうかを示します。 + * Indicates whether the filter is updated. * - * @return {string} - * @default next2d.filters.BitmapFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.BitmapFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object BitmapFilter] - * @method + * @type {boolean} + * @default true * @public */ - toString (): string - { - return "[object BitmapFilter]"; - } + public $updated: boolean; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.BitmapFilter - * @const + * @constructor * @public */ - get namespace (): string + constructor () { - return "next2d.filters.BitmapFilter"; + this.$updated = true; } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$isUpdated (): boolean + canApplyFilter (): boolean { - return this._$updated; + return true; } /** - * @return {void} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$doChanged (): void + getBounds (bounds: Float32Array): Float32Array { - this._$updated = true; - $doUpdated(); + return bounds; } } diff --git a/packages/filters/src/BlurFilter.test.ts b/packages/filters/src/BlurFilter.test.ts new file mode 100644 index 00000000..8d563a45 --- /dev/null +++ b/packages/filters/src/BlurFilter.test.ts @@ -0,0 +1,198 @@ +import { BlurFilter } from "./BlurFilter"; +import { describe, expect, it } from "vitest"; + +describe("BlurFilter.js property test", () => +{ + it("default test success", () => + { + const blurFilter = new BlurFilter(); + expect(blurFilter.blurX).toBe(4); + expect(blurFilter.blurY).toBe(4); + expect(blurFilter.quality).toBe(1); + }); +}); + +describe("BlurFilter.js blurX test", () => +{ + it("test case1", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurX = null; + expect(blurFilter.blurX).toBe(0); + }); + + it("test case2", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurX = undefined; + expect(blurFilter.blurX).toBe(0); + }); + + it("test case3", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurX = true; + expect(blurFilter.blurX).toBe(1); + }); + + it("test case4", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurX = ""; + expect(blurFilter.blurX).toBe(0); + }); + + it("test case5", () => + { + const blurFilter = new BlurFilter(); + + blurFilter.$updated = false; + expect(blurFilter.$updated).toBe(false); + + blurFilter.blurX = 10; + expect(blurFilter.blurX).toBe(10); + expect(blurFilter.$updated).toBe(true); + }); + + it("test case6", () => + { + const blurFilter = new BlurFilter(); + blurFilter.blurX = 1000; + expect(blurFilter.blurX).toBe(255); + }); + + it("test case7", () => + { + const blurFilter = new BlurFilter(); + blurFilter.blurX = -500; + expect(blurFilter.blurX).toBe(0); + }); +}); + +describe("BlurFilter.js blurY test", () => +{ + it("test case1", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurY = null; + expect(blurFilter.blurY).toBe(0); + }); + + it("test case2", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurY = undefined; + expect(blurFilter.blurY).toBe(0); + }); + + it("test case3", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurY = true; + expect(blurFilter.blurY).toBe(1); + }); + + it("test case4", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.blurY = ""; + expect(blurFilter.blurY).toBe(0); + }); + + it("test case5", () => + { + const blurFilter = new BlurFilter(); + + blurFilter.$updated = false; + expect(blurFilter.$updated).toBe(false); + + blurFilter.blurY = 10; + expect(blurFilter.blurY).toBe(10); + expect(blurFilter.$updated).toBe(true); + }); + + it("test case6", () => + { + const blurFilter = new BlurFilter(); + blurFilter.blurY = 1000; + expect(blurFilter.blurY).toBe(255); + }); + + it("test case7", () => + { + const blurFilter = new BlurFilter(); + blurFilter.blurY = -500; + expect(blurFilter.blurY).toBe(0); + }); +}); + +describe("BlurFilter.js quality test", () => +{ + it("test case1", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = null; + expect(blurFilter.quality).toBe(0); + }); + + it("test case2", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = undefined; + expect(blurFilter.quality).toBe(0); + }); + + it("test case3", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = true; + expect(blurFilter.quality).toBe(1); + }); + + it("test case4", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = ""; + expect(blurFilter.quality).toBe(0); + }); + + it("test case5", () => + { + const blurFilter = new BlurFilter(); + + blurFilter.$updated = false; + expect(blurFilter.$updated).toBe(false); + + blurFilter.quality = 10; + expect(blurFilter.quality).toBe(10); + expect(blurFilter.$updated).toBe(true); + }); + + it("test case6", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = 1000; + expect(blurFilter.quality).toBe(15); + }); + + it("test case7", () => + { + const blurFilter = new BlurFilter(); + // @ts-ignore + blurFilter.quality = -500; + expect(blurFilter.quality).toBe(0); + }); +}); diff --git a/packages/filters/src/BlurFilter.ts b/packages/filters/src/BlurFilter.ts index 6235b6d4..b72c577d 100644 --- a/packages/filters/src/BlurFilter.ts +++ b/packages/filters/src/BlurFilter.ts @@ -1,33 +1,24 @@ +import type { IFilterQuality } from "./interface/IFilterQuality"; import { BitmapFilter } from "./BitmapFilter"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $Math, - $getArray, - $clamp, - $getBoundsObject, - $poolBoundsObject, - $devicePixelRatio -} from "@next2d/share"; +import { $clamp } from "./FilterUtil"; +import { execute as blurFilterToArrayService } from "./BlurFilter/service/BlurFilterToArrayService"; +import { execute as blurFilterGetBoundsUseCase } from "./BlurFilter/usecase/BlurFilterGetBoundsUseCase"; +import { execute as blurFilterCanApplyFilterService } from "./BlurFilter/service/BlurFilterCanApplyFilterService"; +import { execute as blurFilterToNumberArrayService } from "./BlurFilter/service/BlurFilterToNumberArrayService"; /** - * BlurFilter クラスを使用すると、表示オブジェクトにぼかし効果を適用できます。 - * ぼかし効果は、イメージの細部をぼかします。ソフトフォーカスがかかっているように見えるぼかしから、 - * 半透明ガラスを通してイメージを見るようにかすんで見えるガウスぼかしまで作成できます。 - * このフィルターの quality プロパティを低く設定すると、ソフトフォーカスがかかっているように見えるぼかしになります。 - * quality プロパティを高く設定すると、ガウスぼかしフィルターに似たものになります。 + * @description BlurFilter クラスを使用すると、表示オブジェクトにぼかし効果を適用できます。 + * ぼかし効果は、イメージの細部をぼかします。ソフトフォーカスがかかっているように見えるぼかしから、 + * 半透明ガラスを通してイメージを見るようにかすんで見えるガウスぼかしまで作成できます。 + * このフィルターの quality プロパティを低く設定すると、ソフトフォーカスがかかっているように見えるぼかしになります。 + * quality プロパティを高く設定すると、ガウスぼかしフィルターに似たものになります。 * - * The BlurFilter class lets you apply a blur visual effect to display objects. - * A blur effect softens the details of an image. - * You can produce blurs that range from a softly unfocused look to a Gaussian blur, - * a hazy appearance like viewing an image through semi-opaque glass. - * When the quality property of this filter is set to low, the result is a softly unfocused look. - * When the quality property is set to high, it approximates a Gaussian blur filter. + * The BlurFilter class lets you apply a blur visual effect to display objects. + * A blur effect softens the details of an image. + * You can produce blurs that range from a softly unfocused look to a Gaussian blur, + * a hazy appearance like viewing an image through semi-opaque glass. + * When the quality property of this filter is set to low, the result is a softly unfocused look. + * When the quality property is set to high, it approximates a Gaussian blur filter. * * @class * @memberOf next2d.filters @@ -35,112 +26,66 @@ import { */ export class BlurFilter extends BitmapFilter { - private _$blurX: number; - private _$blurY: number; - private _$quality: FilterQualityImpl; - /** - * @param {number} [blur_x=4] - * @param {number} [blur_y=4] - * @param {int} [quality=1] + * @description フィルター認識番号 + * Filter Recognition Number * - * @constructor + * @member {number} * @public */ - constructor (blur_x: number = 4, blur_y: number = 4, quality: FilterQualityImpl = 1) - { - super(); - - /** - * @type {number} - * @default 4 - * @private - */ - this._$blurX = 4; - - /** - * @type {number} - * @default 4 - * @private - */ - this._$blurY = 4; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$quality = 1; - - // setup - this.blurX = blur_x; - this.blurY = blur_y; - this.quality = quality; - } + public readonly $filterType: number = 1; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description 水平方向のぼかし量。 + * The amount of horizontal blur. * - * @return {string} - * @default [class BlurFilter] - * @method - * @static + * @member {number} + * @default 4 + * @private */ - static toString (): string - { - return "[class BlurFilter]"; - } + private _$blurX: number; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description 垂直方向のぼかし量。 + * The amount of vertical blur. * - * @return {string} - * @default next2d.filters.BlurFilter - * @const - * @static + * @member {number} + * @default 4 + * @private */ - static get namespace (): string - { - return "next2d.filters.BlurFilter"; - } + private _$blurY: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description ぼかしの実行回数です。 + * The number of times to perform the blur. * - * @return {string} - * @default [object BlurFilter] - * @method - * @public + * @member {number} + * @default 1 + * @private */ - toString (): string - { - return "[object BlurFilter]"; - } + private _$quality: IFilterQuality; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @param {number} [blur_x=4] + * @param {number} [blur_y=4] + * @param {number} [quality=1] * - * @return {string} - * @default next2d.filters.BlurFilter - * @const + * @constructor * @public */ - get namespace (): string + constructor (blur_x: number = 4, blur_y: number = 4, quality: IFilterQuality = 1) { - return "next2d.filters.BlurFilter"; - } + super(); - /** - * @return {array} - * @private - */ - static get STEP (): number[] - { - return [0.5, 1.05, 1.4, 1.55, 1.75, 1.9, 2, 2.15, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5]; + // default + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + + // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; } /** @@ -158,10 +103,11 @@ export class BlurFilter extends BitmapFilter set blurX (blur_x: number) { blur_x = $clamp(+blur_x, 0, 255, 0); - if (blur_x !== this._$blurX) { - this._$blurX = blur_x; - this._$doChanged(); + if (blur_x === this._$blurX) { + return ; } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -179,10 +125,11 @@ export class BlurFilter extends BitmapFilter set blurY (blur_y: number) { blur_y = $clamp(+blur_y, 0, 255, 0); - if (blur_y !== this._$blurY) { - this._$blurY = blur_y; - this._$doChanged(); + if (blur_y === this._$blurY) { + return ; } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -193,17 +140,18 @@ export class BlurFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - quality = $clamp(quality | 0, 0, 15, 1) as FilterQualityImpl; - if (quality !== this._$quality) { - this._$quality = quality; - this._$doChanged(); + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; } + this._$quality = quality; + this.$updated = true; } /** @@ -220,276 +168,55 @@ export class BlurFilter extends BitmapFilter } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): number[] { - return $getArray(1, - this._$blurX, this._$blurY, this._$quality - ); + return blurFilterToArrayService(this); } /** - * @param {object} bounds - * @param {number} [x_scale=0] - * @param {number} [y_scale=0] - * @return {object} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, y_scale: number = 0 - ): BoundsImpl { - - const clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$quality) { - return clone; - } - - const step = BlurFilter.STEP[this._$quality - 1]; - - let dx = 0 >= this._$blurX ? 1 : this._$blurX * step; - let dy = 0 >= this._$blurY ? 1 : this._$blurY * step; - - if (x_scale) { - dx *= x_scale; - } else { - dx = $Math.round(dx); - } - - if (y_scale) { - dy *= y_scale; - } else { - dy = $Math.round(dy); - } - - clone.xMin -= dx; - clone.xMax += dx * 2; - clone.yMin -= dy; - clone.yMax += dy * 2; - - return clone; + toNumberArray (): Float32Array + { + return blurFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$blurX !== 0 && this._$blurY !== 0; + return blurFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {boolean} [removed=true] - * @return {WebGLTexture} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array, - removed: boolean = true - ): WebGLTexture { - - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const baseTexture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - if (!this._$canApply()) { - - if (removed) { - return baseTexture; - } - - return manager - .createTextureFromCurrentAttachment(); - } - - // matrix to scale - let xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - let yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - // after size - const baseBounds = $getBoundsObject( - 0, baseTexture.width, - 0, baseTexture.height - ); - const filterBounds: BoundsImpl = this - ._$generateFilterRect(baseBounds, xScale, yScale); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil(filterBounds.xMax) | 0; - const height: number = $Math.ceil(filterBounds.yMax) | 0; - const offsetX: number = $Math.ceil($Math.abs(filterBounds.xMin) + $Math.abs(width - filterBounds.xMax) * 0.5); - const offsetY: number = $Math.ceil($Math.abs(filterBounds.yMin) + $Math.abs(height - filterBounds.yMax) * 0.5); - - // set offset xy - context._$offsetX = offsetX + context._$offsetX; - context._$offsetY = offsetY + context._$offsetY; - - const baseBlurX: number = this._$blurX * xScale; - const baseBlurY: number = this._$blurY * yScale; - - let bufferScaleX: number = 1; - let bufferScaleY: number = 1; - - if (baseBlurX > 128) { - bufferScaleX = 0.0625; - } else if (baseBlurX > 64) { - bufferScaleX = 0.125; - } else if (baseBlurX > 32) { - bufferScaleX = 0.25; - } else if (baseBlurX > 16) { - bufferScaleX = 0.5; - } - - if (baseBlurY > 128) { - bufferScaleY = 0.0625; - } else if (baseBlurY > 64) { - bufferScaleY = 0.125; - } else if (baseBlurY > 32) { - bufferScaleY = 0.25; - } else if (baseBlurY > 16) { - bufferScaleY = 0.5; - } - - const bufferBlurX: number = baseBlurX * bufferScaleX; - const bufferBlurY: number = baseBlurY * bufferScaleY; - - const bufferWidth: number = $Math.ceil(width * bufferScaleX); - const bufferHeight: number = $Math.ceil(height * bufferScaleY); - - const attachment0 = manager - .createTextureAttachment(bufferWidth, bufferHeight); - - const attachment1 = manager - .createTextureAttachment(bufferWidth, bufferHeight); - - const attachments: AttachmentImpl[] = [attachment0, attachment1]; - let attachmentIndex: number = 0; - - context._$bind(attachment0); - - // draw - context.reset(); - context.setTransform(bufferScaleX, 0, 0, bufferScaleY, 0, 0); - context.drawImage( - baseTexture, offsetX, offsetY, - baseTexture.width, baseTexture.height - ); - - // set alpha - context.blend.toOneZero(); - - // execute - let targetTexture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - for (let q: number = 0; q < this._$quality; ++q) { - - // draw blur x - if (this._$blurX > 0) { - - attachmentIndex = (attachmentIndex + 1) % 2; - - const xTexture: AttachmentImpl = attachments[attachmentIndex]; - context._$bind(xTexture); - - context - ._$applyBlurFilter( - targetTexture, true, bufferBlurX - ); - - targetTexture = manager.getTextureFromCurrentAttachment(); - } - - // draw blur y - if (this._$blurY > 0) { - - attachmentIndex = (attachmentIndex + 1) % 2; - - const yTexture: AttachmentImpl = attachments[attachmentIndex]; - context._$bind(yTexture); - - context - ._$applyBlurFilter( - targetTexture, false, bufferBlurY - ); - - targetTexture = manager.getTextureFromCurrentAttachment(); - } - } - - // reset alpha - context.blend.reset(); - - if (bufferScaleX !== 1 || bufferScaleY !== 1) { - - const resultAttachment: AttachmentImpl = manager - .createTextureAttachment(width, height); - - context._$bind(resultAttachment); - - context.reset(); - context.imageSmoothingEnabled = true; - context.setTransform(1 / bufferScaleX, 0, 0, 1 / bufferScaleY, 0, 0); - context.drawImage(targetTexture, 0, 0, bufferWidth, bufferHeight); - - targetTexture = manager.getTextureFromCurrentAttachment(); - - context.reset(); - context.setTransform(1, 0, 0, 1, 0, 0); - - manager - .releaseAttachment(attachments[0], true); - - manager - .releaseAttachment(attachments[1], true); - - if (removed) { - manager - .releaseAttachment(currentAttachment, true); - } else { - manager - .releaseAttachment(resultAttachment, false); - } - - } else { - - // 最終結果ではない方のAttachmentを解放する - manager - .releaseAttachment(attachments[(attachmentIndex + 1) % 2], true); - - if (removed) { - // 適用前のAttachmentを解放する - manager - .releaseAttachment(currentAttachment, true); - } else { - // 適用後のAttachmentを解放する - manager - .releaseAttachment(attachments[attachmentIndex], false); - } - } - - return targetTexture; + getBounds (bounds: Float32Array): Float32Array + { + return blurFilterGetBoundsUseCase(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.test.ts b/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..f3478874 --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.test.ts @@ -0,0 +1,26 @@ +import { BlurFilter } from "../../BlurFilter"; +import { execute } from "./BlurFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("BlurFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const blurFilter = new BlurFilter(); + expect(execute(blurFilter)).toBe(true); + + blurFilter.blurX = 0; + expect(execute(blurFilter)).toBe(false); + + blurFilter.blurX = 1; + blurFilter.blurY = 0; + expect(execute(blurFilter)).toBe(false); + + blurFilter.blurY = 1; + blurFilter.quality = 0; + expect(execute(blurFilter)).toBe(false); + + blurFilter.quality = 1; + expect(execute(blurFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.ts b/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.ts new file mode 100644 index 00000000..f0149f4f --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterCanApplyFilterService.ts @@ -0,0 +1,20 @@ +import type { BlurFilter } from "../../BlurFilter"; +import type { BevelFilter } from "../../BevelFilter"; +import type { DropShadowFilter } from "../../DropShadowFilter"; +import type { GlowFilter } from "../../GlowFilter"; +import type { GradientBevelFilter } from "../../GradientBevelFilter"; +import type { GradientGlowFilter } from "../../GradientGlowFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {BlurFilter} filter + * @return {boolean} + * @method + * @private + */ +export const execute = (filter: BlurFilter | BevelFilter | DropShadowFilter | GlowFilter | GradientBevelFilter | GradientGlowFilter): boolean => +{ + return filter.blurX > 0 && filter.blurY > 0 && filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.test.ts b/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.test.ts new file mode 100644 index 00000000..a34f4199 --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.test.ts @@ -0,0 +1,16 @@ +import { BlurFilter } from "../../BlurFilter"; +import { execute } from "./BlurFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("BlurFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new BlurFilter()); + expect(array.length).toBe(4); + expect(array[0]).toBe(1); + expect(array[1]).toBe(4); + expect(array[2]).toBe(4); + expect(array[3]).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.ts b/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.ts new file mode 100644 index 00000000..5de5507d --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterToArrayService.ts @@ -0,0 +1,20 @@ +import type { BlurFilter } from "../../BlurFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {BlurFilter} blur_filter + * @return {number[]} + * @method + * @protected + */ +export const execute = (blur_filter: BlurFilter): number[] => +{ + return [ + blur_filter.$filterType, + blur_filter.blurX, + blur_filter.blurY, + blur_filter.quality + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.test.ts b/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..eceede3b --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.test.ts @@ -0,0 +1,16 @@ +import { BlurFilter } from "../../BlurFilter"; +import { execute } from "./BlurFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("BlurFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new BlurFilter()); + expect(array.length).toBe(4); + expect(array[0]).toBe(1); + expect(array[1]).toBe(4); + expect(array[2]).toBe(4); + expect(array[3]).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.ts b/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.ts new file mode 100644 index 00000000..eda4363c --- /dev/null +++ b/packages/filters/src/BlurFilter/service/BlurFilterToNumberArrayService.ts @@ -0,0 +1,20 @@ +import type { BlurFilter } from "../../BlurFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {BlurFilter} blur_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (blur_filter: BlurFilter): Float32Array => +{ + return new Float32Array([ + blur_filter.$filterType, + blur_filter.blurX, + blur_filter.blurY, + blur_filter.quality + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.test.ts b/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..3ea0a598 --- /dev/null +++ b/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.test.ts @@ -0,0 +1,39 @@ +import { BlurFilter } from "../../BlurFilter"; +import { execute } from "./BlurFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("BlurFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BlurFilter(), bounds); + + expect(bounds[0]).toBe(-2); + expect(bounds[1]).toBe(-2); + expect(bounds[2]).toBe(2); + expect(bounds[3]).toBe(2); + }); + + it("test case2", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BlurFilter(30, 70, 5), bounds); + + expect(bounds[0]).toBe(-53); + expect(bounds[1]).toBe(-123); + expect(bounds[2]).toBe(53); + expect(bounds[3]).toBe(123); + }); + + it("test case3", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new BlurFilter(0, 70, 5), bounds); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(0); + expect(bounds[3]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.ts b/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..699a6b77 --- /dev/null +++ b/packages/filters/src/BlurFilter/usecase/BlurFilterGetBoundsUseCase.ts @@ -0,0 +1,41 @@ +import type { BlurFilter } from "../../BlurFilter"; +import type { BevelFilter } from "../../BevelFilter"; +import type { DropShadowFilter } from "../../DropShadowFilter"; +import type { GlowFilter } from "../../GlowFilter"; +import type { GradientBevelFilter } from "../../GradientBevelFilter"; +import type { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute as blurFilterCanApplyFilterService } from "../service/BlurFilterCanApplyFilterService"; + +/** + * @return {array} + * @private + */ +const $STEP: number[] = [0.5, 1.05, 1.4, 1.55, 1.75, 1.9, 2, 2.15, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5]; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {BlurFilter} filter + * @param {Float32Array} bounds + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (filter: BlurFilter | BevelFilter | DropShadowFilter | GlowFilter | GradientBevelFilter | GradientGlowFilter, bounds: Float32Array): Float32Array => +{ + if (!blurFilterCanApplyFilterService(filter)) { + return bounds; + } + + const step = $STEP[filter.quality - 1]; + const dx = Math.round(filter.blurX * step); + const dy = Math.round(filter.blurY * step); + + bounds[0] -= dx; + bounds[2] += dx; + bounds[1] -= dy; + bounds[3] += dy; + + return bounds; +}; \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter.test.ts b/packages/filters/src/ColorMatrixFilter.test.ts new file mode 100644 index 00000000..613de3c7 --- /dev/null +++ b/packages/filters/src/ColorMatrixFilter.test.ts @@ -0,0 +1,38 @@ +import { ColorMatrixFilter } from "./ColorMatrixFilter"; +import { describe, expect, it } from "vitest"; + +describe("ColorMatrixFilter.js property test", () => +{ + // default + it("default test success", () => + { + const matrix = [ + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0 + ]; + + const colorMatrixFilter = new ColorMatrixFilter(); + for (let i = 0; i < matrix.length; i++) { + expect(colorMatrixFilter.matrix[i]).toBe(matrix[i]); + } + }); + + it("matrix test case1", () => + { + const colorMatrixFilter = new ColorMatrixFilter(); + + colorMatrixFilter.$updated = false; + expect(colorMatrixFilter.$updated).toBe(false); + + colorMatrixFilter.matrix = [ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20 + ]; + expect(colorMatrixFilter.matrix.length).toBe(20); + expect(colorMatrixFilter.$updated).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter.ts b/packages/filters/src/ColorMatrixFilter.ts index 62bf38cd..ecac6c4f 100644 --- a/packages/filters/src/ColorMatrixFilter.ts +++ b/packages/filters/src/ColorMatrixFilter.ts @@ -1,28 +1,20 @@ import { BitmapFilter } from "./BitmapFilter"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $Array, - $getArray -} from "@next2d/share"; +import { execute as colorMatrixFilterToArrayService } from "./ColorMatrixFilter/service/ColorMatrixFilterToArrayService"; +import { execute as colorMatrixFilterToNumberArrayService } from "./ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService"; /** - * ColorMatrixFilter クラスを使用すると、表示オブジェクトにぼかし効果を適用できます。 - * ぼかし効果は、イメージの細部をぼかします。ソフトフォーカスがかかっているように見えるぼかしから、 - * 半透明ガラスを通してイメージを見るようにかすんで見えるガウスぼかしまで作成できます。 - * このフィルターの quality プロパティを低く設定すると、ソフトフォーカスがかかっているように見えるぼかしになります。 - * quality プロパティを高く設定すると、ガウスぼかしフィルターに似たものになります。 + * @description ColorMatrixFilter クラスを使用すると、表示オブジェクトにぼかし効果を適用できます。 + * ぼかし効果は、イメージの細部をぼかします。ソフトフォーカスがかかっているように見えるぼかしから、 + * 半透明ガラスを通してイメージを見るようにかすんで見えるガウスぼかしまで作成できます。 + * このフィルターの quality プロパティを低く設定すると、ソフトフォーカスがかかっているように見えるぼかしになります。 + * quality プロパティを高く設定すると、ガウスぼかしフィルターに似たものになります。 * - * The ColorMatrixFilter class lets you apply a blur visual effect to display objects. - * A blur effect softens the details of an image. - * You can produce blurs that range from a softly unfocused look to a Gaussian blur, - * a hazy appearance like viewing an image through semi-opaque glass. - * When the quality property of this filter is set to low, the result is a softly unfocused look. - * When the quality property is set to high, it approximates a Gaussian blur filter. + * The ColorMatrixFilter class lets you apply a blur visual effect to display objects. + * A blur effect softens the details of an image. + * You can produce blurs that range from a softly unfocused look to a Gaussian blur, + * a hazy appearance like viewing an image through semi-opaque glass. + * When the quality property of this filter is set to low, the result is a softly unfocused look. + * When the quality property is set to high, it approximates a Gaussian blur filter. * * @class * @memberOf next2d.filters @@ -30,6 +22,19 @@ import { */ export class ColorMatrixFilter extends BitmapFilter { + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public $filterType: number = 2; + + /** + * @type {array} + * @private + */ private _$matrix: number[]; /** @@ -42,11 +47,7 @@ export class ColorMatrixFilter extends BitmapFilter { super(); - /** - * @type {array} - * @default {array} - * @private - */ + // default this._$matrix = [ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, @@ -55,63 +56,9 @@ export class ColorMatrixFilter extends BitmapFilter ]; // setup - this.matrix = matrix; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class ColorMatrixFilter] - * @method - * @static - */ - static toString (): string - { - return "[class ColorMatrixFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.ColorMatrixFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.ColorMatrixFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object ColorMatrixFilter] - * @method - * @public - */ - toString (): string - { - return "[object ColorMatrixFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.ColorMatrixFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.ColorMatrixFilter"; + if (matrix && matrix.length === 20) { + this.matrix = matrix; + } } /** @@ -126,22 +73,29 @@ export class ColorMatrixFilter extends BitmapFilter { return this._$matrix; } - set matrix (matrix: number[] | null) + set matrix (matrix: number[]) { - if (!matrix || !$Array.isArray(matrix) || matrix.length !== 20) { + if (this._$matrix === matrix) { return ; } - for (let idx: number = 0; idx < 20; ++idx) { + if (!Array.isArray(matrix) || matrix.length !== 20) { + return ; + } + for (let idx = 0; idx < 20; idx++) { if (matrix[idx] === this._$matrix[idx]) { continue; } - this._$doChanged(); + this.$updated = true; break; } + if (!this.$updated) { + return ; + } + this._$matrix = matrix; } @@ -159,75 +113,28 @@ export class ColorMatrixFilter extends BitmapFilter } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] - { - return $getArray(2, - this._$matrix - ); - } - - /** - * @param {object} bounds - * @return {object} - * @method - * @private - */ - _$generateFilterRect (bounds: BoundsImpl): BoundsImpl + toArray (): Array { - return bounds; + return colorMatrixFilterToArrayService(this); } /** - * @return {boolean} - * @method - * @private - */ - _$canApply (): boolean - { - return true; - } - - /** - * @param {CanvasToWebGLContext} context - * @return {WebGLTexture} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter (context: CanvasToWebGLContext) + toNumberArray (): Float32Array { - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - const texture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - const width: number = texture.width; - const height: number = texture.height; - - // new buffer - const targetTextureAttachment: AttachmentImpl = manager - .createTextureAttachment(width, height); - - context._$bind(targetTextureAttachment); - - // apply - context.reset(); - context._$applyColorMatrixFilter(texture, this._$matrix); - - // reset - manager.releaseAttachment(currentAttachment, true); - - return manager.getTextureFromCurrentAttachment(); - + return colorMatrixFilterToNumberArrayService(this); } -} +} \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.test.ts b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.test.ts new file mode 100644 index 00000000..e1f91420 --- /dev/null +++ b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.test.ts @@ -0,0 +1,33 @@ +import { ColorMatrixFilter } from "../../ColorMatrixFilter"; +import { execute } from "./ColorMatrixFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("ColorMatrixFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new ColorMatrixFilter()); + expect(array.length).toBe(2); + expect(array[0]).toBe(2); + expect(array[1][0]).toBe(1); + expect(array[1][1]).toBe(0); + expect(array[1][2]).toBe(0); + expect(array[1][3]).toBe(0); + expect(array[1][4]).toBe(0); + expect(array[1][5]).toBe(0); + expect(array[1][6]).toBe(1); + expect(array[1][7]).toBe(0); + expect(array[1][8]).toBe(0); + expect(array[1][9]).toBe(0); + expect(array[1][10]).toBe(0); + expect(array[1][11]).toBe(0); + expect(array[1][12]).toBe(1); + expect(array[1][13]).toBe(0); + expect(array[1][14]).toBe(0); + expect(array[1][15]).toBe(0); + expect(array[1][16]).toBe(0); + expect(array[1][17]).toBe(0); + expect(array[1][18]).toBe(1); + expect(array[1][19]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.ts b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.ts new file mode 100644 index 00000000..80c66c7e --- /dev/null +++ b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToArrayService.ts @@ -0,0 +1,18 @@ +import type { ColorMatrixFilter } from "../../ColorMatrixFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {BevelFilter} color_matrix_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (color_matrix_filter: ColorMatrixFilter): Array => +{ + return [ + color_matrix_filter.$filterType, + color_matrix_filter.matrix + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.test.ts b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..76abc769 --- /dev/null +++ b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.test.ts @@ -0,0 +1,33 @@ +import { ColorMatrixFilter } from "../../ColorMatrixFilter"; +import { execute } from "./ColorMatrixFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("ColorMatrixFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new ColorMatrixFilter()); + expect(array.length).toBe(21); + expect(array[0]).toBe(2); + expect(array[1]).toBe(1); + expect(array[2]).toBe(0); + expect(array[3]).toBe(0); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0); + expect(array[6]).toBe(0); + expect(array[7]).toBe(1); + expect(array[8]).toBe(0); + expect(array[9]).toBe(0); + expect(array[10]).toBe(0); + expect(array[11]).toBe(0); + expect(array[12]).toBe(0); + expect(array[13]).toBe(1); + expect(array[14]).toBe(0); + expect(array[15]).toBe(0); + expect(array[16]).toBe(0); + expect(array[17]).toBe(0); + expect(array[18]).toBe(0); + expect(array[19]).toBe(1); + expect(array[20]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.ts b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.ts new file mode 100644 index 00000000..9d75bee1 --- /dev/null +++ b/packages/filters/src/ColorMatrixFilter/service/ColorMatrixFilterToNumberArrayService.ts @@ -0,0 +1,18 @@ +import type { ColorMatrixFilter } from "../../ColorMatrixFilter"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {ColorMatrixFilter} color_matrix_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (color_matrix_filter: ColorMatrixFilter): Float32Array => +{ + return new Float32Array([ + color_matrix_filter.$filterType, + ...color_matrix_filter.matrix + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter.test.ts b/packages/filters/src/ConvolutionFilter.test.ts new file mode 100644 index 00000000..363d17ac --- /dev/null +++ b/packages/filters/src/ConvolutionFilter.test.ts @@ -0,0 +1,19 @@ +import { ConvolutionFilter } from "./ConvolutionFilter"; +import { describe, expect, it } from "vitest"; + +describe("ConvolutionFilter.js property test", () => +{ + it("default test success", () => + { + const convolutionFilter = new ConvolutionFilter(); + expect(convolutionFilter.alpha).toBe(0); + expect(convolutionFilter.bias).toBe(0); + expect(convolutionFilter.clamp).toBe(true); + expect(convolutionFilter.color).toBe(0); + expect(convolutionFilter.divisor).toBe(1); + expect(convolutionFilter.matrix).toBe(null); + expect(convolutionFilter.matrixX).toBe(0); + expect(convolutionFilter.matrixY).toBe(0); + expect(convolutionFilter.preserveAlpha).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter.ts b/packages/filters/src/ConvolutionFilter.ts index 664cab77..28795fd6 100644 --- a/packages/filters/src/ConvolutionFilter.ts +++ b/packages/filters/src/ConvolutionFilter.ts @@ -1,30 +1,21 @@ import { BitmapFilter } from "./BitmapFilter"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; +import { execute as convolutionFilterCanApplyFilterService } from "./ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService"; +import { execute as convolutionFilterToArrayService } from "./ConvolutionFilter/service/ConvolutionFilterToArrayService"; +import { execute as convolutionFilterToNumberArrayService } from "./ConvolutionFilter/service/ConvolutionFilterToNumberArrayService"; import { - $Array, $clamp, - $getArray, - $poolArray, - $toColorInt, - $intToR, - $intToG, - $intToB -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * ConvolutionFilter クラスを使用すると、マトリックス畳み込みフィルター効果を適用できます。 - * 畳み込みでは、入力イメージ内のピクセルを、隣接するピクセルと組み合わせて、イメージを作成します。 - * 畳み込みを使用すると、ぼかし、エッジ検出、シャープ、エンボス、ベベルなど、幅広いイメージ効果を実現できます。 + * @description ConvolutionFilter クラスを使用すると、マトリックス畳み込みフィルター効果を適用できます。 + * 畳み込みでは、入力イメージ内のピクセルを、隣接するピクセルと組み合わせて、イメージを作成します。 + * 畳み込みを使用すると、ぼかし、エッジ検出、シャープ、エンボス、ベベルなど、幅広いイメージ効果を実現できます。 * - * The ConvolutionFilter class applies a matrix convolution filter effect. - * A convolution combines pixels in the input image with neighboring pixels to produce an image. - * A wide variety of image effects can be achieved through convolutions, including blurring, - * edge detection, sharpening, embossing, and beveling. + * The ConvolutionFilter class applies a matrix convolution filter effect. + * A convolution combines pixels in the input image with neighboring pixels to produce an image. + * A wide variety of image effects can be achieved through convolutions, including blurring, + * edge detection, sharpening, embossing, and beveling. * * @class * @memberOf next2d.filters @@ -32,14 +23,76 @@ import { */ export class ConvolutionFilter extends BitmapFilter { + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public $filterType: number = 3; + + /** + * @type {number} + * @default 0 + * @private + */ private _$matrixX: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$matrixY: number; + + /** + * @type {array} + * @default null + * @private + */ private _$matrix: number[] | null; + + /** + * @type {number} + * @default 1 + * @private + */ private _$divisor: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$bias: number; + + /** + * @type {boolean} + * @default true + * @private + */ private _$preserveAlpha: boolean; + + /** + * @type {boolean} + * @default true + * @private + */ private _$clamp: boolean; + + /** + * @type {number} + * @default 0 + * @private + */ private _$color: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$alpha: number; /** @@ -57,76 +110,29 @@ export class ConvolutionFilter extends BitmapFilter * @public */ constructor ( - matrix_x: number = 0, matrix_y: number = 0, - matrix: number[] | null = null, divisor: number = 1, - bias: number = 0, preserve_alpha: boolean = true, - clamp: boolean = true, color: number = 0, alpha: number = 0 + matrix_x: number = 0, + matrix_y: number = 0, + matrix: number[] | null = null, + divisor: number = 1, + bias: number = 0, + preserve_alpha: boolean = true, + clamp: boolean = true, + color: number = 0, + alpha: number = 0 ) { super(); - /** - * @type {number} - * @default 0 - * @private - */ - this._$matrixX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$matrixY = 0; - - /** - * @type {array} - * @default null - * @private - */ - this._$matrix = null; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$divisor = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bias = 0; - - /** - * @type {boolean} - * @default true - * @private - */ + // default + this._$matrixX = 0; + this._$matrixY = 0; + this._$matrix = null; + this._$divisor = 1; + this._$bias = 0; this._$preserveAlpha = true; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$clamp = true; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$color = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$alpha = 0; + this._$clamp = true; + this._$color = 0; + this._$alpha = 0; // setup this.matrixX = matrix_x; @@ -140,62 +146,6 @@ export class ConvolutionFilter extends BitmapFilter this.alpha = alpha; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class ConvolutionFilter] - * @method - * @static - */ - static toString (): string - { - return "[class ConvolutionFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.ConvolutionFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.ConvolutionFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object ConvolutionFilter] - * @method - * @public - */ - toString (): string - { - return "[object ConvolutionFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.ConvolutionFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.ConvolutionFilter"; - } - /** * @description アルファ透明度の値です。 * The alpha transparency value for the color. @@ -211,10 +161,11 @@ export class ConvolutionFilter extends BitmapFilter set alpha (alpha: number) { alpha = $clamp(+alpha, 0, 1, 0); - if (alpha !== this._$alpha) { - this._$alpha = alpha; - this._$doChanged(); + if (alpha === this._$alpha) { + return ; } + this._$alpha = alpha; + this.$updated = true; } /** @@ -231,10 +182,12 @@ export class ConvolutionFilter extends BitmapFilter } set bias (bias: number) { - if (bias !== this._$bias) { - this._$bias = bias | 0; - this._$doChanged(); + bias |= 0; + if (bias === this._$bias) { + return ; } + this._$bias = bias; + this.$updated = true; } /** @@ -251,10 +204,12 @@ export class ConvolutionFilter extends BitmapFilter } set clamp (clamp: boolean) { - if (clamp !== this._$clamp) { - this._$clamp = !!clamp; - this._$doChanged(); + clamp = !!clamp; + if (clamp === this._$clamp) { + return ; } + this._$clamp = clamp; + this.$updated = true; } /** @@ -272,13 +227,16 @@ export class ConvolutionFilter extends BitmapFilter set color (color: number) { color = $clamp( - $toColorInt(color), 0, 0xffffff, 0 + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 0 ); - - if (color !== this._$color) { - this._$color = color; - this._$doChanged(); + if (color === this._$color) { + return ; } + this._$color = color; + this.$updated = true; } /** @@ -295,10 +253,12 @@ export class ConvolutionFilter extends BitmapFilter } set divisor (divisor: number) { - if (divisor !== this._$divisor) { - this._$divisor = divisor | 0; - this._$doChanged(); + divisor |= 0; + if (divisor === this._$divisor) { + return ; } + this._$divisor = divisor; + this.$updated = true; } /** @@ -315,14 +275,11 @@ export class ConvolutionFilter extends BitmapFilter } set matrix (matrix: number[] | null) { - if ($Array.isArray(this._$matrix)) { - $poolArray(this._$matrix); + if (this._$matrix === matrix) { + return ; } - - // default - this._$matrix = $Array.isArray(matrix) ? matrix : null; - - this._$doChanged(); + this._$matrix = matrix; + this.$updated = true; } /** @@ -340,10 +297,11 @@ export class ConvolutionFilter extends BitmapFilter set matrixX (matrix_x: number) { matrix_x = $clamp(matrix_x | 0, 0, 15, 0) | 0; - if (matrix_x !== this._$matrixX) { - this._$matrixX = matrix_x; - this._$doChanged(); + if (matrix_x === this._$matrixX) { + return ; } + this._$matrixX = matrix_x; + this.$updated = true; } /** @@ -361,10 +319,11 @@ export class ConvolutionFilter extends BitmapFilter set matrixY (matrix_y: number) { matrix_y = $clamp(matrix_y | 0, 0, 15, 0) | 0; - if (matrix_y !== this._$matrixY) { - this._$matrixY = matrix_y; - this._$doChanged(); + if (matrix_y === this._$matrixY) { + return ; } + this._$matrixY = matrix_y; + this.$updated = true; } /** @@ -383,10 +342,12 @@ export class ConvolutionFilter extends BitmapFilter } set preserveAlpha (preserve_alpha: boolean) { - if (preserve_alpha !== this._$preserveAlpha) { - this._$preserveAlpha = !!preserve_alpha; - this._$doChanged(); + preserve_alpha = !!preserve_alpha; + if (preserve_alpha === this._$preserveAlpha) { + return ; } + this._$preserveAlpha = preserve_alpha; + this.$updated = true; } /** @@ -407,82 +368,41 @@ export class ConvolutionFilter extends BitmapFilter } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(3, - this._$matrixX, this._$matrixY, this._$matrix, - this._$divisor, this._$bias, this._$preserveAlpha, - this._$clamp, this._$color, this._$alpha - ); + return convolutionFilterToArrayService(this); } /** - * @param {object} rect - * @return {object} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$generateFilterRect (rect: BoundsImpl): BoundsImpl + toNumberArray (): Float32Array { - return rect; + return convolutionFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private - */ - _$canApply (): boolean - { - return this._$matrix !== null - && this._$matrixX * this._$matrixY === this._$matrix.length; - } - - /** - * @param {CanvasToWebGLContext} context - * @return {WebGLTexture} - * @method - * @private + * @public */ - _$applyFilter (context: CanvasToWebGLContext): WebGLTexture + canApplyFilter (): boolean { - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - const texture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - if (!this._$canApply() || !this._$matrix) { - return texture; - } - - context._$applyConvolutionFilter( - texture, - this._$matrixX, - this._$matrixY, - this._$matrix, - this._$divisor, - this._$bias, - this._$preserveAlpha, - this._$clamp, - $intToR(this._$color, this._$alpha, false), - $intToG(this._$color, this._$alpha, false), - $intToB(this._$color, this._$alpha, false), - this._$alpha - ); - - manager.releaseAttachment(currentAttachment, true); - - return manager.getTextureFromCurrentAttachment(); + return convolutionFilterCanApplyFilterService(this); } -} +} \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.test.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..82312857 --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.test.ts @@ -0,0 +1,42 @@ +import { ConvolutionFilter } from "../../ConvolutionFilter"; +import { execute } from "./ConvolutionFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("ConvolutionFilterCanApplyFilterService.js test", () => +{ + it("test case1", () => + { + const convolutionFilter = new ConvolutionFilter(); + expect(execute(convolutionFilter)).toBe(false); + }); + + it("test case2", () => + { + const convolutionFilter = new ConvolutionFilter( + 3, 3, [ + 0.05090, 0.12381, 0.05090, + 0.12381, 0.30116, 0.12381, + 0.05090, 0.12381, 0.05090 + ] + ); + expect(execute(convolutionFilter)).toBe(true); + + convolutionFilter.matrixX = 2; + expect(execute(convolutionFilter)).toBe(false); + + convolutionFilter.matrixX = 3; + convolutionFilter.matrixY = 2; + expect(execute(convolutionFilter)).toBe(false); + + convolutionFilter.matrixY = 3; + convolutionFilter.matrix = null; + expect(execute(convolutionFilter)).toBe(false); + + convolutionFilter.matrix = [ + 0.05090, 0.12381, 0.05090, + 0.12381, 0.30116, 0.12381, + 0.05090, 0.12381, 0.05090 + ]; + expect(execute(convolutionFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.ts new file mode 100644 index 00000000..cd0ed6ac --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterCanApplyFilterService.ts @@ -0,0 +1,16 @@ +import type { ConvolutionFilter } from "../../ConvolutionFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {ConvolutionFilter} convolution_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (convolution_filter: ConvolutionFilter): boolean => +{ + return convolution_filter.matrix !== null + && convolution_filter.matrixX * convolution_filter.matrixY === convolution_filter.matrix.length; +}; \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.test.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.test.ts new file mode 100644 index 00000000..34d40a2a --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.test.ts @@ -0,0 +1,22 @@ +import { ConvolutionFilter } from "../../ConvolutionFilter"; +import { execute } from "./ConvolutionFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("ConvolutionFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new ConvolutionFilter()); + expect(array.length).toBe(10); + expect(array[0]).toBe(3); + expect(array[1]).toBe(0); + expect(array[2]).toBe(0); + expect(array[3]).toBe(null); + expect(array[4]).toBe(1); + expect(array[5]).toBe(0); + expect(array[6]).toBe(true); + expect(array[7]).toBe(true); + expect(array[8]).toBe(0); + expect(array[9]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.ts new file mode 100644 index 00000000..35c6c4b6 --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToArrayService.ts @@ -0,0 +1,26 @@ +import type { ConvolutionFilter } from "../../ConvolutionFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {ConvolutionFilter} convolution_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (convolution_filter: ConvolutionFilter): Array => +{ + return [ + convolution_filter.$filterType, + convolution_filter.matrixX, + convolution_filter.matrixY, + convolution_filter.matrix, + convolution_filter.divisor, + convolution_filter.bias, + convolution_filter.preserveAlpha, + convolution_filter.clamp, + convolution_filter.color, + convolution_filter.alpha + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.test.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..4ea982fa --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.test.ts @@ -0,0 +1,32 @@ +import { ConvolutionFilter } from "../../ConvolutionFilter"; +import { execute } from "./ConvolutionFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("ConvolutionFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new ConvolutionFilter( + 3, 3, [1, 2, 3, 4, 5, 6, 7, 8, 9] + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(3); + expect(array[1]).toBe(3); + expect(array[2]).toBe(3); + expect(array[3]).toBe(1); + expect(array[4]).toBe(2); + expect(array[5]).toBe(3); + expect(array[6]).toBe(4); + expect(array[7]).toBe(5); + expect(array[8]).toBe(6); + expect(array[9]).toBe(7); + expect(array[10]).toBe(8); + expect(array[11]).toBe(9); + expect(array[12]).toBe(1); + expect(array[13]).toBe(0); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(0); + expect(array[17]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.ts b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.ts new file mode 100644 index 00000000..04900f2c --- /dev/null +++ b/packages/filters/src/ConvolutionFilter/service/ConvolutionFilterToNumberArrayService.ts @@ -0,0 +1,27 @@ +import type { ConvolutionFilter } from "../../ConvolutionFilter"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {ConvolutionFilter} convolution_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (convolution_filter: ConvolutionFilter): Float32Array => +{ + const matrix: number[] = convolution_filter.matrix || []; + return new Float32Array([ + convolution_filter.$filterType, + convolution_filter.matrixX, + convolution_filter.matrixY, + ...matrix, + convolution_filter.divisor, + convolution_filter.bias, + +convolution_filter.preserveAlpha, + +convolution_filter.clamp, + convolution_filter.color, + convolution_filter.alpha + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter.ts b/packages/filters/src/DisplacementMapFilter.ts index bd745d77..7604c0ea 100644 --- a/packages/filters/src/DisplacementMapFilter.ts +++ b/packages/filters/src/DisplacementMapFilter.ts @@ -1,29 +1,20 @@ +import type { IDisplacementMapFilterMode } from "./interface/IDisplacementMapFilterMode"; +import type { IBitmapDataChannel } from "./interface/IBitmapDataChannel"; import { BitmapFilter } from "./BitmapFilter"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { DisplacementMapFilterModeImpl } from "./interface/DisplacementMapFilterModeImpl"; -import type { BitmapDataChannelImpl } from "./interface/BitmapDataChannelImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { PointImpl } from "@next2d/interface"; +import { execute as displacementMapFilterCanApplyFilterService } from "./DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService"; +import { execute as displacementMapFilterToArrayService } from "./DisplacementMapFilter/service/DisplacementMapFilterToArrayService"; +import { execute as displacementMapFilterToNumberArrayService } from "./DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService"; import { $clamp, - $getArray, - $intToB, - $intToG, - $intToR, - $Math, - $toColorInt -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * DisplacementMapFilter クラスは、指定された BitmapData オブジェクト(置き換えマップイメージと言います) - * のピクセル値を使用して、オブジェクトの置き換え(変位)を実行します。 + * @description DisplacementMapFilter クラスは、指定された BitmapData オブジェクト(置き換えマップイメージと言います) + * のピクセル値を使用して、オブジェクトの置き換え(変位)を実行します。 * - * The DisplacementMapFilter class uses the pixel values from the specified - * BitmapData object (called the displacement map image) to perform a displacement of an object. + * The DisplacementMapFilter class uses the pixel values from the specified + * BitmapData object (called the displacement map image) to perform a displacement of an object. * * @class * @memberOf next2d.filters @@ -31,19 +22,105 @@ import { */ export class DisplacementMapFilter extends BitmapFilter { - private _$mapBitmap: HTMLImageElement | null; - private _$mapPoint: PointImpl | null; - private _$componentX: BitmapDataChannelImpl; - private _$componentY: BitmapDataChannelImpl; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public $filterType: number = 4; + + /** + * @type {Uint8Array} + * @default null + * @private + */ + private _$bitmapBuffer: Uint8Array | null; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$bitmapWidth: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$bitmapHeight: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$mapPointX: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$mapPointY: number; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$componentX: IBitmapDataChannel; + + /** + * @type {number} + * @default 0 + * @private + */ + private _$componentY: IBitmapDataChannel; + + /** + * @type {number} + * @default 0 + * @private + */ private _$scaleX: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$scaleY: number; - private _$mode: DisplacementMapFilterModeImpl; + + /** + * @type {string} + * @default "wrap" + * @private + */ + private _$mode: IDisplacementMapFilterMode; + + /** + * @type {number} + * @default 0 + * @private + */ private _$color: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$alpha: number; /** - * @param {HTMLImageElement} [map_bitmap = null] - * @param {object} [map_point = null] + * @param {Uint8Array} [bitmap_buffer = null] + * @param {number} [bitmap_width = 0] + * @param {number} [bitmap_height = 0] + * @param {number} [map_point_x = 0] + * @param {number} [map_point_y = 0] * @param {number} [component_x = 0] * @param {number} [component_y = 0] * @param {number} [scale_x = 0.0] @@ -56,146 +133,49 @@ export class DisplacementMapFilter extends BitmapFilter * @public */ constructor ( - map_bitmap: HTMLImageElement | null = null, - map_point: PointImpl | null = null, - component_x: BitmapDataChannelImpl = 0, - component_y: BitmapDataChannelImpl = 0, - scale_x: number = 0, scale_y: number = 0, - mode: DisplacementMapFilterModeImpl = "wrap", - color: number = 0, alpha: number = 0 + bitmap_buffer: Uint8Array | null = null, + bitmap_width: number = 0, + bitmap_height: number = 0, + map_point_x: number = 0, + map_point_y: number = 0, + component_x: IBitmapDataChannel = 0, + component_y: IBitmapDataChannel = 0, + scale_x: number = 0, + scale_y: number = 0, + mode: IDisplacementMapFilterMode = "wrap", + color: number = 0, + alpha: number = 0 ) { super(); - /** - * @type {BitmapData} - * @default null - * @private - */ - this._$mapBitmap = null; - - /** - * @type {object} - * @default null - * @private - */ - this._$mapPoint = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$componentX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$componentY = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$scaleX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$scaleY = 0; - - /** - * @type {string} - * @default DisplacementMapFilterMode.WRAP - * @private - */ - this._$mode = "wrap"; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$color = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$alpha = 0; + // default + this._$bitmapBuffer = null; + this._$bitmapWidth = 0; + this._$bitmapHeight = 0; + this._$mapPointX = 0; + this._$mapPointY = 0; + this._$componentX = 0; + this._$componentY = 0; + this._$scaleX = 0; + this._$scaleY = 0; + this._$mode = "wrap"; + this._$color = 0; + this._$alpha = 0; // setup - this.mapBitmap = map_bitmap; - this.mapPoint = map_point; - this.componentX = component_x; - this.componentY = component_y; - this.scaleX = scale_x; - this.scaleY = scale_y; - this.mode = mode; - this.color = color; - this.alpha = alpha; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class DisplacementMapFilter] - * @method - * @static - */ - static toString (): string - { - return "[class DisplacementMapFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.DisplacementMapFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.DisplacementMapFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object DisplacementMapFilter] - * @method - * @public - */ - toString (): string - { - return "[object DisplacementMapFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.DisplacementMapFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.DisplacementMapFilter"; + this.bitmapBuffer = bitmap_buffer; + this.bitmapWidth = bitmap_width; + this.bitmapHeight = bitmap_height; + this.mapPointX = map_point_x; + this.mapPointY = map_point_y; + this.componentX = component_x; + this.componentY = component_y; + this.scaleX = scale_x; + this.scaleY = scale_y; + this.mode = mode; + this.color = color; + this.alpha = alpha; } /** @@ -213,10 +193,11 @@ export class DisplacementMapFilter extends BitmapFilter set alpha (alpha: number) { alpha = $clamp(+alpha, 0, 1, 0); - if (alpha !== this._$alpha) { - this._$alpha = alpha; - this._$doChanged(); + if (alpha === this._$alpha) { + return ; } + this._$alpha = alpha; + this.$updated = true; } /** @@ -234,13 +215,17 @@ export class DisplacementMapFilter extends BitmapFilter set color (color: number) { color = $clamp( - $toColorInt(color),0 ,0xffffff, 0 + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 0 ); - if (color !== this._$color) { - this._$color = color; - this._$doChanged(); + if (color === this._$color) { + return ; } + this._$color = color; + this.$updated = true; } /** @@ -251,16 +236,17 @@ export class DisplacementMapFilter extends BitmapFilter * @default 0 * @public */ - get componentX (): BitmapDataChannelImpl + get componentX (): IBitmapDataChannel { return this._$componentX; } - set componentX (component_x: BitmapDataChannelImpl) + set componentX (component_x: IBitmapDataChannel) { - if (component_x !== this._$componentX) { - this._$componentX = component_x; - this._$doChanged(); + if (component_x === this._$componentX) { + return ; } + this._$componentX = component_x; + this.$updated = true; } /** @@ -271,16 +257,17 @@ export class DisplacementMapFilter extends BitmapFilter * @default 0 * @public */ - get componentY (): BitmapDataChannelImpl + get componentY (): IBitmapDataChannel { return this._$componentY; } - set componentY (component_y: BitmapDataChannelImpl) + set componentY (component_y: IBitmapDataChannel) { - if (component_y !== this._$componentY) { - this._$componentY = component_y; - this._$doChanged(); + if (component_y === this._$componentY) { + return ; } + this._$componentY = component_y; + this.$updated = true; } /** @@ -291,16 +278,59 @@ export class DisplacementMapFilter extends BitmapFilter * @default null * @public */ - get mapBitmap (): HTMLImageElement | null + get bitmapBuffer (): Uint8Array | null + { + return this._$bitmapBuffer; + } + set bitmapBuffer (bitmap_buffer: Uint8Array | null) + { + if (bitmap_buffer === this._$bitmapBuffer) { + return ; + } + this._$bitmapBuffer = bitmap_buffer; + this.$updated = true; + } + + /** + * @description 置き換えマップデータの幅です。 + * The width of the displacement map image. + * + * @member {number} + * @default 0 + * @public + */ + get bitmapWidth (): number + { + return this._$bitmapWidth; + } + set bitmapWidth (bitmap_width: number) + { + if (bitmap_width === this._$bitmapWidth) { + return ; + } + this._$bitmapWidth = bitmap_width; + this.$updated = true; + } + + /** + * @description 置き換えマップデータの高さです。 + * The height of the displacement map image. + * + * @member {number} + * @default 0 + * @public + */ + get bitmapHeight (): number { - return this._$mapBitmap; + return this._$bitmapHeight; } - set mapBitmap (map_bitmap: HTMLImageElement | null) + set bitmapHeight (bitmap_height: number) { - if (map_bitmap !== this._$mapBitmap) { - this._$mapBitmap = map_bitmap; - this._$doChanged(); + if (bitmap_height === this._$bitmapHeight) { + return ; } + this._$bitmapHeight = bitmap_height; + this.$updated = true; } /** @@ -309,20 +339,44 @@ export class DisplacementMapFilter extends BitmapFilter * A value that contains the offset of the upper-left corner * of the target display object from the upper-left corner of the map image. * - * @member {object} - * @default null + * @member {number} + * @default 0 + * @public + */ + get mapPointX (): number + { + return this._$mapPointX; + } + set mapPointX (map_point_x: number) + { + if (map_point_x === this._$mapPointX) { + return ; + } + this._$mapPointX = map_point_x; + this.$updated = true; + } + + /** + * @description マップイメージの左上隅を基準としたターゲット表示オブジェクトの + * 左上隅のオフセットが含まれる値です。 + * A value that contains the offset of the upper-left corner + * of the target display object from the upper-left corner of the map image. + * + * @member {number} + * @default 0 * @public */ - get mapPoint (): PointImpl | null + get mapPointY (): number { - return this._$mapPoint; + return this._$mapPointY; } - set mapPoint (map_point: PointImpl | null) + set mapPointY (map_point_y: number) { - if (map_point !== this._$mapPoint) { - this._$mapPoint = map_point; - this._$doChanged(); + if (map_point_y === this._$mapPointY) { + return ; } + this._$mapPointY = map_point_y; + this.$updated = true; } /** @@ -333,16 +387,17 @@ export class DisplacementMapFilter extends BitmapFilter * @default DisplacementMapFilterMode.WRAP * @public */ - get mode (): DisplacementMapFilterModeImpl + get mode (): IDisplacementMapFilterMode { return this._$mode; } - set mode (mode: DisplacementMapFilterModeImpl) + set mode (mode: IDisplacementMapFilterMode) { - if (mode !== this._$mode) { - this._$mode = mode; - this._$doChanged(); + if (mode === this._$mode) { + return ; } + this._$mode = mode; + this.$updated = true; } /** @@ -360,10 +415,11 @@ export class DisplacementMapFilter extends BitmapFilter set scaleX (scale_x: number) { scale_x = $clamp(+scale_x, -0xffff, 0xffff, 0); - if (scale_x !== this._$scaleX) { - this._$scaleX = scale_x; - this._$doChanged(); + if (scale_x === this._$scaleX) { + return ; } + this._$scaleX = scale_x; + this.$updated = true; } /** @@ -381,10 +437,11 @@ export class DisplacementMapFilter extends BitmapFilter set scaleY (scale_y: number) { scale_y = $clamp(+scale_y, -0xffff, 0xffff, 0); - if (scale_y !== this._$scaleY) { - this._$scaleY = scale_y; - this._$doChanged(); + if (scale_y === this._$scaleY) { + return ; } + this._$scaleY = scale_y; + this.$updated = true; } /** @@ -398,101 +455,51 @@ export class DisplacementMapFilter extends BitmapFilter clone (): DisplacementMapFilter { return new DisplacementMapFilter( - this._$mapBitmap, this._$mapPoint, this._$componentX, this._$componentY, + this._$bitmapBuffer + ? this._$bitmapBuffer.slice() + : null, + this._$bitmapWidth, this._$bitmapHeight, + this._$mapPointX, this._$mapPointX, this._$componentX, this._$componentY, this._$scaleX, this._$scaleY, this._$mode, this._$color, this._$alpha ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(4, - this._$mapBitmap, this._$mapPoint, this._$componentX, this._$componentY, - this._$scaleX, this._$scaleY, this._$mode, this._$color, this._$alpha - ); + return displacementMapFilterToArrayService(this); } /** - * @param {object} bounds - * @return {object} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$generateFilterRect (bounds: BoundsImpl): BoundsImpl + toNumberArray (): Float32Array { - return bounds; + return displacementMapFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$mapBitmap !== null - && this._$componentX > 0 && this._$componentY > 0 - && this._$scaleX !== 0 && this._$scaleY !== 0; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {WebGLTexture} - * @method - * @private - */ - _$applyFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): WebGLTexture { - - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - const texture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - if (!this._$canApply() - || !currentAttachment - || !this._$mapBitmap - ) { - return texture; - } - - // matrix to scale - const xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - const yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - context._$applyDisplacementMapFilter( - texture, - this._$mapBitmap, - texture.width / xScale, - texture.height / yScale, - this._$mapPoint, - this._$componentX, - this._$componentY, - this._$scaleX, - this._$scaleY, - this._$mode, - $intToR(this._$color, this._$alpha, true), - $intToG(this._$color, this._$alpha, true), - $intToB(this._$color, this._$alpha, true), - this._$alpha - ); - - manager.releaseAttachment(currentAttachment, true); - - return manager.getTextureFromCurrentAttachment(); + return displacementMapFilterCanApplyFilterService(this); } -} +} \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.test.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..b7c4614c --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.test.ts @@ -0,0 +1,20 @@ +import { DisplacementMapFilter } from "../../DisplacementMapFilter"; +import { execute } from "./DisplacementMapFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("DisplacementMapFilterCanApplyFilterService.js test", () => +{ + it("test case1", () => + { + const displacementMapFilter = new DisplacementMapFilter(); + expect(execute(displacementMapFilter)).toBe(false); + }); + + it("test case2", () => + { + const displacementMapFilter = new DisplacementMapFilter( + new Uint8Array([0, 0, 0, 0]), 1, 1, 1, 1, 1, 1, 1, 1 + ); + expect(execute(displacementMapFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.ts new file mode 100644 index 00000000..6f63aa5e --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterCanApplyFilterService.ts @@ -0,0 +1,19 @@ +import type { DisplacementMapFilter } from "../../DisplacementMapFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {DisplacementMapFilter} displacement_map_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (displacement_map_filter: DisplacementMapFilter): boolean => +{ + return displacement_map_filter.bitmapBuffer !== null + && displacement_map_filter.componentX > 0 + && displacement_map_filter.componentY > 0 + && displacement_map_filter.scaleX !== 0 + && displacement_map_filter.scaleY !== 0; +}; \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.test.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.test.ts new file mode 100644 index 00000000..f42dc593 --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.test.ts @@ -0,0 +1,25 @@ +import { DisplacementMapFilter } from "../../DisplacementMapFilter"; +import { execute } from "./DisplacementMapFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("DisplacementMapFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new DisplacementMapFilter(new Uint8Array([0, 0, 0, 0]))); + expect(array.length).toBe(13); + expect(array[0]).toBe(4); + expect((array[1] as number[]).length).toBe(4); + expect(array[2]).toBe(0); + expect(array[3]).toBe(0); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0); + expect(array[6]).toBe(0); + expect(array[7]).toBe(0); + expect(array[8]).toBe(0); + expect(array[9]).toBe(0); + expect(array[10]).toBe("wrap"); + expect(array[11]).toBe(0); + expect(array[12]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.ts new file mode 100644 index 00000000..1a0f35c6 --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToArrayService.ts @@ -0,0 +1,31 @@ +import type { DisplacementMapFilter } from "../../DisplacementMapFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {DisplacementMapFilter} displacement_map_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (displacement_map_filter: DisplacementMapFilter): Array => +{ + return [ + displacement_map_filter.$filterType, + displacement_map_filter.bitmapBuffer + ? Array.from(displacement_map_filter.bitmapBuffer) + : null, + displacement_map_filter.bitmapWidth, + displacement_map_filter.bitmapHeight, + displacement_map_filter.mapPointX, + displacement_map_filter.mapPointY, + displacement_map_filter.componentX, + displacement_map_filter.componentY, + displacement_map_filter.scaleX, + displacement_map_filter.scaleY, + displacement_map_filter.mode, + displacement_map_filter.color, + displacement_map_filter.alpha + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.test.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..4d47c6b7 --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.test.ts @@ -0,0 +1,31 @@ +import { DisplacementMapFilter } from "../../DisplacementMapFilter"; +import { execute } from "./DisplacementMapFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("DisplacementMapFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new DisplacementMapFilter( + new Uint8Array([10, 11, 12, 13]) + )); + expect(array.length).toBe(17); + expect(array[0]).toBe(4); + expect(array[1]).toBe(4); + expect(array[2]).toBe(10); + expect(array[3]).toBe(11); + expect(array[4]).toBe(12); + expect(array[5]).toBe(13); + expect(array[6]).toBe(0); + expect(array[7]).toBe(0); + expect(array[8]).toBe(0); + expect(array[9]).toBe(0); + expect(array[10]).toBe(0); + expect(array[11]).toBe(0); + expect(array[12]).toBe(0); + expect(array[13]).toBe(0); + expect(array[14]).toBe(2); + expect(array[15]).toBe(0); + expect(array[16]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.ts b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.ts new file mode 100644 index 00000000..a4592329 --- /dev/null +++ b/packages/filters/src/DisplacementMapFilter/service/DisplacementMapFilterToNumberArrayService.ts @@ -0,0 +1,61 @@ +import type { DisplacementMapFilter } from "../../DisplacementMapFilter"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {DisplacementMapFilter} displacement_map_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (displacement_map_filter: DisplacementMapFilter): Float32Array => +{ + let mode = 2; + switch (displacement_map_filter.mode) { + + case "clamp": + mode = 0; + break; + + case "color": + mode = 1; + break; + + case "wrap": + mode = 2; + break; + + case "ignore": + mode = 3; + break; + + default: + mode = 2; + break; + + } + + const buffer = displacement_map_filter.bitmapBuffer || new Uint8Array(); + const array = [displacement_map_filter.$filterType, buffer.length]; + + for (let idx = 0; idx < buffer.length; idx += 4096) { + array.push(...buffer.subarray(idx, idx + 4096)); + } + + array.push( + displacement_map_filter.bitmapWidth, + displacement_map_filter.bitmapHeight, + displacement_map_filter.mapPointX, + displacement_map_filter.mapPointY, + displacement_map_filter.componentX, + displacement_map_filter.componentY, + displacement_map_filter.scaleX, + displacement_map_filter.scaleY, + mode, + displacement_map_filter.color, + displacement_map_filter.alpha + ); + + return new Float32Array(array); +}; \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter.test.ts b/packages/filters/src/DropShadowFilter.test.ts new file mode 100644 index 00000000..00709117 --- /dev/null +++ b/packages/filters/src/DropShadowFilter.test.ts @@ -0,0 +1,21 @@ +import { DropShadowFilter } from "./DropShadowFilter"; +import { describe, expect, it } from "vitest"; + +describe("DropShadowFilter.js property test", () => +{ + it("default test success", () => + { + const dropShadowFilter = new DropShadowFilter(); + expect(dropShadowFilter.distance).toBe(4); + expect(dropShadowFilter.angle).toBe(45); + expect(dropShadowFilter.color).toBe(0); + expect(dropShadowFilter.alpha).toBe(1); + expect(dropShadowFilter.blurX).toBe(4); + expect(dropShadowFilter.blurY).toBe(4); + expect(dropShadowFilter.strength).toBe(1); + expect(dropShadowFilter.quality).toBe(1); + expect(dropShadowFilter.inner).toBe(false); + expect(dropShadowFilter.knockout).toBe(false); + expect(dropShadowFilter.hideObject).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter.ts b/packages/filters/src/DropShadowFilter.ts index 2c4c8fe6..7070df02 100644 --- a/packages/filters/src/DropShadowFilter.ts +++ b/packages/filters/src/DropShadowFilter.ts @@ -1,35 +1,23 @@ +import type { IFilterQuality } from "./interface/IFilterQuality"; import { BitmapFilter } from "./BitmapFilter"; -import { BlurFilter } from "./BlurFilter"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BitmapFilterTypeImpl } from "./interface/BitmapFilterTypeImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; +import { execute as dropShadowFilterToArrayService } from "./DropShadowFilter/service/DropShadowFilterToArrayService"; +import { execute as dropShadowFilterToNumberArrayService } from "./DropShadowFilter/service/DropShadowFilterToNumberArrayService"; +import { execute as dropShadowFilterCanApplyFilterService } from "./DropShadowFilter/service/DropShadowFilterCanApplyFilterService"; +import { execute as dropShadowFilterGetBoundsUseCase } from "./DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase"; import { $clamp, - $Deg2Rad, - $getArray, - $Math, - $toColorInt, - $intToR, - $intToG, - $intToB, - $getBoundsObject, - $devicePixelRatio -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * DropShadowFilter クラスは、ドロップシャドウを表示オブジェクトに追加します。 - * シャドウアルゴリズムは、ぼかしフィルターで使用するのと同じボックスフィルターに基づいています。 - * ドロップシャドウのスタイルには複数のオプションがあり、内側シャドウ、外側シャドウ、ノックアウトモードなどがあります。 + * @description DropShadowFilter クラスは、ドロップシャドウを表示オブジェクトに追加します。 + * シャドウアルゴリズムは、ぼかしフィルターで使用するのと同じボックスフィルターに基づいています。 + * ドロップシャドウのスタイルには複数のオプションがあり、内側シャドウ、外側シャドウ、ノックアウトモードなどがあります。 * - * The DropShadowFilter class lets you add a drop shadow to display objects. - * The shadow algorithm is based on the same box filter that the blur filter uses. - * You have several options for the style of the drop shadow, including inner - * or outer shadow and knockout mode. + * The DropShadowFilter class lets you add a drop shadow to display objects. + * The shadow algorithm is based on the same box filter that the blur filter uses. + * You have several options for the style of the drop shadow, including inner + * or outer shadow and knockout mode. * * @class * @memberOf next2d.filters @@ -37,14 +25,90 @@ import { */ export class DropShadowFilter extends BitmapFilter { - private _$blurFilter: BlurFilter; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public readonly $filterType: number = 5; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurX: number; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurY: number; + + /** + * @type {IFilterQuality} + * @default 1 + * @private + */ + private _$quality: IFilterQuality; + + /** + * @type {number} + * @default 4 + * @private + */ private _$distance: number; + + /** + * @type {number} + * @default 45 + * @private + */ private _$angle: number; + + /** + * @type {number} + * @default 0 + * @private + */ private _$color: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$alpha: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$strength: number; + + /** + * @type {boolean} + * @default false + * @private + */ private _$inner: boolean; + + /** + * @type {boolean} + * @default false + * @private + */ private _$knockout: boolean; + + /** + * @type {boolean} + * @default false + * @private + */ private _$hideObject: boolean; /** @@ -64,80 +128,38 @@ export class DropShadowFilter extends BitmapFilter * @public */ constructor ( - distance: number = 4, angle: number = 45, - color: number = 0, alpha: number = 1, - blur_x: number = 4, blur_y: number = 4, - strength: number = 1, quality: FilterQualityImpl = 1, - inner: boolean = false, knockout: boolean = false, + distance: number = 4, + angle: number = 45, + color: number = 0, + alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: IFilterQuality = 1, + inner: boolean = false, + knockout: boolean = false, hide_object: boolean = false ) { super(); - /** - * @type {BlurFilter} - * @default BlurFilter - * @private - */ - this._$blurFilter = new BlurFilter(blur_x, blur_y, quality); - - /** - * @type {number} - * @default 4 - * @private - */ - this._$distance = 4; - - /** - * @type {number} - * @default 45 - * @private - */ - this._$angle = 45; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$color = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$alpha = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$strength = 1; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$inner = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$knockout = false; - - /** - * @type {boolean} - * @default false - * @private - */ + // default + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + this._$distance = 4; + this._$angle = 45; + this._$color = 0; + this._$alpha = 1; + this._$strength = 1; + this._$inner = false; + this._$knockout = false; this._$hideObject = false; // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; this.distance = distance; this.angle = angle; this.color = color; @@ -148,62 +170,6 @@ export class DropShadowFilter extends BitmapFilter this.hideObject = hide_object; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class DropShadowFilter] - * @method - * @static - */ - static toString (): string - { - return "[class DropShadowFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.DropShadowFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.DropShadowFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object DropShadowFilter] - * @method - * @public - */ - toString (): string - { - return "[object DropShadowFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.DropShadowFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.DropShadowFilter"; - } - /** * @description アルファ透明度の値です。 * The alpha transparency value for the color. @@ -219,10 +185,11 @@ export class DropShadowFilter extends BitmapFilter set alpha (alpha: number) { alpha = $clamp(+alpha, 0, 1, 0); - if (alpha !== this._$alpha) { - this._$alpha = alpha; - this._$doChanged(); + if (alpha === this._$alpha) { + return ; } + this._$alpha = alpha; + this.$updated = true; } /** @@ -239,11 +206,12 @@ export class DropShadowFilter extends BitmapFilter } set angle (angle: number) { - angle %= 360; - if (angle !== this._$angle) { - this._$angle = $clamp(angle, -360, 360, 45); - this._$doChanged(); + angle = $clamp(angle % 360, -360, 360, 45); + if (angle === this._$angle) { + return ; } + this._$angle = angle; + this.$updated = true; } /** @@ -256,11 +224,16 @@ export class DropShadowFilter extends BitmapFilter */ get blurX (): number { - return this._$blurFilter.blurX; + return this._$blurX; } set blurX (blur_x: number) { - this._$blurFilter.blurX = blur_x; + blur_x = $clamp(+blur_x, 0, 255, 0); + if (blur_x === this._$blurX) { + return ; + } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -273,11 +246,16 @@ export class DropShadowFilter extends BitmapFilter */ get blurY (): number { - return this._$blurFilter.blurY; + return this._$blurY; } set blurY (blur_y: number) { - this._$blurFilter.blurY = blur_y; + blur_y = $clamp(+blur_y, 0, 255, 0); + if (blur_y === this._$blurY) { + return ; + } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -295,13 +273,17 @@ export class DropShadowFilter extends BitmapFilter set color (color: number) { color = $clamp( - $toColorInt(color), 0, 0xffffff, 0 + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 0 ); - if (color !== this._$color) { - this._$color = color; - this._$doChanged(); + if (color === this._$color) { + return ; } + this._$color = color; + this.$updated = true; } /** @@ -319,10 +301,11 @@ export class DropShadowFilter extends BitmapFilter set distance (distance: number) { distance = $clamp(+distance, -255, 255, 4); - if (distance !== this._$distance) { - this._$distance = distance; - this._$doChanged(); + if (distance === this._$distance) { + return ; } + this._$distance = distance; + this.$updated = true; } /** @@ -339,10 +322,12 @@ export class DropShadowFilter extends BitmapFilter } set hideObject (hide_object: boolean) { - if (hide_object !== this._$hideObject) { - this._$hideObject = !!hide_object; - this._$doChanged(); + hide_object = !!hide_object; + if (hide_object === this._$hideObject) { + return ; } + this._$hideObject = hide_object; + this.$updated = true; } /** @@ -359,10 +344,12 @@ export class DropShadowFilter extends BitmapFilter } set inner (inner: boolean) { - if (inner !== this._$inner) { - this._$inner = !!inner; - this._$doChanged(); + inner = !!inner; + if (inner === this._$inner) { + return ; } + this._$inner = inner; + this.$updated = true; } /** @@ -379,10 +366,12 @@ export class DropShadowFilter extends BitmapFilter } set knockout (knockout: boolean) { - if (knockout !== this._$knockout) { - this._$knockout = !!knockout; - this._$doChanged(); + knockout = !!knockout; + if (knockout === this._$knockout) { + return ; } + this._$knockout = knockout; + this.$updated = true; } /** @@ -393,13 +382,18 @@ export class DropShadowFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { - return this._$blurFilter.quality; + return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - this._$blurFilter.quality = quality; + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; + } + this._$quality = quality; + this.$updated = true; } /** @@ -417,10 +411,11 @@ export class DropShadowFilter extends BitmapFilter set strength (strength: number) { strength = $clamp(strength | 0, 0, 255, 0); - if (strength !== this._$strength) { - this._$strength = strength; - this._$doChanged(); + if (strength === this._$strength) { + return ; } + this._$strength = strength; + this.$updated = true; } /** @@ -435,196 +430,61 @@ export class DropShadowFilter extends BitmapFilter { return new DropShadowFilter( this._$distance, this._$angle, this._$color, this._$alpha, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$inner, this._$knockout, this._$hideObject + this._$blurX, this._$blurY, this._$strength, + this._$quality, this._$inner, this._$knockout, this._$hideObject ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(5, - this._$distance, this._$angle, this._$color, this._$alpha, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$inner, this._$knockout, this._$hideObject - ); + return dropShadowFilterToArrayService(this); } /** - * @return {boolean} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method * @public */ - _$isUpdated (): boolean + toNumberArray (): Float32Array { - return this._$updated || this._$blurFilter._$isUpdated(); - } - - /** - * @param {object} bounds - * @param {number} [x_scale=null] - * @param {number} [y_scale=null] - * @return {object} - * @method - * @private - */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, - y_scale: number = 0 - ): BoundsImpl { - - let clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$canApply()) { - return clone; - } - - clone = this - ._$blurFilter - ._$generateFilterRect(clone, x_scale, y_scale); - - const radian = this._$angle * $Deg2Rad; - - let x = $Math.cos(radian) * this._$distance; - let y = $Math.sin(radian) * this._$distance; - - if (x_scale) { - x *= x_scale; - } - - if (y_scale) { - y *= y_scale; - } - - clone.xMin = $Math.min(clone.xMin, x); - if (x > 0) { - clone.xMax += x; - } - clone.yMin = $Math.min(clone.yMin, y); - if (y > 0) { - clone.yMax += y; - } - - return clone; + return dropShadowFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$alpha > 0 && this._$strength > 0 && this._$blurFilter._$canApply(); + return dropShadowFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {WebGLTexture} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter (context: CanvasToWebGLContext, matrix: Float32Array) + getBounds (bounds: Float32Array): Float32Array { - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment) { - throw new Error("the current attachment is null."); - } - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - if (!this._$canApply()) { - return manager.getTextureFromCurrentAttachment(); - } - - const baseWidth: number = currentAttachment.width; - const baseHeight: number = currentAttachment.height; - const baseOffsetX: number = context._$offsetX; - const baseOffsetY: number = context._$offsetY; - - const blurTexture: WebGLTexture = this - ._$blurFilter - ._$applyFilter(context, matrix, false); - - const blurWidth: number = blurTexture.width; - const blurHeight: number = blurTexture.height; - const blurOffsetX: number = context._$offsetX; - const blurOffsetY: number = context._$offsetY; - - const offsetDiffX: number = blurOffsetX - baseOffsetX; - const offsetDiffY: number = blurOffsetY - baseOffsetY; - - let xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - let yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - // shadow point - const radian: number = this._$angle * $Deg2Rad; - const x: number = $Math.cos(radian) * this._$distance * xScale; - const y: number = $Math.sin(radian) * this._$distance * yScale; - - // dropShadow canvas - const w: number = this._$inner ? baseWidth : blurWidth + $Math.max(0, $Math.abs(x) - offsetDiffX); - const h: number = this._$inner ? baseHeight : blurHeight + $Math.max(0, $Math.abs(y) - offsetDiffY); - const width: number = $Math.ceil(w); - const height: number = $Math.ceil(h); - const fractionX: number = (width - w) / 2; - const fractionY: number = (height - h) / 2; - - const baseTextureX = this._$inner ? 0 : $Math.max(0, offsetDiffX - x) + fractionX; - const baseTextureY = this._$inner ? 0 : $Math.max(0, offsetDiffY - y) + fractionY; - const blurTextureX = this._$inner ? x - blurOffsetX : (x > 0 ? $Math.max(0, x - offsetDiffX) : 0) + fractionX; - const blurTextureY = this._$inner ? y - blurOffsetY : (y > 0 ? $Math.max(0, y - offsetDiffY) : 0) + fractionY; - - let type: BitmapFilterTypeImpl; - let knockout: boolean; - if (this._$inner) { - type = "inner"; - knockout = this._$knockout || this._$hideObject; - } else if (!this._$knockout && this._$hideObject) { - type = "full"; - knockout = true; - } else { - type = "outer"; - knockout = this._$knockout; - } - - context._$bind(currentAttachment); - context._$applyBitmapFilter( - blurTexture, width, height, - baseWidth, baseHeight, baseTextureX, baseTextureY, - blurWidth, blurHeight, blurTextureX, blurTextureY, - true, type, knockout, - this._$strength, null, null, null, - $intToR(this._$color, this._$alpha, true), - $intToG(this._$color, this._$alpha, true), - $intToB(this._$color, this._$alpha, true), - this._$alpha, - 0, 0, 0, 0 - ); - - context._$offsetX = baseOffsetX + baseTextureX; - context._$offsetY = baseOffsetY + baseTextureY; - - manager.releaseTexture(blurTexture); - - return manager.getTextureFromCurrentAttachment(); + return dropShadowFilterGetBoundsUseCase(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.test.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..70b0880e --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.test.ts @@ -0,0 +1,36 @@ +import { DropShadowFilter } from "../../DropShadowFilter"; +import { execute } from "./DropShadowFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("DropShadowFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const dropShadowFilter = new DropShadowFilter(); + expect(execute(dropShadowFilter)).toBe(true); + + dropShadowFilter.blurX = 0; + expect(execute(dropShadowFilter)).toBe(false); + + dropShadowFilter.blurX = 1; + dropShadowFilter.blurY = 0; + expect(execute(dropShadowFilter)).toBe(false); + + dropShadowFilter.blurY = 1; + dropShadowFilter.quality = 0; + expect(execute(dropShadowFilter)).toBe(false); + + dropShadowFilter.quality = 1; + expect(execute(dropShadowFilter)).toBe(true); + + dropShadowFilter.alpha = 0; + expect(execute(dropShadowFilter)).toBe(false); + + dropShadowFilter.alpha = 1; + dropShadowFilter.strength = 0; + expect(execute(dropShadowFilter)).toBe(false); + + dropShadowFilter.strength = 1; + expect(execute(dropShadowFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.ts new file mode 100644 index 00000000..06de296e --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterCanApplyFilterService.ts @@ -0,0 +1,19 @@ +import type { DropShadowFilter } from "../../DropShadowFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {DropShadowFilter} drop_shadow_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (drop_shadow_filter: DropShadowFilter): boolean => +{ + return drop_shadow_filter.alpha > 0 + && drop_shadow_filter.strength !== 0 + && drop_shadow_filter.blurX > 0 + && drop_shadow_filter.blurY > 0 + && drop_shadow_filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.test.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.test.ts new file mode 100644 index 00000000..48a5c53a --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.test.ts @@ -0,0 +1,24 @@ +import { DropShadowFilter } from "../../DropShadowFilter"; +import { execute } from "./DropShadowFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("DropShadowFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new DropShadowFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(5); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(0); + expect(array[4]).toBe(1); + expect(array[5]).toBe(4); + expect(array[6]).toBe(4); + expect(array[7]).toBe(1); + expect(array[8]).toBe(1); + expect(array[9]).toBe(false); + expect(array[10]).toBe(false); + expect(array[11]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.ts new file mode 100644 index 00000000..676cdffa --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToArrayService.ts @@ -0,0 +1,28 @@ +import type { DropShadowFilter } from "../../DropShadowFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {DropShadowFilter} drop_shadow_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (drop_shadow_filter: DropShadowFilter): Array => +{ + return [ + drop_shadow_filter.$filterType, + drop_shadow_filter.distance, + drop_shadow_filter.angle, + drop_shadow_filter.color, + drop_shadow_filter.alpha, + drop_shadow_filter.blurX, + drop_shadow_filter.blurY, + drop_shadow_filter.strength, + drop_shadow_filter.quality, + drop_shadow_filter.inner, + drop_shadow_filter.knockout, + drop_shadow_filter.hideObject + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.test.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..aec4e184 --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.test.ts @@ -0,0 +1,24 @@ +import { DropShadowFilter } from "../../DropShadowFilter"; +import { execute } from "./DropShadowFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("DropShadowFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new DropShadowFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(5); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(0); + expect(array[4]).toBe(1); + expect(array[5]).toBe(4); + expect(array[6]).toBe(4); + expect(array[7]).toBe(1); + expect(array[8]).toBe(1); + expect(array[9]).toBe(0); + expect(array[10]).toBe(0); + expect(array[11]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.ts b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.ts new file mode 100644 index 00000000..9465164f --- /dev/null +++ b/packages/filters/src/DropShadowFilter/service/DropShadowFilterToNumberArrayService.ts @@ -0,0 +1,28 @@ +import type { DropShadowFilter } from "../../DropShadowFilter"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {DropShadowFilter} drop_shadow_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (drop_shadow_filter: DropShadowFilter): Float32Array => +{ + return new Float32Array([ + drop_shadow_filter.$filterType, + drop_shadow_filter.distance, + drop_shadow_filter.angle, + drop_shadow_filter.color, + drop_shadow_filter.alpha, + drop_shadow_filter.blurX, + drop_shadow_filter.blurY, + drop_shadow_filter.strength, + drop_shadow_filter.quality, + +drop_shadow_filter.inner, + +drop_shadow_filter.knockout, + +drop_shadow_filter.hideObject + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.test.ts b/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..687afc27 --- /dev/null +++ b/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.test.ts @@ -0,0 +1,17 @@ +import { DropShadowFilter } from "../../DropShadowFilter"; +import { execute } from "./DropShadowFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("DropShadowFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new DropShadowFilter(), bounds); + + expect(bounds[0]).toBe(-2); + expect(bounds[1]).toBe(-2); + expect(bounds[2]).toBe(4.828427314758301); + expect(bounds[3]).toBe(4.828427314758301); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.ts b/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..fc5afc6a --- /dev/null +++ b/packages/filters/src/DropShadowFilter/usecase/DropShadowFilterGetBoundsUseCase.ts @@ -0,0 +1,43 @@ +import type { DropShadowFilter } from "../../DropShadowFilter"; +import { $Deg2Rad } from "../../FilterUtil"; +import { execute as dropShadowFilterCanApplyFilterService } from "../service/DropShadowFilterCanApplyFilterService"; +import { execute as blurFilterGetBoundsUseCase } from "../../BlurFilter/usecase/BlurFilterGetBoundsUseCase"; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {DropShadowFilter} drop_shadow_filter + * @param {Float32Array} bounds + * @return {Float32Array} + */ +export const execute = (drop_shadow_filter: DropShadowFilter, bounds: Float32Array): Float32Array => +{ + if (!dropShadowFilterCanApplyFilterService(drop_shadow_filter)) { + return bounds; + } + + if (drop_shadow_filter.inner) { + return bounds; + } + + blurFilterGetBoundsUseCase(drop_shadow_filter, bounds); + + const radian = drop_shadow_filter.angle * $Deg2Rad; + const distance = drop_shadow_filter.distance; + + const x = Math.cos(radian) * distance; + const y = Math.sin(radian) * distance; + + bounds[0] = Math.min(bounds[0], x); + if (x > 0) { + bounds[2] += x; + } + + bounds[1] = Math.min(bounds[1], y); + if (y > 0) { + bounds[3] += y; + } + + return bounds; +}; \ No newline at end of file diff --git a/packages/filters/src/FilterUtil.ts b/packages/filters/src/FilterUtil.ts new file mode 100644 index 00000000..3e616236 --- /dev/null +++ b/packages/filters/src/FilterUtil.ts @@ -0,0 +1,81 @@ +import type { IBitmapFilterType } from "./interface/IBitmapFilterType"; + +/** + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @protected + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +const canvas = document.createElement("canvas"); +canvas.width = canvas.height = 1; +const colorContext = canvas.getContext("2d"); + +/** + * @description カラー文字列を数値に変換 + * Convert color string to number + * + * @param {string} value + * @return {number} + * @method + * @protected + */ +export const $convertColorStringToNumber = (value: string): number => +{ + if (!colorContext) { + return 0; + } + + colorContext.fillStyle = value; + return +`0x${colorContext.fillStyle.slice(1)}`; +}; + +/** + * @type {number} + * @private + */ +export const $Deg2Rad: number = Math.PI / 180; + +/** + * @description 種類を数値に変換 + * Convert type to number + * + * @param {IBitmapFilterType} type + * @return {number} + * @method + * @protected + */ +export const $typeToNumber = (type: IBitmapFilterType): number => +{ + switch (type) { + + case "full": + return 0; + + case "inner": + return 1; + + case "outer": + return 2; + + default: + return 0; + + } +}; \ No newline at end of file diff --git a/packages/filters/src/GlowFilter.test.ts b/packages/filters/src/GlowFilter.test.ts new file mode 100644 index 00000000..db5db99e --- /dev/null +++ b/packages/filters/src/GlowFilter.test.ts @@ -0,0 +1,18 @@ +import { GlowFilter } from "./GlowFilter"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilter.js property test", () => +{ + it("default test success", () => + { + const glowFilter = new GlowFilter(); + expect(glowFilter.color).toBe(0); + expect(glowFilter.alpha).toBe(1); + expect(glowFilter.blurX).toBe(4); + expect(glowFilter.blurY).toBe(4); + expect(glowFilter.strength).toBe(1); + expect(glowFilter.quality).toBe(1); + expect(glowFilter.inner).toBe(false); + expect(glowFilter.knockout).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GlowFilter.ts b/packages/filters/src/GlowFilter.ts index fe5a53e9..cb4872d4 100644 --- a/packages/filters/src/GlowFilter.ts +++ b/packages/filters/src/GlowFilter.ts @@ -1,32 +1,23 @@ +import type { IFilterQuality } from "./interface/IFilterQuality"; import { BitmapFilter } from "./BitmapFilter"; -import { BlurFilter } from "./BlurFilter"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BitmapFilterTypeImpl } from "./interface/BitmapFilterTypeImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; +import { execute as glowFilterToArrayService } from "./GlowFilter/service/GlowFilterToArrayService"; +import { execute as glowFilterToNumberArrayService } from "./GlowFilter/service/GlowFilterToNumberArrayService"; +import { execute as glowFilterCanApplyFilterService } from "./GlowFilter/service/GlowFilterCanApplyFilterService"; +import { execute as glowFilterGetBoundsUseCase } from "./GlowFilter/usecase/GlowFilterGetBoundsUseCase"; import { - $intToB, - $intToG, - $intToR, $clamp, - $toColorInt, - $getArray, - $getBoundsObject -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * GlowFilter クラスを使用すると、表示オブジェクトにグロー効果を適用できます。 - * グローのスタイルには複数のオプションがあり、内側グロー、外側グロー、ノックアウトモードなどがあります。 - * グローフィルターは、distance プロパティと angle プロパティを 0 に設定したドロップシャドウフィルターによく似ています。 + * @description GlowFilter クラスを使用すると、表示オブジェクトにグロー効果を適用できます。 + * グローのスタイルには複数のオプションがあり、内側グロー、外側グロー、ノックアウトモードなどがあります。 + * グローフィルターは、distance プロパティと angle プロパティを 0 に設定したドロップシャドウフィルターによく似ています。 * - * The GlowFilter class lets you apply a glow effect to display objects. - * You have several options for the style of the glow, including inner or outer glow and knockout mode. - * The glow filter is similar to the drop shadow filter with the distance - * and angle properties of the drop shadow filter set to 0. + * The GlowFilter class lets you apply a glow effect to display objects. + * You have several options for the style of the glow, including inner or outer glow and knockout mode. + * The glow filter is similar to the drop shadow filter with the distance + * and angle properties of the drop shadow filter set to 0. * * @class * @memberOf next2d.filters @@ -34,11 +25,69 @@ import { */ export class GlowFilter extends BitmapFilter { - private readonly _$blurFilter: BlurFilter; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public readonly $filterType: number = 6; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurX: number; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurY: number; + + /** + * @type {IFilterQuality} + * @default 1 + * @private + */ + private _$quality: IFilterQuality; + + /** + * @type {number} + * @default 0 + * @private + */ private _$color: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$alpha: number; + + /** + * @type {number} + * @default 1 + * @private + */ private _$strength: number; + + /** + * @type {boolean} + * @default false + * @private + */ private _$inner: boolean; + + /** + * @type {boolean} + * @default false + * @private + */ private _$knockout: boolean; /** @@ -47,7 +96,7 @@ export class GlowFilter extends BitmapFilter * @param {number} [blur_x=6] * @param {number} [blur_y=6] * @param {number} [strength=2] - * @param {int} [quality=1] + * @param {number} [quality=1] * @param {boolean} [inner=false] * @param {boolean} [knockout=false] * @@ -55,57 +104,32 @@ export class GlowFilter extends BitmapFilter * @public */ constructor ( - color: number = 0, alpha: number = 1, - blur_x: number = 4, blur_y: number = 4, - strength: number = 1, quality: FilterQualityImpl = 1, - inner: boolean = false, knockout: boolean = false + color: number = 0, + alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: IFilterQuality = 1, + inner: boolean = false, + knockout: boolean = false ) { super(); - /** - * @type {BlurFilter} - * @default BlurFilter - * @private - */ - this._$blurFilter = new BlurFilter(blur_x, blur_y, quality); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$color = 0; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$alpha = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$strength = 1; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$inner = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$knockout = false; + // default + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + this._$color = 0; + this._$alpha = 1; + this._$strength = 1; + this._$inner = false; + this._$knockout = false; // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; this.color = color; this.alpha = alpha; this.strength = strength; @@ -113,62 +137,6 @@ export class GlowFilter extends BitmapFilter this.knockout = knockout; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class GlowFilter] - * @method - * @static - */ - static toString (): string - { - return "[class GlowFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.GlowFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.GlowFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object GlowFilter] - * @method - * @public - */ - toString (): string - { - return "[object GlowFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.GlowFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.GlowFilter"; - } - /** * @description アルファ透明度の値です。 * The alpha transparency value for the color. @@ -184,10 +152,11 @@ export class GlowFilter extends BitmapFilter set alpha (alpha: number) { alpha = $clamp(+alpha, 0, 1, 0); - if (alpha !== this._$alpha) { - this._$alpha = alpha; - this._$doChanged(); + if (alpha === this._$alpha) { + return ; } + this._$alpha = alpha; + this.$updated = true; } /** @@ -200,11 +169,16 @@ export class GlowFilter extends BitmapFilter */ get blurX (): number { - return this._$blurFilter.blurX; + return this._$blurX; } set blurX (blur_x: number) { - this._$blurFilter.blurX = blur_x; + blur_x = $clamp(+blur_x, 0, 255, 0); + if (blur_x === this._$blurX) { + return ; + } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -217,11 +191,16 @@ export class GlowFilter extends BitmapFilter */ get blurY (): number { - return this._$blurFilter.blurY; + return this._$blurY; } set blurY (blur_y: number) { - this._$blurFilter.blurY = blur_y; + blur_y = $clamp(+blur_y, 0, 255, 0); + if (blur_y === this._$blurY) { + return ; + } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -238,11 +217,17 @@ export class GlowFilter extends BitmapFilter } set color (color: number) { - color = $clamp($toColorInt(color), 0, 0xffffff, 4); - if (color !== this._$color) { - this._$color = color; - this._$doChanged(); + color = $clamp( + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 4 + ); + if (color === this._$color) { + return ; } + this._$color = color; + this.$updated = true; } /** @@ -259,10 +244,12 @@ export class GlowFilter extends BitmapFilter } set inner (inner: boolean) { - if (inner !== this._$inner) { - this._$inner = !!inner; - this._$doChanged(); + inner = !!inner; + if (inner === this._$inner) { + return ; } + this._$inner = inner; + this.$updated = true; } /** @@ -279,10 +266,12 @@ export class GlowFilter extends BitmapFilter } set knockout (knockout: boolean) { - if (knockout !== this._$knockout) { - this._$knockout = !!knockout; - this._$doChanged(); + knockout = !!knockout; + if (knockout === this._$knockout) { + return ; } + this._$knockout = knockout; + this.$updated = true; } /** @@ -293,13 +282,18 @@ export class GlowFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { - return this._$blurFilter.quality; + return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - this._$blurFilter.quality = quality; + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; + } + this._$quality = quality; + this.$updated = true; } /** @@ -317,10 +311,11 @@ export class GlowFilter extends BitmapFilter set strength (strength: number) { strength = $clamp(strength | 0, 0, 255, 0); - if (strength !== this._$strength) { - this._$strength = strength; - this._$doChanged(); + if (strength === this._$strength) { + return ; } + this._$strength = strength; + this.$updated = true; } /** @@ -334,146 +329,61 @@ export class GlowFilter extends BitmapFilter clone (): GlowFilter { return new GlowFilter( - this._$color, this._$alpha, this._$blurFilter.blurX, this._$blurFilter.blurY, - this._$strength, this._$blurFilter.quality, this._$inner, this._$knockout + this._$color, this._$alpha, this._$blurX, this._$blurY, + this._$strength, this._$quality, this._$inner, this._$knockout ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(6, - this._$color, this._$alpha, this._$blurFilter.blurX, this._$blurFilter.blurY, - this._$strength, this._$blurFilter.quality, this._$inner, this._$knockout - ); + return glowFilterToArrayService(this); } /** - * @return {boolean} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method * @public */ - _$isUpdated (): boolean + toNumberArray (): Float32Array { - return this._$updated || this._$blurFilter._$isUpdated(); - } - - /** - * @param {object} bounds - * @param {number} [x_scale=0] - * @param {number} [y_scale=0] - * @return {object} - * @method - * @private - */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, - y_scale: number = 0 - ): BoundsImpl { - - const clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$canApply()) { - return clone; - } - - return this - ._$blurFilter - ._$generateFilterRect(clone, x_scale, y_scale); + return glowFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$alpha > 0 - && this._$strength > 0 - && this._$blurFilter._$canApply(); + return glowFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {array} matrix - * @return {WebGLTexture} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter (context: CanvasToWebGLContext, matrix: Float32Array): WebGLTexture + getBounds (bounds: Float32Array): Float32Array { - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - if (!currentAttachment) { - throw new Error("the current attachment is null."); - } - - this._$updated = false; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - if (!this._$canApply()) { - return manager - .getTextureFromCurrentAttachment(); - } - - const baseWidth: number = currentAttachment.width; - const baseHeight: number = currentAttachment.height; - const baseOffsetX: number = context._$offsetX; - const baseOffsetY: number = context._$offsetY; - - const blurTexture: WebGLTexture = this - ._$blurFilter - ._$applyFilter(context, matrix, false); - - const blurWidth: number = blurTexture.width; - const blurHeight: number = blurTexture.height; - const blurOffsetX: number = context._$offsetX; - const blurOffsetY: number = context._$offsetY; - - const width: number = this._$inner ? baseWidth : blurWidth; - const height: number = this._$inner ? baseHeight : blurHeight; - - const baseTextureX: number = this._$inner ? 0 : blurOffsetX - baseOffsetX; - const baseTextureY: number = this._$inner ? 0 : blurOffsetY - baseOffsetY; - const blurTextureX: number = this._$inner ? -blurOffsetX : 0; - const blurTextureY: number = this._$inner ? -blurOffsetY : 0; - - const type: BitmapFilterTypeImpl = this._$inner - ? "inner" - : "outer"; - - context._$bind(currentAttachment); - context._$applyBitmapFilter( - blurTexture, width, height, - baseWidth, baseHeight, baseTextureX, baseTextureY, - blurWidth, blurHeight, blurTextureX, blurTextureY, - true, type, this._$knockout, - this._$strength, null, null, null, - $intToR(this._$color, this._$alpha, true), - $intToG(this._$color, this._$alpha, true), - $intToB(this._$color, this._$alpha, true), - this._$alpha, - 0, 0, 0, 0 - ); - - context._$offsetX = baseOffsetX + baseTextureX; - context._$offsetY = baseOffsetY + baseTextureY; - - manager.releaseTexture(blurTexture); - - return manager.getTextureFromCurrentAttachment(); + return glowFilterGetBoundsUseCase(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.test.ts b/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..81a33331 --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.test.ts @@ -0,0 +1,36 @@ +import { GlowFilter } from "../../GlowFilter"; +import { execute } from "./GlowFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const glowFilter = new GlowFilter(); + expect(execute(glowFilter)).toBe(true); + + glowFilter.blurX = 0; + expect(execute(glowFilter)).toBe(false); + + glowFilter.blurX = 1; + glowFilter.blurY = 0; + expect(execute(glowFilter)).toBe(false); + + glowFilter.blurY = 1; + glowFilter.quality = 0; + expect(execute(glowFilter)).toBe(false); + + glowFilter.quality = 1; + expect(execute(glowFilter)).toBe(true); + + glowFilter.alpha = 0; + expect(execute(glowFilter)).toBe(false); + + glowFilter.alpha = 1; + glowFilter.strength = 0; + expect(execute(glowFilter)).toBe(false); + + glowFilter.strength = 1; + expect(execute(glowFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.ts b/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.ts new file mode 100644 index 00000000..e19ea3cc --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterCanApplyFilterService.ts @@ -0,0 +1,19 @@ +import type { GlowFilter } from "../../GlowFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {GlowFilter} glow_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (glow_filter: GlowFilter): boolean => +{ + return glow_filter.alpha > 0 + && glow_filter.strength !== 0 + && glow_filter.blurX > 0 + && glow_filter.blurY > 0 + && glow_filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.test.ts b/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.test.ts new file mode 100644 index 00000000..418a878e --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.test.ts @@ -0,0 +1,21 @@ +import { GlowFilter } from "../../GlowFilter"; +import { execute } from "./GlowFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new GlowFilter()); + expect(array.length).toBe(9); + expect(array[0]).toBe(6); + expect(array[1]).toBe(0); + expect(array[2]).toBe(1); + expect(array[3]).toBe(4); + expect(array[4]).toBe(4); + expect(array[5]).toBe(1); + expect(array[6]).toBe(1); + expect(array[7]).toBe(false); + expect(array[8]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.ts b/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.ts new file mode 100644 index 00000000..8d9c0220 --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterToArrayService.ts @@ -0,0 +1,25 @@ +import type { GlowFilter } from "../../GlowFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {GlowFilter} glow_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (glow_filter: GlowFilter): Array => +{ + return [ + glow_filter.$filterType, + glow_filter.color, + glow_filter.alpha, + glow_filter.blurX, + glow_filter.blurY, + glow_filter.strength, + glow_filter.quality, + glow_filter.inner, + glow_filter.knockout + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.test.ts b/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..a8efabd2 --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.test.ts @@ -0,0 +1,21 @@ +import { GlowFilter } from "../../GlowFilter"; +import { execute } from "./GlowFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new GlowFilter()); + expect(array.length).toBe(9); + expect(array[0]).toBe(6); + expect(array[1]).toBe(0); + expect(array[2]).toBe(1); + expect(array[3]).toBe(4); + expect(array[4]).toBe(4); + expect(array[5]).toBe(1); + expect(array[6]).toBe(1); + expect(array[7]).toBe(0); + expect(array[8]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.ts b/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.ts new file mode 100644 index 00000000..a0d53670 --- /dev/null +++ b/packages/filters/src/GlowFilter/service/GlowFilterToNumberArrayService.ts @@ -0,0 +1,25 @@ +import type { GlowFilter } from "../../GlowFilter"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {GlowFilter} glow_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (glow_filter: GlowFilter): Float32Array => +{ + return new Float32Array([ + glow_filter.$filterType, + glow_filter.color, + glow_filter.alpha, + glow_filter.blurX, + glow_filter.blurY, + glow_filter.strength, + glow_filter.quality, + +glow_filter.inner, + +glow_filter.knockout + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.test.ts b/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..4c7778c3 --- /dev/null +++ b/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.test.ts @@ -0,0 +1,17 @@ +import { GlowFilter } from "../../GlowFilter"; +import { execute } from "./GlowFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new GlowFilter(), bounds); + + expect(bounds[0]).toBe(-2); + expect(bounds[1]).toBe(-2); + expect(bounds[2]).toBe(2); + expect(bounds[3]).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.ts b/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..bc8645eb --- /dev/null +++ b/packages/filters/src/GlowFilter/usecase/GlowFilterGetBoundsUseCase.ts @@ -0,0 +1,24 @@ +import type { GlowFilter } from "../../GlowFilter"; +import { execute as glowFilterCanApplyFilterService } from "../service/GlowFilterCanApplyFilterService"; +import { execute as blurFilterGetBoundsUseCase } from "../../BlurFilter/usecase/BlurFilterGetBoundsUseCase"; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {GlowFilter} glow_filter + * @param {Float32Array} bounds + * @return {Float32Array} + */ +export const execute = (glow_filter: GlowFilter, bounds: Float32Array): Float32Array => +{ + if (!glowFilterCanApplyFilterService(glow_filter)) { + return bounds; + } + + if (glow_filter.inner) { + return bounds; + } + + return blurFilterGetBoundsUseCase(glow_filter, bounds); +}; \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter.test.ts b/packages/filters/src/GradientBevelFilter.test.ts new file mode 100644 index 00000000..92ae2f77 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter.test.ts @@ -0,0 +1,21 @@ +import { GradientBevelFilter } from "./GradientBevelFilter"; +import { describe, expect, it } from "vitest"; + +describe("GradientBevelFilter.js property test", () => +{ + it("default test success", () => + { + const gradientBevelFilter = new GradientBevelFilter(); + expect(gradientBevelFilter.distance).toBe(4); + expect(gradientBevelFilter.angle).toBe(45); + expect(gradientBevelFilter.colors).toBe(null); + expect(gradientBevelFilter.alphas).toBe(null); + expect(gradientBevelFilter.ratios).toBe(null); + expect(gradientBevelFilter.blurX).toBe(4); + expect(gradientBevelFilter.blurY).toBe(4); + expect(gradientBevelFilter.strength).toBe(1); + expect(gradientBevelFilter.quality).toBe(1); + expect(gradientBevelFilter.type).toBe("inner"); + expect(gradientBevelFilter.knockout).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter.ts b/packages/filters/src/GradientBevelFilter.ts index bae41e80..45917c9e 100644 --- a/packages/filters/src/GradientBevelFilter.ts +++ b/packages/filters/src/GradientBevelFilter.ts @@ -1,33 +1,24 @@ +import type { IFilterQuality } from "./interface/IFilterQuality"; +import type { IBitmapFilterType } from "./interface/IBitmapFilterType"; import { BitmapFilter } from "./BitmapFilter"; -import { BlurFilter } from "./BlurFilter"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BitmapFilterTypeImpl } from "./interface/BitmapFilterTypeImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; +import { execute as gradientBevelFilterCanApplyFilterService } from "./GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService"; +import { execute as gradientBevelFilterToArrayService } from "./GradientBevelFilter/service/GradientBevelFilterToArrayService"; +import { execute as gradientBevelFilterToNumberArrayService } from "./GradientBevelFilter/service/GradientBevelFilterToNumberArrayService"; +import { execute as gradientBevelFilterGetBoundsUseCase } from "./GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase"; import { - $Array, $clamp, - $Deg2Rad, - $devicePixelRatio, - $getArray, - $getBoundsObject, - $Math, - $toColorInt -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * GradientBevelFilter クラスを使用すると、オブジェクトにグラデーションベベル効果を適用し、表示できます。 - * グラデーションベベルは、オブジェクトの外側、内側、または上側が斜めになったエッジであり、グラデーションカラーで強調されます。 - * 斜めのエッジによってオブジェクトが 3 次元に見えます。 + * @description GradientBevelFilter クラスを使用すると、オブジェクトにグラデーションベベル効果を適用し、表示できます。 + * グラデーションベベルは、オブジェクトの外側、内側、または上側が斜めになったエッジであり、グラデーションカラーで強調されます。 + * 斜めのエッジによってオブジェクトが 3 次元に見えます。 * - * The GradientBevelFilter class lets you apply a gradient bevel effect to display objects. - * A gradient bevel is a beveled edge, enhanced with gradient color, - * on the outside, inside, or top of an object. - * Beveled edges make objects look three-dimensional. + * The GradientBevelFilter class lets you apply a gradient bevel effect to display objects. + * A gradient bevel is a beveled edge, enhanced with gradient color, + * on the outside, inside, or top of an object. + * Beveled edges make objects look three-dimensional. * * @class * @memberOf next2d.filters @@ -35,14 +26,90 @@ import { */ export class GradientBevelFilter extends BitmapFilter { - private _$blurFilter: BlurFilter; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public readonly $filterType: number = 7; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurX: number; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurY: number; + + /** + * @type {IFilterQuality} + * @default 1 + * @private + */ + private _$quality: IFilterQuality; + + /** + * @type {number} + * @default 4 + * @private + */ private _$distance: number; + + /** + * @type {number} + * @default 45 + * @private + */ private _$angle: number; + + /** + * @type {array} + * @default null + * @private + */ private _$colors: number[] | null; + + /** + * @type {array} + * @default null + * @private + */ private _$alphas: number[] | null; + + /** + * @type {array} + * @default null + * @private + */ private _$ratios: number[] | null; + + /** + * @type {number} + * @default 1 + * @private + */ private _$strength: number; - private _$type: BitmapFilterTypeImpl; + + /** + * @type {string} + * @default "inner" + * @private + */ + private _$type: IBitmapFilterType; + + /** + * @type {boolean} + * @default false + * @private + */ private _$knockout: boolean; /** @@ -62,82 +129,38 @@ export class GradientBevelFilter extends BitmapFilter * @public */ constructor ( - distance: number = 4, angle: number = 45, + distance: number = 4, + angle: number = 45, colors: number[] | null = null, alphas: number[] | null = null, ratios: number[] | null = null, - blur_x: number = 4, blur_y: number = 4, strength: number = 1, - quality: FilterQualityImpl = 1, - type: BitmapFilterTypeImpl = "inner", + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: IFilterQuality = 1, + type: IBitmapFilterType = "inner", knockout: boolean = false ) { super(); - /** - * @type {BlurFilter} - * @default BlurFilter - * @private - */ - this._$blurFilter = new BlurFilter(blur_x, blur_y, quality); - - /** - * @type {number} - * @default 4 - * @private - */ - this._$distance = 4; - - /** - * @type {number} - * @default 45 - * @private - */ - this._$angle = 45; - - /** - * @type {array} - * @default null - * @private - */ - this._$colors = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$alphas = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$ratios = null; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$strength = 1; - - /** - * @type {string} - * @default BitmapFilterType.INNER - * @private - */ - this._$type = "inner"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$knockout = false; + // default + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + this._$distance = 4; + this._$angle = 45; + this._$colors = null; + this._$alphas = null; + this._$ratios = null; + this._$strength = 1; + this._$type = "inner"; + this._$knockout = false; // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; this.distance = distance; this.angle = angle; this.colors = colors; @@ -148,62 +171,6 @@ export class GradientBevelFilter extends BitmapFilter this.knockout = knockout; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class GradientBevelFilter] - * @method - * @static - */ - static toString (): string - { - return "[class GradientBevelFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.GradientBevelFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.GradientBevelFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object GradientBevelFilter] - * @method - * @public - */ - toString (): string - { - return "[object GradientBevelFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.GradientBevelFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.GradientBevelFilter"; - } - /** * @description カラー配列内の各色に対応するアルファ透明度の値の配列です。 * An array of alpha transparency values @@ -219,18 +186,17 @@ export class GradientBevelFilter extends BitmapFilter } set alphas (alphas: number[] | null) { - if (alphas !== this._$alphas) { - this._$alphas = alphas; - if ($Array.isArray(alphas)) { - for (let idx: number = 0; idx < alphas.length; ++idx) { - const alpha = alphas[idx]; - alphas[idx] = $clamp(+alpha, 0, 1, 0); - } - - this._$alphas = alphas; + if (alphas === this._$alphas) { + return ; + } + if (Array.isArray(alphas)) { + for (let idx = 0; idx < alphas.length; ++idx) { + const alpha = alphas[idx]; + alphas[idx] = $clamp(+alpha, 0, 1, 0); } - this._$doChanged(); } + this._$alphas = alphas; + this.$updated = true; } /** @@ -248,11 +214,11 @@ export class GradientBevelFilter extends BitmapFilter set angle (angle: number) { angle = $clamp(angle % 360, -360, 360, 45); - if (angle !== this._$angle) { - this._$angle = angle; - this._$doChanged(); + if (angle === this._$angle) { + return ; } - + this._$angle = angle; + this.$updated = true; } /** @@ -265,11 +231,16 @@ export class GradientBevelFilter extends BitmapFilter */ get blurX (): number { - return this._$blurFilter.blurX; + return this._$blurX; } set blurX (blur_x: number) { - this._$blurFilter.blurX = blur_x; + blur_x = $clamp(+blur_x, 0, 255, 0); + if (blur_x === this._$blurX) { + return ; + } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -282,11 +253,16 @@ export class GradientBevelFilter extends BitmapFilter */ get blurY (): number { - return this._$blurFilter.blurY; + return this._$blurY; } set blurY (blur_y: number) { - this._$blurFilter.blurY = blur_y; + blur_y = $clamp(+blur_y, 0, 255, 0); + if (blur_y === this._$blurY) { + return ; + } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -303,20 +279,22 @@ export class GradientBevelFilter extends BitmapFilter } set colors (colors: number[] | null) { - if (this._$colors !== colors) { - this._$colors = colors; - if ($Array.isArray(colors)) { - - for (let idx: number = 0; idx < colors.length; ++idx) { - colors[idx] = $clamp( - $toColorInt(colors[idx]), 0, 0xffffff, 0 - ); - } - - this._$colors = colors; + if (this._$colors === colors) { + return ; + } + if (Array.isArray(colors)) { + for (let idx = 0; idx < colors.length; ++idx) { + const color = colors[idx]; + colors[idx] = $clamp( + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 0 + ); } - this._$doChanged(); } + this._$colors = colors; + this.$updated = true; } /** @@ -334,10 +312,11 @@ export class GradientBevelFilter extends BitmapFilter set distance (distance: number) { distance = $clamp(+distance, -255, 255, 4); - if (distance !== this._$distance) { - this._$distance = distance; - this._$doChanged(); + if (distance === this._$distance) { + return ; } + this._$distance = distance; + this.$updated = true; } /** @@ -354,10 +333,12 @@ export class GradientBevelFilter extends BitmapFilter } set knockout (knockout: boolean) { - if (knockout !== this._$knockout) { - this._$knockout = !!knockout; - this._$doChanged(); + knockout = !!knockout; + if (knockout === this._$knockout) { + return ; } + this._$knockout = knockout; + this.$updated = true; } /** @@ -368,13 +349,18 @@ export class GradientBevelFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { - return this._$blurFilter.quality; + return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - this._$blurFilter.quality = quality; + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; + } + this._$quality = quality; + this.$updated = true; } /** @@ -392,18 +378,18 @@ export class GradientBevelFilter extends BitmapFilter } set ratios (ratios: number[] | null) { - if (this._$ratios !== ratios) { - this._$ratios = ratios; - if ($Array.isArray(ratios)) { - - for (let idx: number = 0; idx < ratios.length; ++idx) { - ratios[idx] = $clamp(+ratios[idx], 0, 255, 0); - } - - this._$ratios = ratios; + if (this._$ratios === ratios) { + return ; + } + if (Array.isArray(ratios)) { + for (let idx = 0; idx < ratios.length; ++idx) { + const ratio = ratios[idx]; + ratios[idx] = $clamp(+ratio, 0, 255, 0); } - this._$doChanged(); } + + this._$ratios = ratios; + this.$updated = true; } /** @@ -421,10 +407,11 @@ export class GradientBevelFilter extends BitmapFilter set strength (strength: number) { strength = $clamp(strength | 0, 0, 255, 0); - if (strength !== this._$strength) { - this._$strength = strength; - this._$doChanged(); + if (strength === this._$strength) { + return ; } + this._$strength = strength; + this.$updated = true; } /** @@ -435,16 +422,17 @@ export class GradientBevelFilter extends BitmapFilter * @default BitmapFilterType.INNER * @public */ - get type (): BitmapFilterTypeImpl + get type (): IBitmapFilterType { return this._$type; } - set type (type: BitmapFilterTypeImpl) + set type (type: IBitmapFilterType) { - if (type !== this._$type) { - this._$type = type; - this._$doChanged(); + if (type === this._$type) { + return ; } + this._$type = type; + this.$updated = true; } /** @@ -462,198 +450,61 @@ export class GradientBevelFilter extends BitmapFilter this._$colors ? this._$colors.slice() : null, this._$alphas ? this._$alphas.slice() : null, this._$ratios ? this._$ratios.slice() : null, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$type, this._$knockout + this._$blurX, this._$blurY, this._$strength, + this._$quality, this._$type, this._$knockout ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] + toArray (): Array { - return $getArray(7, - this._$distance, this._$angle, this._$colors, this._$alphas, this._$ratios, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$type, this._$knockout - ); + return gradientBevelFilterToArrayService(this); } /** - * @return {boolean} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$isUpdated (): boolean + toNumberArray (): Float32Array { - return this._$updated || this._$blurFilter._$isUpdated(); - } - - /** - * @param {object} bounds - * @param {number} [x_scale=0] - * @param {number} [y_scale=0] - * @return {object} - * @method - * @private - */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, - y_scale: number = 0 - ): BoundsImpl { - - let clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$canApply()) { - return clone; - } - - clone = this - ._$blurFilter - ._$generateFilterRect(clone, x_scale, y_scale); - - const radian: number = this._$angle * $Deg2Rad; - - let x: number = $Math.abs($Math.cos(radian) * this._$distance); - let y: number = $Math.abs($Math.sin(radian) * this._$distance); - - if (x_scale) { - x *= x_scale; - } - - if (y_scale) { - y *= y_scale; - } - - clone.xMin = $Math.min(clone.xMin, x); - if (x > 0) { - clone.xMax += x; - } - clone.yMin = $Math.min(clone.yMin, y); - if (y > 0) { - clone.yMax += y; - } - - return clone; + return gradientBevelFilterToNumberArrayService(this); } /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$strength > 0 && this._$distance > 0 - && this._$alphas !== null && this._$ratios !== null && this._$colors !== null - && this._$blurFilter._$canApply(); + return gradientBevelFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {WebGLTexture} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): WebGLTexture { - - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - const baseTexture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - if (!this._$canApply() || !currentAttachment) { - return baseTexture; - } - - const baseWidth: number = currentAttachment.width; - const baseHeight: number = currentAttachment.height; - const baseOffsetX: number = context._$offsetX; - const baseOffsetY: number = context._$offsetY; - - // matrix to scale - let xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - let yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - // pointer - const radian: number = +(this._$angle * $Deg2Rad); - const x: number = +($Math.cos(radian) * this._$distance * xScale); - const y: number = +($Math.sin(radian) * this._$distance * yScale); - - // highlight buffer - const highlightTextureBaseAttachment: AttachmentImpl = manager - .createTextureAttachment(baseWidth, baseHeight); - - context._$bind(highlightTextureBaseAttachment); - - context.reset(); - context.drawImage(baseTexture, 0, 0, baseWidth, baseHeight); - - context.globalCompositeOperation = "erase"; - context.drawImage(baseTexture, x * 2, y * 2, baseWidth, baseHeight); - - const highlightTextureBase = this - ._$blurFilter - ._$applyFilter(context, matrix, false); - - const blurWidth: number = highlightTextureBase.width; - const blurHeight: number = highlightTextureBase.height; - const bevelWidth: number = $Math.ceil(blurWidth + $Math.abs(x) * 2); - const bevelHeight: number = $Math.ceil(blurHeight + $Math.abs(y) * 2); - - // bevel filter buffer - const isInner: boolean = this._$type === "inner"; - const width: number = isInner ? baseWidth : bevelWidth; - const height: number = isInner ? baseHeight : bevelHeight; - - const absX: number = $Math.abs(x); - const absY: number = $Math.abs(y); - const blurOffsetX: number = (blurWidth - baseWidth) / 2; - const blurOffsetY: number = (blurHeight - baseHeight) / 2; - - const baseTextureX = isInner ? 0 : absX + blurOffsetX; - const baseTextureY = isInner ? 0 : absY + blurOffsetY; - const blurTextureX = isInner ? -blurOffsetX - x : absX - x; - const blurTextureY = isInner ? -blurOffsetY - y : absY - y; - - context._$bind(currentAttachment); - context._$applyBitmapFilter( - highlightTextureBase, width, height, - baseWidth, baseHeight, baseTextureX, baseTextureY, - blurWidth, blurHeight, blurTextureX, blurTextureY, - false, this._$type, this._$knockout, - this._$strength, this._$ratios, this._$colors, this._$alphas, - 0, 0, 0, 0, 0, 0, 0, 0 - ); - - context._$offsetX = baseOffsetX + baseTextureX; - context._$offsetY = baseOffsetY + baseTextureY; - - manager.releaseAttachment(highlightTextureBaseAttachment, true); - - return manager.getTextureFromCurrentAttachment(); + getBounds (bounds: Float32Array): Float32Array + { + return gradientBevelFilterGetBoundsUseCase(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.test.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..e7e1804e --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.test.ts @@ -0,0 +1,28 @@ +import { GradientBevelFilter } from "../../GradientBevelFilter"; +import { execute } from "./GradientBevelFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("GradientBevelFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const gradientBevelFilter = new GradientBevelFilter( + 4, 45, [0x000000, 0xFFFFFF], [0, 1], [0, 255], 4, 4, 1 + ); + expect(execute(gradientBevelFilter)).toBe(true); + + gradientBevelFilter.blurX = 0; + expect(execute(gradientBevelFilter)).toBe(false); + + gradientBevelFilter.blurX = 1; + gradientBevelFilter.blurY = 0; + expect(execute(gradientBevelFilter)).toBe(false); + + gradientBevelFilter.blurY = 1; + gradientBevelFilter.quality = 0; + expect(execute(gradientBevelFilter)).toBe(false); + + gradientBevelFilter.quality = 1; + expect(execute(gradientBevelFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.ts new file mode 100644 index 00000000..9d710b52 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterCanApplyFilterService.ts @@ -0,0 +1,22 @@ +import type { GradientBevelFilter } from "../../GradientBevelFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {GradientBevelFilter} gradient_bevel_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (gradient_bevel_filter: GradientBevelFilter): boolean => +{ + return gradient_bevel_filter.strength > 0 + && gradient_bevel_filter.distance > 0 + && gradient_bevel_filter.alphas !== null + && gradient_bevel_filter.ratios !== null + && gradient_bevel_filter.colors !== null + && gradient_bevel_filter.blurX > 0 + && gradient_bevel_filter.blurY > 0 + && gradient_bevel_filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.test.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.test.ts new file mode 100644 index 00000000..392cf739 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.test.ts @@ -0,0 +1,24 @@ +import { GradientBevelFilter } from "../../GradientBevelFilter"; +import { execute } from "./GradientBevelFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GradientBevelFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new GradientBevelFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(7); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(null); + expect(array[4]).toBe(null); + expect(array[5]).toBe(null); + expect(array[6]).toBe(4); + expect(array[7]).toBe(4); + expect(array[8]).toBe(1); + expect(array[9]).toBe(1); + expect(array[10]).toBe("inner"); + expect(array[11]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.ts new file mode 100644 index 00000000..05761f2a --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToArrayService.ts @@ -0,0 +1,28 @@ +import type { GradientBevelFilter } from "../../GradientBevelFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {GradientBevelFilter} gradient_bevel_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (gradient_bevel_filter: GradientBevelFilter): Array => +{ + return [ + gradient_bevel_filter.$filterType, + gradient_bevel_filter.distance, + gradient_bevel_filter.angle, + gradient_bevel_filter.colors, + gradient_bevel_filter.alphas, + gradient_bevel_filter.ratios, + gradient_bevel_filter.blurX, + gradient_bevel_filter.blurY, + gradient_bevel_filter.strength, + gradient_bevel_filter.quality, + gradient_bevel_filter.type, + gradient_bevel_filter.knockout + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.test.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..c3d79fe4 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.test.ts @@ -0,0 +1,102 @@ +import { GradientBevelFilter } from "../../GradientBevelFilter"; +import { execute } from "./GradientBevelFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GradientBevelFilterToNumberArrayService.js test", () => +{ + it("test case1", () => + { + const array = execute(new GradientBevelFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(7); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(0); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0); + expect(array[6]).toBe(4); + expect(array[7]).toBe(4); + expect(array[8]).toBe(1); + expect(array[9]).toBe(1); + expect(array[10]).toBe(1); + expect(array[11]).toBe(0); + }); + + it("test case2", () => + { + const array = execute(new GradientBevelFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "inner" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(7); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(1); + expect(array[17]).toBe(0); + }); + + it("test case3", () => + { + const array = execute(new GradientBevelFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "outer" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(7); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(2); + expect(array[17]).toBe(0); + }); + + it("test case4", () => + { + const array = execute(new GradientBevelFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "full" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(7); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(0); + expect(array[17]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.ts b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.ts new file mode 100644 index 00000000..c83896e9 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/service/GradientBevelFilterToNumberArrayService.ts @@ -0,0 +1,33 @@ +import type { GradientBevelFilter } from "../../GradientBevelFilter"; +import { $typeToNumber } from "../../FilterUtil"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {GradientBevelFilter} gradient_bevel_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (gradient_bevel_filter: GradientBevelFilter): Float32Array => +{ + const colors: number[] = gradient_bevel_filter.colors ? gradient_bevel_filter.colors : []; + const alphas: number[] = gradient_bevel_filter.alphas ? gradient_bevel_filter.alphas : []; + const ratios: number[] = gradient_bevel_filter.ratios ? gradient_bevel_filter.ratios : []; + + return new Float32Array([ + gradient_bevel_filter.$filterType, + gradient_bevel_filter.distance, + gradient_bevel_filter.angle, + colors.length, ...colors, + alphas.length, ...alphas, + ratios.length, ...ratios, + gradient_bevel_filter.blurX, + gradient_bevel_filter.blurY, + gradient_bevel_filter.strength, + gradient_bevel_filter.quality, + $typeToNumber(gradient_bevel_filter.type), + +gradient_bevel_filter.knockout + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.test.ts b/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..1d99ac7d --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.test.ts @@ -0,0 +1,17 @@ +import { GradientBevelFilter } from "../../GradientBevelFilter"; +import { execute } from "./GradientBevelFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("GradientBevelFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new GradientBevelFilter(), bounds); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(0); + expect(bounds[3]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.ts b/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..27367fd9 --- /dev/null +++ b/packages/filters/src/GradientBevelFilter/usecase/GradientBevelFilterGetBoundsUseCase.ts @@ -0,0 +1,37 @@ +import type { GradientBevelFilter } from "../../GradientBevelFilter"; +import { $Deg2Rad } from "../../FilterUtil"; +import { execute as gradientBevelFilterCanApplyFilterService } from "../service/GradientBevelFilterCanApplyFilterService"; +import { execute as blurFilterGetBoundsUseCase } from "../../BlurFilter/usecase/BlurFilterGetBoundsUseCase"; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {GradientBevelFilter} gradient_bevel_filter + * @param {Float32Array} bounds + * @return {Float32Array} + */ +export const execute = (gradient_bevel_filter: GradientBevelFilter, bounds: Float32Array): Float32Array => +{ + if (!gradientBevelFilterCanApplyFilterService(gradient_bevel_filter)) { + return bounds; + } + + if (gradient_bevel_filter.type === "inner") { + return bounds; + } + + blurFilterGetBoundsUseCase(gradient_bevel_filter, bounds); + + const radian = gradient_bevel_filter.angle * $Deg2Rad; + const distance = gradient_bevel_filter.distance; + const x = Math.abs(Math.cos(radian) * distance); + const y = Math.abs(Math.sin(radian) * distance); + + bounds[0] -= x; + bounds[2] += x; + bounds[1] -= y; + bounds[3] += y; + + return bounds; +}; \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter.test.ts b/packages/filters/src/GradientGlowFilter.test.ts new file mode 100644 index 00000000..2503d982 --- /dev/null +++ b/packages/filters/src/GradientGlowFilter.test.ts @@ -0,0 +1,21 @@ +import { GradientGlowFilter } from "./GradientGlowFilter"; +import { describe, expect, it } from "vitest"; + +describe("GradientGlowFilter.js property test", () => +{ + it("default test success", () => + { + const gradientGlowFilter = new GradientGlowFilter(); + expect(gradientGlowFilter.distance).toBe(4); + expect(gradientGlowFilter.angle).toBe(45); + expect(gradientGlowFilter.colors).toBe(null); + expect(gradientGlowFilter.alphas).toBe(null); + expect(gradientGlowFilter.ratios).toBe(null); + expect(gradientGlowFilter.blurX).toBe(4); + expect(gradientGlowFilter.blurY).toBe(4); + expect(gradientGlowFilter.strength).toBe(1); + expect(gradientGlowFilter.quality).toBe(1); + expect(gradientGlowFilter.type).toBe("outer"); + expect(gradientGlowFilter.knockout).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter.ts b/packages/filters/src/GradientGlowFilter.ts index 7023f14d..d120370f 100644 --- a/packages/filters/src/GradientGlowFilter.ts +++ b/packages/filters/src/GradientGlowFilter.ts @@ -1,31 +1,23 @@ +import type { IFilterQuality } from "./interface/IFilterQuality"; +import type { IBitmapFilterType } from "./interface/IBitmapFilterType"; import { BitmapFilter } from "./BitmapFilter"; -import { BlurFilter } from "./BlurFilter"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { FilterQualityImpl } from "./interface/FilterQualityImpl"; -import type { BitmapFilterTypeImpl } from "./interface/BitmapFilterTypeImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; +import { execute as gradientGlowFilterToArrayService } from "./GradientGlowFilter/service/GradientGlowFilterToArrayService"; +import { execute as gradientGlowFilterCanApplyFilterService } from "./GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService"; +import { execute as gradientGlowFilterToNumberArrayService } from "./GradientGlowFilter/service/GradientGlowFilterToNumberArrayService"; +import { execute as gradientGlowFilterGetBoundsUseCase } from "./GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase"; import { - $Array, $clamp, - $Deg2Rad, - $getArray, $getBoundsObject, - $Math, - $toColorInt, - $devicePixelRatio -} from "@next2d/share"; + $convertColorStringToNumber +} from "./FilterUtil"; /** - * GradientGlowFilter クラスを使用すると、表示オブジェクトにグラデーショングロー効果を適用できます。 - * グラデーショングローとは、制御可能なカラーグラデーションによるリアルな輝きです。 - * グラデーショングローは、オブジェクトの内側エッジや外側エッジの周囲、またはオブジェクトの上に適用できます。 + * @description GradientGlowFilter クラスを使用すると、表示オブジェクトにグラデーショングロー効果を適用できます。 + * グラデーショングローとは、制御可能なカラーグラデーションによるリアルな輝きです。 + * グラデーショングローは、オブジェクトの内側エッジや外側エッジの周囲、またはオブジェクトの上に適用できます。 * - * The GradientGlowFilter class lets you apply a gradient glow effect to display objects. - * A gradient glow is a realistic-looking glow with a color gradient that you can control. - * You can apply a gradient glow around the inner or outer edge of an object or on top of an object. + * The GradientGlowFilter class lets you apply a gradient glow effect to display objects. + * A gradient glow is a realistic-looking glow with a color gradient that you can control. + * You can apply a gradient glow around the inner or outer edge of an object or on top of an object. * * @class * @memberOf next2d.filters @@ -33,14 +25,90 @@ import { */ export class GradientGlowFilter extends BitmapFilter { - private _$blurFilter: BlurFilter; + /** + * @description フィルター認識番号 + * Filter Recognition Number + * + * @member {number} + * @public + */ + public readonly $filterType: number = 8; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurX: number; + + /** + * @type {number} + * @default 4 + * @private + */ + private _$blurY: number; + + /** + * @type {IFilterQuality} + * @default 1 + * @private + */ + private _$quality: IFilterQuality; + + /** + * @type {number} + * @default 4 + * @private + */ private _$distance: number; + + /** + * @type {number} + * @default 45 + * @private + */ private _$angle: number; + + /** + * @type {array} + * @default null + * @private + */ private _$colors: number[] | null; + + /** + * @type {array} + * @default null + * @private + */ private _$alphas: number[] | null; + + /** + * @type {array} + * @default null + * @private + */ private _$ratios: number[] | null; + + /** + * @type {number} + * @default 1 + * @private + */ private _$strength: number; - private _$type: BitmapFilterTypeImpl; + + /** + * @type {string} + * @default "outer" + * @private + */ + private _$type: IBitmapFilterType; + + /** + * @type {boolean} + * @default false + * @private + */ private _$knockout: boolean; /** @@ -53,89 +121,45 @@ export class GradientGlowFilter extends BitmapFilter * @param {number} [blur_y=4.0] * @param {number} [strength=1] * @param {number} [quality=1] - * @param {string} [type=BitmapFilterType.INNER] + * @param {string} [type=BitmapFilterType.OUTER] * @param {boolean} [knockout=false] * * @constructor * @public */ constructor ( - distance: number = 4, angle: number = 45, + distance: number = 4, + angle: number = 45, colors: number[] | null = null, alphas: number[] | null = null, ratios: number[] | null = null, - blur_x: number = 4, blur_y: number = 4, strength: number = 1, - quality: FilterQualityImpl = 1, - type: BitmapFilterTypeImpl = "inner", + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: IFilterQuality = 1, + type: IBitmapFilterType = "outer", knockout: boolean = false ) { super(); - /** - * @type {BlurFilter} - * @default BlurFilter - * @private - */ - this._$blurFilter = new BlurFilter(blur_x, blur_y, quality); - - /** - * @type {number} - * @default 4 - * @private - */ - this._$distance = 4; - - /** - * @type {number} - * @default 45 - * @private - */ - this._$angle = 45; - - /** - * @type {array} - * @default null - * @private - */ - this._$colors = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$alphas = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$ratios = null; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$strength = 1; - - /** - * @type {string} - * @default BitmapFilterType.INNER - * @private - */ - this._$type = "inner"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$knockout = false; + // default + this._$blurX = 4; + this._$blurY = 4; + this._$quality = 1; + this._$distance = 4; + this._$angle = 45; + this._$colors = null; + this._$alphas = null; + this._$ratios = null; + this._$strength = 1; + this._$type = "outer"; + this._$knockout = false; // setup + this.blurX = blur_x; + this.blurY = blur_y; + this.quality = quality; this.distance = distance; this.angle = angle; this.colors = colors; @@ -146,62 +170,6 @@ export class GradientGlowFilter extends BitmapFilter this.knockout = knockout; } - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class GradientGlowFilter] - * @method - * @static - */ - static toString (): string - { - return "[class GradientGlowFilter]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.filters.GradientGlowFilter - * @const - * @static - */ - static get namespace (): string - { - return "next2d.filters.GradientGlowFilter"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object GradientGlowFilter] - * @method - * @public - */ - toString (): string - { - return "[object GradientGlowFilter]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.filters.GradientGlowFilter - * @const - * @public - */ - get namespace (): string - { - return "next2d.filters.GradientGlowFilter"; - } - /** * @description カラー配列内の各色に対応するアルファ透明度の値の配列です。 * An array of alpha transparency values @@ -217,18 +185,17 @@ export class GradientGlowFilter extends BitmapFilter } set alphas (alphas: number[] | null) { - if (alphas !== this._$alphas) { - this._$alphas = alphas; - if ($Array.isArray(alphas)) { - - for (let idx: number = 0; idx < alphas.length; ++idx) { - alphas[idx] = $clamp(+alphas[idx], 0, 1, 0); - } - - this._$alphas = alphas; + if (alphas === this._$alphas) { + return ; + } + if (Array.isArray(alphas)) { + for (let idx = 0; idx < alphas.length; ++idx) { + const alpha = alphas[idx]; + alphas[idx] = $clamp(+alpha, 0, 1, 0); } - this._$doChanged(); } + this._$alphas = alphas; + this.$updated = true; } /** @@ -246,10 +213,11 @@ export class GradientGlowFilter extends BitmapFilter set angle (angle: number) { angle = $clamp(angle % 360, -360, 360, 45); - if (angle !== this._$angle) { - this._$angle = angle; - this._$doChanged(); + if (angle === this._$angle) { + return ; } + this._$angle = angle; + this.$updated = true; } /** @@ -262,11 +230,16 @@ export class GradientGlowFilter extends BitmapFilter */ get blurX (): number { - return this._$blurFilter.blurX; + return this._$blurX; } set blurX (blur_x: number) { - this._$blurFilter.blurX = blur_x; + blur_x = $clamp(+blur_x, 0, 255, 0); + if (blur_x === this._$blurX) { + return ; + } + this._$blurX = blur_x; + this.$updated = true; } /** @@ -279,11 +252,16 @@ export class GradientGlowFilter extends BitmapFilter */ get blurY (): number { - return this._$blurFilter.blurY; + return this._$blurY; } set blurY (blur_y: number) { - this._$blurFilter.blurY = blur_y; + blur_y = $clamp(+blur_y, 0, 255, 0); + if (blur_y === this._$blurY) { + return ; + } + this._$blurY = blur_y; + this.$updated = true; } /** @@ -300,20 +278,22 @@ export class GradientGlowFilter extends BitmapFilter } set colors (colors: number[] | null) { - if (this._$colors !== colors) { - this._$colors = colors; - if ($Array.isArray(colors)) { - - for (let idx: number = 0; idx < colors.length; ++idx) { - colors[idx] = $clamp( - $toColorInt(colors[idx]), 0, 0xffffff, 0 - ); - } - - this._$colors = colors; + if (this._$colors === colors) { + return ; + } + if (Array.isArray(colors)) { + for (let idx = 0; idx < colors.length; ++idx) { + const color = colors[idx]; + colors[idx] = $clamp( + typeof color === "string" + ? $convertColorStringToNumber(color) + : color + , 0, 0xffffff, 0 + ); } - this._$doChanged(); } + this._$colors = colors; + this.$updated = true; } /** @@ -331,10 +311,11 @@ export class GradientGlowFilter extends BitmapFilter set distance (distance: number) { distance = $clamp(+distance, -255, 255, 4); - if (distance !== this._$distance) { - this._$distance = distance; - this._$doChanged(); + if (distance === this._$distance) { + return ; } + this._$distance = distance; + this.$updated = true; } /** @@ -351,10 +332,12 @@ export class GradientGlowFilter extends BitmapFilter } set knockout (knockout: boolean) { - if (knockout !== this._$knockout) { - this._$knockout = !!knockout; - this._$doChanged(); + knockout = !!knockout; + if (knockout === this._$knockout) { + return ; } + this._$knockout = knockout; + this.$updated = true; } /** @@ -365,13 +348,18 @@ export class GradientGlowFilter extends BitmapFilter * @default 1 * @public */ - get quality (): FilterQualityImpl + get quality (): IFilterQuality { - return this._$blurFilter.quality; + return this._$quality; } - set quality (quality: FilterQualityImpl) + set quality (quality: IFilterQuality) { - this._$blurFilter.quality = quality; + quality = $clamp(quality | 0, 0, 15, 1) as IFilterQuality; + if (quality === this._$quality) { + return ; + } + this._$quality = quality; + this.$updated = true; } /** @@ -389,18 +377,16 @@ export class GradientGlowFilter extends BitmapFilter } set ratios (ratios: number[] | null) { - if (this._$ratios !== ratios) { - this._$ratios = ratios; - if ($Array.isArray(ratios)) { - - for (let idx: number = 0; idx < ratios.length; ++idx) { - ratios[idx] = $clamp(+ratios[idx], 0, 255, 0); - } - - this._$ratios = ratios; + if (this._$ratios === ratios) { + return ; + } + if (Array.isArray(ratios)) { + for (let idx = 0; idx < ratios.length; ++idx) { + ratios[idx] = $clamp(+ratios[idx], 0, 255, 0); } - this._$doChanged(); } + this._$ratios = ratios; + this.$updated = true; } /** @@ -418,10 +404,11 @@ export class GradientGlowFilter extends BitmapFilter set strength (strength: number) { strength = $clamp(strength | 0, 0, 255, 0); - if (strength !== this._$strength) { - this._$strength = strength; - this._$doChanged(); + if (strength === this._$strength) { + return ; } + this._$strength = strength; + this.$updated = true; } /** @@ -432,16 +419,17 @@ export class GradientGlowFilter extends BitmapFilter * @default BitmapFilterType.INNER * @public */ - get type (): BitmapFilterTypeImpl + get type (): IBitmapFilterType { return this._$type; } - set type (type: BitmapFilterTypeImpl) + set type (type: IBitmapFilterType) { - if (type !== this._$type) { - this._$type = type; - this._$doChanged(); + if (type === this._$type) { + return ; } + this._$type = type; + this.$updated = true; } /** @@ -456,185 +444,61 @@ export class GradientGlowFilter extends BitmapFilter { return new GradientGlowFilter( this._$distance, this._$angle, this._$colors, this._$alphas, this._$ratios, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$type, this._$knockout + this._$blurX, this._$blurY, this._$strength, + this._$quality, this._$type, this._$knockout ); } /** + * @description 設定されたフィルターの値を配列で返します。 + * Returns the value of the specified filter as an array. + * * @return {array} * @method * @public */ - _$toArray (): any[] - { - return $getArray(8, - this._$distance, this._$angle, this._$colors, this._$alphas, this._$ratios, - this._$blurFilter.blurX, this._$blurFilter.blurY, this._$strength, - this._$blurFilter.quality, this._$type, this._$knockout - ); - } - - /** - * @return {boolean} - * @method - * @private - */ - _$isUpdated (): boolean + toArray (): Array { - return this._$updated || this._$blurFilter._$isUpdated(); + return gradientGlowFilterToArrayService(this); } /** - * @param {object} bounds - * @param {number} [x_scale=0] - * @param {number} [y_scale=0] - * @return {object} + * @description 設定されたフィルターの値を数値配列で返します。 + * Returns the value of the specified filter as a number array. + * + * @return {Float32Array} * @method - * @private + * @public */ - _$generateFilterRect ( - bounds: BoundsImpl, - x_scale: number = 0, - y_scale: number = 0 - ): BoundsImpl + toNumberArray (): Float32Array { - let clone: BoundsImpl = $getBoundsObject( - bounds.xMin, bounds.xMax, - bounds.yMin, bounds.yMax - ); - - if (!this._$canApply()) { - return clone; - } - - clone = this - ._$blurFilter - ._$generateFilterRect(clone, x_scale, y_scale); - - const radian: number = this._$angle * $Deg2Rad; - - let x: number = $Math.abs($Math.cos(radian) * this._$distance); - let y: number = $Math.abs($Math.sin(radian) * this._$distance); - - if (x_scale) { - x *= x_scale; - } - - if (y_scale) { - y *= y_scale; - } - - clone.xMin = $Math.min(clone.xMin, x); - if (x > 0) { - clone.xMax += x; - } - clone.yMin = $Math.min(clone.yMin, y); - if (y > 0) { - clone.yMax += y; - } - - return clone; + return gradientGlowFilterToNumberArrayService(this); } /** - /** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * * @return {boolean} * @method - * @private + * @public */ - _$canApply (): boolean + canApplyFilter (): boolean { - return this._$strength > 0 && this._$distance > 0 - && this._$alphas !== null && this._$ratios !== null && this._$colors !== null - && this._$blurFilter._$canApply(); + return gradientGlowFilterCanApplyFilterService(this); } /** - * @param {CanvasToWebGLContext} context - * @param {array} matrix - * @return {WebGLTexture} + * @description フィルターの描画範囲のバウンディングボックスを返します。 + * Returns the bounding box of the filter drawing area. + * + * @param {Float32Array} bounds + * @return {Float32Array} * @method - * @private + * @public */ - _$applyFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): WebGLTexture { - - this._$updated = false; - - const manager: FrameBufferManager = context.frameBuffer; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - // reset - context.setTransform(1, 0, 0, 1, 0, 0); - - if (!this._$canApply() || !currentAttachment) { - return manager.getTextureFromCurrentAttachment(); - } - - const baseWidth: number = currentAttachment.width; - const baseHeight: number = currentAttachment.height; - const baseOffsetX: number = context._$offsetX; - const baseOffsetY: number = context._$offsetY; - - const blurTexture: WebGLTexture = this - ._$blurFilter - ._$applyFilter(context, matrix, false); - - const blurWidth: number = blurTexture.width; - const blurHeight: number = blurTexture.height; - const blurOffsetX: number = context._$offsetX; - const blurOffsetY: number = context._$offsetY; - - const offsetDiffX: number = blurOffsetX - baseOffsetX; - const offsetDiffY: number = blurOffsetY - baseOffsetY; - - // matrix to scale - let xScale: number = $Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); - let yScale: number = $Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - // shadow point - const radian: number = +(this._$angle * $Deg2Rad); - const x: number = +($Math.cos(radian) * this._$distance * xScale); - const y: number = +($Math.sin(radian) * this._$distance * yScale); - - const isInner: boolean = this.type === "inner"; - const w: number = isInner ? baseWidth : blurWidth + $Math.max(0, $Math.abs(x) - offsetDiffX); - const h: number = isInner ? baseHeight : blurHeight + $Math.max(0, $Math.abs(y) - offsetDiffY); - const width: number = $Math.ceil(w); - const height: number = $Math.ceil(h); - const fractionX: number = (width - w) / 2; - const fractionY: number = (height - h) / 2; - - const baseTextureX = isInner ? 0 : $Math.max(0, offsetDiffX - x) + fractionX; - const baseTextureY = isInner ? 0 : $Math.max(0, offsetDiffY - y) + fractionY; - const blurTextureX = isInner ? x - blurOffsetX : (x > 0 ? $Math.max(0, x - offsetDiffX) : 0) + fractionX; - const blurTextureY = isInner ? y - blurOffsetY : (y > 0 ? $Math.max(0, y - offsetDiffY) : 0) + fractionY; - - context._$bind(currentAttachment); - context._$applyBitmapFilter( - blurTexture, width, height, - baseWidth, baseHeight, baseTextureX, baseTextureY, - blurWidth, blurHeight, blurTextureX, blurTextureY, - true, this._$type, this._$knockout, - this._$strength, this._$ratios, this._$colors, this._$alphas, - 0, 0, 0, 0, 0, 0, 0, 0 - ); - - context._$offsetX = baseOffsetX + baseTextureX; - context._$offsetY = baseOffsetY + baseTextureY; - - manager.releaseTexture(blurTexture); - - return manager.getTextureFromCurrentAttachment(); + getBounds (bounds: Float32Array): Float32Array + { + return gradientGlowFilterGetBoundsUseCase(this, bounds); } -} +} \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.test.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.test.ts new file mode 100644 index 00000000..fcafd71e --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.test.ts @@ -0,0 +1,28 @@ +import { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute } from "./GradientGlowFilterCanApplyFilterService"; +import { describe, expect, it } from "vitest"; + +describe("GradientGlowFilterCanApplyFilterService.js test", () => +{ + it("test case", () => + { + const gradientGlowFilter = new GradientGlowFilter( + 4, 45, [0x000000, 0xFFFFFF], [0, 1], [0, 255], 4, 4, 1 + ); + expect(execute(gradientGlowFilter)).toBe(true); + + gradientGlowFilter.blurX = 0; + expect(execute(gradientGlowFilter)).toBe(false); + + gradientGlowFilter.blurX = 1; + gradientGlowFilter.blurY = 0; + expect(execute(gradientGlowFilter)).toBe(false); + + gradientGlowFilter.blurY = 1; + gradientGlowFilter.quality = 0; + expect(execute(gradientGlowFilter)).toBe(false); + + gradientGlowFilter.quality = 1; + expect(execute(gradientGlowFilter)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.ts new file mode 100644 index 00000000..cfa13f39 --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterCanApplyFilterService.ts @@ -0,0 +1,22 @@ +import type { GradientGlowFilter } from "../../GradientGlowFilter"; + +/** + * @description フィルターを適用できるかどうかを返します。 + * Returns whether the filter can be applied. + * + * @param {GradientGlowFilter} gradient_glow_filter + * @return {boolean} + * @method + * @private + */ +export const execute = (gradient_glow_filter: GradientGlowFilter): boolean => +{ + return gradient_glow_filter.strength > 0 + && gradient_glow_filter.distance > 0 + && gradient_glow_filter.alphas !== null + && gradient_glow_filter.ratios !== null + && gradient_glow_filter.colors !== null + && gradient_glow_filter.blurX > 0 + && gradient_glow_filter.blurY > 0 + && gradient_glow_filter.quality > 0; +}; \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.test.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.test.ts new file mode 100644 index 00000000..7799296e --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.test.ts @@ -0,0 +1,24 @@ +import { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute } from "./GradientGlowFilterToArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GradientGlowFilterToArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new GradientGlowFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(8); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(null); + expect(array[4]).toBe(null); + expect(array[5]).toBe(null); + expect(array[6]).toBe(4); + expect(array[7]).toBe(4); + expect(array[8]).toBe(1); + expect(array[9]).toBe(1); + expect(array[10]).toBe("outer"); + expect(array[11]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.ts new file mode 100644 index 00000000..60bea381 --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToArrayService.ts @@ -0,0 +1,28 @@ +import type { GradientGlowFilter } from "../../GradientGlowFilter"; + +/** + * @description フィルターの設定値を配列で返却します。 + * Returns an array of filter settings. + * + * @param {GradientGlowFilter} gradient_glow_filter + * @return {Array} + * @method + * @protected + */ +export const execute = (gradient_glow_filter: GradientGlowFilter): Array => +{ + return [ + gradient_glow_filter.$filterType, + gradient_glow_filter.distance, + gradient_glow_filter.angle, + gradient_glow_filter.colors, + gradient_glow_filter.alphas, + gradient_glow_filter.ratios, + gradient_glow_filter.blurX, + gradient_glow_filter.blurY, + gradient_glow_filter.strength, + gradient_glow_filter.quality, + gradient_glow_filter.type, + gradient_glow_filter.knockout + ]; +}; \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.test.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.test.ts new file mode 100644 index 00000000..09c4b78e --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.test.ts @@ -0,0 +1,102 @@ +import { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute } from "./GradientGlowFilterToNumberArrayService"; +import { describe, expect, it } from "vitest"; + +describe("GradientGlowFilterToNumberArrayService.js test", () => +{ + it("test case", () => + { + const array = execute(new GradientGlowFilter()); + expect(array.length).toBe(12); + expect(array[0]).toBe(8); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(0); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0); + expect(array[6]).toBe(4); + expect(array[7]).toBe(4); + expect(array[8]).toBe(1); + expect(array[9]).toBe(1); + expect(array[10]).toBe(2); + expect(array[11]).toBe(0); + }); + + it("test case2", () => + { + const array = execute(new GradientGlowFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "inner" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(8); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(1); + expect(array[17]).toBe(0); + }); + + it("test case3", () => + { + const array = execute(new GradientGlowFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "outer" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(8); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(2); + expect(array[17]).toBe(0); + }); + + it("test case4", () => + { + const array = execute(new GradientGlowFilter( + 4, 45, [0, 0xffffff], [0, 1], [0, 255], 4, 4, 1, 1, "full" + )); + expect(array.length).toBe(18); + expect(array[0]).toBe(8); + expect(array[1]).toBe(4); + expect(array[2]).toBe(45); + expect(array[3]).toBe(2); + expect(array[4]).toBe(0); + expect(array[5]).toBe(0xffffff); + expect(array[6]).toBe(2); + expect(array[7]).toBe(0); + expect(array[8]).toBe(1); + expect(array[9]).toBe(2); + expect(array[10]).toBe(0); + expect(array[11]).toBe(255); + expect(array[12]).toBe(4); + expect(array[13]).toBe(4); + expect(array[14]).toBe(1); + expect(array[15]).toBe(1); + expect(array[16]).toBe(0); + expect(array[17]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.ts b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.ts new file mode 100644 index 00000000..4f6ff52d --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/service/GradientGlowFilterToNumberArrayService.ts @@ -0,0 +1,33 @@ +import type { GradientGlowFilter } from "../../GradientGlowFilter"; +import { $typeToNumber } from "../../FilterUtil"; + +/** + * @description フィルターの設定値を数値配列で返却します。 + * Returns a numeric array of filter settings. + * + * @param {GradientGlowFilter} gradient_glow_filter + * @return {Float32Array} + * @method + * @protected + */ +export const execute = (gradient_glow_filter: GradientGlowFilter): Float32Array => +{ + const colors: number[] = gradient_glow_filter.colors ? gradient_glow_filter.colors : []; + const alphas: number[] = gradient_glow_filter.alphas ? gradient_glow_filter.alphas : []; + const ratios: number[] = gradient_glow_filter.ratios ? gradient_glow_filter.ratios : []; + + return new Float32Array([ + gradient_glow_filter.$filterType, + gradient_glow_filter.distance, + gradient_glow_filter.angle, + colors.length, ...colors, + alphas.length, ...alphas, + ratios.length, ...ratios, + gradient_glow_filter.blurX, + gradient_glow_filter.blurY, + gradient_glow_filter.strength, + gradient_glow_filter.quality, + $typeToNumber(gradient_glow_filter.type), + +gradient_glow_filter.knockout + ]); +}; \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.test.ts b/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.test.ts new file mode 100644 index 00000000..f2447716 --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.test.ts @@ -0,0 +1,17 @@ +import { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute } from "./GradientGlowFilterGetBoundsUseCase"; +import { describe, expect, it } from "vitest"; + +describe("GlowFilterGetBoundsUseCase.js test", () => +{ + it("test case1", () => + { + const bounds = new Float32Array([0, 0, 0, 0]); + execute(new GradientGlowFilter(), bounds); + + expect(bounds[0]).toBe(0); + expect(bounds[1]).toBe(0); + expect(bounds[2]).toBe(0); + expect(bounds[3]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.ts b/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.ts new file mode 100644 index 00000000..e655673a --- /dev/null +++ b/packages/filters/src/GradientGlowFilter/usecase/GradientGlowFilterGetBoundsUseCase.ts @@ -0,0 +1,24 @@ +import type { GradientGlowFilter } from "../../GradientGlowFilter"; +import { execute as gradientGlowFilterCanApplyFilterService } from "../service/GradientGlowFilterCanApplyFilterService"; +import { execute as blurFilterGetBoundsUseCase } from "../../BlurFilter/usecase/BlurFilterGetBoundsUseCase"; + +/** + * @description Filterの描画後の描画範囲を返却 + * Returns the drawing range after drawing Filter + * + * @param {GradientGlowFilter} gradient_glow_filter + * @param {Float32Array} bounds + * @return {Float32Array} + */ +export const execute = (gradient_glow_filter: GradientGlowFilter, bounds: Float32Array): Float32Array => +{ + if (!gradientGlowFilterCanApplyFilterService(gradient_glow_filter)) { + return bounds; + } + + if (gradient_glow_filter.type === "inner") { + return bounds; + } + + return blurFilterGetBoundsUseCase(gradient_glow_filter, bounds); +}; \ No newline at end of file diff --git a/packages/filters/src/interface/AttachmentImpl.ts b/packages/filters/src/interface/AttachmentImpl.ts deleted file mode 100644 index 34e91f64..00000000 --- a/packages/filters/src/interface/AttachmentImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AttachmentImpl { - width: number; - height: number; - color: WebGLTexture | WebGLRenderbuffer | null; - texture: WebGLTexture | null; - msaa: boolean; - stencil: WebGLRenderbuffer | null; - mask: boolean; - clipLevel: number; - isActive: boolean; -} \ No newline at end of file diff --git a/packages/filters/src/interface/BitmapDataChannelImpl.ts b/packages/filters/src/interface/BitmapDataChannelImpl.ts deleted file mode 100644 index 3887fd72..00000000 --- a/packages/filters/src/interface/BitmapDataChannelImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type BitmapDataChannelImpl = 8 | 4 | 2 | 1 | 0; \ No newline at end of file diff --git a/packages/filters/src/interface/BitmapFilterTypeImpl.ts b/packages/filters/src/interface/BitmapFilterTypeImpl.ts deleted file mode 100644 index 0410c69b..00000000 --- a/packages/filters/src/interface/BitmapFilterTypeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type BitmapFilterTypeImpl = "full" | "inner" | "outer"; \ No newline at end of file diff --git a/packages/filters/src/interface/BoundsImpl.ts b/packages/filters/src/interface/BoundsImpl.ts deleted file mode 100644 index 7af56a28..00000000 --- a/packages/filters/src/interface/BoundsImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BoundsImpl { - xMin: number; - xMax: number; - yMin: number; - yMax: number; -} \ No newline at end of file diff --git a/packages/filters/src/interface/DisplacementMapFilterModeImpl.ts b/packages/filters/src/interface/DisplacementMapFilterModeImpl.ts deleted file mode 100644 index 105eff8c..00000000 --- a/packages/filters/src/interface/DisplacementMapFilterModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type DisplacementMapFilterModeImpl = "clamp" | "color" | "ignore" | "wrap"; \ No newline at end of file diff --git a/packages/filters/src/interface/FilterQualityImpl.ts b/packages/filters/src/interface/FilterQualityImpl.ts deleted file mode 100644 index 08c532cb..00000000 --- a/packages/filters/src/interface/FilterQualityImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type FilterQualityImpl = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15; \ No newline at end of file diff --git a/packages/filters/src/interface/IBitmapDataChannel.ts b/packages/filters/src/interface/IBitmapDataChannel.ts new file mode 100644 index 00000000..4b069164 --- /dev/null +++ b/packages/filters/src/interface/IBitmapDataChannel.ts @@ -0,0 +1 @@ +export type IBitmapDataChannel = 8 | 4 | 2 | 1 | 0; \ No newline at end of file diff --git a/packages/filters/src/interface/IBitmapFilterType.ts b/packages/filters/src/interface/IBitmapFilterType.ts new file mode 100644 index 00000000..040aee61 --- /dev/null +++ b/packages/filters/src/interface/IBitmapFilterType.ts @@ -0,0 +1 @@ +export type IBitmapFilterType = "full" | "inner" | "outer"; \ No newline at end of file diff --git a/packages/filters/src/interface/IBounds.ts b/packages/filters/src/interface/IBounds.ts new file mode 100644 index 00000000..b20c1ee3 --- /dev/null +++ b/packages/filters/src/interface/IBounds.ts @@ -0,0 +1,6 @@ +export interface IBounds { + xMin: number; + xMax: number; + yMin: number; + yMax: number; +} \ No newline at end of file diff --git a/packages/filters/src/interface/IDisplacementMapFilterMode.ts b/packages/filters/src/interface/IDisplacementMapFilterMode.ts new file mode 100644 index 00000000..712fd429 --- /dev/null +++ b/packages/filters/src/interface/IDisplacementMapFilterMode.ts @@ -0,0 +1 @@ +export type IDisplacementMapFilterMode = "clamp" | "color" | "ignore" | "wrap"; \ No newline at end of file diff --git a/packages/filters/src/interface/IFilterQuality.ts b/packages/filters/src/interface/IFilterQuality.ts new file mode 100644 index 00000000..cb9ae5b5 --- /dev/null +++ b/packages/filters/src/interface/IFilterQuality.ts @@ -0,0 +1 @@ +export type IFilterQuality = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15; \ No newline at end of file diff --git a/packages/geom/package.json b/packages/geom/package.json index 31c09f82..a3707893 100644 --- a/packages/geom/package.json +++ b/packages/geom/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/geom", "version": "*", - "description": "Next2D Geom Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Geom Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -30,13 +22,5 @@ "repository": { "type": "git", "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/interface": "file:../interface", - "@next2d/display": "file:../display", - "@next2d/core": "file:../core", - "@next2d/util": "file:../util", - "@next2d/filters": "file:../filters", - "@next2d/share": "file:../share" } } diff --git a/packages/geom/src/ColorTransform.test.ts b/packages/geom/src/ColorTransform.test.ts new file mode 100644 index 00000000..88f3ac23 --- /dev/null +++ b/packages/geom/src/ColorTransform.test.ts @@ -0,0 +1,19 @@ +import { ColorTransform } from "./ColorTransform"; +import { describe, expect, it } from "vitest"; + +describe("ColorTransform.js property test", () => +{ + it("test case1", () => { + + const colorTransform = new ColorTransform(0.1, 0.2, 0.3, 0.4, 1, 2, 3, 4); + + expect(colorTransform.redMultiplier).toBe(0.10000000149011612); + expect(colorTransform.greenMultiplier).toBe(0.20000000298023224); + expect(colorTransform.blueMultiplier).toBe(0.30000001192092896); + expect(colorTransform.alphaMultiplier).toBe(0.4000000059604645); + expect(colorTransform.redOffset).toBe(1); + expect(colorTransform.greenOffset).toBe(2); + expect(colorTransform.blueOffset).toBe(3); + expect(colorTransform.alphaOffset).toBe(4); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/ColorTransform.ts b/packages/geom/src/ColorTransform.ts index d3b02a6c..4a6c2f04 100644 --- a/packages/geom/src/ColorTransform.ts +++ b/packages/geom/src/ColorTransform.ts @@ -1,40 +1,43 @@ -import { $getColorTransform } from "@next2d/util"; +import { execute as colorTransformConcatService } from "../src/ColorTransform/service/ColorTransformConcatService"; import { $getFloat32Array8, - $clamp, - $multiplicationColor, - $poolFloat32Array8 -} from "@next2d/share"; + $poolFloat32Array8, + $clamp +} from "./GeomUtil"; /** - * ColorTransform クラスを使用すると、表示オブジェクトのカラー値を調整することができます。 - * カラー調整、つまり "カラー変換" は、赤、緑、青、アルファ透明度の 4 つのチャンネルすべてに適用できます。 - *
    - *
  • 新しい red 値 = (古い red 値 * redMultiplier ) + redOffset
  • - *
  • 新しい green 値 = (古い green 値 * greenMultiplier ) + greenOffset
  • - *
  • 新しい blue 値 = (古い blue 値 * blueMultiplier ) + blueOffset
  • - *
  • 新しい alpha 値 = (古い alpha 値 * alphaMultiplier ) + alphaOffset
  • - *
- * 算出後、カラーチャンネル値が 255 よりも大きい場合は 255 に設定されます。 - * 0 より小さい場合は 0 に設定されます。 + * @description ColorTransform クラスを使用すると、表示オブジェクトのカラー値を調整することができます。 + * カラー調整、つまり "カラー変換" は、赤、緑、青、アルファ透明度の 4 つのチャンネルすべてに適用できます。 + *
    + *
  • 新しい red 値 = (古い red 値 * redMultiplier ) + redOffset
  • + *
  • 新しい green 値 = (古い green 値 * greenMultiplier ) + greenOffset
  • + *
  • 新しい blue 値 = (古い blue 値 * blueMultiplier ) + blueOffset
  • + *
  • 新しい alpha 値 = (古い alpha 値 * alphaMultiplier ) + alphaOffset
  • + *
+ * 算出後、カラーチャンネル値が 255 よりも大きい場合は 255 に設定されます。 + * 0 より小さい場合は 0 に設定されます。 * - * The ColorTransform class lets you adjust the color values in a display object. - * The color adjustment or color transformation can be applied - * to all four channels: red, green, blue, and alpha transparency. - *
    - *
  • New red value = (old red value * redMultiplier) + redOffset
  • - *
  • New green value = (old green value * greenMultiplier) + greenOffset
  • - *
  • New blue value = (old blue value * blueMultiplier) + blueOffset
  • - *
  • New alpha value = (old alpha value * alphaMultiplier) + alphaOffset
  • - *
- * If any of the color channel values is greater than 255 after the calculation, - * it is set to 255. If it is less than 0, it is set to 0. + * The ColorTransform class lets you adjust the color values in a display object. + * The color adjustment or color transformation can be applied + * to all four channels: red, green, blue, and alpha transparency. + *
    + *
  • New red value = (old red value * redMultiplier) + redOffset
  • + *
  • New green value = (old green value * greenMultiplier) + greenOffset
  • + *
  • New blue value = (old blue value * blueMultiplier) + blueOffset
  • + *
  • New alpha value = (old alpha value * alphaMultiplier) + alphaOffset
  • + *
+ * If any of the color channel values is greater than 255 after the calculation, + * it is set to 255. If it is less than 0, it is set to 0. * * @class * @memberOf next2d.geom */ export class ColorTransform { + /** + * @type {Float32Array} + * @private + */ public readonly _$colorTransform: Float32Array; /** @@ -47,15 +50,6 @@ export class ColorTransform * @param {number} [blue_offset=0] * @param {number} [alpha_offset=0] * - * @example Example usage of ColorTransform. - * // new ColorTransform - * const {ColorTransform} = next2d.geom; - * const colorTransform = new ColorTransform(); - * // set new ColorTransform - * const {MovieClip} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.transform.colorTransform = colorTransform; - * * @constructor * @public */ @@ -65,84 +59,23 @@ export class ColorTransform red_offset: number = 0, green_offset: number = 0, blue_offset: number = 0, alpha_offset: number = 0 ) { - - /** - * @type {Float32Array} - * @private - */ - this._$colorTransform = $getFloat32Array8(); - - // setup - this.redMultiplier = red_multiplier; - this.greenMultiplier = green_multiplier; - this.blueMultiplier = blue_multiplier; - this.alphaMultiplier = alpha_multiplier; - this.redOffset = red_offset; - this.greenOffset = green_offset; - this.blueOffset = blue_offset; - this.alphaOffset = alpha_offset; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class ColorTransform] - * @method - * @static - */ - static toString (): string - { - return "[class ColorTransform]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.geom.ColorTransform - * @const - * @static - */ - static get namespace (): string - { - return "next2d.geom.ColorTransform"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return "(redMultiplier=" + this._$colorTransform[0] + ", " + - "greenMultiplier=" + this._$colorTransform[1] + ", " + - "blueMultiplier=" + this._$colorTransform[2] + ", " + - "alphaMultiplier=" + this._$colorTransform[3] + ", " + - "redOffset=" + this._$colorTransform[4] + ", " + - "greenOffset=" + this._$colorTransform[5] + ", " + - "blueOffset=" + this._$colorTransform[6] + ", " + - "alphaOffset=" + this._$colorTransform[7] + ")"; + this._$colorTransform = $getFloat32Array8( + red_multiplier, green_multiplier, blue_multiplier, alpha_multiplier, + red_offset, green_offset, blue_offset, alpha_offset + ); } /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description ColorTransform の内部Float32Arrayデータを返却 + * Returns the internal Float32Array data of ColorTransform * - * @member {string} - * @default next2d.geom.ColorTransform - * @const + * @member {Float32Array} + * @readonly * @public */ - get namespace (): string + get rawData (): Float32Array { - return "next2d.geom.ColorTransform"; + return this._$colorTransform; } /** @@ -159,7 +92,7 @@ export class ColorTransform } set alphaMultiplier (alpha_multiplier: number) { - this._$colorTransform[3] = $clamp(+alpha_multiplier, 0, 1, 0); + this._$colorTransform[3] = $clamp(alpha_multiplier, 0, 1, 1); } /** @@ -178,7 +111,7 @@ export class ColorTransform } set alphaOffset (alpha_offset: number) { - this._$colorTransform[7] = $clamp(alpha_offset | 0, -255, 255, 0); + this._$colorTransform[7] = $clamp(alpha_offset, -255, 255, 0); } /** @@ -195,7 +128,7 @@ export class ColorTransform } set blueMultiplier (blue_multiplier: number) { - this._$colorTransform[2] = $clamp(+blue_multiplier, 0, 1, 0); + this._$colorTransform[2] = $clamp(blue_multiplier, 0, 1, 1); } /** @@ -214,7 +147,7 @@ export class ColorTransform } set blueOffset (blue_offset: number) { - this._$colorTransform[6] = $clamp(blue_offset | 0, -255, 255, 0); + this._$colorTransform[6] = $clamp(blue_offset, -255, 255, 0); } /** @@ -231,7 +164,7 @@ export class ColorTransform } set greenMultiplier (green_multiplier: number) { - this._$colorTransform[1] = $clamp(+green_multiplier, 0, 1, 0); + this._$colorTransform[1] = $clamp(green_multiplier, 0, 1, 1); } /** @@ -250,7 +183,7 @@ export class ColorTransform } set greenOffset (green_offset: number) { - this._$colorTransform[5] = $clamp(green_offset | 0, -255, 255, 0); + this._$colorTransform[5] = $clamp(green_offset, -255, 255, 0); } /** @@ -267,7 +200,7 @@ export class ColorTransform } set redMultiplier (red_multiplier: number) { - this._$colorTransform[0] = $clamp(+red_multiplier, 0, 1, 0); + this._$colorTransform[0] = $clamp(red_multiplier, 0, 1, 1); } /** @@ -286,7 +219,20 @@ export class ColorTransform } set redOffset (red_offset: number) { - this._$colorTransform[4] = $clamp(red_offset | 0, -255, 255, 0); + this._$colorTransform[4] = $clamp(red_offset, -255, 255, 0); + } + + /** + * @description オブジェクトの複製を返します。 + * Returns a copy of this ColorTransform object. + * + * @return {ColorTransform} + * @method + * @public + */ + clone (): ColorTransform + { + return new ColorTransform(...this._$colorTransform); } /** @@ -298,43 +244,50 @@ export class ColorTransform * and sets the current object as the result, * which is an additive combination of the two color transformations. * - * @param {ColorTransform} second - ColorTransformオブジェクト + * @param {ColorTransform} color_transform * @return {void} * @method * @public */ - concat (second: ColorTransform): void + concat (color_transform: ColorTransform): void { - const multiColor = $multiplicationColor( - this._$colorTransform, - second._$colorTransform - ); - - // update - this.redMultiplier = multiColor[0]; - this.greenMultiplier = multiColor[1]; - this.blueMultiplier = multiColor[2]; - this.alphaMultiplier = multiColor[3]; - this.redOffset = multiColor[4]; - this.greenOffset = multiColor[5]; - this.blueOffset = multiColor[6]; - this.alphaOffset = multiColor[7]; - - $poolFloat32Array8(multiColor); + colorTransformConcatService(this, color_transform); } /** - * @return {ColorTransform} + * @description 指定された配列の値を乗算します + * Multiplies the value of the specified array. + * + * @param {Float32Array} a + * @param {Float32Array} b + * @return {Float32Array} * @method * @private */ - _$clone (): ColorTransform + static multiply (a: Float32Array, b: Float32Array): Float32Array { - return $getColorTransform( - this._$colorTransform[0], this._$colorTransform[1], - this._$colorTransform[2], this._$colorTransform[3], - this._$colorTransform[4], this._$colorTransform[5], - this._$colorTransform[6], this._$colorTransform[7] + return $getFloat32Array8( + a[0] * b[0], + a[1] * b[1], + a[2] * b[2], + a[3] * b[3], + a[0] * b[4] + a[4], + a[1] * b[5] + a[5], + a[2] * b[6] + a[6], + a[3] * b[7] + a[7] ); } + + /** + * @description 利用したFloat32Arrayを再利用する為にプールします。 + * Pool the Float32Array used for reuse. + * + * @param {Float32Array} buffer + * @method + * @private + */ + static release (buffer: Float32Array): void + { + $poolFloat32Array8(buffer); + } } diff --git a/packages/geom/src/ColorTransform/service/ColorTransformConcatService.test.ts b/packages/geom/src/ColorTransform/service/ColorTransformConcatService.test.ts new file mode 100644 index 00000000..fdb30597 --- /dev/null +++ b/packages/geom/src/ColorTransform/service/ColorTransformConcatService.test.ts @@ -0,0 +1,69 @@ +import { ColorTransform } from "../../ColorTransform"; +import { describe, expect, it } from "vitest"; + +describe("ColorTransform.js concat test", () => +{ + it("concat test case1", () => + { + const colorTransform1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 50, 100, 150, 200); + const colorTransform2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); + colorTransform1.concat(colorTransform2); + + expect(colorTransform1.redMultiplier).toBe(0.08999999612569809); + expect(colorTransform1.greenMultiplier).toBe(0.1600000113248825); + expect(colorTransform1.blueMultiplier).toBe(0.21000000834465027); + expect(colorTransform1.alphaMultiplier).toBe(0.30000001192092896); + expect(colorTransform1.redOffset).toBe(24.5); + expect(colorTransform1.greenOffset).toBe(60); + expect(colorTransform1.blueOffset).toBe(105); + expect(colorTransform1.alphaOffset).toBe(150); + }); + + it("concat test case2", () => + { + const colorTransform1 = new ColorTransform(100, 0.2, 0.3, 0.5, 50, 100, 150, 200); + const colorTransform2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); + colorTransform1.concat(colorTransform2); + + expect(colorTransform1.redMultiplier).toBe(90); + expect(colorTransform1.greenMultiplier).toBe(0.1600000113248825); + expect(colorTransform1.blueMultiplier).toBe(0.21000000834465027); + expect(colorTransform1.alphaMultiplier).toBe(0.30000001192092896); + expect(colorTransform1.redOffset).toBe(-25450); + expect(colorTransform1.greenOffset).toBe(60); + expect(colorTransform1.blueOffset).toBe(105); + expect(colorTransform1.alphaOffset).toBe(150); + }); + + it("concat test case3", () => + { + const colorTransform1 = new ColorTransform(0.1, 0.2, 0.3, 0.5, 5000, 100, 150, 200); + const colorTransform2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); + colorTransform1.concat(colorTransform2); + + expect(colorTransform1.redMultiplier).toBe(0.08999999612569809); + expect(colorTransform1.greenMultiplier).toBe(0.1600000113248825); + expect(colorTransform1.blueMultiplier).toBe(0.21000000834465027); + expect(colorTransform1.alphaMultiplier).toBe(0.30000001192092896); + expect(colorTransform1.redOffset).toBe(4974.5); + expect(colorTransform1.greenOffset).toBe(60); + expect(colorTransform1.blueOffset).toBe(105); + expect(colorTransform1.alphaOffset).toBe(150); + }); + + it("concat test case4", () => + { + const colorTransform1 = new ColorTransform(0, -9, 0.3, 0.5, 50, 100, 150, 200); + const colorTransform2 = new ColorTransform(0.9, 0.8, 0.7, 0.6, -255, -200, -150, -100); + colorTransform1.concat(colorTransform2); + + expect(colorTransform1.redMultiplier).toBe(0); + expect(colorTransform1.greenMultiplier).toBe(-7.200000286102295); + expect(colorTransform1.blueMultiplier).toBe(0.21000000834465027); + expect(colorTransform1.alphaMultiplier).toBe(0.30000001192092896); + expect(colorTransform1.redOffset).toBe(50); + expect(colorTransform1.greenOffset).toBe(1900); + expect(colorTransform1.blueOffset).toBe(105); + expect(colorTransform1.alphaOffset).toBe(150); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/ColorTransform/service/ColorTransformConcatService.ts b/packages/geom/src/ColorTransform/service/ColorTransformConcatService.ts new file mode 100644 index 00000000..e4e2fff6 --- /dev/null +++ b/packages/geom/src/ColorTransform/service/ColorTransformConcatService.ts @@ -0,0 +1,35 @@ +import { ColorTransform } from "../../ColorTransform"; + +/** + * @description 指定のColorTransformを連結 + * Concatenate the specified ColorTransform + * + * @param {ColorTransform} color_transform1 + * @param {ColorTransform} color_transform2 + * @return {void} + * @method + * @public + */ +export const execute = ( + color_transform1: ColorTransform, + color_transform2: ColorTransform +): void => { + + const multiColor = ColorTransform.multiply( + color_transform1._$colorTransform, + color_transform2._$colorTransform + ); + + // update + color_transform1._$colorTransform[0] = multiColor[0]; + color_transform1._$colorTransform[1] = multiColor[1]; + color_transform1._$colorTransform[2] = multiColor[2]; + color_transform1._$colorTransform[3] = multiColor[3]; + color_transform1._$colorTransform[4] = multiColor[4]; + color_transform1._$colorTransform[5] = multiColor[5]; + color_transform1._$colorTransform[6] = multiColor[6]; + color_transform1._$colorTransform[7] = multiColor[7]; + + // pool + ColorTransform.release(multiColor); +}; \ No newline at end of file diff --git a/packages/geom/src/GeomUtil.ts b/packages/geom/src/GeomUtil.ts new file mode 100644 index 00000000..2df5e9a5 --- /dev/null +++ b/packages/geom/src/GeomUtil.ts @@ -0,0 +1,132 @@ +/** + * @type {Float32Array[]} + * @private + */ +const $objectPool6: Float32Array[] = []; + +/** + * @description オブジェクトプールから Float32Array オブジェクトを取得します。 + * Get a Float32Array object from the object pool. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @return {Float32Array} + * @method + * @private + */ +export const $getFloat32Array6 = ( + f0: number = 1, f1: number = 0, + f2: number = 0, f3: number = 1, + f4: number = 0, f5: number = 0 +): Float32Array => { + + const array: Float32Array = $objectPool6.pop() || new Float32Array(6); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + + return array; +}; + +/** + * @description 再利用する為に、オブジェクトプールに Float32Array オブジェクトを追加します。 + * Add a Float32Array object to the object pool for reuse. + * + * @param {Float32Array} array + * @method + * @private + */ +export const $poolFloat32Array6 = (array: Float32Array): void => +{ + $objectPool6.push(array); +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $objectPool8: Float32Array[] = []; + +/** + * @description オブジェクトプールから Float32Array オブジェクトを取得します。 + * Get a Float32Array object from the object pool. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @param {number} [f6=0] + * @param {number} [f7=0] + * @return {Float32Array} + * @method + * @private + */ +export const $getFloat32Array8 = ( + f0: number = 1, f1: number = 1, + f2: number = 1, f3: number = 1, + f4: number = 0, f5: number = 0, + f6: number = 0, f7: number = 0 +): Float32Array => { + + const array: Float32Array = $objectPool8.pop() || new Float32Array(8); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + array[6] = f6; + array[7] = f7; + + return array; +}; + +/** + * @description 再利用する為に、オブジェクトプールに Float32Array オブジェクトを追加します。 + * Add a Float32Array object to the object pool for reuse. + * + * @param {Float32Array} array + * @method + * @private + */ +export const $poolFloat32Array8 = (array: Float32Array): void => +{ + $objectPool8.push(array); +}; + +/** + * @description 値が最小値と最大値の間に収まるように調整します。 + * Adjust the value so that it falls between the minimum and maximum values. + * + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @static + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix.ts b/packages/geom/src/Matrix.ts index 03fe1671..c0648325 100644 --- a/packages/geom/src/Matrix.ts +++ b/packages/geom/src/Matrix.ts @@ -1,33 +1,33 @@ import { Point } from "./Point"; -import { $getMatrix } from "@next2d/util"; +import { execute as matrixCloneService } from "../src/Matrix/service/MatrixCloneService"; +import { execute as matirxConcatService } from "../src/Matrix/service/MatirxConcatService"; +import { execute as matrixCopyFromService } from "../src/Matrix/service/MatrixCopyFromService"; +import { execute as matrixCreateBoxService } from "../src/Matrix/service/MatrixCreateBoxService"; +import { execute as matrixCreateGradientBoxService } from "../src/Matrix/service/MatrixCreateGradientBoxService"; +import { execute as matrixDeltaTransformPointService } from "../src/Matrix/service/MatrixDeltaTransformPointService"; +import { execute as matrixIdentityService } from "../src/Matrix/service/MatrixIdentityService"; +import { execute as matrixInvertService } from "../src/Matrix/service/MatrixInvertService"; +import { execute as matrixRotateService } from "../src/Matrix/service/MatrixRotateService"; +import { execute as matrixScaleService } from "../src/Matrix/service/MatrixScaleService"; +import { execute as matrixSetToService } from "../src/Matrix/service/MatrixSetToService"; +import { execute as matrixTransformPointService } from "../src/Matrix/service/MatrixTransformPointService"; +import { execute as matrixTranslateService } from "../src/Matrix/service/MatrixTranslateService"; import { $getFloat32Array6, - $clamp, - $Math, - $SHORT_INT_MIN, - $SHORT_INT_MAX -} from "@next2d/share"; + $poolFloat32Array6 +} from "./GeomUtil"; /** - * Matrix クラスは、2 つの座標空間の間におけるポイントのマッピング方法を決定する変換マトリックスを表します。 - * Matrix オブジェクトのプロパティを設定し、Matrix オブジェクトを Transform オブジェクトの matrix プロパティに適用し、 - * 次に Transform オブジェクトを表示オブジェクトの transform プロパティとして適用することで、表示オブジェクトに対する各種グラフィック変換を実行できます。 - * これらの変換機能には、平行移動(x と y の位置変更)、回転、拡大 / 縮小、傾斜などが含まれます。 + * @description Matrix クラスは、2 つの座標空間の間におけるポイントのマッピング方法を決定する変換マトリックスを表します。 + * Matrix オブジェクトのプロパティを設定し、Matrix オブジェクトを Transform オブジェクトの matrix プロパティに適用し、 + * 次に Transform オブジェクトを表示オブジェクトの transform プロパティとして適用することで、表示オブジェクトに対する各種グラフィック変換を実行できます。 + * これらの変換機能には、平行移動(x と y の位置変更)、回転、拡大 / 縮小、傾斜などが含まれます。 * - * The Matrix class represents a transformation matrix that determines how to map points from one coordinate space to another. - * You can perform various graphical transformations on a display object by setting the properties of a Matrix object, - * applying that Matrix object to the matrix property of a Transform object, - * and then applying that Transform object as the transform property of the display object. - * These transformation functions include translation (x and y repositioning), rotation, scaling, and skewing. - * - * @example Example usage of Matrix. - * // new Matrix - * const {Matrix} = next2d.geom; - * const matrix = new Matrix(); - * // set new Matrix - * const {MovieClip} = next2d.display; - * const movieClip = new MovieClip(); - * movieClip.transform.matrix = matrix; + * The Matrix class represents a transformation matrix that determines how to map points from one coordinate space to another. + * You can perform various graphical transformations on a display object by setting the properties of a Matrix object, + * applying that Matrix object to the matrix property of a Transform object, + * and then applying that Transform object as the transform property of the display object. + * These transformation functions include translation (x and y repositioning), rotation, scaling, and skewing. * * @class * @memberOf next2d.geom @@ -56,70 +56,7 @@ export class Matrix * @type {Float32Array} * @private */ - this._$matrix = $getFloat32Array6(1, 0, 0, 1, 0, 0); - - // setup - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tx = tx; - this.ty = ty; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Matrix] - * @method - * @static - */ - static toString (): string - { - return "[class Matrix]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.geom.Matrix - * @const - * @static - */ - static get namespace (): string - { - return "next2d.geom.Matrix"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return `(a=${this.a}, b=${this.b}, c=${this.c}, d=${this.d}, tx=${this.tx}, ty=${this.ty})`; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.geom.Matrix - * @const - * @public - */ - get namespace (): string - { - return "next2d.geom.Matrix"; + this._$matrix = $getFloat32Array6(a, b, c, d, tx, ty); } /** @@ -137,7 +74,7 @@ export class Matrix } set a (a: number) { - this._$matrix[0] = $clamp(+a, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[0] = a; } /** @@ -155,7 +92,7 @@ export class Matrix } set b (b: number) { - this._$matrix[1] = $clamp(+b, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[1] = b; } /** @@ -173,7 +110,7 @@ export class Matrix } set c (c: number) { - this._$matrix[2] = $clamp(+c, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[2] = c; } /** @@ -191,7 +128,7 @@ export class Matrix } set d (d: number) { - this._$matrix[3] = $clamp(+d, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[3] = d; } /** @@ -208,7 +145,7 @@ export class Matrix } set tx (tx: number) { - this._$matrix[4] = $clamp(+tx, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[4] = tx; } /** @@ -225,17 +162,20 @@ export class Matrix } set ty (ty: number) { - this._$matrix[5] = $clamp(+ty, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + this._$matrix[5] = ty; } /** - * @return {Matrix} - * @method - * @private + * @description Matrixの内部Float32Arrayデータを返却 + * Returns the internal Float32Array data of Matrix + * + * @member {Float32Array} + * @readonly + * @public */ - _$clone (): Matrix + get rawData (): Float32Array { - return this.clone(); + return this._$matrix; } /** @@ -250,11 +190,7 @@ export class Matrix */ clone (): Matrix { - return $getMatrix( - this._$matrix[0], this._$matrix[1], - this._$matrix[2], this._$matrix[3], - this._$matrix[4], this._$matrix[5] - ); + return matrixCloneService(this); } /** @@ -263,56 +199,28 @@ export class Matrix * Concatenates a matrix with the current matrix, * effectively combining the geometric effects of the two. * - * @param {Matrix} m + * @param {Matrix} matrix * @return {void} * @method * @public */ - concat (m: Matrix): void + concat (matrix: Matrix): void { - const matrix = this._$matrix; - const target = m._$matrix; - - let a = matrix[0] * target[0]; - let b = 0.0; - let c = 0.0; - let d = matrix[3] * target[3]; - let tx = matrix[4] * target[0] + target[4]; - let ty = matrix[5] * target[3] + target[5]; - - if (matrix[1] || matrix[2] || target[1] || target[2]) { - a += matrix[1] * target[2]; - d += matrix[2] * target[1]; - b += matrix[0] * target[1] + matrix[1] * target[3]; - c += matrix[2] * target[0] + matrix[3] * target[2]; - tx += matrix[5] * target[2]; - ty += matrix[4] * target[1]; - } - - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tx = tx; - this.ty = ty; + matirxConcatService(this, matrix); } /** * @description すべてのマトリックスデータを、ソース Matrix オブジェクトから、 * 呼び出し元の Matrix オブジェクトにコピーします。 * - * @param {Matrix} source_matrix - * @method + * @param {Matrix} matrix * @return {void} + * @method + * @public */ - copyFrom (source_matrix: Matrix): void + copyFrom (matrix: Matrix): void { - this.a = source_matrix.a; - this.b = source_matrix.b; - this.c = source_matrix.c; - this.d = source_matrix.d; - this.tx = source_matrix.tx; - this.ty = source_matrix.ty; + matrixCopyFromService(this, matrix); } /** @@ -333,10 +241,7 @@ export class Matrix rotation: number = 0, tx: number = 0, ty: number = 0 ): void { - this.identity(); - this.rotate(rotation); - this.scale(scale_x, scale_y); - this.translate(tx, ty); + matrixCreateBoxService(this, scale_x, scale_y, rotation, tx, ty); } /** @@ -358,25 +263,7 @@ export class Matrix rotation: number = 0, tx: number = 0, ty: number = 0 ): void { - - this.a = width / 1638.4; - this.d = height / 1638.4; - - if (rotation) { - const cos = $Math.cos(rotation); - const sin = $Math.sin(rotation); - - this.b = sin * this.d; - this.c = -sin * this.a; - this.a *= cos; - this.d *= cos; - } else { - this.b = 0; - this.c = 0; - } - - this.tx = tx + width / 2; - this.ty = ty + height / 2; + matrixCreateGradientBoxService(this, width, height, rotation, tx, ty); } /** @@ -391,10 +278,7 @@ export class Matrix */ deltaTransformPoint (point: Point): Point { - return new Point( - point.x * this._$matrix[0] + point.y * this._$matrix[2], - point.x * this._$matrix[1] + point.y * this._$matrix[3] - ); + return matrixDeltaTransformPointService(this, point); } /** @@ -407,12 +291,7 @@ export class Matrix */ identity (): void { - this._$matrix[0] = 1; - this._$matrix[1] = 0; - this._$matrix[2] = 0; - this._$matrix[3] = 1; - this._$matrix[4] = 0; - this._$matrix[5] = 0; + matrixIdentityService(this); } /** @@ -425,40 +304,7 @@ export class Matrix */ invert (): void { - const a: number = this._$matrix[0]; - const b: number = this._$matrix[1]; - const c: number = this._$matrix[2]; - const d: number = this._$matrix[3]; - const tx: number = this._$matrix[4]; - const ty: number = this._$matrix[5]; - - if (b === 0 && c === 0) { - - this.a = 1 / a; - this.b = 0; - this.c = 0; - this.d = 1 / d; - this.tx = -this.a * tx; - this.ty = -this.d * ty; - - } else { - - const det = a * d - b * c; - - if (det) { - - const rdet: number = 1 / det; - - this.a = d * rdet; - this.b = -b * rdet; - this.c = -c * rdet; - this.d = a * rdet; - this.tx = -(this.a * tx + this.c * ty); - this.ty = -(this.b * tx + this.d * ty); - - } - - } + matrixInvertService(this); } /** @@ -472,40 +318,22 @@ export class Matrix */ rotate (rotation: number): void { - const a = this._$matrix[0]; - const b = this._$matrix[1]; - const c = this._$matrix[2]; - const d = this._$matrix[3]; - const tx = this._$matrix[4]; - const ty = this._$matrix[5]; - - this.a = a * $Math.cos(rotation) - b * $Math.sin(rotation); - this.b = a * $Math.sin(rotation) + b * $Math.cos(rotation); - this.c = c * $Math.cos(rotation) - d * $Math.sin(rotation); - this.d = c * $Math.sin(rotation) + d * $Math.cos(rotation); - this.tx = tx * $Math.cos(rotation) - ty * $Math.sin(rotation); - this.ty = tx * $Math.sin(rotation) + ty * $Math.cos(rotation); + matrixRotateService(this, rotation); } /** * @description 行列に拡大 / 縮小の変換を適用します。 * Applies a scaling transformation to the matrix. * - * @param {number} sx - * @param {number} sy + * @param {number} scale_x + * @param {number} scale_y * @return {void} * @method * @public */ - scale (sx: number, sy: number): void + scale (scale_x: number, scale_y: number): void { - this.a *= sx; - this.c *= sx; - this.tx *= sx; - - this.b *= sy; - this.d *= sy; - this.ty *= sy; + matrixScaleService(this, scale_x, scale_y); } /** @@ -527,12 +355,7 @@ export class Matrix c: number, d: number, tx: number, ty: number ): void { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.tx = tx; - this.ty = ty; + matrixSetToService(this, a, b, c, d, tx, ty); } /** @@ -547,10 +370,7 @@ export class Matrix */ transformPoint (point: Point): Point { - return new Point( - point.x * this._$matrix[0] + point.y * this._$matrix[2] + this._$matrix[4], - point.x * this._$matrix[1] + point.y * this._$matrix[3] + this._$matrix[5] - ); + return matrixTransformPointService(this, point); } /** @@ -567,7 +387,44 @@ export class Matrix */ translate (dx: number, dy: number): void { - this.tx += dx; - this.ty += dy; + matrixTranslateService(this, dx, dy); + } + + /** + * @description 指定された配列の値を乗算します + * Multiplies the value of the specified array. + * + * @param {Float32Array} a + * @param {Float32Array} b + * @return {Float32Array} + * @method + * @private + */ + static multiply (a: Float32Array, b: Float32Array): Float32Array + { + const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5]; + const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5]; + + return $getFloat32Array6( + a0 * b0 + a2 * b1, + a1 * b0 + a3 * b1, + a0 * b2 + a2 * b3, + a1 * b2 + a3 * b3, + a0 * b4 + a2 * b5 + a4, + a1 * b4 + a3 * b5 + a5 + ); + } + + /** + * @description 利用したFloat32Arrayを再利用する為にプールします。 + * Pool the Float32Array used for reuse. + * + * @param {Float32Array} buffer + * @method + * @private + */ + static release (buffer: Float32Array): void + { + $poolFloat32Array6(buffer); } -} +} \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatirxConcatService.test.ts b/packages/geom/src/Matrix/service/MatirxConcatService.test.ts new file mode 100644 index 00000000..031a856b --- /dev/null +++ b/packages/geom/src/Matrix/service/MatirxConcatService.test.ts @@ -0,0 +1,117 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js concat test", () => +{ + it("concat test case1", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(2.5999999046325684); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(-1.2999999523162842); + expect(matrix1.d).toBe(-2.25); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-17.5); + }); + + it("concat test case2", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(0, 0.75, 0, -1.5, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(0); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(0); + expect(matrix1.d).toBe(-2.25); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-17.5); + }); + + it("concat test case3", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(1.3, 0, 0, -1.5, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(2.5999999046325684); + expect(matrix1.b).toBe(-1.5); + expect(matrix1.c).toBe(-1.2999999523162842); + expect(matrix1.d).toBe(-1.5); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-17.5); + }); + + it("concat test case4", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(1.3, 0.75, 0, 0, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(2.5999999046325684); + expect(matrix1.b).toBe(1.5); + expect(matrix1.c).toBe(-1.2999999523162842); + expect(matrix1.d).toBe(-0.75); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-10); + }); + + it("concat test case5", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(1.3, 0.75, 0, -1.5, 0, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(2.5999999046325684); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(-1.2999999523162842); + expect(matrix1.d).toBe(-2.25); + expect(matrix1.tx).toBe(0); + expect(matrix1.ty).toBe(-17.5); + }); + + it("concat test case6", () => + { + const matrix1 = new Matrix(2, 1, -1, 1, 0, 5); + const matrix2 = new Matrix(1.3, 0.75, 0, -1.5, 10, 0); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(2.5999999046325684); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(-1.2999999523162842); + expect(matrix1.d).toBe(-2.25); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-7.5); + }); + + it("concat test case7", () => + { + const matrix1 = new Matrix(1,0,0,1,0,0); + const matrix2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(1.2999999523162842); + expect(matrix1.b).toBe(0.75); + expect(matrix1.c).toBe(0); + expect(matrix1.d).toBe(-1.5); + expect(matrix1.tx).toBe(10); + expect(matrix1.ty).toBe(-10); + }); + + it("concat test case8", () => + { + const matrix1 = new Matrix(1,0,0,1,10,10); + const matrix2 = new Matrix(1.3, 0.75, 0, -1.5, 10, -10); + matrix1.concat(matrix2); + + expect(matrix1.a).toBe(1.2999999523162842); + expect(matrix1.b).toBe(0.75); + expect(matrix1.c).toBe(0); + expect(matrix1.d).toBe(-1.5); + expect(matrix1.tx).toBe(23); + expect(matrix1.ty).toBe(-17.5); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatirxConcatService.ts b/packages/geom/src/Matrix/service/MatirxConcatService.ts new file mode 100644 index 00000000..ff17d263 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatirxConcatService.ts @@ -0,0 +1,40 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description 指定のMatrixを連結 + * Concatenate specified Matrix + * + * @param {Matrix} matrix1 + * @param {Matrix} matrix2 + * @return {void} + * @method + * @public + */ +export const execute = (matrix1: Matrix, matrix2: Matrix): void => +{ + const matrixArray1 = matrix1._$matrix; + const matrixArray2 = matrix2._$matrix; + + let a = matrixArray1[0] * matrixArray2[0]; + let b = 0.0; + let c = 0.0; + let d = matrixArray1[3] * matrixArray2[3]; + let tx = matrixArray1[4] * matrixArray2[0] + matrixArray2[4]; + let ty = matrixArray1[5] * matrixArray2[3] + matrixArray2[5]; + + if (matrixArray1[1] || matrixArray1[2] || matrixArray2[1] || matrixArray2[2]) { + a += matrixArray1[1] * matrixArray2[2]; + d += matrixArray1[2] * matrixArray2[1]; + b += matrixArray1[0] * matrixArray2[1] + matrixArray1[1] * matrixArray2[3]; + c += matrixArray1[2] * matrixArray2[0] + matrixArray1[3] * matrixArray2[2]; + tx += matrixArray1[5] * matrixArray2[2]; + ty += matrixArray1[4] * matrixArray2[1]; + } + + matrixArray1[0] = a; + matrixArray1[1] = b; + matrixArray1[2] = c; + matrixArray1[3] = d; + matrixArray1[4] = tx; + matrixArray1[5] = ty; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCloneService.test.ts b/packages/geom/src/Matrix/service/MatrixCloneService.test.ts new file mode 100644 index 00000000..d098efe6 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCloneService.test.ts @@ -0,0 +1,23 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js property valid test and clone test", () => +{ + it("property success case1", () => + { + const matrix = new Matrix(); + matrix.a = 1.2; + matrix.b = 0.765; + matrix.c = -0.872; + matrix.d = -1.5; + matrix.tx = 10; + matrix.ty = -10; + + expect(matrix.a).toBe(1.2000000476837158); + expect(matrix.b).toBe(0.7649999856948853); + expect(matrix.c).toBe(-0.871999979019165); + expect(matrix.d).toBe(-1.5); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(-10); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCloneService.ts b/packages/geom/src/Matrix/service/MatrixCloneService.ts new file mode 100644 index 00000000..824235b6 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCloneService.ts @@ -0,0 +1,19 @@ +import { Matrix } from "../../Matrix"; + +/** + * @description 指定のMatrixオブジェクトのクローンを生成して返却 + * Generate and return a clone of the specified Matrix object + * + * @param {Matrix} matrix + * @return {Matrix} + * @method + * @public + */ +export const execute = (matrix: Matrix): Matrix => +{ + return new Matrix( + matrix._$matrix[0], matrix._$matrix[1], + matrix._$matrix[2], matrix._$matrix[3], + matrix._$matrix[4], matrix._$matrix[5] + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCopyFromService.test.ts b/packages/geom/src/Matrix/service/MatrixCopyFromService.test.ts new file mode 100644 index 00000000..b27fa454 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCopyFromService.test.ts @@ -0,0 +1,48 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js copyFrom", () => +{ + it("copy test case1", () => + { + const matrix1 = new Matrix(); + + expect(matrix1.a).toBe(1); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(0); + expect(matrix1.d).toBe(1); + expect(matrix1.tx).toBe(0); + expect(matrix1.ty).toBe(0); + + const matrix2 = new Matrix(1, 2, 3, 4, 5, 6); + matrix1.copyFrom(matrix2); + + expect(matrix1.a).toBe(1); + expect(matrix1.b).toBe(2); + expect(matrix1.c).toBe(3); + expect(matrix1.d).toBe(4); + expect(matrix1.tx).toBe(5); + expect(matrix1.ty).toBe(6); + + matrix1.a = 1; + matrix1.b = 0; + matrix1.c = 0; + matrix1.d = 1; + matrix1.tx = 100; + matrix1.ty = 200; + + expect(matrix1.a).toBe(1); + expect(matrix1.b).toBe(0); + expect(matrix1.c).toBe(0); + expect(matrix1.d).toBe(1); + expect(matrix1.tx).toBe(100); + expect(matrix1.ty).toBe(200); + + expect(matrix2.a).toBe(1); + expect(matrix2.b).toBe(2); + expect(matrix2.c).toBe(3); + expect(matrix2.d).toBe(4); + expect(matrix2.tx).toBe(5); + expect(matrix2.ty).toBe(6); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCopyFromService.ts b/packages/geom/src/Matrix/service/MatrixCopyFromService.ts new file mode 100644 index 00000000..3a688fbc --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCopyFromService.ts @@ -0,0 +1,21 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description 指定のMatrixオブジェクトの値をコピー + * Copy the value of the specified Matrix object + * + * @param {Matrix} dst + * @param {Matrix} src + * @return {void} + * @method + * @public + */ +export const execute = (dst: Matrix, src: Matrix): void => +{ + dst._$matrix[0] = src._$matrix[0]; + dst._$matrix[1] = src._$matrix[1]; + dst._$matrix[2] = src._$matrix[2]; + dst._$matrix[3] = src._$matrix[3]; + dst._$matrix[4] = src._$matrix[4]; + dst._$matrix[5] = src._$matrix[5]; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCreateBoxService.test.ts b/packages/geom/src/Matrix/service/MatrixCreateBoxService.test.ts new file mode 100644 index 00000000..e10a1d69 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCreateBoxService.test.ts @@ -0,0 +1,563 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js createBox test", () => +{ + it("createBox test case1", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = 2.0; + const yScale = 3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = 10; + const ty = 20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(20); + }); + + it("createBox test case2", () => + { + const matrix = new Matrix(-1, 0.5, -0.2); + const xScale = 2.0; + const yScale = 3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = 10; + const ty = 20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(20); + }); + + it("createBox test case3", () => + { + const matrix = new Matrix(-1, -0.5, -0.2); + const xScale = 2.0; + const yScale = 3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = 10; + const ty = 20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(20); + }); + + it("createBox test case4", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = 2.0; + const yScale = 3.0; + const rotation = -2 * Math.PI * (45 / 360); + const tx = 10; + const ty = 20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(20); + }); + + it("createBox test case5", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = 10; + const ty = 20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(10); + expect(matrix.ty).toBe(20); + }); + + it("createBox test case6", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = 2.0; + const yScale = 3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case7", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (45 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case8", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (90 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.2246468525851679e-16); + expect(matrix.b).toBe(3); + expect(matrix.c).toBe(-2); + expect(matrix.d).toBe(-1.8369702788777518e-16); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case9", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (135 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case10", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (180 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(3.6739405577555036e-16); + expect(matrix.c).toBe(-2.4492937051703357e-16); + expect(matrix.d).toBe(3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case11", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (225 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case12", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (270 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(3.6739402930577075e-16); + expect(matrix.b).toBe(-3); + expect(matrix.c).toBe(2); + expect(matrix.d).toBe(5.510910704284357e-16); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case13", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (315 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case14", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = -2 * Math.PI * (360 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-2); + expect(matrix.b).toBe(-7.347881115511007e-16); + expect(matrix.c).toBe(4.898587410340671e-16); + expect(matrix.d).toBe(-3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case15", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 0; + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-2); + expect(matrix.b).toBe(-0); + expect(matrix.c).toBe(-0); + expect(matrix.d).toBe(-3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case16", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (45 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case17", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (90 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.2246468525851679e-16); + expect(matrix.b).toBe(-3); + expect(matrix.c).toBe(2); + expect(matrix.d).toBe(-1.8369702788777518e-16); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case18", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (135 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case19", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (180 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(-3.6739405577555036e-16); + expect(matrix.c).toBe(2.4492937051703357e-16); + expect(matrix.d).toBe(3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case20", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (225 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case21", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (270 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(3.6739402930577075e-16); + expect(matrix.b).toBe(3); + expect(matrix.c).toBe(-2); + expect(matrix.d).toBe(5.510910704284357e-16); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case22", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (315 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(2.1213202476501465); + expect(matrix.c).toBe(-1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case23", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = 2 * Math.PI * (360 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-2); + expect(matrix.b).toBe(7.347881115511007e-16); + expect(matrix.c).toBe(-4.898587410340671e-16); + expect(matrix.d).toBe(-3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case24", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (45 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.8477590084075928); + expect(matrix.b).toBe(-1.148050308227539); + expect(matrix.c).toBe(0.7653668522834778); + expect(matrix.d).toBe(-2.7716383934020996); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case25", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (90 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(-2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case26", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (135 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-0.7653668522834778); + expect(matrix.b).toBe(-2.7716383934020996); + expect(matrix.c).toBe(1.8477590084075928); + expect(matrix.d).toBe(-1.148050308227539); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case27", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (180 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(-1.2246468525851679e-16); + expect(matrix.b).toBe(-3); + expect(matrix.c).toBe(2); + expect(matrix.d).toBe(-1.8369702788777518e-16); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case28", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (225 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(0.7653668522834778); + expect(matrix.b).toBe(-2.7716383934020996); + expect(matrix.c).toBe(1.8477590084075928); + expect(matrix.d).toBe(1.148050308227539); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case29", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (270 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.4142135381698608); + expect(matrix.b).toBe(-2.1213202476501465); + expect(matrix.c).toBe(1.4142135381698608); + expect(matrix.d).toBe(2.1213202476501465); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case30", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (315 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(1.8477590084075928); + expect(matrix.b).toBe(-1.148050308227539); + expect(matrix.c).toBe(0.7653668522834778); + expect(matrix.d).toBe(2.7716383934020996); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); + + it("createBox test case31", () => + { + const matrix = new Matrix(1, 0.5, -0.2); + const xScale = -2.0; + const yScale = -3.0; + const rotation = Math.PI * (360 / 360); + const tx = -10; + const ty = -20; + matrix.createBox(xScale, yScale, rotation, tx, ty); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(-3.6739405577555036e-16); + expect(matrix.c).toBe(2.4492937051703357e-16); + expect(matrix.d).toBe(3); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(-20); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCreateBoxService.ts b/packages/geom/src/Matrix/service/MatrixCreateBoxService.ts new file mode 100644 index 00000000..70f82cf4 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCreateBoxService.ts @@ -0,0 +1,27 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description + * + * @param {Matrix} matrix + * @param {number} scale_x + * @param {number} scale_y + * @param {number} rotation + * @param {number} [tx=0] + * @param {number} [ty=0] + * @return {void} + * @method + * @public + */ +export const execute = ( + matrix: Matrix, + scale_x: number, scale_y: number, + rotation: number = 0, + tx: number = 0, ty: number = 0 +): void => +{ + matrix.identity(); + matrix.rotate(rotation); + matrix.scale(scale_x, scale_y); + matrix.translate(tx, ty); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.test.ts b/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.test.ts new file mode 100644 index 00000000..1540a550 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.test.ts @@ -0,0 +1,181 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js createGradientBox test", () => +{ + it("createGradientBox test case1", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 0, 9, 0, 0); + + expect(matrix.a).toBeCloseTo(-0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(0); + expect(matrix.tx).toBeCloseTo(0.5); + expect(matrix.ty).toBeCloseTo(0); + }); + + it("createGradientBox test case2", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 9, 0, 0); + + expect(matrix.a).toBeCloseTo(-0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0.0002515371597837657); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(-0.0005561097641475499); + expect(matrix.tx).toBeCloseTo(0.5); + expect(matrix.ty).toBeCloseTo(0.5); + }); + + it("createGradientBox test case3", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 0, 9, 1, 0); + + expect(matrix.a).toBeCloseTo(-0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(0); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(0); + }); + + it("createGradientBox test case4", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 0, 9, 0, 1); + + expect(matrix.a).toBeCloseTo(-0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(0); + expect(matrix.tx).toBeCloseTo(0.5); + expect(matrix.ty).toBeCloseTo(1); + }); + + it("createGradientBox test case5", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 9, 1, 1); + + expect(matrix.a).toBeCloseTo(-0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0.0002515371597837657); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(-0.0005561097641475499); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case6", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(-1, -1, -9, -1, -1); + + expect(matrix.a).toBeCloseTo(0.0005561097641475499); + expect(matrix.b).toBeCloseTo(0.0002515371597837657); + expect(matrix.c).toBeCloseTo(-0.0002515371597837657); + expect(matrix.d).toBeCloseTo(0.0005561097641475499); + expect(matrix.tx).toBeCloseTo(-1.5); + expect(matrix.ty).toBeCloseTo(-1.5); + }); + + it("createGradientBox test case6", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 45 / 180 * Math.PI, 1, 1); + + expect(matrix.a).toBeCloseTo(0.0004315837286412716); + expect(matrix.b).toBeCloseTo(0.0004315837286412716); + expect(matrix.c).toBeCloseTo(-0.0004315837286412716); + expect(matrix.d).toBeCloseTo(0.0004315837286412716); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case7", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 90 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(3.7373254383716084e-20); + expect(matrix.b).toBeCloseTo(0.0006103515625); + expect(matrix.c).toBeCloseTo(-0.0006103515625); + expect(matrix.d).toBeCloseTo(3.7373254383716084e-20); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case8", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 135 / 180 * Math.PI, 1, 1); + + expect(matrix.a).toBeCloseTo(-0.0004315837286412716); + expect(matrix.b).toBeCloseTo(0.0004315837286412716); + expect(matrix.c).toBeCloseTo(-0.0004315837286412716); + expect(matrix.d).toBeCloseTo(-0.0004315837286412716); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case9", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, 180 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(-0.0006103515625); + expect(matrix.b).toBeCloseTo(7.474650876743217e-20); + expect(matrix.c).toBeCloseTo(-7.474650876743217e-20); + expect(matrix.d).toBeCloseTo(-0.0006103515625); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case10", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, -45 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(0.0004315837286412716); + expect(matrix.b).toBeCloseTo(-0.0004315837286412716); + expect(matrix.c).toBeCloseTo(0.0004315837286412716); + expect(matrix.d).toBeCloseTo(0.0004315837286412716); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case11", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, -90 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(3.7373254383716084e-20); + expect(matrix.b).toBeCloseTo(-0.0006103515625); + expect(matrix.c).toBeCloseTo(0.0006103515625); + expect(matrix.d).toBeCloseTo(3.7373254383716084e-20); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case12", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, -135 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(-0.0004315837286412716); + expect(matrix.b).toBeCloseTo(-0.0004315837286412716); + expect(matrix.c).toBeCloseTo(0.0004315837286412716); + expect(matrix.d).toBeCloseTo(-0.0004315837286412716); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); + + it("createGradientBox test case13", () => + { + const matrix = new Matrix(); + matrix.createGradientBox(1, 1, -180 / 180 * Math.PI, 1, 1); + expect(matrix.a).toBeCloseTo(-0.0006103515625); + expect(matrix.b).toBeCloseTo(-7.474650876743217e-20); + expect(matrix.c).toBeCloseTo(7.474650876743217e-20); + expect(matrix.d).toBeCloseTo(-0.0006103515625); + expect(matrix.tx).toBeCloseTo(1.5); + expect(matrix.ty).toBeCloseTo(1.5); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.ts b/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.ts new file mode 100644 index 00000000..57db5060 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixCreateGradientBoxService.ts @@ -0,0 +1,44 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description Gradient用のMatrixを生成 + * Create a Matrix for Gradient + * + * @param {Matrix} matrix + * @param {number} width + * @param {number} height + * @param {number} [rotation=0] + * @param {number} [tx=0] + * @param {number} [ty=0] + * @return {void} + * @method + * @public + */ +export const execute = ( + matrix: Matrix, + width: number, height: number, + rotation: number = 0, + tx: number = 0, ty: number = 0 +): void => { + + const array = matrix._$matrix; + + array[0] = width / 1638.4; + array[3] = height / 1638.4; + + if (rotation) { + const cos = Math.cos(rotation); + const sin = Math.sin(rotation); + + array[1] = sin * array[3]; + array[2] = -sin * array[0]; + array[0] *= cos; + array[3] *= cos; + } else { + array[1] = 0; + array[2] = 0; + } + + array[4] = tx + width / 2; + array[5] = ty + height / 2; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.test.ts b/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.test.ts new file mode 100644 index 00000000..b8efd4a3 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.test.ts @@ -0,0 +1,241 @@ +import { Matrix } from "../../Matrix"; +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js deltaTransformPoint test", () => +{ + it("deltaTransformPoint test case1", () => + { + const matrix = new Matrix(1, 0, 0, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(-12.727921843528748); + expect(point2.y).toBeCloseTo(15.55634891986847); + }); + + it("deltaTransformPoint test case2", () => + { + const matrix = new Matrix(10, 0, 0, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(2.384185791015625e-7); + expect(point2.y).toBeCloseTo(28.284271001815796); + }); + + it("deltaTransformPoint test case3", () => + { + const matrix = new Matrix(1, 10, 0, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x | 0).toBe(-26); + expect(point2.y | 0).toBe(29); + }); + + it("deltaTransformPoint test case4", () => + { + const matrix = new Matrix(1, 0, 10, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(128.6934379339218); + expect(point2.y).toBeCloseTo(156.97770154476166); + }); + + it("deltaTransformPoint test case5", () => + { + const matrix = new Matrix(1, 0, 0, 10, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(-140.007142663002); + expect(point2.y).toBeCloseTo(142.83556973934174); + }); + + it("deltaTransformPoint test case6", () => + { + const matrix = new Matrix(-1, 0, 0, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(-15.55634891986847); + expect(point2.y).toBeCloseTo(12.727921843528748); + }); + + it("deltaTransformPoint test case7", () => + { + const matrix = new Matrix(1, 0, 0, -1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(15.55634891986847); + expect(point2.y).toBeCloseTo(-12.727921843528748); + }); + + it("deltaTransformPoint test case8", () => + { + const matrix = new Matrix(-1, 0, 0, -1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(12.727921843528748); + expect(point2.y).toBeCloseTo(-15.55634891986847); + }); + + it("deltaTransformPoint test case9", () => + { + const matrix = new Matrix(-1, -1, 0, -1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(14.142135381698608); + expect(point2.y).toBeCloseTo(-16.97056245803833); + }); + + it("deltaTransformPoint test case10", () => + { + const matrix = new Matrix(-1, 0, -1, -1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x | 0).toBe(-1); + expect(point2.y | 0).toBe(-29); + }); + + it("deltaTransformPoint test case11", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x | 0).toBe(0); + expect(point2.y | 0).toBe(-31); + }); + + it("deltaTransformPoint test case12", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(90 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBe(-22); + expect(point2.y).toBe(22); + }); + + it("deltaTransformPoint test case13", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(135 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBe(-31.11269783973694); + expect(point2.y).toBe(2.4424906541753444e-15); + }); + + it("deltaTransformPoint test case14", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(180 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBeCloseTo(-22); + expect(point2.y).toBeCloseTo(-22); + }); + + it("deltaTransformPoint test case15", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(-45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x | 0).toBe(31); + expect(point2.y | 0).toBe(0); + }); + + it("deltaTransformPoint test case16", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(-90 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBe(22); + expect(point2.y).toBe(-22); + }); + + it("deltaTransformPoint test case17", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(-135 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x | 0).toBe(0); + expect(point2.y | 0).toBe(-31); + + }); + + it("deltaTransformPoint test case18", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.translate(10, 0); + matrix.rotate(-180 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.deltaTransformPoint(point1); + + expect(point2.x).toBe(-22); + expect(point2.y).toBe(-22); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.ts b/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.ts new file mode 100644 index 00000000..e08ffcd8 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixDeltaTransformPointService.ts @@ -0,0 +1,20 @@ +import type { Matrix } from "../../Matrix"; +import { Point } from "../../Point"; + +/** + * @description Matrixを使ってPointを変換 + * Transform Point using Matrix + * + * @param {Matrix} matrix + * @param {Point} point + * @return {Point} + * @method + * @public + */ +export const execute = (matrix: Matrix, point: Point): Point => +{ + return new Point( + point.x * matrix._$matrix[0] + point.y * matrix._$matrix[2], + point.x * matrix._$matrix[1] + point.y * matrix._$matrix[3] + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixIdentityService.test.ts b/packages/geom/src/Matrix/service/MatrixIdentityService.test.ts new file mode 100644 index 00000000..d0d03f4f --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixIdentityService.test.ts @@ -0,0 +1,25 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js identity", () => +{ + it("identity test case1", () => + { + const matrix = new Matrix(1, 2, 3, 4, 5, 6); + + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(2); + expect(matrix.c).toBe(3); + expect(matrix.d).toBe(4); + expect(matrix.tx).toBe(5); + expect(matrix.ty).toBe(6); + + matrix.identity(); + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixIdentityService.ts b/packages/geom/src/Matrix/service/MatrixIdentityService.ts new file mode 100644 index 00000000..1dda9e38 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixIdentityService.ts @@ -0,0 +1,20 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description Matrixの値を初期化 + * Initialize Matrix values + * + * @param {Matrix} matrix + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix): void => +{ + matrix._$matrix[0] = 1; + matrix._$matrix[1] = 0; + matrix._$matrix[2] = 0; + matrix._$matrix[3] = 1; + matrix._$matrix[4] = 0; + matrix._$matrix[5] = 0; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixInvertService.test.ts b/packages/geom/src/Matrix/service/MatrixInvertService.test.ts new file mode 100644 index 00000000..301dfb95 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixInvertService.test.ts @@ -0,0 +1,97 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js invert test", () => +{ + it("invert test case1", () => + { + const matrix = new Matrix(2, 1, 1, 2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(0.6666666865348816); + expect(matrix.b).toBe(-0.3333333432674408); + expect(matrix.c).toBe(-0.3333333432674408); + expect(matrix.d).toBe(0.6666666865348816); + expect(matrix.tx).toBe(66.66667175292969); + expect(matrix.ty).toBe(66.66667175292969); + }); + + it("invert test case2", () => + { + const matrix = new Matrix(2, 1, 1, 2, -200, -200); + matrix.invert(); + matrix.invert(); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(1); + expect(matrix.c).toBe(1); + expect(matrix.d).toBe(2); + expect(matrix.tx).toBe(-200.00001525878906); + expect(matrix.ty).toBe(-200.00001525878906); + }); + + it("invert test case3", () => + { + const matrix = new Matrix(-2, 1, 1, 2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(-0.4000000059604645); + expect(matrix.b).toBe(0.20000000298023224); + expect(matrix.c).toBe(0.20000000298023224); + expect(matrix.d).toBe(0.4000000059604645); + expect(matrix.tx).toBe(-40); + expect(matrix.ty).toBe(120); + }); + + it("invert test case4", () => + { + const matrix = new Matrix(2, -1, 1, 2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(0.4000000059604645); + expect(matrix.b).toBe(0.20000000298023224); + expect(matrix.c).toBe(-0.20000000298023224); + expect(matrix.d).toBe(0.4000000059604645); + expect(matrix.tx).toBe(40); + expect(matrix.ty).toBe(120); + }); + + it("invert test case5", () => + { + const matrix = new Matrix(2, 1, -1, 2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(0.4000000059604645); + expect(matrix.b).toBe(-0.20000000298023224); + expect(matrix.c).toBe(0.20000000298023224); + expect(matrix.d).toBe(0.4000000059604645); + expect(matrix.tx).toBe(120); + expect(matrix.ty).toBe(40); + }); + + it("invert test case6", () => + { + const matrix = new Matrix(2, 1, 1, -2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(0.4000000059604645); + expect(matrix.b).toBe(0.20000000298023224); + expect(matrix.c).toBe(0.20000000298023224); + expect(matrix.d).toBe(-0.4000000059604645); + expect(matrix.tx).toBe(120); + expect(matrix.ty).toBe(-40); + }); + + it("invert test case7", () => + { + const matrix = new Matrix(-2, -1, -1, -2, -200, -200); + matrix.invert(); + + expect(matrix.a).toBe(-0.6666666865348816); + expect(matrix.b).toBe(0.3333333432674408); + expect(matrix.c).toBe(0.3333333432674408); + expect(matrix.d).toBe(-0.6666666865348816); + expect(matrix.tx).toBe(-66.66667175292969); + expect(matrix.ty).toBe(-66.66667175292969); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixInvertService.ts b/packages/geom/src/Matrix/service/MatrixInvertService.ts new file mode 100644 index 00000000..0acd0391 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixInvertService.ts @@ -0,0 +1,48 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description マトリックスを逆変換 + * Invert the matrix + * + * @param {Matrix} matrix + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix): void => +{ + const a: number = matrix._$matrix[0]; + const b: number = matrix._$matrix[1]; + const c: number = matrix._$matrix[2]; + const d: number = matrix._$matrix[3]; + const tx: number = matrix._$matrix[4]; + const ty: number = matrix._$matrix[5]; + + if (b === 0 && c === 0) { + + matrix._$matrix[0] = 1 / a; + matrix._$matrix[1] = 0; + matrix._$matrix[2] = 0; + matrix._$matrix[3] = 1 / d; + matrix._$matrix[4] = -matrix._$matrix[0] * tx; + matrix._$matrix[5] = -matrix._$matrix[3] * ty; + + } else { + + const det = a * d - b * c; + + if (det) { + + const rdet: number = 1 / det; + + matrix._$matrix[0] = d * rdet; + matrix._$matrix[1] = -b * rdet; + matrix._$matrix[2] = -c * rdet; + matrix._$matrix[3] = a * rdet; + matrix._$matrix[4] = -(matrix._$matrix[0] * tx + matrix._$matrix[2] * ty); + matrix._$matrix[5] = -(matrix._$matrix[1] * tx + matrix._$matrix[3] * ty); + + } + + } +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixRotateService.test.ts b/packages/geom/src/Matrix/service/MatrixRotateService.test.ts new file mode 100644 index 00000000..8edc6c73 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixRotateService.test.ts @@ -0,0 +1,247 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js rotate test", () => +{ + it("rotate test case1", () => + { + const matrix = new Matrix(1, 0, 0, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(0.7071067690849304); + expect(matrix.b).toBeCloseTo(0.7071067690849304); + expect(matrix.c).toBeCloseTo(-0.7071067690849304); + expect(matrix.d).toBeCloseTo(0.7071067690849304); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case2", () => + { + const matrix = new Matrix(1, 0, 0, -1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(0.7071067690849304); + expect(matrix.b).toBeCloseTo(0.7071067690849304); + expect(matrix.c).toBeCloseTo(0.7071067690849304); + expect(matrix.d).toBeCloseTo(-0.7071067690849304); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case3", () => + { + const matrix = new Matrix(-1, 0, 0, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-0.7071067690849304); + expect(matrix.b).toBeCloseTo(-0.7071067690849304); + expect(matrix.c).toBeCloseTo(-0.7071067690849304); + expect(matrix.d).toBeCloseTo(0.7071067690849304); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case4", () => + { + const matrix = new Matrix(-1, 0, 0, -1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-0.7071067690849304); + expect(matrix.b).toBeCloseTo(-0.7071067690849304); + expect(matrix.c).toBeCloseTo(0.7071067690849304); + expect(matrix.d).toBeCloseTo(-0.7071067690849304); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case5", () => + { + const matrix = new Matrix(1, 10, 10, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-6.363961219787598); + expect(matrix.b).toBeCloseTo(7.77817440032959); + expect(matrix.c).toBeCloseTo(6.363961219787598); + expect(matrix.d).toBeCloseTo(7.77817440032959); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case6", () => + { + const matrix = new Matrix(1, -10, 10, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(7.77817440032959); + expect(matrix.b).toBeCloseTo(-6.363961219787598); + expect(matrix.c).toBeCloseTo(6.363961219787598); + expect(matrix.d).toBeCloseTo(7.77817440032959); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case7", () => + { + const matrix = new Matrix(1, 10, -10, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + expect(matrix.a).toBeCloseTo(-6.363961219787598); + expect(matrix.b).toBeCloseTo(7.77817440032959); + expect(matrix.c).toBeCloseTo(-7.77817440032959); + expect(matrix.d).toBeCloseTo(-6.363961219787598); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case8", () => + { + const matrix = new Matrix(1, 10, 10, 1, -100, 110); + matrix.rotate(45 / 180 * Math.PI); + expect(matrix.a).toBeCloseTo(-6.363961219787598); + expect(matrix.b).toBeCloseTo(7.77817440032959); + expect(matrix.c).toBeCloseTo(6.363961219787598); + expect(matrix.d).toBeCloseTo(7.77817440032959); + expect(matrix.tx).toBeCloseTo(-148.492431640625); + expect(matrix.ty).toBeCloseTo(7.071067810058594); + }); + + it("rotate test case9", () => + { + const matrix = new Matrix(1, 10, 10, 1, 100, -110); + matrix.rotate(45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-6.363961219787598); + expect(matrix.b).toBeCloseTo(7.77817440032959); + expect(matrix.c).toBeCloseTo(6.363961219787598); + expect(matrix.d).toBeCloseTo(7.77817440032959); + expect(matrix.tx).toBeCloseTo(148.492431640625); + expect(matrix.ty).toBeCloseTo(-7.071067810058594); + }); + + it("rotate test case10", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(45 / 180 * Math.PI); + expect(matrix.a).toBeCloseTo(6.363961219787598); + expect(matrix.b).toBeCloseTo(-7.77817440032959); + expect(matrix.c).toBeCloseTo(-6.363961219787598); + expect(matrix.d).toBeCloseTo(-7.77817440032959); + expect(matrix.tx).toBeCloseTo(7.071067810058594); + expect(matrix.ty).toBeCloseTo(-148.492431640625); + }); + + it("rotate test case11", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(0.5); + + expect(matrix.a).toBeCloseTo(3.916672706604004); + expect(matrix.b).toBeCloseTo(-9.255250930786133); + expect(matrix.c).toBeCloseTo(-8.29640007019043); + expect(matrix.d).toBeCloseTo(-5.67183780670166); + expect(matrix.tx).toBeCloseTo(-35.021446228027344); + expect(matrix.ty).toBeCloseTo(-144.4766387939453); + }); + + it("rotate test case12", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(-0.5); + + expect(matrix.a).toBeCloseTo(-5.67183780670166); + expect(matrix.b).toBeCloseTo(-8.29640007019043); + expect(matrix.c).toBeCloseTo(-9.255250930786133); + expect(matrix.d).toBeCloseTo(3.916672706604004); + expect(matrix.tx).toBeCloseTo(-140.4950714111328); + expect(matrix.ty).toBeCloseTo(-48.591529846191406); + }); + + it("rotate test case13", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(90 / 180 * Math.PI); + expect(matrix.a).toBeCloseTo(10); + expect(matrix.b).toBeCloseTo(-1); + expect(matrix.c).toBeCloseTo(1); + expect(matrix.d).toBeCloseTo(-10); + expect(matrix.tx).toBeCloseTo(110); + expect(matrix.ty).toBeCloseTo(-100); + }); + + it("rotate test case14", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(135 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(7.77817440032959); + expect(matrix.b).toBeCloseTo(6.363961219787598); + expect(matrix.c).toBeCloseTo(7.77817440032959); + expect(matrix.d).toBeCloseTo(-6.363961219787598); + expect(matrix.tx).toBeCloseTo(148.492431640625); + expect(matrix.ty).toBeCloseTo(7.071067810058594); + }); + + it("rotate test case15", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(Math.PI); + + expect(matrix.a).toBeCloseTo(1); + expect(matrix.b).toBeCloseTo(10); + expect(matrix.c).toBeCloseTo(10); + expect(matrix.d).toBeCloseTo(1); + expect(matrix.tx).toBeCloseTo(100); + expect(matrix.ty).toBeCloseTo(110); + }); + + it("rotate test case16", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(-45 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-7.77817440032959); + expect(matrix.b).toBeCloseTo(-6.363961219787598); + expect(matrix.c).toBeCloseTo(-7.77817440032959); + expect(matrix.d).toBeCloseTo(6.363961219787598); + expect(matrix.tx).toBeCloseTo(-148.492431640625); + expect(matrix.ty).toBeCloseTo(-7.071067810058594); + }); + + it("rotate test case17", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(-90 / 180 * Math.PI); + + expect(matrix.a).toBeCloseTo(-10); + expect(matrix.b).toBeCloseTo(1); + expect(matrix.c).toBeCloseTo(-1); + expect(matrix.d).toBeCloseTo(10); + expect(matrix.tx).toBeCloseTo(-110); + expect(matrix.ty).toBeCloseTo(100); + }); + + it("rotate test case18", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(-135 / 180 * Math.PI); + expect(matrix.a).toBeCloseTo(-6.363961219787598); + expect(matrix.b).toBeCloseTo(7.77817440032959); + expect(matrix.c).toBeCloseTo(6.363961219787598); + expect(matrix.d).toBeCloseTo(7.77817440032959); + expect(matrix.tx).toBeCloseTo(-7.071067810058594); + expect(matrix.ty).toBeCloseTo(148.492431640625); + }); + + it("rotate test case19", () => + { + const matrix = new Matrix(-1, -10, -10, -1, -100, -110); + matrix.rotate(-1 * Math.PI); + expect(matrix.a).toBeCloseTo(1); + expect(matrix.b).toBeCloseTo(10); + expect(matrix.c).toBeCloseTo(10); + expect(matrix.d).toBeCloseTo(1); + expect(matrix.tx).toBeCloseTo(100); + expect(matrix.ty).toBeCloseTo(110); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixRotateService.ts b/packages/geom/src/Matrix/service/MatrixRotateService.ts new file mode 100644 index 00000000..fd6e167b --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixRotateService.ts @@ -0,0 +1,29 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description マトリックスの回転変換 + * Rotate transformation of matrix + * + * @param {Matrix} matrix + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix, rotation: number): void => +{ + const array = matrix._$matrix; + + const a = array[0]; + const b = array[1]; + const c = array[2]; + const d = array[3]; + const tx = array[4]; + const ty = array[5]; + + array[0] = a * Math.cos(rotation) - b * Math.sin(rotation); + array[1] = a * Math.sin(rotation) + b * Math.cos(rotation); + array[2] = c * Math.cos(rotation) - d * Math.sin(rotation); + array[3] = c * Math.sin(rotation) + d * Math.cos(rotation); + array[4] = tx * Math.cos(rotation) - ty * Math.sin(rotation); + array[5] = tx * Math.sin(rotation) + ty * Math.cos(rotation); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixScaleService.test.ts b/packages/geom/src/Matrix/service/MatrixScaleService.test.ts new file mode 100644 index 00000000..e19ddd49 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixScaleService.test.ts @@ -0,0 +1,69 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js scale test", () => +{ + it("scale test case1", () => + { + const matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); + matrix.scale(2, 2); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(1); + expect(matrix.c).toBe(-1); + expect(matrix.d).toBe(2); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + }); + + it("scale test case2", () => + { + const matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); + matrix.scale(2, 3); + + expect(matrix.a).toBe(2); + expect(matrix.b).toBe(1.5); + expect(matrix.c).toBe(-1); + expect(matrix.d).toBe(3); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + }); + + it("scale test case3", () => + { + const matrix = new Matrix(1, 0.5, -0.5, 1, 0, 0); + matrix.scale(3, 2); + + expect(matrix.a).toBe(3); + expect(matrix.b).toBe(1); + expect(matrix.c).toBe(-1.5); + expect(matrix.d).toBe(2); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + }); + + it("scale test case4", () => + { + const matrix = new Matrix(1, 0.5, -0.5, 1, 10, 0); + matrix.scale(-1, 2); + + expect(matrix.a).toBe(-1); + expect(matrix.b).toBe(1); + expect(matrix.c).toBe(0.5); + expect(matrix.d).toBe(2); + expect(matrix.tx).toBe(-10); + expect(matrix.ty).toBe(0); + }); + + it("scale test case5", () => + { + const matrix = new Matrix(1, 0.5, -0.5, 1, 0, 10); + matrix.scale(3, -2); + expect(matrix.a).toBe(3); + expect(matrix.b).toBe(-1); + expect(matrix.c).toBe(-1.5); + expect(matrix.d).toBe(-2); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(-20); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixScaleService.ts b/packages/geom/src/Matrix/service/MatrixScaleService.ts new file mode 100644 index 00000000..c4b8505b --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixScaleService.ts @@ -0,0 +1,25 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description マトリックスの拡大変換 + * Scale transformation of matrix + * + * @param {Matrix} matrix + * @param {number} scale_x + * @param {number} scale_y + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix, scale_x: number, scale_y: number): void => +{ + const array = matrix._$matrix; + + array[0] *= scale_x; + array[2] *= scale_x; + array[4] *= scale_x; + + array[1] *= scale_y; + array[3] *= scale_y; + array[5] *= scale_y; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixSetToService.test.ts b/packages/geom/src/Matrix/service/MatrixSetToService.test.ts new file mode 100644 index 00000000..f2e51ad5 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixSetToService.test.ts @@ -0,0 +1,26 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js setTo", () => +{ + it("copy test case1", () => + { + + const matrix = new Matrix(); + + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + + matrix.setTo(1, 2, 3, 4, 5, 6); + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(2); + expect(matrix.c).toBe(3); + expect(matrix.d).toBe(4); + expect(matrix.tx).toBe(5); + expect(matrix.ty).toBe(6); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixSetToService.ts b/packages/geom/src/Matrix/service/MatrixSetToService.ts new file mode 100644 index 00000000..87fca257 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixSetToService.ts @@ -0,0 +1,32 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description マトリックスの設定変換 + * Set transformation of matrix + * + * @param {Matrix} matrix + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} tx + * @param {number} ty + * @return {void} + * @method + * @public + */ +export const execute = ( + matrix: Matrix, + a: number, b: number, + c: number, d: number, + tx: number, ty: number +): void => { + + const array = matrix._$matrix; + array[0] = a; + array[1] = b; + array[2] = c; + array[3] = d; + array[4] = tx; + array[5] = ty; +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixTransformPointService.test.ts b/packages/geom/src/Matrix/service/MatrixTransformPointService.test.ts new file mode 100644 index 00000000..f63265f6 --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixTransformPointService.test.ts @@ -0,0 +1,164 @@ +import { Matrix } from "../../Matrix"; +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js transformPoint test", () => +{ + it("transformPoint test case1", () => + { + const matrix = new Matrix(1, 0, 0, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-19.79898965358734); + expect(point2.y).toBe(164.04878056049347); + }); + + it("transformPoint test case2", () => + { + const matrix = new Matrix(1, 1, 0, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-21.213203191757202); + expect(point2.y).toBe(165.46299409866333); + }); + + it("transformPoint test case3", () => + { + const matrix = new Matrix(1, 0, 1, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x | 0).toBe(-5); + expect(point2.y | 0).toBe(178); + + }); + + it("transformPoint test case4", () => + { + const matrix = new Matrix(1, 1, 1, 1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x | 0).toBe(-7); + expect(point2.y | 0).toBe(179); + }); + + it("transformPoint test case5", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x | 0).toBe(-7); + expect(point2.y | 0).toBe(117); + }); + + it("transformPoint test case6", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x | 0).toBe(-7); + expect(point2.y | 0).toBe(117); + }); + + it("transformPoint test case7", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(90 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-88); + expect(point2.y).toBe(78); + }); + + it("transformPoint test case8", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(135 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-117.37973380088806); + expect(point2.y).toBe(-7.071067810058596); + }); + + it("transformPoint test case9", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(180 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-78); + expect(point2.y).toBe(-88); + }); + + it("transformPoint test case10", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(-45 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x | 0).toBe(117); + expect(point2.y | 0).toBe(7); + + }); + + it("transformPoint test case11", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(-90 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(88); + expect(point2.y).toBe(-78); + }); + + it("transformPoint test case12", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(-135 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(7.071067810058591); + expect(point2.y).toBe(-117.37973380088806); + }); + + it("transformPoint test case13", () => + { + const matrix = new Matrix(-1, -1, -1, -1, 100, 110); + matrix.rotate(-180 / 180 * Math.PI); + + const point1 = new Point(2, 20); + const point2 = matrix.transformPoint(point1); + + expect(point2.x).toBe(-78); + expect(point2.y).toBe(-88); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixTransformPointService.ts b/packages/geom/src/Matrix/service/MatrixTransformPointService.ts new file mode 100644 index 00000000..9497c51f --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixTransformPointService.ts @@ -0,0 +1,23 @@ +import type { Matrix } from "../../Matrix"; +import { Point } from "../../Point"; + +/** + * @description マトリックスの座標変換 + * Coordinate transformation of matrix + * + * @param {Matrix} matrix + * @param {Point} point + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix, point: Point): Point => +{ + const x = point.x; + const y = point.y; + const array = matrix._$matrix; + return new Point( + x * array[0] + y * array[2] + array[4], + x * array[1] + y * array[3] + array[5] + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixTranslateService.test.ts b/packages/geom/src/Matrix/service/MatrixTranslateService.test.ts new file mode 100644 index 00000000..4db45dae --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixTranslateService.test.ts @@ -0,0 +1,62 @@ +import { Matrix } from "../../Matrix"; +import { describe, expect, it } from "vitest"; + +describe("Matrix.js translate", () => +{ + it("default test case1", () => + { + const matrix = new Matrix(); + + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(0); + expect(matrix.ty).toBe(0); + + matrix.translate(-100, -100); + expect(matrix.a).toBe(1); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(-100); + expect(matrix.ty).toBe(-100); + + matrix.scale(0.0, 1.0); + expect(matrix.a).toBe(0); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(-0); + expect(matrix.ty).toBe(-100); + + matrix.translate(100, 100); + expect(matrix.a).toBe(0); + expect(matrix.b).toBe(0); + expect(matrix.c).toBe(0); + expect(matrix.d).toBe(1); + expect(matrix.tx).toBe(100); + expect(matrix.ty).toBe(0); + }); + + it("pattern test case2", () => + { + const matrix = new Matrix(); + + // 単位行列化 + matrix.identity(); + // 拡大縮小成分を乗算 + matrix.scale( 256 / 1638.4 , 256 / 1638.4 ); + // 角度成分を乗算 + matrix.rotate(Math.PI / 180); + // 移動成分を乗算 + matrix.translate( 128.0 , 128.0 ); + + expect(matrix.a).toBeCloseTo(0.15622620284557343); + expect(matrix.b).toBeCloseTo(0.0027269385755062103); + expect(matrix.c).toBeCloseTo(-0.0027269385755062103); + expect(matrix.d).toBeCloseTo(0.15622620284557343); + expect(matrix.tx).toBe(128); + expect(matrix.ty).toBe(128); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Matrix/service/MatrixTranslateService.ts b/packages/geom/src/Matrix/service/MatrixTranslateService.ts new file mode 100644 index 00000000..e7becf4e --- /dev/null +++ b/packages/geom/src/Matrix/service/MatrixTranslateService.ts @@ -0,0 +1,18 @@ +import type { Matrix } from "../../Matrix"; + +/** + * @description マトリックスの座標移動 + * Coordinate movement of matrix + * + * @param {Matrix} matrix + * @param {number} dx + * @param {number} dy + * @return {void} + * @method + * @public + */ +export const execute = (matrix: Matrix, dx: number, dy: number): void => +{ + matrix.tx += dx; + matrix.ty += dy; +}; \ No newline at end of file diff --git a/packages/geom/src/Point.test.ts b/packages/geom/src/Point.test.ts new file mode 100644 index 00000000..adc31818 --- /dev/null +++ b/packages/geom/src/Point.test.ts @@ -0,0 +1,16 @@ +import { Point } from "./Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js length test", () => +{ + it("default test case1", () => + { + expect(new Point().length).toBe(0); + }); + + it("default test case2", () => + { + const point = new Point(10, 30); + expect(point.length).toBe(31.622776601683793); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point.ts b/packages/geom/src/Point.ts index 1692125a..20c720e0 100644 --- a/packages/geom/src/Point.ts +++ b/packages/geom/src/Point.ts @@ -1,111 +1,56 @@ -import { - $Math, - $clamp, - $SHORT_INT_MIN, - $SHORT_INT_MAX -} from "@next2d/share"; +import { execute as pointAddService } from "./Point/service/PointAddService"; +import { execute as pointCloneService } from "./Point/service/PointCloneService"; +import { execute as pointCopyFromService } from "./Point/service/PointCopyFromService"; +import { execute as pointDistanceService } from "./Point/service/PointDistanceService"; +import { execute as pointEqualsService } from "./Point/service/PointEqualsService"; +import { execute as pointInterpolateService } from "./Point/service/PointInterpolateService"; +import { execute as pointNormalizeService } from "./Point/service/PointNormalizeService"; +import { execute as pointOffsetService } from "./Point/service/PointOffsetService"; +import { execute as pointPolarService } from "./Point/service/PointPolarService"; +import { execute as pointSetToService } from "./Point/service/PointSetToService"; +import { execute as pointSubtractService } from "./Point/service/PointSubtractService"; /** - * Point オブジェクトは 2 次元の座標系の位置を表します。 - * x は水平方向の軸を表し、y は垂直方向の軸を表します。 - * - * The Point object represents a location in a two-dimensional coordinate system, - * where x represents the horizontal axis and y represents the vertical axis. - * - * @example Example usage of Point. - * // new Point - * const {Point} = next2d.geom; - * const point = new Point(); + * @description Point オブジェクトは 2 次元の座標系の位置を表します。 + * x は水平方向の軸を表し、y は垂直方向の軸を表します。 + * The Point object represents a location in a two-dimensional coordinate system, + * where x represents the horizontal axis and y represents the vertical axis. * * @class * @memberOf next2d.geom */ export class Point { - private _$x: number; - private _$y: number; - /** - * @param {number} [x=0] - * @param {number} [y=0] + * @description ポイントの水平座標です。 + * The horizontal coordinate of the point. * - * @constructor + * @member {number} + * @default 0 * @public */ - constructor (x: number = 0, y: number = 0) - { - /** - * @type {number} - * @default 0 - * @private - */ - this._$x = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$y = 0; - - // setup - this.x = x; - this.y = y; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Point] - * @method - * @static - */ - static toString (): string - { - return "[class Point]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.geom.Point - * @const - * @static - */ - static get namespace (): string - { - return "next2d.geom.Point"; - } + public x: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description ポイントの垂直座標です。 + * The vertical coordinate of the point. * - * @return {string} - * @method + * @member {number} + * @default 0 * @public */ - toString (): string - { - return `(x=${this.x}, y=${this.y})`; - } + public y: number; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.geom.Point - * @const + * @param {number} [x = 0] + * @param {number} [y = 0] + * @constructor * @public */ - get namespace (): string + constructor (x: number = 0, y: number = 0) { - return "next2d.geom.Point"; + this.x = x; + this.y = y; } /** @@ -119,41 +64,7 @@ export class Point */ get length (): number { - return $Math.sqrt($Math.pow(this.x, 2) + $Math.pow(this.y, 2)); - } - - /** - * @description ポイントの水平座標です。 - * The horizontal coordinate of the point. - * - * @member {number} - * @default 0 - * @public - */ - get x (): number - { - return this._$x; - } - set x (x: number) - { - this._$x = $clamp(+x, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); - } - - /** - * @description ポイントの垂直座標です。 - * The vertical coordinate of the point. - * - * @member {number} - * @default 0 - * @public - */ - get y (): number - { - return this._$y; - } - set y (y: number) - { - this._$y = $clamp(+y, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); + return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); } /** @@ -161,14 +72,14 @@ export class Point * Adds the coordinates of another point * to the coordinates of this point to create a new point. * - * @param {Point} v + * @param {Point} point * @returns {Point} * @method * @public */ - add (v: Point): Point + add (point: Point): Point { - return new Point(this.x + v.x, this.y + v.y); + return pointAddService(this, point); } /** @@ -181,7 +92,7 @@ export class Point */ clone (): Point { - return new Point(this.x, this.y); + return pointCloneService(this); } /** @@ -190,14 +101,14 @@ export class Point * Copies all of the point data from * the source Point object into the calling Point object. * - * @param {Point} source_point + * @param {Point} point * @returns void + * @method * @public */ - copyFrom (source_point: Point): void + copyFrom (point: Point): void { - this._$x = source_point._$x; - this._$y = source_point._$y; + pointCopyFromService(this, point); } /** @@ -212,24 +123,21 @@ export class Point */ static distance (point1: Point, point2: Point): number { - return $Math.sqrt( - $Math.pow(point1._$x - point2._$x, 2) - + $Math.pow(point1._$y - point2._$y, 2) - ); + return pointDistanceService(point1, point2); } /** * @description 2 つのポイントが等しいかどうかを判別します。 * Determines whether two points are equal. * - * @param {Point} to_compare + * @param {Point} point * @return {boolean} * @method * @public */ - equals (to_compare: Point): boolean + equals (point: Point): boolean { - return this._$x === to_compare._$x && this._$y === to_compare._$y; + return pointEqualsService(this, point); } /** @@ -240,14 +148,12 @@ export class Point * @param {Point} point2 * @param {number} f * @return {Point} + * @method * @static */ static interpolate (point1: Point, point2: Point, f: number): Point { - return new Point( - point1.x + (point2.x - point1.x) * (1 - f), - point1.y + (point2.y - point1.y) * (1 - f) - ); + return pointInterpolateService(point1, point2, f); } /** @@ -261,9 +167,7 @@ export class Point */ normalize (thickness: number): void { - const length = this.length; - this.x = this.x * thickness / length; - this.y = this.y * thickness / length; + pointNormalizeService(this, thickness); } /** @@ -272,45 +176,43 @@ export class Point * * @param {number} dx * @param {number} dy - * @return {Point} + * @return {void} * @method * @public */ - offset (dx: number, dy: number) + offset (dx: number, dy: number): void { - this.x += dx; - this.y += dy; + pointOffsetService(this, dx, dy); } /** * @description 極座標ペアを直交点座標に変換します。 * Converts a pair of polar coordinates to a Cartesian point coordinate. * - * @param {number} len + * @param {number} length * @param {number} angle * @return {Point} * @method * @static */ - static polar (len: number, angle: number): Point + static polar (length: number, angle: number): Point { - return new Point(len * $Math.cos(angle), len * $Math.sin(angle)); + return pointPolarService(length, angle); } /** * @description Point のメンバーを指定の値に設定します。 * Sets the members of Point to the specified values * - * @param {number} xa - * @param {number} ya + * @param {number} x + * @param {number} y * @return {void} * @method * @public */ - setTo (xa: number, ya: number): void + setTo (x: number, y: number): void { - this.x = xa; - this.y = ya; + pointSetToService(this, x, y); } /** @@ -318,13 +220,13 @@ export class Point * Subtracts the coordinates of another point * from the coordinates of this point to create a new point. * - * @param {Point} v + * @param {Point} point * @return {Point} * @method * @public */ - subtract (v: Point): Point + subtract (point: Point): Point { - return new Point(this.x - v.x, this.y - v.y); + return pointSubtractService(this, point); } -} +} \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointAddService.test.ts b/packages/geom/src/Point/service/PointAddService.test.ts new file mode 100644 index 00000000..2dfd5e56 --- /dev/null +++ b/packages/geom/src/Point/service/PointAddService.test.ts @@ -0,0 +1,65 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js add test", () => +{ + it("add test case1", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(20, 20); + + const p3 = p1.add(p1); + const p4 = p2.add(p2); + const p5 = p1.add(p2); + const p6 = p2.add(p1); + + expect(p3.x).toBe(20); + expect(p3.y).toBe(20); + expect(p4.x).toBe(40); + expect(p4.y).toBe(40); + expect(p5.x).toBe(30); + expect(p5.y).toBe(30); + expect(p6.x).toBe(30); + expect(p6.y).toBe(30); + }); + + it("add test case2", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(20, -20); + + const p3 = p1.add(p1); + const p4 = p2.add(p2); + const p5 = p1.add(p2); + const p6 = p2.add(p1); + + expect(p3.x).toBe(-20); + expect(p3.y).toBe(-20); + expect(p4.x).toBe(40); + expect(p4.y).toBe(-40); + expect(p5.x).toBe(10); + expect(p5.y).toBe(-30); + expect(p6.x).toBe(10); + expect(p6.y).toBe(-30); + }); + + it("add test case3", () => + { + const p1 = new Point(0, 10); + const p2 = new Point(20, 0); + + const p3 = p1.add(p1); + const p4 = p2.add(p2); + const p5 = p1.add(p2); + const p6 = p2.add(p1); + + expect(p3.x).toBe(0); + expect(p3.y).toBe(20); + expect(p4.x).toBe(40); + expect(p4.y).toBe(0); + expect(p5.x).toBe(20); + expect(p5.y).toBe(10); + expect(p6.x).toBe(20); + expect(p6.y).toBe(10); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointAddService.ts b/packages/geom/src/Point/service/PointAddService.ts new file mode 100644 index 00000000..f8310b1d --- /dev/null +++ b/packages/geom/src/Point/service/PointAddService.ts @@ -0,0 +1,16 @@ +import { Point } from "../../Point"; + +/** + * @description 2つの座標を加算します。 + * Adds two points. + * + * @param {Point} point1 + * @param {Point} point2 + * @return {Point} + * @method + * @public + */ +export const execute = (point1: Point, point2: Point): Point => +{ + return new Point(point1.x + point2.x, point1.y + point2.y); +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointCloneService.test.ts b/packages/geom/src/Point/service/PointCloneService.test.ts new file mode 100644 index 00000000..1d64d02f --- /dev/null +++ b/packages/geom/src/Point/service/PointCloneService.test.ts @@ -0,0 +1,28 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js property valid test and clone test", () => +{ + it("clone test", function () { + + // origin + const point1 = new Point(0, 0); + + // clone + const point2 = point1.clone(); + point2.x = 10; + point2.y = 20; + + // origin + expect(point1.x).toBe(0); + expect(point1.y).toBe(0); + + // clone + expect(point2.x).toBe(10); + expect(point2.y).toBe(20); + expect(point2.length).toBe(22.360679774997898); + + expect(point2.x).toBe(10); + expect(point2.y).toBe(20); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointCloneService.ts b/packages/geom/src/Point/service/PointCloneService.ts new file mode 100644 index 00000000..10ef34ef --- /dev/null +++ b/packages/geom/src/Point/service/PointCloneService.ts @@ -0,0 +1,15 @@ +import { Point } from "../../Point"; + +/** + * @description 座標を複製します。 + * Duplicates the coordinates. + * + * @param {Point} src + * @return {Point} + * @method + * @public + */ +export const execute = (src: Point): Point => +{ + return new Point(src.x, src.y); +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointCopyFromService.test.ts b/packages/geom/src/Point/service/PointCopyFromService.test.ts new file mode 100644 index 00000000..7d2800ea --- /dev/null +++ b/packages/geom/src/Point/service/PointCopyFromService.test.ts @@ -0,0 +1,58 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js copyFrom test", () => +{ + it("copyFrom test case1", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(20, 20); + + p1.copyFrom(p2); + p1.x = 10; + + expect(p1.x).toBe(10); + expect(p1.y).toBe(20); + expect(p2.x).toBe(20); + expect(p2.y).toBe(20); + }); + + it("copyFrom test case2", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(20, -20); + + p1.copyFrom(p2); + + expect(p1.x).toBe(20); + expect(p1.y).toBe(-20); + expect(p2.x).toBe(20); + expect(p2.y).toBe(-20); + }); + + it("copyFrom test case3", () => + { + const p1 = new Point(0, 10); + const p2 = new Point(20, 0); + + p1.copyFrom(p2); + + expect(p1.x).toBe(20); + expect(p1.y).toBe(0); + expect(p2.x).toBe(20); + expect(p2.y).toBe(0); + }); + + it("copyFrom test case4", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(0, 20); + + p1.copyFrom(p2); + + expect(p1.x).toBe(0); + expect(p1.y).toBe(20); + expect(p2.x).toBe(0); + expect(p2.y).toBe(20); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointCopyFromService.ts b/packages/geom/src/Point/service/PointCopyFromService.ts new file mode 100644 index 00000000..1218a7b6 --- /dev/null +++ b/packages/geom/src/Point/service/PointCopyFromService.ts @@ -0,0 +1,17 @@ +import type { Point } from "../../Point"; + +/** + * @description 指定されたポイントの値をコピーします。 + * Copies the value of the specified point. + * + * @param {Point} dst + * @param {Point} src + * @return {void} + * @method + * @public + */ +export const execute = (dst: Point, src: Point): void => +{ + dst.x = src.x; + dst.y = src.y; +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointDistanceService.test.ts b/packages/geom/src/Point/service/PointDistanceService.test.ts new file mode 100644 index 00000000..e801d441 --- /dev/null +++ b/packages/geom/src/Point/service/PointDistanceService.test.ts @@ -0,0 +1,117 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js distance test", () => +{ + it("distance test case1", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(20, 20); + expect(Point.distance(p1, p2)).toBe(14.142135623730951); + }); + + it("distance test case2", () => + { + const p1 = new Point(-10, 10); + const p2 = new Point(20, 20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case3", () => + { + const p1 = new Point(10, -10); + const p2 = new Point(20, 20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case4", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(-20, 20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case5", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(20, -20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case6", () => + { + const p1 = new Point(10, -10); + const p2 = new Point(20, -20); + expect(Point.distance(p1, p2)).toBe(14.142135623730951); + }); + + it("distance test case7", () => + { + const p1 = new Point(-10, 10); + const p2 = new Point(-20, 20); + expect(Point.distance(p1, p2)).toBe(14.142135623730951); + }); + + it("distance test case8", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(20, 20); + expect(Point.distance(p1, p2)).toBe(42.42640687119285); + }); + + it("distance test case9", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(-20, -20); + expect(Point.distance(p1, p2)).toBe(42.42640687119285); + }); + + it("distance test case10", () => + { + const p1 = new Point(-10, 10); + const p2 = new Point(20, -20); + expect(Point.distance(p1, p2)).toBe(42.42640687119285); + }); + + it("distance test case11", () => + { + const p1 = new Point(10, -10); + const p2 = new Point(-20, 20); + expect(Point.distance(p1, p2)).toBe(42.42640687119285); + }); + + it("distance test case12", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(-20, 20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case13", () => + { + const p1 = new Point(10, -10); + const p2 = new Point(-20, -20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case14", () => + { + const p1 = new Point(-10, 10); + const p2 = new Point(-20, -20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case15", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(20, -20); + expect(Point.distance(p1, p2)).toBe(31.622776601683793); + }); + + it("distance test case16", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(-20, -20); + expect(Point.distance(p1, p2)).toBe(14.142135623730951); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointDistanceService.ts b/packages/geom/src/Point/service/PointDistanceService.ts new file mode 100644 index 00000000..6c818bb1 --- /dev/null +++ b/packages/geom/src/Point/service/PointDistanceService.ts @@ -0,0 +1,19 @@ +import type { Point } from "../../Point"; + +/** + * @description 2点間の距離を返します。 + * Returns the distance between two points. + * + * @param {Point} point1 + * @param {Point} point2 + * @return {number} + * @method + * @public + */ +export const execute = (point1: Point, point2: Point): number => +{ + return Math.sqrt( + Math.pow(point1.x - point2.x, 2) + + Math.pow(point1.y - point2.y, 2) + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointEqualsService.test.ts b/packages/geom/src/Point/service/PointEqualsService.test.ts new file mode 100644 index 00000000..a2675abc --- /dev/null +++ b/packages/geom/src/Point/service/PointEqualsService.test.ts @@ -0,0 +1,62 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js equals test", () => +{ + + it("equals test case1", () => + { + const p1 = new Point(10, 10); + const p2 = new Point(10, 10); + const p3 = new Point(10, 20); + const p4 = new Point(20, 10); + const p5 = new Point(20, 20); + + expect(p1.equals(p2)).toBe(true); + expect(p1.equals(p3)).toBe(false); + expect(p1.equals(p4)).toBe(false); + expect(p1.equals(p5)).toBe(false); + }); + + it("equals test case2", () => + { + const p1 = new Point(-10, -10); + const p2 = new Point(-10, -10); + const p3 = new Point(-10, -20); + const p4 = new Point(-20, -10); + const p5 = new Point(-20, -20); + + expect(p1.equals(p2)).toBe(true); + expect(p1.equals(p3)).toBe(false); + expect(p1.equals(p4)).toBe(false); + expect(p1.equals(p5)).toBe(false); + }); + + it("equals test case3", () => + { + const p1 = new Point(1, 10); + const p2 = new Point(1, 10); + const p3 = new Point(1, 10); + const p4 = new Point(0, 10); + const p5 = new Point(1, 1); + + expect(p1.equals(p2)).toBe(true); + expect(p1.equals(p3)).toBe(true); + expect(p1.equals(p4)).toBe(false); + expect(p1.equals(p5)).toBe(false); + }); + + it("equals test case4", () => + { + const p1 = new Point(1, 10); + const p2 = new Point(1, 10); + const p3 = new Point(1, 10); + const p4 = new Point(0, 10); + const p5 = new Point(1, 0); + + expect(p1.equals(p2)).toBe(true); + expect(p1.equals(p3)).toBe(true); + expect(p1.equals(p4)).toBe(false); + expect(p1.equals(p5)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointEqualsService.ts b/packages/geom/src/Point/service/PointEqualsService.ts new file mode 100644 index 00000000..4f742109 --- /dev/null +++ b/packages/geom/src/Point/service/PointEqualsService.ts @@ -0,0 +1,16 @@ +import type { Point } from "../../Point"; + +/** + * @description 2点が等しいかどうかを返します。 + * Returns whether two points are equal. + * + * @param {Point} point1 + * @param {Point} point2 + * @return {boolean} + * @method + * @public + */ +export const execute = (point1: Point, point2: Point): boolean => +{ + return point1.x === point2.x && point1.y === point2.y; +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointInterpolateService.test.ts b/packages/geom/src/Point/service/PointInterpolateService.test.ts new file mode 100644 index 00000000..745223ac --- /dev/null +++ b/packages/geom/src/Point/service/PointInterpolateService.test.ts @@ -0,0 +1,227 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js interpolate test", () => +{ + + it("interpolate test case1", () => + { + const p1 = new Point(0, 0); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(3); + expect(p3.y).toBe(4); + }); + + it("interpolate test2 case", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(7.5); + expect(p3.y).toBe(9); + }); + + it("interpolate test case3", () => + { + const p1 = new Point(-9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(-1.5); + expect(p3.y).toBe(9); + }); + + it("interpolate test case4", () => + { + const p1 = new Point(9, -10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(7.5); + expect(p3.y).toBe(-1); + }); + + it("interpolate test case5", () => + { + const p1 = new Point(-9, -10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(-1.5); + expect(p3.y).toBe(-1); + }); + + it("interpolate test case6", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(-6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(1.5); + expect(p3.y).toBe(9); + }); + + it("interpolate test case7", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, -8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(7.5); + expect(p3.y).toBe(1); + }); + + it("interpolate test case8", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(-6, -8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(1.5); + expect(p3.y).toBe(1); + }); + + it("interpolate test case9", () => + { + const p1 = new Point(-9, 10); + const p2 = new Point(-6, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(-7.5); + expect(p3.y).toBe(9); + }); + + it("interpolate test case10", () => + { + const p1 = new Point(9, -10); + const p2 = new Point(6, -8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(7.5); + expect(p3.y).toBe(-9); + }); + + it("interpolate test case11", () => + { + const p1 = new Point(-9, -10); + const p2 = new Point(-6, -8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(-7.5); + expect(p3.y).toBe(-9); + }); + + it("interpolate test case12", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, -0.5); + + expect(p3.x).toBe(4.5); + expect(p3.y).toBe(7); + }); + + it("interpolate test case13", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(9, 8); + const p3 = Point.interpolate(p1, p2, 0.5); + + expect(p3.x).toBe(9); + expect(p3.y).toBe(9); + }); + + it("interpolate test case14", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 1); + + expect(p3.x).toBe(9); + expect(p3.y).toBe(10); + }); + + it("interpolate test case15", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0); + + expect(p3.x).toBe(6); + expect(p3.y).toBe(8); + }); + + it("interpolate test case16", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 1.5); + + expect(p3.x).toBe(10.5); + expect(p3.y).toBe(11); + }); + + it("interpolate test case17", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 0.2); + + expect(p3.x).toBe(6.6); + expect(p3.y).toBe(8.4); + }); + + it("interpolate test case18", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, -0.2); + + expect(p3.x).toBe(5.4); + expect(p3.y).toBe(7.6); + }); + + it("interpolate test case19", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, 1.2); + + expect(p3.x).toBe(9.6); + expect(p3.y).toBe(10.4); + }); + + it("interpolate test case20", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, -1.2); + + expect(p3.x).toBe(2.3999999999999995); + expect(p3.y).toBe(5.6); + }); + + it("interpolate test case21", () => + { + const p1 = new Point(9, 10); + const p2 = new Point(6, 8); + const p3 = Point.interpolate(p1, p2, -1); + + expect(p3.x).toBe(3); + expect(p3.y).toBe(6); + }); + + it("interpolate test case22", () => + { + const p1 = new Point(6, 8); + const p2 = new Point(9, 10); + const p3 = Point.interpolate(p1, p2, -1); + + expect(p3.x).toBe(12); + expect(p3.y).toBe(12); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointInterpolateService.ts b/packages/geom/src/Point/service/PointInterpolateService.ts new file mode 100644 index 00000000..739223a6 --- /dev/null +++ b/packages/geom/src/Point/service/PointInterpolateService.ts @@ -0,0 +1,20 @@ +import { Point } from "../../Point"; + +/** + * @description 2点間の補間を返します。 + * Returns the interpolation between two points. + * + * @param {Point} point1 + * @param {Point} point2 + * @param {number} f + * @return {Point} + * @method + * @public + */ +export const execute = (point1: Point, point2: Point, f: number): Point => +{ + return new Point( + point1.x + (point2.x - point1.x) * (1 - f), + point1.y + (point2.y - point1.y) * (1 - f) + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointNormalizeService.test.ts b/packages/geom/src/Point/service/PointNormalizeService.test.ts new file mode 100644 index 00000000..30ba74be --- /dev/null +++ b/packages/geom/src/Point/service/PointNormalizeService.test.ts @@ -0,0 +1,60 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js normalize test", () => +{ + + it("normalize test case1", () => + { + const point = new Point(6, 8); + point.normalize(2.5); + + expect(point.x).toBe(1.5); + expect(point.y).toBe(2); + }); + + it("normalize test case2", () => + { + const point = new Point(6, 8); + point.normalize(0); + + expect(point.x).toBe(0); + expect(point.y).toBe(0); + }); + + it("normalize test case3", () => + { + const point = new Point(6, 8); + point.normalize(-2.5); + + expect(point.x).toBe(-1.5); + expect(point.y).toBe(-2); + }); + + it("normalize test case4", () => + { + const point = new Point(-6, 8); + point.normalize(2.5); + + expect(point.x).toBe(-1.5); + expect(point.y).toBe(2); + }); + + it("normalize test case5", () => + { + const point = new Point(6, -8); + point.normalize(2.5); + + expect(point.x).toBe(1.5); + expect(point.y).toBe(-2); + }); + + it("normalize test case6", () => + { + const point = new Point(-6, -8); + point.normalize(2.5); + + expect(point.x).toBe(-1.5); + expect(point.y).toBe(-2); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointNormalizeService.ts b/packages/geom/src/Point/service/PointNormalizeService.ts new file mode 100644 index 00000000..b91ad425 --- /dev/null +++ b/packages/geom/src/Point/service/PointNormalizeService.ts @@ -0,0 +1,18 @@ +import type { Point } from "../../Point"; + +/** + * @description (0,0) と現在のポイント間の線のセグメントを設定された長さに拡大 / 縮小します。 + * Expands / contracts the line segment between (0,0) and the current point to the specified length. + * + * @param {Point} point + * @param {number} thickness + * @return {void} + * @method + * @public + */ +export const execute = (point: Point, thickness: number): void => +{ + const value = thickness / point.length; + point.x *= value; + point.y *= value; +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointOffsetService.test.ts b/packages/geom/src/Point/service/PointOffsetService.test.ts new file mode 100644 index 00000000..157de0d4 --- /dev/null +++ b/packages/geom/src/Point/service/PointOffsetService.test.ts @@ -0,0 +1,52 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js offset test", () => +{ + + it("offset test case1", () => + { + const point = new Point(10, 20); + point.offset(30, 40); + + expect(point.x).toBe(40); + expect(point.y).toBe(60); + }); + + it("offset test case2", () => + { + const point = new Point(10, 20); + point.offset(-30, 40); + + expect(point.x).toBe(-20); + expect(point.y).toBe(60); + }); + + it("offset test case3", () => + { + const point = new Point(10, 20); + point.offset(30, -40); + + expect(point.x).toBe(40); + expect(point.y).toBe(-20); + }); + + it("offset test case4", () => + { + const point = new Point(-10, 20); + point.offset(30, 40); + + expect(point.x).toBe(20); + expect(point.y).toBe(60); + }); + + it("offset test case5", () => + { + const point = new Point(10, -20); + point.offset(30, 40); + + expect(point.x).toBe(40); + expect(point.y).toBe(20); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointOffsetService.ts b/packages/geom/src/Point/service/PointOffsetService.ts new file mode 100644 index 00000000..e54ffe72 --- /dev/null +++ b/packages/geom/src/Point/service/PointOffsetService.ts @@ -0,0 +1,18 @@ +import type { Point } from "../../Point"; + +/** + * @description 2点間の補間を返します。 + * Returns the interpolation between two points. + * + * @param {Point} point + * @param {number} dx + * @param {number} dy + * @return {Point} + * @method + * @public + */ +export const execute = (point: Point, dx: number, dy: number): void => +{ + point.x += dx; + point.y += dy; +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointPolarService.test.ts b/packages/geom/src/Point/service/PointPolarService.test.ts new file mode 100644 index 00000000..759f2007 --- /dev/null +++ b/packages/geom/src/Point/service/PointPolarService.test.ts @@ -0,0 +1,192 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js polar test case", () => +{ + + it("polar test case1", () => + { + const angle = Math.PI * 2 * (30 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(3.464101615137755); + expect(point.y).toBe(1.9999999999999998); + }); + + it("polar test case2", () => + { + const angle = Math.PI * 2 * (45 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x | 0).toBe(2); + expect(point.y | 0).toBe(2); + + }); + + it("polar test case3", () => + { + const angle = Math.PI * 2 * (90 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(2.4492935982947064e-16); + expect(point.y).toBe(4); + }); + + it("polar test case4", () => + { + const angle = Math.PI * 2 * (135 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-2.82842712474619); + expect(point.y).toBe(2.8284271247461903); + }); + + it("polar test case5", () => + { + const angle = Math.PI * 2 * (180 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-4); + expect(point.y).toBe(4.898587196589413e-16); + }); + + it("polar test case6", () => + { + const angle = Math.PI * 2 * (225 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-2.8284271247461907); + expect(point.y).toBe(-2.82842712474619); + }); + + it("polar test case7", () => + { + const angle = Math.PI * 2 * (270 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-7.347880794884119e-16); + expect(point.y).toBe(-4); + }); + + it("polar test case8", () => + { + const angle = Math.PI * 2 * (315 / 360); // 30 degrees + const point = Point.polar(4, angle); + if (point.x > 2.8284271247461894) { + expect(point.x).toBe(2.82842712474619); + } else { + expect(point.x).toBe(2.8284271247461894); + } + expect(point.y).toBe(-2.8284271247461907); + }); + + it("polar test case9", () => + { + const angle = Math.PI * 2 * (360 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(4); + expect(point.y).toBe(-9.797174393178826e-16); + }); + + it("polar test case10", () => + { + const angle = Math.PI * 2 * (-30 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(3.464101615137755); + expect(point.y).toBe(-1.9999999999999998); + }); + + it("polar test case11", () => + { + const angle = Math.PI * 2 * (-45 / 360); // 30 degrees + const point = Point.polar(4, angle); + expect(point.x | 0).toBe(2); + expect(point.y | 0).toBe(-2); + }); + + it("polar test case12", () => + { + const angle = Math.PI * 2 * (-90 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(2.4492935982947064e-16); + expect(point.y).toBe(-4); + }); + + it("polar test case13", () => + { + const angle = Math.PI * 2 * (-135 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-2.82842712474619); + expect(point.y).toBe(-2.8284271247461903); + }); + + it("polar test case14", () => + { + const angle = Math.PI * 2 * (-180 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-4); + expect(point.y).toBe(-4.898587196589413e-16); + }); + + it("polar test case15", () => + { + const angle = Math.PI * 2 * (-225 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-2.8284271247461907); + expect(point.y).toBe(2.82842712474619); + }); + + it("polar test case16", () => + { + const angle = Math.PI * 2 * (-270 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(-7.347880794884119e-16); + expect(point.y).toBe(4); + }); + + it("polar test case17", () => + { + const angle = Math.PI * 2 * (-315 / 360); // 30 degrees + const point = Point.polar(4, angle); + if (point.x > 2.8284271247461894) { + expect(point.x).toBe(2.82842712474619); + } else { + expect(point.x).toBe(2.8284271247461894); + } + expect(point.y).toBe(2.8284271247461907); + }); + + it("polar test case18", () => + { + const angle = Math.PI * 2 * (-360 / 360); // 30 degrees + const point = Point.polar(4, angle); + + expect(point.x).toBe(4); + expect(point.y).toBe(9.797174393178826e-16); + }); + + it("polar test case19", () => + { + const angle = Math.PI * 2 * (30 / 360); // 30 degrees + const point = Point.polar(0, angle); + + expect(point.x).toBe(0); + expect(point.y).toBe(0); + }); + + it("polar test case20", () => + { + const angle = Math.PI * 2 * (30 / 360); // 30 degrees + const point = Point.polar(-4, angle); + + expect(point.x).toBe(-3.464101615137755); + expect(point.y).toBe(-1.9999999999999998); + }); +}); diff --git a/packages/geom/src/Point/service/PointPolarService.ts b/packages/geom/src/Point/service/PointPolarService.ts new file mode 100644 index 00000000..cdb184ec --- /dev/null +++ b/packages/geom/src/Point/service/PointPolarService.ts @@ -0,0 +1,16 @@ +import { Point } from "../../Point"; + +/** + * @description 指定された長さと角度からポイントを返します。 + * Returns a point from the specified length and angle. + * + * @param {number} length + * @param {number} angle + * @return {Point} + * @method + * @public + */ +export const execute = (length: number, angle: number): Point => +{ + return new Point(length * Math.cos(angle), length * Math.sin(angle)); +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointSetToService.test.ts b/packages/geom/src/Point/service/PointSetToService.test.ts new file mode 100644 index 00000000..14af73d2 --- /dev/null +++ b/packages/geom/src/Point/service/PointSetToService.test.ts @@ -0,0 +1,25 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js setTo test", () => +{ + + it("setTo test case1", () => + { + const point = new Point(10, 20); + point.setTo(30, 40); + + expect(point.x).toBe(30); + expect(point.y).toBe(40); + }); + + it("setTo test case2", () => + { + const p = new Point(10, 20); + p.setTo(0, 40); + + expect(p.x).toBe(0); + expect(p.y).toBe(40); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointSetToService.ts b/packages/geom/src/Point/service/PointSetToService.ts new file mode 100644 index 00000000..16a6e29a --- /dev/null +++ b/packages/geom/src/Point/service/PointSetToService.ts @@ -0,0 +1,18 @@ +import type { Point } from "../../Point"; + +/** + * @description 指定されたポイントの座標を設定します。 + * Sets the coordinates of the specified point. + * + * @param {Point} point + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @public + */ +export const execute = (point: Point, x: number, y: number): void => +{ + point.x = x; + point.y = y; +}; \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointSubtractService.test.ts b/packages/geom/src/Point/service/PointSubtractService.test.ts new file mode 100644 index 00000000..6af7342d --- /dev/null +++ b/packages/geom/src/Point/service/PointSubtractService.test.ts @@ -0,0 +1,27 @@ +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Point.js subtract test", () => +{ + + it("subtract test case1", () => + { + const p1 = new Point(6, 8); + const p2 = new Point(1.5, 2); + const p3 = p1.subtract(p2); + + expect(p3.x).toBe(4.5); + expect(p3.y).toBe(6); + }); + + it("subtract test case2", () => + { + const p1 = new Point(6, 8); + const p2 = new Point(-1, 2); + const p3 = p1.subtract(p2); + + expect(p3.x).toBe(7); + expect(p3.y).toBe(6); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Point/service/PointSubtractService.ts b/packages/geom/src/Point/service/PointSubtractService.ts new file mode 100644 index 00000000..38bc67a6 --- /dev/null +++ b/packages/geom/src/Point/service/PointSubtractService.ts @@ -0,0 +1,16 @@ +import { Point } from "../../Point"; + +/** + * @description 指定の座標を減算して、新しいポイントを作成 + * Subtracts the specified coordinates to create a new point. + * + * @param {Point} point1 + * @param {Point} point2 + * @return {Point} + * @method + * @public + */ +export const execute = (point1: Point, point2: Point): Point => +{ + return new Point(point1.x - point2.x, point1.y - point2.y); +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle.test.ts b/packages/geom/src/Rectangle.test.ts new file mode 100644 index 00000000..ed663134 --- /dev/null +++ b/packages/geom/src/Rectangle.test.ts @@ -0,0 +1,230 @@ +import { Rectangle } from "./Rectangle"; +import { Point } from "./Point"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js property test", () => +{ + it("top test case1", () => + { + const rectangle = new Rectangle(50, 50, 100, 100); + expect(rectangle.top).toBe(50); + expect(rectangle.bottom).toBe(150); + + // success + rectangle.top = 160; + expect(rectangle.x).toBe(50); + expect(rectangle.y).toBe(160); + expect(rectangle.width).toBe(100); + expect(rectangle.height).toBe(-10); + expect(rectangle.bottom).toBe(150); + expect(rectangle.y).toBe(160); + + rectangle.top = 0; + expect(rectangle.y).toBe(0); + expect(rectangle.height).toBe(150); + }); + + it("top test case2", () => + { + const rectangle = new Rectangle(-50, -50, -100, -100); + expect(rectangle.top).toBe(-50); + expect(rectangle.bottom).toBe(-150); + + // success + rectangle.top = 160; + expect(rectangle.x).toBe(-50); + expect(rectangle.y).toBe(160); + expect(rectangle.width).toBe(-100); + expect(rectangle.height).toBe(-310); + expect(rectangle.bottom).toBe(-150); + expect(rectangle.y).toBe(160); + }); + + it("right test case1", () => + { + const rectangle = new Rectangle(50, 100, 100, 100); + expect(rectangle.right).toBe(150); + + // success + rectangle.right = 20; + expect(rectangle.x).toBe(50); + expect(rectangle.y).toBe(100); + expect(rectangle.width).toBe(-30); + expect(rectangle.height).toBe(100); + + rectangle.right = 0; + expect(rectangle.right).toBe(0); + }); + + it("right test case2", () => + { + const rectangle = new Rectangle(50, -100, -100, -100); + expect(rectangle.right).toBe(-50); + + // success + rectangle.right = 20; + expect(rectangle.x).toBe(50); + expect(rectangle.y).toBe(-100); + expect(rectangle.width).toBe(-30); + expect(rectangle.height).toBe(-100); + + rectangle.right = 0; + expect(rectangle.right).toBe(0); + }); + + it("bottom test case1", () => + { + const rectangle = new Rectangle(0, 100, 100, 100); + expect(rectangle.bottom).toBe(200); + + // success + rectangle.bottom = 50; + expect(rectangle.x).toBe(0); + expect(rectangle.y).toBe(100); + expect(rectangle.width).toBe(100); + expect(rectangle.height).toBe(-50); + + rectangle.bottom = 0; + expect(rectangle.height).toBe(-100); + }); + + it("bottom test case2", () => + { + const rectangle = new Rectangle(0, -100, -100, -100); + expect(rectangle.bottom).toBe(-200); + + // success + rectangle.bottom = -50; + expect(rectangle.x).toBe(0); + expect(rectangle.y).toBe(-100); + expect(rectangle.width).toBe(-100); + expect(rectangle.height).toBe(50); + + rectangle.bottom = 0; + expect(rectangle.height).toBe(100); + }); + + it("left test case1", () => + { + const rectangle = new Rectangle(50, 50, 100, 100); + expect(rectangle.left).toBe(50); + expect(rectangle.right).toBe(150); + + // success + rectangle.left = 160; + expect(rectangle.x).toBe(160); + expect(rectangle.y).toBe(50); + expect(rectangle.width).toBe(-10); + expect(rectangle.height).toBe(100); + expect(rectangle.right).toBe(150); + expect(rectangle.x).toBe(160); + + rectangle.left = 0; + expect(rectangle.x).toBe(0); + expect(rectangle.width).toBe(150); + }); + + it("left test case2", () => + { + const rectangle = new Rectangle(-50, -50, -100, -100); + expect(rectangle.left).toBe(-50); + expect(rectangle.right).toBe(-150); + + // success + rectangle.left = 160; + expect(rectangle.x).toBe(160); + expect(rectangle.y).toBe(-50); + expect(rectangle.width).toBe(-310); + expect(rectangle.height).toBe(-100); + expect(rectangle.right).toBe(-150); + expect(rectangle.x).toBe(160); + + rectangle.left = 0; + expect(rectangle.x).toBe(0); + expect(rectangle.width).toBe(-150); + }); + + it("bottomRight test case1", () => + { + const rectangle = new Rectangle(30, 50, 80, 100); + const point = rectangle.bottomRight; + expect(point.x).toBe(110); + expect(point.y).toBe(150); + + rectangle.bottomRight = new Point(10 ,10); + expect(rectangle.x).toBe(30); + expect(rectangle.y).toBe(50); + expect(rectangle.width).toBe(-20); + expect(rectangle.height).toBe(-40); + }); + + it("bottomRight test case2", () => + { + const rectangle = new Rectangle(-30, -50, -80, -100); + const point = rectangle.bottomRight; + expect(point.x).toBe(-110); + expect(point.y).toBe(-150); + + rectangle.bottomRight = new Point(10 ,10); + expect(rectangle.x).toBe(-30); + expect(rectangle.y).toBe(-50); + expect(rectangle.width).toBe(40); + expect(rectangle.height).toBe(60); + }); + + it("topLeft test case1", () => + { + const rectangle = new Rectangle(30, 50, 80, 100); + const point = rectangle.topLeft; + expect(point.x).toBe(30); + expect(point.y).toBe(50); + + rectangle.topLeft = new Point(10 ,10); + expect(rectangle.x).toBe(10); + expect(rectangle.y).toBe(10); + expect(rectangle.width).toBe(100); + expect(rectangle.height).toBe(140); + }); + + it("topLeft test case2", () => + { + const rectangle = new Rectangle(-30, -50, -80, -100); + const point = rectangle.topLeft; + expect(point.x).toBe(-30); + expect(point.y).toBe(-50); + + rectangle.topLeft = new Point(10 ,10); + expect(rectangle.x).toBe(10); + expect(rectangle.y).toBe(10); + expect(rectangle.width).toBe(-120); + expect(rectangle.height).toBe(-160); + }); + + it("size test case1", () => + { + const rectangle = new Rectangle(30, 50, 80, 100); + const point = rectangle.size; + expect(point.x).toBe(80); + expect(point.y).toBe(100); + + rectangle.size = new Point(10 ,10); + expect(rectangle.x).toBe(30); + expect(rectangle.y).toBe(50); + expect(rectangle.width).toBe(10); + expect(rectangle.height).toBe(10); + }); + + it("size test case2", () => + { + const rectangle = new Rectangle(-30, -50, -80, -100); + const point = rectangle.size; + expect(point.x).toBe(-80); + expect(point.y).toBe(-100); + + rectangle.size = new Point(10 ,10); + expect(rectangle.x).toBe(-30); + expect(rectangle.y).toBe(-50); + expect(rectangle.width).toBe(10); + expect(rectangle.height).toBe(10); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle.ts b/packages/geom/src/Rectangle.ts index d21eb15a..6a81924b 100644 --- a/packages/geom/src/Rectangle.ts +++ b/packages/geom/src/Rectangle.ts @@ -1,140 +1,95 @@ import { Point } from "./Point"; -import { - $Math, - $clamp, - $SHORT_INT_MIN, - $SHORT_INT_MAX -} from "@next2d/share"; +import { execute as rectangleCloneService } from "../src/Rectangle/service/RectangleCloneService"; +import { execute as rectangleContainsService } from "../src/Rectangle/service/RectangleContainsService"; +import { execute as rectangleContainsPointService } from "../src/Rectangle/service/RectangleContainsPointService"; +import { execute as rectangleContainsRectService } from "../src/Rectangle/service/RectangleContainsRectService"; +import { execute as rectangleCopyFromService } from "../src/Rectangle/service/RectangleCopyFromService"; +import { execute as rectangleEqualsService } from "../src/Rectangle/service/RectangleEqualsService"; +import { execute as rectangleInflateService } from "../src/Rectangle/service/RectangleInflateService"; +import { execute as rectangleInflatePointService } from "../src/Rectangle/service/RectangleInflatePointService"; +import { execute as rectangleIntersectionService } from "../src/Rectangle/service/RectangleIntersectionService"; +import { execute as rectangleIntersectsService } from "../src/Rectangle/service/RectangleIntersectsService"; +import { execute as rectangleIsEmptyService } from "../src/Rectangle/service/RectangleIsEmptyService"; +import { execute as rectangleOffsetService } from "../src/Rectangle/service/RectangleOffsetService"; +import { execute as rectangleOffsetPointService } from "../src/Rectangle/service/RectangleOffsetPointService"; +import { execute as rectangleSetEmptyService } from "../src/Rectangle/service/RectangleSetEmptyService"; +import { execute as rectangleSetToService } from "../src/Rectangle/service/RectangleSetToService"; +import { execute as rectangleUnionService } from "../src/Rectangle/service/RectangleUnionService"; /** - * Rectangle オブジェクトは、その位置(左上隅のポイント (x, y) で示される)、および幅と高さで定義される領域です。 - * Rectangle クラスの x、y、width、および height の各プロパティは、互いに独立しているため、 - * あるプロパティの値を変更しても、他のプロパティに影響はありません。 - * ただし、right プロパティと bottom プロパティはこれら 4 つのプロパティと不可分に関連しています。 - * 例えば、right プロパティの値を変更すると width プロパティの値も変更されます。 - * bottom プロパティの値を変更すると、height プロパティの値も変更されます。 + * @description Rectangle オブジェクトは、その位置(左上隅のポイント (x, y) で示される)、および幅と高さで定義される領域です。 + * Rectangle クラスの x、y、width、および height の各プロパティは、互いに独立しているため、 + * あるプロパティの値を変更しても、他のプロパティに影響はありません。 + * ただし、right プロパティと bottom プロパティはこれら 4 つのプロパティと不可分に関連しています。 + * 例えば、right プロパティの値を変更すると width プロパティの値も変更されます。 + * bottom プロパティの値を変更すると、height プロパティの値も変更されます。 * - * A Rectangle object is an area defined by its position, - * as indicated by its top-left corner point (x, y) and by its width and its height. - * The x, y, width, and height properties of the Rectangle class are independent of each other; - * changing the value of one property has no effect on the others. However, - * the right and bottom properties are integrally related to those four properties. - * For example, if you change the value of the right property, the value of the width property changes; - * if you change the bottom property, the value of the height property changes. - * - * @example Example usage of Rectangle. - * // new Rectangle - * const {Rectangle} = next2d.geom; - * const rectangle = new Rectangle(0, 0, 100, 100); + * A Rectangle object is an area defined by its position, + * as indicated by its top-left corner point (x, y) and by its width and its height. + * The x, y, width, and height properties of the Rectangle class are independent of each other; + * changing the value of one property has no effect on the others. However, + * the right and bottom properties are integrally related to those four properties. + * For example, if you change the value of the right property, the value of the width property changes; + * if you change the bottom property, the value of the height property changes. * * @class * @memberOf next2d.geom */ export class Rectangle { - private _$x: number; - private _$y: number; - private _$width: number; - private _$height: number; - /** - * @param {number} [x=0] - * @param {number} [y=0] - * @param {number} [width=0] - * @param {number} [height=0] + * @description 矩形の左上隅の x 座標です。 + * The x coordinate of the top-left corner of the rectangle. * - * @constructor + * @member {number} * @public */ - constructor ( - x: number = 0, y: number = 0, - width: number = 0, height: number = 0 - ) { - /** - * @type {number} - * @default 0 - * @private - */ - this._$x = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$y = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$width = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$height = 0; - - // init - this.setTo(x, y, width, height); - } + public x: number; /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description 矩形の左上隅の y 座標です。 + * The y coordinate of the top-left corner of the rectangle. * - * @return {string} - * @default [class Rectangle] - * @method - * @static + * @member {number} + * @public */ - static toString (): string - { - return "[class Rectangle]"; - } + public y: number; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description 矩形の幅(ピクセル単位)です。 + * The width of the rectangle, in pixels. * - * @member {string} - * @default next2d.geom.Rectangle - * @const - * @static + * @member {number} + * @public */ - static get namespace (): string - { - return "next2d.geom.Rectangle"; - } + public width: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description 矩形の高さ(ピクセル単位)です。 + * The height of the rectangle, in pixels. * - * @return {string} - * @method + * @member {number} * @public */ - toString (): string - { - return `(x=${this.x}, y=${this.y}, w=${this.width}, h=${this.height})`; - } + public height: number; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @param {number} [x=0] + * @param {number} [y=0] + * @param {number} [width=0] + * @param {number} [height=0] * - * @member {string} - * @default next2d.geom.Rectangle - * @const + * @constructor * @public */ - get namespace (): string - { - return "next2d.geom.Rectangle"; + constructor ( + x: number = 0, y: number = 0, + width: number = 0, height: number = 0 + ) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; } /** @@ -150,7 +105,7 @@ export class Rectangle } set bottom (bottom: number) { - this.height = +bottom - this.y; + this.height = bottom - this.y; } /** @@ -172,22 +127,6 @@ export class Rectangle this.bottom = point.y; } - /** - * @description 矩形の高さ(ピクセル単位)です。 - * The height of the rectangle, in pixels. - * - * @member {number} - * @public - */ - get height (): number - { - return this._$height; - } - set height (height: number) - { - this._$height = $clamp(+height, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); - } - /** * @description 矩形の左上隅の x 座標です。 * The x coordinate of the top-left corner of the rectangle. @@ -201,7 +140,7 @@ export class Rectangle } set left (left: number) { - this.width = this.right - +left; + this.width = this.right - left; this.x = left; } @@ -218,7 +157,7 @@ export class Rectangle } set right (right: number) { - this.width = +right - this.x; + this.width = right - this.x; } /** @@ -253,7 +192,7 @@ export class Rectangle } set top (top: number) { - this.height = +(this.bottom - +top); + this.height = this.bottom - top; this.y = top; } @@ -276,54 +215,6 @@ export class Rectangle this.top = point.y; } - /** - * @description 矩形の幅(ピクセル単位)です。 - * The width of the rectangle, in pixels. - * - * @member {number} - * @public - */ - get width (): number - { - return this._$width; - } - set width (width: number) - { - this._$width = $clamp(+width, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); - } - - /** - * @description 矩形の左上隅の x 座標です。 - * The x coordinate of the top-left corner of the rectangle. - * - * @member {number} - * @public - */ - get x (): number - { - return this._$x; - } - set x (x: number) - { - this._$x = $clamp(+x, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); - } - - /** - * @description 矩形の左上隅の y 座標です。 - * The y coordinate of the top-left corner of the rectangle. - * - * @member {number} - * @public - */ - get y (): number - { - return this._$y; - } - set y (y: number) - { - this._$y = $clamp(+y, $SHORT_INT_MIN, $SHORT_INT_MAX, 0); - } - /** * @description 元の Rectangle オブジェクトと x、y、width、および height の各プロパティの値が同じである、 * 新しい Rectangle オブジェクトを返します。 @@ -336,7 +227,7 @@ export class Rectangle */ clone (): Rectangle { - return new Rectangle(this.x, this.y, this.width, this.height); + return rectangleCloneService(this); } /** @@ -352,7 +243,7 @@ export class Rectangle */ contains (x: number, y: number): boolean { - return this.x <= x && this.y <= y && this.right > x && this.bottom > y; + return rectangleContainsService(this, x, y); } /** @@ -367,8 +258,7 @@ export class Rectangle */ containsPoint (point: Point): boolean { - return this.x <= point.x && this.y <= point.y && - this.right > point.x && this.bottom > point.y; + return rectangleContainsPointService(this, point); } /** @@ -376,15 +266,14 @@ export class Rectangle * Determines whether the Rectangle object specified by * the rect parameter is contained within this Rectangle object. * - * @param {Rectangle} rect + * @param {Rectangle} rectangle * @return {boolean} * @method * @public */ - containsRect (rect: Rectangle): boolean + containsRect (rectangle: Rectangle): boolean { - return this.x <= rect.x && this.y <= rect.y && - this.right >= rect.right && this.bottom >= rect.bottom; + return rectangleContainsRectService(this, rectangle); } /** @@ -393,17 +282,14 @@ export class Rectangle * Copies all of rectangle data from * the source Rectangle object into the calling Rectangle object. * - * @param {Rectangle} source_rect + * @param {Rectangle} rectangle * @return {void} * @method * @public */ - copyFrom (source_rect: Rectangle): void + copyFrom (rectangle: Rectangle): void { - this.x = source_rect.x; - this.y = source_rect.y; - this.width = source_rect.width; - this.height = source_rect.height; + rectangleCopyFromService(this, rectangle); } /** @@ -412,15 +298,14 @@ export class Rectangle * Determines whether the object specified * in the toCompare parameter is equal to this Rectangle object. * - * @param {Rectangle} to_compare + * @param {Rectangle} rectangle * @return {boolean} * @method * @public */ - equals (to_compare: Rectangle): boolean + equals (rectangle: Rectangle): boolean { - return this.x === to_compare.x && this.y === to_compare.y && - this.width === to_compare.width && this.height === to_compare.height; + return rectangleEqualsService(this, rectangle); } /** @@ -429,17 +314,13 @@ export class Rectangle * * @param {number} dx * @param {number} dy - * @return void + * @return {void} * @method * @public */ inflate (dx: number, dy: number): void { - this.x = this.x - +dx; - this.width = this.width + 2 * +dx; - - this.y = this.y - +dy; - this.height = this.height + 2 * +dy; + rectangleInflateService(this, dx, dy); } /** @@ -453,11 +334,7 @@ export class Rectangle */ inflatePoint (point: Point): void { - this.x = this.x - point.x; - this.width = this.width + 2 * point.x; - - this.y = this.y - point.y; - this.height = this.height + 2 * point.y; + rectangleInflatePointService(this, point); } /** @@ -466,21 +343,14 @@ export class Rectangle * If the Rectangle object specified in the toIntersect parameter intersects * with this Rectangle object, returns the area of intersection as a Rectangle object. * - * @param {Rectangle} to_intersect + * @param {Rectangle} rectangle * @return {Rectangle} * @method * @public */ - intersection (to_intersect: Rectangle): Rectangle + intersection (rectangle: Rectangle): Rectangle { - const sx = $Math.max(this.x, to_intersect.x); - const sy = $Math.max(this.y, to_intersect.y); - const ex = $Math.min(this.right, to_intersect.right); - const ey = $Math.min(this.bottom, to_intersect.bottom); - - const w = ex - sx; - const h = ey - sy; - return w > 0 && h > 0 ? new Rectangle(sx, sy, w, h) : new Rectangle(0, 0, 0, 0); + return rectangleIntersectionService(this, rectangle); } /** @@ -489,18 +359,14 @@ export class Rectangle * Determines whether the object specified * in the toIntersect parameter intersects with this Rectangle object. * - * @param {Rectangle} to_intersect + * @param {Rectangle} rectangle * @return {boolean} * @method * @public */ - intersects (to_intersect: Rectangle): boolean + intersects (rectangle: Rectangle): boolean { - const sx = $Math.max(this.x, to_intersect.x); - const sy = $Math.max(this.y, to_intersect.y); - const ex = $Math.min(this.right, to_intersect.right); - const ey = $Math.min(this.bottom, to_intersect.bottom); - return ex - sx > 0 && ey - sy > 0; + return rectangleIntersectsService(this, rectangle); } /** @@ -513,7 +379,7 @@ export class Rectangle */ isEmpty (): boolean { - return this.width <= 0 || this.height <= 0; + return rectangleIsEmptyService(this); } /** @@ -529,8 +395,7 @@ export class Rectangle */ offset (dx: number ,dy: number): void { - this.x += dx; - this.y += dy; + rectangleOffsetService(this, dx, dy); } /** @@ -544,8 +409,7 @@ export class Rectangle */ offsetPoint (point: Point): void { - this.x += point.x; - this.y += point.y; + rectangleOffsetPointService(this, point); } /** @@ -558,10 +422,7 @@ export class Rectangle */ setEmpty (): void { - this._$x = 0; - this._$y = 0; - this._$width = 0; - this._$height = 0; + rectangleSetEmptyService(this); } /** @@ -578,10 +439,7 @@ export class Rectangle */ setTo (x: number, y: number, width: number, height: number): void { - this.x = x; - this.y = y; - this.width = width; - this.height = height; + rectangleSetToService(this, x, y, width, height); } /** @@ -590,26 +448,13 @@ export class Rectangle * Adds two rectangles together to create a new Rectangle object, * by filling in the horizontal and vertical space between the two rectangles. * - * @param {Rectangle} to_union + * @param {Rectangle} rectangle * @return {Rectangle} * @method * @public */ - union (to_union: Rectangle): Rectangle + union (rectangle: Rectangle): Rectangle { - if (this.isEmpty()) { - return to_union.clone(); - } - - if (to_union.isEmpty()) { - return this.clone(); - } - - return new Rectangle( - $Math.min(this.x, to_union.x), - $Math.min(this.y, to_union.y), - $Math.max(this.right - to_union.left, to_union.right - this.left), - $Math.max(this.bottom - to_union.top, to_union.bottom - this.top) - ); + return rectangleUnionService(this, rectangle); } } diff --git a/packages/geom/src/Rectangle/service/RectangleCloneService.test.ts b/packages/geom/src/Rectangle/service/RectangleCloneService.test.ts new file mode 100644 index 00000000..f3ffe1e9 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleCloneService.test.ts @@ -0,0 +1,21 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js clone test", () => +{ + it("clone test", () => + { + const rectangle1 = new Rectangle(30, 50, 80, 100); + const rectangle2 = rectangle1.clone(); + rectangle2.x = 100; + + expect(rectangle1.x).toBe(30); + expect(rectangle2.x).toBe(100); + expect(rectangle1.y).toBe(50); + expect(rectangle2.y).toBe(50); + expect(rectangle1.width).toBe(80); + expect(rectangle2.width).toBe(80); + expect(rectangle1.height).toBe(100); + expect(rectangle2.height).toBe(100); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleCloneService.ts b/packages/geom/src/Rectangle/service/RectangleCloneService.ts new file mode 100644 index 00000000..519a59c0 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleCloneService.ts @@ -0,0 +1,15 @@ +import { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleを複製を返却 + * Returns a duplicate of the specified Rectangle + * + * @param {Rectangle} rectangle + * @return {Rectangle} + * @method + * @public + */ +export const execute = (rectangle: Rectangle): Rectangle => +{ + return new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsPointService.test.ts b/packages/geom/src/Rectangle/service/RectangleContainsPointService.test.ts new file mode 100644 index 00000000..0c0a6b88 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsPointService.test.ts @@ -0,0 +1,46 @@ +import { Rectangle } from "../../Rectangle"; +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js containsPoint test", () => +{ + it("containsPoint test case1", () => + { + const rectangle = new Rectangle(30, 50, 80, 100); + + const p1 = new Point(30, 50); + expect(rectangle.containsPoint(p1)).toBe(true); + + const p2 = new Point(110, 150); + expect(rectangle.containsPoint(p2)).toBe(false); + + const p3 = new Point(109, 149); + expect(rectangle.containsPoint(p3)).toBe(true); + + const p4 = new Point(20, 40); + expect(rectangle.containsPoint(p4)).toBe(false); + }); + + it("containsPoint test case2", () => + { + const rectangle = new Rectangle(-30, -50, -80, -100); + + const p1 = new Point(-30, -50); + expect(rectangle.containsPoint(p1)).toBe(false); + + const p2 = new Point(-110, -150); + expect(rectangle.containsPoint(p2)).toBe(false); + + const p3 = new Point(-109, -149); + expect(rectangle.containsPoint(p3)).toBe(false); + + const p5 = new Point(110, 150); + expect(rectangle.containsPoint(p5)).toBe(false); + + const p6 = new Point(109, 149); + expect(rectangle.containsPoint(p6)).toBe(false); + + const p4 = new Point(-20, -40); + expect(rectangle.containsPoint(p4)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsPointService.ts b/packages/geom/src/Rectangle/service/RectangleContainsPointService.ts new file mode 100644 index 00000000..cefaa0ee --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsPointService.ts @@ -0,0 +1,20 @@ +import type { Rectangle } from "../../Rectangle"; +import type { Point } from "../../Point"; + +/** + * @description 指定の座標がRectangle内に含まれるかを判定 + * Determines whether the specified coordinates are within the Rectangle + * + * @param {Rectangle} rectangle + * @param {Point} point + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, point: Point): boolean => +{ + return rectangle.x <= point.x + && rectangle.y <= point.y + && rectangle.right > point.x + && rectangle.bottom > point.y; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsRectService.test.ts b/packages/geom/src/Rectangle/service/RectangleContainsRectService.test.ts new file mode 100644 index 00000000..b107a9a4 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsRectService.test.ts @@ -0,0 +1,35 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js containsRect test", () => +{ + it("containsRect test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(15, 15, 5, 5); + expect(rectangle1.containsRect(rectangle2)).toBe(true); + + const rectangle3 = new Rectangle(10, 10, 20, 20); + const rectangle4 = new Rectangle(10, 10, 20, 20); + expect(rectangle3.containsRect(rectangle4)).toBe(true); + + const rectangle5 = new Rectangle(10, 10, 20, 20); + const rectangle6 = new Rectangle(9, 9, 20, 20); + expect(rectangle5.containsRect(rectangle6)).toBe(false); + + const rectangle7 = new Rectangle(10, 10, 20, 20); + const rectangle8 = new Rectangle(15, 15, 20, 20); + expect(rectangle7.containsRect(rectangle8)).toBe(false); + }); + + it("containsRect test case2", () => + { + const rectangle1 = new Rectangle(-10, -10, -20, -20); + const rectangle2 = new Rectangle(-15, -15, -5, -5); + expect(rectangle1.containsRect(rectangle2)).toBe(false); + + const rectangle3 = new Rectangle(-10, -10, 20, 20); + const rectangle4 = new Rectangle(-15, -15, 5, 5); + expect(rectangle3.containsRect(rectangle4)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsRectService.ts b/packages/geom/src/Rectangle/service/RectangleContainsRectService.ts new file mode 100644 index 00000000..6c3193c5 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsRectService.ts @@ -0,0 +1,19 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleがRectangle内に含まれるかを判定 + * Determines whether the specified Rectangle is within the Rectangle + * + * @param {Rectangle} rectangle1 + * @param {Rectangle} rectangle2 + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle1: Rectangle, rectangle2: Rectangle): boolean => +{ + return rectangle1.x <= rectangle2.x + && rectangle1.y <= rectangle2.y + && rectangle1.right >= rectangle2.right + && rectangle1.bottom >= rectangle2.bottom; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsService.test.ts b/packages/geom/src/Rectangle/service/RectangleContainsService.test.ts new file mode 100644 index 00000000..b2b15fae --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsService.test.ts @@ -0,0 +1,41 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js contains test", () => +{ + it("contains test case1", () => + { + const rectangle = new Rectangle(30, 50, 80, 100); + expect(rectangle.contains(30, 50)).toBe(true); + expect(rectangle.contains(110, 150)).toBe(false); + expect(rectangle.contains(109, 149)).toBe(true); + expect(rectangle.contains(20, 40)).toBe(false); + }); + + it("contains test case2", () => + { + const rectangle = new Rectangle(0, 0, 0, 0); + expect(rectangle.contains(0, 0)).toBe(false); + }); + + it("contains test case3", () => + { + const rectangle = new Rectangle(0, 0, 1, 1); + expect(rectangle.contains(0, 0)).toBe(true); + expect(rectangle.contains(0.000001, 0.000001)).toBe(true); + expect(rectangle.contains(0.999999, 0.999999)).toBe(true); + expect(rectangle.contains(1, 0)).toBe(false); + expect(rectangle.contains(0, 1)).toBe(false); + expect(rectangle.contains(1, 1)).toBe(false); + }); + + it("contains test case4", () => + { + const rectangle = new Rectangle(-1, -1, 1, 1); + expect(rectangle.contains(0, 0)).toBe(false); + expect(rectangle.contains(-1, -1)).toBe(true); + expect(rectangle.contains(-1, -0.5)).toBe(true); + expect(rectangle.contains(-0.5, -1)).toBe(true); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleContainsService.ts b/packages/geom/src/Rectangle/service/RectangleContainsService.ts new file mode 100644 index 00000000..9cd5d59c --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleContainsService.ts @@ -0,0 +1,20 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定の座標がRectangle内に含まれるかを判定 + * Determines whether the specified coordinates are within the Rectangle + * + * @param {Rectangle} rectangle + * @param {number} x + * @param {number} y + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, x: number, y: number): boolean => +{ + return rectangle.x <= x + && rectangle.y <= y + && rectangle.right > x + && rectangle.bottom > y; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleCopyFromService.test.ts b/packages/geom/src/Rectangle/service/RectangleCopyFromService.test.ts new file mode 100644 index 00000000..2f25cb0c --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleCopyFromService.test.ts @@ -0,0 +1,32 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js copyFrom test", () => +{ + it("copyFrom test", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(15, 15, 5, 5); + + rectangle1.copyFrom(rectangle2); + expect(rectangle1.x).toBe(15); + expect(rectangle1.y).toBe(15); + expect(rectangle1.width).toBe(5); + expect(rectangle1.height).toBe(5); + expect(rectangle2.x).toBe(15); + expect(rectangle2.y).toBe(15); + expect(rectangle2.width).toBe(5); + expect(rectangle2.height).toBe(5); + + rectangle1.x = rectangle1.y = 10; + rectangle1.width = rectangle1.height = 20; + expect(rectangle1.x).toBe(10); + expect(rectangle1.y).toBe(10); + expect(rectangle1.width).toBe(20); + expect(rectangle1.height).toBe(20); + expect(rectangle2.x).toBe(15); + expect(rectangle2.y).toBe(15); + expect(rectangle2.width).toBe(5); + expect(rectangle2.height).toBe(5); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleCopyFromService.ts b/packages/geom/src/Rectangle/service/RectangleCopyFromService.ts new file mode 100644 index 00000000..9cb0a8fc --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleCopyFromService.ts @@ -0,0 +1,19 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleの値をコピー + * Copy the value of the specified Rectangle + * + * @param {Rectangle} dst + * @param {Rectangle} src + * @return {void} + * @method + * @public + */ +export const execute = (dst: Rectangle, src: Rectangle): void => +{ + dst.x = src.x; + dst.y = src.y; + dst.width = src.width; + dst.height = src.height; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleEqualsService.test.ts b/packages/geom/src/Rectangle/service/RectangleEqualsService.test.ts new file mode 100644 index 00000000..83790e92 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleEqualsService.test.ts @@ -0,0 +1,16 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js equals test", () => +{ + it("equals test", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(10, 10, 20, 20); + expect(rectangle1.equals(rectangle2)).toBe(true); + + const rectangle3 = new Rectangle(10, 10, 20, 20); + const rectangle4 = new Rectangle(15, 15, 5, 5); + expect(rectangle3.equals(rectangle4)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleEqualsService.ts b/packages/geom/src/Rectangle/service/RectangleEqualsService.ts new file mode 100644 index 00000000..dbfc4b9e --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleEqualsService.ts @@ -0,0 +1,19 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleが等しいかを判定 + * Determines whether the specified Rectangle is equal + * + * @param {Rectangle} rectangle1 + * @param {Rectangle} dst + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle1: Rectangle, rectangle2: Rectangle): boolean => +{ + return rectangle1.x === rectangle2.x + && rectangle1.y === rectangle2.y + && rectangle1.width === rectangle2.width + && rectangle1.height === rectangle2.height; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleInflatePointService.test.ts b/packages/geom/src/Rectangle/service/RectangleInflatePointService.test.ts new file mode 100644 index 00000000..844eb675 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleInflatePointService.test.ts @@ -0,0 +1,48 @@ +import { Rectangle } from "../../Rectangle"; +import { Point } from "../../Point"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js inflatePoint test", () => +{ + it("inflatePoint test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const point1 = new Point(10, 10); + rectangle1.inflatePoint(point1); + + expect(rectangle1.x).toBe(0); + expect(rectangle1.y).toBe(0); + expect(rectangle1.width).toBe(40); + expect(rectangle1.height).toBe(40); + + const rectangle2 = new Rectangle(10, 10, 20, 20); + const point2 = new Point(20, 20); + rectangle2.inflatePoint(point2); + + expect(rectangle2.x).toBe(-10); + expect(rectangle2.y).toBe(-10); + expect(rectangle2.width).toBe(60); + expect(rectangle2.height).toBe(60); + }); + + it("inflatePoint test case2", () => + { + const rectangle1 = new Rectangle(-10, -10, -20, -20); + const point1 = new Point(10, 10); + rectangle1.inflatePoint(point1); + + expect(rectangle1.x).toBe(-20); + expect(rectangle1.y).toBe(-20); + expect(rectangle1.width).toBe(0); + expect(rectangle1.height).toBe(0); + + const rectangle2 = new Rectangle(-10, -10, 20, 20); + const point2 = new Point(20, 20); + rectangle2.inflatePoint(point2); + expect(rectangle2.x).toBe(-30); + expect(rectangle2.y).toBe(-30); + expect(rectangle2.width).toBe(60); + expect(rectangle2.height).toBe(60); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleInflatePointService.ts b/packages/geom/src/Rectangle/service/RectangleInflatePointService.ts new file mode 100644 index 00000000..ff920568 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleInflatePointService.ts @@ -0,0 +1,20 @@ +import type { Point } from "../../Point"; +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleの値を変更 + * Change the value of the specified Rectangle + * + * @param {Rectangle} rectangle + * @param {Point} point + * @return {void} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, point: Point): void => +{ + rectangle.x -= point.x; + rectangle.width += 2 * point.x; + rectangle.y -= point.y; + rectangle.height += 2 * point.y; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleInflateService.test.ts b/packages/geom/src/Rectangle/service/RectangleInflateService.test.ts new file mode 100644 index 00000000..834330b1 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleInflateService.test.ts @@ -0,0 +1,40 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js inflate test", () => +{ + it("inflate test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + rectangle1.inflate(10, 10); + + expect(rectangle1.x).toBe(0); + expect(rectangle1.y).toBe(0); + expect(rectangle1.width).toBe(40); + expect(rectangle1.height).toBe(40); + + const rectangle2 = new Rectangle(10, 10, 20, 20); + rectangle2.inflate(20, 20); + expect(rectangle2.x).toBe(-10); + expect(rectangle2.y).toBe(-10); + expect(rectangle2.width).toBe(60); + expect(rectangle2.height).toBe(60); + }); + + it("inflate test case2", () => + { + const rectangle1 = new Rectangle(-10, -10, -20, -20); + rectangle1.inflate(10, 10); + expect(rectangle1.x).toBe(-20); + expect(rectangle1.y).toBe(-20); + expect(rectangle1.width).toBe(0); + expect(rectangle1.height).toBe(0); + + const rectangle2 = new Rectangle(10, 10, 20, 20); + rectangle2.inflate(-20, -20); + expect(rectangle2.x).toBe(30); + expect(rectangle2.y).toBe(30); + expect(rectangle2.width).toBe(-20); + expect(rectangle2.height).toBe(-20); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleInflateService.ts b/packages/geom/src/Rectangle/service/RectangleInflateService.ts new file mode 100644 index 00000000..ab6af663 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleInflateService.ts @@ -0,0 +1,20 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleの値を変更 + * Change the value of the specified Rectangle + * + * @param {Rectangle} rectangle + * @param {number} dx + * @param {number} dy + * @return {void} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, dx: number, dy: number): void => +{ + rectangle.x -= dx; + rectangle.width += 2 * dx; + rectangle.y -= dy; + rectangle.height += 2 * dy; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIntersectionService.test.ts b/packages/geom/src/Rectangle/service/RectangleIntersectionService.test.ts new file mode 100644 index 00000000..820e9862 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIntersectionService.test.ts @@ -0,0 +1,125 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js intersection test", () => +{ + it("intersection test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(5, 5, 5, 5); + const rectangle3 = rectangle1.intersection(rectangle2); + + expect(rectangle3.x).toBe(0); + expect(rectangle3.y).toBe(0); + expect(rectangle3.width).toBe(0); + expect(rectangle3.height).toBe(0); + + const rectangle4 = new Rectangle(10, 10, 20, 20); + const rectangle5 = new Rectangle(15, 15, 5, 5); + const rectangle6 = rectangle4.intersection(rectangle5); + + expect(rectangle6.x).toBe(15); + expect(rectangle6.y).toBe(15); + expect(rectangle6.width).toBe(5); + expect(rectangle6.height).toBe(5); + + const rectangle7 = new Rectangle(10, 10, 20, 20); + const rectangle8 = new Rectangle(5, 5, 25, 25); + const rectangle9 = rectangle7.intersection(rectangle8); + + expect(rectangle9.x).toBe(10); + expect(rectangle9.y).toBe(10); + expect(rectangle9.width).toBe(20); + expect(rectangle9.height).toBe(20); + }); + + it("intersection test case2", () => + { + const rectangle1 = new Rectangle(-10, -10, -20, -20); + const rectangle2 = new Rectangle(-5, -5, -5, -5); + const rectangle3 = rectangle1.intersection(rectangle2); + + expect(rectangle3.x).toBe(0); + expect(rectangle3.y).toBe(0); + expect(rectangle3.width).toBe(0); + expect(rectangle3.height).toBe(0); + + const rectangle4 = new Rectangle(-10, -10, -20, -20); + const rectangle5 = new Rectangle(-15, -15, -5, -5); + const rectangle6 = rectangle4.intersection(rectangle5); + + expect(rectangle6.x).toBe(0); + expect(rectangle6.y).toBe(0); + expect(rectangle6.width).toBe(0); + expect(rectangle6.height).toBe(0); + + const rectangle7 = new Rectangle(-10, -10, -20, -20); + const rectangle8 = new Rectangle(-5, -5, -25, -25); + const rectangle9 = rectangle7.intersection(rectangle8); + + expect(rectangle9.x).toBe(0); + expect(rectangle9.y).toBe(0); + expect(rectangle9.width).toBe(0); + expect(rectangle9.height).toBe(0); + }); + + it("intersection test case3", () => + { + const rectangle1 = new Rectangle(-10, -10, 20, 20); + const rectangle2 = new Rectangle(-5, -5, 5, 5); + const rectangle3 = rectangle1.intersection(rectangle2); + + expect(rectangle3.x).toBe(-5); + expect(rectangle3.y).toBe(-5); + expect(rectangle3.width).toBe(5); + expect(rectangle3.height).toBe(5); + + const rectangle4 = new Rectangle(-10, -10, 20, 20); + const rectangle5 = new Rectangle(-15, -15, 5, 5); + const rectangle6 = rectangle4.intersection(rectangle5); + + expect(rectangle6.x).toBe(0); + expect(rectangle6.y).toBe(0); + expect(rectangle6.width).toBe(0); + expect(rectangle6.height).toBe(0); + + const rectangle7 = new Rectangle(-10, -10, 20, 20); + const rectangle8 = new Rectangle(-5, -5, 25, 25); + const rectangle9 = rectangle7.intersection(rectangle8); + + expect(rectangle9.x).toBe(-5); + expect(rectangle9.y).toBe(-5); + expect(rectangle9.width).toBe(15); + expect(rectangle9.height).toBe(15); + }); + + it("intersection test case4", () => + { + const rectangle1 = new Rectangle(-10, -10, 20, 20); + const rectangle2 = new Rectangle(-10, -10, 0, 10); + const rectangle3 = rectangle1.intersection(rectangle2); + + expect(rectangle3.x).toBe(0); + expect(rectangle3.y).toBe(0); + expect(rectangle3.width).toBe(0); + expect(rectangle3.height).toBe(0); + + const rectangle4 = new Rectangle(-10, -10, 20, 20); + const rectangle5 = new Rectangle(-10, -10, 5, 5); + const rectangle6 = rectangle4.intersection(rectangle5); + + expect(rectangle6.x).toBe(-10); + expect(rectangle6.y).toBe(-10); + expect(rectangle6.width).toBe(5); + expect(rectangle6.height).toBe(5); + + const rectangle7 = new Rectangle(-5, -5, -25, -25); + const rectangle8 = new Rectangle(-10, -10, -20, -20); + const rectangle9 = rectangle7.intersection(rectangle8); + + expect(rectangle9.x).toBe(0); + expect(rectangle9.y).toBe(0); + expect(rectangle9.width).toBe(0); + expect(rectangle9.height).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIntersectionService.ts b/packages/geom/src/Rectangle/service/RectangleIntersectionService.ts new file mode 100644 index 00000000..9f7e6159 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIntersectionService.ts @@ -0,0 +1,25 @@ +import { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleの交差部分を取得 + * Get the intersection of the specified Rectangle + * + * @param {Rectangle} rectangle1 + * @param {Rectangle} rectangle2 + * @return {Rectangle} + * @method + * @public + */ +export const execute = (rectangle1: Rectangle, rectangle2: Rectangle): Rectangle => +{ + const sx = Math.max(rectangle1.x, rectangle2.x); + const sy = Math.max(rectangle1.y, rectangle2.y); + const ex = Math.min(rectangle1.right, rectangle2.right); + const ey = Math.min(rectangle1.bottom, rectangle2.bottom); + + const w = ex - sx; + const h = ey - sy; + return w > 0 && h > 0 + ? new Rectangle(sx, sy, w, h) + : new Rectangle(0, 0, 0, 0); +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIntersectsService.test.ts b/packages/geom/src/Rectangle/service/RectangleIntersectsService.test.ts new file mode 100644 index 00000000..df278e3b --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIntersectsService.test.ts @@ -0,0 +1,38 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js intersects test", () => +{ + it("intersects test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(5, 5, 5, 5); + expect(rectangle1.intersects(rectangle2)).toBe(false); + + const rectangle3 = new Rectangle(10, 10, 20, 20); + const rectangle4 = new Rectangle(5, 5, 25, 25); + expect(rectangle3.intersects(rectangle4)).toBe(true); + }); + + it("intersects test case2", () => + { + const rectangle1 = new Rectangle(-10, -10, -20, -20); + const rectangle2 = new Rectangle(-5, -5, -25, -25); + expect(rectangle1.intersects(rectangle2)).toBe(false); + + const rectangle3 = new Rectangle(-10, -10, 20, 20); + const rectangle4 = new Rectangle(-5, -5, 25, 25); + expect(rectangle3.intersects(rectangle4)).toBe(true); + }); + + it("intersects test case3", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(5, 40, 10, 10); + expect(rectangle1.intersects(rectangle2)).toBe(false); + + const rectangle3 = new Rectangle(10, 10, 20, 20); + const rectangle4 = new Rectangle(5, 15, 10, 10); + expect(rectangle3.intersects(rectangle4)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIntersectsService.ts b/packages/geom/src/Rectangle/service/RectangleIntersectsService.ts new file mode 100644 index 00000000..37fc4440 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIntersectsService.ts @@ -0,0 +1,20 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangle同士が重なっているかどうかを判定 + * Determine whether the specified Rectangles overlap + * + * @param {Rectangle} rectangle1 + * @param {Rectangle} rectangle2 + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle1: Rectangle, rectangle2: Rectangle): boolean => +{ + const sx = Math.max(rectangle1.x, rectangle2.x); + const sy = Math.max(rectangle1.y, rectangle2.y); + const ex = Math.min(rectangle1.right, rectangle2.right); + const ey = Math.min(rectangle1.bottom, rectangle2.bottom); + return ex - sx > 0 && ey - sy > 0; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIsEmptyService.test.ts b/packages/geom/src/Rectangle/service/RectangleIsEmptyService.test.ts new file mode 100644 index 00000000..41975122 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIsEmptyService.test.ts @@ -0,0 +1,25 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js isEmpty test", () => +{ + it("isEmpty test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(-55, -55, 0, 0); + expect(rectangle1.isEmpty()).toBe(false); + expect(rectangle2.isEmpty()).toBe(true); + }); + + it("isEmpty test case2", () => + { + const rectangle1 = new Rectangle(10, 10, 0, 20); + expect(rectangle1.isEmpty()).toBe(true); + }); + + it("isEmpty test case3", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 0); + expect(rectangle1.isEmpty()).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleIsEmptyService.ts b/packages/geom/src/Rectangle/service/RectangleIsEmptyService.ts new file mode 100644 index 00000000..15828774 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleIsEmptyService.ts @@ -0,0 +1,15 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 指定のRectangleが空かどうかを返す + * Returns whether the specified Rectangle is empty + * + * @param {Rectangle} rectangle + * @return {boolean} + * @method + * @public + */ +export const execute = (rectangle: Rectangle): boolean => +{ + return rectangle.width <= 0 || rectangle.height <= 0; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleOffsetPointService.test.ts b/packages/geom/src/Rectangle/service/RectangleOffsetPointService.test.ts new file mode 100644 index 00000000..60257896 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleOffsetPointService.test.ts @@ -0,0 +1,26 @@ +import { Point } from "../../Point"; +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js offsetPoint test", () => +{ + it("offsetPoint test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(-55, -55, 0, 0); + + rectangle1.offsetPoint(new Point(5, 8)); + rectangle2.offsetPoint(new Point(60, 30)); + + expect(rectangle1.x).toBe(15); + expect(rectangle1.y).toBe(18); + expect(rectangle1.width).toBe(20); + expect(rectangle1.height).toBe(20); + + expect(rectangle2.x).toBe(5); + expect(rectangle2.y).toBe(-25); + expect(rectangle2.width).toBe(0); + expect(rectangle2.height).toBe(0); + }); + +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleOffsetPointService.ts b/packages/geom/src/Rectangle/service/RectangleOffsetPointService.ts new file mode 100644 index 00000000..8f9199d8 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleOffsetPointService.ts @@ -0,0 +1,18 @@ +import type { Point } from "../../Point"; +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 矩形を指定された量だけオフセットする。 + * Offset the Rectangle by the given amount. + * + * @param {Rectangle} rectangle + * @param {Point} point + * @return {void} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, point: Point): void => +{ + rectangle.x += point.x; + rectangle.y += point.y; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleOffsetService.test.ts b/packages/geom/src/Rectangle/service/RectangleOffsetService.test.ts new file mode 100644 index 00000000..b95a398c --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleOffsetService.test.ts @@ -0,0 +1,24 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js offset test", () => +{ + it("offset test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(-55, -55, 0, 0); + + rectangle1.offset(5, 8); + rectangle2.offset(60, 30); + + expect(rectangle1.x).toBe(15); + expect(rectangle1.y).toBe(18); + expect(rectangle1.width).toBe(20); + expect(rectangle1.height).toBe(20); + + expect(rectangle2.x).toBe(5); + expect(rectangle2.y).toBe(-25); + expect(rectangle2.width).toBe(0); + expect(rectangle2.height).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleOffsetService.ts b/packages/geom/src/Rectangle/service/RectangleOffsetService.ts new file mode 100644 index 00000000..3f888823 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleOffsetService.ts @@ -0,0 +1,18 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 矩形を指定された量だけオフセットする。 + * Offset the Rectangle by the given amount. + * + * @param {Rectangle} rectangle + * @param {number} dx + * @param {number} dy + * @return {void} + * @method + * @public + */ +export const execute = (rectangle: Rectangle, dx: number, dy: number): void => +{ + rectangle.x += dx; + rectangle.y += dy; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleSetEmptyService.test.ts b/packages/geom/src/Rectangle/service/RectangleSetEmptyService.test.ts new file mode 100644 index 00000000..b9962270 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleSetEmptyService.test.ts @@ -0,0 +1,24 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js setEmpty test", () => +{ + it("setEmpty test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(-55, -55, 0, 0); + + rectangle1.setEmpty(); + rectangle2.setEmpty(); + + expect(rectangle1.x).toBe(0); + expect(rectangle1.y).toBe(0); + expect(rectangle1.width).toBe(0); + expect(rectangle1.height).toBe(0); + + expect(rectangle2.x).toBe(0); + expect(rectangle2.y).toBe(0); + expect(rectangle2.width).toBe(0); + expect(rectangle2.height).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleSetEmptyService.ts b/packages/geom/src/Rectangle/service/RectangleSetEmptyService.ts new file mode 100644 index 00000000..089f92ad --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleSetEmptyService.ts @@ -0,0 +1,15 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 矩形を空にする + * Make the rectangle empty + * + * @param {Rectangle} rectangle + * @return {void} + * @method + * @public + */ +export const execute = (rectangle: Rectangle): void => +{ + rectangle.x = rectangle.y = rectangle.width = rectangle.height = 0; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleSetToService.test.ts b/packages/geom/src/Rectangle/service/RectangleSetToService.test.ts new file mode 100644 index 00000000..090f354e --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleSetToService.test.ts @@ -0,0 +1,24 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js setTo test", () => +{ + it("setTo test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 20, 20); + const rectangle2 = new Rectangle(-55, -55, 0, 0); + + rectangle1.setTo(5, 5, 5, 5); + rectangle2.setTo(10, 10, 10, 10); + + expect(rectangle1.x).toBe(5); + expect(rectangle1.y).toBe(5); + expect(rectangle1.width).toBe(5); + expect(rectangle1.height).toBe(5); + + expect(rectangle2.x).toBe(10); + expect(rectangle2.y).toBe(10); + expect(rectangle2.width).toBe(10); + expect(rectangle2.height).toBe(10); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleSetToService.ts b/packages/geom/src/Rectangle/service/RectangleSetToService.ts new file mode 100644 index 00000000..1c1ff7f3 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleSetToService.ts @@ -0,0 +1,27 @@ +import type { Rectangle } from "../../Rectangle"; + +/** + * @description 矩形を指定された値に設定する + * Set the rectangle to the specified value + * + * @param {Rectangle} rectangle + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @public + */ +export const execute = ( + rectangle: Rectangle, + x: number, + y: number, + width: number, + height: number +): void => { + rectangle.x = x; + rectangle.y = y; + rectangle.width = width; + rectangle.height = height; +}; \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleUnionService.test.ts b/packages/geom/src/Rectangle/service/RectangleUnionService.test.ts new file mode 100644 index 00000000..c0123822 --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleUnionService.test.ts @@ -0,0 +1,121 @@ +import { Rectangle } from "../../Rectangle"; +import { describe, expect, it } from "vitest"; + +describe("Rectangle.js union test", () => +{ + it("union test case1", () => + { + const rectangle1 = new Rectangle(10, 10, 0, 10); + const rectangle2 = new Rectangle(-55, -25, 0, 20); + const rectangle3 = rectangle1.union(rectangle2); + + expect(rectangle3.x).toBe(-55); + expect(rectangle3.y).toBe(-25); + expect(rectangle3.width).toBe(0); + expect(rectangle3.height).toBe(20); + + const rectangle4 = new Rectangle(10, 10, 10, 10); + const rectangle5 = new Rectangle(-55, -25, 0, 20); + const rectangle6 = rectangle4.union(rectangle5); + expect(rectangle6.x).toBe(10); + expect(rectangle6.y).toBe(10); + expect(rectangle6.width).toBe(10); + expect(rectangle6.height).toBe(10); + + const rectangle7 = new Rectangle(10, 10, 10, 10); + const rectangle8 = new Rectangle(-55, -25, 20, 20); + const rectangle9 = rectangle7.union(rectangle8); + expect(rectangle9.x).toBe(-55); + expect(rectangle9.y).toBe(-25); + expect(rectangle9.width).toBe(75); + expect(rectangle9.height).toBe(45); + }); + + it("union test case2", () => + { + const rectangle1 = new Rectangle(10, 10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle1.union(rectangle2); + expect(rectangle3.x).toBe(10); + expect(rectangle3.y).toBe(10); + expect(rectangle3.width).toBe(20); + expect(rectangle3.height).toBe(20); + }); + + it("union test case3", () => + { + const rectangle1 = new Rectangle(-10, 10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle1.union(rectangle2); + expect(rectangle3.x).toBe(-10); + expect(rectangle3.y).toBe(10); + expect(rectangle3.width).toBe(40); + expect(rectangle3.height).toBe(20); + }); + + it("union test case4", () => + { + const rectangle1 = new Rectangle(10, -10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle1.union(rectangle2); + expect(rectangle3.x).toBe(10); + expect(rectangle3.y).toBe(-10); + expect(rectangle3.width).toBe(20); + expect(rectangle3.height).toBe(40); + }); + + it("union test cacse5", () => + { + const rectangle1 = new Rectangle(-10, -10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle1.union(rectangle2); + expect(rectangle3.x).toBe(-10); + expect(rectangle3.y).toBe(-10); + expect(rectangle3.width).toBe(40); + expect(rectangle3.height).toBe(40); + }); + + it("union test case6", () => + { + const rectangle1 = new Rectangle(10, 10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle2.union(rectangle1); + expect(rectangle3.x).toBe(10); + expect(rectangle3.y).toBe(10); + expect(rectangle3.width).toBe(20); + expect(rectangle3.height).toBe(20); + }); + + it("union test case7", () => + { + const rectangle1 = new Rectangle(-10, 10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle2.union(rectangle1); + expect(rectangle3.x).toBe(-10); + expect(rectangle3.y).toBe(10); + expect(rectangle3.width).toBe(40); + expect(rectangle3.height).toBe(20); + }); + + it("union test case8", () => + { + const rectangle1 = new Rectangle(10, -10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle2.union(rectangle1); + expect(rectangle3.x).toBe(10); + expect(rectangle3.y).toBe(-10); + expect(rectangle3.width).toBe(20); + expect(rectangle3.height).toBe(40); + }); + + it("union test case9", () => + { + const rectangle1 = new Rectangle(-10, -10, 10, 10); + const rectangle2 = new Rectangle(20, 20, 10, 10); + const rectangle3 = rectangle2.union(rectangle1); + expect(rectangle3.x).toBe(-10); + expect(rectangle3.y).toBe(-10); + expect(rectangle3.width).toBe(40); + expect(rectangle3.height).toBe(40); + }); +}); \ No newline at end of file diff --git a/packages/geom/src/Rectangle/service/RectangleUnionService.ts b/packages/geom/src/Rectangle/service/RectangleUnionService.ts new file mode 100644 index 00000000..bc336c0e --- /dev/null +++ b/packages/geom/src/Rectangle/service/RectangleUnionService.ts @@ -0,0 +1,29 @@ +import { Rectangle } from "../../Rectangle"; + +/** + * @description 2つの矩形を結合した矩形を返す + * Returns a rectangle that is the union of two rectangles + * + * @param {Rectangle} src + * @param {Rectangle} dst + * @return {void} + * @method + * @public + */ +export const execute = (rectangle1: Rectangle, rectangle2: Rectangle): Rectangle => +{ + if (rectangle1.isEmpty()) { + return rectangle2.clone(); + } + + if (rectangle2.isEmpty()) { + return rectangle1.clone(); + } + + return new Rectangle( + Math.min(rectangle1.x, rectangle2.x), + Math.min(rectangle1.y, rectangle2.y), + Math.max(rectangle1.right - rectangle2.left, rectangle2.right - rectangle1.left), + Math.max(rectangle1.bottom - rectangle2.top, rectangle2.bottom - rectangle1.top) + ); +}; \ No newline at end of file diff --git a/packages/geom/src/Transform.ts b/packages/geom/src/Transform.ts deleted file mode 100644 index 212a3888..00000000 --- a/packages/geom/src/Transform.ts +++ /dev/null @@ -1,702 +0,0 @@ -import { Rectangle } from "./Rectangle"; -import type { ColorTransform } from "./ColorTransform"; -import type { Matrix } from "./Matrix"; -import { - BevelFilter, - BlurFilter, - ColorMatrixFilter, - ConvolutionFilter, - DisplacementMapFilter, - DropShadowFilter, - GlowFilter, - GradientBevelFilter, - GradientGlowFilter -} from "@next2d/filters"; -import type { - PlaceObjectImpl, - DisplayObjectImpl, - ParentImpl, - BoundsImpl, - FilterArrayImpl, - BlendModeImpl, - SurfaceFilterImpl -} from "@next2d/interface"; -import { - $getColorTransform, - $getMatrix -} from "@next2d/util"; -import { - $Array, - $Math, - $MATRIX_ARRAY_IDENTITY, - $COLOR_ARRAY_IDENTITY, - $doUpdated, - $getFloat32Array6, - $getArray, - $poolArray, - $multiplicationColor, - $multiplicationMatrix, - $poolBoundsObject, - $getFloat32Array8 -} from "@next2d/share"; - -/** - * Transform クラスは、表示オブジェクトに適用されるカラー調整プロパティと 2 次元の変換オブジェクトへのアクセスを提供します。 - * 変換時に、表示オブジェクトのカラーまたは方向と位置が、現在の値または座標から新しい値または座標に調整(オフセット)されます。 - * Transform クラスは、表示オブジェクトおよびすべての親オブジェクトに適用されるカラー変換と 2 次元マトリックス変換に関するデータも収集します。 - * concatenatedColorTransform プロパティと concatenatedMatrix プロパティを使用して、これらの結合された変換にアクセスできます。 - * カラー変換を適用するには、ColorTransform オブジェクトを作成し、オブジェクトのメソッドとプロパティを使用してカラー調整を設定した後、 - * colorTransformation プロパティ(表示オブジェクトの transform プロパティの)を新しい ColorTransformation オブジェクトに割り当てます。 - * 2 次元変換を適用するには、Matrix オブジェクトを作成し、マトリックスの 2 次元変換を設定した後、表示オブジェクトの transform.matrix プロパティを新しい Matrix オブジェクトに割り当てます。 - * - * The Transform class provides access to color adjustment properties and two--dimensional transformation objects that can be applied to a display object. - * During the transformation, the color or the orientation and position of a display object is adjusted (offset) from the current values or coordinates to new values or coordinates. - * The Transform class also collects data about color and two-dimensional matrix transformations that are applied to a display object and all of its parent objects. - * You can access these combined transformations through the concatenatedColorTransform and concatenatedMatrix properties. - * To apply color transformations: create a ColorTransform object, - * set the color adjustments using the object's methods and properties, - * and then assign the colorTransformation property of the transform property of the display object to the new ColorTransformation object. - * To apply two-dimensional transformations: create a Matrix object, - * set the matrix's two-dimensional transformation, - * and then assign the transform.matrix property of the display object to the new Matrix object. - * - * @example Example usage of Transform. - * // new Transform - * const {Transform} = next2d.geom; - * const transform = new Transform(displayObject); - * - * @class - * @memberOf next2d.geom - */ -export class Transform -{ - private readonly _$displayObject: DisplayObjectImpl; - public _$matrix: Matrix|null; - public _$colorTransform: ColorTransform|null; - public _$blendMode: BlendModeImpl|null; - public _$filters: FilterArrayImpl|null; - - /** - * @param {DisplayObject} src - * - * @constructor - * @public - */ - constructor (src: DisplayObjectImpl) - { - /** - * @type {DisplayObject} - * @private - */ - this._$displayObject = src; - - /** - * @type {Matrix} - * @default null - * @private - */ - this._$matrix = null; - - /** - * @type {ColorTransform} - * @default null - * @private - */ - this._$colorTransform = null; - - /** - * @type {string} - * @default null - * @private - */ - this._$blendMode = null; - - /** - * @type {array} - * @default null - * @private - */ - this._$filters = null; - } - - /** - * 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Transform] - * @method - * @static - */ - static toString (): string - { - return "[class Transform]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @member {string} - * @default next2d.geom.Transform - * @const - * @static - */ - static get namespace (): string - { - return "next2d.geom.Transform"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @method - * @public - */ - toString (): string - { - return "[object Transform]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @member {string} - * @default next2d.geom.Transform - * @const - * @public - */ - get namespace (): string - { - return "next2d.geom.Transform"; - } - - /** - * @description 表示オブジェクトのカラーを全体的に調整する値を格納している - * ColorTransform オブジェクトです。 - * A ColorTransform object containing values that universally adjust - * the colors in the display object. - * - * @member {ColorTransform} - * @public - */ - get colorTransform (): ColorTransform - { - if (this._$colorTransform) { - return this._$colorTransform._$clone(); - } - - const displayObject = this._$displayObject; - const placeObject: PlaceObjectImpl | null = displayObject._$placeObject || displayObject._$getPlaceObject(); - - if (placeObject && placeObject.colorTransform) { - const buffer: number[] | Float32Array = placeObject.colorTransform; - return $getColorTransform( - buffer[0], buffer[1], buffer[2], buffer[3], - buffer[4], buffer[5], buffer[6], buffer[7] - ); - } - - this._$transform(); - if (!this._$colorTransform) { - this._$colorTransform = $getColorTransform(); - } - - return this._$colorTransform._$clone(); - } - set colorTransform (color_transform: ColorTransform) - { - this._$transform(null, color_transform._$colorTransform); - } - - /** - * @description この表示オブジェクトおよびルートレベルまでのすべての親オブジェクトに適用される、 - * 結合されたカラー変換を表す ColorTransform オブジェクトです。 - * A ColorTransform object representing - * the combined color transformations applied to the display object - * and all of its parent objects, back to the root level. - * - * @member {ColorTransform} - * @readonly - * @public - */ - get concatenatedColorTransform (): ColorTransform - { - let colorTransform: Float32Array = this._$rawColorTransform(); - - let parent: ParentImpl | null = this._$displayObject._$parent; - while (parent) { - - colorTransform = $multiplicationColor( - parent._$transform._$rawColorTransform(), - colorTransform - ); - - parent = parent._$parent; - } - - return $getColorTransform( - colorTransform[0], colorTransform[1], - colorTransform[2], colorTransform[3], - colorTransform[4], colorTransform[5], - colorTransform[6], colorTransform[7] - ); - } - - /** - * @description 表示オブジェクトの拡大 / 縮小、回転、および移動を変更する値を格納している - * Matrix オブジェクトです。 - * A Matrix object containing values that alter the scaling, - * rotation, and translation of the display object. - * - * @member {Matrix} - * @public - */ - get matrix (): Matrix - { - if (this._$matrix) { - return this._$matrix._$clone(); - } - - const displayObject = this._$displayObject; - const placeObject: PlaceObjectImpl | null = displayObject._$placeObject || displayObject._$getPlaceObject(); - - if (placeObject && placeObject.matrix) { - const buffer: number[] | Float32Array = placeObject.matrix; - return $getMatrix( - buffer[0], buffer[1], buffer[2], - buffer[3], buffer[4], buffer[5] - ); - } - - this._$transform(); - if (!this._$matrix) { - this._$matrix = $getMatrix(); - } - - return this._$matrix._$clone(); - } - set matrix (matrix: Matrix) - { - this._$transform(matrix._$matrix, null); - } - - /** - * @description この表示オブジェクトおよびルートレベルまでのそのすべての親オブジェクトの結合された - * 変換マトリックスを表す Matrix オブジェクトです。 - * A Matrix object representing the combined transformation matrixes - * of the display object and all of its parent objects, back to the root level. - * - * @member {Matrix} - * @readonly - * @method - * @public - */ - get concatenatedMatrix (): Matrix - { - let matrix: Float32Array = this._$rawMatrix(); - - let parent: ParentImpl | null = this._$displayObject._$parent; - while (parent) { - - matrix = $multiplicationMatrix( - parent._$transform._$rawMatrix(), - matrix - ); - - parent = parent._$parent; - } - - return $getMatrix( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } - - /** - * @description ステージ上の表示オブジェクトの境界を示す矩形を定義する Transform オブジェクトです。 - * A Transform object that defines the bounding rectangle of - * the display object on the stage. - * - * @member {Transform} - * @readonly - * @method - * @public - */ - pixelBounds (): Rectangle - { - if (!this._$displayObject) { - return new Rectangle(0, 0, 0, 0); - } - - const bounds: BoundsImpl = this - ._$displayObject - ._$getBounds(null); - - const rectangle: Rectangle = new Rectangle( - bounds.xMin, - bounds.yMin, - +$Math.abs(bounds.xMax - bounds.xMin), - +$Math.abs(bounds.yMax - bounds.yMin) - ); - - $poolBoundsObject(bounds); - - return rectangle; - } - - /** - * matrix プロパティから取得される Matrix の Matrix._$matrix と同じ値を返しますが、matrix プロパティと異なり Matrix を複製しません。 - * 返される値は一時的に使用することのみできます。返される値の要素を直接更新してはいけません。返される値をプール(Util.$poolFloat32Array)してはいけません。 - * - * @return {Float32Array} - * @method - * @private - */ - _$rawMatrix (): Float32Array - { - if (this._$matrix !== null) { - return this._$matrix._$matrix; - } - - const displayObject = this._$displayObject; - const placeObject: PlaceObjectImpl | null = displayObject._$placeObject || displayObject._$getPlaceObject(); - - if (placeObject && placeObject.matrix) { - if ($Array.isArray(placeObject.matrix)) { - const matrix: number[] = placeObject.matrix; - placeObject.matrix = $getFloat32Array6( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - $poolArray(matrix); - } - return placeObject.matrix; - } - - return $MATRIX_ARRAY_IDENTITY; - } - - /** - * colorTransform プロパティから取得される ColorTransform の colorTransform._$colorTransform と同じ値を返しますが、colorTransform プロパティと異なり ColorTransform を複製しません。 - * 返される値は一時的に使用することのみできます。返される値の要素を直接更新してはいけません。返される値をプール(Util.$poolFloat32Array)してはいけません。 - * - * @return {Float32Array} - * @method - * @private - */ - _$rawColorTransform (): Float32Array - { - if (this._$colorTransform !== null) { - return this._$colorTransform._$colorTransform; - } - - const displayObject = this._$displayObject; - const placeObject: PlaceObjectImpl | null = displayObject._$placeObject || displayObject._$getPlaceObject(); - - if (placeObject && placeObject.colorTransform) { - if ($Array.isArray(placeObject.colorTransform)) { - const colorTransform: number[] = placeObject.colorTransform; - placeObject.colorTransform = $getFloat32Array8( - colorTransform[0], colorTransform[1], - colorTransform[2], colorTransform[3], - colorTransform[4], colorTransform[5], - colorTransform[6], colorTransform[7] - ); - $poolArray(colorTransform); - } - return placeObject.colorTransform; - } - - return $COLOR_ARRAY_IDENTITY; - } - - /** - * @param {Float32Array} [matrix=null] - * @param {Float32Array} [color_transform=null] - * @param {array} [filters=null] - * @param {string} [blend_mode=""] - * @return {void} - * @method - * @private - */ - _$transform ( - matrix: Float32Array | null = null, - color_transform: Float32Array | null = null, - filters: FilterArrayImpl | null = null, - blend_mode: BlendModeImpl | "" = "" - ): void { - - const displayObject = this._$displayObject; - const placeObject: PlaceObjectImpl | null = displayObject._$placeObject || displayObject._$getPlaceObject(); - - // Matrix - this._$setMatrix(matrix, placeObject); - - // ColorTransform - this._$setColorTransform(color_transform, placeObject); - - // Filter - this._$setFilters(filters, placeObject); - - // BlendMode - this._$setBlendMode(blend_mode, placeObject); - - } - - /** - * @param {Float32Array} [matrix=null] - * @param {object} [place_object=null] - * @return {void} - * @method - * @private - */ - _$setMatrix ( - matrix: Float32Array | number[] | null = null, - place_object: PlaceObjectImpl | null = null - ): void { - - if (matrix || place_object) { - this._$displayObject._$doChanged(); - $doUpdated(); - } - - // Matrix - if (!this._$matrix) { - this._$matrix = $getMatrix(1, 0, 0, 1, 0, 0); - if (!matrix - && place_object - && place_object.matrix - ) { - matrix = place_object.matrix; - } - } - - // update - if (matrix) { - const currentMatrix = this._$matrix._$matrix; - currentMatrix[0] = matrix[0]; - currentMatrix[1] = matrix[1]; - currentMatrix[2] = matrix[2]; - currentMatrix[3] = matrix[3]; - currentMatrix[4] = matrix[4]; - currentMatrix[5] = matrix[5]; - } - } - - /** - * @param {Float32Array} [color_transform=null] - * @param {object} [place_object=null] - * @return {void} - * @method - * @private - */ - _$setColorTransform ( - color_transform: Float32Array | number[] | null = null, - place_object: PlaceObjectImpl | null = null - ): void { - - if (color_transform || place_object) { - this._$displayObject._$doChanged(); - $doUpdated(); - } - - if (!this._$colorTransform) { - this._$colorTransform = $getColorTransform(1, 1, 1, 1, 0, 0, 0, 0); - if (!color_transform - && place_object - && place_object.colorTransform - ) { - color_transform = place_object.colorTransform; - } - } - - if (color_transform) { - const colorTransform = this._$colorTransform._$colorTransform; - colorTransform[0] = color_transform[0]; - colorTransform[1] = color_transform[1]; - colorTransform[2] = color_transform[2]; - colorTransform[3] = color_transform[3]; - colorTransform[4] = color_transform[4]; - colorTransform[5] = color_transform[5]; - colorTransform[6] = color_transform[6]; - colorTransform[7] = color_transform[7]; - } - } - - /** - * @param {array} [filters=null] - * @param {object} [place_object=null] - * @return {void} - * @method - * @private - */ - _$setFilters ( - filters: FilterArrayImpl | null = null, - place_object: PlaceObjectImpl | null = null - ): void { - - if ($Array.isArray(filters)) { - - if (this._$filters) { - $poolArray(this._$filters); - } - - this._$filters = filters.slice(0); - - this._$displayObject._$doChanged(); - $doUpdated(); - - return ; - } - - if (this._$filters) { - return ; - } - - if (!place_object) { - this._$filters = $getArray(); - return ; - } - - if (place_object.filters) { - this._$filters = place_object.filters.slice(0); - for (let idx: number = 0; idx < this._$filters.length; ++idx) { - this._$filters[idx] = this._$filters[idx].clone(); - } - return ; - } - - if (place_object.surfaceFilterList) { - - // build origin - place_object.filters = this._$buildFilter( - place_object.surfaceFilterList - ); - - // use clone - this._$filters = place_object.filters.slice(0); - for (let idx: number = 0; idx < this._$filters.length; ++idx) { - this._$filters[idx] = this._$filters[idx].clone(); - } - } - - } - - /** - * @param {array} surface_filter_list - * @return {array} - * @method - * @public - */ - _$buildFilter (surface_filter_list: SurfaceFilterImpl[]): FilterArrayImpl - { - const filters: FilterArrayImpl = $getArray(); - - const length: number = surface_filter_list.length; - for (let idx: number = 0; idx < length; ++idx) { - - const filter = surface_filter_list[idx]; - if (filter.params[0] === null) { - filter.params.shift(); - } - - switch (filter.class) { - - case "BevelFilter": - filters.push( - new BevelFilter(...filter.params) - ); - break; - - case "BlurFilter": - filters.push( - new BlurFilter(...filter.params) - ); - break; - - case "ColorMatrixFilter": - filters.push( - new ColorMatrixFilter(...filter.params) - ); - break; - - case "ConvolutionFilter": - filters.push( - new ConvolutionFilter(...filter.params) - ); - break; - - case "DisplacementMapFilter": - filters.push( - new DisplacementMapFilter(...filter.params) - ); - break; - - case "DropShadowFilter": - filters.push( - new DropShadowFilter(...filter.params) - ); - break; - - case "GlowFilter": - filters.push( - new GlowFilter(...filter.params) - ); - break; - - case "GradientBevelFilter": - filters.push( - new GradientBevelFilter(...filter.params) - ); - break; - - case "GradientGlowFilter": - filters.push( - new GradientGlowFilter(...filter.params) - ); - break; - - } - } - - return filters; - } - - /** - * @param {string} [blend_mode=""] - * @param {object} [place_object=null] - * @return {void} - * @method - * @private - */ - _$setBlendMode ( - blend_mode: BlendModeImpl | "" = "", - place_object: PlaceObjectImpl | null = null - ): void { - - if (blend_mode) { - - this._$blendMode = blend_mode; - - this._$displayObject._$doChanged(); - $doUpdated(); - - return ; - } - - if (this._$blendMode) { - return ; - } - - if (place_object && place_object.blendMode) { - this._$blendMode = place_object.blendMode; - return ; - } - - this._$blendMode = "normal"; - } -} diff --git a/packages/geom/src/index.ts b/packages/geom/src/index.ts index 14b63989..e81a8c32 100644 --- a/packages/geom/src/index.ts +++ b/packages/geom/src/index.ts @@ -1,5 +1,4 @@ export * from "./ColorTransform"; export * from "./Matrix"; export * from "./Point"; -export * from "./Rectangle"; -export * from "./Transform"; \ No newline at end of file +export * from "./Rectangle"; \ No newline at end of file diff --git a/packages/interface/README.md b/packages/interface/README.md deleted file mode 100644 index e33aa673..00000000 --- a/packages/interface/README.md +++ /dev/null @@ -1,11 +0,0 @@ -@next2d/interface -============= - -## Installation - -``` -npm install @next2d/interface -``` - -## License -This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/interface/package.json b/packages/interface/package.json deleted file mode 100644 index 209d12ef..00000000 --- a/packages/interface/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@next2d/interface", - "version": "*", - "description": "Next2D Interface Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", - "license": "MIT", - "homepage": "https://next2d.app", - "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - } - }, - "keywords": [ - "Next2D", - "Next2D Interface" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/display": "file:../display", - "@next2d/net": "file:../net", - "@next2d/geom": "file:../geom" - } -} diff --git a/packages/interface/src/AjaxEventImpl.ts b/packages/interface/src/AjaxEventImpl.ts deleted file mode 100644 index 6cd6ce28..00000000 --- a/packages/interface/src/AjaxEventImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface AjaxEventImpl { - loadstart?: Function; - progress?: Function; - loadend?: Function; -} \ No newline at end of file diff --git a/packages/interface/src/AjaxOptionImpl.ts b/packages/interface/src/AjaxOptionImpl.ts deleted file mode 100644 index 66db8ca7..00000000 --- a/packages/interface/src/AjaxOptionImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { URLRequestMethodImpl } from "./URLRequestMethodImpl"; -import { URLLoaderDataFormatImpl } from "./URLLoaderDataFormatImpl"; -import { AjaxEventImpl } from "./AjaxEventImpl"; -import { URLRequestHeader } from "@next2d/net"; - -export interface AjaxOptionImpl { - url: string; - format: URLLoaderDataFormatImpl; - method: URLRequestMethodImpl; - withCredentials: boolean; - headers: URLRequestHeader[]; - data?: any; - event?: AjaxEventImpl; -} \ No newline at end of file diff --git a/packages/interface/src/AttachmentImpl.ts b/packages/interface/src/AttachmentImpl.ts deleted file mode 100644 index 34e91f64..00000000 --- a/packages/interface/src/AttachmentImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AttachmentImpl { - width: number; - height: number; - color: WebGLTexture | WebGLRenderbuffer | null; - texture: WebGLTexture | null; - msaa: boolean; - stencil: WebGLRenderbuffer | null; - mask: boolean; - clipLevel: number; - isActive: boolean; -} \ No newline at end of file diff --git a/packages/interface/src/BitmapDrawObjectImpl.ts b/packages/interface/src/BitmapDrawObjectImpl.ts deleted file mode 100644 index 3a57dd2a..00000000 --- a/packages/interface/src/BitmapDrawObjectImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DisplayObjectImpl } from "./DisplayObjectImpl"; - -export interface BitmapDrawObjectImpl { - source: DisplayObjectImpl; - context: CanvasRenderingContext2D; - callback: Function | null; -} diff --git a/packages/interface/src/BlendModeImpl.ts b/packages/interface/src/BlendModeImpl.ts deleted file mode 100644 index 6ed290eb..00000000 --- a/packages/interface/src/BlendModeImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type BlendModeImpl = "copy" - | "add" - | "alpha" - | "darken" - | "difference" - | "erase" - | "hardlight" - | "invert" - | "layer" - | "lighten" - | "multiply" - | "normal" - | "overlay" - | "screen" - | "subtract"; \ No newline at end of file diff --git a/packages/interface/src/BoundsImpl.ts b/packages/interface/src/BoundsImpl.ts deleted file mode 100644 index 7af56a28..00000000 --- a/packages/interface/src/BoundsImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BoundsImpl { - xMin: number; - xMax: number; - yMin: number; - yMax: number; -} \ No newline at end of file diff --git a/packages/interface/src/CachePositionImpl.ts b/packages/interface/src/CachePositionImpl.ts deleted file mode 100644 index 98f72340..00000000 --- a/packages/interface/src/CachePositionImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CachePositionImpl { - index: number; - x: number; - y: number; - w: number; - h: number; - filterState?: boolean; - matrix?: string; - offsetX?: number; - offsetY?: number; -} \ No newline at end of file diff --git a/packages/interface/src/CapsStyleImpl.ts b/packages/interface/src/CapsStyleImpl.ts deleted file mode 100644 index db351e90..00000000 --- a/packages/interface/src/CapsStyleImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type CapsStyleImpl = "none" | "round" | "square"; \ No newline at end of file diff --git a/packages/interface/src/Character.ts b/packages/interface/src/Character.ts deleted file mode 100644 index 7b37721a..00000000 --- a/packages/interface/src/Character.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { CharacterImpl } from "./CharacterImpl"; - -export type Character = T; \ No newline at end of file diff --git a/packages/interface/src/CharacterImpl.ts b/packages/interface/src/CharacterImpl.ts deleted file mode 100644 index a4a2ac84..00000000 --- a/packages/interface/src/CharacterImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export interface CharacterImpl {} \ No newline at end of file diff --git a/packages/interface/src/ClipObjectImpl.ts b/packages/interface/src/ClipObjectImpl.ts deleted file mode 100644 index 412babf7..00000000 --- a/packages/interface/src/ClipObjectImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ClipObjectImpl { - vertexArrayObject: WebGLVertexArrayObject; - matrixA: number; - matrixB: number; - matrixC: number; - matrixD: number; - matrixE: number; - matrixF: number; - matrixG: number; - matrixH: number; - matrixI: number; - viewportWidth: number; - viewportHeight: number; -} \ No newline at end of file diff --git a/packages/interface/src/ColorStopImpl.ts b/packages/interface/src/ColorStopImpl.ts deleted file mode 100644 index 65b98fb4..00000000 --- a/packages/interface/src/ColorStopImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ColorStopImpl { - ratio: number; - R: number; - G: number; - B: number; - A: number; -} \ No newline at end of file diff --git a/packages/interface/src/DictionaryTagImpl.ts b/packages/interface/src/DictionaryTagImpl.ts deleted file mode 100644 index a441e1e6..00000000 --- a/packages/interface/src/DictionaryTagImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface DictionaryTagImpl { - characterId: number; - name: string; - startFrame: number; - endFrame: number; - clipDepth: number; -} \ No newline at end of file diff --git a/packages/interface/src/DisplayImpl.ts b/packages/interface/src/DisplayImpl.ts deleted file mode 100644 index a2ce17eb..00000000 --- a/packages/interface/src/DisplayImpl.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - DisplayObject, - InteractiveObject, - DisplayObjectContainer, - BitmapData, - BlendMode, - FrameLabel, - Graphics, - Loader, - LoaderInfo, - MovieClip, - Shape, - Sprite, - Stage, - TextField -} from "@next2d/display"; -export interface DisplayImpl { - DisplayObject: typeof DisplayObject; - InteractiveObject: typeof InteractiveObject; - DisplayObjectContainer: typeof DisplayObjectContainer; - BitmapData: typeof BitmapData; - BlendMode: typeof BlendMode; - FrameLabel: typeof FrameLabel; - Graphics: typeof Graphics; - Loader: typeof Loader; - LoaderInfo: typeof LoaderInfo; - MovieClip: typeof MovieClip; - Shape: typeof Shape; - Sprite: typeof Sprite; - Stage: typeof Stage; - TextField: typeof TextField; -} diff --git a/packages/interface/src/DisplayObjectImpl.ts b/packages/interface/src/DisplayObjectImpl.ts deleted file mode 100644 index cbdfdd27..00000000 --- a/packages/interface/src/DisplayObjectImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DisplayObject } from "@next2d/display"; - -export type DisplayObjectImpl = T; \ No newline at end of file diff --git a/packages/interface/src/DragRulesImpl.ts b/packages/interface/src/DragRulesImpl.ts deleted file mode 100644 index 2f876f7b..00000000 --- a/packages/interface/src/DragRulesImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { PointImpl } from "./PointImpl"; -import { Rectangle } from "@next2d/geom"; - -export interface DragRulesImpl { - lock: boolean; - position: PointImpl; - bounds: Rectangle | null -} \ No newline at end of file diff --git a/packages/interface/src/DropTargetImpl.ts b/packages/interface/src/DropTargetImpl.ts deleted file mode 100644 index 3811a544..00000000 --- a/packages/interface/src/DropTargetImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DisplayObject } from "@next2d/display"; - -export type DropTargetImpl = T; \ No newline at end of file diff --git a/packages/interface/src/EventDispatcherImpl.ts b/packages/interface/src/EventDispatcherImpl.ts deleted file mode 100644 index cfb3100d..00000000 --- a/packages/interface/src/EventDispatcherImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { EventDispatcher } from "@next2d/events"; - -export type EventDispatcherImpl = T; diff --git a/packages/interface/src/EventListenerImpl.ts b/packages/interface/src/EventListenerImpl.ts deleted file mode 100644 index 2e1c9cfe..00000000 --- a/packages/interface/src/EventListenerImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface EventListenerImpl { - listener: Function; - priority: number; - useCapture: boolean; - target: any; -} \ No newline at end of file diff --git a/packages/interface/src/EventsImpl.ts b/packages/interface/src/EventsImpl.ts deleted file mode 100644 index 600de0da..00000000 --- a/packages/interface/src/EventsImpl.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - Event, - EventDispatcher, - EventPhase, - FocusEvent, - HTTPStatusEvent, - IOErrorEvent, - MouseEvent, - ProgressEvent, - VideoEvent -} from "@next2d/events"; - -export interface EventsImpl { - Event: typeof Event; - EventDispatcher: typeof EventDispatcher; - EventPhase: typeof EventPhase; - FocusEvent: typeof FocusEvent; - HTTPStatusEvent: typeof HTTPStatusEvent; - IOErrorEvent: typeof IOErrorEvent; - MouseEvent: typeof MouseEvent; - ProgressEvent: typeof ProgressEvent; - VideoEvent: typeof VideoEvent; -} \ No newline at end of file diff --git a/packages/interface/src/FillMeshImpl.ts b/packages/interface/src/FillMeshImpl.ts deleted file mode 100644 index 1f100adb..00000000 --- a/packages/interface/src/FillMeshImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IndexRangeImpl } from "./IndexRangeImpl"; - -export interface FillMeshImpl { - vertexBufferData: Float32Array; - indexRanges: IndexRangeImpl[]; -} \ No newline at end of file diff --git a/packages/interface/src/FilterArrayImpl.ts b/packages/interface/src/FilterArrayImpl.ts deleted file mode 100644 index ea57db72..00000000 --- a/packages/interface/src/FilterArrayImpl.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - BlurFilter, - BevelFilter, - ColorMatrixFilter, - ConvolutionFilter, - DisplacementMapFilter, - DropShadowFilter, - GlowFilter, - GradientBevelFilter, - GradientGlowFilter -} from "@next2d/filters"; - -export type FilterArrayImpl = Array< - BlurFilter - | BevelFilter - | ColorMatrixFilter - | ConvolutionFilter - | DisplacementMapFilter - | DropShadowFilter - | GlowFilter - | GradientBevelFilter - | GradientGlowFilter ->; \ No newline at end of file diff --git a/packages/interface/src/FiltersImpl.ts b/packages/interface/src/FiltersImpl.ts deleted file mode 100644 index 0a01f425..00000000 --- a/packages/interface/src/FiltersImpl.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - BevelFilter, - BlurFilter, - ColorMatrixFilter, - ConvolutionFilter, - DisplacementMapFilter, - DropShadowFilter, - GlowFilter, - GradientBevelFilter, - GradientGlowFilter -} from "@next2d/filters"; - -export interface FiltersImpl { - BevelFilter: typeof BevelFilter; - BlurFilter: typeof BlurFilter; - ColorMatrixFilter: typeof ColorMatrixFilter; - ConvolutionFilter: typeof ConvolutionFilter; - DisplacementMapFilter: typeof DisplacementMapFilter; - DropShadowFilter: typeof DropShadowFilter; - GlowFilter: typeof GlowFilter; - GradientBevelFilter: typeof GradientBevelFilter; - GradientGlowFilter: typeof GradientGlowFilter; -} \ No newline at end of file diff --git a/packages/interface/src/GeomImpl.ts b/packages/interface/src/GeomImpl.ts deleted file mode 100644 index a8448bbb..00000000 --- a/packages/interface/src/GeomImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { - ColorTransform, - Matrix, - Point, - Rectangle, - Transform -} from "@next2d/geom"; - -export interface GeomImpl { - ColorTransform: typeof ColorTransform; - Matrix: typeof Matrix; - Point: typeof Point; - Rectangle: typeof Rectangle; - Transform: typeof Transform; -} \ No newline at end of file diff --git a/packages/interface/src/GradientTypeImpl.ts b/packages/interface/src/GradientTypeImpl.ts deleted file mode 100644 index 37f6dabc..00000000 --- a/packages/interface/src/GradientTypeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type GradientTypeImpl = "linear" | "radial"; \ No newline at end of file diff --git a/packages/interface/src/GraphicsParentImpl.ts b/packages/interface/src/GraphicsParentImpl.ts deleted file mode 100644 index 4255150e..00000000 --- a/packages/interface/src/GraphicsParentImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DisplayObject } from "@next2d/display"; - -export type GraphicsParentImpl = T; \ No newline at end of file diff --git a/packages/interface/src/GridImpl.ts b/packages/interface/src/GridImpl.ts deleted file mode 100644 index eecb9aba..00000000 --- a/packages/interface/src/GridImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GridImpl { - x: number; - y: number; - w: number; - h: number; -} \ No newline at end of file diff --git a/packages/interface/src/ImageTypeImpl.ts b/packages/interface/src/ImageTypeImpl.ts deleted file mode 100644 index ce832765..00000000 --- a/packages/interface/src/ImageTypeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type ImageTypeImpl = "jpeg" | "gif" | "png" | "bmp"; \ No newline at end of file diff --git a/packages/interface/src/IndexRangeImpl.ts b/packages/interface/src/IndexRangeImpl.ts deleted file mode 100644 index ac74cc1b..00000000 --- a/packages/interface/src/IndexRangeImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IndexRangeImpl { - first: number; - count: number; -} \ No newline at end of file diff --git a/packages/interface/src/InterpolationMethodImpl.ts b/packages/interface/src/InterpolationMethodImpl.ts deleted file mode 100644 index a623a088..00000000 --- a/packages/interface/src/InterpolationMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type InterpolationMethodImpl = "rgb" | "linearRGB"; \ No newline at end of file diff --git a/packages/interface/src/JointStyleImpl.ts b/packages/interface/src/JointStyleImpl.ts deleted file mode 100644 index d9b443a2..00000000 --- a/packages/interface/src/JointStyleImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type JointStyleImpl = "bevel" | "miter" | "round"; \ No newline at end of file diff --git a/packages/interface/src/LoaderInfoDataImpl.ts b/packages/interface/src/LoaderInfoDataImpl.ts deleted file mode 100644 index c8af0813..00000000 --- a/packages/interface/src/LoaderInfoDataImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { StageDataImpl } from "./StageDataImpl"; -import { Character } from "./Character"; - -export interface LoaderInfoDataImpl { - stage: StageDataImpl; - characters: Character[]; - symbols: Map; -} \ No newline at end of file diff --git a/packages/interface/src/LoopConfigImpl.ts b/packages/interface/src/LoopConfigImpl.ts deleted file mode 100644 index fa4bfe9b..00000000 --- a/packages/interface/src/LoopConfigImpl.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { LoopTypeImpl } from "./LoopTypeImpl"; - -export interface LoopConfigImpl { - type: LoopTypeImpl; - frame: number; - start: number; - end: number; - tweenFrame?: number; -} \ No newline at end of file diff --git a/packages/interface/src/LoopTypeImpl.ts b/packages/interface/src/LoopTypeImpl.ts deleted file mode 100644 index 75998297..00000000 --- a/packages/interface/src/LoopTypeImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type LoopTypeImpl = 0 // REPEAT - | 1 // NO_REPEAT - | 2 // FIXED - | 3 // NO_REPEAT_REVERSAL - | 4 ;// REPEAT_REVERSAL \ No newline at end of file diff --git a/packages/interface/src/MediaImpl.ts b/packages/interface/src/MediaImpl.ts deleted file mode 100644 index 4c4d880a..00000000 --- a/packages/interface/src/MediaImpl.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { - Sound, - SoundMixer, - SoundTransform, - Video -} from "@next2d/media"; - -export interface MediaImpl { - Sound: typeof Sound; - SoundMixer: typeof SoundMixer; - SoundTransform: typeof SoundTransform; - Video: typeof Video; -} \ No newline at end of file diff --git a/packages/interface/src/MovieClipActionObjectImpl.ts b/packages/interface/src/MovieClipActionObjectImpl.ts deleted file mode 100644 index 023a8655..00000000 --- a/packages/interface/src/MovieClipActionObjectImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface MovieClipActionObjectImpl { - frame: number; - action: string; - script?: Function; -} \ No newline at end of file diff --git a/packages/interface/src/MovieClipCharacterImpl.ts b/packages/interface/src/MovieClipCharacterImpl.ts deleted file mode 100644 index 431eada1..00000000 --- a/packages/interface/src/MovieClipCharacterImpl.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CharacterImpl } from "./CharacterImpl"; -import { MovieClipSoundObjectImpl } from "./MovieClipSoundObjectImpl"; -import { MovieClipActionObjectImpl } from "./MovieClipActionObjectImpl"; -import { MovieClipLabelObjectImpl } from "./MovieClipLabelObjectImpl"; -import { PlaceObjectImpl } from "./PlaceObjectImpl"; -import { DictionaryTagImpl } from "./DictionaryTagImpl"; - -export interface MovieClipCharacterImpl extends CharacterImpl { - extends: string; - totalFrame: number; - controller: Array>; - dictionary: DictionaryTagImpl[]; - placeMap: Array>; - placeObjects: PlaceObjectImpl[]; - labels?: MovieClipLabelObjectImpl[]; - actions?: MovieClipActionObjectImpl[]; - symbol?: string; - sounds?: MovieClipSoundObjectImpl[]; -} \ No newline at end of file diff --git a/packages/interface/src/MovieClipLabelObjectImpl.ts b/packages/interface/src/MovieClipLabelObjectImpl.ts deleted file mode 100644 index 85b40e77..00000000 --- a/packages/interface/src/MovieClipLabelObjectImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface MovieClipLabelObjectImpl { - name: string; - frame: number; -} \ No newline at end of file diff --git a/packages/interface/src/MovieClipSoundObjectImpl.ts b/packages/interface/src/MovieClipSoundObjectImpl.ts deleted file mode 100644 index 5eaf9644..00000000 --- a/packages/interface/src/MovieClipSoundObjectImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SoundTagImpl } from "./SoundTagImpl"; - -export interface MovieClipSoundObjectImpl { - frame: number; - sound: SoundTagImpl[]; -} \ No newline at end of file diff --git a/packages/interface/src/NetImpl.ts b/packages/interface/src/NetImpl.ts deleted file mode 100644 index 5a65a341..00000000 --- a/packages/interface/src/NetImpl.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { - URLRequest, - URLRequestHeader -} from "@next2d/net"; - -export interface NetImpl { - URLRequest: typeof URLRequest; - URLRequestHeader: typeof URLRequestHeader; -} \ No newline at end of file diff --git a/packages/interface/src/NoCodeDataImpl.ts b/packages/interface/src/NoCodeDataImpl.ts deleted file mode 100644 index 2cf30a8b..00000000 --- a/packages/interface/src/NoCodeDataImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { StageDataImpl } from "./StageDataImpl"; - -export interface NoCodeDataImpl { - type: "json"; - stage: StageDataImpl; - characters: any[]; - symbols: any[]; -} \ No newline at end of file diff --git a/packages/interface/src/NoCodeDataZlibImpl.ts b/packages/interface/src/NoCodeDataZlibImpl.ts deleted file mode 100644 index d7b36d56..00000000 --- a/packages/interface/src/NoCodeDataZlibImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface NoCodeDataZlibImpl { - type: "zlib"; - buffer: number[]; -} \ No newline at end of file diff --git a/packages/interface/src/ParentImpl.ts b/packages/interface/src/ParentImpl.ts deleted file mode 100644 index 2567c608..00000000 --- a/packages/interface/src/ParentImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DisplayObjectContainer } from "@next2d/display"; - -export type ParentImpl = T; \ No newline at end of file diff --git a/packages/interface/src/PlaceObjectImpl.ts b/packages/interface/src/PlaceObjectImpl.ts deleted file mode 100644 index 686402a5..00000000 --- a/packages/interface/src/PlaceObjectImpl.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { LoopConfigImpl } from "./LoopConfigImpl"; -import { SurfaceFilterImpl } from "./SurfaceFilterImpl"; -import { FilterArrayImpl } from "./FilterArrayImpl"; -import { BlendModeImpl } from "./BlendModeImpl"; - -export interface PlaceObjectImpl { - matrix?: number[] | Float32Array; - colorTransform?: number[] | Float32Array; - blendMode?: BlendModeImpl; - surfaceFilterList?: SurfaceFilterImpl[]; - filters?: FilterArrayImpl; - loop?: LoopConfigImpl; -} \ No newline at end of file diff --git a/packages/interface/src/PlayerHitObjectImpl.ts b/packages/interface/src/PlayerHitObjectImpl.ts deleted file mode 100644 index b7fcd0aa..00000000 --- a/packages/interface/src/PlayerHitObjectImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { DisplayObjectImpl } from "./DisplayObjectImpl"; - -export interface PlayerHitObjectImpl { - x: number; - y: number; - pointer: string; - hit: DisplayObjectImpl|null; -} \ No newline at end of file diff --git a/packages/interface/src/PlayerModeImpl.ts b/packages/interface/src/PlayerModeImpl.ts deleted file mode 100644 index a72252f5..00000000 --- a/packages/interface/src/PlayerModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type PlayerModeImpl = "loader" | "create"; \ No newline at end of file diff --git a/packages/interface/src/PlayerOptionsImpl.ts b/packages/interface/src/PlayerOptionsImpl.ts deleted file mode 100644 index 2f1d9ef9..00000000 --- a/packages/interface/src/PlayerOptionsImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface PlayerOptionsImpl { - width?: number; - height?: number; - tagId?: string; - base?: string; - bgColor?: string; - fullScreen?: boolean; -} \ No newline at end of file diff --git a/packages/interface/src/PointImpl.ts b/packages/interface/src/PointImpl.ts deleted file mode 100644 index b912bda3..00000000 --- a/packages/interface/src/PointImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface PointImpl { - x: number; - y: number; -} \ No newline at end of file diff --git a/packages/interface/src/PreObjectImpl.ts b/packages/interface/src/PreObjectImpl.ts deleted file mode 100644 index f3118066..00000000 --- a/packages/interface/src/PreObjectImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BlendModeImpl } from "./BlendModeImpl"; - -export interface PreObjectImpl -{ - isLayer: boolean; - isUpdated: boolean | null; - canApply: boolean | null; - matrix: Float32Array | null; - color: Float32Array | null; - blendMode: BlendModeImpl; - filters: any[] | null; - sw: number; - sh: number; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyBitmapDataMessageImpl.ts b/packages/interface/src/PropertyBitmapDataMessageImpl.ts deleted file mode 100644 index e46a8ba0..00000000 --- a/packages/interface/src/PropertyBitmapDataMessageImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface PropertyBitmapDataMessageImpl { - command: string; - sourceId: number; - width: number; - height: number; - matrix?: Float32Array; - colorTransform?: Float32Array; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyContainerMessageImpl.ts b/packages/interface/src/PropertyContainerMessageImpl.ts deleted file mode 100644 index 66a81c8c..00000000 --- a/packages/interface/src/PropertyContainerMessageImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyContainerMessageImpl extends PropertyMessageImpl { - maxAlpha?: number; - canDraw?: boolean; - recodes?: Float32Array; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyMessageImpl.ts b/packages/interface/src/PropertyMessageImpl.ts deleted file mode 100644 index f7d9611e..00000000 --- a/packages/interface/src/PropertyMessageImpl.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BlendModeImpl } from "./BlendModeImpl"; -import { GridImpl } from "./GridImpl"; - -export interface PropertyMessageImpl { - command: string; - buffer: Float32Array; - instanceId?: number; - parentId?: number; - visible?: boolean; - isMask?: boolean; - clipDepth?: number; - depth?: number; - maskId?: number; - loaderInfoId?: number; - characterId?: number; - maskMatrix?: Float32Array; - xMin?: number; - yMin?: number; - xMax?: number; - yMax?: number; - a?: number; - b?: number; - c?: number; - d?: number; - tx?: number; - ty?: number; - f0?: number; - f1?: number; - f2?: number; - f3?: number; - f4?: number; - f5?: number; - f6?: number; - f7?: number; - filters?: any[]; - blendMode?: BlendModeImpl; - matrixBase?: Float32Array; - grid?: GridImpl; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyMessageMapImpl.ts b/packages/interface/src/PropertyMessageMapImpl.ts deleted file mode 100644 index 7cff7045..00000000 --- a/packages/interface/src/PropertyMessageMapImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export type PropertyMessageMapImpl = T; \ No newline at end of file diff --git a/packages/interface/src/PropertyShapeMessageImpl.ts b/packages/interface/src/PropertyShapeMessageImpl.ts deleted file mode 100644 index 8a14a2e8..00000000 --- a/packages/interface/src/PropertyShapeMessageImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyShapeMessageImpl extends PropertyMessageImpl { - maxAlpha?: number; - canDraw?: boolean; - recodes?: Float32Array; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyTextMessageImpl.ts b/packages/interface/src/PropertyTextMessageImpl.ts deleted file mode 100644 index 27b6acd3..00000000 --- a/packages/interface/src/PropertyTextMessageImpl.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; -import { TextDataImpl } from "./TextDataImpl"; -import { TextFieldAutoSizeImpl } from "./TextFieldAutoSizeImpl"; - -export interface PropertyTextMessageImpl extends PropertyMessageImpl { - textData?: TextDataImpl[]; - limitWidth: number; - limitHeight: number; - textHeight: number; - autoSize: TextFieldAutoSizeImpl; - wordWrap: boolean; - border: boolean; - borderColor?: number; - background: boolean; - backgroundColor?: number; - thickness: number; - thicknessColor?: number; -} \ No newline at end of file diff --git a/packages/interface/src/PropertyVideoMessageImpl.ts b/packages/interface/src/PropertyVideoMessageImpl.ts deleted file mode 100644 index f2089b28..00000000 --- a/packages/interface/src/PropertyVideoMessageImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyVideoMessageImpl extends PropertyMessageImpl { - smoothing?: boolean; - imageBitmap?: ImageBitmap; -} \ No newline at end of file diff --git a/packages/interface/src/RGBAImpl.ts b/packages/interface/src/RGBAImpl.ts deleted file mode 100644 index 240c24bc..00000000 --- a/packages/interface/src/RGBAImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface RGBAImpl { - A: number; - R: number; - G: number; - B: number; -} \ No newline at end of file diff --git a/packages/interface/src/ShapeCharacterImpl.ts b/packages/interface/src/ShapeCharacterImpl.ts deleted file mode 100644 index 942449d5..00000000 --- a/packages/interface/src/ShapeCharacterImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { GridImpl } from "./GridImpl"; -import { BoundsImpl } from "./BoundsImpl"; -import { CharacterImpl } from "./CharacterImpl"; - -export interface ShapeCharacterImpl extends CharacterImpl { - symbol: string; - extends: string; - bitmapId: number; - grid: GridImpl | null; - inBitmap: boolean; - bounds: BoundsImpl; - recodes?: any[]; - buffer?: number[] | null; - _$buffer?: Uint8Array; -} \ No newline at end of file diff --git a/packages/interface/src/ShapeModeImpl.ts b/packages/interface/src/ShapeModeImpl.ts deleted file mode 100644 index 9c9dbe2d..00000000 --- a/packages/interface/src/ShapeModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type ShapeModeImpl = "shape" | "bitmap"; \ No newline at end of file diff --git a/packages/interface/src/SoundCharacterImpl.ts b/packages/interface/src/SoundCharacterImpl.ts deleted file mode 100644 index fd915d6e..00000000 --- a/packages/interface/src/SoundCharacterImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { CharacterImpl } from "./CharacterImpl"; - -export interface SoundCharacterImpl extends CharacterImpl { - buffer: number[] | null; - audioBuffer: AudioBuffer | null; -} \ No newline at end of file diff --git a/packages/interface/src/SoundTagImpl.ts b/packages/interface/src/SoundTagImpl.ts deleted file mode 100644 index 1f87f809..00000000 --- a/packages/interface/src/SoundTagImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface SoundTagImpl { - characterId: number; - volume: number; - autoPlay: boolean; - loopCount: number; -} \ No newline at end of file diff --git a/packages/interface/src/SpreadMethodImpl.ts b/packages/interface/src/SpreadMethodImpl.ts deleted file mode 100644 index b04e5e3f..00000000 --- a/packages/interface/src/SpreadMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type SpreadMethodImpl = "pad" | "reflect" | "repeat"; \ No newline at end of file diff --git a/packages/interface/src/SpriteImpl.ts b/packages/interface/src/SpriteImpl.ts deleted file mode 100644 index eb648682..00000000 --- a/packages/interface/src/SpriteImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Sprite } from "@next2d/display"; - -export type SpriteImpl = T; \ No newline at end of file diff --git a/packages/interface/src/StageDataImpl.ts b/packages/interface/src/StageDataImpl.ts deleted file mode 100644 index 1eab12a2..00000000 --- a/packages/interface/src/StageDataImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface StageDataImpl { - width: number; - height: number; - fps: number - bgColor: string; -} \ No newline at end of file diff --git a/packages/interface/src/StageQualityImpl.ts b/packages/interface/src/StageQualityImpl.ts deleted file mode 100644 index ad80be15..00000000 --- a/packages/interface/src/StageQualityImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type StageQualityImpl = "high" | "low" | "medium"; \ No newline at end of file diff --git a/packages/interface/src/StrokeMethImpl.ts b/packages/interface/src/StrokeMethImpl.ts deleted file mode 100644 index fd4c58a3..00000000 --- a/packages/interface/src/StrokeMethImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface StrokeMethImpl { - vertexBufferData: Float32Array; - indexBufferData: Int16Array; -} \ No newline at end of file diff --git a/packages/interface/src/SurfaceFilterImpl.ts b/packages/interface/src/SurfaceFilterImpl.ts deleted file mode 100644 index 3bddb677..00000000 --- a/packages/interface/src/SurfaceFilterImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -type ClassName = "BevelFilter" - | "BlurFilter" - | "ColorMatrixFilter" - | "ConvolutionFilter" - | "DisplacementMapFilter" - | "DropShadowFilter" - | "GlowFilter" - | "GradientBevelFilter" - | "GradientGlowFilter"; - -export interface SurfaceFilterImpl { - class: ClassName; - params: any[]; -} \ No newline at end of file diff --git a/packages/interface/src/TextBreakObjectImpl.ts b/packages/interface/src/TextBreakObjectImpl.ts deleted file mode 100644 index dc47bdf3..00000000 --- a/packages/interface/src/TextBreakObjectImpl.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { TextObjectImpl } from "./TextObjectImpl"; -import { TextObjectModeImpl } from "./TextObjectModeImpl"; -import { TextFormat } from "@next2d/text"; - -export interface TextBreakObjectImpl extends TextObjectImpl { - mode: TextObjectModeImpl; - x: number; - yIndex: number; - textFormat: TextFormat; -} \ No newline at end of file diff --git a/packages/interface/src/TextCharacterImpl.ts b/packages/interface/src/TextCharacterImpl.ts deleted file mode 100644 index 1853007e..00000000 --- a/packages/interface/src/TextCharacterImpl.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CharacterImpl } from "./CharacterImpl"; -import { TextFormatAlignImpl } from "./TextFormatAlignImpl"; -import { TextFieldTypeImpl } from "./TextFieldTypeImpl"; -import { BoundsImpl } from "./BoundsImpl"; - -export interface TextCharacterImpl extends CharacterImpl { - symbol: string; - extends: string; - text: string; - font: string; - size: number; - align: TextFormatAlignImpl; - color: number; - leading: number; - letterSpacing: number; - leftMargin: number; - rightMargin: number; - fontType: number; - inputType: TextFieldTypeImpl; - multiline: boolean; - wordWrap: boolean; - border: boolean; - scroll: boolean; - thickness: number; - thicknessColor: number; - originBounds: BoundsImpl; - autoSize: number; -} \ No newline at end of file diff --git a/packages/interface/src/TextDataImpl.ts b/packages/interface/src/TextDataImpl.ts deleted file mode 100644 index cb165d41..00000000 --- a/packages/interface/src/TextDataImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { TextObjectImpl } from "./TextObjectImpl"; - -export type TextDataImpl = T; \ No newline at end of file diff --git a/packages/interface/src/TextFieldAutoSizeImpl.ts b/packages/interface/src/TextFieldAutoSizeImpl.ts deleted file mode 100644 index 470b9cae..00000000 --- a/packages/interface/src/TextFieldAutoSizeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFieldAutoSizeImpl = "center" | "left" | "none" | "right"; \ No newline at end of file diff --git a/packages/interface/src/TextFieldTypeImpl.ts b/packages/interface/src/TextFieldTypeImpl.ts deleted file mode 100644 index cfe1c329..00000000 --- a/packages/interface/src/TextFieldTypeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFieldTypeImpl = "input" | "static"; \ No newline at end of file diff --git a/packages/interface/src/TextFormatAlignImpl.ts b/packages/interface/src/TextFormatAlignImpl.ts deleted file mode 100644 index 07bad1e3..00000000 --- a/packages/interface/src/TextFormatAlignImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFormatAlignImpl = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/interface/src/TextFormatImpl.ts b/packages/interface/src/TextFormatImpl.ts deleted file mode 100644 index 8f7fbb8c..00000000 --- a/packages/interface/src/TextFormatImpl.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TextFormatAlignImpl } from "./TextFormatAlignImpl"; - -export interface TextFormatImpl { - _$font: string; - _$size: number; - _$color: number; - _$bold: boolean; - _$italic: boolean; - _$underline: boolean; - _$align: TextFormatAlignImpl; - _$leftMargin: number; - _$rightMargin: number; - _$indent: number; - _$leading: number; - _$blockIndent: number; - _$letterSpacing: number; -} \ No newline at end of file diff --git a/packages/interface/src/TextFormatVerticalAlignImpl.ts b/packages/interface/src/TextFormatVerticalAlignImpl.ts deleted file mode 100644 index ef84ee58..00000000 --- a/packages/interface/src/TextFormatVerticalAlignImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFormatVerticalAlignImpl = "top" | "middle" | "bottom"; \ No newline at end of file diff --git a/packages/interface/src/TextImageObjectImpl.ts b/packages/interface/src/TextImageObjectImpl.ts deleted file mode 100644 index cc60b477..00000000 --- a/packages/interface/src/TextImageObjectImpl.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TextObjectImpl } from "./TextObjectImpl"; - -export interface TextImageObjectImpl extends TextObjectImpl { - image: HTMLImageElement; - src: string; - loaded: boolean; - y: number; - width: number; - height: number; - hspace: number; - vspace: number; -} \ No newline at end of file diff --git a/packages/interface/src/TextImpl.ts b/packages/interface/src/TextImpl.ts deleted file mode 100644 index 37f6f16b..00000000 --- a/packages/interface/src/TextImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TextFormat } from "@next2d/text"; - -export interface TextImpl { - TextFormat: typeof TextFormat; -} \ No newline at end of file diff --git a/packages/interface/src/TextObjectImpl.ts b/packages/interface/src/TextObjectImpl.ts deleted file mode 100644 index e9c41f3b..00000000 --- a/packages/interface/src/TextObjectImpl.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { TextFormat } from "@next2d/text"; -import { TextObjectModeImpl } from "./TextObjectModeImpl"; - -export interface TextObjectImpl { - mode: TextObjectModeImpl; - text: string; - textFormat: TextFormat; - x: number; - y: number; - w: number; - h: number; - line: number; -} \ No newline at end of file diff --git a/packages/interface/src/TextObjectModeImpl.ts b/packages/interface/src/TextObjectModeImpl.ts deleted file mode 100644 index 3f558522..00000000 --- a/packages/interface/src/TextObjectModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextObjectModeImpl = "break" | "wrap" | "image" | "text"; \ No newline at end of file diff --git a/packages/interface/src/TextStringObjectImpl.ts b/packages/interface/src/TextStringObjectImpl.ts deleted file mode 100644 index 98779f88..00000000 --- a/packages/interface/src/TextStringObjectImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TextObjectImpl } from "./TextObjectImpl"; - -export interface TextStringObjectImpl extends TextObjectImpl { - text: string; - width: number; - height: number; - yIndex: number; -} \ No newline at end of file diff --git a/packages/interface/src/UIImpl.ts b/packages/interface/src/UIImpl.ts deleted file mode 100644 index e459df28..00000000 --- a/packages/interface/src/UIImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { - Easing, - Job, - Tween -} from "@next2d/ui"; - -export interface UIImpl { - Easing: typeof Easing; - Job: typeof Job; - Tween: typeof Tween; -} \ No newline at end of file diff --git a/packages/interface/src/URLLoaderDataFormatImpl.ts b/packages/interface/src/URLLoaderDataFormatImpl.ts deleted file mode 100644 index af72978d..00000000 --- a/packages/interface/src/URLLoaderDataFormatImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type URLLoaderDataFormatImpl = "json" | "arraybuffer" | "text"; \ No newline at end of file diff --git a/packages/interface/src/URLRequestMethodImpl.ts b/packages/interface/src/URLRequestMethodImpl.ts deleted file mode 100644 index 866e2360..00000000 --- a/packages/interface/src/URLRequestMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type URLRequestMethodImpl = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT"; \ No newline at end of file diff --git a/packages/interface/src/UniformDataImpl.ts b/packages/interface/src/UniformDataImpl.ts deleted file mode 100644 index 1931fd77..00000000 --- a/packages/interface/src/UniformDataImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface UniformDataImpl { - method?: Function; - array? : Int32Array | Float32Array; - assign? : number; -} \ No newline at end of file diff --git a/packages/interface/src/VerticesImpl.ts b/packages/interface/src/VerticesImpl.ts deleted file mode 100644 index 454c4108..00000000 --- a/packages/interface/src/VerticesImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type VerticesImpl = Array>; \ No newline at end of file diff --git a/packages/interface/src/VideoCharacterImpl.ts b/packages/interface/src/VideoCharacterImpl.ts deleted file mode 100644 index 673afe0e..00000000 --- a/packages/interface/src/VideoCharacterImpl.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BoundsImpl } from "./BoundsImpl"; -import { CharacterImpl } from "./CharacterImpl"; - -export interface VideoCharacterImpl extends CharacterImpl { - symbol: string; - extends: string; - buffer: number[] | null; - _$buffer: Uint8Array; - bounds: BoundsImpl; - volume: number; - loop: boolean; - autoPlay: boolean; -} \ No newline at end of file diff --git a/packages/interface/src/index.ts b/packages/interface/src/index.ts deleted file mode 100644 index fa2d6efd..00000000 --- a/packages/interface/src/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -export * from "./AjaxEventImpl"; -export * from "./AjaxOptionImpl"; -export * from "./AttachmentImpl"; -export * from "./BitmapDrawObjectImpl"; -export * from "./BlendModeImpl"; -export * from "./BoundsImpl"; -export * from "./Character"; -export * from "./CharacterImpl"; -export * from "./ClipObjectImpl"; -export * from "./ColorStopImpl"; -export * from "./DictionaryTagImpl"; -export * from "./DisplayImpl"; -export * from "./DisplayObjectImpl"; -export * from "./DragRulesImpl"; -export * from "./DropTargetImpl"; -export * from "./EventDispatcherImpl"; -export * from "./EventListenerImpl"; -export * from "./EventsImpl"; -export * from "./FillMeshImpl"; -export * from "./FilterArrayImpl"; -export * from "./FiltersImpl"; -export * from "./GeomImpl"; -export * from "./GradientTypeImpl"; -export * from "./GraphicsParentImpl"; -export * from "./GridImpl"; -export * from "./ImageTypeImpl"; -export * from "./IndexRangeImpl"; -export * from "./LoaderInfoDataImpl"; -export * from "./LoopConfigImpl"; -export * from "./LoopTypeImpl"; -export * from "./MediaImpl"; -export * from "./MovieClipActionObjectImpl"; -export * from "./MovieClipCharacterImpl"; -export * from "./MovieClipLabelObjectImpl"; -export * from "./MovieClipSoundObjectImpl"; -export * from "./NetImpl"; -export * from "./NoCodeDataImpl"; -export * from "./NoCodeDataZlibImpl"; -export * from "./ParentImpl"; -export * from "./PlaceObjectImpl"; -export * from "./PlayerHitObjectImpl"; -export * from "./PlayerModeImpl"; -export * from "./PlayerOptionsImpl"; -export * from "./PointImpl"; -export * from "./PreObjectImpl"; -export * from "./PropertyBitmapDataMessageImpl"; -export * from "./PropertyContainerMessageImpl"; -export * from "./PropertyMessageImpl"; -export * from "./PropertyMessageMapImpl"; -export * from "./PropertyShapeMessageImpl"; -export * from "./PropertyTextMessageImpl"; -export * from "./PropertyVideoMessageImpl"; -export * from "./RGBAImpl"; -export * from "./ShapeCharacterImpl"; -export * from "./SoundCharacterImpl"; -export * from "./SoundTagImpl"; -export * from "./StageDataImpl"; -export * from "./StageQualityImpl"; -export * from "./StrokeMethImpl"; -export * from "./CapsStyleImpl"; -export * from "./JointStyleImpl"; -export * from "./SurfaceFilterImpl"; -export * from "./TextBreakObjectImpl"; -export * from "./TextCharacterImpl"; -export * from "./TextDataImpl"; -export * from "./TextFieldAutoSizeImpl"; -export * from "./TextFieldTypeImpl"; -export * from "./TextFormatAlignImpl"; -export * from "./TextFormatImpl"; -export * from "./TextFormatVerticalAlignImpl"; -export * from "./TextImpl"; -export * from "./TextObjectImpl"; -export * from "./UIImpl"; -export * from "./UniformDataImpl"; -export * from "./URLLoaderDataFormatImpl"; -export * from "./URLRequestMethodImpl"; -export * from "./VerticesImpl"; -export * from "./VideoCharacterImpl"; -export * from "./SpreadMethodImpl"; -export * from "./InterpolationMethodImpl"; -export * from "./SpriteImpl"; -export * from "./CachePositionImpl"; -export * from "./ShapeModeImpl"; \ No newline at end of file diff --git a/packages/media/README.md b/packages/media/README.md index 11a622fb..14d387bf 100644 --- a/packages/media/README.md +++ b/packages/media/README.md @@ -1,10 +1,10 @@ -@next2d/media +@next2d/renderer ============= ## Installation ``` -npm install @next2d/media +npm install @next2d/renderer ``` ## License diff --git a/packages/media/package.json b/packages/media/package.json index d58fe311..cf25bd48 100644 --- a/packages/media/package.json +++ b/packages/media/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/media", "version": "*", - "description": "Next2D Media Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Media Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -35,11 +27,6 @@ "@next2d/events": "file:../events", "@next2d/net": "file:../net", "@next2d/display": "file:../display", - "@next2d/interface": "file:../interface", - "@next2d/core": "file:../core", - "@next2d/share": "file:../share", - "@next2d/util": "file:../util", - "@next2d/geom": "file:../geom", - "@next2d/webgl": "file:../webgl" + "@next2d/geom": "file:../geom" } } diff --git a/packages/media/src/MediaUtil.ts b/packages/media/src/MediaUtil.ts new file mode 100644 index 00000000..a3ca1be8 --- /dev/null +++ b/packages/media/src/MediaUtil.ts @@ -0,0 +1,254 @@ +import type { Sound } from "./Sound"; +import type { Video } from "./Video"; +import type { IAjaxOption } from "./interface/IAjaxOption"; + +/** + * @type {Video[]} + * @public + */ +const $mutedVideos: Video[] = []; + +/** + * @description ミュートになっているビデオを返却 + * Returns the muted video. + * + * @return {Video[]} + * @method + * @public + */ +export const $getMutedVideos = (): Video[] => +{ + return $mutedVideos; +}; + +/** + * @description ミュートになっているビデオを追加 + * Add a video that has been muted. + * + * @param {Video} video + * @return {void} + * @method + * @public + */ +export const $pushMutedVideos = (video: Video): void => +{ + $mutedVideos.push(video); +}; + +/** + * @type {AudioContext} + * @private + */ +let $audioContext: AudioContext | null = null; + +/** + * @description AudioContext が存在するか返却 + * Returns whether AudioContext exists. + * + * @return {boolean} + * @method + * @public + */ +export const $isAudioContext = (): boolean => +{ + return !!$audioContext; +}; + +/** + * @description AudioContext を起動 + * Start AudioContext. + * + * @method + * @private + */ +export const $bootAudioContext = (): void => +{ + if (!$audioContext) { + $audioContext = new AudioContext(); + } + $audioContext.resume(); +}; + +/** + * @description AudioContext を返却 + * Returns AudioContext. + * + * @return {AudioContext} + * @method + * @protected + */ +export const $getAudioContext = (): AudioContext => +{ + if (!$audioContext) { + $audioContext = new AudioContext(); + } + return $audioContext; +}; + +/** + * @param {object} option + * @return {void} + * @method + * @public + */ +export const $ajax = (option: IAjaxOption): void => +{ + // get or post + let postData: string | null = null; + switch (option.method.toUpperCase()) { + + case "GET": + if (option.data) { + const urls = option.url.split("?"); + + urls[1] = urls.length === 1 + ? option.data.toString() + : `${urls[1]}&${option.data.toString()}`; + + option.url = urls.join("?"); + } + break; + + case "PUT": + case "POST": + if (option.data) { + postData = option.data.toString(); + } + break; + + default: + break; + + } + + // start + const xmlHttpRequest = new XMLHttpRequest(); + + // init + xmlHttpRequest.open(option.method, option.url, true); + + // set mimeType + xmlHttpRequest.responseType = option.format; + + // use cookie + xmlHttpRequest.withCredentials = option.withCredentials; + + // add event + if (option.event) { + const keys: string[] = Object.keys(option.event); + for (let idx = 0; idx < keys.length; ++idx) { + + const name: string = keys[idx]; + + // @ts-ignore + xmlHttpRequest.addEventListener(name, option.event[name]); + } + } + + // set request header + for (let idx: number = 0; idx < option.headers.length; ++idx) { + const header = option.headers[idx]; + if (!header) { + continue; + } + xmlHttpRequest.setRequestHeader(header.name, header.value); + } + + xmlHttpRequest.send(postData); +}; + +/** + * @description 値が最小値と最大値の間に収まるように調整します。 + * Adjust the value so that it falls between the minimum and maximum values. + * + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @static + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +/** + * @type {number} + * @private + */ +let $volume: number = 1; + +/** + * @description 音量を返却 + * Returns the volume. + * + * @return {number} + * @method + * @public + */ +export const $getVolume = (): number => +{ + return $volume; +}; + +/** + * @description 音量を設定 + * Set the volume. + * + * @param {number} volume + * @method + * @public + */ +export const $setVolume = (volume: number): void => +{ + $volume = $clamp(volume, 0, 1, 1); +}; + +/** + * @type {Sound[]} + * @private + */ +const $playingSounds: Sound[] = []; + +/** + * @description 再生中のサウンドを返却 + * Returns the sound being played. + * + * @returns {Sound[]} + * @method + * @public + */ +export const $getPlayingSounds = (): Sound[] => +{ + return $playingSounds; +}; + +/** + * @type {Video[]} + * @private + */ +const $playingVideos: Video[] = []; + +/** + * @description 再生中のビデオを返却 + * Returns the video being played. + * + * @returns {Video[]} + * @method + * @public + */ +export const $getPlayingVideos = (): Video[] => +{ + return $playingVideos; +}; \ No newline at end of file diff --git a/packages/media/src/Sound.ts b/packages/media/src/Sound.ts index c388fa97..5f90886f 100644 --- a/packages/media/src/Sound.ts +++ b/packages/media/src/Sound.ts @@ -1,43 +1,22 @@ +import type { ISoundCharacter } from "./interface/ISoundCharacter"; +import type { URLRequest } from "@next2d/net"; import { SoundMixer } from "./SoundMixer"; -import { URLRequest } from "@next2d/net"; -import { Player } from "@next2d/core"; +import { execute as soundEndedEventService } from "./Sound/service/SoundEndedEventService"; +import { execute as soundLoadUseCase } from "./Sound/usecase/SoundLoadUseCase"; +import { execute as soundBuildFromCharacterUseCase } from "./Sound/usecase/SoundBuildFromCharacterUseCase"; +import { EventDispatcher } from "@next2d/events"; import { - EventDispatcher, - Event as Next2DEvent, - IOErrorEvent, - ProgressEvent as Next2DProgressEvent -} from "@next2d/events"; -import type { - DisplayObjectContainer, - LoaderInfo -} from "@next2d/display"; -import type { - SoundTagImpl, - SoundCharacterImpl, - Character -} from "@next2d/interface"; -import { - $Math, - $performance, - $requestAnimationFrame, $clamp, - $getArray -} from "@next2d/share"; -import { - $ajax, - $audioContext, - $audios, - $currentPlayer, - $decodeAudioData -} from "@next2d/util"; + $getAudioContext, + $getPlayingSounds +} from "./MediaUtil"; /** - * Sound クラスを使用すると、アプリケーション内のサウンドを処理することができます。 - * Sound クラスを使用すると、Sound オブジェクトの作成や、外部 MP3 ファイルのオブジェクトへのロードと再生ができます。 - * - * The Sound class lets you work with sound in an application. - * The Sound class lets you create a Sound object, - * load and play an external MP3 file into that object. + * @description Sound クラスを使用すると、アプリケーション内のサウンドを処理することができます。 + * Sound クラスを使用すると、Sound オブジェクトの作成や、外部 MP3 ファイルのオブジェクトへのロードと再生ができます。 + * The Sound class lets you work with sound in an application. + * The Sound class lets you create a Sound object, + * load and play an external MP3 file into that object. * * @class * @memberOf next2d.media @@ -45,220 +24,78 @@ import { */ export class Sound extends EventDispatcher { - public readonly _$sources: AudioBufferSourceNode[]; - private _$bytesLoaded: number; - private _$bytesTotal: number; - private _$volume: number; - private _$currentCount: number; - private _$src: string; - private _$loopCount: number; - private _$stopFlag: boolean; - public _$character: Character | null; - public _$audioBuffer: AudioBuffer | null; - public _$arrayBuffer: ArrayBuffer | null; - /** - * @constructor - * @public - */ - constructor () - { - - super(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesLoaded = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesTotal = 0; - - /** - * @type {AudioBuffer} - * @default null - * @private - */ - this._$arrayBuffer = null; - - /** - * @type {Uint8Array} - * @default null - * @private - */ - this._$audioBuffer = null; - - /** - * @type {object} - * @default null - * @private - */ - this._$character = null; - - /** - * @type {array} - * @private - */ - this._$sources = $getArray(); - - /** - * @type {number} - * @default 1 - * @private - */ - this._$volume = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$currentCount = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$loopCount = 0; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$stopFlag = true; - - /** - * @type {string} - * @default "" - * @private - */ - this._$src = ""; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Sound] - * @method - * @static + * @type {AudioBufferSourceNode} + * @default null + * @private */ - static toString (): string - { - return "[class Sound]"; - } + private _$source: AudioBufferSourceNode | null; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.media.Sound - * @const - * @static + * @type {GainNode} + * @default null + * @private */ - static get namespace (): string - { - return "next2d.media.Sound"; - } + private _$gainNode: GainNode | null; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object Sound] - * @method - * @public + * @type {boolean} + * @default true + * @private */ - toString (): string - { - return "[object Sound]"; - } + private _$stopFlag: boolean; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.media.Sound - * @const - * @public + * @type {number} + * @default 0 + * @private */ - get namespace (): string - { - return "next2d.media.Sound"; - } + private _$currentCount: number; /** - * @description 既にアプリケーションにロードされているデータのバイト数です。 - * The number of bytes of data that have been loaded into the application. - * - * @member {number} - * @default 0 - * @readonly - * @public + * @type {number} + * @default 1 + * @private */ - get bytesLoaded (): number - { - return this._$bytesLoaded; - } + private _$volume: number; /** - * @description アプリケーションにロードされるファイルの総バイト数。 - * The total size in bytes of the file being loaded into the application. + * @description AudioBuffer + * AudioBuffer * - * @member {number} - * @default 0 - * @readonly + * @type {AudioBuffer} + * @default null * @public */ - get bytesTotal (): number - { - return this._$bytesTotal; - } + public audioBuffer: AudioBuffer | null; /** * @description ループ回数の設定 * Loop count setting. * - * @member {number} + * @type {string} * @default 0 * @public */ - get loopCount (): number - { - return this._$loopCount; - } - set loopCount (loop_count: number) - { - this._$loopCount = loop_count; - } + public loopCount: number; /** - * @description 外部サウンドのURL - * URL for external sound. - * - * @member {string} - * @default "" + * @constructor * @public */ - get src (): string - { - return this._$src; - } - set src (url: string) + constructor () { - this.load(new URLRequest(url)); + super(); + + this.loopCount = 0; + this.audioBuffer = null; + + // private + this._$volume = 1; + this._$currentCount = 0; + this._$stopFlag = true; + this._$source = null; + this._$gainNode = null; } /** @@ -275,25 +112,29 @@ export class Sound extends EventDispatcher } set volume (volume: number) { - this._$volume = $Math.min( + this._$volume = $clamp(Math.min( SoundMixer.volume, - $clamp(volume, 0, 1, 1) - ); - - const length: number = this._$sources.length; - if (length && $audioContext) { - for (let idx: number = 0; idx < length; ++idx) { - - const source: AudioBufferSourceNode = this._$sources[idx]; + volume + ), 0, 1, 1); - if (source._$gainNode) { - source._$gainNode.gain.value = this._$volume; - source._$volume = this._$volume; - } - } + if (this._$gainNode) { + this._$gainNode.gain.value = this._$volume; } } + /** + * @description サウンドがループするかどうかを示します。 + * Indicates whether the sound loops. + * + * @member {boolean} + * @readonly + * @public + */ + get canLoop (): boolean + { + return !this._$stopFlag && this.loopCount >= this._$currentCount; + } + /** * @description Sound クラスを複製します。 * Duplicate the Sound class. @@ -304,17 +145,10 @@ export class Sound extends EventDispatcher */ clone (): Sound { - const sound = new Sound(); - sound.volume = this.volume; - - sound._$loopCount = this._$loopCount; - - if (this._$character) { - sound._$character = this._$character; - } else { - sound._$audioBuffer = this._$audioBuffer; - } - + const sound = new Sound(); + sound.volume = this._$volume; + sound.loopCount = this.loopCount; + sound.audioBuffer = this.audioBuffer; return sound; } @@ -322,132 +156,14 @@ export class Sound extends EventDispatcher * @description 指定した URL から外部 MP3 ファイルのロードを開始します。 * Initiates loading of an external MP3 file from the specified URL. * - * @param {URLRequest} request - * @return {void} + * @param {URLRequest} request + * @return {Promise} * @method * @public */ - load (request: URLRequest): void - { - this._$src = request.url; - - $ajax({ - "format": "arraybuffer", - "url": request.url, - "method": request.method, - "data": request.data, - "headers": request.headers, - "withCredentials": request.withCredentials, - "event": { - "loadstart": (event: ProgressEvent) => - { - this._$loadStart(event); - }, - "progress": (event: ProgressEvent) => - { - this._$progress(event); - }, - "loadend": (event: ProgressEvent) => - { - this._$loadEnd(event); - } - } - }); - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$loadStart (event: ProgressEvent): void - { - this._$bytesLoaded = event.loaded; - this._$bytesTotal = event.total; - - if (this.willTrigger(Next2DEvent.OPEN)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.OPEN)); - } - - if (this.willTrigger(Next2DProgressEvent.PROGRESS)) { - this.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, false, false, - event.loaded, event.total - )); - } - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$progress (event: ProgressEvent): void + async load (request: URLRequest): Promise { - this._$bytesLoaded = event.loaded; - this._$bytesTotal = event.total; - - if (this.willTrigger(Next2DProgressEvent.PROGRESS)) { - this.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, false, false, - event.loaded, event.total - )); - } - } - - /** - * @param {ProgressEvent} event - * @return {void} - * @method - * @private - */ - _$loadEnd (event: ProgressEvent): void - { - this._$bytesLoaded = event.loaded; - this._$bytesTotal = event.total; - - if (this.willTrigger(Next2DProgressEvent.PROGRESS)) { - this.dispatchEvent(new Next2DProgressEvent( - Next2DProgressEvent.PROGRESS, false, false, - event.loaded, event.total - )); - } - - const target: any = event.target; - if (!target) { - throw new Error("the Sound target is null."); - } - - if (199 < target.status && 400 > target.status) { - - this._$arrayBuffer = target.response; - - if ($audioContext) { - $decodeAudioData(this) - .then((sound) => - { - if (sound.hasEventListener(Next2DEvent.INIT) - || sound.hasEventListener(Next2DEvent.COMPLETE) - ) { - const player: Player = $currentPlayer(); - player._$loaders.push(sound); - } - }); - } else { - $audios.push(this); - } - - } else { - - if (this.willTrigger(IOErrorEvent.IO_ERROR)) { - this.dispatchEvent(new IOErrorEvent( - IOErrorEvent.IO_ERROR, false, false, target.statusText - )); - } - - } + await soundLoadUseCase(this, request); } /** @@ -461,37 +177,37 @@ export class Sound extends EventDispatcher */ play (start_time: number = 0): void { - const buffer: AudioBuffer | null = this._$character - ? this._$character.audioBuffer - : this._$audioBuffer; - - // execute - if (!$audioContext || !buffer) { - - const now: number = $performance.now(); - const wait = () => - { - const buffer = this._$character - ? this._$character.audioBuffer - : this._$audioBuffer; + // 再生中なら終了 + if (!this._$stopFlag) { + return ; + } - if (buffer !== null && $audioContext !== null) { - const offset = ($performance.now() - now) / 1000; - this._$createBufferSource(start_time, offset); - return ; - } + if (!this.audioBuffer) { + return ; + } - $requestAnimationFrame(wait); + // 初期化 + this.stop(); - }; + const audioContext = $getAudioContext(); + this._$gainNode = audioContext.createGain(); + this._$gainNode.connect(audioContext.destination); + this._$gainNode.gain.value = Math.min(SoundMixer.volume, this._$volume); - $requestAnimationFrame(wait); + this._$source = audioContext.createBufferSource(); + this._$source.addEventListener("ended", (): void => + { + soundEndedEventService(this); + }); - } else { + this._$source.buffer = this.audioBuffer; + this._$source.connect(this._$gainNode); + this._$source.start(start_time); - this._$createBufferSource(start_time); + this._$stopFlag = false; + this._$currentCount++; - } + $getPlayingSounds().push(this); } /** @@ -504,179 +220,42 @@ export class Sound extends EventDispatcher */ stop (): void { - this._$stopFlag = true; - const length: number = this._$sources.length; - if (length) { - - const player = $currentPlayer(); - if ($audioContext) { - - for (let idx: number = 0; idx < length; ++idx) { - - const source: AudioBufferSourceNode = this._$sources[idx]; - - if (source._$gainNode) { - source._$gainNode.gain.value = 0; - source._$gainNode.disconnect(); - source._$gainNode = null; - } - - source.onended = null; - source.disconnect(); - } - } - - player._$sources.splice( - player._$sources.indexOf(this), 1 - ); - - this._$currentCount = 0; - this._$sources.length = 0; + if (this._$stopFlag) { + return ; } - } - /** - * @param {object} tag - * @param {MovieClip} parent - * @return {void} - * @method - * @private - */ - _$build ( - tag: SoundTagImpl, - parent: DisplayObjectContainer - ) { + this._$stopFlag = true; + this._$currentCount = 0; - const loaderInfo: LoaderInfo | null = parent.loaderInfo; - if (!loaderInfo || !loaderInfo._$data) { - throw new Error("the loaderInfo or data is null."); + if (this._$source) { + this._$source.disconnect(); + this._$source = null; } - this._$character = loaderInfo - ._$data - .characters[tag.characterId]; - - if (!this._$character) { - throw new Error("character is null."); + if (this._$gainNode) { + this._$gainNode.gain.value = 0; + this._$gainNode.disconnect(); + this._$gainNode = null; } - // load AudioBuffer - if (!this._$character.audioBuffer) { - if ($audioContext) { - $decodeAudioData(this) - .then((sound) => - { - if (sound.hasEventListener(Next2DEvent.INIT) - || sound.hasEventListener(Next2DEvent.COMPLETE) - ) { - const player: Player = $currentPlayer(); - player._$loaders.push(sound); - } - }); - } else { - $audios.push(this); - } + const playingSounds = $getPlayingSounds(); + const index = playingSounds.indexOf(this); + if (index > -1) { + playingSounds.splice(index, 1); } - - this._$loopCount = tag.loopCount | 0; - this._$volume = $Math.min(SoundMixer.volume, tag.volume); } /** - * @param {number} [start_time=0] - * @param {number} [offset=0] - * @return {void} - * @method - * @private - */ - _$createBufferSource (start_time = 0, offset = 0) - { - if (!$audioContext) { - throw new Error("the Audio Context is null."); - } - - // setup - const source: AudioBufferSourceNode = $audioContext.createBufferSource(); - - source.onended = (event: Event): void => - { - return this._$endEventHandler(event); - }; - - // main - source.buffer = this._$character - ? this._$character.audioBuffer - : this._$audioBuffer; - - source._$gainNode = $audioContext.createGain(); - source._$gainNode.connect($audioContext.destination); - - const volume = $Math.min(SoundMixer.volume, this._$volume); - - source._$gainNode.gain.value = volume; - source._$volume = volume; - - source.connect(source._$gainNode); - source.start(start_time | 0, offset); - - const player = $currentPlayer(); - if (player._$sources.indexOf(this) === -1) { - player._$sources.push(this); - } - - this._$sources.push(source); - - this._$stopFlag = false; - } - - /** - * @param {Event} event - * @return {void} + * @description Character DataからSoundを作成 + * Create Sound from Character Data + * + * @param {Character} character + * @return {Promise} * @method - * @private + * @protected */ - _$endEventHandler (event: Event): void + async $build (character: ISoundCharacter): Promise { - const source: any = event.target; - - this._$sources.splice( - this._$sources.indexOf(source), 1 - ); - - if (!this._$stopFlag && this._$loopCount > this._$currentCount) { - - this._$createBufferSource(); - - this._$currentCount++; - - } else { - - this._$currentCount = 0; - - if ($audioContext) { - - if (source._$gainNode) { - source._$gainNode.gain.value = 0; - source._$gainNode.disconnect(); - source._$gainNode = null; - } - - // Firefoxにて、disconnectした時にonendedが呼び出されるのを回避 - source.onended = null; - source.disconnect(); - } - - if (!this._$sources.length) { - const player = $currentPlayer(); - player._$sources.splice( - player._$sources.indexOf(this), 1 - ); - } - - if (this.willTrigger(Next2DEvent.SOUND_COMPLETE)) { - this.dispatchEvent(new Next2DEvent(Next2DEvent.SOUND_COMPLETE)); - } - - } + await soundBuildFromCharacterUseCase(this, character); } -} +} \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundDecodeService.ts b/packages/media/src/Sound/service/SoundDecodeService.ts new file mode 100644 index 00000000..af730b4c --- /dev/null +++ b/packages/media/src/Sound/service/SoundDecodeService.ts @@ -0,0 +1,39 @@ +import { $getAudioContext } from "../../MediaUtil"; + +/** + * @description ArrayBufferをデコードしてAudioBufferを返却 + * Decode Uint8Array and return AudioBuffer. + * + * @param {ArrayBuffer} array_buffer + * @return {AudioBuffer | void} + * @method + * @public + */ +export const execute = async (array_buffer: ArrayBuffer): Promise => +{ + if (!array_buffer.byteLength) { + return ; + } + + try { + + return await $getAudioContext().decodeAudioData(array_buffer); + + } catch (_) { + + const buffer = new Uint8Array(array_buffer); + let idx = 0; + for ( ; idx > buffer.byteLength; ) { + + idx = buffer.indexOf(0xff, idx); + + if (idx === -1 || (buffer[idx + 1] & 0xe0) === 0xe0) { + break; + } + + ++idx; + } + + return await execute(buffer.subarray(idx).buffer); + } +}; \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundEndedEventService.test.ts b/packages/media/src/Sound/service/SoundEndedEventService.test.ts new file mode 100644 index 00000000..64e3cb54 --- /dev/null +++ b/packages/media/src/Sound/service/SoundEndedEventService.test.ts @@ -0,0 +1,48 @@ +import { Sound } from "../../Sound"; +import { Event } from "@next2d/events"; +import { execute } from "./SoundEndedEventService"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundEndedEventService.js test", () => +{ + it("execute test case1", () => + { + let state = ""; + const MockSound = vi.fn().mockImplementation(() => + { + return { + "canLoop": true, + "play": vi.fn(() => { state = "play" }), + "stop": vi.fn(), + "hasEventListener": vi.fn(), + "dispatchEvent": vi.fn() + } as unknown as Sound; + }); + + expect(state).toBe(""); + execute(new MockSound()); + expect(state).toBe("play"); + }); + + it("execute test case2", () => + { + let state = ""; + let type = ""; + const MockSound = vi.fn().mockImplementation(() => + { + return { + "canLoop": false, + "play": vi.fn(), + "stop": vi.fn(() => { state = "stop" }), + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn((event: Event) => { type = event.type }) + } as unknown as Sound; + }); + + expect(type).toBe(""); + expect(state).toBe(""); + execute(new MockSound()); + expect(state).toBe("stop"); + expect(type).toBe(Event.COMPLETE); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundEndedEventService.ts b/packages/media/src/Sound/service/SoundEndedEventService.ts new file mode 100644 index 00000000..3e3cfc67 --- /dev/null +++ b/packages/media/src/Sound/service/SoundEndedEventService.ts @@ -0,0 +1,27 @@ +import type { Sound } from "../../Sound"; +import { Event } from "@next2d/events"; + +/** + * @description サウンドデータの再生終了時のイベント実行関数 + * Event execution function when sound data playback ends + * + * @param {Sound} sound + * @return {void} + * @method + * @public + */ +export const execute = (sound: Sound): void => +{ + if (sound.canLoop) { + + sound.play(); + + } else { + + sound.stop(); + + if (sound.willTrigger(Event.COMPLETE)) { + sound.dispatchEvent(new Event(Event.COMPLETE)); + } + } +}; \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundLoadStartEventService.test.ts b/packages/media/src/Sound/service/SoundLoadStartEventService.test.ts new file mode 100644 index 00000000..61c661dc --- /dev/null +++ b/packages/media/src/Sound/service/SoundLoadStartEventService.test.ts @@ -0,0 +1,70 @@ +import { Sound } from "../../Sound"; +import { execute } from "./SoundLoadStartEventService"; +import { + Event, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundLoadStartEventService.js test", () => +{ + it("execute test case1", () => + { + const sound = new Sound(); + + let openState = ""; + sound.addEventListener(Event.OPEN, (event: Event): void => + { + openState = event.type; + }); + + expect(openState).toBe(""); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(sound, new MockEvent()); + + expect(openState).toBe(Event.OPEN); + }); + + it("execute test case2", () => + { + const sound = new Sound(); + + let openState = ""; + let loaded = 0; + let total = 0; + sound.addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(sound, new MockEvent()); + + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + expect(loaded).toBe(1); + expect(total).toBe(10); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundLoadStartEventService.ts b/packages/media/src/Sound/service/SoundLoadStartEventService.ts new file mode 100644 index 00000000..9f2ccaad --- /dev/null +++ b/packages/media/src/Sound/service/SoundLoadStartEventService.ts @@ -0,0 +1,29 @@ +import type { Sound } from "../../Sound"; +import { + Event, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; + +/** + * @description サウンドの読み込みが開始イベントの実行関数 + * Execution function of the sound loading start event + * + * @param {Sound} sound + * @param {ProgressEvent} event + * @return {void} + * @method + * @public + */ +export const execute = (sound: Sound, event: ProgressEvent): void => +{ + if (sound.willTrigger(Event.OPEN)) { + sound.dispatchEvent(new Event(Event.OPEN)); + } + + if (sound.willTrigger(Next2DProgressEvent.PROGRESS)) { + sound.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } +}; \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundProgressEventService.test.ts b/packages/media/src/Sound/service/SoundProgressEventService.test.ts new file mode 100644 index 00000000..6cbdd677 --- /dev/null +++ b/packages/media/src/Sound/service/SoundProgressEventService.test.ts @@ -0,0 +1,42 @@ +import { Sound } from "../../Sound"; +import { execute } from "./SoundProgressEventService"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundProgressEventService.js test", () => +{ + it("execute test case1", () => + { + const sound = new Sound(); + + let openState = ""; + let loaded = 0; + let total = 0; + sound.addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(sound, new MockEvent()); + + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + expect(loaded).toBe(1); + expect(total).toBe(10); + + }); +}); \ No newline at end of file diff --git a/packages/media/src/Sound/service/SoundProgressEventService.ts b/packages/media/src/Sound/service/SoundProgressEventService.ts new file mode 100644 index 00000000..7ac3e7ca --- /dev/null +++ b/packages/media/src/Sound/service/SoundProgressEventService.ts @@ -0,0 +1,22 @@ +import type { Sound } from "../../Sound"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; + +/** + * @description サウンドデータのローディング中のイベント実行関数 + * Event execution function during sound data loading + * + * @param {Sound} sound + * @param {ProgressEvent} event + * @return {void} + * @method + * @public + */ +export const execute = (sound: Sound, event: ProgressEvent): void => +{ + if (sound.willTrigger(Next2DProgressEvent.PROGRESS)) { + sound.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } +}; \ No newline at end of file diff --git a/packages/media/src/Sound/usecase/SoundBuildFromCharacterUseCase.ts b/packages/media/src/Sound/usecase/SoundBuildFromCharacterUseCase.ts new file mode 100644 index 00000000..46d097b5 --- /dev/null +++ b/packages/media/src/Sound/usecase/SoundBuildFromCharacterUseCase.ts @@ -0,0 +1,28 @@ +import type { ISoundCharacter } from "../../interface/ISoundCharacter"; +import type { Sound } from "../../Sound"; +import { execute as soundDecodeService } from "../service/SoundDecodeService"; + +/** + * @description キャラクターからSoundを構築 + * Build Sound from character + * + * @param {Sound} sound + * @param {ISoundCharacter} character + * @return {Promise} + * @method + * @protected + */ +export const execute = async (sound: Sound, character: ISoundCharacter): Promise => +{ + // load AudioBuffer + if (!character.audioBuffer) { + const uint8Array = new Uint8Array(character.buffer as number[]); + const audioBuffer = await soundDecodeService(uint8Array.buffer); + if (!audioBuffer) { + return ; + } + character.audioBuffer = audioBuffer; + } + + sound.audioBuffer = character.audioBuffer; +}; \ No newline at end of file diff --git a/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.test.ts b/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.test.ts new file mode 100644 index 00000000..c731d247 --- /dev/null +++ b/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.test.ts @@ -0,0 +1,77 @@ +import { Sound } from "../../Sound"; +import { execute } from "./SoundLoadEndEventUseCase"; +import { + IOErrorEvent, + ProgressEvent as Next2DProgressEvent +} from "@next2d/events"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundLoadendEventService.js test", () => +{ + it("execute test case1", () => + { + const sound = new Sound(); + + let openState = ""; + let loaded = 0; + let total = 0; + sound.addEventListener(Next2DProgressEvent.PROGRESS, (event: Next2DProgressEvent): void => + { + openState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }); + + expect(openState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "target": { + "status": 200, + "statusText": "OK", + "response": new ArrayBuffer(0) + }, + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(sound, new MockEvent()); + + expect(openState).toBe(Next2DProgressEvent.PROGRESS); + }); + + it("execute test case2", () => + { + const sound = new Sound(); + + let openState = ""; + sound.addEventListener(IOErrorEvent.IO_ERROR, (event: IOErrorEvent): void => + { + openState = event.type; + }); + + expect(openState).toBe(""); + + // mock event + const MockEvent = vi.fn().mockImplementation(() => + { + return { + "target": { + "status": 404, + "statusText": "Not Found" + }, + "loaded": 1, + "total": 10 + } as unknown as ProgressEvent; + }); + + execute(sound, new MockEvent()); + + expect(openState).toBe(IOErrorEvent.IO_ERROR); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.ts b/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.ts new file mode 100644 index 00000000..dfd027ba --- /dev/null +++ b/packages/media/src/Sound/usecase/SoundLoadEndEventUseCase.ts @@ -0,0 +1,48 @@ +import type { Sound } from "../../Sound"; +import { execute as soundDecodeService } from "../service/SoundDecodeService"; +import { + Event, + ProgressEvent as Next2DProgressEvent, + IOErrorEvent +} from "@next2d/events"; + +/** + * @description サウンドデータのローディング完了イベントの実行関数 + * Execution function for sound data loading completion event + * + * @param {Sound} sound + * @param {ProgressEvent} event + * @return {Promise} + * @method + * @public + */ +export const execute = async (sound: Sound, event: ProgressEvent): Promise => +{ + const target = event.target as XMLHttpRequest; + if (!target) { + return ; + } + + if (sound.willTrigger(Next2DProgressEvent.PROGRESS)) { + sound.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } + + if (199 < target.status && 400 > target.status) { + const audioBuffer = await soundDecodeService(target.response); + if (audioBuffer) { + sound.audioBuffer = audioBuffer; + if (sound.willTrigger(Event.COMPLETE)) { + sound.dispatchEvent(new Event(Event.COMPLETE)); + } + } + } else { + if (sound.willTrigger(IOErrorEvent.IO_ERROR)) { + sound.dispatchEvent(new IOErrorEvent( + IOErrorEvent.IO_ERROR, false, target.statusText + )); + } + } +}; \ No newline at end of file diff --git a/packages/media/src/Sound/usecase/SoundLoadUseCase.ts b/packages/media/src/Sound/usecase/SoundLoadUseCase.ts new file mode 100644 index 00000000..5ab8c18f --- /dev/null +++ b/packages/media/src/Sound/usecase/SoundLoadUseCase.ts @@ -0,0 +1,46 @@ +import type { URLRequest } from "@next2d/net"; +import type { Sound } from "../../Sound"; +import { execute as soundLoadStartEventService } from "../service/SoundLoadStartEventService"; +import { execute as soundProgressEventService } from "../service/SoundProgressEventService"; +import { execute as soundLoadEndEventUseCase } from "../usecase/SoundLoadEndEventUseCase"; +import { $ajax } from "../../MediaUtil"; + +/** + * @description 外部サウンドの読み込みを実行 + * Execute sound loading + * + * @param {Sound} sound + * @param {URLRequest} request + * @return {Promise} + * @method + * @protected + */ +export const execute = async (sound: Sound, request: URLRequest): Promise => +{ + await new Promise((resolve): void => + { + $ajax({ + "format": "arraybuffer", + "url": request.url, + "method": request.method, + "data": request.data, + "headers": request.headers, + "withCredentials": request.withCredentials, + "event": { + "loadstart": (event: ProgressEvent): void => + { + soundLoadStartEventService(sound, event); + }, + "progress": (event: ProgressEvent): void => + { + soundProgressEventService(sound, event); + }, + "loadend": async (event: ProgressEvent): Promise => + { + await soundLoadEndEventUseCase(sound, event); + resolve(); + } + } + }); + }); +}; \ No newline at end of file diff --git a/packages/media/src/SoundMixer.ts b/packages/media/src/SoundMixer.ts index 02e749d1..8f1f847a 100644 --- a/packages/media/src/SoundMixer.ts +++ b/packages/media/src/SoundMixer.ts @@ -1,15 +1,6 @@ -import type { Sound } from "./Sound"; -import type { Video } from "./Video"; -import type { Player } from "@next2d/core"; -import { - $currentPlayer, - $getSoundMixerVolume, - $setSoundMixerVolume -} from "@next2d/util"; -import { - $clamp, - $Math -} from "@next2d/share"; +import { execute as soundMixerUpdateVolumeService } from "./SoundMixer/service/SoundMixerUpdateVolumeService"; +import { execute as soundMixerStopAllService } from "./SoundMixer/service/SoundMixerStopAllService"; +import { $getVolume } from "./MediaUtil"; /** * SoundMixer クラスには、静的プロパティやアプリケーションのグローバルサウンドコントロールのメソッドが含まれます。 @@ -20,62 +11,6 @@ import { */ export class SoundMixer { - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class SoundMixer] - * @method - * @static - */ - static toString (): string - { - return "[class SoundMixer]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.media.SoundMixer - * @const - * @static - */ - static get namespace (): string - { - return "next2d.media.SoundMixer"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object SoundMixer] - * @method - * @public - */ - toString (): string - { - return "[object SoundMixer]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.media.SoundMixer - * @const - * @public - */ - get namespace (): string - { - return "next2d.media.SoundMixer"; - } - /** * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。 * The volume, ranging from 0 (silent) to 1 (full volume). @@ -86,48 +21,11 @@ export class SoundMixer */ static get volume (): number { - return $getSoundMixerVolume(); + return $getVolume(); } static set volume (volume: number) { - $setSoundMixerVolume($clamp(volume, 0, 1, 1)); - - const soundMixerVolume = $getSoundMixerVolume(); - - const player: Player = $currentPlayer(); - - const sounds: Sound[] = player._$sources; - for (let idx: number = 0; idx < sounds.length; ++idx) { - - const sound: Sound = sounds[idx]; - - for (let idx: number = 0; idx < sound._$sources.length; ++idx) { - - const source: AudioBufferSourceNode = sound._$sources[idx]; - - if (source._$gainNode) { - source._$gainNode.gain.value = $Math.min( - soundMixerVolume, - source._$volume - ); - } - - } - } - - const videos: Video[] = player._$videos; - for (let idx: number = 0; idx < videos.length; ++idx) { - - const video: Video = videos[idx]; - - if (video._$video) { - video._$video.volume = $Math.min( - soundMixerVolume, - video.volume - ); - } - } - + soundMixerUpdateVolumeService(volume); } /** @@ -140,20 +38,6 @@ export class SoundMixer */ static stopAll (): void { - const player: Player = $currentPlayer(); - - // sounds - const sources: Sound[] = player._$sources; - for (let idx: number = 0; idx < sources.length; ++idx) { - sources[idx].stop(); - } - - const videos = player._$videos; - for (let idx: number = 0; idx < videos.length; ++idx) { - videos[idx].pause(); - } - - player._$sources.length = 0; - player._$videos.length = 0; + soundMixerStopAllService(); } -} +} \ No newline at end of file diff --git a/packages/media/src/SoundMixer/service/SoundMixerStopAllService.test.ts b/packages/media/src/SoundMixer/service/SoundMixerStopAllService.test.ts new file mode 100644 index 00000000..389dae15 --- /dev/null +++ b/packages/media/src/SoundMixer/service/SoundMixerStopAllService.test.ts @@ -0,0 +1,47 @@ +import type { Video } from "../../Video"; +import type { Sound } from "../../Sound"; +import { $getPlayingSounds, $getPlayingVideos } from "../../MediaUtil"; +import { execute } from "./SoundMixerStopAllService"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundMixerStopAllService.js test", () => +{ + it("execute test case1", () => + { + let soundState = ""; + const MockSound = vi.fn().mockImplementation(() => + { + return { + "stop": vi.fn(() => { soundState = "stop" }) + } as unknown as Sound; + }); + + const playingSounds = $getPlayingSounds(); + playingSounds.push(new MockSound()); + + let videoState = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "pause": vi.fn(() => { videoState = "pause" }) + } as unknown as Video; + }); + + const playingVideos = $getPlayingVideos(); + playingVideos.push(new MockVideo()); + + // before + expect(soundState).toBe(""); + expect(videoState).toBe(""); + expect(playingSounds.length).toBe(1); + expect(playingVideos.length).toBe(1); + + execute(); + + // after + expect(soundState).toBe("stop"); + expect(videoState).toBe("pause"); + expect(playingSounds.length).toBe(0); + expect(playingVideos.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/media/src/SoundMixer/service/SoundMixerStopAllService.ts b/packages/media/src/SoundMixer/service/SoundMixerStopAllService.ts new file mode 100644 index 00000000..06cf1b6d --- /dev/null +++ b/packages/media/src/SoundMixer/service/SoundMixerStopAllService.ts @@ -0,0 +1,41 @@ +import type { Sound } from "../../Sound"; +import type { Video } from "../../Video"; +import { + $getPlayingSounds, + $getPlayingVideos +} from "../../MediaUtil"; + +/** + * @description 再生中のサウンドとビデオの全ての再生を停止 + * Stops all playing sounds and videos. + * + * @return {void} + * @method + * @public + */ +export const execute = (): void => +{ + // sounds + const playingSounds: Sound[] = $getPlayingSounds(); + for (let idx: number = 0; idx < playingSounds.length; ++idx) { + const sound = playingSounds[idx]; + if (!sound) { + continue; + } + sound.stop(); + } + + // videos + const playingVideos: Video[] = $getPlayingVideos(); + for (let idx: number = 0; idx < playingVideos.length; ++idx) { + const video: Video = playingVideos[idx]; + if (!video) { + continue; + } + video.pause(); + } + + // reset + playingSounds.length = 0; + playingVideos.length = 0; +}; \ No newline at end of file diff --git a/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.test.ts b/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.test.ts new file mode 100644 index 00000000..78894ac2 --- /dev/null +++ b/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.test.ts @@ -0,0 +1,71 @@ +import type { Video } from "../../Video"; +import type { Sound } from "../../Sound"; +import { SoundMixer } from "../../SoundMixer"; +import { $clamp } from "../../MediaUtil"; +import { $getPlayingSounds, $getPlayingVideos } from "../../MediaUtil"; +import { execute } from "./SoundMixerUpdateVolumeService"; +import { describe, expect, it, vi } from "vitest"; + +describe("SoundMixerUpdateVolumeService.js test", () => +{ + it("execute test case1", () => + { + const MockSound = vi.fn().mockImplementation(() => + { + let volume = 1; + return { + get volume (): number + { + return volume + }, + set volume(value: number) + { + volume = $clamp(value, 0, 1); + } + } as unknown as Sound; + }); + + const mockSound = new MockSound(); + const playingSounds = $getPlayingSounds(); + playingSounds.push(mockSound); + + const MockVideo = vi.fn().mockImplementation(() => + { + let volume = 1; + return { + get volume (): number + { + return volume + }, + set volume(value: number) + { + volume = $clamp(value, 0, 1); + } + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + const playingVideos = $getPlayingVideos(); + playingVideos.push(mockVideo); + + // before + expect(mockSound.volume).toBe(1); + expect(mockVideo.volume).toBe(1); + expect(SoundMixer.volume).toBe(1); + + execute(0.5); + expect(mockSound.volume).toBe(0.5); + expect(mockVideo.volume).toBe(0.5); + expect(SoundMixer.volume).toBe(0.5); + + execute(-0.5); + expect(mockSound.volume).toBe(0); + expect(mockVideo.volume).toBe(0); + expect(SoundMixer.volume).toBe(0); + + execute(3); + expect(mockSound.volume).toBe(1); + expect(mockVideo.volume).toBe(1); + expect(SoundMixer.volume).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.ts b/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.ts new file mode 100644 index 00000000..7c5e1c2c --- /dev/null +++ b/packages/media/src/SoundMixer/service/SoundMixerUpdateVolumeService.ts @@ -0,0 +1,43 @@ +import type { Sound } from "../../Sound"; +import type { Video } from "../../Video"; +import { + $setVolume, + $getPlayingSounds, + $getPlayingVideos +} from "../../MediaUtil"; + +/** + * @description 再生中のサウンドとビデオの全ての音量を更新 + * Updates the volume of all playing sounds and videos. + * + * @param {number} volume + * @method + * @public + */ +export const execute = (volume: number): void => +{ + // update volume + $setVolume(volume); + + const playingSounds: Sound[] = $getPlayingSounds(); + for (let idx: number = 0; idx < playingSounds.length; ++idx) { + + const sound: Sound = playingSounds[idx]; + if (!sound) { + continue; + } + + sound.volume = volume; + } + + const playingVideos: Video[] = $getPlayingVideos(); + for (let idx: number = 0; idx < playingVideos.length; ++idx) { + + const video: Video = playingVideos[idx]; + if (!video) { + continue; + } + + video.volume = volume; + } +}; \ No newline at end of file diff --git a/packages/media/src/SoundTransform.ts b/packages/media/src/SoundTransform.ts index 5fc7fafb..093c9e7e 100644 --- a/packages/media/src/SoundTransform.ts +++ b/packages/media/src/SoundTransform.ts @@ -1,133 +1,42 @@ -import { $clamp } from "@next2d/share"; - /** - * SoundTransform クラスにはボリュームとループのプロパティが含まれます。 - * - * The SoundTransform class contains properties for volume and loop. + * @description SoundTransform クラスにはボリュームとループのプロパティが含まれます。 + * The SoundTransform class contains properties for volume and loop. * * @class * @memberOf next2d.media */ export class SoundTransform { - private _$volume: number; - private _$loop: boolean; - - /** - * @param {number} [volume=1] - * @param {boolean} [loop=false] - * - * @constructor - * @public - */ - constructor (volume: number = 1, loop: boolean = false) - { - /** - * @type {number} - * @default 1 - * @private - */ - this._$volume = 1; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$loop = false; - - // setup - this.volume = volume; - this.loop = loop; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class SoundTransform] - * @method - * @static - */ - static toString (): string - { - return "[class SoundTransform]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.media.SoundTransform - * @const - * @static - */ - static get namespace (): string - { - return "next2d.media.SoundTransform"; - } - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object SoundTransform] - * @method - * @public - */ - toString (): string - { - return "[object SoundTransform]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。 + * The volume, ranging from 0 (silent) to 1 (full volume). * - * @return {string} - * @default next2d.media.SoundTransform - * @const + * @member {number} + * @default 1 * @public */ - get namespace (): string - { - return "next2d.media.SoundTransform"; - } + public volume: number; /** - * @description ループ設定です。 - * loop setting. + * @description ループ回数の設定 + * Loop count setting. * - * @member {boolean} + * @type {boolean} * @default false * @public */ - get loop (): boolean - { - return this._$loop; - } - set loop (loop: boolean) - { - this._$loop = loop; - } + public loopCount: number; /** - * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。 - * The volume, ranging from 0 (silent) to 1 (full volume). + * @param {number} [volume=1] + * @param {number} [loop_count=0] * - * @member {number} - * @default 1 + * @constructor * @public */ - get volume (): number - { - return this._$volume; - } - set volume (volume: number) + constructor (volume: number = 1, loop_count: number = 0) { - this._$volume = $clamp(+volume, 0, 1, 0); + this.volume = volume; + this.loopCount = loop_count; } -} +} \ No newline at end of file diff --git a/packages/media/src/Video.ts b/packages/media/src/Video.ts index 7f7d4161..7a6aa5d1 100644 --- a/packages/media/src/Video.ts +++ b/packages/media/src/Video.ts @@ -1,61 +1,25 @@ +import type { IVideoCharacter } from "./interface/IVideoCharacter"; +import type { ICharacter } from "./interface/ICharacter"; +import type { LoaderInfo } from "@next2d/display"; import { SoundMixer } from "./SoundMixer"; import { DisplayObject } from "@next2d/display"; import { VideoEvent } from "@next2d/events"; -import type { Player } from "@next2d/core"; -import type { - BoundsImpl, - VideoCharacterImpl, - DictionaryTagImpl, - ParentImpl, - AttachmentImpl, - FilterArrayImpl, - BlendModeImpl, - PlayerHitObjectImpl, - PropertyVideoMessageImpl, - Character, - CachePositionImpl -} from "@next2d/interface"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; +import { execute as videoCreateElementService } from "./Video/service/VideoCreateElementService"; +import { execute as videoRegisterEventUseCase } from "./Video/usecase/VideoRegisterEventUseCase"; +import { execute as videoPlayEventUseCase } from "./Video/usecase/VideoPlayEventUseCase"; +import { execute as videoBuildFromCharacterUseCase } from "./Video/usecase/VideoBuildFromCharacterUseCase"; import { - $document, - $audioContext, - $currentPlayer, - $isTouch, - $rendererWorker -} from "@next2d/util"; -import { - $Math, - $cancelAnimationFrame, - $requestAnimationFrame, - $getBoundsObject, - $boundsMatrix, $clamp, - $multiplicationMatrix, - $poolFloat32Array6, - $MATRIX_ARRAY_IDENTITY, - $OffscreenCanvas, - $multiplicationColor, - $poolFloat32Array8, - $Infinity, - $poolBoundsObject, - $getArray, - $Number, - $poolArray, - $cacheStore, - $getFloat32Array6 -} from "@next2d/share"; + $getPlayingVideos +} from "./MediaUtil"; /** - * サーバーまたはローカルに保存された録画済みビデオファイルを再生する Video オブジェクトです。 - * ビデオストリームを再生するには、attachNetStream() を使用して、ビデオを Video オブジェクトに関連付けます。 - * 次に、addChild() を使用して、Video オブジェクトを表示リストに追加します。 - * - * A Video object that plays a recorded video file stored on a server or locally. - * To play a video stream, use attachNetStream() to attach the video to the Video object. - * Then, add the Video object to the display list using addChild(). + * @description サーバーまたはローカルに保存された録画済みビデオファイルを再生する Video オブジェクトです。 + * ビデオストリームを再生するには、attachNetStream() を使用して、ビデオを Video オブジェクトに関連付けます。 + * 次に、addChild() を使用して、Video オブジェクトを表示リストに追加します。 + * A Video object that plays a recorded video file stored on a server or locally. + * To play a video stream, use attachNetStream() to attach the video to the Video object. + * Then, add the Video object to the display list using addChild(). * * @class * @memberOf next2d.media @@ -63,1206 +27,384 @@ import { */ export class Video extends DisplayObject { - private _$smoothing: boolean; - private _$loop: boolean; - private _$autoPlay: boolean; - private readonly _$bounds: BoundsImpl; - private _$bytesLoaded: number; - private _$bytesTotal: number; - private _$timerId: number; - public _$video: HTMLVideoElement | null; - private _$stop: boolean; - private _$volume: number; - private _$ready: boolean; - private _$context: OffscreenCanvasRenderingContext2D | null; - private _$cacheKeys: string[]; - private readonly _$cacheParams: number[]; - /** - * @param {number} [width = 0] - * @param {number} [height = 0] + * @description キーフレーム総数 + * Total number of keyframes * - * @constructor + * @type {number} + * @default 0 * @public */ - constructor (width: number = 0, height: number = 0) - { - super(); - - /** - * @type {boolean} - * @default true - * @private - */ - this._$smoothing = true; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$loop = false; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$autoPlay = true; - - /** - * @type {object} - * @private - */ - this._$bounds = $getBoundsObject(0, width, 0, height); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesLoaded = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bytesTotal = 0; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$timerId = -1; - - /** - * @type {HTMLVideoElement} - * @default null - * @private - */ - this._$video = null; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$stop = true; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$ready = false; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$volume = 1; - - /** - * @type {CanvasRenderingContext2D} - * @default null - * @private - */ - this._$context = null; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - } + public duration: number; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description ビデオを拡大 / 縮小する際にスムージング(補間)するかどうかを指定します。 + * Specifies whether the video should be smoothed (interpolated) + * when it is scaled. * - * @return {string} - * @default [class Video] - * @method - * @static + * @type {boolean} + * @default true + * @public */ - static toString (): string - { - return "[class Video]"; - } + public smoothing: boolean; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description ビデオをループ生成するかどうかを指定します。 + * Specifies whether to generate a video loop. * - * @return {string} - * @default next2d.media.Video - * @const - * @static + * @type {boolean} + * @default false + * @public */ - static get namespace (): string - { - return "next2d.media.Video"; - } + public loop: boolean; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description ビデオの自動再生の設定。 + * Setting up automatic video playback. * - * @return {string} - * @default [object Video] - * @method + * @type {boolean} + * @default true * @public */ - toString (): string - { - return "[object Video]"; - } + public autoPlay: boolean; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description 現在のキーフレーム + * Current keyframe * - * @return {string} - * @default next2d.media.Video - * @const + * + * @member {number} * @public */ - get namespace (): string - { - return "next2d.media.Video"; - } + public currentTime: number; /** - * @description 既にアプリケーションにロードされているデータのバイト数です。 - * The number of bytes of data that have been loaded into the application. + * @description ビデオの幅をピクセル単位で指定する整数です。 + * An integer specifying the width of the video, in pixels. * * @member {number} * @default 0 - * @readonly * @public */ - get bytesLoaded (): number - { - return this._$bytesLoaded; - } + public videoWidth: number; /** - * @description アプリケーションにロードされるファイルの総バイト数。 - * The total size in bytes of the file being loaded into the application. + * @description ビデオの高さをピクセル単位で指定する整数です。 + * An integer specifying the height of the video, in pixels. * * @member {number} * @default 0 - * @readonly * @public */ - get bytesTotal (): number - { - return this._$bytesTotal; - } + public videoHeight: number; /** - * @description 現在のキーフレーム - * Current keyframe - * + * @description ビデオが読み込まれているかどうかを返します。 + * Returns whether the video has been loaded. * - * @member {number} - * @readonly + * @type {boolean} + * @default false * @public */ - get currentTime (): number - { - return this._$video ? this._$video.currentTime : 0; - } + public loaded: boolean; /** - * @description キーフレーム総数 - * Total number of keyframes + * @description ビデオが終了したかどうかを返します。 + * Returns whether the video has ended. * - * @member {number} - * @readonly + * @type {boolean} + * @default false * @public */ - get duration (): number - { - return this._$video ? this._$video.duration : 0; - } + public ended: boolean; /** - * @description ビデオをループ生成するかどうかを指定します。 - * Specifies whether to generate a video loop. + * @description Videoの機能を所持しているかを返却 + * Returns whether the display object has Video functionality. * - * @member {boolean} - * @default false + * @type {boolean} + * @readonly * @public */ - get loop (): boolean - { - return this._$loop; - } - set loop (loop: boolean) - { - this._$loop = !!loop; - } + public readonly isVideo: boolean; /** - * @description ビデオの自動再生の設定。 - * Setting up automatic video playback. - * - * @member {boolean} - * @default true + * @type {HTMLVideoElement} + * @default null * @public */ - get autoPlay (): boolean - { - return this._$autoPlay; - } - set autoPlay (auto_play: boolean) - { - this._$autoPlay = !!auto_play; - } + public $videoElement: HTMLVideoElement | null; /** - * @description ビデオを拡大 / 縮小する際にスムージング(補間)するかどうかを指定します。 - * Specifies whether the video should be smoothed (interpolated) - * when it is scaled. - * - * @member {boolean} - * @default true + * @type {OffscreenCanvas} + * @default null * @public */ - get smoothing (): boolean - { - return this._$smoothing; - } - set smoothing (smoothing: boolean) - { - this._$smoothing = !!smoothing; - } + public $offscreenCanvas: OffscreenCanvas | null; /** - * @description 映像コンテンツへの URL を指定します。 - * Specifies the URL to the video content. - * - * @member {string} - * @default "" + * @type {OffscreenCanvasRenderingContext2D} + * @default null * @public */ - get src (): string - { - return this._$video ? this._$video.src : ""; - } - set src (src: string) - { - if (!this._$video) { - this._$video = this._$initializeVideo(); - } - - this._$video.src = src; - this._$video.load(); - } + public $context: OffscreenCanvasRenderingContext2D | null; /** - * @description ビデオストリームの高さをピクセル単位で指定する整数です。 - * An integer specifying the height of the video stream, in pixels. - * - * @member {number} - * @default 320 - * @readonly + * @type {boolean} + * @default true * @public */ - get videoHeight (): number - { - return this._$video ? this._$video.videoHeight : this._$bounds.yMax; - } + public paused: boolean; /** - * @description ビデオストリームの幅をピクセル単位で指定する整数です。 - * An integer specifying the width of the video stream, in pixels. - * - * @member {number} - * @default 240 - * @readonly - * @public + * @type {number} + * @default -1 + * @private */ - get videoWidth (): number - { - return this._$video ? this._$video.videoWidth : this._$bounds.xMax; - } + private _$timerId: number; /** - * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。 - * The volume, ranging from 0 (silent) to 1 (full volume). - * - * @member {number} + * @type {number} * @default 1 - * @public + * @private */ - get volume (): number - { - return this._$volume; - } - set volume (volume: number) - { - this._$volume = $clamp($Math.min( - SoundMixer.volume, - volume - ), 0, 1, 1); - - if (this._$video) { - this._$video.volume = this._$volume; - } - } + private _$volume: number; /** - * @description Video オブジェクトに現在表示されているイメージ(ビデオストリームではない)をクリアします。 - * Clears the image currently displayed - * in the Video object (not the video stream). - * - * @return {void} - * @method - * @public + * @type {string} + * @default "" + * @private */ - clear (): void - { - if (this._$video) { - this._$video.pause(); - } - - // reset - this._$video = null; - this._$bounds.xMax = 0; - this._$bounds.yMax = 0; - - this._$doChanged(); - } + private _$src: string; /** - * @description ビデオストリームの再生を一時停止します。 - * Pauses playback of a video stream. + * @param {number} [width = 0] + * @param {number} [height = 0] * - * @return {void} - * @method + * @constructor * @public */ - pause (): void + constructor (width: number = 0, height: number = 0) { - if (this._$video && !this._$stop) { + super(); - this._$stop = true; - this._$video.pause(); + this.videoWidth = width; + this.videoHeight = height; - $cancelAnimationFrame(this._$timerId); - this._$timerId = -1; + // public params + this.isVideo = true; + this.duration = 0; + this.smoothing = true; + this.loop = false; + this.loaded = false; + this.ended = false; + this.paused = true; + this.autoPlay = true; + this.currentTime = 0; - if (this.hasEventListener(VideoEvent.PAUSE)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.PAUSE, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } + // private params + this._$src = ""; + this._$timerId = -1; + this._$volume = 1; - const player: Player = $currentPlayer(); - player._$videos.splice( - player._$videos.indexOf(this), 1 - ); - } + // element + this.$videoElement = null; + this.$offscreenCanvas = null; + this.$context = null; } /** - * @description ローカルディレクトリまたは Web サーバーからメディアファイルを再生します。 - * Plays a media file from a local directory or a web server; + * @description 指定されたクラスの空間名を返します。 + * Returns the space name of the specified class. * - * @returns {void} - * @method - * @public + * @return {string} + * @const + * @static */ - play (): void + static get namespace (): string { - if (this._$video && this._$stop) { - - this._$stop = false; - - this._$video.volume = $Math.min(this._$volume, SoundMixer.volume); - this - ._$video - .play() - .then(() => - { - this._$timerId = $requestAnimationFrame(() => - { - this._$update(); - }); - - if (this.hasEventListener(VideoEvent.PLAY)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.PLAY, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } - - const player = $currentPlayer(); - if (player._$videos.indexOf(this) === -1) { - player._$videos.push(this); - } - - this._$ready = true; - }); - } + return "next2d.media.Video"; } /** - * @description 指定された位置に最も近いキーフレームをシークします。 - * Seeks the keyframe closest to the specified location. + * @description 指定されたオブジェクトの空間名を返します。 + * Returns the space name of the specified object. * - * @param {number} offset - * @return {void} - * @method + * @return {string} + * @const * @public */ - seek (offset: number): void + get namespace (): string { - if (this._$video) { - this._$video.currentTime = offset; - - if (this.hasEventListener(VideoEvent.SEEK)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.SEEK, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } - } + return "next2d.media.Video"; } /** - * @return {void} - * @method - * @private + * @description ビデオコンテンツへの URL を指定します。 + * Specifies the URL of the video content. + * + * @member {string} + * @default "" + * @public */ - _$update (): void + get src (): string { - const player: Player = $currentPlayer(); - if (!this.stage || !this._$video) { - - if (this._$video) { - this._$video.pause(); - } - - $cancelAnimationFrame(this._$timerId); - this._$timerId = -1; - - player._$videos.splice( - player._$videos.indexOf(this), 1 - ); - - return ; - } - - if ($rendererWorker) { - this._$postProperty(); - } - - // update - this._$bytesLoaded = this._$video.currentTime; - if (this._$video.currentTime) { - - if (this.hasEventListener(VideoEvent.PROGRESS)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.PROGRESS, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } - - this._$doChanged(); - } - - this._$timerId = $requestAnimationFrame(() => - { - this._$update(); - }); + return this._$src; } - - /** - * @return {void} - * @method - * @private - */ - _$start () + set src (src: string) { - if (!this._$video) { + if (this._$src === src) { return ; } - this._$bounds.xMax = this._$video.videoWidth; - this._$bounds.yMax = this._$video.videoHeight; - this._$bytesTotal = this._$video.duration; - - const player = $currentPlayer(); - if (this._$autoPlay) { - - this._$stop = false; - this - ._$video - .play() - .then(() => - { - if (player._$videos.indexOf(this) === -1) { - player._$videos.push(this); - } - - if (this.hasEventListener(VideoEvent.PLAY_START)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.PLAY_START, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } - - this._$timerId = $requestAnimationFrame(() => - { - this._$update(); - }); - - this._$ready = true; - - this._$doChanged(); - }); - } - - this._$createContext(); - } - - /** - * @return {HTMLVideoElement} - * @method - * @private - */ - _$initializeVideo (): HTMLVideoElement - { - // clear cache key - this._$cacheKeys.length = 0; - - const video = $document.createElement("video"); - - video.autoplay = false; - video.crossOrigin = "anonymous"; - - if (!$audioContext) { - video.muted = true; - } - - if ($isTouch) { - video.setAttribute("playsinline", ""); - } - - video.addEventListener("canplaythrough", () => - { - this._$start(); - }); - - video.addEventListener("ended", () => - { - if (this._$loop) { - video.currentTime = 0; - return ; - } - - if (this.hasEventListener(VideoEvent.PLAY_END)) { - this.dispatchEvent(new VideoEvent( - VideoEvent.PLAY_END, false, false, - this._$bytesLoaded, this._$bytesTotal - )); - } - - $cancelAnimationFrame(this._$timerId); - - this._$timerId = -1; + // reset + this.loaded = false; + this.currentTime = 0; + this.$videoElement = null; + this.$offscreenCanvas = null; + this.$context = null; - }); + this.$videoElement = videoCreateElementService(); + videoRegisterEventUseCase(this.$videoElement, this); - return video; + this._$src = this.$videoElement.src = src; + this.$videoElement.load(); } /** - * @return {void} - * @method - * @private + * @description ビデオがミュートされているかどうかを返します。 + * Returns whether the video is muted. + * + * @member {boolean} + * @default false + * @public */ - _$createContext (): void + get muted (): boolean { - if ($rendererWorker) { - const canvas = new $OffscreenCanvas( - this._$bounds.xMax, - this._$bounds.yMax - ); - this._$context = canvas.getContext("2d"); - } + return this.$videoElement ? this.$videoElement.muted : false; } - - /** - * @param {object} character - * @return {void} - * @method - * @private - */ - _$buildCharacter (character: Character): void + set muted (muted: boolean) { - if (character.buffer && !character._$buffer) { - character._$buffer = new Uint8Array(character.buffer); - character.buffer = null; - } - - this._$loop = character.loop; - this._$autoPlay = character.autoPlay; - this._$bounds.xMin = character.bounds.xMin; - this._$bounds.yMin = character.bounds.yMin; - this._$bounds.xMax = character.bounds.xMax; - this._$bounds.yMax = character.bounds.yMax; - - if (!this._$video) { - this._$video = this._$initializeVideo(); - } - - this._$video.src = URL.createObjectURL(new Blob( - [character._$buffer], - { "type": "video/mp4" } - )); - - // setup - this._$video.volume = $Math.min(character.volume, SoundMixer.volume); - this._$video.load(); - - if ($rendererWorker && this._$stage) { - this._$createWorkerInstance(); + if (!this.$videoElement) { + return ; } + this.$videoElement.muted = muted; } /** - * @param {object} character - * @return {void} - * @method - * @private + * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。 + * The volume, ranging from 0 (silent) to 1 (full volume). + * + * @member {number} + * @default 1 + * @public */ - _$sync (character: VideoCharacterImpl): void + get volume (): number { - this._$buildCharacter(character); - } - - /** - * @param {object} tag - * @param {DisplayObjectContainer} parent - * @return {object} - * @method - * @private - */ - _$build ( - tag: DictionaryTagImpl, - parent: ParentImpl - ): VideoCharacterImpl { - - const character: VideoCharacterImpl = this._$baseBuild(tag, parent); - - this._$buildCharacter(character); - - return character; + return this._$volume; } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @returns {void} - * @method - * @private - */ - _$clip (context: CanvasToWebGLContext, matrix: Float32Array): void + set volume (volume: number) { - const width: number = this._$bounds.xMax; - const height: number = this._$bounds.yMax; - if (!width || !height) { - return; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - context.reset(); - context.setTransform( - multiMatrix[0], multiMatrix[1], multiMatrix[2], - multiMatrix[3], multiMatrix[4], multiMatrix[5] - ); - - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - context.clip(); + this._$volume = $clamp(Math.min( + SoundMixer.volume, + volume + ), 0, 1, 1); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); + if (this.$videoElement) { + this.$videoElement.volume = this._$volume; } } /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform + * @description ビデオの再生を一時停止します。 + * Pauses the video playback. + * * @return {void} * @method - * @private + * @public */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible || !this._$video || !this._$ready) { - return ; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$transform._$rawColorTransform(); - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } + pause (): void + { + if (!this.$videoElement || this.paused) { return ; } - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - // default bounds - const bounds: BoundsImpl = $boundsMatrix(this._$bounds, multiMatrix); - const xMax = +bounds.xMax; - const xMin = +bounds.xMin; - const yMax = +bounds.yMax; - const yMin = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const filters: FilterArrayImpl = this._$filters || this.filters; - const canApply: boolean = filters && filters.length > 0 && this._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - $poolBoundsObject(filterBounds); - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - const keys: number[] = $getArray(); - keys[0] = xScale; - keys[1] = yScale; - - this._$cacheKeys = $cacheStore.generateKeys( - this._$instanceId, keys, color_transform - ); - - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - - const blendMode: BlendModeImpl = this._$blendMode || this.blendMode; - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - const width: number = $Math.ceil($Math.abs(this._$bounds.xMax - this._$bounds.xMin)); - const height: number = $Math.ceil($Math.abs(this._$bounds.yMax - this._$bounds.yMin)); - - const position: CachePositionImpl = manager - .createCachePosition(width, height); - - context.cachePosition = position; - $cacheStore.set(this._$cacheKeys, position); - } - - const texture: WebGLTexture = manager.createTextureFromVideo( - this._$video, this._$smoothing - ); + this.paused = true; + this.$videoElement.pause(); - let offsetX: number = 0; - let offsetY: number = 0; - if (canApply) { + cancelAnimationFrame(this._$timerId); - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - - context._$bind(attachment); - - context.reset(); - - const parentMatrix: Float32Array = $getFloat32Array6( - xScale, 0, 0, yScale, - width / 2, height / 2 - ); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - -texture.width / 2, - -texture.height / 2 - ); - - const scaleMatrix = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - context.setTransform( - scaleMatrix[0], scaleMatrix[1], - scaleMatrix[2], scaleMatrix[3], - scaleMatrix[4], scaleMatrix[5] - ); - - context.drawImage(texture, 0, 0, texture.width, texture.height); - - const videoTexture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - context._$bind(currentAttachment); - - manager.releaseAttachment(attachment); - - // release - context.drawTextureFromRect(texture, context.cachePosition); - - const position: CachePositionImpl = this._$drawFilter( - context, multiMatrix, filters, - width, height, videoTexture - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } else { - - context.drawTextureFromRect(texture, context.cachePosition); - - context.setTransform( - multiMatrix[0], multiMatrix[1], multiMatrix[2], - multiMatrix[3], multiMatrix[4], multiMatrix[5] - ); - - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = true; - context.globalCompositeOperation = blendMode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; + if (this.willTrigger(VideoEvent.PAUSE)) { + this.dispatchEvent(new VideoEvent(VideoEvent.PAUSE)); } - // pool - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); + const playingVideos = $getPlayingVideos(); + const index = playingVideos.indexOf(this); + if (index > -1) { + playingVideos.splice(index, 1); } } /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {object} options - * @return {boolean} - * @method - * @private - */ - _$mouseHit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl - ): boolean { - - if (!this._$visible) { - return false; - } - - return this._$hit(context, matrix, options); - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {array} matrix - * @param {object} options - * @return {boolean} - * @method - * @private - */ - _$hit ( - context: CanvasRenderingContext2D, - matrix: Float32Array, - options: PlayerHitObjectImpl - ): boolean { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix !== $MATRIX_ARRAY_IDENTITY) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const baseBounds: BoundsImpl = this._$getBounds(null); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - const xMax = +bounds.xMax; - const xMin = +bounds.xMin; - const yMax = +bounds.yMax; - const yMin = +bounds.yMin; - $poolBoundsObject(bounds); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - - context.setTransform(1, 0, 0, 1, xMin, yMin); - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return context.isPointInPath(options.x, options.y); - } - - /** - * @param {Float32Array} [matrix=null] - * @return {object} + * @description ビデオファイルを再生します。 + * Plays the video file. + * + * @returns {void} * @method - * @private + * @public */ - _$getBounds (matrix: Float32Array | null = null): BoundsImpl + async play (): Promise { - if (matrix) { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$transform._$rawMatrix(); - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } + if (!this.$videoElement || !this.paused) { + return ; + } - const bounds: BoundsImpl = $boundsMatrix(this._$bounds, multiMatrix); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } + this.paused = false; + this.ended = false; - return bounds; - } + this.$videoElement.volume = this._$volume; + await this.$videoElement.play(); - return $getBoundsObject( - this._$bounds.xMin, this._$bounds.xMax, - this._$bounds.yMin, this._$bounds.yMax - ); + this._$timerId = videoPlayEventUseCase(this); } /** + * @description 指定された位置に最も近いキーフレームをシークします。 + * Seeks the keyframe closest to the specified location. + * + * @param {number} offset * @return {void} * @method - * @private + * @public */ - _$createWorkerInstance (): void + seek (offset: number): void { - if (!$rendererWorker || this._$created) { + if (!this.$videoElement) { return ; } - this._$created = true; - - const message: PropertyVideoMessageImpl = { - "command": "createVideo", - "buffer": new Float32Array(), - "instanceId": this._$instanceId, - "parentId": this._$parent ? this._$parent._$instanceId : -1, - "smoothing": this._$smoothing, - "xMin": this._$bounds.xMin, - "yMin": this._$bounds.yMin, - "xMax": this._$bounds.xMax, - "yMax": this._$bounds.yMax - }; - if (this._$characterId > -1) { - message.characterId = this._$characterId; - } - - if (this._$loaderInfo) { - message.loaderInfoId = this._$loaderInfo._$id; - } + this.currentTime = this.$videoElement.currentTime = Math.min(this.duration, offset); - if (this._$scale9Grid) { - message.grid = { - "x": this._$scale9Grid.x, - "y": this._$scale9Grid.y, - "w": this._$scale9Grid.width, - "h": this._$scale9Grid.height - }; + if (this.willTrigger(VideoEvent.SEEK)) { + this.dispatchEvent(new VideoEvent(VideoEvent.SEEK)); } - - $rendererWorker.postMessage(message); } /** + * @description character 情報を元に DisplayObject を構築 + * Build DisplayObject based on character + * + * @param {ICharacter} character + * @param {LoaderInfo} [loader_info=null] * @return {void} * @method - * @private + * @protected */ - _$postProperty (): void + $sync (character: ICharacter, loader_info: LoaderInfo | null = null): void { - if (!$rendererWorker) { - return ; + if (loader_info) { + super.$syncLoaderInfo(loader_info); } - - const message: PropertyVideoMessageImpl = this._$createMessage(); - message.smoothing = this._$smoothing; - - const options = $getArray(); - const context = this._$context; - if (context && this._$video) { - - message.xMin = this._$bounds.xMin; - message.yMin = this._$bounds.yMin; - message.xMax = this._$bounds.xMax; - message.yMax = this._$bounds.yMax; - - context.drawImage(this._$video, 0, 0); - - const imageBitmap = context.canvas.transferToImageBitmap(); - message.imageBitmap = imageBitmap; - options.push(imageBitmap); - } - - $rendererWorker.postMessage(message, options); - - $poolArray(options); - - this._$posted = true; - this._$updated = false; + videoBuildFromCharacterUseCase(this, character as IVideoCharacter); } -} +} \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoApplyChangesService.test.ts b/packages/media/src/Video/service/VideoApplyChangesService.test.ts new file mode 100644 index 00000000..a799b4b1 --- /dev/null +++ b/packages/media/src/Video/service/VideoApplyChangesService.test.ts @@ -0,0 +1,17 @@ +import { execute } from "./VideoApplyChangesService"; +import { Video } from "../../Video"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldApplyChangesService.js test", () => +{ + it("execute test case1", () => + { + const video = new Video(); + + video.changed = false; + expect(video.changed).toBe(false); + + execute(video); + expect(video.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoApplyChangesService.ts b/packages/media/src/Video/service/VideoApplyChangesService.ts new file mode 100644 index 00000000..e39ba760 --- /dev/null +++ b/packages/media/src/Video/service/VideoApplyChangesService.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "@next2d/display"; + +/** + * @description DisplayObjectの更新フラグを立てる + * Set the update flag of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + display_object.changed = true; + + const parent = display_object.parent as unknown as D; + if (parent && !parent.changed) { + execute(parent); + } +}; \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoCreateElementService.test.ts b/packages/media/src/Video/service/VideoCreateElementService.test.ts new file mode 100644 index 00000000..e5cfdb2f --- /dev/null +++ b/packages/media/src/Video/service/VideoCreateElementService.test.ts @@ -0,0 +1,14 @@ +import { execute } from "./VideoCreateElementService"; +import { describe, expect, it } from "vitest"; + +describe("VideoCreateElementService.js test", () => +{ + it("execute test case1", () => + { + const element = execute(); + + expect(element.autoplay).toBe(false); + expect(element.crossOrigin).toBe("anonymous"); + expect(element.getAttribute("playsinline")).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoCreateElementService.ts b/packages/media/src/Video/service/VideoCreateElementService.ts new file mode 100644 index 00000000..b6a4c245 --- /dev/null +++ b/packages/media/src/Video/service/VideoCreateElementService.ts @@ -0,0 +1,19 @@ +/** + * @description HTMLVideoElementを作成して、各種イベントを設定する + * Create an HTMLVideoElement and set various events + * + * @return {HTMLVideoElement} + * @method + * @protected + */ +export const execute = (): HTMLVideoElement => +{ + const element = document.createElement("video"); + element.autoplay = false; + element.crossOrigin = "anonymous"; + + // Required for iOS + element.setAttribute("playsinline", ""); + + return element; +}; \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoEndedEventService.test.ts b/packages/media/src/Video/service/VideoEndedEventService.test.ts new file mode 100644 index 00000000..7c5d3cda --- /dev/null +++ b/packages/media/src/Video/service/VideoEndedEventService.test.ts @@ -0,0 +1,63 @@ +import type { Video } from "../../Video"; +import { VideoEvent } from "@next2d/events"; +import { execute } from "./VideoEndedEventService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoEndedEventService.js test", () => +{ + it("execute test case1", () => + { + let eventType = ""; + let pauseState = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn((event: VideoEvent) => { eventType = event.type }), + "loop": true, + "pause": vi.fn(() => { pauseState = "pause" }), + "currentTime": 100 + } as unknown as Video; + }); + + expect(eventType).toBe(""); + expect(pauseState).toBe(""); + + const mockVideo = new MockVideo(); + expect(mockVideo.currentTime).toBe(100); + + execute(mockVideo); + + expect(eventType).toBe(VideoEvent.ENDED); + expect(pauseState).toBe(""); + expect(mockVideo.currentTime).toBe(0); + }); + + it("execute test case2", () => + { + let eventType = ""; + let pauseState = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn((event: VideoEvent) => { eventType = event.type }), + "loop": false, + "pause": vi.fn(() => { pauseState = "pause" }), + "currentTime": 100 + } as unknown as Video; + }); + + expect(eventType).toBe(""); + expect(pauseState).toBe(""); + + const mockVideo = new MockVideo(); + expect(mockVideo.currentTime).toBe(100); + + execute(mockVideo); + + expect(eventType).toBe(VideoEvent.ENDED); + expect(pauseState).toBe("pause"); + expect(mockVideo.currentTime).toBe(100); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoEndedEventService.ts b/packages/media/src/Video/service/VideoEndedEventService.ts new file mode 100644 index 00000000..a3e4f7e9 --- /dev/null +++ b/packages/media/src/Video/service/VideoEndedEventService.ts @@ -0,0 +1,27 @@ +import type { Video } from "../../Video"; +import { VideoEvent } from "@next2d/events"; + +/** + * @description ビデオが最終フレームに達したときの処理 + * Processing when the video reaches the last frame + * + * @param {Video} video + * @return {void} + * @method + * @protected + */ +export const execute = (video: Video): void => +{ + if (video.willTrigger(VideoEvent.ENDED)) { + video.dispatchEvent(new VideoEvent(VideoEvent.ENDED)); + } + + if (video.loop) { + video.currentTime = 0; + return ; + } + + video.ended = true; + video.changed = false; + video.pause(); +}; \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoLoadedmetadataEventService.test.ts b/packages/media/src/Video/service/VideoLoadedmetadataEventService.test.ts new file mode 100644 index 00000000..aae12f13 --- /dev/null +++ b/packages/media/src/Video/service/VideoLoadedmetadataEventService.test.ts @@ -0,0 +1,43 @@ +import type { Video } from "../../Video"; +import { execute } from "./VideoLoadedmetadataEventService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoLoadedmetadataEventService.js test", () => +{ + it("execute test case1", () => + { + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "currentTime": 100, + "duration": 0 + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + expect(mockVideo.currentTime).toBe(100); + expect(mockVideo.duration).toBe(0); + + const MockHTMLVideoElement = vi.fn().mockImplementation(() => + { + return { + "duration": 100, + "videoWidth": 200, + "videoHeight": 300 + } as unknown as HTMLVideoElement; + }); + + const mockElement = new MockHTMLVideoElement(); + expect(mockElement.duration).toBe(100); + expect(mockElement.videoWidth).toBe(200); + expect(mockElement.videoHeight).toBe(300); + + execute(mockElement, mockVideo); + + // after + expect(mockVideo.currentTime).toBe(0); + expect(mockVideo.duration).toBe(100); + expect(mockVideo.videoWidth).toBe(200); + expect(mockVideo.videoHeight).toBe(300); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoLoadedmetadataEventService.ts b/packages/media/src/Video/service/VideoLoadedmetadataEventService.ts new file mode 100644 index 00000000..80dcc258 --- /dev/null +++ b/packages/media/src/Video/service/VideoLoadedmetadataEventService.ts @@ -0,0 +1,28 @@ +import type { Video } from "../../Video"; + +/** + * @description Videoオブジェクトの幅と高さを更新する + * Update the width and height of the Video object + * + * @param {HTMLVideoElement} element + * @param {Video} video + * @return {void} + * @method + * @protected + */ +export const execute = ( + element: HTMLVideoElement, + video: Video +): void => { + + // update metadata + video.currentTime = 0; + video.duration = element.duration; + video.videoWidth = element.videoWidth; + video.videoHeight = element.videoHeight; + + // reset + const offscreenCanvas = new OffscreenCanvas(video.videoWidth, video.videoHeight); + video.$context = offscreenCanvas.getContext("2d"); + video.$offscreenCanvas = offscreenCanvas; +}; \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoProgressEventService.test.ts b/packages/media/src/Video/service/VideoProgressEventService.test.ts new file mode 100644 index 00000000..07b2a9fe --- /dev/null +++ b/packages/media/src/Video/service/VideoProgressEventService.test.ts @@ -0,0 +1,44 @@ +import type { Video } from "../../Video"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; +import { execute } from "./VideoProgressEventService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoProgressEventService.js test", () => +{ + it("execute test case1", () => + { + let eventState = ""; + let loaded = 0; + let total = 0; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn((event: Next2DProgressEvent) => + { + eventState = event.type; + loaded = event.bytesLoaded; + total = event.bytesTotal; + }) + } as unknown as Video; + }); + + const MockProgressEvent = vi.fn().mockImplementation(() => + { + return { + "loaded": 10, + "total": 20 + } as unknown as ProgressEvent; + }); + + expect(eventState).toBe(""); + expect(loaded).toBe(0); + expect(total).toBe(0); + + execute(new MockVideo(), new MockProgressEvent()); + + expect(eventState).toBe(Next2DProgressEvent.PROGRESS); + expect(loaded).toBe(10); + expect(total).toBe(20); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/service/VideoProgressEventService.ts b/packages/media/src/Video/service/VideoProgressEventService.ts new file mode 100644 index 00000000..731e0aac --- /dev/null +++ b/packages/media/src/Video/service/VideoProgressEventService.ts @@ -0,0 +1,21 @@ +import type { Video } from "../../Video"; +import { ProgressEvent as Next2DProgressEvent } from "@next2d/events"; + +/** + * @description ビデオ読み込み中の進捗状態の確認イベント処理関数 + * Event processing function to check the progress status of video loading + * + * @param {Video} video + * @return {void} + * @method + * @protected + */ +export const execute = (video: Video, event: ProgressEvent): void => +{ + if (video.willTrigger(Next2DProgressEvent.PROGRESS)) { + video.dispatchEvent(new Next2DProgressEvent( + Next2DProgressEvent.PROGRESS, false, + event.loaded, event.total + )); + } +}; \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoBuildFromCharacterUseCase.ts b/packages/media/src/Video/usecase/VideoBuildFromCharacterUseCase.ts new file mode 100644 index 00000000..5f4753fc --- /dev/null +++ b/packages/media/src/Video/usecase/VideoBuildFromCharacterUseCase.ts @@ -0,0 +1,31 @@ +import type { Video } from "@next2d/media"; +import type { IVideoCharacter } from "../../interface/IVideoCharacter"; + +/** + * @description characterを元にTextFieldを構築 + * Build TextField based on character + * + * @param {Video} video + * @param {IVideoCharacter} character + * @return {void} + * @method + * @protected + */ +export const execute = (video: Video, character: IVideoCharacter): void => +{ + if (!character.videoData) { + character.videoData = new Uint8Array(character.buffer as number[]); + character.buffer = null; + } + + video.loop = character.loop; + video.autoPlay = character.autoPlay; + video.videoWidth = character.bounds.xMax; + video.videoHeight = character.bounds.yMax; + video.volume = character.volume; + + video.src = URL.createObjectURL(new Blob( + [character.videoData], + { "type": "video/mp4" } + )); +}; \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.test.ts b/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.test.ts new file mode 100644 index 00000000..be5b756f --- /dev/null +++ b/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.test.ts @@ -0,0 +1,64 @@ +import type { Video } from "../../Video"; +import { Event } from "@next2d/events"; +import { execute } from "./VideoCanplaythroughEventUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoCanplaythroughEventService.js test", () => +{ + it("execute test case1", async () => + { + let playState = "stop"; + let eventState = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "autoPlay": true, + "play": vi.fn(() => { playState = "play" }), + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn(() => { eventState = Event.COMPLETE }) + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + + // before + expect(playState).toBe("stop"); + expect(eventState).toBe(""); + + mockVideo.changed = false; + expect(mockVideo.changed).toBe(false); + + await execute(mockVideo); + + // after + expect(playState).toBe("play"); + expect(eventState).toBe(Event.COMPLETE); + expect(mockVideo.changed).toBe(true); + }); + + it("execute test case2", async () => + { + let eventState = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "autoPlay": false, + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn(() => { eventState = Event.COMPLETE }) + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + + // before + mockVideo.changed = false; + expect(eventState).toBe(""); + expect(mockVideo.changed).toBe(false); + + await execute(mockVideo); + + // after + expect(eventState).toBe(Event.COMPLETE); + expect(mockVideo.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.ts b/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.ts new file mode 100644 index 00000000..1c6332de --- /dev/null +++ b/packages/media/src/Video/usecase/VideoCanplaythroughEventUseCase.ts @@ -0,0 +1,34 @@ +import type { Video } from "../../Video"; +import { Event } from "@next2d/events"; +import { execute as videoApplyChangesService } from "../service/VideoApplyChangesService"; +import { + $isAudioContext, + $pushMutedVideos +} from "../../MediaUtil"; + +/** + * @description 再生可能処理 + * Playable processing + * + * @param {Video} video + * @return {Promise} + * @method + * @protected + */ +export const execute = async (video: Video): Promise => +{ + if (video.autoPlay) { + if (!$isAudioContext()) { + video.muted = true; + $pushMutedVideos(video); + } + await video.play(); + } + + video.loaded = true; + videoApplyChangesService(video); + + if (video.willTrigger(Event.COMPLETE)) { + video.dispatchEvent(new Event(Event.COMPLETE)); + } +}; \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoPlayEventUseCase.test.ts b/packages/media/src/Video/usecase/VideoPlayEventUseCase.test.ts new file mode 100644 index 00000000..97329ec6 --- /dev/null +++ b/packages/media/src/Video/usecase/VideoPlayEventUseCase.test.ts @@ -0,0 +1,42 @@ +import type { Video } from "../../Video"; +import { $getPlayingVideos } from "../../MediaUtil"; +import { VideoEvent } from "@next2d/events"; +import { execute } from "./VideoPlayEventUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoPlayEventService.js test", () => +{ + it("execute test case1", () => + { + let pauseState = ""; + let eventState = ""; + let state = ""; + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "pause": vi.fn(() => { pauseState = "pause" }), + "loaded": true, + "willTrigger": vi.fn(() => true), + "dispatchEvent": vi.fn((event: VideoEvent) => { eventState = event.type }), + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + expect(pauseState).toBe(""); + expect(eventState).toBe(""); + + mockVideo.changed = false; + expect(mockVideo.changed).toBe(false); + + const playingVideos = $getPlayingVideos(); + playingVideos.length = 0; + expect(playingVideos.length).toBe(0); + + cancelAnimationFrame(execute(mockVideo)); + + expect(playingVideos.length).toBe(1); + expect(mockVideo.changed).toBe(true); + expect(pauseState).toBe(""); + expect(eventState).toBe(VideoEvent.PLAY); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoPlayEventUseCase.ts b/packages/media/src/Video/usecase/VideoPlayEventUseCase.ts new file mode 100644 index 00000000..f05ee655 --- /dev/null +++ b/packages/media/src/Video/usecase/VideoPlayEventUseCase.ts @@ -0,0 +1,42 @@ +import type { Video } from "../../Video"; +import { $getPlayingVideos } from "../../MediaUtil"; +import { VideoEvent } from "@next2d/events"; +import { execute as videoApplyChangesService } from "../service/VideoApplyChangesService"; + +/** + * @description ビデオ再生中のイベント処理関数 + * Event processing function during video playback + * + * @param {Video} video + * @return {void} + * @method + * @protected + */ +export const execute = (video: Video): number => +{ + if (video.paused || !video.loaded) { + return 0; + } + + if (video.willTrigger(VideoEvent.PLAY)) { + video.dispatchEvent(new VideoEvent(VideoEvent.PLAY)); + } + + videoApplyChangesService(video); + + const playingVideos = $getPlayingVideos(); + if (playingVideos.indexOf(video) === -1) { + playingVideos.push(video); + } + + if (video.$context && video.$videoElement) { + video.$context.drawImage(video.$videoElement, + 0, 0, video.videoWidth, video.videoHeight + ); + } + + return requestAnimationFrame((): void => + { + execute(video); + }); +}; \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoRegisterEventUseCase.test.ts b/packages/media/src/Video/usecase/VideoRegisterEventUseCase.test.ts new file mode 100644 index 00000000..8a1b8761 --- /dev/null +++ b/packages/media/src/Video/usecase/VideoRegisterEventUseCase.test.ts @@ -0,0 +1,48 @@ +import { Video } from "../../Video"; +import { execute } from "./VideoRegisterEventUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("VideoRegisterEventUseCase.js test", () => +{ + it("execute test case1", () => + { + const types: string[] = []; + const MockHTMLVideoElement = vi.fn().mockImplementation(() => + { + return { + "duration": 100, + "videoWidth": 200, + "videoHeight": 300, + "addEventListener": vi.fn((type: string) => { + types.push(type); + }) + } as unknown as HTMLVideoElement; + }); + + const mockElement = new MockHTMLVideoElement(); + + const MockVideo = vi.fn().mockImplementation(() => + { + return { + "currentTime": 100, + "duration": 0 + } as unknown as Video; + }); + + const mockVideo = new MockVideo(); + + expect(types.length).toBe(0); + execute(mockElement, mockVideo, { + "xMin": 0, + "yMin": 0, + "xMax": 0, + "yMax": 0 + }); + + expect(types.length).toBe(4); + expect(types[0]).toBe("loadedmetadata"); + expect(types[1]).toBe("progress"); + expect(types[2]).toBe("canplaythrough"); + expect(types[3]).toBe("ended"); + }); +}); \ No newline at end of file diff --git a/packages/media/src/Video/usecase/VideoRegisterEventUseCase.ts b/packages/media/src/Video/usecase/VideoRegisterEventUseCase.ts new file mode 100644 index 00000000..86dfaefb --- /dev/null +++ b/packages/media/src/Video/usecase/VideoRegisterEventUseCase.ts @@ -0,0 +1,39 @@ +import type { Video } from "../../Video"; +import { execute as videoCanplaythroughEventUseCase } from "./VideoCanplaythroughEventUseCase"; +import { execute as videoLoadedmetadataEventService } from "../service/VideoLoadedmetadataEventService"; +import { execute as videoProgressEventService } from "../service/VideoProgressEventService"; +import { execute as videoEndedEventService } from "../service/VideoEndedEventService"; + +/** + * @description HTMLVideoElementにイベントを登録する + * Register events on HTMLVideoElement + * + * @param {HTMLVideoElement} element + * @param {Video} video + * @return {void} + * @method + * @protected + */ +export const execute = (element: HTMLVideoElement, video: Video): void => +{ + element.addEventListener("loadedmetadata", (): void => + { + videoLoadedmetadataEventService(element, video); + }, { "once": true }); + + element.addEventListener("progress", (event: ProgressEvent): void => + { + videoProgressEventService(video, event); + }); + + element.addEventListener("canplaythrough", async (): Promise => + { + await videoCanplaythroughEventUseCase(video); + element.volume = video.volume; + }, { "once": true }); + + element.addEventListener("ended", (): void => + { + videoEndedEventService(video); + }); +}; \ No newline at end of file diff --git a/packages/media/src/index.ts b/packages/media/src/index.ts index 5c86f6e4..52da8e52 100644 --- a/packages/media/src/index.ts +++ b/packages/media/src/index.ts @@ -1,4 +1,9 @@ export * from "./Sound"; export * from "./SoundMixer"; export * from "./SoundTransform"; -export * from "./Video"; \ No newline at end of file +export * from "./Video"; +export { + $bootAudioContext, + $getAudioContext, + $getMutedVideos +} from "./MediaUtil"; \ No newline at end of file diff --git a/packages/media/src/interface/IAjaxEvent.ts b/packages/media/src/interface/IAjaxEvent.ts new file mode 100644 index 00000000..c9a77edb --- /dev/null +++ b/packages/media/src/interface/IAjaxEvent.ts @@ -0,0 +1,5 @@ +export interface IAjaxEvent { + loadstart?: Function; + progress?: Function; + loadend?: Function; +} \ No newline at end of file diff --git a/packages/media/src/interface/IAjaxOption.ts b/packages/media/src/interface/IAjaxOption.ts new file mode 100644 index 00000000..3d25ea4b --- /dev/null +++ b/packages/media/src/interface/IAjaxOption.ts @@ -0,0 +1,14 @@ +import type { IURLRequestMethod } from "./IURLRequestMethod"; +import type { IURLLoaderDataFormat } from "./IURLLoaderDataFormat"; +import type { IAjaxEvent } from "./IAjaxEvent"; +import type { IURLRequestHeader } from "./IURLRequestHeader"; + +export interface IAjaxOption { + url: string; + format: IURLLoaderDataFormat; + method: IURLRequestMethod; + withCredentials: boolean; + headers: IURLRequestHeader[]; + data?: any; + event?: IAjaxEvent; +} \ No newline at end of file diff --git a/packages/media/src/interface/IBlendMode.ts b/packages/media/src/interface/IBlendMode.ts new file mode 100644 index 00000000..9480efe7 --- /dev/null +++ b/packages/media/src/interface/IBlendMode.ts @@ -0,0 +1,15 @@ +export type IBlendMode = "copy" + | "add" + | "alpha" + | "darken" + | "difference" + | "erase" + | "hardlight" + | "invert" + | "layer" + | "lighten" + | "multiply" + | "normal" + | "overlay" + | "screen" + | "subtract"; \ No newline at end of file diff --git a/packages/media/src/interface/IBounds.ts b/packages/media/src/interface/IBounds.ts new file mode 100644 index 00000000..b20c1ee3 --- /dev/null +++ b/packages/media/src/interface/IBounds.ts @@ -0,0 +1,6 @@ +export interface IBounds { + xMin: number; + xMax: number; + yMin: number; + yMax: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/ICharacter.ts b/packages/media/src/interface/ICharacter.ts new file mode 100644 index 00000000..1b583b65 --- /dev/null +++ b/packages/media/src/interface/ICharacter.ts @@ -0,0 +1,6 @@ +import type { IMovieClipCharacter } from "./IMovieClipCharacter"; +import type { IVideoCharacter } from "./IVideoCharacter"; +import type { IShapeCharacter } from "./IShapeCharacter"; +import type { ITextFieldCharacter } from "./ITextFieldCharacter"; + +export type ICharacter = IMovieClipCharacter | IVideoCharacter | IShapeCharacter | ITextFieldCharacter; \ No newline at end of file diff --git a/packages/media/src/interface/IDictionaryTag.ts b/packages/media/src/interface/IDictionaryTag.ts new file mode 100644 index 00000000..31e8ba03 --- /dev/null +++ b/packages/media/src/interface/IDictionaryTag.ts @@ -0,0 +1,8 @@ +export interface IDictionaryTag { + characterId: number; + name: string; + startFrame: number; + endFrame: number; + depth: number; + clipDepth: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/IFilterArray.ts b/packages/media/src/interface/IFilterArray.ts new file mode 100644 index 00000000..1cc8fd6c --- /dev/null +++ b/packages/media/src/interface/IFilterArray.ts @@ -0,0 +1,23 @@ +import type { + BlurFilter, + BevelFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; + +export type IFilterArray = Array< + BlurFilter + | BevelFilter + | ColorMatrixFilter + | ConvolutionFilter + | DisplacementMapFilter + | DropShadowFilter + | GlowFilter + | GradientBevelFilter + | GradientGlowFilter +>; \ No newline at end of file diff --git a/packages/media/src/interface/IGrid.ts b/packages/media/src/interface/IGrid.ts new file mode 100644 index 00000000..f95d98fd --- /dev/null +++ b/packages/media/src/interface/IGrid.ts @@ -0,0 +1,6 @@ +export interface IGrid { + x: number; + y: number; + w: number; + h: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/ILoopConfig.ts b/packages/media/src/interface/ILoopConfig.ts new file mode 100644 index 00000000..835d87a4 --- /dev/null +++ b/packages/media/src/interface/ILoopConfig.ts @@ -0,0 +1,9 @@ +import type { ILoopType } from "./ILoopType"; + +export interface ILoopConfig { + type: ILoopType; + frame: number; + start: number; + end: number; + tweenFrame?: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/ILoopType.ts b/packages/media/src/interface/ILoopType.ts new file mode 100644 index 00000000..7ee3e8ad --- /dev/null +++ b/packages/media/src/interface/ILoopType.ts @@ -0,0 +1,5 @@ +export type ILoopType = 0 // REPEAT + | 1 // NO_REPEAT + | 2 // FIXED + | 3 // NO_REPEAT_REVERSAL + | 4 ;// REPEAT_REVERSAL \ No newline at end of file diff --git a/packages/media/src/interface/IMovieClipActionObject.ts b/packages/media/src/interface/IMovieClipActionObject.ts new file mode 100644 index 00000000..a7960988 --- /dev/null +++ b/packages/media/src/interface/IMovieClipActionObject.ts @@ -0,0 +1,5 @@ +export interface IMovieClipActionObject { + frame: number; + action: string; + script?: Function; +} \ No newline at end of file diff --git a/packages/media/src/interface/IMovieClipCharacter.ts b/packages/media/src/interface/IMovieClipCharacter.ts new file mode 100644 index 00000000..305a9f20 --- /dev/null +++ b/packages/media/src/interface/IMovieClipCharacter.ts @@ -0,0 +1,18 @@ +import type { IMovieClipSoundObject } from "./IMovieClipSoundObject"; +import type { IMovieClipActionObject } from "./IMovieClipActionObject"; +import type { IMovieClipLabelObject } from "./IMovieClipLabelObject"; +import type { IPlaceObject } from "./IPlaceObject"; +import type { IDictionaryTag } from "./IDictionaryTag"; + +export interface IMovieClipCharacter { + symbol?: string; + extends: string; + totalFrame: number; + controller: Array>; + dictionary: IDictionaryTag[]; + placeMap: Array>; + placeObjects: IPlaceObject[]; + labels?: IMovieClipLabelObject[]; + actions?: IMovieClipActionObject[]; + sounds?: IMovieClipSoundObject[]; +} \ No newline at end of file diff --git a/packages/media/src/interface/IMovieClipLabelObject.ts b/packages/media/src/interface/IMovieClipLabelObject.ts new file mode 100644 index 00000000..da1d6877 --- /dev/null +++ b/packages/media/src/interface/IMovieClipLabelObject.ts @@ -0,0 +1,4 @@ +export interface IMovieClipLabelObject { + name: string; + frame: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/IMovieClipSoundObject.ts b/packages/media/src/interface/IMovieClipSoundObject.ts new file mode 100644 index 00000000..ab1b961e --- /dev/null +++ b/packages/media/src/interface/IMovieClipSoundObject.ts @@ -0,0 +1,6 @@ +import type { ISoundTag } from "./ISoundTag"; + +export interface IMovieClipSoundObject { + frame: number; + sound: ISoundTag[]; +} \ No newline at end of file diff --git a/packages/media/src/interface/IPlaceObject.ts b/packages/media/src/interface/IPlaceObject.ts new file mode 100644 index 00000000..2b0741a3 --- /dev/null +++ b/packages/media/src/interface/IPlaceObject.ts @@ -0,0 +1,15 @@ +import type { ILoopConfig } from "./ILoopConfig"; +import type { ISurfaceFilter } from "./ISurfaceFilter"; +import type { IFilterArray } from "./IFilterArray"; +import type { IBlendMode } from "./IBlendMode"; + +export interface IPlaceObject { + matrix?: number[]; + typedMatrix?: Float32Array; + colorTransform?: number[]; + typedColorTransform?: Float32Array; + blendMode?: IBlendMode; + surfaceFilterList?: ISurfaceFilter[]; + filters?: IFilterArray; + loop?: ILoopConfig; +} \ No newline at end of file diff --git a/packages/media/src/interface/IShapeCharacter.ts b/packages/media/src/interface/IShapeCharacter.ts new file mode 100644 index 00000000..9f52aca2 --- /dev/null +++ b/packages/media/src/interface/IShapeCharacter.ts @@ -0,0 +1,15 @@ +import type { IGrid } from "./IGrid"; +import type { IBounds } from "./IBounds"; + +export interface IShapeCharacter { + symbol?: string; + extends: string; + bounds: IBounds; + bitmapId: number; + inBitmap?: boolean; + grid?: IGrid | null; + recodes?: any[] | null; + buffer?: number[] | null; + imageBuffer?: Uint8Array; + recodeBuffer?: Float32Array; +} \ No newline at end of file diff --git a/packages/media/src/interface/ISoundCharacter.ts b/packages/media/src/interface/ISoundCharacter.ts new file mode 100644 index 00000000..b0363a15 --- /dev/null +++ b/packages/media/src/interface/ISoundCharacter.ts @@ -0,0 +1,4 @@ +export interface ISoundCharacter { + buffer: number[] | null; + audioBuffer: AudioBuffer | null; +} \ No newline at end of file diff --git a/packages/media/src/interface/ISoundTag.ts b/packages/media/src/interface/ISoundTag.ts new file mode 100644 index 00000000..51e955dd --- /dev/null +++ b/packages/media/src/interface/ISoundTag.ts @@ -0,0 +1,6 @@ +export interface ISoundTag { + characterId: number; + volume: number; + autoPlay: boolean; + loopCount: number; +} \ No newline at end of file diff --git a/packages/media/src/interface/ISurfaceFilter.ts b/packages/media/src/interface/ISurfaceFilter.ts new file mode 100644 index 00000000..c761a9d3 --- /dev/null +++ b/packages/media/src/interface/ISurfaceFilter.ts @@ -0,0 +1,14 @@ +type ClassName = "BevelFilter" + | "BlurFilter" + | "ColorMatrixFilter" + | "ConvolutionFilter" + | "DisplacementMapFilter" + | "DropShadowFilter" + | "GlowFilter" + | "GradientBevelFilter" + | "GradientGlowFilter"; + +export interface ISurfaceFilter { + class: ClassName; + params: any[]; +} \ No newline at end of file diff --git a/packages/media/src/interface/ITextFieldCharacter.ts b/packages/media/src/interface/ITextFieldCharacter.ts new file mode 100644 index 00000000..f3c9837e --- /dev/null +++ b/packages/media/src/interface/ITextFieldCharacter.ts @@ -0,0 +1,27 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; +import type { ITextFieldType } from "./ITextFieldType"; +import type { IBounds } from "./IBounds"; + +export interface ITextFieldCharacter { + symbol?: string; + extends: string; + font: string; + size: number; + align: ITextFormatAlign; + color: number; + leading: number; + letterSpacing: number; + leftMargin: number; + rightMargin: number; + fontType: number; + autoSize: number; + inputType: ITextFieldType; + multiline: boolean; + wordWrap: boolean; + border: boolean; + scroll: boolean; + thickness: number; + thicknessColor: number; + bounds: IBounds; + text: string; +} \ No newline at end of file diff --git a/packages/media/src/interface/ITextFieldType.ts b/packages/media/src/interface/ITextFieldType.ts new file mode 100644 index 00000000..3f92a04b --- /dev/null +++ b/packages/media/src/interface/ITextFieldType.ts @@ -0,0 +1 @@ +export type ITextFieldType = "input" | "static"; \ No newline at end of file diff --git a/packages/media/src/interface/ITextFormatAlign.ts b/packages/media/src/interface/ITextFormatAlign.ts new file mode 100644 index 00000000..aa48dea1 --- /dev/null +++ b/packages/media/src/interface/ITextFormatAlign.ts @@ -0,0 +1 @@ +export type ITextFormatAlign = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/media/src/interface/IURLLoaderDataFormat.ts b/packages/media/src/interface/IURLLoaderDataFormat.ts new file mode 100644 index 00000000..1b80009d --- /dev/null +++ b/packages/media/src/interface/IURLLoaderDataFormat.ts @@ -0,0 +1 @@ +export type IURLLoaderDataFormat = "json" | "arraybuffer" | "text"; \ No newline at end of file diff --git a/packages/media/src/interface/IURLRequestHeader.ts b/packages/media/src/interface/IURLRequestHeader.ts new file mode 100644 index 00000000..f7901811 --- /dev/null +++ b/packages/media/src/interface/IURLRequestHeader.ts @@ -0,0 +1,5 @@ +export interface IURLRequestHeader +{ + name: string; + value: string; +} \ No newline at end of file diff --git a/packages/media/src/interface/IURLRequestMethod.ts b/packages/media/src/interface/IURLRequestMethod.ts new file mode 100644 index 00000000..202e485b --- /dev/null +++ b/packages/media/src/interface/IURLRequestMethod.ts @@ -0,0 +1 @@ +export type IURLRequestMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT"; \ No newline at end of file diff --git a/packages/media/src/interface/IVideoCharacter.ts b/packages/media/src/interface/IVideoCharacter.ts new file mode 100644 index 00000000..da241a86 --- /dev/null +++ b/packages/media/src/interface/IVideoCharacter.ts @@ -0,0 +1,12 @@ +import type { IBounds } from "./IBounds"; + +export interface IVideoCharacter { + symbol?: string; + extends: string; + buffer: number[] | null; + videoData: Uint8Array; + volume: number; + loop: boolean; + autoPlay: boolean; + bounds: IBounds; +} \ No newline at end of file diff --git a/packages/net/package.json b/packages/net/package.json index f8f6c5eb..340d5c46 100644 --- a/packages/net/package.json +++ b/packages/net/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/net", "version": "*", - "description": "Next2D Net Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Net Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -30,11 +22,5 @@ "repository": { "type": "git", "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/interface": "file:../interface", - "@next2d/core": "file:../core", - "@next2d/util": "file:../util", - "@next2d/share": "file:../share" } } diff --git a/packages/net/src/URLRequest.test.ts b/packages/net/src/URLRequest.test.ts new file mode 100644 index 00000000..90324050 --- /dev/null +++ b/packages/net/src/URLRequest.test.ts @@ -0,0 +1,64 @@ +import { URLRequest } from "./URLRequest"; +import { describe, expect, it } from "vitest"; + +describe("URLRequest.js properties test", () => +{ + it("contentType case1", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.contentType).toBe("application/json"); + urlRequest.contentType = "text/html; charset=utf-8"; + expect(urlRequest.contentType).toBe("text/html; charset=utf-8"); + }); + + it("data test case1", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.data).toBe(null); + urlRequest.data = "data"; + expect(urlRequest.data).toBe("data"); + }); + + it("data test case2", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.data).toBe(null); + urlRequest.data = 0; + expect(urlRequest.data).toBe(0); + }); + + it("method test case1", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.method).toBe("GET"); + urlRequest.method = "POST"; + expect(urlRequest.method).toBe("POST"); + }); + + it("requestHeaders test case1", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.requestHeaders.length).toBe(0); + + urlRequest.requestHeaders = [{ + "name": "test", + "value": "aaa" + }]; + + expect(urlRequest.requestHeaders.length).toBe(1); + for (let i = 0; urlRequest.requestHeaders.length > i; i++) { + const requestHeader = urlRequest.requestHeaders[i]; + expect(requestHeader.name).toBe("test"); + expect(requestHeader.value).toBe("aaa"); + } + }); + + it("url test case1", () => + { + const urlRequest = new URLRequest(); + expect(urlRequest.url).toBe(""); + urlRequest.url = "http://test.com"; + expect(urlRequest.url).toBe("http://test.com"); + }); + +}); diff --git a/packages/net/src/URLRequest.ts b/packages/net/src/URLRequest.ts index 9f3980fb..b4f5db47 100644 --- a/packages/net/src/URLRequest.ts +++ b/packages/net/src/URLRequest.ts @@ -1,271 +1,100 @@ -import { URLRequestHeader } from "./URLRequestHeader"; -import type { - URLRequestMethodImpl, - URLLoaderDataFormatImpl -} from "@next2d/interface"; -import type { Player } from "@next2d/core"; -import { $currentPlayer } from "@next2d/util"; -import { $getArray } from "@next2d/share"; +import type { IURLRequestHeader } from "./interface/IURLRequestHeader"; +import type { IURLLoaderDataFormat } from "./interface/IURLLoaderDataFormat"; +import type { IURLRequestMethod } from "./interface/IURLRequestMethod"; /** - * URLRequestクラスは、外部へのリクエストを管理するクラスです - * The URLRequest class is a class that manages external requests + * @description URLRequestクラスは、外部へのリクエストを管理するクラスです + * The URLRequest class is a class that manages external requests * * @class * @memberOf next2d.net */ export class URLRequest { - private _$url: string; - private _$contentType: string; - private _$data: string; - private _$method: URLRequestMethodImpl; - private readonly _$requestHeaders: URLRequestHeader[]; - private _$responseDataFormat: URLLoaderDataFormatImpl; - private _$withCredentials: boolean; - - /** - * @param {string} [url=""] - * - * @constructor - * @public - */ - constructor (url: string = "") - { - /** - * @type {string} - * @default "" - * @private - */ - this._$url = `${url}`; - - /** - * @type {string} - * @default application/json - * @private - */ - this._$contentType = "application/json"; - - /** - * @type {string} - * @default "" - * @private - */ - this._$data = ""; - - /** - * @type {string} - * @default "GET" - * @private - */ - this._$method = "GET"; - - /** - * @type {array} - * @private - */ - this._$requestHeaders = $getArray(); - - /** - * @type {string} - * @default "json" - * @private - */ - this._$responseDataFormat = "json"; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$withCredentials = false; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class URLRequest] - * @method - * @static - */ - static toString (): string - { - return "[class URLRequest]"; - } - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.net.URLRequest - * @const - * @static - */ - static get namespace (): string - { - return "next2d.net.URLRequest"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description HTTP リクエストヘッダーの配列が HTTP リクエストに追加されます。 + * The array of HTTP request headers to be appended to the HTTP request. * - * @return {string} - * @default [object URLRequest] - * @method + * @type {array} * @public */ - toString (): string - { - return "[object URLRequest]"; - } + public requestHeaders: IURLRequestHeader[]; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description リクエストされる URL です。 + * The URL to be requested. * - * @return {string} - * @default next2d.net.URLRequest - * @const + * @type {string} + * @default "" * @public */ - get namespace (): string - { - return "next2d.net.URLRequest"; - } + public url: string; /** * @description data プロパティのコンテンツの MIME コンテンツタイプです。 * The MIME content type of the content in the the data property. * - * @member {string} + * @type {string} * @default application/json * @public */ - get contentType (): string - { - return this._$contentType; - } - set contentType (content_type: string) - { - this._$contentType = `${content_type}`; - } + public contentType: string; /** * @description URL リクエストで送信されるデータを含むオブジェクトです。 * An object containing data to be transmitted with the URL request. * - * @member {string} + * @type {any} + * @default null * @public */ - get data (): any - { - return this._$data; - } - set data (data: any) - { - this._$data = data; - } + public data: any; /** * @description HTTP フォーム送信メソッドを制御します。 * Controls the HTTP form submission method. * - * @member {string} + * @type {string} * @default "GET" * @public */ - get method (): URLRequestMethodImpl - { - return this._$method; - } - set method (method: URLRequestMethodImpl) - { - this._$method = `${method}`; - } - - /** - * @description HTTP リクエストヘッダーの配列が HTTP リクエストに追加されます。 - * The array of HTTP request headers to be appended to the HTTP request. - * - * @member {URLRequestHeader[]} - * @public - */ - get requestHeaders (): URLRequestHeader[] - { - return this._$requestHeaders; - } - set requestHeaders (request_headers: URLRequestHeader[]) - { - this._$requestHeaders.length = 0; - this._$requestHeaders.push(...request_headers); - } - - /** - * @description リクエストされる URL です。 - * The URL to be requested. - * - * @member {string} - * @public - */ - get url (): string - { - if (this._$url && this._$url.indexOf("//") === -1) { - - const urls: string [] = this._$url.split("/"); - if (urls[0] === "" || urls[0] === ".") { - urls.shift(); - } - - const player: Player = $currentPlayer(); - if (player) { - return `${player.base}${urls.join("/")}`; - } - } - - return this._$url; - } - set url (url: string) - { - this._$url = `${url}`; - } + public method: IURLRequestMethod; /** * @description レスポンスのデータフォーマットを指定します。 * Specifies the data format of the response. * - * @member {string} + * @type {string} * @default "json" * @public */ - get responseDataFormat (): URLLoaderDataFormatImpl - { - return this._$responseDataFormat; - } - set responseDataFormat (format: URLLoaderDataFormatImpl) - { - this._$responseDataFormat = `${format}`; - } + public responseDataFormat: IURLLoaderDataFormat; /** * @description HTTP 要求で使用されるユーザーエージェントストリングを指定します。 * Specifies the user-agent string to be used in the HTTP request. * - * @member {boolean} + * @type {boolean} * @default false - * @readonly * @public */ - get withCredentials (): boolean - { - return this._$withCredentials; - } - set withCredentials (with_credentials: boolean) + public withCredentials: boolean; + + /** + * @param {string} [url=""] + * + * @constructor + * @public + */ + constructor (url: string = "") { - this._$withCredentials = with_credentials; + this.url = `${url}`; + this.contentType = "application/json"; + this.data = null; + this.method = "GET"; + this.requestHeaders = []; + this.responseDataFormat = "json"; + this.withCredentials = false; } /** @@ -276,12 +105,15 @@ export class URLRequest * @readonly * @public */ - get headers (): URLRequestHeader[] - { - const headers = $getArray(); - headers.push(new URLRequestHeader("Content-Type", `${this._$contentType}`)); - if (this._$requestHeaders.length) { - headers.push(...this._$requestHeaders); + get headers (): IURLRequestHeader[] + { + const headers = []; + headers.push({ + "name": "Content-Type", + "value": this.contentType + }); + if (this.requestHeaders.length) { + headers.push(...this.requestHeaders); } return headers; } diff --git a/packages/net/src/URLRequestHeader.ts b/packages/net/src/URLRequestHeader.ts deleted file mode 100644 index 4fe7b24f..00000000 --- a/packages/net/src/URLRequestHeader.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * URLRequestHeader オブジェクトは 1 つの HTTP のリクエストヘッダーをカプセル化し、名前と値のペアを構成します。 - * URLRequestHeader オブジェクトは URLRequest クラスの requestHeaders プロパティで使用されます。 - * - * A URLRequestHeader object encapsulates a single HTTP request header and consists of a name/value pair. - * URLRequestHeader objects are used in the requestHeaders property of the URLRequest class. - * - * @class - * @memberOf next2d.net - */ -export class URLRequestHeader -{ - private readonly _$name: string; - private readonly _$value: string; - - /** - * @param {string} [name=""] - * @param {string} [value=""] - * - * @constructor - * @public - */ - constructor (name: string = "", value: string = "") - { - /** - * @type {string} - * @default "" - * @private - */ - this._$name = `${name}`; - - /** - * @type {string} - * @default "" - * @private - */ - this._$value = `${value}`; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class URLRequestHeader] - * @method - * @static - */ - static toString (): string - { - return "[class URLRequestHeader]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.net.URLRequestHeader - * @const - * @static - */ - static get namespace (): string - { - return "next2d.net.URLRequestHeader"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object URLRequestHeader] - * @method - * @public - */ - toString (): string - { - return "[object URLRequestHeader]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.net.URLRequestHeader - * @const - * @public - */ - get namespace (): string - { - return "next2d.net.URLRequestHeader"; - } - - /** - * @description HTTP リクエストヘッダー名(Content-Type や SOAPAction など)です。 - * An HTTP request header name (such as Content-Type or SOAPAction). - * - * @member {string} - * @default "" - * @readonly - * @public - */ - get name (): string - { - return this._$name; - } - - /** - * @description name プロパティに関連付けられた値(text/plain など)です。 - * The value associated with the name property (such as text/plain). - * - * @member {string} - * @default "" - * @readonly - * @public - */ - get value (): string - { - return this._$value; - } -} diff --git a/packages/net/src/index.ts b/packages/net/src/index.ts index 2d36afc6..eb9aa41f 100644 --- a/packages/net/src/index.ts +++ b/packages/net/src/index.ts @@ -1,2 +1 @@ -export * from "./URLRequest"; -export * from "./URLRequestHeader"; \ No newline at end of file +export * from "./URLRequest"; \ No newline at end of file diff --git a/packages/net/src/interface/IURLLoaderDataFormat.ts b/packages/net/src/interface/IURLLoaderDataFormat.ts new file mode 100644 index 00000000..1b80009d --- /dev/null +++ b/packages/net/src/interface/IURLLoaderDataFormat.ts @@ -0,0 +1 @@ +export type IURLLoaderDataFormat = "json" | "arraybuffer" | "text"; \ No newline at end of file diff --git a/packages/net/src/interface/IURLRequestHeader.ts b/packages/net/src/interface/IURLRequestHeader.ts new file mode 100644 index 00000000..f7901811 --- /dev/null +++ b/packages/net/src/interface/IURLRequestHeader.ts @@ -0,0 +1,5 @@ +export interface IURLRequestHeader +{ + name: string; + value: string; +} \ No newline at end of file diff --git a/packages/net/src/interface/IURLRequestMethod.ts b/packages/net/src/interface/IURLRequestMethod.ts new file mode 100644 index 00000000..202e485b --- /dev/null +++ b/packages/net/src/interface/IURLRequestMethod.ts @@ -0,0 +1 @@ +export type IURLRequestMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT"; \ No newline at end of file diff --git a/packages/interface/LICENSE b/packages/render-queue/LICENSE similarity index 100% rename from packages/interface/LICENSE rename to packages/render-queue/LICENSE diff --git a/packages/render-queue/README.md b/packages/render-queue/README.md new file mode 100644 index 00000000..17901ce5 --- /dev/null +++ b/packages/render-queue/README.md @@ -0,0 +1,11 @@ +@next2d/render-queue +============= + +## Installation + +``` +npm install @next2d/render-queue +``` + +## License +This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/render-queue/package.json b/packages/render-queue/package.json new file mode 100644 index 00000000..a5871b22 --- /dev/null +++ b/packages/render-queue/package.json @@ -0,0 +1,26 @@ +{ + "name": "@next2d/render-queue", + "version": "*", + "description": "Next2D Renderer Queue Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "license": "MIT", + "homepage": "https://next2d.app", + "bugs": "https://github.com/Next2D/Player/issues", + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.js" + } + }, + "keywords": [ + "Next2D", + "Next2D Renderer Queue" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Next2D/Player.git" + } +} diff --git a/packages/render-queue/src/RenderQueue.ts b/packages/render-queue/src/RenderQueue.ts new file mode 100644 index 00000000..d3c2a47f --- /dev/null +++ b/packages/render-queue/src/RenderQueue.ts @@ -0,0 +1,101 @@ +import { $upperPowerOfTwo } from "./RenderQueueUtil"; + +/** + * @description レンダーキューの管理クラス + * Management class of the render queue + * + * @class + * @public + */ +class RenderQueue +{ + /** + * @description バッファ + * Buffer + * + * @type {Float32Array} + * @public + */ + public buffer: Float32Array; + + /** + * @description オフセット + * Offset + * + * @type {number} + * @public + */ + public offset: number; + + /** + * @constructor + * @public + */ + constructor () + { + this.buffer = new Float32Array(256); + this.offset = 0; + } + + /** + * @description バッファにデータを追加 + * Add data to the buffer + * + * @param {...number} args + * @return {void} + * @method + * @public + */ + push (...args: number[]): void + { + if (this.buffer.length < this.offset + args.length) { + this.resize(args.length); + } + + for (let idx = 0; idx < args.length; idx++) { + this.buffer[this.offset++] = args[idx]; + } + } + + /** + * @description バッファをセット + * Set the buffer + * + * @param {Float32Array | Uint8Array} args + * @return {void} + * @method + * @public + */ + set (array: Float32Array | Uint8Array): void + { + if (this.buffer.length < this.offset + array.length) { + this.resize(array.length); + } + + this.buffer.set(array, this.offset); + this.offset += array.length; + } + + /** + * @description バッファをリサイズ + * Resize the buffer + * + * @param {number} length + * @return {void} + * @method + * @public + */ + resize (length: number): void + { + const newBuffer = new Float32Array( + $upperPowerOfTwo(this.offset + length) + ); + + if (this.buffer.length) { + newBuffer.set(this.buffer); + } + this.buffer = newBuffer; + } +} + +export const renderQueue = new RenderQueue(); \ No newline at end of file diff --git a/packages/render-queue/src/RenderQueueUtil.ts b/packages/render-queue/src/RenderQueueUtil.ts new file mode 100644 index 00000000..4d7f0f1a --- /dev/null +++ b/packages/render-queue/src/RenderQueueUtil.ts @@ -0,0 +1,20 @@ +/** + * @description 指定された値を2の累乗に切り上げます。 + * Rounds the specified value up to a power of two. + * + * @param {number} v + * @return {number} + * @method + * @protected + */ +export const $upperPowerOfTwo = (v: number): number => +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +}; \ No newline at end of file diff --git a/packages/render-queue/src/index.ts b/packages/render-queue/src/index.ts new file mode 100644 index 00000000..ccd008ea --- /dev/null +++ b/packages/render-queue/src/index.ts @@ -0,0 +1 @@ +export * from "./RenderQueue"; \ No newline at end of file diff --git a/packages/share/LICENSE b/packages/renderer/LICENSE similarity index 100% rename from packages/share/LICENSE rename to packages/renderer/LICENSE diff --git a/packages/renderer/README.md b/packages/renderer/README.md new file mode 100644 index 00000000..14d387bf --- /dev/null +++ b/packages/renderer/README.md @@ -0,0 +1,11 @@ +@next2d/renderer +============= + +## Installation + +``` +npm install @next2d/renderer +``` + +## License +This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/renderer/package.json b/packages/renderer/package.json new file mode 100644 index 00000000..ed0506d2 --- /dev/null +++ b/packages/renderer/package.json @@ -0,0 +1,31 @@ +{ + "name": "@next2d/renderer", + "version": "*", + "description": "Next2D Renderer Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "license": "MIT", + "homepage": "https://next2d.app", + "bugs": "https://github.com/Next2D/Player/issues", + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.js" + } + }, + "keywords": [ + "Next2D", + "Next2D Renderer" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Next2D/Player.git" + }, + "peerDependencies": { + "@next2d/webgl": "file:../webgl", + "@next2d/cache": "file:../cache", + "@next2d/texture-packer": "file:../texture-packer" + } +} diff --git a/packages/renderer/src/Command/service/CommandInitializeContextService.test.ts b/packages/renderer/src/Command/service/CommandInitializeContextService.test.ts new file mode 100644 index 00000000..b8f2b80c --- /dev/null +++ b/packages/renderer/src/Command/service/CommandInitializeContextService.test.ts @@ -0,0 +1,67 @@ +import { execute } from "./CommandInitializeContextService"; +import { describe, expect, it, vi } from "vitest"; + +describe("CommandInitializeContextService.js test", () => +{ + it("execute test case1", () => + { + const MockCanvas = vi.fn().mockImplementation(() => + { + return { + "getContext": vi.fn((contextId: string, options: any) => + { + expect(contextId).toBe("webgl2"); + expect(options.stencil).toBe(true); + expect(options.premultipliedAlpha).toBe(true); + expect(options.antialias).toBe(false); + expect(options.depth).toBe(false); + + return { + "clearColor": vi.fn(), + "getParameter": vi.fn(), + "createFramebuffer": vi.fn(), + "bindFramebuffer": vi.fn(), + "pixelStorei": vi.fn(), + "createRenderbuffer": vi.fn(), + "bindRenderbuffer": vi.fn(), + "renderbufferStorageMultisample": vi.fn(), + "createTexture": vi.fn(() => + { + return {}; + }), + "activeTexture": vi.fn(), + "bindTexture": vi.fn(), + "texParameteri": vi.fn(), + "texStorage2D": vi.fn(), + "createBuffer": vi.fn(), + "createVertexArray": vi.fn(), + "bindBuffer": vi.fn(), + "bufferData": vi.fn(), + "enableVertexAttribArray": vi.fn(), + "vertexAttribPointer": vi.fn(), + "vertexAttribDivisor": vi.fn(), + "createProgram": vi.fn(() => + { + return {}; + }), + "createShader": vi.fn(), + "shaderSource": vi.fn(), + "compileShader": vi.fn(), + "attachShader": vi.fn(), + "linkProgram": vi.fn(), + "detachShader": vi.fn(), + "deleteShader": vi.fn(), + "getProgramParameter": vi.fn(), + "enable": vi.fn(), + "blendFunc": vi.fn(), + "renderbufferStorage": vi.fn(), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + }; + }) + } as unknown as HTMLCanvasElement; + }); + + execute(new MockCanvas()); + }); +}); \ No newline at end of file diff --git a/packages/renderer/src/Command/service/CommandInitializeContextService.ts b/packages/renderer/src/Command/service/CommandInitializeContextService.ts new file mode 100644 index 00000000..985b49ba --- /dev/null +++ b/packages/renderer/src/Command/service/CommandInitializeContextService.ts @@ -0,0 +1,36 @@ +import { Context } from "@next2d/webgl"; +import { + $setCanvas, + $setContext, + $samples +} from "../../RendererUtil"; + +/** + * @description OffscreenCanvasからWebGL2のコンテキストを取得 + * Get WebGL2 context from OffscreenCanvas + * + * @param {OffscreenCanvas} canvas + * @param {number} device_pixel_ratio + * @return {void} + * @method + * @public + */ +export const execute = (canvas: OffscreenCanvas, device_pixel_ratio: number): void => +{ + // Set OffscreenCanvas + $setCanvas(canvas); + + const gl: WebGL2RenderingContext | null = canvas.getContext("webgl2", { + "stencil": true, + "premultipliedAlpha": true, + "antialias": false, + "depth": false + }); + + if (!gl) { + throw new Error("webgl2 is not supported."); + } + + // Set CanvasToWebGLContext + $setContext(new Context(gl, $samples, device_pixel_ratio)); +}; \ No newline at end of file diff --git a/packages/renderer/src/Command/service/CommandRemoveCacheService.ts b/packages/renderer/src/Command/service/CommandRemoveCacheService.ts new file mode 100644 index 00000000..71330581 --- /dev/null +++ b/packages/renderer/src/Command/service/CommandRemoveCacheService.ts @@ -0,0 +1,29 @@ +import { $cacheStore } from "@next2d/cache"; +import { $context } from "../../RendererUtil"; + +/** + * @description キャッシュキーを削除 + * Remove cache key + * + * @param {Float32Array} remove_cache_keys + * @return {void} + * @method + * @public + */ +export const execute = (remove_cache_keys: Float32Array): void => +{ + for (let idx = 0; idx < remove_cache_keys.length; ++idx) { + + const cacheKey = `${remove_cache_keys[idx]}`; + if (!$cacheStore.has(cacheKey)) { + continue; + } + + const cache = $cacheStore.getById(cacheKey); + for (const node of cache.values()) { + $context.removeNode(node); + } + + $cacheStore.removeById(cacheKey); + } +}; \ No newline at end of file diff --git a/packages/renderer/src/Command/service/CommandResizeService.test.ts b/packages/renderer/src/Command/service/CommandResizeService.test.ts new file mode 100644 index 00000000..1c5c189f --- /dev/null +++ b/packages/renderer/src/Command/service/CommandResizeService.test.ts @@ -0,0 +1,46 @@ +import { execute } from "./CommandResizeService"; +import { $cacheStore } from "@next2d/cache"; +import { + $getRendererWidth, + $getRendererHeight, +} from "../../RendererUtil"; +import { describe, expect, it, vi } from "vitest"; + +describe("CommandResizeService.js test", () => +{ + it("execute test", () => + { + vi.mock("../../RendererUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $canvas: { + "width": vi.fn((width) => + { + expect(width).toBe(500); + }), + "height": vi.fn((height) => + { + expect(height).toBe(600); + }) + }, + $context: { + "resize": vi.fn((width, height) => + { + expect(width).toBe(500); + expect(height).toBe(600); + }) + } + } + }); + const spyResetFunction = vi.spyOn($cacheStore, "reset"); + + expect($getRendererWidth()).toBe(0); + expect($getRendererHeight()).toBe(0); + execute(500, 600); + + + expect(spyResetFunction).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/packages/renderer/src/Command/service/CommandResizeService.ts b/packages/renderer/src/Command/service/CommandResizeService.ts new file mode 100644 index 00000000..06bc354e --- /dev/null +++ b/packages/renderer/src/Command/service/CommandResizeService.ts @@ -0,0 +1,42 @@ +import { $cacheStore } from "@next2d/cache"; +import { + $setRendererSize, + $context, + $getRendererWidth, + $getRendererHeight +} from "../../RendererUtil"; + +/** + * @description 画面リサイズ時にcanvasのリサイズ、内部情報の更新を行う + * Resize the canvas when resizing the screen and update internal information + * + * @param {number} renderer_width + * @param {number} renderer_height + * @param {boolean} cache_clear + * @return {void} + * @method + * @public + */ +export const execute = ( + renderer_width: number, + renderer_height: number, + cache_clear: boolean = true +): void => { + + if ($getRendererWidth() === renderer_width + && $getRendererHeight() === renderer_height + ) { + return ; + } + + // resize + $setRendererSize(renderer_width, renderer_height); + + // context reset and update + $context.resize(renderer_width, renderer_height, cache_clear); + + // cache clear + if (cache_clear) { + $cacheStore.reset(); + } +}; \ No newline at end of file diff --git a/packages/renderer/src/Command/usecase/CommandCaptureUseCase.ts b/packages/renderer/src/Command/usecase/CommandCaptureUseCase.ts new file mode 100644 index 00000000..af5b3cb9 --- /dev/null +++ b/packages/renderer/src/Command/usecase/CommandCaptureUseCase.ts @@ -0,0 +1,73 @@ +import { execute as displayObjectContainerRenderUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase"; +import { execute as shapeRenderUseCase } from "../../Shape/usecase/ShapeRenderUseCase"; +import { execute as textFieldRenderUseCase } from "../../TextField/usecase/TextFieldRenderUseCase"; +import { execute as videoRenderUseCase } from "../../Video/usecase/VideoRenderUseCase"; +import { $context } from "../../RendererUtil"; + +/** + * @description 描画コマンドから描画を実行してcanvasに描画して返却 + * Execute drawing from drawing command and draw to canvas and return + * + * @param {Float32Array} render_queue + * @param {number} width + * @param {number} height + * @param {ImageBitmap[]} [image_bitmaps=null] + * @return {Promise} + * @method + * @protected + */ +export const execute = async ( + render_queue: Float32Array, + width: number, + height: number, + image_bitmaps: ImageBitmap[] | null +): Promise => { + + // reset transfer bounds + $context.clearTransferBounds(); + + let index = 1; + + // reset + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + $context.fillBackgroundColor(); + + while (render_queue.length > index) { + + // hidden + if (!render_queue[index++]) { + continue; + } + + // display object type + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = displayObjectContainerRenderUseCase(render_queue, index, image_bitmaps); + break; + + case 0x01: // shape + index = shapeRenderUseCase(render_queue, index); + break; + + case 0x02: // text + index = textFieldRenderUseCase(render_queue, index); + break; + + case 0x03: // video + index = videoRenderUseCase(render_queue, index, image_bitmaps); + break; + + default: + console.error("unknown type", type); + break; + + } + } + + $context.drawArraysInstanced(); + + return await $context.createImageBitmap(width, height); +}; \ No newline at end of file diff --git a/packages/renderer/src/Command/usecase/CommandRenderUseCase.ts b/packages/renderer/src/Command/usecase/CommandRenderUseCase.ts new file mode 100644 index 00000000..cdea2fe9 --- /dev/null +++ b/packages/renderer/src/Command/usecase/CommandRenderUseCase.ts @@ -0,0 +1,99 @@ +import { execute as displayObjectContainerRenderUseCase } from "../../DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase"; +import { execute as shapeRenderUseCase } from "../../Shape/usecase/ShapeRenderUseCase"; +import { execute as textFieldRenderUseCase } from "../../TextField/usecase/TextFieldRenderUseCase"; +import { execute as videoRenderUseCase } from "../../Video/usecase/VideoRenderUseCase"; +import { + $context, + $isResize, + $resizeComplete +} from "../../RendererUtil"; + +/** + * @type {number} + * @private + */ +let $color: number = -1; + +/** + * @description 描画コマンドから描画を実行 + * Execute drawing from drawing command + * + * @param {Float32Array} render_queue + * @param {ImageBitmap[]} [image_bitmaps=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + render_queue: Float32Array, + image_bitmaps: ImageBitmap[] | null +): void => { + + // reset transfer bounds + $context.clearTransferBounds(); + + let index = 0; + + // update background color + const color = render_queue[index++]; + if ($color !== color) { + $color = color; + if ($color === -1) { + $context.updateBackgroundColor(0, 0, 0, 0); + } else { + $context.updateBackgroundColor( + $color >> 16 & 0xff / 255, + $color >> 8 & 0xff / 255, + $color & 0xff / 255, + 1 + ); + } + } + + // reset + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + $context.fillBackgroundColor(); + + while (render_queue.length > index) { + + // hidden + if (!render_queue[index++]) { + continue; + } + + // display object type + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = displayObjectContainerRenderUseCase(render_queue, index, image_bitmaps); + break; + + case 0x01: // shape + index = shapeRenderUseCase(render_queue, index); + break; + + case 0x02: // text + index = textFieldRenderUseCase(render_queue, index); + break; + + case 0x03: // video + index = videoRenderUseCase(render_queue, index, image_bitmaps); + break; + + default: + console.error("unknown type", type); + break; + + } + } + + $context.drawArraysInstanced(); + + if ($isResize()) { + $resizeComplete(); + } + + $context.transferMainCanvas(); +}; \ No newline at end of file diff --git a/packages/renderer/src/CommandController.ts b/packages/renderer/src/CommandController.ts new file mode 100644 index 00000000..831a2592 --- /dev/null +++ b/packages/renderer/src/CommandController.ts @@ -0,0 +1,128 @@ +import type { IMessage } from "./interface/IMessage"; +import { execute as commandInitializeContextService } from "./Command/service/CommandInitializeContextService"; +import { execute as commandResizeService } from "./Command/service/CommandResizeService"; +import { execute as commandRenderUseCase } from "./Command/usecase/CommandRenderUseCase"; +import { execute as commandRemoveCacheService } from "./Command/service/CommandRemoveCacheService"; +import { execute as commandCaptureUseCase } from "./Command/usecase/CommandCaptureUseCase"; +import { $cacheStore } from "@next2d/cache"; + +/** + * @class + */ +export class CommandController +{ + /** + * @description workerの実行状態 + * Execution status of worker + * + * @type {string} + * @default "deactivate" + * @public + */ + public state: string; + + /** + * @description 受け取ったメッセージ配列 + * Received message array + * + * @type {array} + * @default [] + * @public + */ + public queue: IMessage[]; + + /** + * @constructor + * @public + */ + constructor () + { + this.state = "deactivate"; + this.queue = []; + } + + /** + * @description 処理を実行 + * Execute process + * + * @return {Promise} + * @method + * @public + */ + async execute (): Promise + { + this.state = "active"; + while (this.queue.length) { + + const object: IMessage | void = this.queue.shift(); + if (!object) { + continue; + } + + switch (object.command) { + + case "render": + commandRenderUseCase( + object.buffer.subarray(0, object.length), + object.imageBitmaps as ImageBitmap[] | null + ); + + // 描画完了したらメインスレッドにbufferを返却する + globalThis.postMessage({ + "message": "render", + "buffer": object.buffer + // @ts-ignore + }, [object.buffer.buffer]); + break; + + case "resize": + commandResizeService( + object.buffer[0] as number, + object.buffer[1] as number, + Boolean(object.buffer[2]) + ); + break; + + case "initialize": + commandInitializeContextService( + object.canvas as OffscreenCanvas, + object.devicePixelRatio as number + ); + break; + + case "removeCache": + commandRemoveCacheService(object.buffer); + break; + + case "cacheClear": + $cacheStore.reset(); + break; + + case "capture": + { + const imageBitmap = await commandCaptureUseCase( + object.buffer.subarray(0, object.length), + object.width as number, + object.height as number, + object.imageBitmaps as ImageBitmap[] | null + ); + + // 描画完了したらメインスレッドにbufferを返却する + globalThis.postMessage({ + "message": "capture", + "buffer": object.buffer, + "imageBitmap": imageBitmap + // @ts-ignore + }, [object.buffer.buffer, imageBitmap]); + } + break; + + default: + break; + + } + } + + this.state = "deactivate"; + } +} \ No newline at end of file diff --git a/packages/renderer/src/DisplayObject/service/DisplayObjectGetBlendModeService.ts b/packages/renderer/src/DisplayObject/service/DisplayObjectGetBlendModeService.ts new file mode 100644 index 00000000..9c135db9 --- /dev/null +++ b/packages/renderer/src/DisplayObject/service/DisplayObjectGetBlendModeService.ts @@ -0,0 +1,65 @@ +import type { IBlendMode } from "../../interface/IBlendMode"; + +/** + * @description ブレンドモードを取得します。 + * Get the blend mode. + * + * @param {number} blend_number + * @return {IBlendMode} + * @method + * @protected + */ +export const execute = (blend_number: number): IBlendMode => +{ + switch (blend_number) { + + case 0: + return "copy"; + + case 1: + return "add"; + + case 2: + return "alpha"; + + case 3: + return "darken"; + + case 4: + return "difference"; + + case 5: + return "erase"; + + case 6: + return "hardlight"; + + case 7: + return "invert"; + + case 8: + return "layer"; + + case 9: + return "lighten"; + + case 10: + return "multiply"; + + case 11: + return "normal"; + + case 12: + return "overlay"; + + case 13: + return "screen"; + + case 14: + return "subtract"; + + default: + return "normal"; // normal + + } +}; \ No newline at end of file diff --git a/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerClipRenderUseCase.ts b/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerClipRenderUseCase.ts new file mode 100644 index 00000000..82693bd5 --- /dev/null +++ b/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerClipRenderUseCase.ts @@ -0,0 +1,37 @@ +import { execute as shapeClipRenderUseCase } from "../../Shape/usecase/ShapeClipRenderUseCase"; + +/** + * @description DisplayObjectContainerのマスク描画を実行します。 + * Execute the mask drawing of DisplayObjectContainer. + * + * @param {Float32Array} render_queue + * @param {number} index + * @return {number} + * @method + * @protected + */ +export const execute = (render_queue: Float32Array, index: number): number => +{ + const length = render_queue[index++]; + for (let idx = 0; idx < length; ++idx) { + + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = execute(render_queue, index); + break; + + case 0x01: // shape + index = shapeClipRenderUseCase(render_queue, index); + break; + + // text, videoはマスク対象外 + default: + break; + + } + } + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase.ts b/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase.ts new file mode 100644 index 00000000..42391a3b --- /dev/null +++ b/packages/renderer/src/DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase.ts @@ -0,0 +1,173 @@ +import { $context } from "../../RendererUtil"; +import { execute as shapeRenderUseCase } from "../../Shape/usecase/ShapeRenderUseCase"; +import { execute as shapeClipRenderUseCase } from "../../Shape/usecase/ShapeClipRenderUseCase"; +import { execute as textFieldRenderUseCase } from "../../TextField/usecase/TextFieldRenderUseCase"; +import { execute as videoRenderUseCase } from "../../Video/usecase/VideoRenderUseCase"; +import { execute as displayObjectContainerClipRenderUseCase } from "./DisplayObjectContainerClipRenderUseCase"; + +/** + * @description DisplayObjectContainerの描画を実行します。 + * Execute the drawing of DisplayObjectContainer. + * + * @param {Float32Array} render_queue + * @param {number} index + * @param {ImageBitmap[]} [image_bitmaps=null] + * @return {number} + * @method + * @protected + */ +export const execute = ( + render_queue: Float32Array, + index: number, + image_bitmaps: ImageBitmap[] | null +): number => { + + const useMaskDisplayObject = Boolean(render_queue[index++]); + if (useMaskDisplayObject) { + + // これまでの描画データを描画して初期化 + $context.drawArraysInstanced(); + + // 設定値を保存 + $context.save(); + + // マスク描画の開始準備 + $context.beginMask(); + + // マスクの範囲を設定 + $context.setMaskBounds( + render_queue[index++], + render_queue[index++], + render_queue[index++], + render_queue[index++] + ); + + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = displayObjectContainerClipRenderUseCase(render_queue, index); + break; + + case 0x01: // shape + index = shapeClipRenderUseCase(render_queue, index); + break; + + default: + break; + + } + $context.endMask(); + } + + let endClipDepth = 0; + let canRenderMask = true; + + const length = render_queue[index++]; + for (let idx = 0; length > idx; idx++) { + + const depth = render_queue[index++]; + const clipDepth = render_queue[index++]; + + // end mask + if (endClipDepth && depth > endClipDepth) { + if (canRenderMask) { + $context.restore(); + $context.leaveMask(); + } + + // reset + endClipDepth = 0; + canRenderMask = true; + } + + if (!canRenderMask) { + continue; + } + + // start mask + if (clipDepth) { + + endClipDepth = clipDepth; + canRenderMask = Boolean(render_queue[index++]); + if (!canRenderMask) { + continue; + } + + // これまでの描画データを描画して初期化 + $context.drawArraysInstanced(); + + // 設定値を保存 + $context.save(); + + // マスク描画の開始準備 + $context.beginMask(); + + // マスクの範囲を設定 + $context.setMaskBounds( + render_queue[index++], + render_queue[index++], + render_queue[index++], + render_queue[index++] + ); + + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = displayObjectContainerClipRenderUseCase(render_queue, index); + break; + + case 0x01: // shape + index = shapeClipRenderUseCase(render_queue, index); + break; + + // text, videoはマスク対象外 + default: + break; + + } + + $context.endMask(); + continue; + } + + // hidden + if (!render_queue[index++]) { + continue; + } + + const type = render_queue[index++]; + switch (type) { + + case 0x00: // container + index = execute(render_queue, index, image_bitmaps); + break; + + case 0x01: // shape + index = shapeRenderUseCase(render_queue, index); + break; + + case 0x02: // text + index = textFieldRenderUseCase(render_queue, index); + break; + + case 0x03: // video + index = videoRenderUseCase(render_queue, index, image_bitmaps); + break; + + default: + console.error("unknown type", type); + break; + + } + } + + // end mask + if (endClipDepth || useMaskDisplayObject) { + $context.restore(); + $context.leaveMask(); + } + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/RendererUtil.ts b/packages/renderer/src/RendererUtil.ts new file mode 100644 index 00000000..46702bda --- /dev/null +++ b/packages/renderer/src/RendererUtil.ts @@ -0,0 +1,253 @@ +import type { Context } from "@next2d/webgl"; +import type { IRGBA } from "./interface/IRGBA"; + +/** + * @type {number} + * @public + */ +export const $samples: number = 4; + +/** + * @type {Context} + * @public + */ +export let $context: Context; + +/** + * @description Next2DのWebGLの描画コンテキストを設定 + * Set the drawing context of Next2D's WebGL + * + * @param {number} context + * @return {void} + * @method + * @public + */ +export const $setContext = (context: Context): void => +{ + $context = context; +}; + +/** + * @type {CanvasToWebGLContext} + * @public + */ +export let $canvas: OffscreenCanvas; + +/** + * @description OffscreenCanvasをutilの変数のセット + * Set OffscreenCanvas to util variable + * + * @param {OffscreenCanvas} canvas + * @return {void} + * @method + * @public + */ +export const $setCanvas = (canvas: OffscreenCanvas): void => +{ + $canvas = canvas; +}; + +/** + * @type {number} + * @public + */ +let $rendererWidth: number = 0; + +/** + * @description 描画エリアの幅を返却 + * Returns the width of the drawing area + * + * @return {number} + * @method + * @protected + */ +export const $getRendererWidth = (): number => +{ + return $rendererWidth; +}; + +/** + * @type {number} + * @public + */ +let $rendererHeight: number = 0; + +/** + * @description 描画エリアの高さを返却 + * Returns the height of the drawing area + * + * @return {number} + * @method + * @protected + */ +export const $getRendererHeight = (): number => +{ + return $rendererHeight; +}; + +/** + * @type {boolean} + * @private + */ +let $resizeState: boolean = false; + +/** + * @description 描画エリアの幅を設定 + * Set the width of the drawing area + * + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @public + */ +export const $setRendererSize = (width: number, height: number): void => +{ + $resizeState = true; + $rendererWidth = width; + $rendererHeight = height; +}; + +/** + * @description リサイズ中かどうかを取得 + * Get whether it is being resized + * + * @return {boolean} + * @method + * @public + */ +export const $isResize = (): boolean => +{ + return $resizeState; +}; + +/** + * @description リサイズ処理完了 + * Resize processing complete + * + * @return {void} + * @method + * @public + */ +export const $resizeComplete = (): void => +{ + $canvas.width = $rendererWidth; + $canvas.height = $rendererHeight; + $resizeState = false; +}; + +/** + * @description 使用済みになったBoundsのArrayのプール配列 + * Pool array of used Bounds Array. + * + * @type {Float32Array[]} + * @const + * @private + */ +const $boundsArrays: Float32Array[] = []; + +/** + * @description プールされたArrayがあればプールから、なければ新規作成して返却 + * If there is a pooled Array, return it from the pool, + * otherwise create a new one and return it. + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @return {number[]} + * @method + * @protected + */ +export const $getBoundsArray = ( + x_min: number, y_min: number, + x_max: number, y_max: number +): Float32Array => { + + const array = $boundsArrays.length + ? $boundsArrays.pop() as unknown as Float32Array + : new Float32Array(4); + + array[0] = x_min; + array[1] = y_min; + array[2] = x_max; + array[3] = y_max; + + return array; +}; + +/** + * @description 使用済みになったBoundsのArrayをプール + * Pool used Bounds Array. + * + * @param {Float32Array} array + * @method + * @protected + */ +export const $poolBoundsArray = (array: Float32Array): void => +{ + $boundsArrays.push(array); +}; + +/** + * @description 使用済みになったArrayのプール配列 + * Pool array of used Array. + * + * @type {array[]} + * @const + * @private + */ +const $arrays: Array = []; + +/** + * @description プールされたArrayがあればプールから、なければ新規作成して返却 + * If there is a pooled Array, return it from the pool, + * otherwise create a new one and return it. + * + * @param {array} args + * @return {array} + * @method + * @protected + */ +export const $getArray = (...args: any[]): any[] => +{ + const array: any[] = $arrays.pop() || []; + if (args.length) { + array.push(...args); + } + return array; +}; + +/** + * @description 使用済みになったArrayをプール + * Pool used Array. + * + * @param {array} array + * @return {void} + * @method + * @protected + */ +export const $poolArray = (array: any[]): void => +{ + if (array.length) { + array.length = 0; + } + $arrays.push(array); +}; + +/** + * @param {number} color + * @param {number} [alpha=1] + * @returns {IRGBA} + * @method + * @static + */ +export const $intToRGBA = (color: number, alpha: number = 1): IRGBA => +{ + return { + "R": (color & 0xff0000) >> 16, + "G": (color & 0x00ff00) >> 8, + "B": color & 0x0000ff, + "A": alpha * 255 + }; +}; \ No newline at end of file diff --git a/packages/renderer/src/Shape/service/ShapeCommandService.ts b/packages/renderer/src/Shape/service/ShapeCommandService.ts new file mode 100644 index 00000000..33cfc138 --- /dev/null +++ b/packages/renderer/src/Shape/service/ShapeCommandService.ts @@ -0,0 +1,349 @@ +import { + $context, + $getArray +} from "../../RendererUtil"; + +/** + * @type {number} + * @private + */ +const MOVE_TO: number = 0; + +/** + * @type {number} + * @private + */ +const CURVE_TO: number = 1; + +/** + * @type {number} + * @private + */ +const LINE_TO: number = 2; + +/** + * @type {number} + * @private + */ +const CUBIC: number = 3; + +/** + * @type {number} + * @private + */ +const ARC: number = 4; + +/** + * @type {number} + * @private + */ +const FILL_STYLE: number = 5; + +/** + * @type {number} + * @private + */ +const STROKE_STYLE: number = 6; + +/** + * @type {number} + * @private + */ +const END_FILL: number = 7; + +/** + * @type {number} + * @private + */ +const END_STROKE: number = 8; + +/** + * @type {number} + * @private + */ +const BEGIN_PATH: number = 9; + +/** + * @type {number} + * @private + */ +const GRADIENT_FILL: number = 10; + +/** + * @type {number} + * @private + */ +const GRADIENT_STROKE: number = 11; + +/** + * @type {number} + * @private + */ +const CLOSE_PATH: number = 12; + +/** + * @type {number} + * @private + */ +const BITMAP_FILL: number = 13; + +/** + * @type {number} + * @private + */ +const BITMAP_STROKE: number = 14; + +/** + * @description Shapeのグラフィックコマンドを実行します。 + * Execute the graphic commands of Shape. + * + * @param {Float32Array} commands + * @param {boolean} [is_clip=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + commands: Float32Array, + is_clip: boolean = false +): void => { + + let index = 0; + while (commands.length > index) { + + switch (commands[index++]) { + + case BEGIN_PATH: + $context.beginPath(); + break; + + case MOVE_TO: + $context.moveTo(commands[index++], commands[index++]); + break; + + case LINE_TO: + $context.lineTo(commands[index++], commands[index++]); + break; + + case CURVE_TO: + $context.quadraticCurveTo( + commands[index++], commands[index++], + commands[index++], commands[index++] + ); + break; + + case FILL_STYLE: + if (is_clip) { + index += 4; + break; + } + + $context.fillStyle( + commands[index++] / 255, commands[index++] / 255, + commands[index++] / 255, commands[index++] / 255 + ); + break; + + case END_FILL: + $context.fill(); + break; + + case STROKE_STYLE: + if (is_clip) { + index += 8; + break; + } + + $context.thickness = commands[index++]; + $context.caps = commands[index++]; + $context.joints = commands[index++]; + $context.miterLimit = commands[index++]; + $context.strokeStyle( + commands[index++] / 255, commands[index++] / 255, + commands[index++] / 255, commands[index++] / 255 + ); + + break; + + case END_STROKE: + if (is_clip) { + break; + } + + $context.stroke(); + break; + + case CLOSE_PATH: + $context.closePath(); + break; + + case ARC: + $context.arc(commands[index++], commands[index++], commands[index++]); + break; + + case CUBIC: + $context.bezierCurveTo( + commands[index++], commands[index++], + commands[index++], commands[index++], + commands[index++], commands[index++] + ); + break; + + case GRADIENT_FILL: + { + if (is_clip) { + index += 1; + const length = commands[index++]; + index += length * 5; + index += 9; + $context.fill(); + break; + } + + const type = commands[index++]; + + const stops = $getArray(); + const length = commands[index++]; + for (let idx = 0; idx < length; ++idx) { + stops.push( + commands[index++], // ratio + commands[index++], // red + commands[index++], // green + commands[index++], // blue + commands[index++] // alpha + ); + } + + const matrix = new Float32Array([ + commands[index++], commands[index++], commands[index++], + commands[index++], commands[index++], commands[index++] + ]); + + const spread = commands[index++]; + const interpolation = commands[index++]; + const focal = commands[index++]; + + $context.gradientFill( + type, stops, matrix, + spread, interpolation, focal + ); + } + break; + + case BITMAP_FILL: + { + if (is_clip) { + index += 2; + const length = commands[index++]; + index += length; + index += 8; + $context.fill(); + break; + } + + const width = commands[index++]; + const height = commands[index++]; + + const length = commands[index++]; + const buffer = new Uint8Array( + commands.subarray(index, index + length) + ); + index += length; + + const matrix = new Float32Array([ + commands[index++], commands[index++], commands[index++], + commands[index++], commands[index++], commands[index++] + ]); + + $context.bitmapFill( + buffer, matrix, width, height, + Boolean(commands[index++]), + Boolean(commands[index++]) + ); + } + break; + + case GRADIENT_STROKE: + { + if (is_clip) { + index += 20; + $context.fill(); + break; + } + + $context.thickness = commands[index++]; + $context.caps = commands[index++]; + $context.joints = commands[index++]; + $context.miterLimit = commands[index++]; + + const type = commands[index++]; + + const stops = $getArray(); + const length = commands[index++]; + for (let idx = 0; idx < length; ++idx) { + stops.push( + commands[index++], // ratio + commands[index++], // red + commands[index++], // green + commands[index++], // blue + commands[index++] // alpha + ); + } + + const matrix = new Float32Array([ + commands[index++], commands[index++], commands[index++], + commands[index++], commands[index++], commands[index++] + ]); + + const spread = commands[index++]; + const interpolation = commands[index++]; + const focal = commands[index++]; + + $context.gradientStroke( + type, stops, matrix, + spread, interpolation, focal + ); + } + break; + + case BITMAP_STROKE: + { + if (is_clip) { + index += 6; + const length = commands[index++]; + index += length; + index += 8; + $context.fill(); + break; + } + + $context.thickness = commands[index++]; + $context.caps = commands[index++]; + $context.joints = commands[index++]; + $context.miterLimit = commands[index++]; + + const width = commands[index++]; + const height = commands[index++]; + + const length = commands[index++]; + const buffer = new Uint8Array( + commands.subarray(index, index + length) + ); + index += length; + + const matrix = new Float32Array([ + commands[index++], commands[index++], commands[index++], + commands[index++], commands[index++], commands[index++] + ]); + + $context.bitmapStroke( + buffer, matrix, width, height, + Boolean(commands[index++]), + Boolean(commands[index++]) + ); + } + break; + + } + } +}; \ No newline at end of file diff --git a/packages/renderer/src/Shape/usecase/ShapeClipRenderUseCase.ts b/packages/renderer/src/Shape/usecase/ShapeClipRenderUseCase.ts new file mode 100644 index 00000000..5fbbbf86 --- /dev/null +++ b/packages/renderer/src/Shape/usecase/ShapeClipRenderUseCase.ts @@ -0,0 +1,46 @@ +import { execute as shapeCommandService } from "../service/ShapeCommandService"; +import { $context } from "../../RendererUtil"; + +/** + * @description シェイプクリップの描画を実行 + * Execute drawing of shape clip + * + * @param {Float32Array} render_queue + * @param {number} index + * @return {number} + * @method + * @protected + */ +export const execute = (render_queue: Float32Array, index: number): number => +{ + const matrix = render_queue.subarray(index, index + 6); + index += 6; + + $context.reset(); + $context.setTransform( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); + + const isGridEnabled = Boolean(render_queue[index++]); + const gridData = isGridEnabled + ? new Float32Array(28) + : null; + + $context.useGrid(gridData); + + if (gridData) { + gridData.set(render_queue.subarray(index, index + 24)); + index += 24; + } + + const length = render_queue[index++]; + const commands = render_queue.subarray(index, index + length); + shapeCommandService(commands, true); + + index += length; + + $context.clip(); + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/Shape/usecase/ShapeRenderUseCase.ts b/packages/renderer/src/Shape/usecase/ShapeRenderUseCase.ts new file mode 100644 index 00000000..7664c2a3 --- /dev/null +++ b/packages/renderer/src/Shape/usecase/ShapeRenderUseCase.ts @@ -0,0 +1,240 @@ +import type { Node } from "@next2d/texture-packer"; +import { $cacheStore } from "@next2d/cache"; +import { execute as shapeCommandService } from "../service/ShapeCommandService"; +import { execute as displayObjectGetBlendModeService } from "../../DisplayObject/service/DisplayObjectGetBlendModeService"; +import { $context } from "../../RendererUtil"; + +/** + * @description Shapeの描画を実行します。 + * Execute the drawing of Shape. + * + * @param {Float32Array} render_queue + * @param {number} index + * @return {number} + * @method + * @protected + */ +export const execute = (render_queue: Float32Array, index: number): number => +{ + const matrix = render_queue.subarray(index, index + 6); + index += 6; + + const colorTransform = render_queue.subarray(index, index + 8); + index += 8; + + const bounds = render_queue.subarray(index, index + 4); + index += 4; + + // baseBounds + const xMin = render_queue[index++]; + const yMin = render_queue[index++]; + const xMax = render_queue[index++]; + const yMax = render_queue[index++]; + + const isGridEnabled = Boolean(render_queue[index++]); + const isDrawable = Boolean(render_queue[index++]); + const isBitmap = Boolean(render_queue[index++]); + + // cache uniqueKey + const uniqueKey = `${render_queue[index++]}`; + const cacheKey = render_queue[index++]; + + const xScale = Math.round(Math.sqrt( + matrix[0] * matrix[0] + + matrix[1] * matrix[1] + ) * 10000) / 10000; + + const yScale = Math.round(Math.sqrt( + matrix[2] * matrix[2] + + matrix[3] * matrix[3] + ) * 10000) / 10000; + + let node: Node; + const hasCache = render_queue[index++]; + if (!hasCache) { + + const gridData = isGridEnabled + ? new Float32Array(28) + : null; + + if (gridData) { + gridData.set(render_queue.subarray(index, index + 24)); + index += 24; + } + + $context.useGrid(gridData); + + const length = render_queue[index++]; + const commands = render_queue.subarray(index, index + length); + + if (isBitmap && !isGridEnabled) { + + // Bitmapなので、スケールなし + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + + // fixed logic + node = $context.createNode(width, height); + $cacheStore.set(uniqueKey, `${cacheKey}`, node); + + // fixed logic + const currentAttachment = $context.currentAttachmentObject; + $context.bind($context.atlasAttachmentObject); + + $context.reset(); + $context.beginNodeRendering(node); + + const offsetY = $context.atlasAttachmentObject.height - node.y - height; + $context.setTransform(1, 0, 0, 1, + node.x, + offsetY + ); + + if (isDrawable) { + shapeCommandService(commands); + $context.drawFill(); + } else { + $context.drawPixels(node, new Uint8Array(commands)); + } + + $context.endNodeRendering(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + + } else { + + const width = Math.ceil(Math.abs(xMax - xMin) * xScale); + const height = Math.ceil(Math.abs(yMax - yMin) * yScale); + + // fixed logic + node = $context.createNode(width, height); + $cacheStore.set(uniqueKey, `${cacheKey}`, node); + + // fixed logic + const currentAttachment = $context.currentAttachmentObject; + $context.bind($context.atlasAttachmentObject); + + // 初期化して、描画範囲を初期化 + $context.reset(); + $context.beginNodeRendering(node); + + // matrix設定 + const offsetY = $context.atlasAttachmentObject.height - node.y - height; + $context.setTransform( + xScale, 0, 0, yScale, + -xMin * xScale + node.x, + -yMin * yScale + offsetY + ); + + if (gridData) { + gridData[24] = node.x; + gridData[25] = offsetY; + } + + // 描画コマンドを実行 + shapeCommandService(commands); + + // 描画実行 + $context.drawFill(); + + // 描画終了 + $context.endNodeRendering(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + } + + index += length; + + } else { + node = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node; + if (!node) { + return index; + } + } + + const blendMode = render_queue[index++]; + + // フィルター設定があればフィルターを実行 + const useFilfer = Boolean(render_queue[index++]); + if (useFilfer) { + const updated = Boolean(render_queue[index++]); + const filterBounds = render_queue.subarray(index, index + 4); + index += 4; + + const length = render_queue[index++]; + const params = render_queue.subarray(index, index + length); + + const width = Math.ceil(Math.abs(bounds[2] - bounds[0])); + const height = Math.ceil(Math.abs(bounds[3] - bounds[1])); + + $context.applyFilter( + node, uniqueKey, updated, + width, height, isBitmap, + matrix, colorTransform, displayObjectGetBlendModeService(blendMode), + filterBounds, params + ); + + index += length; + + return index; + } + + $context.globalAlpha = Math.min(Math.max(0, colorTransform[3] + colorTransform[7] / 255), 1); + $context.imageSmoothingEnabled = true; + $context.globalCompositeOperation = displayObjectGetBlendModeService(blendMode); + + if (isBitmap && !isGridEnabled) { + $context.setTransform( + matrix[0], matrix[1], + matrix[2], matrix[3], + matrix[4], matrix[5] + ); + + $context.drawDisplayObject( + node, + bounds[0], bounds[1], bounds[2], bounds[3], + colorTransform + ); + } else { + + const radianX = Math.atan2(matrix[1], matrix[0]); + const radianY = Math.atan2(-matrix[2], matrix[3]); + if (radianX || radianY) { + + const tx = xMin * xScale; + const ty = yMin * yScale; + + const cosX = Math.cos(radianX); + const sinX = Math.sin(radianX); + const cosY = Math.cos(radianY); + const sinY = Math.sin(radianY); + + $context.setTransform( + cosX, sinX, -sinY, cosY, + tx * cosX - ty * sinY + matrix[4], + tx * sinX + ty * cosY + matrix[5] + ); + + } else { + + $context.setTransform(1, 0, 0, 1, + bounds[0], bounds[1] + ); + + } + + // 描画範囲をinstanced arrayに設定 + $context.drawDisplayObject( + node, + bounds[0], bounds[1], bounds[2], bounds[3], + colorTransform + ); + + } + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/TextField/service/TextFieldGenerateFontStyleService.ts b/packages/renderer/src/TextField/service/TextFieldGenerateFontStyleService.ts new file mode 100644 index 00000000..aef2c96e --- /dev/null +++ b/packages/renderer/src/TextField/service/TextFieldGenerateFontStyleService.ts @@ -0,0 +1,25 @@ +import type { ITextFormat } from "../../interface/ITextFormat"; + +/** + * @description テキストフォーマットを元にフォントスタイルを生成します。 + * Generate font style based on text format. + * + * @param {ITextFormat} text_format + * @return {string} + * @method + * @protected + */ +export const execute = (text_format: ITextFormat): string => +{ + let fontStyle = ""; + + if (text_format.italic) { + fontStyle += "italic "; + } + + if (text_format.bold) { + fontStyle += "bold "; + } + + return `${fontStyle}${text_format.size}px '${text_format.font}','sans-serif'`; +}; \ No newline at end of file diff --git a/packages/renderer/src/TextField/service/TextFiledGetAlignOffsetService.ts b/packages/renderer/src/TextField/service/TextFiledGetAlignOffsetService.ts new file mode 100644 index 00000000..045fc2aa --- /dev/null +++ b/packages/renderer/src/TextField/service/TextFiledGetAlignOffsetService.ts @@ -0,0 +1,46 @@ +import type { ITextData } from "../../interface/ITextData"; +import type { ITextObject } from "../../interface/ITextObject"; +import type { ITextSetting } from "../../interface/ITextSetting"; + +/** + * @description テキストの揃え位置のオフセット値を取得します。 + * Get the offset value of the alignment position of the text. + * + * @param {ITextData} text_data + * @param {ITextObject} text_object + * @param {ITextSetting} text_setting + * @return {number} + * @method + * @protected + */ +export const execute = ( + text_data: ITextData, + text_object: ITextObject, + text_setting: ITextSetting +): number => { + + const lineWidth = text_data.widthTable[text_object.line] || 0; + + const textFormat = text_object.textFormat; + const leftMargin = textFormat.leftMargin || 0; + if (!text_setting.wordWrap && lineWidth > text_setting.rawWidth) { + return Math.max(0, leftMargin); + } + + const rightMargin = textFormat.rightMargin || 0; + if (textFormat.align === "center" // format CENTER + || text_setting.autoSize === "center" // autoSize CENTER + ) { + return Math.max(0, text_setting.rawWidth / 2 - leftMargin - rightMargin - lineWidth / 2 - 2); + } + + if (textFormat.align === "right" // format RIGHT + || text_setting.autoSize === "right" // autoSize RIGHT + ) { + return Math.max(0, text_setting.rawWidth - leftMargin - lineWidth - rightMargin - 4); + } + + // autoSize LEFT + // format LEFT + return Math.max(0, leftMargin); +}; \ No newline at end of file diff --git a/packages/renderer/src/TextField/usecase/TextFieldDrawOffscreenCanvasUseCase.ts b/packages/renderer/src/TextField/usecase/TextFieldDrawOffscreenCanvasUseCase.ts new file mode 100644 index 00000000..7c75cf04 --- /dev/null +++ b/packages/renderer/src/TextField/usecase/TextFieldDrawOffscreenCanvasUseCase.ts @@ -0,0 +1,364 @@ +import type { ITextData } from "../../interface/ITextData"; +import type { ITextSetting } from "../../interface/ITextSetting"; +import { execute as textFiledGetAlignOffsetService } from "../service/TextFiledGetAlignOffsetService"; +import { execute as textFieldGenerateFontStyleService } from "../service/TextFieldGenerateFontStyleService"; +import { $intToRGBA } from "../../RendererUtil"; + +/** + * @description TextDataを元にOffscreenCanvasにテキストを描画 + * Draw text in OffscreenCanvas based on TextData + * + * @param {ITextData} text_data + * @param {object} text_setting + * @param {number} x_scale + * @param {number} y_scale + * @return {OffscreenCanvas} + * @method + * @protected + */ +export const execute = ( + text_data: ITextData | null, + text_setting: ITextSetting, + x_scale: number, + y_scale: number +): OffscreenCanvas => { + + const canvas = new OffscreenCanvas( + text_setting.width, text_setting.height + ); + + const context = canvas.getContext("2d"); + if (!context) { + return canvas; + } + + const lineWidth = Math.min(1, Math.max(x_scale, y_scale)); + + // border and background + if (text_setting.background || text_setting.border) { + + context.beginPath(); + context.moveTo(0, 0); + context.lineTo(text_setting.width, 0); + context.lineTo(text_setting.width, text_setting.height); + context.lineTo(0, text_setting.height); + context.lineTo(0, 0); + + if (text_setting.background) { + + const color = $intToRGBA(text_setting.backgroundColor); + + context.fillStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + context.fill(); + } + + if (text_setting.border) { + + const color = $intToRGBA(text_setting.borderColor); + + context.lineWidth = lineWidth; + context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + context.stroke(); + } + + } + + if (!text_data) { + return canvas; + } + + context.save(); + context.beginPath(); + context.moveTo(2, 2); + context.lineTo(text_setting.width - 2, 2); + context.lineTo(text_setting.width - 2, text_setting.height - 2); + context.lineTo(2, text_setting.height - 2); + context.lineTo(2, 2); + context.clip(); + + let tx = 2; + if (text_setting.scrollX > 0) { + const scaleX = (text_setting.textWidth + 4 - text_setting.rawWidth) / text_setting.rawWidth; + tx += -text_setting.scrollX * scaleX; + } + + let ty = 2; + if (text_setting.scrollY > 0) { + const scaleY = (text_setting.textHeight + 2 - text_setting.rawHeight) / text_setting.rawHeight; + ty += -text_setting.scrollY * scaleY; + } + + context.setTransform(x_scale, 0, 0, y_scale, tx * x_scale, ty * y_scale); + context.beginPath(); + + if (text_setting.selectIndex > -1 + && text_setting.focusIndex > -1 + ) { + const range = text_data.textTable.length - 1; + + let minIndex = 0; + let maxIndex = 0; + if (text_setting.focusIndex <= text_setting.selectIndex) { + minIndex = Math.min(text_setting.focusIndex, range); + maxIndex = Math.min(text_setting.selectIndex, range); + } else { + minIndex = Math.min(text_setting.selectIndex, range); + maxIndex = Math.min(text_setting.focusIndex - 1, range); + } + + const textObject = text_data.textTable[minIndex]; + const lineObject = text_data.lineTable[textObject.line]; + const offsetAlign = textFiledGetAlignOffsetService( + text_data, lineObject, text_setting + ); + + let x = 0; + if (minIndex && textObject.mode === "text") { + let idx = minIndex; + while (idx) { + const textObject = text_data.textTable[--idx]; + if (textObject.mode !== "text") { + break; + } + x += textObject.w; + } + } + + context.fillStyle = "#b4d7ff"; + + let w = 0; + for (let idx = minIndex; idx <= maxIndex; ++idx) { + + const textObject = text_data.textTable[idx]; + if (textObject.mode === "text") { + + w += textObject.w; + + if (idx !== maxIndex) { + continue; + } + } + + let y = 0; + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + for (let idx = 0; idx < line; ++idx) { + y += text_data.heightTable[idx]; + } + + context.beginPath(); + context.rect( + x, y, + w + offsetAlign, + text_data.heightTable[line] + ); + context.fill(); + + x = 0; + w = 0; + } + } + + const rawWidth = text_setting.rawWidth; + let scrollX = 0; + if (text_setting.scrollX > 0) { + const scaleX = (text_setting.textWidth - rawWidth) / rawWidth; + scrollX = text_setting.scrollX * scaleX; + } + const limitWidth = rawWidth + scrollX; + + const rawHeight = text_setting.rawHeight; + let scrollY = 0; + if (text_setting.scrollY > 0) { + const scaleY = (text_setting.textHeight - rawHeight) / rawHeight; + scrollY = text_setting.scrollY * scaleY; + } + const limitHeight = rawHeight + scrollY; + + // setup + let offsetWidth = 0; + let offsetHeight = 0; + let offsetAlign = 0; + let verticalAlign = 0; + + let skip = false; + let currentIndex = -1; + for (let idx = 0; idx < text_data.textTable.length; ++idx) { + + const textObject = text_data.textTable[idx]; + if (!textObject) { + continue; + } + + if (textObject.mode === "text" || textObject.mode === "break") { + currentIndex++; + if (text_setting.stopIndex > -1 + && currentIndex > text_setting.stopIndex + ) { + break; + } + } + + if (skip && textObject.mode === "text") { + continue; + } + + const textFormat = textObject.textFormat; + + // check + if (text_setting.autoSize === "none") { + + if (offsetHeight > limitHeight) { + break; + } + + if (textObject.mode === "text") { + if (scrollX > offsetWidth + textObject.w + || offsetWidth > limitWidth + ) { + offsetWidth += textObject.w; + continue; + } + } + + } + + // color setting + const color = $intToRGBA(textFormat.color || 0); + context.fillStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + + // focus line + if (text_setting.focusVisible + && text_setting.focusIndex === idx + ) { + + const x = offsetWidth + offsetAlign + 0.1; + let line = textObject.line; + + let h = textObject.y; + let y = text_data.ascentTable[line]; + if (textObject.mode !== "text") { + + h = textObject.mode === "break" + ? textObject.h + : text_data.ascentTable[line - 1]; + + if (line > 0) { + line = textObject.line - 1; + y = text_data.ascentTable[line]; + } else { + y = textObject.h; + } + } + + if (line > 0 && text_data.ascentTable[line] === 0) { + line++; + } + for (let idx = 0; idx < line; ++idx) { + y += text_data.heightTable[idx]; + } + + context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + context.beginPath(); + context.moveTo(x, y); + context.lineTo(x, y - h); + context.stroke(); + } + + if (text_setting.thickness) { + const color = $intToRGBA(text_setting.thicknessColor); + context.lineWidth = text_setting.thickness; + context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + } + + const line = textObject.line | 0; + switch (textObject.mode) { + + case "break": + case "wrap": + // reset width + offsetWidth = 0; + if (line) { + offsetHeight += text_data.heightTable[line - 1]; + } + + if (scrollY > offsetHeight + text_data.heightTable[line]) { + skip = true; + continue; + } + + verticalAlign = text_data.ascentTable[line]; + offsetAlign = textFiledGetAlignOffsetService( + text_data, textObject, text_setting + ); + + skip = false; + break; + + case "text": + { + context.beginPath(); + context.font = textFieldGenerateFontStyleService(textFormat); + + const x = offsetWidth + offsetAlign; + const y = offsetHeight + verticalAlign; + if (textFormat.underline) { + + const color = $intToRGBA(textFormat.color || 0); + context.lineWidth = lineWidth; + context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + + context.beginPath(); + context.moveTo(x, y + 2); + context.lineTo(x + textObject.w, y + 2); + context.stroke(); + + } + + if (text_setting.thickness) { + context.strokeText(textObject.text, x, y); + } + + context.fillText(textObject.text, x, y); + + offsetWidth += textObject.w; + } + break; + + case "image": + break; + + default: + break; + + } + } + + if (text_setting.focusVisible + && text_setting.focusIndex >= text_data.textTable.length + ) { + const textObject = text_data.textTable[text_setting.focusIndex - 1]; + if (textObject) { + const color = $intToRGBA(textObject.textFormat.color || 0); + context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${color.A})`; + + const x = offsetWidth + offsetAlign + 0.1; + const y = offsetHeight + verticalAlign; + + context.beginPath(); + if (textObject.mode === "text") { + context.moveTo(x, y - textObject.y); + } else { + context.moveTo(x, y + textObject.h); + } + context.lineTo(x, y); + context.stroke(); + } + } + + context.restore(); + + return canvas; +}; \ No newline at end of file diff --git a/packages/renderer/src/TextField/usecase/TextFieldRenderUseCase.ts b/packages/renderer/src/TextField/usecase/TextFieldRenderUseCase.ts new file mode 100644 index 00000000..3859518b --- /dev/null +++ b/packages/renderer/src/TextField/usecase/TextFieldRenderUseCase.ts @@ -0,0 +1,225 @@ +import type { Node } from "@next2d/texture-packer"; +import type { ITextFieldAutoSize } from "../../interface/ITextFieldAutoSize"; +import type { ITextSetting } from "../../interface/ITextSetting"; +import { $cacheStore } from "@next2d/cache"; +import { execute as displayObjectGetBlendModeService } from "../../DisplayObject/service/DisplayObjectGetBlendModeService"; +import { execute as textFieldDrawOffscreenCanvasUseCase } from "./TextFieldDrawOffscreenCanvasUseCase"; +import { $context } from "../../RendererUtil"; + +/** + * @type {TextDecoder} + * @private + */ +const $textDecoder: TextDecoder = new TextDecoder(); + +/** + * @description TextFieldの描画を実行します。 + * Execute the drawing of TextField. + * + * @param {Float32Array} render_queue + * @param {number} index + * @return {number} + * @method + * @protected + */ +export const execute = (render_queue: Float32Array, index: number): number => +{ + const matrix = render_queue.subarray(index, index + 6); + index += 6; + + const colorTransform = render_queue.subarray(index, index + 8); + index += 8; + + const bounds = render_queue.subarray(index, index + 4); + index += 4; + + // baseBounds + const xMin = render_queue[index++]; + const yMin = render_queue[index++]; + const xMax = render_queue[index++]; + const yMax = render_queue[index++]; + + // cache uniqueKey + const uniqueKey = `${render_queue[index++]}`; + const cacheKey = render_queue[index++]; + + // text state + const changed = Boolean(render_queue[index++]); + + const xScale = Math.round(Math.sqrt( + matrix[0] * matrix[0] + + matrix[1] * matrix[1] + ) * 10000) / 10000; + + const yScale = Math.round(Math.sqrt( + matrix[2] * matrix[2] + + matrix[3] * matrix[3] + ) * 10000) / 10000; + + let node: Node; + const hasCache = render_queue[index++]; + if (!hasCache) { + + const width = Math.ceil(Math.abs(xMax - xMin) * xScale); + const height = Math.ceil(Math.abs(yMax - yMin) * yScale); + + const hasNode = Boolean(render_queue[index++]); + + node = hasNode + ? $cacheStore.get(uniqueKey, `${cacheKey}`) as Node + : $context.createNode(width, height); + + if (!hasNode) { + $cacheStore.set(uniqueKey, `${cacheKey}`, node); + } + + const length = render_queue[index++]; + const buffer = new Uint8Array(render_queue.subarray(index, index + length)); + index += length; + + let autoSize: ITextFieldAutoSize = "none"; + switch (render_queue[index++]) { + + case 0: + autoSize = "center"; + break; + + case 1: + autoSize = "left"; + break; + + case 2: + autoSize = "none"; + break; + + case 3: + autoSize = "right"; + break; + + } + + const textSetting: ITextSetting = { + "width": width, + "height": height, + "autoSize": autoSize, + "stopIndex": render_queue[index++], + "scrollX": render_queue[index++], + "scrollY": render_queue[index++], + "textWidth": render_queue[index++], + "textHeight": render_queue[index++], + "rawWidth": render_queue[index++], + "rawHeight": render_queue[index++], + "focusIndex": render_queue[index++], + "selectIndex": render_queue[index++], + "focusVisible": Boolean(render_queue[index++]), + "thickness": render_queue[index++], + "thicknessColor": render_queue[index++], + "wordWrap": Boolean(render_queue[index++]), + "border": Boolean(render_queue[index++]), + "borderColor": render_queue[index++], + "background": Boolean(render_queue[index++]), + "backgroundColor": render_queue[index++], + "defaultColor": render_queue[index++], + "defaultSize": render_queue[index++] + }; + + const canvas = textFieldDrawOffscreenCanvasUseCase( + JSON.parse($textDecoder.decode(buffer)), + textSetting, + xScale, yScale + ); + + // fixed logic + const currentAttachment = $context.currentAttachmentObject; + $context.bind($context.atlasAttachmentObject); + + $context.reset(); + $context.beginNodeRendering(node); + + const offsetY = $context.atlasAttachmentObject.height - node.y - height; + $context.setTransform(1, 0, 0, 1, + node.x, + offsetY + ); + + $context.drawElement(node, canvas); + + $context.endNodeRendering(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + + } else { + node = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node; + if (!node) { + return index; + } + } + + const blendMode = render_queue[index++]; + + // フィルター設定があればフィルターを実行 + const useFilfer = Boolean(render_queue[index++]); + if (useFilfer) { + const updated = Boolean(render_queue[index++]); + const filterBounds = render_queue.subarray(index, index + 4); + index += 4; + + const length = render_queue[index++]; + const params = render_queue.subarray(index, index + length); + + const width = Math.ceil(Math.abs(bounds[2] - bounds[0])); + const height = Math.ceil(Math.abs(bounds[3] - bounds[1])); + + $context.applyFilter( + node, uniqueKey, Boolean(Math.max(+changed, +updated)), + width, height, false, + matrix, colorTransform, displayObjectGetBlendModeService(blendMode), + filterBounds, params + ); + + index += length; + + return index; + } + + $context.globalAlpha = Math.min(Math.max(0, colorTransform[3] + colorTransform[7] / 255), 1); + $context.imageSmoothingEnabled = true; + $context.globalCompositeOperation = displayObjectGetBlendModeService(blendMode); + + const radianX = Math.atan2(matrix[1], matrix[0]); + const radianY = Math.atan2(-matrix[2], matrix[3]); + if (radianX || radianY) { + + const tx = xMin * xScale; + const ty = yMin * yScale; + + const cosX = Math.cos(radianX); + const sinX = Math.sin(radianX); + const cosY = Math.cos(radianY); + const sinY = Math.sin(radianY); + + $context.setTransform( + cosX, sinX, -sinY, cosY, + tx * cosX - ty * sinY + matrix[4], + tx * sinX + ty * cosY + matrix[5] + ); + + } else { + + $context.setTransform(1, 0, 0, 1, + bounds[0], bounds[1] + ); + + } + + // 描画範囲をinstanced arrayに設定 + $context.drawDisplayObject( + node, + bounds[0], bounds[1], bounds[2], bounds[3], + colorTransform + ); + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/Video/usecase/VideoRenderUseCase.ts b/packages/renderer/src/Video/usecase/VideoRenderUseCase.ts new file mode 100644 index 00000000..aee1eaf1 --- /dev/null +++ b/packages/renderer/src/Video/usecase/VideoRenderUseCase.ts @@ -0,0 +1,138 @@ +import type { Node } from "@next2d/texture-packer"; +import { $cacheStore } from "@next2d/cache"; +import { $context } from "../../RendererUtil"; +import { execute as displayObjectGetBlendModeService } from "../../DisplayObject/service/DisplayObjectGetBlendModeService"; + +/** + * @description Videoの描画を実行します。 + * Execute the drawing of Video. + * + * @param {Float32Array} render_queue + * @param {number} index + * @param {ImageBitmap[]} image_bitmaps + * @return {number} + * @method + * @protected + */ +export const execute = ( + render_queue: Float32Array, + index: number, + image_bitmaps: ImageBitmap[] | null +): number => { + + const matrix = render_queue.subarray(index, index + 6); + index += 6; + + const colorTransform = render_queue.subarray(index, index + 8); + index += 8; + + const bounds = render_queue.subarray(index, index + 4); + index += 4; + + // baseBounds + const xMin = render_queue[index++]; + const yMin = render_queue[index++]; + const xMax = render_queue[index++]; + const yMax = render_queue[index++]; + + // cache uniqueKey + const uniqueKey = `${render_queue[index++]}`; + const cacheKey = "0"; + + // video state + const changed = Boolean(render_queue[index++]); + + let node: Node; + const hasCache = render_queue[index++]; + if (!hasCache) { + + const width = Math.abs(xMax - xMin); + const height = Math.abs(yMax - yMin); + + const hasNode = Boolean(render_queue[index++]); + + node = hasNode + ? $cacheStore.get(uniqueKey, `${cacheKey}`) as Node + : $context.createNode(width, height); + + if (!hasNode) { + $cacheStore.set(uniqueKey, `${cacheKey}`, node); + } + + if (image_bitmaps && image_bitmaps.length) { + + // fixed logic + const currentAttachment = $context.currentAttachmentObject; + $context.bind($context.atlasAttachmentObject); + + $context.reset(); + $context.beginNodeRendering(node); + + const offsetY = $context.atlasAttachmentObject.height - node.y - height; + $context.setTransform(1, 0, 0, 1, + node.x, + offsetY + ); + + const imageBitmap = image_bitmaps.shift() as ImageBitmap; + $context.drawElement(node, imageBitmap); + + $context.endNodeRendering(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + } + } else { + node = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node; + if (!node) { + return index; + } + } + + const blendMode = render_queue[index++]; + + // フィルター設定があればフィルターを実行 + const useFilfer = Boolean(render_queue[index++]); + if (useFilfer) { + const updated = Boolean(render_queue[index++]); + const filterBounds = render_queue.subarray(index, index + 4); + index += 4; + + const length = render_queue[index++]; + const params = render_queue.subarray(index, index + length); + + const width = Math.ceil(Math.abs(bounds[2] - bounds[0])); + const height = Math.ceil(Math.abs(bounds[3] - bounds[1])); + + $context.applyFilter( + node, uniqueKey, Boolean(Math.max(+changed, +updated)), + width, height, true, + matrix, colorTransform, displayObjectGetBlendModeService(blendMode), + filterBounds, params + ); + + index += length; + + return index; + } + + $context.globalAlpha = Math.min(Math.max(0, colorTransform[3] + colorTransform[7] / 255), 1); + $context.imageSmoothingEnabled = true; + $context.globalCompositeOperation = displayObjectGetBlendModeService(blendMode); + + $context.setTransform( + matrix[0], matrix[1], + matrix[2], matrix[3], + matrix[4], matrix[5] + ); + + // 描画範囲をinstanced arrayに設定 + $context.drawDisplayObject( + node, + bounds[0], bounds[1], bounds[2], bounds[3], + colorTransform + ); + + return index; +}; \ No newline at end of file diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts new file mode 100644 index 00000000..3981049c --- /dev/null +++ b/packages/renderer/src/index.ts @@ -0,0 +1,31 @@ +"use strict"; + +import { CommandController } from "./CommandController"; + +/** + * @description CommandControllerのインスタンス + * Instance of CommandController + * + * @type {CommandController} + * @public + */ +const command: CommandController = new CommandController(); + +/** + * @description OffscreenCanvasのメッセージイベント + * OffscreenCanvas message event + * + * @params {MessageEvent} event + * @return {Promise} + * @method + * @public + */ +self.addEventListener("message", async (event: MessageEvent): Promise => +{ + command.queue.push(event.data); + if (command.state === "deactivate") { + await command.execute(); + } +}); + +export default {}; \ No newline at end of file diff --git a/packages/renderer/src/interface/IBlendMode.ts b/packages/renderer/src/interface/IBlendMode.ts new file mode 100644 index 00000000..9480efe7 --- /dev/null +++ b/packages/renderer/src/interface/IBlendMode.ts @@ -0,0 +1,15 @@ +export type IBlendMode = "copy" + | "add" + | "alpha" + | "darken" + | "difference" + | "erase" + | "hardlight" + | "invert" + | "layer" + | "lighten" + | "multiply" + | "normal" + | "overlay" + | "screen" + | "subtract"; \ No newline at end of file diff --git a/packages/renderer/src/interface/IMessage.ts b/packages/renderer/src/interface/IMessage.ts new file mode 100644 index 00000000..64bbc531 --- /dev/null +++ b/packages/renderer/src/interface/IMessage.ts @@ -0,0 +1,11 @@ +export interface IMessage { + command: string; + buffer: Float32Array; + width?: number; + height?: number; + length?: number; + imageBitmaps?: ImageBitmap[] | null; + canvas?: OffscreenCanvas; + devicePixelRatio?: number; + id?: string; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/INode.ts b/packages/renderer/src/interface/INode.ts new file mode 100644 index 00000000..41ff6234 --- /dev/null +++ b/packages/renderer/src/interface/INode.ts @@ -0,0 +1,7 @@ +export interface INode { + index: number; + x: number; + y: number; + w: number; + h: number; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/IRGBA.ts b/packages/renderer/src/interface/IRGBA.ts new file mode 100644 index 00000000..aa6feffc --- /dev/null +++ b/packages/renderer/src/interface/IRGBA.ts @@ -0,0 +1,6 @@ +export interface IRGBA { + A: number; + R: number; + G: number; + B: number; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextData.ts b/packages/renderer/src/interface/ITextData.ts new file mode 100644 index 00000000..a8696479 --- /dev/null +++ b/packages/renderer/src/interface/ITextData.ts @@ -0,0 +1,9 @@ +import type { ITextObject } from "./ITextObject"; + +export interface ITextData { + ascentTable: number[]; + heightTable: number[]; + lineTable: ITextObject[]; + textTable: ITextObject[]; + widthTable: number[]; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextFieldAutoSize.ts b/packages/renderer/src/interface/ITextFieldAutoSize.ts new file mode 100644 index 00000000..e38ed299 --- /dev/null +++ b/packages/renderer/src/interface/ITextFieldAutoSize.ts @@ -0,0 +1 @@ +export type ITextFieldAutoSize = "center" | "left" | "none" | "right"; \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextFormat.ts b/packages/renderer/src/interface/ITextFormat.ts new file mode 100644 index 00000000..3de0ef9b --- /dev/null +++ b/packages/renderer/src/interface/ITextFormat.ts @@ -0,0 +1,15 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; + +export interface ITextFormat { + align: ITextFormatAlign | null; + bold: boolean | null; + color: number | null; + font: string | null; + italic: boolean | null; + leading: number | null; + leftMargin: number | null; + letterSpacing: number | null; + rightMargin: number | null; + size: number | null; + underline: boolean | null; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextFormatAlign.ts b/packages/renderer/src/interface/ITextFormatAlign.ts new file mode 100644 index 00000000..aa48dea1 --- /dev/null +++ b/packages/renderer/src/interface/ITextFormatAlign.ts @@ -0,0 +1 @@ +export type ITextFormatAlign = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextObject.ts b/packages/renderer/src/interface/ITextObject.ts new file mode 100644 index 00000000..00e0e2e5 --- /dev/null +++ b/packages/renderer/src/interface/ITextObject.ts @@ -0,0 +1,13 @@ +import type { ITextFormat } from "./ITextFormat"; +import type { ITextObjectMode } from "./ITextObjectMode"; + +export interface ITextObject { + mode: ITextObjectMode; + text: string; + textFormat: ITextFormat; + x: number; + y: number; + w: number; + h: number; + line: number; +} \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextObjectMode.ts b/packages/renderer/src/interface/ITextObjectMode.ts new file mode 100644 index 00000000..0c41854e --- /dev/null +++ b/packages/renderer/src/interface/ITextObjectMode.ts @@ -0,0 +1 @@ +export type ITextObjectMode = "break" | "wrap" | "image" | "text"; \ No newline at end of file diff --git a/packages/renderer/src/interface/ITextSetting.ts b/packages/renderer/src/interface/ITextSetting.ts new file mode 100644 index 00000000..f2864db9 --- /dev/null +++ b/packages/renderer/src/interface/ITextSetting.ts @@ -0,0 +1,26 @@ +import { ITextFieldAutoSize } from "./ITextFieldAutoSize"; + +export interface ITextSetting { + width: number; + height: number; + stopIndex: number; + scrollX: number; + scrollY: number; + textWidth: number; + textHeight: number; + rawWidth: number; + rawHeight: number; + autoSize: ITextFieldAutoSize; + focusIndex: number; + selectIndex: number; + focusVisible: boolean; + thickness: number; + thicknessColor: number; + wordWrap: boolean; + border: boolean; + borderColor: number; + background: boolean; + backgroundColor: number; + defaultColor: number; + defaultSize: number; +} \ No newline at end of file diff --git a/packages/share/README.md b/packages/share/README.md deleted file mode 100644 index 252590f0..00000000 --- a/packages/share/README.md +++ /dev/null @@ -1,11 +0,0 @@ -@next2d/share -============= - -## Installation - -``` -npm install @next2d/share -``` - -## License -This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/share/package.json b/packages/share/package.json deleted file mode 100644 index 5bca16ca..00000000 --- a/packages/share/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@next2d/share", - "version": "*", - "description": "Next2D Share Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", - "license": "MIT", - "homepage": "https://next2d.app", - "bugs": "https://github.com/Next2D/Player/issues", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist" - ], - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - } - }, - "keywords": [ - "Next2D", - "Next2D Share" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/webgl": "file:../webgl" - } -} diff --git a/packages/share/src/CacheStore.ts b/packages/share/src/CacheStore.ts deleted file mode 100644 index f5415986..00000000 --- a/packages/share/src/CacheStore.ts +++ /dev/null @@ -1,371 +0,0 @@ -import type { CanvasToWebGLContext } from "@next2d/webgl"; -import { - $requestAnimationFrame, - $clearTimeout, - $setTimeout, - $getMap, - $poolMap, - $getArray -} from "./RenderUtil"; -import type { CachePositionImpl } from "./interface/CachePositionImpl"; - -/** - * @class - */ -export class CacheStore -{ - private readonly _$pool: HTMLCanvasElement[]; - private readonly _$store: Map; - private readonly _$timerMap: Map; - private _$context: CanvasToWebGLContext | null; - - /** - * @constructor - */ - constructor () - { - /** - * @type {array} - * @private - */ - this._$pool = []; - - /** - * @type {Map} - * @private - */ - this._$store = new Map(); - - /** - * @type {Map} - * @private - */ - this._$timerMap = new Map(); - - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = null; - } - - /** - * @member {CanvasToWebGLContext} - * @param {CanvasToWebGLContext} context - * @public - */ - set context (context: CanvasToWebGLContext) - { - this._$context = context; - } - - /** - * @return {void} - * @method - * @public - */ - reset (): void - { - for (const data of this._$store.values()) { - - for (const value of data.values()) { - this.destroy(value); - } - - $poolMap(data); - } - - this._$store.clear(); - - if (this._$context) { - this - ._$context - .frameBuffer - .clearCache(); - } - } - - /** - * @param {CanvasRenderingContext2D|WebGLTexture} object - * @return {void} - * @method - * @public - */ - destroy ( - object: CanvasRenderingContext2D | WebGLTexture | CachePositionImpl |null = null - ): void { - - if (!object || typeof object !== "object") { - return ; - } - - if (object instanceof WebGLTexture) { - $requestAnimationFrame((): void => - { - if (!this._$context) { - return ; - } - - this - ._$context - .frameBuffer - .releaseTexture(object); - }); - - return ; - } - - if ("canvas" in object - && object instanceof CanvasRenderingContext2D - ) { - - const canvas: HTMLCanvasElement = object.canvas; - const width: number = canvas.width; - const height: number = canvas.height; - - object.clearRect(0, 0, width + 1, height + 1); - - // canvas reset - canvas.width = canvas.height = 1; - - // pool - this._$pool.push(canvas); - } - - if (this._$context && "index" in object) { - this - ._$context - .frameBuffer - .textureManager - .releasePosition(object); - } - } - - /** - * @return {HTMLCanvasElement} - * @method - * @public - */ - getCanvas (): HTMLCanvasElement - { - return this._$pool.pop() || document.createElement("canvas"); - } - - /** - * @param {string} id - * @param {string} type - * @returns {void} - * @method - * @public - */ - remove (id: string, type: string): void - { - if (!this._$store.has(id)) { - return ; - } - - const data: Map = this._$store.get(id); - if (!data.has(type)) { - return ; - } - - // delete key - data.delete(type); - - if (!data.size) { - $poolMap(data); - this._$store.delete(id); - } - } - - /** - * @param {string|number} id - * @return {void} - * @method - * @public - */ - stopTimer (id: string | number): void - { - id = `${id}`; - if (this._$timerMap.has(id)) { - $clearTimeout(this._$timerMap.get(id)); - this._$timerMap.delete(id); - } - } - - /** - * @param {string|number} id - * @returns {void} - * @method - * @public - */ - removeCache (id: string | number): void - { - id = `${id}`; - if (this._$store.has(id)) { - - const data: Map = this._$store.get(id); - for (const value of data.values()) { - this.destroy(value); - } - - data.clear(); - $poolMap(data); - this._$store.delete(id); - } - - this._$timerMap.delete(id); - } - - /** - * @description 5秒後にキャッシュを削除 - * - * @param {number|string} id - * @return {void} - * @method - * @public - */ - setRemoveTimer (id: string | number): void - { - id = `${id}`; - - this.stopTimer(id); - - // set timer - if (this._$store.has(id)) { - // @ts-ignore - const timerId: number = $setTimeout(() => - { - this.removeCache(id); - }, 5000); - this._$timerMap.set(id, timerId); - } - } - - /** - * @param {array} keys - * @return {*} - * @method - * @public - */ - get (keys: any[]): any - { - const id: string = `${keys[0]}`; - const type: string = `${keys[1]}`; - - if (this._$store.has(id)) { - - this.stopTimer(id); - - const data: Map = this._$store.get(id); - if (data.has(type)) { - return data.get(type); - } - - } - - return null; - } - - /** - * @param {array} keys - * @param {*} value - * @return {void} - * @method - * @public - */ - set (keys: any[], value: any = null): void - { - const id: string = `${keys[0]}`; - const type: string = `${keys[1]}`; - - // init - if (!this._$store.has(id)) { - this._$store.set(id, $getMap()); - } - - const data: Map = this._$store.get(id); - if (value === null) { - - if (!data.has(type)) { - return ; - } - - this.destroy(data.get(type)); - - data.delete(type); - - if (!data.size) { - $poolMap(data); - this._$store.delete(id); - } - - return ; - - } - - // set cache - data.set(type, value); - } - - /** - * @param {array} keys - * @return {boolean} - * @method - * @public - */ - has (keys: any[]): boolean - { - const id: string = `${keys[0]}`; - if (!this._$store.has(id)) { - return false; - } - return this._$store.get(id).has(`${keys[1]}`); - } - - /** - * @param {*} unique_key - * @param {array} [matrix=null] - * @param {Float32Array} [color=null] - * @return {array} - * @method - * @public - */ - generateKeys ( - unique_key: any, - matrix: number[] | null = null, - color: Float32Array | null = null - ): string[] { - - let str: string = ""; - if (matrix && matrix.length) { - str += `${matrix[0]}_${matrix[1]}`; - } - - // color - if (color && color.length) { - str += color[7] === 0 ? "" : `_${color[7]}`; - } - - const keys: string[] = $getArray(); - if (str) { - let hash = 0; - const length: number = str.length; - for (let idx: number = 0; idx < length; idx++) { - - const chr: number = str.charCodeAt(idx); - - hash = (hash << 5) - hash + chr; - hash |= 0; - } - keys[1] = `_${hash}`; - } else { - keys[1] = "_0"; - } - keys[0] = `${unique_key}`; - - return keys; - } -} - -export const $cacheStore: CacheStore = new CacheStore(); diff --git a/packages/share/src/RenderUtil.ts b/packages/share/src/RenderUtil.ts deleted file mode 100644 index ef3c71a8..00000000 --- a/packages/share/src/RenderUtil.ts +++ /dev/null @@ -1,980 +0,0 @@ -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { RGBAImpl } from "./interface/RGBAImpl"; -import type { PreObjectImpl } from "./interface/PreObjectImpl"; -import { BlendModeImpl } from "./interface/BlendModeImpl"; - -/** - * @type {number} - * @public - */ -export let $devicePixelRatio: number = 1; - -/** - * @param {number} device_pixel_ratio - * @method - * @public - */ -export const $setDevicePixelRatio = (device_pixel_ratio: number): void => -{ - $devicePixelRatio = device_pixel_ratio; -}; - -/** - * @type {number} - */ -let programId: number = 0; - -/** - * @return {number} - * @method - * @public - */ -export const $getProgramId = (): number => -{ - return programId++; -}; - -/** - * @return {number} - * @public - */ -let $updated: boolean = false; - -/** - * @return {boolean} - * @method - * @public - */ -export const $isUpdated = (): boolean => -{ - return $updated; -}; - -/** - * @param {boolean} update - * @return {void} - * @method - * @public - */ -export const $doUpdated = (update: boolean = true) => -{ - $updated = update; -}; - -/** - * @shortcut - * @type {number} - * @const - */ -export const $Infinity: number = Infinity; - -/** - * @shortcut - * @type {Math} - * @const - */ -export const $Math: Math = Math; - -/** - * @shortcut - * @type {ArrayConstructor} - * @const - */ -export const $Array: ArrayConstructor = Array; - -/** - * @shortcut - * @type {MapConstructor} - * @const - */ -export const $Map: MapConstructor = Map; - -/** - * @shortcut - * @type {NumberConstructor} - * @const - */ -export const $Number: NumberConstructor = Number; - -/** - * @shortcut - * @type {Float32Array} - * @const - */ -export const $Float32Array: Float32ArrayConstructor = Float32Array; - -/** - * @shortcut - * @type {Int32Array} - * @const - */ -export const $Int32Array: Int32ArrayConstructor = Int32Array; - -/** - * @shortcut - * @type {Int16Array} - * @const - */ -export const $Int16Array: Int16ArrayConstructor = Int16Array; - -/** - * @shortcut - * @type {OffscreenCanvas} - * @const - */ -export const $OffscreenCanvas: typeof OffscreenCanvas = OffscreenCanvas; - -/** - * @shortcut - * @type {object} - * @const - */ -export const $CanvasRenderingContext2D = null; - -/** - * @shortcut - * @type {(number: number) => boolean} - * @const - */ -export const $isNaN: typeof isNaN = isNaN; - -/** - * @shortcut - * @type {function} - * @const - */ -export const $requestAnimationFrame: typeof requestAnimationFrame = requestAnimationFrame; - -/** - * @shortcut - * @type {function} - * @const - */ -export const $cancelAnimationFrame: typeof cancelAnimationFrame = cancelAnimationFrame; - -/** - * @shortcut - * @type {Performance} - * @const - */ -export const $performance: Performance = performance; - -/** - * @shortcut - * @type {function} - * @const - * @static - */ -export const $setTimeout: typeof setTimeout = setTimeout; - -/** - * @shortcut - * @type {function} - * @const - * @static - */ -export const $clearTimeout: typeof clearTimeout = clearTimeout; - -/** - * @type {Float32Array} - * @const - * @static - */ -export const $MATRIX_ARRAY_IDENTITY: Float32Array = new $Float32Array([1, 0, 0, 1, 0, 0]); - -/** - * @type {Float32Array} - * @const - * @static - */ -export const $COLOR_ARRAY_IDENTITY: Float32Array = new $Float32Array([1, 1, 1, 1, 0, 0, 0, 0]); - -/** - * @type {number} - * @const - * @static - */ -export const $SHORT_INT_MIN: number = -32768; - -/** - * @type {number} - * @const - * @static - */ -export const $SHORT_INT_MAX: number = 32767; - -/** - * @shortcut - * @type {number} - * @const - * @static - */ -export const $Deg2Rad: number = $Math.PI / 180; - -/** - * @shortcut - * @type {number} - * @const - * @static - */ -export const $Rad2Deg: number = 180 / $Math.PI; - -/** - * @type {array} - * @static - */ -export const $preObjects: PreObjectImpl[] = []; - -/** - * 使用済みになったInt32Arrayをプール、サイズは4固定 - * Pool used Int32Array, size fixed at 4. - * - * @type {Int32Array[]} - * @const - * @static - */ -export const $int32Array4: Int32Array[] = []; - -/** - * 使用済みになったFloat32Arrayをプール、サイズは4固定 - * Pool used Float32Array, size fixed at 4. - * - * @type {Float32Array[]} - * @const - * @static - */ -export const $float32Array4: Float32Array[] = []; - -/** - * 使用済みになったFloat32Arrayをプール、サイズは6固定 - * Pool used Float32Array, size fixed at 6. - * - * @type {Float32Array[]} - * @const - * @static - */ -export const $float32Array6: Float32Array[] = []; - -/** - * 使用済みになったFloat32Arrayをプール、サイズは8固定 - * Pool used Float32Array, size fixed at 8. - * - * @type {Float32Array[]} - * @const - * @static - */ -export const $float32Array8: Float32Array[] = []; - -/** - * 使用済みになったFloat32Arrayをプール、サイズは9固定 - * Pool used Float32Array, size fixed at 9. - * - * @type {Float32Array[]} - * @const - * @static - */ -export const $float32Array9: Float32Array[] = []; - -/** - * 使用済みになったArray Objectをプール - * Pool Array objects that are no longer in use. - * - * @type {array[]} - * @const - * @static - */ -export const $arrays: any[] = []; - -/** - * 使用済みになったMap Objectをプール - * Pool Map objects that are no longer in use. - * - * @type {Map[]} - * @const - * @static - */ -export const $maps: Map[] = []; - -/** - * 使用済みになったbounds Objectをプール - * Pool bounds objects that are no longer in use. - * - * @type {object[]} - * @const - * @static - */ -export const $bounds: BoundsImpl[] = []; - -/** - * @type {CanvasRenderingContext2D} - * @static - */ -const $colorContext: OffscreenCanvasRenderingContext2D | null = new $OffscreenCanvas(1, 1).getContext("2d"); - -/** - * @param {number} x_min - * @param {number} x_max - * @param {number} y_min - * @param {number} y_max - * @return {object} - * @method - * @static - */ -export const $getBoundsObject = ( - x_min: number = 0, x_max: number = 0, - y_min: number = 0, y_max: number = 0 -): BoundsImpl => -{ - const object: BoundsImpl = $bounds.pop() || { "xMin": 0, "xMax": 0, "yMin": 0, "yMax": 0 }; - - object.xMin = x_min; - object.xMax = x_max; - object.yMin = y_min; - object.yMax = y_max; - - return object; -}; - -/** - * @return {object} - * @method - * @static - */ -export const $poolBoundsObject = (bounds: BoundsImpl): void => -{ - $bounds.push(bounds); -}; - -/** - * @param {number} [f0=0] - * @param {number} [f1=0] - * @param {number} [f2=0] - * @param {number} [f3=0] - * @return {Float32Array} - * @method - * @static - */ -export const $getFloat32Array4 = ( - f0: number = 0, f1: number = 0, - f2: number = 0, f3: number = 0 -): Float32Array => { - - const array: Float32Array = $float32Array4.pop() || new $Float32Array(4); - - array[0] = f0; - array[1] = f1; - array[2] = f2; - array[3] = f3; - - return array; -}; - -/** - * @param {Float32Array} array - * @return {void} - * @method - * @static - */ -export const $poolFloat32Array4 = (array: Float32Array): void => -{ - $float32Array4.push(array); -}; - -/** - * @param {number} [f0=0] - * @param {number} [f1=0] - * @param {number} [f2=0] - * @param {number} [f3=0] - * @return {Float32Array} - * @method - * @static - */ -export const $getInt32Array4 = ( - f0: number = 0, f1: number = 0, - f2: number = 0, f3: number = 0 -): Int32Array => { - - const array: Int32Array = $int32Array4.pop() || new $Int32Array(4); - - array[0] = f0; - array[1] = f1; - array[2] = f2; - array[3] = f3; - - return array; -}; - -/** - * @param {Float32Array} array - * @return {void} - * @method - * @static - */ -export const $poolInt32Array4 = (array: Int32Array): void => -{ - $int32Array4.push(array); -}; - -/** - * @param {number} [f0=0] - * @param {number} [f1=0] - * @param {number} [f2=0] - * @param {number} [f3=0] - * @param {number} [f4=0] - * @param {number} [f5=0] - * @return {Float32Array} - * @method - * @static - */ -export const $getFloat32Array6 = ( - f0: number = 0, f1: number = 0, - f2: number = 0, f3: number = 0, - f4: number = 0, f5: number = 0 -): Float32Array => { - - const array: Float32Array = $float32Array6.pop() || new $Float32Array(6); - - array[0] = f0; - array[1] = f1; - array[2] = f2; - array[3] = f3; - array[4] = f4; - array[5] = f5; - - return array; -}; - -/** - * @param {Float32Array} array - * @return {void} - * @method - * @static - */ -export const $poolFloat32Array6 = (array: Float32Array): void => -{ - $float32Array6.push(array); -}; - -/** - * @param {number} [f0=0] - * @param {number} [f1=0] - * @param {number} [f2=0] - * @param {number} [f3=0] - * @param {number} [f4=0] - * @param {number} [f5=0] - * @param {number} [f6=0] - * @param {number} [f7=0] - * @return {Float32Array} - * @method - * @static - */ -export const $getFloat32Array8 = ( - f0: number = 1, f1: number = 1, - f2: number = 1, f3: number = 1, - f4: number = 0, f5: number = 0, - f6: number = 0, f7: number = 0 -): Float32Array => { - - const array: Float32Array = $float32Array8.pop() || new $Float32Array(8); - - array[0] = f0; - array[1] = f1; - array[2] = f2; - array[3] = f3; - array[4] = f4; - array[5] = f5; - array[6] = f6; - array[7] = f7; - - return array; -}; - -/** - * @param {Float32Array} array - * @return {void} - * @method - * @static - */ -export const $poolFloat32Array8 = (array: Float32Array): void => -{ - $float32Array8.push(array); -}; - -/** - * @param {number} [f0=0] - * @param {number} [f1=0] - * @param {number} [f2=0] - * @param {number} [f3=0] - * @param {number} [f4=0] - * @param {number} [f5=0] - * @param {number} [f6=0] - * @param {number} [f7=0] - * @param {number} [f8=0] - * @return {Float32Array} - * @method - * @static - */ -export const $getFloat32Array9 = ( - f0: number = 0, f1: number = 0, f2: number = 0, - f3: number = 0, f4: number = 0, f5: number = 0, - f6: number = 0, f7: number = 0, f8: number = 0 -): Float32Array => { - - const array: Float32Array = $float32Array9.pop() || new $Float32Array(9); - - array[0] = f0; - array[1] = f1; - array[2] = f2; - array[3] = f3; - array[4] = f4; - array[5] = f5; - array[6] = f6; - array[7] = f7; - array[8] = f8; - - return array; -}; - -/** - * @param {Float32Array} array - * @return {void} - * @method - * @static - */ -export const $poolFloat32Array9 = (array: Float32Array): void => -{ - $float32Array9.push(array); -}; - -/** - * @param {array} args - * @return {array} - * @method - * @static - */ -export const $getArray = (...args: any[]): any[] => -{ - const array: any[] = $arrays.pop() || []; - if (args.length) { - array.push(...args); - } - return array; -}; - -/** - * @param {array} array - * @return {void} - * @method - * @static - */ -export const $poolArray = (array: any[] | null = null): void => -{ - if (!array) { - return ; - } - - if (array.length) { - array.length = 0; - } - - $arrays.push(array); -}; - -/** - * @param {Map} map - * @return void - * @method - * @static - */ -export const $poolMap = (map: Map): void => -{ - if (map.size) { - map.clear(); - } - $maps.push(map); -}; - -/** - * @return {Map} - * @method - * @static - */ -export const $getMap = (): Map => -{ - return $maps.pop() || new $Map(); -}; - -/** - * @param {number} v - * @return {number} - * @method - * @static - */ -export const $upperPowerOfTwo = (v: number): number => -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -}; - -/** - * @param {Float32Array} matrix - * @return {Float32Array} - * @method - * @static - */ -export const $linearGradientXY = (matrix: Float32Array): Float32Array => -{ - const x0: number = -819.2 * matrix[0] - 819.2 * matrix[2] + matrix[4]; - const x1: number = 819.2 * matrix[0] - 819.2 * matrix[2] + matrix[4]; - const x2: number = -819.2 * matrix[0] + 819.2 * matrix[2] + matrix[4]; - const y0: number = -819.2 * matrix[1] - 819.2 * matrix[3] + matrix[5]; - const y1: number = 819.2 * matrix[1] - 819.2 * matrix[3] + matrix[5]; - const y2: number = -819.2 * matrix[1] + 819.2 * matrix[3] + matrix[5]; - - let vx2: number = x2 - x0; - let vy2: number = y2 - y0; - - const r1: number = $Math.sqrt(vx2 * vx2 + vy2 * vy2); - if (r1) { - vx2 = vx2 / r1; - vy2 = vy2 / r1; - } else { - vx2 = 0; - vy2 = 0; - } - - const r2: number = (x1 - x0) * vx2 + (y1 - y0) * vy2; - - return $getFloat32Array4(x0 + r2 * vx2, y0 + r2 * vy2, x1, y1); -}; - -/** - * @param {Float32Array} m - * @returns {Float32Array} - * @method - * @static - */ -export const $inverseMatrix = (m: Float32Array): Float32Array => -{ - const rdet: number = 1 / (m[0] * m[4] - m[3] * m[1]); - const tx: number = m[3] * m[7] - m[4] * m[6]; - const ty: number = m[1] * m[6] - m[0] * m[7]; - - return $getFloat32Array9( - m[4] * rdet, 0 - m[1] * rdet, 0, - 0 - m[3] * rdet, m[0] * rdet, 0, - tx * rdet, ty * rdet, 1 - ); -}; - -/** - * @param {number} value - * @param {number} min - * @param {number} max - * @param {number} [default_value=null] - * @return {number} - * @method - * @static - */ -export const $clamp = ( - value: number, - min: number, max: number, - default_value: number|null = null -): number => { - - const number: number = +value; - - return $isNaN(number) && default_value !== null - ? default_value - : $Math.min($Math.max(min, $isNaN(number) ? 0 : number), max); -}; - -/** - * @param {Float32Array} a - * @param {Float32Array} b - * @return {Float32Array} - * @method - * @static - */ -export const $multiplicationMatrix = (a: Float32Array, b: Float32Array): Float32Array => -{ - return $getFloat32Array6( - a[0] * b[0] + a[2] * b[1], - a[1] * b[0] + a[3] * b[1], - a[0] * b[2] + a[2] * b[3], - a[1] * b[2] + a[3] * b[3], - a[0] * b[4] + a[2] * b[5] + a[4], - a[1] * b[4] + a[3] * b[5] + a[5] - ); -}; - -/** - * @param {Float32Array} a - * @param {Float32Array} b - * @return {Float32Array} - * @method - * @static - */ -export const $multiplicationColor = (a: Float32Array, b: Float32Array): Float32Array => -{ - return $getFloat32Array8( - a[0] * b[0], - a[1] * b[1], - a[2] * b[2], - a[3] * b[3], - a[0] * b[4] + a[4], - a[1] * b[5] + a[5], - a[2] * b[6] + a[6], - a[3] * b[7] + a[7] - ); -}; - -/** - * @param {object} bounds - * @param {Float32Array} matrix - * @return {object} - * @method - * @static - */ -export const $boundsMatrix = (bounds: BoundsImpl, matrix: Float32Array): BoundsImpl => -{ - const x0: number = bounds.xMax * matrix[0] + bounds.yMax * matrix[2] + matrix[4]; - const x1: number = bounds.xMax * matrix[0] + bounds.yMin * matrix[2] + matrix[4]; - const x2: number = bounds.xMin * matrix[0] + bounds.yMax * matrix[2] + matrix[4]; - const x3: number = bounds.xMin * matrix[0] + bounds.yMin * matrix[2] + matrix[4]; - const y0: number = bounds.xMax * matrix[1] + bounds.yMax * matrix[3] + matrix[5]; - const y1: number = bounds.xMax * matrix[1] + bounds.yMin * matrix[3] + matrix[5]; - const y2: number = bounds.xMin * matrix[1] + bounds.yMax * matrix[3] + matrix[5]; - const y3: number = bounds.xMin * matrix[1] + bounds.yMin * matrix[3] + matrix[5]; - - const xMin: number = $Math.min( $Number.MAX_VALUE, x0, x1, x2, x3); - const xMax: number = $Math.max(0 - $Number.MAX_VALUE, x0, x1, x2, x3); - const yMin: number = $Math.min( $Number.MAX_VALUE, y0, y1, y2, y3); - const yMax: number = $Math.max(0 - $Number.MAX_VALUE, y0, y1, y2, y3); - - return $getBoundsObject(xMin, xMax, yMin, yMax); -}; - -/** - * @param {string} str - * @return {number} - * @method - * @static - */ -export const $colorStringToInt = (str: string): number => -{ - if (!$colorContext) { - return 0; - } - - $colorContext.fillStyle = str; - const color: number = +`0x${$colorContext.fillStyle.slice(1)}`; - - // reset - $colorContext.fillStyle = "rgba(0, 0, 0, 1)"; - - return color; -}; - -/** - * @param {number|string} rgb - * @return {number} - * @method - * @static - */ -export const $toColorInt = (rgb: any): number => -{ - return $isNaN(+rgb) - ? $colorStringToInt(`${rgb}`) - : +rgb; -}; - -/** - * @param {number} uint - * @return {RGBAImpl} - * @method - * @static - */ -export const $uintToRGBA = (uint: number): RGBAImpl => -{ - return { - "A": uint >>> 24, - "R": (uint & 0x00ff0000) >> 16, - "G": (uint & 0x0000ff00) >> 8, - "B": uint & 0x000000ff - }; -}; - -/** - * @param {number} int - * @param {number} alpha - * @param {boolean} premultiplied - * @return {number} - * @method - * @static - */ -export const $intToR = ( - int: number, alpha: number, - premultiplied: boolean -): number => { - return (int >> 16) * (premultiplied ? alpha : 1) / 255; -}; - -/** - * @param {number} int - * @param {number} alpha - * @param {boolean} premultiplied - * @return {number} - * @method - * @static - */ -export const $intToG = ( - int: number, alpha: number, - premultiplied: boolean -): number => { - return (int >> 8 & 0xFF) * (premultiplied ? alpha : 1) / 255; -}; - -/** - * @param {number} int - * @param {number} alpha - * @param {boolean} premultiplied - * @return {number} - * @method - * @static - */ -export const $intToB = ( - int: number, alpha: number, - premultiplied: boolean -): number => { - return (int & 0xFF) * (premultiplied ? alpha : 1) / 255; -}; - -/** - * @param {number} color - * @param {number} [alpha=1] - * @returns {RGBAImpl} - * @method - * @static - */ -export const $intToRGBA = (color: number, alpha: number = 1): RGBAImpl => -{ - return { - "R": (color & 0xff0000) >> 16, - "G": (color & 0x00ff00) >> 8, - "B": color & 0x0000ff, - "A": alpha * 255 - }; -}; - -/** - * @param {string} font - * @param {number} size - * @param {boolean} [italic=false] - * @param {boolean} [bold=false] - * @return {string} - * @method - * @static - */ -export const $generateFontStyle = ( - font: string, size: number, - italic: boolean = false, bold: boolean = false -): string => { - - let fontStyle: string = ""; - - if (italic) { - fontStyle = "italic "; - } - - if (bold) { - fontStyle += "bold "; - } - - return `${fontStyle}${size}px '${font}','sans-serif'`; -}; - -/** - * @return {object} - * @method - * @static - */ -export const $getPreObject = (): PreObjectImpl => -{ - return $preObjects.pop() || - { - "isLayer": false, - "isUpdated": null, - "canApply": null, - "matrix": null, - "color": null, - "blendMode": "normal", - "filters": null, - "sw": 0, - "sh": 0 - }; -}; - -/** - * @param {PreObjectImpl} object - * @return {void} - * @method - * @static - */ -export const $poolPreObject = (object: PreObjectImpl): void => -{ - if (object.color) { - $poolFloat32Array8(object.color); - } - - // reset - object.isLayer = false; - object.isUpdated = null; - object.canApply = null; - object.matrix = null; - object.color = null; - object.filters = null; - object.blendMode = "normal"; - object.sw = 0; - object.sh = 0; - - // pool - $preObjects.push(object); -}; - -/** - * @type {Map} - * @private - */ -const blendMap: Map = new Map([ - [1, "normal"], - [2, "layer"], - [3, "multiply"], - [4, "screen"], - [5, "lighten"], - [6, "darken"], - [7, "difference"], - [8, "add"], - [9, "subtract"], - [10, "invert"], - [11, "alpha"], - [12, "erase"], - [13, "overlay"], - [14, "hardlight"] -]); - -/** - * @return {string} - * @method - * @public - */ -export const $blendToString = (number: number): BlendModeImpl => -{ - return blendMap.has(number) ? blendMap.get(number) || "normal" : "normal"; -}; \ No newline at end of file diff --git a/packages/share/src/index.ts b/packages/share/src/index.ts deleted file mode 100644 index 6e173b06..00000000 --- a/packages/share/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./CacheStore"; -export * from "./RenderUtil"; \ No newline at end of file diff --git a/packages/share/src/interface/BlendModeImpl.ts b/packages/share/src/interface/BlendModeImpl.ts deleted file mode 100644 index 6ed290eb..00000000 --- a/packages/share/src/interface/BlendModeImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type BlendModeImpl = "copy" - | "add" - | "alpha" - | "darken" - | "difference" - | "erase" - | "hardlight" - | "invert" - | "layer" - | "lighten" - | "multiply" - | "normal" - | "overlay" - | "screen" - | "subtract"; \ No newline at end of file diff --git a/packages/share/src/interface/BoundsImpl.ts b/packages/share/src/interface/BoundsImpl.ts deleted file mode 100644 index 7af56a28..00000000 --- a/packages/share/src/interface/BoundsImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BoundsImpl { - xMin: number; - xMax: number; - yMin: number; - yMax: number; -} \ No newline at end of file diff --git a/packages/share/src/interface/CachePositionImpl.ts b/packages/share/src/interface/CachePositionImpl.ts deleted file mode 100644 index 98f72340..00000000 --- a/packages/share/src/interface/CachePositionImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CachePositionImpl { - index: number; - x: number; - y: number; - w: number; - h: number; - filterState?: boolean; - matrix?: string; - offsetX?: number; - offsetY?: number; -} \ No newline at end of file diff --git a/packages/share/src/interface/PreObjectImpl.ts b/packages/share/src/interface/PreObjectImpl.ts deleted file mode 100644 index f3118066..00000000 --- a/packages/share/src/interface/PreObjectImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BlendModeImpl } from "./BlendModeImpl"; - -export interface PreObjectImpl -{ - isLayer: boolean; - isUpdated: boolean | null; - canApply: boolean | null; - matrix: Float32Array | null; - color: Float32Array | null; - blendMode: BlendModeImpl; - filters: any[] | null; - sw: number; - sh: number; -} \ No newline at end of file diff --git a/packages/share/src/interface/RGBAImpl.ts b/packages/share/src/interface/RGBAImpl.ts deleted file mode 100644 index 240c24bc..00000000 --- a/packages/share/src/interface/RGBAImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface RGBAImpl { - A: number; - R: number; - G: number; - B: number; -} \ No newline at end of file diff --git a/packages/text/package.json b/packages/text/package.json index 2cbb6261..7a7e11fb 100644 --- a/packages/text/package.json +++ b/packages/text/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/text", "version": "*", - "description": "Next2D Text Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Text Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -31,7 +23,14 @@ "type": "git", "url": "git+https://github.com/Next2D/Player.git" }, + "dependencies": { + "htmlparser2": "^10.0.0" + }, "peerDependencies": { - "@next2d/share": "file:../share" + "@next2d/display": "file:../display", + "@next2d/geom": "file:../geom", + "@next2d/events": "file:../events", + "@next2d/ui": "file:../ui", + "@next2d/cache": "file:../cache" } -} +} \ No newline at end of file diff --git a/packages/text/src/TextArea/service/TextAreaMovePositionService.ts b/packages/text/src/TextArea/service/TextAreaMovePositionService.ts new file mode 100644 index 00000000..6f0e985c --- /dev/null +++ b/packages/text/src/TextArea/service/TextAreaMovePositionService.ts @@ -0,0 +1,54 @@ +import type { TextField } from "../../TextField"; +import { Point } from "@next2d/geom"; +import { stage } from "@next2d/display"; +import { + $textArea, + $mainCanvasPosition +} from "../../TextUtil"; + +/** + * @type {number} + * @private + */ +const $devicePixelRatio: number = window.devicePixelRatio; + +/** + * @description フォーカスしているテキストの位置にテキストエリアを移動します。 + * Move the text area to the position of the text that is focusing. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + const point = text_field.localToGlobal(new Point()); + + const textData = text_field.$textData; + if (textData) { + + const focusTextObject = textData.textTable[text_field.focusIndex]; + if (focusTextObject) { + for (let idx = text_field.focusIndex - 1; idx > -1; --idx) { + const textObject = textData.textTable[idx]; + if (!textObject || textObject.line !== focusTextObject.line) { + break; + } + point.x += textObject.w; + } + + const line = focusTextObject.mode === "break" + ? focusTextObject.line - 1 + : focusTextObject.line; + + for (let idx = 0; idx < line; ++idx) { + point.y += textData.heightTable[idx]; + } + } + } + + const scale = stage.rendererScale / $devicePixelRatio; + $textArea.style.left = `${$mainCanvasPosition.x + point.x * scale}px`; + $textArea.style.top = `${$mainCanvasPosition.y + point.y * scale}px`; +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaCompositionEndUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaCompositionEndUseCase.ts new file mode 100644 index 00000000..474eec4f --- /dev/null +++ b/packages/text/src/TextArea/usecase/TextAreaCompositionEndUseCase.ts @@ -0,0 +1,19 @@ +import { $getSelectedTextField } from "../../TextUtil"; +import { execute as textFieldCompositionEndUseCase } from "../../TextField/usecase/TextFieldCompositionEndUseCase"; + +/** + * @description テキストフィールドのコンポジションエンドイベントを処理します。 + * Processes the composition end event of the text field. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const textField = $getSelectedTextField(); + if (!textField) { + return ; + } + textFieldCompositionEndUseCase(textField); +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaCompositionStartUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaCompositionStartUseCase.ts new file mode 100644 index 00000000..f518fc21 --- /dev/null +++ b/packages/text/src/TextArea/usecase/TextAreaCompositionStartUseCase.ts @@ -0,0 +1,19 @@ +import { $getSelectedTextField } from "../../TextUtil"; +import { execute as textFieldCompositionStartService } from "../../TextField/service/TextFieldCompositionStartService"; + +/** + * @description IME などのテキスト変換システムが新しい変換セッションを開始した時に発生します。 + * Occurs when a text conversion system, such as an IME, starts a new conversion session. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const textField = $getSelectedTextField(); + if (!textField) { + return ; + } + textFieldCompositionStartService(textField); +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaCompositionUpdateUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaCompositionUpdateUseCase.ts new file mode 100644 index 00000000..e5617465 --- /dev/null +++ b/packages/text/src/TextArea/usecase/TextAreaCompositionUpdateUseCase.ts @@ -0,0 +1,20 @@ +import { $getSelectedTextField } from "../../TextUtil"; +import { execute as textFieldCompositionUpdateUseCase } from "../../TextField/usecase/TextFieldCompositionUpdateUseCase"; + +/** + * @description IME などのテキスト変換システムによって制御されているテキスト変換セッションに新しい文字が入力されたときに発生します。 + * Occurs when a new character is entered into a text conversion session controlled by a text conversion system such as an IME. + * + * @param {CompositionEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: CompositionEvent): void => +{ + const textField = $getSelectedTextField(); + if (!textField) { + return ; + } + textFieldCompositionUpdateUseCase(textField, event.data); +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaInputUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaInputUseCase.ts new file mode 100644 index 00000000..f756c968 --- /dev/null +++ b/packages/text/src/TextArea/usecase/TextAreaInputUseCase.ts @@ -0,0 +1,29 @@ +import { $getSelectedTextField } from "../../TextUtil"; +import { Event } from "@next2d/events"; +import { execute as textFieldInsertTextUseCase } from "../../TextField/usecase/TextFieldInsertTextUseCase"; + +/** + * @description テキストエリアに入力された文字を挿入します。 + * Inserts the characters entered in the text area. + * + * @param {InputEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (event: InputEvent): void => +{ + if (!event.data) { + return ; + } + + const textField = $getSelectedTextField(); + if (!textField) { + return ; + } + textFieldInsertTextUseCase(textField, event.data); + + if (textField.hasEventListener(Event.INPUT)) { + textField.dispatchEvent(new Event(Event.INPUT)); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts new file mode 100644 index 00000000..d6572299 --- /dev/null +++ b/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts @@ -0,0 +1,24 @@ +import { execute as textAreaCompositionStartUseCase } from "./TextAreaCompositionStartUseCase"; +import { execute as textAreaCompositionUpdateUseCase } from "./TextAreaCompositionUpdateUseCase"; +import { execute as textAreaCompositionEndUseCase } from "./TextAreaCompositionEndUseCase"; +import { execute as textAreaInputUseCase } from "./TextAreaInputUseCase"; + +/** + * @description テキストエリアにイベントを登録します。 + * Registers events in the text area. + * + * @param {HTMLTextAreaElement} text_area + * @return {void} + * @method + * @protected + */ +export const execute = (text_area: HTMLTextAreaElement): void => +{ + // omposition evnet + text_area.addEventListener("compositionstart", textAreaCompositionStartUseCase as EventListener); + text_area.addEventListener("compositionupdate", textAreaCompositionUpdateUseCase as EventListener); + text_area.addEventListener("compositionend", textAreaCompositionEndUseCase as EventListener); + + // input event + text_area.addEventListener("input", textAreaInputUseCase as EventListener); +}; \ No newline at end of file diff --git a/packages/text/src/TextData.ts b/packages/text/src/TextData.ts index 39adb002..49af13fc 100644 --- a/packages/text/src/TextData.ts +++ b/packages/text/src/TextData.ts @@ -1,17 +1,60 @@ -import type { TextObjectImpl } from "./interface/TextObjectImpl"; +import type { ITextObject } from "./interface/ITextObject"; /** + * @description テキストの文字毎のデータ + * Data for each character of text * @class + * @public */ export class TextData { private _$textWidth: number; private _$textHeight: number; - private readonly _$heightTable: number[]; - private readonly _$ascentTable: number[]; - private readonly _$widthTable: number[]; - private readonly _$textTable: TextObjectImpl[]; - private readonly _$lineTable: TextObjectImpl[]; + + /** + * @description テキストの行ごとの高さ + * Height of each line of text + * + * @type {array} + * @public + */ + public readonly heightTable: number[]; + + /** + * @description テキストの行ごとのベースラインから上方線までの距離 + * Distance from the baseline to the top line of each line of text + * + * @type {array} + * @public + */ + public readonly ascentTable: number[]; + + /** + * @description テキストの行ごとの幅 + * Width of each line of text + * + * @type {array} + * @public + */ + public readonly widthTable: number[]; + + /** + * @description テキストの行ごとのテキストオブジェクト + * Text object for each line of text + * + * @type {array} + * @public + */ + public readonly textTable: ITextObject[]; + + /** + * @description テキストの行ごとのテキストオブジェクト + * Text object for each line of text + * + * @type {array} + * @public + */ + public readonly lineTable: ITextObject[]; /** * @class @@ -31,70 +74,59 @@ export class TextData */ this._$textHeight = -1; - /** - * @type {array} - * @private - */ - this._$widthTable = []; - - /** - * @type {array} - * @private - */ - this._$heightTable = []; - - /** - * @type {array} - * @private - */ - this._$ascentTable = []; - - /** - * @type {array} - * @private - */ - this._$textTable = []; - - /** - * @type {array} - * @private - */ - this._$lineTable = []; + this.widthTable = []; + this.heightTable = []; + this.ascentTable = []; + this.textTable = []; + this.lineTable = []; } /** + * @description テキストエリアの幅 + * Width of text area + * * @member {number} * @readonly * @public */ get textWidth (): number { - if (this._$textWidth === -1) { - this._$textWidth = 0; - for (let idx: number = 0; idx < this._$widthTable.length; ++idx) { - this._$textWidth = Math.max(this._$textWidth, this._$widthTable[idx]); - } + if (this._$textWidth !== -1) { + return this._$textWidth; + } + + this._$textWidth = 0; + for (let idx = 0; idx < this.widthTable.length; ++idx) { + this._$textWidth = Math.max(this._$textWidth, this.widthTable[idx]); } return this._$textWidth; } /** + * @description テキストエリアの高さ + * Height of text area + * * @member {number} * @readonly * @public */ get textHeight (): number { - if (this._$textHeight === -1) { - this._$textHeight = 0; - for (let idx: number = 0; idx < this._$heightTable.length; ++idx) { - this._$textHeight += this._$heightTable[idx]; - } + if (this._$textHeight !== -1) { + return this._$textHeight; + } + + this._$textHeight = 0; + for (let idx = 0; idx < this.heightTable.length; ++idx) { + this._$textHeight += this.heightTable[idx]; } return this._$textHeight; } /** + * @description 指定した行のテキストの幅 + * Width of text for the specified line + * * @param {number} line_index * @return {number} * @method @@ -102,10 +134,13 @@ export class TextData */ getLineWidth (line_index: number): number { - return line_index in this._$widthTable ? this._$widthTable[line_index] : 0; + return line_index in this.widthTable ? this.widthTable[line_index] : 0; } /** + * @description 指定した行のテキストの高さ + * Height of text for the specified line + * * @param {number} line_index * @return {number} * @method @@ -113,56 +148,25 @@ export class TextData */ getLineHeight (line_index: number): number { - return line_index in this._$heightTable ? this._$heightTable[line_index] : 0; + return line_index in this.heightTable ? this.heightTable[line_index] : 0; } /** - * @member {array} - * @readonly - * @public - */ - get widthTable (): number[] - { - return this._$widthTable; - } - - /** - * @member {array} - * @readonly - * @public - */ - get heightTable (): number[] - { - return this._$heightTable; - } - - /** - * @member {array} - * @readonly - * @public - */ - get ascentTable (): number[] - { - return this._$ascentTable; - } - - /** - * @member {array} - * @readonly - * @public - */ - get textTable (): TextObjectImpl[] - { - return this._$textTable; - } - - /** - * @member {array} - * @readonly + * @description 設定を初期化 + * Initialize settings + * + * @return {void} + * @method * @public */ - get lineTable (): TextObjectImpl[] + clear (): void { - return this._$lineTable; + this._$textWidth = -1; + this._$textHeight = -1; + this.widthTable.length = 0; + this.heightTable.length = 0; + this.ascentTable.length = 0; + this.textTable.length = 0; + this.lineTable.length = 0; } } \ No newline at end of file diff --git a/packages/text/src/TextField.ts b/packages/text/src/TextField.ts new file mode 100644 index 00000000..a1f859b7 --- /dev/null +++ b/packages/text/src/TextField.ts @@ -0,0 +1,1298 @@ +import type { IBounds } from "./interface/IBounds"; +import type { ITextFieldAutoSize } from "./interface/ITextFieldAutoSize"; +import type { ITextFieldType } from "./interface/ITextFieldType"; +import type { ITextFieldCharacter } from "./interface/ITextFieldCharacter"; +import type { ICharacter } from "./interface/ICharacter"; +import type { LoaderInfo } from "@next2d/display"; +import { FocusEvent } from "@next2d/events"; +import { Rectangle } from "@next2d/geom"; +import { TextData } from "./TextData"; +import { TextFormat } from "./TextFormat"; +import { execute as textFormatSetDefaultService } from "./TextFormat/service/TextFormatSetDefaultService"; +import { execute as textFormatHtmlTextGenerateStyleService } from "./TextFormat/service/TextFormatHtmlTextGenerateStyleService"; +import { execute as textFieldGetTextDataUseCase } from "./TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldReloadUseCase } from "./TextField/usecase/TextFieldReloadUseCase"; +import { execute as textFieldUpdateStopIndexUseCase } from "./TextField/usecase/TextFieldUpdateStopIndexUseCase"; +import { execute as textFieldSetFocusUseCase } from "./TextField/usecase/TextFieldSetFocusUseCase"; +import { execute as textFieldSetScrollXUseCase } from "./TextField/usecase/TextFieldSetScrollXUseCase"; +import { execute as textFieldSetScrollYUseCase } from "./TextField/usecase/TextFieldSetScrollYUseCase"; +import { execute as textFieldHtmlTextToRawTextUseCase } from "./TextField/usecase/TextFieldHtmlTextToRawTextUseCase"; +import { execute as textFieldGetLineTextUseCase } from "./TextField/usecase/TextFieldGetLineTextUseCase"; +import { execute as textFieldReplaceTextUseCase } from "./TextField/usecase/TextFieldReplaceTextUseCase"; +import { execute as textFieldCopyUseCase } from "./TextField/usecase/TextFieldCopyUseCase"; +import { execute as textFieldPasteService } from "./TextField/service/TextFieldPasteService"; +import { execute as textFieldInsertTextUseCase } from "./TextField/usecase/TextFieldInsertTextUseCase"; +import { execute as textFieldApplyChangesService } from "./TextField/service/TextFieldApplyChangesService"; +import { execute as textFieldSetFocusIndexUseCase } from "./TextField/usecase/TextFieldSetFocusIndexUseCase"; +import { execute as textFieldKeyDownEventUseCase } from "./TextField/usecase/TextFieldKeyDownEventUseCase"; +import { execute as textFieldDeleteTextUseCase } from "./TextField/usecase/TextFieldDeleteTextUseCase"; +import { execute as textFieldSelectAllUseCase } from "./TextField/usecase/TextFieldSelectAllUseCase"; +import { execute as textFieldBuildFromCharacterUseCase } from "./TextField/usecase/TextFieldBuildFromCharacterUseCase"; +import { + $clamp, + $toColorInt +} from "./TextUtil"; +import { + InteractiveObject, + Shape +} from "@next2d/display"; + +/** + * @description TextField クラスは、テキストの表示と入力用の表示オブジェクトを作成するために使用されます。 + * プロパティインスペクターを使用して、テキストフィールドにインスタンス名を付けることができます。 + * また、TextField クラスのメソッドとプロパティを使用して、JavaScript でテキストフィールドを操作できます。 + * + * The TextField class is used to create display objects for text display and input. + * You can give a text field an instance name in the Property inspector + * nd use the methods and properties of the TextField class to manipulate it with JavaScript. + * + * @class + * @memberOf next2d.display + * @extends InteractiveObject + */ +export class TextField extends InteractiveObject +{ + /** + * @description TextFieldの機能を所持しているかを返却 + * Returns whether the display object has TextField functionality. + * + * @type {boolean} + * @readonly + * @public + */ + public readonly isText: boolean; + + /** + * @description セットされたテキストを描画用に分解したデータ + * Data that breaks down the set text for drawing + * + * @member {TextData} + * @protected + */ + public $textData: TextData | null; + + /** + * @description テキストが HTML であるかどうかを示します。 + * Indicates whether the text is HTML. + * + * @member {boolean} + * @private + */ + private _$isHTML: boolean; + + /** + * @description バウンディングボックスのxMin座標 + * Bounding box coordinates + * + * @member {number} + * @public + */ + public xMin: number; + + /** + * @description バウンディングボックスのyMin座標 + * Bounding box coordinates + * + * @member {number} + * @public + */ + public yMin: number; + + /** + * @description バウンディングボックスのxMax座標 + * Bounding box coordinates + * + * @member {number} + * @public + */ + public xMax: number; + + /** + * @description バウンディングボックスのyMax座標 + * Bounding box coordinates + * + * @member {number} + * @public + */ + public yMax: number; + + /** + * @description テキストフィールドの描画範囲のバウンディングボックス + * The bounding box of the drawing area of the text field + * + * @member {IBounds} + * @private + */ + public readonly bounds: IBounds; + + /** + * @description スクロール機能のON/OFFの制御。 + * Control ON/OFF of the scroll function. + * + * @member {boolean} + * @default true + * @public + */ + public scrollEnabled: boolean; + + /** + * @description xスクロールバーの表示用の Shape オブジェクト + * Shape object for x scroll bar display + * + * @member {Shape} + * @protected + */ + public readonly xScrollShape: Shape; + + /** + * @description yスクロールバーの表示用の Shape オブジェクト + * Shape object for y scroll bar display + * + * @member {Shape | null} + * @protected + */ + public readonly yScrollShape: Shape; + + /** + * @description テキストフィールドの点滅線の表示・非表示を制御します。 + * Controls the visibility of the text field's blinking line. + * + * @member {boolean} + * @default false + * @public + */ + public focusVisible: boolean; + + /** + * @description テキストフィールドのフォーカス位置のインデックス + * Index of the focus position of the text field + * + * @member {number} + * @default -1 + * @public + */ + public focusIndex: number; + + /** + * @description テキストフィールドの選択位置のインデックス + * Index of the selected position of the text field + * + * @member {number} + * @default -1 + * @public + */ + public selectIndex: number; + + /** + * @description ユーザーが入力するときに、テキストフィールドに入力できる最大の文字数です。 + * The maximum number of characters that the text field can contain, + * as entered by a user. + * + * @member {number} + * @default 0 + * @public + */ + public maxChars: number; + + /** + * @description ユーザーがテキストフィールドに入力できる文字のセットを指定します。 + * Indicates the set of characters that a user can enter into the text field. + * + * @member {string} + * @default null + * @public + */ + public restrict: string; + + /** + * @description テキストフィールドのタイプです。 + * The type of the text field. + * + * @member {string} + * @default TextFieldType.STATIC + * @public + */ + public type: ITextFieldType; + + /** + * @description テキストフィールドのコンポジション開始インデックス + * Composition start index of the text field + * + * @member {number} + * @default -1 + * @public + */ + public compositionStartIndex: number; + + /** + * @description テキストフィールドのコンポジション終了インデックス + * Composition end index of the text field + * + * @member {number} + * @default -1 + * @public + */ + public compositionEndIndex: number; + + /** + * @description テキストフィールドのテキストフォーマットの配列です。 + * An array of text formats for the text field. + * + * @member {TextFormat[]} + * @default null + * @protected + */ + public $textFormats: TextFormat[] | null; + + /** + * @description x軸のスクロール位置 + * Scroll position on the x-axis + * + * @member {number} + * @default 0 + * @protected + */ + public $scrollX: number; + + /** + * @description y軸のスクロール位置 + * Scroll position on the y-axis + * + * @member {number} + * @default 0 + * @protected + */ + public $scrollY: number; + + /** + * @description ビルドされたキャッシュキー + * Built cache key + * + * @type {number} + * @default 0 + * @public + */ + public cacheKey: number; + + /** + * @description キャッシュのビルドに利用されるパラメータ + * Parameters used to build the cache + * + * @type {number[]} + * @public + */ + public readonly cacheParams: number[]; + + private _$background: boolean; + private _$backgroundColor: number; + private _$border: boolean; + private _$borderColor: number; + private _$htmlText: string; + private _$multiline: boolean; + private _$text: string; + private _$wordWrap: boolean; + private _$defaultTextFormat: TextFormat; + private _$rawHtmlText: string; + private _$autoSize: ITextFieldAutoSize; + private _$autoFontSize: boolean; + private _$focus: boolean; + private _$thickness: number; + private _$thicknessColor: number; + private _$stopIndex: number; + + /** + * @constructor + * @public + */ + constructor () + { + super(); + + this.isText = true; + + /** + * @type {TextData} + * @default null + * @public + */ + this.$textData = null; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$background = false; + + /** + * @type {number} + * @default 0xffffff + * @private + */ + this._$backgroundColor = 0xffffff; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$border = false; + + /** + * @type {number} + * @default 0x000000 + * @private + */ + this._$borderColor = 0x000000; + + /** + * @type {string} + * @default "" + * @private + */ + this._$htmlText = ""; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$multiline = false; + + /** + * @type {string} + * @default "" + * @private + */ + this._$text = ""; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$wordWrap = false; + + /** + * @type {number} + * @default 0 + * @protected + */ + this.$scrollX = 0; + + /** + * @type {number} + * @default 0 + * @protected + */ + this.$scrollY = 0; + + /** + * @type {array} + * @default null + * @protected + */ + this.$textFormats = null; + + /** + * @type {number} + * @default 0 + * @public + */ + this.maxChars = 0; + + /** + * @type {number} + * @default -1 + * @private + */ + this._$stopIndex = -1; + + /** + * @type {number} + * @default -1 + * @public + */ + this.compositionStartIndex = -1; + + /** + * @type {number} + * @default -1 + * @public + */ + this.compositionEndIndex = -1; + + // TextFormat + const textFormat: TextFormat = new TextFormat(); + textFormatSetDefaultService(textFormat); + + /** + * @type {TextFormat} + * @private + */ + this._$defaultTextFormat = textFormat; + + /** + * @type {string} + * @default "" + * @private + */ + this._$rawHtmlText = ""; + + /** + * @type {number} + * @default 0 + * @public + */ + this.xMin = 0; + this.yMin = 0; + this.xMax = 100; + this.yMax = 100; + this.bounds = { + "xMin": 0 , + "xMax": this.xMax, + "yMin": 0 , + "yMax": this.yMax + }; + + /** + * @type {string} + * @default null + * @private + */ + this.restrict = ""; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$isHTML = false; + + /** + * @type {string} + * @default TextFieldAutoSize.NONE + * @private + */ + this._$autoSize = "none"; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$autoFontSize = false; + + /** + * @type {boolean} + * @default true + * @private + */ + this.focusVisible = false; + + /** + * @type {number} + * @default -1 + * @public + */ + this.focusIndex = -1; + + /** + * @type {number} + * @default -1 + * @public + */ + this.selectIndex = -1; + + /** + * @type {boolean} + * @default true + * @public + */ + this.scrollEnabled = true; + + /** + * @type {Shape} + * @public + */ + this.xScrollShape = new Shape(); + this + .xScrollShape + .graphics + .beginFill("#000", 0.3) + .drawRoundRect(0, 0, 3, 3, 3); + + this + .xScrollShape + .scale9Grid = new Rectangle(1.5, 1.5, 0.1, 0.1); + + /** + * @type {Shape} + * @public + */ + this.yScrollShape = new Shape(); + this + .yScrollShape + .graphics + .beginFill("#000", 0.3) + .drawRoundRect(0, 0, 3, 3, 3); + + this + .yScrollShape + .scale9Grid = new Rectangle(1.5, 1.5, 0.1, 0.1); + + /** + * @type {string} + * @default "static" + * @public + */ + this.type = "static"; + + /** + * @type {boolean} + * @default false + * @private + */ + this._$focus = false; + + /** + * @type {number} + * @default 0 + * @private + */ + this._$thickness = 0; + + /** + * @type {number} + * @default 0 + * @private + */ + this._$thicknessColor = 0; + + /** + * @type {array} + * @default 0 + * @public + */ + this.cacheKey = 0; + + /** + * @type {array} + * @public + */ + this.cacheParams = [0, 0, 0]; + } + + /** + * @description 指定されたクラスの空間名を返します。 + * Returns the space name of the specified class. + * + * @return {string} + * @default next2d.display.TextField + * @const + * @static + */ + static get namespace (): string + { + return "next2d.display.TextField"; + } + + /** + * @description 指定されたオブジェクトの空間名を返します。 + * Returns the space name of the specified object. + * + * @return {string} + * @default next2d.display.TextField + * @const + * @public + */ + get namespace (): string + { + return "next2d.display.TextField"; + } + + /** + * @description テキストサイズの自動的な拡大 / 縮小および整列を制御します。 + * Controls automatic sizing and alignment of text size. + * + * @member {boolean} + * @default false + * @public + */ + get autoFontSize (): boolean + { + return this._$autoFontSize; + } + set autoFontSize (auto_font_size: boolean) + { + if (auto_font_size === this._$autoFontSize) { + return ; + } + this._$autoFontSize = !!auto_font_size; + textFieldReloadUseCase(this); + } + + /** + * @description テキストフィールドの自動的な拡大 / 縮小および整列を制御します。 + * Controls automatic sizing and alignment of text fields. + * + * @member {string} + * @default "none" + * @public + */ + get autoSize (): ITextFieldAutoSize + { + return this._$autoSize; + } + set autoSize (auto_size: ITextFieldAutoSize) + { + if (auto_size === this._$autoSize) { + return ; + } + this._$autoSize = auto_size; + textFieldReloadUseCase(this); + } + + /** + * @description テキストフィールドに背景の塗りつぶしがあるかどうかを指定します。 + * Specifies whether the text field has a background fill. + * + * @member {boolean} + * @default false + * @public + */ + get background (): boolean + { + return this._$background; + } + set background (background: boolean) + { + if (background === this._$background) { + return ; + } + this._$background = !!background; + textFieldApplyChangesService(this); + } + + /** + * @description テキストフィールドの背景の色です。 + * The color of the text field background. + * + * @member {number} + * @default 0xffffff + * @public + */ + get backgroundColor (): number + { + return this._$backgroundColor; + } + set backgroundColor (background_color: string | number) + { + background_color = $clamp( + $toColorInt(background_color), 0, 0xffffff, 0xffffff + ); + + if (background_color === this._$backgroundColor) { + return ; + } + + this._$backgroundColor = background_color; + textFieldApplyChangesService(this); + } + + /** + * @description テキストフィールドに境界線があるかどうかを指定します。 + * Specifies whether the text field has a border. + * + * @member {boolean} + * @default false + * @public + */ + get border (): boolean + { + return this._$border; + } + set border (border: boolean) + { + if (border === this._$border) { + return ; + } + + this._$border = !!border; + textFieldApplyChangesService(this); + } + + /** + * @description テキストフィールドの境界線の色です。 + * The color of the text field border. + * + * @member {number} + * @default 0x000000 + * @public + */ + get borderColor (): number + { + return this._$borderColor; + } + set borderColor (border_color: string | number) + { + border_color = $clamp( + $toColorInt(border_color), 0, 0xffffff, 0 + ); + + if (border_color === this._$borderColor) { + return ; + } + + this._$borderColor = border_color; + textFieldApplyChangesService(this); + } + + /** + * @description テキストの任意の表示終了位置の設定 + * Setting an arbitrary display end position for text. + * + * @member {number} + * @default -1 + * @public + */ + get stopIndex (): number + { + return this._$stopIndex; + } + set stopIndex (stop_index: number) + { + stop_index |= 0; + if (this._$stopIndex === stop_index) { + return ; + } + + this._$stopIndex = stop_index; + + textFieldUpdateStopIndexUseCase(this, stop_index); + } + + /** + * @description テキストに適用するフォーマットを指定します。 + * Specifies the formatting to be applied to the text. + * + * @member {TextFormat} + * @public + */ + get defaultTextFormat (): TextFormat + { + return this._$defaultTextFormat.clone(); + } + set defaultTextFormat (text_format: TextFormat) + { + this._$rawHtmlText = ""; + if (this._$isHTML) { + this._$text = ""; + } else { + this._$htmlText = ""; + } + + this._$defaultTextFormat = text_format; + textFieldReloadUseCase(this); + } + + /** + * @description このオブジェクトでマウスまたはその他のユーザー入力メッセージを + * + * @member {boolean} + * @default false + * @public + */ + get focus (): boolean + { + return this._$focus; + } + set focus (focus: boolean) + { + if (this._$focus === focus) { + return ; + } + + if (this.type !== "input") { + return ; + } + + this._$focus = !!focus; + + textFieldSetFocusUseCase( + this, this._$focus ? FocusEvent.FOCUS_IN : FocusEvent.FOCUS_OUT + ); + } + + /** + * @description テキストフィールドの内容を HTML で表します。 + * Contains the HTML representation of the text field contents. + * + * @member {string} + * @default "" + * @public + */ + get htmlText (): string + { + if (this._$htmlText) { + return this._$htmlText; + } + + const style = textFormatHtmlTextGenerateStyleService(this.defaultTextFormat); + this._$htmlText = `${this._$text.replace(/\n/g, "
")}
`; + + return this._$htmlText; + } + set htmlText (html_text: string) + { + if (this._$htmlText === html_text) { + return ; + } + + this._$htmlText = `${html_text}`; + this._$rawHtmlText = ""; + this._$text = ""; + this._$isHTML = true; + textFieldReloadUseCase(this); + } + + /** + * @description テキストフィールド内の文字数です。 + * The int of characters in a text field. + * + * @member {number} + * @readonly + * @public + */ + get length (): number + { + return this.text.length; + } + + /** + * @description フィールドが複数行テキストフィールドであるかどうかを示します。 + * Indicates whether field is a multiline text field. + * + * @member {boolean} + * @default false + * @public + */ + get multiline (): boolean + { + return this._$multiline; + } + set multiline (multiline: boolean) + { + if (multiline === this._$multiline) { + return ; + } + this._$multiline = !!multiline; + textFieldReloadUseCase(this); + } + + /** + * @description フィールドが複数行テキストフィールドであるかどうかを示します。 + * Indicates whether field is a multiline text field. + * + * @member {number} + * @readonly + * @public + */ + get numLines (): number + { + const textData = textFieldGetTextDataUseCase(this); + return textData.lineTable.length; + } + + /** + * @description テキストフィールドのスクロール垂直位置です。 + * The scroll vertical position of the text field. + * + * @member {number} + * @public + */ + get scrollX (): number + { + return this.$scrollX; + } + set scrollX (scroll_x: number) + { + textFieldSetScrollXUseCase(this, scroll_x); + } + + /** + * @description テキストフィールドのスクロール垂直位置です。 + * The scroll vertical position of the text field. + * + * @member {number} + * @public + */ + get scrollY (): number + { + return this.$scrollY; + } + set scrollY (scroll_y: number) + { + textFieldSetScrollYUseCase(this, scroll_y); + } + + /** + * @description テキストフィールド内の現在のテキストであるストリングです。 + * A string that is the current text in the text field. + * + * @member {string} + * @default "" + * @public + */ + get text (): string + { + if (!this._$isHTML) { + return this._$text; + } + + if (!this._$rawHtmlText) { + this._$rawHtmlText = textFieldHtmlTextToRawTextUseCase(this); + } + return this._$rawHtmlText; + } + set text (text: string) + { + text = `${text}`; + if (text === "") { + if (this.scrollX) { + this.scrollX = 0; + } + if (this.scrollY) { + this.scrollY = 0; + } + } + + if (text === this._$text) { + return ; + } + + // reset + this._$htmlText = ""; + this._$rawHtmlText = ""; + this._$isHTML = false; + + this._$text = text; + textFieldReloadUseCase(this); + } + + /** + * @description テキストの高さです(ピクセル単位)。 + * The height of the text in pixels. + * + * @member {number} + * @readonly + * @public + */ + get textHeight (): number + { + const textData = textFieldGetTextDataUseCase(this); + return textData.textHeight; + } + + /** + * @description テキストの幅です(ピクセル単位)。 + * The width of the text in pixels. + * + * @member {number} + * @readonly + * @public + */ + get textWidth () + { + const textData = textFieldGetTextDataUseCase(this); + return textData.textWidth; + } + + /** + * @description 輪郭のテキスト幅です。0(デフォルト値)で無効にできます。 + * The text width of the outline, which can be disabled with 0 (the default value). + * + * @member {number} + * @default 0 + * @public + */ + get thickness (): number + { + return this._$thickness; + } + set thickness (thickness: number) + { + thickness |= 0; + if (thickness === this._$thickness) { + return ; + } + this._$thickness = thickness; + textFieldApplyChangesService(this); + } + + /** + * @description 輪郭のテキストの色です(16 進数形式)。 + * The color of the outline text. (Hexadecimal format) + * + * @member {number} + * @default 0 + * @public + */ + get thicknessColor (): number + { + return this._$thicknessColor; + } + set thicknessColor (thickness_color: number) + { + thickness_color = $clamp( + $toColorInt(thickness_color), 0, 0xffffff, 0 + ); + if (thickness_color === this._$thicknessColor) { + return ; + } + this._$thicknessColor = thickness_color; + textFieldApplyChangesService(this); + } + + /** + * @description テキストフィールドのテキストを折り返すかどうかを示すブール値です。 + * A Boolean value that indicates whether the text field has word wrap. + * + * @member {boolean} + * @default false + * @public + */ + get wordWrap (): boolean + { + return this._$wordWrap; + } + set wordWrap (word_wrap: boolean) + { + if (this._$wordWrap === word_wrap) { + return ; + } + this._$wordWrap = !!word_wrap; + textFieldReloadUseCase(this); + } + + /** + * @description 表示オブジェクトの幅を示します(ピクセル単位)。 + * Indicates the width of the display object, in pixels. + * + * @member {number} + * @public + */ + get width (): number + { + return super.width; + } + set width (width: number) + { + width = +width; + if (!isNaN(width) && 0 > width) { + return ; + } + + const xMax = width + this.bounds.xMin; + if (xMax === this.bounds.xMax) { + return ; + } + + this.bounds.xMax = xMax; + textFieldReloadUseCase(this); + } + + /** + * @description 表示オブジェクトの高さを示します(ピクセル単位)。 + * Indicates the height of the display object, in pixels. + * + * @member {number} + * @public + */ + get height (): number + { + return super.height; + } + set height (height: number) + { + height = +height; + if (!isNaN(height) && 0 > height) { + return ; + } + + const yMax = height + this.bounds.yMin; + if (yMax === this.bounds.yMax) { + return ; + } + + this.bounds.yMax = yMax; + textFieldReloadUseCase(this); + } + + /** + * @description newText パラメーターで指定されたストリングを、 + * テキストフィールドのテキストの最後に付加します。 + * Appends the string specified by the newText parameter + * to the end of the text of the text field. + * + * @param {string} new_text + * @return void + * @method + * @public + */ + appendText (new_text: string): void + { + const currentText = this.text; + this.text = currentText + `${new_text}`; + } + + /** + * @description テキストフィールドのフォーカス位置にテキスtを追加します。 + * Adds text to the focus position of the text field. + * + * @param {string} new_text + * @return {void} + * @method + * @public + */ + insertText (new_text: string): void + { + textFieldInsertTextUseCase(this, new_text); + } + + /** + * @description テキストフィールドの選択範囲を削除します。 + * Deletes the selection range of the text field. + * + * @return {void} + * @method + * @public + */ + deleteText (): void + { + textFieldDeleteTextUseCase(this); + } + + /** + * @description lineIndex パラメーターで指定された行のテキストを返します。 + * Returns the text of the line specified by the lineIndex parameter. + * + * @param {number} line_index + * @return {string} + * @public + */ + getLineText (line_index: number): string + { + return this._$text || this._$htmlText + ? textFieldGetLineTextUseCase(this, line_index | 0) + : ""; + } + + /** + * @description beginIndex パラメーターと endIndex パラメーターで指定された文字範囲を、 + * newText パラメーターの内容に置き換えます。 + * Replaces the range of characters that the beginIndex + * and endIndex parameters specify with the contents of the newText parameter. + * + * @param {string} new_text + * @param {number} begin_index + * @param {number} end_index + * @return {void} + * @method + * @public + */ + replaceText ( + new_text: string, + begin_index: number, + end_index: number + ): void { + textFieldReplaceTextUseCase(this, new_text, begin_index | 0, end_index | 0); + } + + /** + * @return {void} + * @method + * @public + */ + selectAll (): void + { + textFieldSelectAllUseCase(this); + } + + /** + * @description テキストフィールドの選択範囲をコピーします。 + * Copy a selection of text fields. + * + * @return {void} + * @method + * @public + */ + copy (): void + { + textFieldCopyUseCase(this); + } + + /** + * @description コピーしたテキストを選択範囲に貼り付けます。 + * Paste the copied text into the selected range. + * + * @return {void} + * @method + * @public + */ + paste (): void + { + textFieldPasteService(this); + } + + /** + * @description テキストフィールドのフォーカス位置を設定します。 + * Sets the focus position of the text field. + * + * @param {number} stage_x + * @param {number} stage_y + * @param {boolean} [selected=false] + * @return {void} + * @method + * @public + */ + setFocusIndex ( + stage_x: number, + stage_y: number, + selected: boolean = false + ): void { + textFieldSetFocusIndexUseCase(this, stage_x, stage_y, selected); + } + + /** + * @description キーダウンイベントを処理します。 + * Processes the key down event. + * + * @param {KeyboardEvent} event + * @return {void} + * @method + * @public + */ + keyDown (event: KeyboardEvent): void + { + textFieldKeyDownEventUseCase(this, event); + } + + /** + * @description character 情報を元に DisplayObject を構築 + * Build DisplayObject based on character + * + * @param {ICharacter} character + * @param {LoaderInfo} [loader_info=null] + * @return {void} + * @method + * @protected + */ + $sync (character: ICharacter, loader_info: LoaderInfo | null = null): void + { + if (loader_info) { + super.$syncLoaderInfo(loader_info); + } + textFieldBuildFromCharacterUseCase(this, character as ITextFieldCharacter); + } +} \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldApplyChangesService.test.ts b/packages/text/src/TextField/service/TextFieldApplyChangesService.test.ts new file mode 100644 index 00000000..54375d45 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldApplyChangesService.test.ts @@ -0,0 +1,17 @@ +import { execute } from "./TextFieldApplyChangesService"; +import { DisplayObject } from "@next2d/display"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldApplyChangesService.js test", () => +{ + it("execute test case1", () => + { + const displayObject = new DisplayObject(); + + displayObject.changed = false; + expect(displayObject.changed).toBe(false); + + execute(displayObject); + expect(displayObject.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldApplyChangesService.ts b/packages/text/src/TextField/service/TextFieldApplyChangesService.ts new file mode 100644 index 00000000..e39ba760 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldApplyChangesService.ts @@ -0,0 +1,20 @@ +import type { DisplayObject } from "@next2d/display"; + +/** + * @description DisplayObjectの更新フラグを立てる + * Set the update flag of DisplayObject + * + * @param {DisplayObject} display_object + * @return {void} + * @method + * @protected + */ +export const execute = (display_object: D): void => +{ + display_object.changed = true; + + const parent = display_object.parent as unknown as D; + if (parent && !parent.changed) { + execute(parent); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldBlinkingClearTimeoutService.ts b/packages/text/src/TextField/service/TextFieldBlinkingClearTimeoutService.ts new file mode 100644 index 00000000..58edd6c8 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldBlinkingClearTimeoutService.ts @@ -0,0 +1,21 @@ +import { + $getBlinkingTimerId, + $setBlinkingTimerId +} from "../../TextUtil"; + +/** + * @description テキストの点滅のタイマーをクリアします。 + * Clear the text blinking timer. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const timerId = $getBlinkingTimerId(); + if (timerId !== undefined) { + clearTimeout(timerId); + } + $setBlinkingTimerId(void 0); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldCompositionStartService.test.ts b/packages/text/src/TextField/service/TextFieldCompositionStartService.test.ts new file mode 100644 index 00000000..135860f1 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldCompositionStartService.test.ts @@ -0,0 +1,19 @@ +import { execute } from "./TextFieldCompositionStartService"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldCompositionStartService.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.focusIndex = 1; + expect(textField.focusIndex).toBe(1); + expect(textField.compositionStartIndex).toBe(-1); + + execute(textField); + + expect(textField.focusIndex).toBe(1); + expect(textField.compositionStartIndex).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldCompositionStartService.ts b/packages/text/src/TextField/service/TextFieldCompositionStartService.ts new file mode 100644 index 00000000..fa7ae668 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldCompositionStartService.ts @@ -0,0 +1,15 @@ +import type { TextField } from "../../TextField"; + +/** + * @description 選択中のインデックスを記録します。 + * Records the currently selected index. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + text_field.compositionStartIndex = text_field.focusIndex; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldPasteService.test.ts b/packages/text/src/TextField/service/TextFieldPasteService.test.ts new file mode 100644 index 00000000..0b010089 --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldPasteService.test.ts @@ -0,0 +1,47 @@ +import { execute } from "./TextFieldPasteService"; +import { TextField } from "../../TextField"; +import { describe, expect, it, beforeEach, afterAll } from "vitest"; + +describe("TextFieldPasteService.js test", () => +{ + beforeEach(() => { + Object.assign(navigator, { + "clipboard": { + "text": "", + readText () + { + return Promise.resolve(this.text); + }, + writeText (data: string) + { + this.text = data; + return Promise.resolve(); + } + } + }); + }); + + afterAll(() => { + Object.assign(navigator, { "clipboard": undefined }); + }); + + it("execute test case1", async () => + { + await navigator + .clipboard + .writeText("あいうえお\nかきくけこ\nさしすせそ"); + + + + const textField = new TextField(); + textField.multiline = true; + textField.focusIndex = 1; + textField.selectIndex = 99; + + textField.text = "xyz"; + expect(textField.text).toBe("xyz"); + + await execute(textField); + expect(textField.text).toBe("あいうえお\nかきくけこ\nさしすせそ"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/service/TextFieldPasteService.ts b/packages/text/src/TextField/service/TextFieldPasteService.ts new file mode 100644 index 00000000..c39db77c --- /dev/null +++ b/packages/text/src/TextField/service/TextFieldPasteService.ts @@ -0,0 +1,20 @@ +import type { TextField } from "../../TextField"; + +/** + * @description コピーしたテキストをペーストします。 + * Pastes the copied text. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = async (text_field: TextField): Promise => +{ + const text = await navigator.clipboard.readText(); + if (text === "" || text_field.focusIndex === -1) { + return ; + } + + text_field.insertText(text); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts new file mode 100644 index 00000000..17b46d41 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts @@ -0,0 +1,224 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; + +/** + * @description テキストフィールドのフォーカスインデックスを下に移動します。 + * Moves the focus index of the text field down. + * + * @param {TextField} text_field + * @param {boolean} shift_key + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, shift_key: boolean): void => +{ + if (text_field.focusIndex === -1) { + return ; + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + const textObject = textData.textTable[text_field.focusIndex]; + if (!textObject) { + return ; + } + + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + // 最終行の場合は、フォーカスインデックスを最終位置に設定 + if (line === textData.lineTable.length - 1) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + text_field.focusVisible = false; + text_field.focusIndex = textData.textTable.length; + + const width = text_field.width; + const scaleX = (text_field.textWidth - width) / width; + + let scrollX = 0; + for (let idx = 1; text_field.focusIndex >= idx; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject || textObject.line > line) { + break; + } + + if (textObject.line !== line) { + continue; + } + + scrollX += textObject.w; + } + + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX = (scrollX - width) / scaleX; + + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + + const height = text_field.height; + const scaleY = (text_field.textHeight - height) / height; + + let currentHeight = -text_field.scrollY * scaleY - 2; + let endLine = 0; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > height) { + break; + } + + endLine++; + } + + let currentWidth = 2; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (text_field.focusIndex === idx) { + if (textObject.mode === "text") { + currentWidth += textObject.w / 2; + } + break; + } + + if (textObject.line > line) { + break; + } + + if (textObject.line !== line || textObject.mode !== "text") { + continue; + } + + currentWidth += textObject.w; + } + + let textWidth = 2; + const targetLine = line + 1; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (textObject.line > targetLine) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + if (textObject.line >= endLine) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY += textData.heightTable[textObject.line] / scaleY; + } + + text_field.focusVisible = false; + text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; + + if (text_field.scrollX && textWidth < currentWidth) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX = text_field.width * ((textWidth - 2) / text_field.textWidth); + } + + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + + if (textObject.line !== targetLine || textObject.mode !== "text") { + continue; + } + + textWidth += textObject.w; + if (textWidth > currentWidth) { + + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === idx - 1) { + text_field.selectIndex = -1; + } + } + } + + if (textObject.line >= endLine) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY += textData.heightTable[textObject.line] / scaleY; + } + + if (text_field.scrollX) { + const width = text_field.width; + const scaleX = (text_field.textWidth - width) / width; + const scrollWidth = text_field.scrollX * scaleX - 2; + if (scrollWidth > textWidth) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX = text_field.width * ((textWidth - 2) / text_field.textWidth); + } + } + + text_field.focusVisible = false; + text_field.focusIndex = idx; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + } + + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } + } + + text_field.focusVisible = false; + text_field.focusIndex = textData.textTable.length; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts new file mode 100644 index 00000000..eba47692 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts @@ -0,0 +1,118 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; + +/** + * @description テキストフィールドのフォーカスインデックスを左に移動します。 + * Moves the focus index of the text field to the left. + * + * @param {TextField} text_field + * @param {boolean} shift_key + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, shift_key: boolean): void => +{ + if (!text_field.focusIndex) { + return ; + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (textData.textTable.length && text_field.focusIndex < 2) { + text_field.focusIndex = 1; + return ; + } + + // fixed logic + text_field.focusIndex--; + + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + const textObject = textData.textTable[text_field.focusIndex]; + if (textObject) { + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + const height = text_field.height; + const scaleY = (text_field.textHeight - height) / height; + + let currentHeight = -text_field.scrollY * scaleY - 2; + let startLine = 0; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > 0) { + break; + } + + startLine++; + } + + if (startLine > line) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY -= textData.heightTable[textObject.line] / scaleY; + } + + const currentTextObject = textData.textTable[text_field.focusIndex + 1]; + if (currentTextObject) { + const currentLine = currentTextObject.mode === "text" + ? currentTextObject.line + : currentTextObject.line - 1; + + let textWidth = 2; + for (let idx = 1; text_field.focusIndex > idx; ++idx) { + const textObject = textData.textTable[idx]; + if (!textObject || textObject.line > line) { + break; + } + + if (textObject.line !== line) { + continue; + } + + textWidth += textObject.w; + } + + const width = text_field.width; + const scaleX = (text_field.textWidth - width) / width; + + const scrollWidth = text_field.scrollX * scaleX - 2; + if (textWidth > width && currentTextObject && line < currentLine) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + + text_field.scrollX = text_field.width; + return ; + } + + if (scrollWidth > textWidth) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + + text_field.scrollX = text_field.width * ((textWidth - 2) / text_field.textWidth); + return ; + } + } + } + + text_field.focusVisible = false; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts new file mode 100644 index 00000000..f07ced2a --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts @@ -0,0 +1,110 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; + +/** + * @description テキストフィールドのフォーカス位置を右に移動します。 + * Moves the focus position of the text field to the right. + * + * @param {TextField} text_field + * @param {boolean} shift_key + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, shift_key: boolean): void => +{ + const textData = textFieldGetTextDataUseCase(text_field); + if (textData.textTable.length === text_field.focusIndex) { + return ; + } + + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + // fixed logic + text_field.focusVisible = false; + text_field.focusIndex++; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + + const textObject = textData.textTable[text_field.focusIndex]; + if (textObject) { + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + const height = text_field.height; + const scaleY = (text_field.textHeight - height) / height; + + let currentHeight = -text_field.scrollY * scaleY - 2; + let endLine = 0; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > height) { + break; + } + + endLine++; + } + + if (line >= endLine) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY += textData.heightTable[line] / scaleY; + } + + const currentTextObject = textData.textTable[text_field.focusIndex - 1]; + const currentLine = currentTextObject.mode === "text" + ? currentTextObject.line + : currentTextObject.line - 1; + + if (currentTextObject && line > currentLine) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX = 0; + return ; + } + + const width = text_field.width; + const scaleX = (text_field.textWidth - width) / width; + + let limitWidth = text_field.scrollX * scaleX - 2 + width; + for (let idx = 1; text_field.focusIndex >= idx; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject || textObject.line > line) { + break; + } + + if (textObject.line !== line) { + continue; + } + + limitWidth -= textObject.w; + if (limitWidth > 0) { + continue; + } + + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX += textObject.w / scaleX; + break; + } + + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts new file mode 100644 index 00000000..55d99c12 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts @@ -0,0 +1,179 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; + +/** + * @description テキストフィールドのフォーカスインデックスを上に移動します。 + * Moves the focus index of the text field up. + * + * @param {TextField} text_field + * @param {boolean} shift_key + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, shift_key: boolean): void => +{ + if (text_field.focusIndex === -1) { + return ; + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + const index = textData.textTable.length === text_field.focusIndex + ? text_field.focusIndex - 1 + : text_field.focusIndex; + + const textObject = textData.textTable[index]; + if (!textObject || !textObject.line) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + + text_field.focusVisible = false; + text_field.focusIndex = 1; + text_field.scrollX = 0; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + const height = text_field.height; + const scaleY = (text_field.textHeight - height) / height; + + let startLine = 0; + let currentHeight = -text_field.scrollY * scaleY - 2; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > 0) { + break; + } + + startLine++; + } + + let currentWidth = 2; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (text_field.focusIndex === idx) { + if (textObject.mode === "text") { + currentWidth += textObject.w / 2; + } + break; + } + + if (textObject.line > line) { + break; + } + + if (textObject.line !== line || textObject.mode !== "text") { + continue; + } + + currentWidth += textObject.w; + } + + let textWidth = 2; + const targetLine = line - 1; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (textObject.line > targetLine) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + if (startLine >= textObject.line) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY -= textData.heightTable[textObject.line] / scaleY; + } + + if (text_field.scrollX) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX = text_field.width * ((textWidth - 2) / text_field.textWidth); + } + + // fixed logic + text_field.focusVisible = false; + text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + + if (textObject.line !== targetLine || textObject.mode !== "text") { + continue; + } + + textWidth += textObject.w; + if (textWidth > currentWidth) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex - 1; + } else { + if (text_field.selectIndex === idx + 1) { + text_field.selectIndex = -1; + } + } + } + + if (startLine > textObject.line) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + text_field.scrollY -= textData.heightTable[textObject.line] / scaleY; + } + + // fixed logic + text_field.focusVisible = false; + text_field.focusIndex = idx; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); + return ; + } + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.test.ts new file mode 100644 index 00000000..41893d4e --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.test.ts @@ -0,0 +1,26 @@ +import { execute } from "./TextFieldBlinkingUseCase"; +import { TextField } from "../../TextField"; +import { $getBlinkingTimerId } from "../../TextUtil"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldBlinkingUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.changed = false; + textField.focusVisible = false; + + expect(textField.changed).toBe(false); + expect(textField.focusVisible).toBe(false); + expect($getBlinkingTimerId()).toBe(undefined); + + execute(textField); + + expect(textField.changed).toBe(true); + expect(textField.focusVisible).toBe(true); + + clearTimeout($getBlinkingTimerId()); + + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.ts b/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.ts new file mode 100644 index 00000000..35b4e8b5 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldBlinkingUseCase.ts @@ -0,0 +1,26 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; +import { execute as textAreaMovePositionService } from "../../TextArea/service/TextAreaMovePositionService"; +import { $setBlinkingTimerId } from "../../TextUtil"; + +/** + * @description テキストの点滅を実行します。 + * Execute text blinking. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + // update + text_field.focusVisible = !text_field.focusVisible; + textFieldApplyChangesService(text_field); + + // next timer + $setBlinkingTimerId(setTimeout(() => execute(text_field), 500)); + + // TextArea move position + textAreaMovePositionService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldBuildFromCharacterUseCase.ts b/packages/text/src/TextField/usecase/TextFieldBuildFromCharacterUseCase.ts new file mode 100644 index 00000000..098a8059 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldBuildFromCharacterUseCase.ts @@ -0,0 +1,76 @@ +import type { TextField } from "../../TextField"; +import type { ITextFieldCharacter } from "../../interface/ITextFieldCharacter"; + +/** + * @description characterを元にTextFieldを構築 + * Build TextField based on character + * + * @param {TextField} text_field + * @param {ITextFieldCharacter} character + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, character: ITextFieldCharacter): void => +{ + const textFormat = text_field.defaultTextFormat; + textFormat.font = character.font; + textFormat.size = character.size; + textFormat.align = character.align; + textFormat.color = character.color; + textFormat.leading = character.leading; + textFormat.letterSpacing = character.letterSpacing; + textFormat.leftMargin = character.leftMargin; + textFormat.rightMargin = character.rightMargin; + + switch (character.fontType) { + + case 1: + textFormat.bold = true; + break; + + case 2: + textFormat.italic = true; + break; + + case 3: + textFormat.bold = true; + textFormat.italic = true; + break; + + } + + // update textFormat + text_field.defaultTextFormat = textFormat; + + // setup params + text_field.type = character.inputType; + text_field.multiline = character.multiline; + text_field.wordWrap = character.wordWrap; + text_field.border = character.border; + text_field.scrollEnabled = character.scroll; + text_field.thickness = character.thickness; + text_field.thicknessColor = character.thicknessColor; + + switch (character.autoSize) { + + case 1: + text_field.autoSize = character.align; + break; + + case 2: + text_field.autoFontSize = true; + break; + + default: + break; + + } + + text_field.xMin = text_field.bounds.xMin = character.bounds.xMin; + text_field.xMax = text_field.bounds.xMax = character.bounds.xMax; + text_field.yMin = text_field.bounds.yMin = character.bounds.yMin; + text_field.yMax = text_field.bounds.yMax = character.bounds.yMax + 4; + + text_field.text = character.text; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldCompositionEndUseCase.ts b/packages/text/src/TextField/usecase/TextFieldCompositionEndUseCase.ts new file mode 100644 index 00000000..ed30af0c --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldCompositionEndUseCase.ts @@ -0,0 +1,40 @@ +import type { TextField } from "../../TextField"; +import { $textArea } from "../../TextUtil"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; + +/** + * @description テキストフィールドのコンポジションエンドイベントを処理します。 + * Processes the composition end event of the text field. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + if (text_field.compositionEndIndex > -1) { + + const textData = textFieldGetTextDataUseCase(text_field); + for (let idx = text_field.compositionStartIndex; idx < text_field.compositionEndIndex; ++idx) { + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + textObject.textFormat.underline = false; + } + + text_field.focusIndex = text_field.compositionEndIndex; + } + + $textArea.blur(); + $textArea.value = ""; + + if (text_field.focus) { + $textArea.focus(); + } + + text_field.selectIndex = -1; + text_field.compositionStartIndex = -1; + text_field.compositionEndIndex = -1; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldCompositionUpdateUseCase.ts b/packages/text/src/TextField/usecase/TextFieldCompositionUpdateUseCase.ts new file mode 100644 index 00000000..f5ce1a1a --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldCompositionUpdateUseCase.ts @@ -0,0 +1,138 @@ +import type { TextField } from "../../TextField"; +import { TextFormat } from "../../TextFormat"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; +import { execute as textFieldDeleteTextUseCase } from "../../TextField/usecase/TextFieldDeleteTextUseCase"; + +/** + * @description 新しい文字が入力されたときのイベント処理関数 + * Event processing function when a new character is entered + * + * @param {TextField} text_field + * @param {string} texts + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, texts: string): void => +{ + if (text_field.compositionEndIndex > -1) { + const cacheIndex = text_field.compositionStartIndex; + text_field.focusIndex = text_field.compositionStartIndex; + text_field.selectIndex = text_field.compositionEndIndex - 1; + + text_field.compositionStartIndex = -1; + textFieldDeleteTextUseCase(text_field); + + // reset + text_field.compositionStartIndex = cacheIndex; + text_field.selectIndex = -1; + } + + let textData = textFieldGetTextDataUseCase(text_field); + const textFormats: TextFormat[] = []; + + const length = texts.length; + let newText = ""; + if (2 > textData.textTable.length) { + newText = texts; + text_field.focusIndex = 1; + text_field.compositionStartIndex = 1; + } else { + + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (text_field.compositionStartIndex === idx) { + for (let idx = 0; idx < length; ++idx) { + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += texts[idx]; + } + } + + switch (textObject.mode) { + + case "break": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += "\n"; + break; + + case "text": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += textObject.text; + break; + + default: + continue; + + } + } + + // last text + if (text_field.compositionStartIndex === textData.textTable.length) { + + const textObject = textData.textTable[text_field.compositionStartIndex - 1]; + if (!textObject) { + return ; + } + + for (let idx = 0; idx < length; ++idx) { + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += texts[idx]; + } + } + } + + // update + if (textFormats.length) { + text_field.$textFormats = textFormats; + } + + // fixed logic + text_field.text = newText; + + // fixed logic + text_field.$textFormats = null; + + textData = textFieldGetTextDataUseCase(text_field); + let index = text_field.compositionStartIndex + length; + for (let idx = text_field.compositionStartIndex; idx < index; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + break; + } + + textObject.textFormat.underline = true; + if (textObject.mode !== "wrap") { + continue; + } + + if (idx === text_field.compositionStartIndex) { + + let subIndex = 1; + for (;;) { + const textObject = textData.textTable[idx - subIndex]; + if (!textObject) { + break; + } + + if (textObject.mode === "text") { + textObject.textFormat.underline = true; + break; + } + + subIndex++; + } + } + + if (idx > text_field.compositionStartIndex) { + index++; + } + } + + text_field.compositionEndIndex = text_field.focusIndex = index; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts new file mode 100644 index 00000000..0e4bfc57 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts @@ -0,0 +1,90 @@ +import { execute } from "./TextFieldCopyUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it, beforeEach, afterAll } from "vitest"; + +describe("TextFieldCopyUseCase.js test", () => +{ + beforeEach(() => { + Object.assign(navigator, { + "clipboard": { + "text": "", + readText () + { + return Promise.resolve(this.text); + }, + writeText (data: string) + { + this.text = data; + return Promise.resolve(); + } + } + }); + }); + + afterAll(() => { + Object.assign(navigator, { "clipboard": undefined }); + }); + + it("execute test case1", async () => + { + const textField = new TextField(); + textField.focusIndex = 11; + textField.selectIndex = 15; + + textField.htmlText = ` +

line1

+

line2

+

line3

+`; + + await execute(textField); + + navigator + .clipboard + .readText().then((text) => + { + expect(text).toBe("line3"); + }); + }); + + it("execute test case2", async () => + { + const textField = new TextField(); + textField.focusIndex = 6; + textField.selectIndex = 10; + + textField.text = `あいうえお +かきくけこ +さしすせそ`; + + await execute(textField); + + navigator + .clipboard + .readText().then((text) => + { + expect(text).toBe("かきくけこ"); + }); + }); + + it("execute test case3", async () => + { + const textField = new TextField(); + textField.multiline = true; + textField.focusIndex = 1; + textField.selectIndex = 99; + + textField.text = `あいうえお +かきくけこ +さしすせそ`; + + await execute(textField); + + navigator + .clipboard + .readText().then((text) => + { + expect(text).toBe("あいうえお\nかきくけこ\nさしすせそ"); + }); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldCopyUseCase.ts b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.ts new file mode 100644 index 00000000..ed9f92f9 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.ts @@ -0,0 +1,51 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description テキストフィールドの選択範囲のテキストを返却します。 + * Returns the text of the selection in the text field. + * + * @param {TextField} text_field + * @return {Promise} + * @method + * @protected + */ +export const execute = async (text_field: TextField): Promise => +{ + if (text_field.focusIndex === -1 || text_field.selectIndex === -1) { + return ; + } + + let text = ""; + + const minIndex = Math.min(text_field.focusIndex, text_field.selectIndex); + const maxIndex = Math.max(text_field.focusIndex, text_field.selectIndex) + 1; + + const textData = textFieldGetTextDataUseCase(text_field); + for (let idx = minIndex; idx < maxIndex; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + switch (textObject.mode) { + + case "text": + text += textObject.text; + break; + + case "break": + text += "\n"; + break; + + default: + break; + + } + } + + await navigator + .clipboard + .writeText(text); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.test.ts new file mode 100644 index 00000000..229ee970 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.test.ts @@ -0,0 +1,33 @@ +import { execute } from "./TextFieldDeleteTextUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldDeleteTextUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + textField.text = "あいうえおかきくけこさしすせそ"; + textField.focusIndex = 6; + textField.selectIndex = 10; + + execute(textField); + + expect(textField.text).toBe("あいうえおさしすせそ"); + }); + + it("execute test case2", () => + { + const textField = new TextField(); + + textField.text = "あいうえおかきくけこさしすせそ"; + textField.compositionStartIndex = -1; + textField.focusIndex = 0; + textField.selectIndex = 15; + + execute(textField); + + expect(textField.text).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.ts new file mode 100644 index 00000000..fcc6549c --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldDeleteTextUseCase.ts @@ -0,0 +1,140 @@ +import type { TextField } from "../../TextField"; +import { TextFormat } from "../../TextFormat"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; + +/** + * @description 選択中のテキストを削除します。 + * Deletes the selected text. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + if (text_field.compositionStartIndex > -1) { + return ; + } + + let minIndex = 0; + let maxIndex = 0; + if (text_field.selectIndex > -1) { + minIndex = Math.min(text_field.focusIndex, text_field.selectIndex); + maxIndex = Math.max(text_field.focusIndex, text_field.selectIndex) + 1; + text_field.focusIndex = minIndex; + } else { + if (2 > text_field.focusIndex) { + return ; + } + + text_field.focusIndex--; + } + + const textData = textFieldGetTextDataUseCase(text_field); + const textObject = textData.textTable[text_field.focusIndex]; + if (textObject && textObject.mode === "wrap") { + text_field.focusIndex--; + } + + const textFormats: TextFormat[] = []; + + let newText = ""; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (text_field.focusIndex === idx + || minIndex <= idx && maxIndex > idx + ) { + continue; + } + + switch (textObject.mode) { + + case "break": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += "\n"; + break; + + case "text": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += textObject.text; + break; + + default: + continue; + + } + } + + if (textData.textTable.length === text_field.focusIndex) { + textFormats.pop(); + newText = newText.slice(0, -1); + } + + text_field.selectIndex = -1; + if (!newText) { + // reset + text_field.text = ""; + text_field.focusIndex = 1; + } else { + + const beforeTextWidth = text_field.textWidth; + const beforeTextHeight = text_field.textHeight; + + // fixed logic + text_field.$textFormats = textFormats; + + // fixed logic + text_field.text = newText; + + if (text_field.scrollX > 0) { + const textWidth = text_field.textWidth; + const width = text_field.width; + + switch (true) { + + case width > textWidth: + text_field.$scrollX = 0; + break; + + case beforeTextWidth !== textWidth: + text_field.$scrollX -= (beforeTextWidth - textWidth) + / (textWidth / width); + break; + + default: + break; + + } + } + + if (text_field.scrollY > 0) { + const textHeight = text_field.textHeight; + const height = text_field.height; + + switch (true) { + + case height > textHeight: + text_field.$scrollY = 0; + break; + + case beforeTextHeight !== textHeight: + text_field.$scrollY -= (beforeTextHeight - textHeight) + / (textHeight / height); + break; + + default: + break; + + } + } + + // fixed logic + text_field.$textFormats = null; + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.test.ts new file mode 100644 index 00000000..d4023f28 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.test.ts @@ -0,0 +1,19 @@ +import { execute } from "./TextFieldGetLineTextUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldGetLineTextUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.multiline = true; + + textField.htmlText = ` +

line1

+

line2

+

line3

+`; + expect(execute(textField, 2)).toBe("line2"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.ts new file mode 100644 index 00000000..a065acee --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldGetLineTextUseCase.ts @@ -0,0 +1,42 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description 指定された行のテキストを返します。 + * Returns the text of a given line. + * + * @param {TextField} text_field + * @param {number} index + * @return {string} + * @method + * @protected + */ +export const execute = (text_field: TextField, index: number): string => +{ + let text: string = ""; + + const textData = textFieldGetTextDataUseCase(text_field); + for (let idx = 1; idx < textData.textTable.length; idx++) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (textObject.line > index) { + break; + } + + if (textObject.line !== index) { + continue; + } + + if (textObject.mode !== "text") { + continue; + } + + text += textObject.text; + } + + return text; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.test.ts new file mode 100644 index 00000000..8f7bb5fa --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.test.ts @@ -0,0 +1,26 @@ +import { execute } from "./TextFieldGetTextDataUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldGetTextDataUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + expect(textField.$textData).toBe(null); + + const value = "test"; + textField.text = value; + execute(textField); + + const textTable = textField.$textData?.textTable; + if (!textTable) { + throw new Error("textTable is null"); + } + + for (let idx = 0; idx < 4; ++idx) { + expect(textTable[idx + 1].text).toBe(value[idx]); + } + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.ts b/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.ts new file mode 100644 index 00000000..44897a0e --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldGetTextDataUseCase.ts @@ -0,0 +1,39 @@ +import type { TextData } from "../../TextData"; +import type { TextField } from "../../TextField"; +import { execute as textParserParseHtmlTextUseCase } from "../../TextParser/usecase/TextParserParseHtmlTextUseCase"; + +/** + * @description テキスト情報を元に描画用のデータを生成して返します。 + * 生成済みのデータがある場合は、そのデータを返します。 + * Generates and returns data for drawing based on text information. + * If data has already been generated, that data is returned. + * + * @param {TextField} text_field + * @param {number} sub_font_size + * @return {TextData} + * @method + * @protected + */ +export const execute = ( + text_field: TextField, + sub_font_size: number = 0 +): TextData => { + + if (text_field.$textData) { + return text_field.$textData; + } + + text_field.$textData = textParserParseHtmlTextUseCase( + text_field.htmlText, + text_field.defaultTextFormat, + { + "width": text_field.width, + "multiline": text_field.multiline, + "wordWrap": text_field.wordWrap, + "subFontSize": sub_font_size, + "textFormats": text_field.$textFormats + } + ); + + return text_field.$textData; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.test.ts new file mode 100644 index 00000000..53fa4c44 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.test.ts @@ -0,0 +1,13 @@ +import { execute } from "./TextFieldHtmlTextToRawTextUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldHtmlTextToRawTextUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.htmlText = "

test

"; + expect(execute(textField)).toBe("test"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.ts new file mode 100644 index 00000000..acd01a59 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldHtmlTextToRawTextUseCase.ts @@ -0,0 +1,41 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description HTMLテキストをプレーンテキストに変換します。 + * Convert HTML text to plain text. + * + * @param {TextField} text_field + * @return {string} + * @method + * @protected + */ +export const execute = (text_field: TextField): string => +{ + let text = ""; + const textData = textFieldGetTextDataUseCase(text_field); + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + switch (textObject.mode) { + + case "text": + text += textObject.text; + break; + + case "break": + text += "\r"; + break; + + default: + continue; + + } + } + + return text; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts new file mode 100644 index 00000000..1bf97683 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts @@ -0,0 +1,102 @@ +import type { TextField } from "../../TextField"; +import { TextFormat } from "../../TextFormat"; +import { $textArea } from "../../TextUtil"; +import { execute as textFieldDeleteTextUseCase } from "../../TextField/usecase/TextFieldDeleteTextUseCase"; +import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; + +/** + * @description TextField にテキストを挿入します。 + * Inserts text into the TextField. + * + * @param {TextField} text_field + * @param {string} texts + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, texts: string): void => +{ + if (text_field.focusIndex === -1 + || text_field.compositionStartIndex > -1 + ) { + return ; + } + + if (text_field.selectIndex > -1) { + textFieldDeleteTextUseCase(text_field); + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + text_field.focusIndex = 2; + text_field.appendText(texts); + return ; + } + + const textFormats: TextFormat[] = []; + + let newText = ""; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + if (text_field.focusIndex === idx) { + for (let idx = 0; idx < texts.length; ++idx) { + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += texts[idx]; + } + } + + switch (textObject.mode) { + + case "break": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += "\n"; + break; + + case "text": + textFormats.push(new TextFormat(...Object.values(textObject.textFormat))); + newText += textObject.text; + break; + + default: + continue; + + } + } + + if (textData.textTable.length === text_field.focusIndex) { + + let textFormat: TextFormat; + if (textData.textTable.length) { + const textObject = textData.textTable[textData.textTable.length - 1]; + + textFormat = new TextFormat(...Object.values(textObject.textFormat)); + } else { + textFormat = text_field.defaultTextFormat; + text_field.focusIndex++; + } + + for (let idx = 0; idx < texts.length; ++idx) { + textFormats.push(new TextFormat(...Object.values(textFormat.toObject()))); + newText += texts[idx]; + } + } + + // fixed logic + text_field.$textFormats = textFormats; + + // fixed logic + text_field.text = newText; + + // fixed logic + text_field.$textFormats = null; + + text_field.focusIndex += texts.length; + text_field.selectIndex = -1; + + $textArea.value = ""; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts b/packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts new file mode 100644 index 00000000..cb48cc47 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts @@ -0,0 +1,75 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldArrowDownUseCase } from "../../TextField/usecase/TextFieldArrowDownUseCase"; +import { execute as textFieldArrowUpUseCase } from "../../TextField/usecase/TextFieldArrowUpUseCase"; +import { execute as textFieldArrowLeftUseCase } from "../../TextField/usecase/TextFieldArrowLeftUseCase"; +import { execute as textFieldArrowRightUseCase } from "../../TextField/usecase/TextFieldArrowRightUseCase"; + +/** + * @description テキストフィールドのキーボードダウンイベントを実行する + * Execute the keyboard down event of the text field + * + * @param {TextField} text_field + * @param {KeyboardEvent} event + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, event: KeyboardEvent): void => +{ + if (text_field.focusIndex === -1) { + return ; + } + + switch (event.key) { + + case "Backspace": + case "Delete": + text_field.deleteText(); + break; + + case "Enter": + text_field.insertText("\n"); + break; + + case "ArrowLeft": + textFieldArrowLeftUseCase(text_field, event.shiftKey); + break; + + case "ArrowRight": + textFieldArrowRightUseCase(text_field, event.shiftKey); + break; + + case "ArrowUp": + textFieldArrowUpUseCase(text_field, event.shiftKey); + break; + + case "ArrowDown": + textFieldArrowDownUseCase(text_field, event.shiftKey); + break; + + case "a": + if (event.metaKey || event.ctrlKey) { + event.preventDefault(); + text_field.selectAll(); + } + break; + + case "c": + if (event.metaKey || event.ctrlKey) { + event.preventDefault(); + text_field.copy(); + } + break; + + case "v": + if (event.metaKey || event.ctrlKey) { + event.preventDefault(); + text_field.paste(); + } + break; + + default: + break; + + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldReloadUseCase.ts b/packages/text/src/TextField/usecase/TextFieldReloadUseCase.ts new file mode 100644 index 00000000..65251cdf --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldReloadUseCase.ts @@ -0,0 +1,31 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldResetUseCase } from "./TextFieldResetUseCase"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; +import { execute as textFieldResizeUseCase } from "./TextFieldResizeUseCase"; +import { execute as textFieldResizeAutoFontSizeUseCase } from "./TextFieldResizeAutoFontSizeUseCase"; + +/** + * @description テキストフィールドの描画生成情報を再計算します + * Recalculates the drawing generation information of the text field + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + // 初期化 + textFieldResetUseCase(text_field); + + // 描画情報を生成 + textFieldGetTextDataUseCase(text_field); + + // テキストフィールドの自動フォントサイズ設定がonならば、自動フォントサイズを適用する + if (text_field.autoSize === "none" && text_field.autoFontSize) { + textFieldResizeAutoFontSizeUseCase(text_field); + } + + // テキストエリアをリサイズ + textFieldResizeUseCase(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.test.ts new file mode 100644 index 00000000..dbcff53e --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./TextFieldReplaceTextUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldReplaceTextUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.text = "abc"; + + execute(textField, "xyz", 0, 0) + expect(textField.text).toBe("xyzabc"); + }); + + it("execute test case2", () => + { + const textField = new TextField(); + textField.text = "abc"; + + execute(textField, "xyz", 2, 3) + expect(textField.text).toBe("abxyz"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.ts new file mode 100644 index 00000000..46043457 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldReplaceTextUseCase.ts @@ -0,0 +1,35 @@ +import type { TextField } from "../../TextField"; + +/** + * @description 指定した範囲の文字列を置き換える + * Replaces strings in the specified range + * + * @param {TextField} text_filed + * @param {string} new_text + * @param {number} begin_index + * @param {number} end_index + * @return {void} + * @method + * @protected + */ +export const execute = ( + text_filed: TextField, + new_text: string, + begin_index: number, + end_index: number +): void => { + + if (0 > begin_index || begin_index > end_index) { + return ; + } + + const text = text_filed.text; + + const beginText = begin_index + ? text.slice(0, begin_index) + : ""; + + text_filed.text = beginText + + new_text + + text.slice(end_index, text.length); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResetUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldResetUseCase.test.ts new file mode 100644 index 00000000..82f321b3 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResetUseCase.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./TextFieldResetUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; +import { TextData } from "../../TextData"; + +describe("TextFieldResetUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + textField.changed = false; + expect(textField.changed).toBe(false); + + textField.$textData = new TextData(); + expect(textField.$textData !== null).toBe(true); + + execute(textField); + expect(textField.$textData).toBe(null); + expect(textField.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResetUseCase.ts b/packages/text/src/TextField/usecase/TextFieldResetUseCase.ts new file mode 100644 index 00000000..54ea99b2 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResetUseCase.ts @@ -0,0 +1,25 @@ +import type { TextField } from "../../TextField"; +import { $cacheStore } from "@next2d/cache"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; + +/** + * @description 指定したテキストフィールドのテキストデータをリセットします。 + * Resets the text data in the specified text field. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + text_field.$textData = null; + + textFieldApplyChangesService(text_field); + + // Remove cache + if (text_field.uniqueKey !== "" && $cacheStore.has(text_field.uniqueKey)) { + $cacheStore.removeById(text_field.uniqueKey); + $cacheStore.$removeIds.push(+text_field.uniqueKey); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.test.ts new file mode 100644 index 00000000..d3cdd657 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./TextFieldResizeAutoFontSizeUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldResizeAutoFontSizeUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + expect(textField.defaultTextFormat.size).toBe(12); + + textField.text = "かきくけこ"; + textField.changed = false; + + textField.xMax = 5; + textField.yMax = 5; + + expect(textField.changed).toBe(false); + + execute(textField); + + const textFormat = textField.$textData?.textTable[1].textFormat; + if (!textFormat) { + throw new Error("textFormat is null"); + } + + expect(textFormat.size).toBe(1); + expect(textField.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.ts b/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.ts new file mode 100644 index 00000000..0d77b05c --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResizeAutoFontSizeUseCase.ts @@ -0,0 +1,49 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; +import { execute as textFieldResetUseCase } from "./TextFieldResetUseCase"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; + +/** + * @description 現在の TextField のエリア内に収まる最大のフォントサイズを計算し、描画情報を生成します + * Calculates the maximum font size that will fit within the current TextField area and generates drawing information + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + let maxFontSize = 0; + const textData = textFieldGetTextDataUseCase(text_field); + for (let idx = 0; idx < textData.textTable.length; ++idx) { + const textObject = textData.textTable[idx]; + maxFontSize = Math.max(maxFontSize, textObject.textFormat.size || 0); + } + + let subSize = 1; + if (text_field.width && text_field.textWidth) { + + while (maxFontSize > subSize + && text_field.textWidth + 4 > text_field.width + ) { + textFieldResetUseCase(text_field); + textFieldGetTextDataUseCase(text_field, subSize++); + } + + } + + if (text_field.height && text_field.textHeight) { + + while (maxFontSize > subSize + && text_field.textHeight + 4 > text_field.height + ) { + textFieldResetUseCase(text_field); + textFieldGetTextDataUseCase(text_field, subSize++); + } + + } + + // Apply changes + textFieldApplyChangesService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResizeUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldResizeUseCase.test.ts new file mode 100644 index 00000000..89672dde --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResizeUseCase.test.ts @@ -0,0 +1,49 @@ +import { execute } from "./TextFieldResizeUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldResizeUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + textField.changed = false; + expect(textField.changed).toBe(false); + + textField.xMax = 200; + textField.yMax = 200; + expect(textField.xMin).toBe(0); + expect(textField.yMin).toBe(0); + expect(textField.xMax).toBe(200); + expect(textField.yMax).toBe(200); + + execute(textField); + + expect(textField.xMin).toBe(0); + expect(textField.yMin).toBe(0); + expect(textField.xMax).toBe(100); + expect(textField.yMax).toBe(100); + expect(textField.changed).toBe(true); + }); + + it("execute test case2", () => + { + const textField = new TextField(); + textField.autoSize = "left"; + + textField.xMax = 200; + textField.yMax = 200; + expect(textField.xMin).toBe(0); + expect(textField.yMin).toBe(0); + expect(textField.xMax).toBe(200); + expect(textField.yMax).toBe(200); + + execute(textField); + + expect(textField.xMin).toBe(0); + expect(textField.yMin).toBe(0); + expect(textField.xMax).toBe(4); + expect(textField.yMax).toBe(4); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldResizeUseCase.ts b/packages/text/src/TextField/usecase/TextFieldResizeUseCase.ts new file mode 100644 index 00000000..3efa8dab --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldResizeUseCase.ts @@ -0,0 +1,64 @@ +import type { TextField } from "../../TextField"; +import { execute as textFormatGetWidthMarginService } from "../../TextFormat/service/TextFormatGetWidthMarginService"; +import { execute as textFieldApplyChangesService } from "../../TextField/service/TextFieldApplyChangesService"; + +/** + * @description 設定に合わせてテキストフィールドのサイズを変更します + * Resize the text field to fit your settings + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + // apply changes + textFieldApplyChangesService(text_field); + + // update bounds + if (text_field.autoSize === "none") { + text_field.xMin = text_field.bounds.xMin; + text_field.yMin = text_field.bounds.yMin; + text_field.xMax = text_field.bounds.xMax; + text_field.yMax = text_field.bounds.yMax; + return ; + } + + const textFormat = text_field.defaultTextFormat; + + // 4は左右の1pxのボーダーと1pxのマージンの合計値 + const width = text_field.textWidth + 4 + textFormatGetWidthMarginService(textFormat); + if (text_field.wordWrap) { + + text_field.xMin = text_field.bounds.xMin; + text_field.xMax = text_field.bounds.xMax; + + } else { + + switch (text_field.autoSize) { + + case "left": + text_field.xMax = width + text_field.xMin; + break; + + case "center": + text_field.xMax = width + text_field.xMin; + break; + + case "right": + text_field.xMax = text_field.bounds.xMax + - (text_field.bounds.xMax - text_field.bounds.xMin + - (width - text_field.bounds.xMin)); + break; + + default: + break; + + } + + } + + // 4は上下の1pxのボーダーと1pxのマージンの合計値 + text_field.yMax = text_field.textHeight + text_field.bounds.yMin + 4; +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts new file mode 100644 index 00000000..1b0cd6c4 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts @@ -0,0 +1,24 @@ +import type { TextField } from "@next2d/text"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description テキストフィールドの全選択を実行する + * Execute the text field's select all + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + text_field.selectIndex = 1; + text_field.focusIndex = textData.textTable.length; + textFieldApplyChangesService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSelectedFocusMoveUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSelectedFocusMoveUseCase.ts new file mode 100644 index 00000000..d30a29ef --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSelectedFocusMoveUseCase.ts @@ -0,0 +1,119 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description テキストフィールドの選択中のテキスト位置にスクロールを移動します。 + * Move the scroll to the selected text position in the text field. + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + if (text_field.selectIndex === -1) { + return ; + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + const textObject = textData.textTable[text_field.selectIndex]; + if (!textObject) { + return ; + } + + const line = textObject.mode === "text" + ? textObject.line + : textObject.line - 1; + + const height = text_field.height; + const scaleY = (text_field.textHeight - height) / height; + + let currentHeight = -text_field.scrollY * scaleY - 2; + + let endLine = 0; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > height) { + break; + } + + endLine++; + } + + currentHeight = -text_field.scrollY * scaleY - 2; + let startLine = 0; + for (let idx = 0; idx < textData.heightTable.length; ++idx) { + + currentHeight += textData.heightTable[idx]; + if (currentHeight > 0) { + break; + } + + startLine++; + } + + if (startLine >= textObject.line) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + + text_field.scrollY -= textData.heightTable[textObject.line] / scaleY; + } + + if (textObject.line >= endLine) { + if (text_field.xScrollShape.hasLocalVariable("job")) { + text_field.xScrollShape.deleteLocalVariable("job"); + } + + text_field.scrollY += textData.heightTable[textObject.line] / scaleY; + } + + const width = text_field.width; + const scaleX = (text_field.textWidth - width) / width; + + let textWidth = 2; + let limitWidth = text_field.scrollX * scaleX - 2 + width; + for (let idx = 1; text_field.selectIndex >= idx; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject || textObject.line > line) { + break; + } + + if (textObject.line !== line) { + continue; + } + + if (text_field.selectIndex !== idx) { + textWidth += textObject.w; + } + + limitWidth -= textObject.w; + if (limitWidth > 0) { + continue; + } + + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + text_field.scrollX += textObject.w / scaleX; + + break; + } + + const scrollWidth = text_field.scrollX * scaleX - 2; + if (scrollWidth > textWidth) { + if (text_field.yScrollShape.hasLocalVariable("job")) { + text_field.yScrollShape.deleteLocalVariable("job"); + } + + text_field.scrollX = text_field.width * ((textWidth - 2) / text_field.textWidth); + + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetFocusIndexUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSetFocusIndexUseCase.ts new file mode 100644 index 00000000..49a75896 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetFocusIndexUseCase.ts @@ -0,0 +1,229 @@ +import type { TextField } from "../../TextField"; +import { Point } from "@next2d/geom"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; +import { execute as textFieldBlinkingUseCase } from "./TextFieldBlinkingUseCase"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; +import { execute as textFieldBlinkingClearTimeoutService } from "../service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldSelectedFocusMoveUseCase } from "./TextFieldSelectedFocusMoveUseCase"; +import { $getBlinkingTimerId } from "../../TextUtil"; + +/** + * @description テキストフィールドのフォーカスしてるテキスト位置(インデックス)を設定します。 + * Set the text position (index) that the text field is focusing on. + * + * @param {TextField} text_field + * @param {number} stage_x + * @param {number} stage_y + * @param {boolean} [selected=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + text_field: TextField, + stage_x: number, + stage_y: number, + selected: boolean = false +): void => { + + if (text_field.type !== "input") { + return ; + } + + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + text_field.focusIndex = 1; + text_field.selectIndex = -1; + + if ($getBlinkingTimerId() === undefined) { + textFieldBlinkingUseCase(text_field); + } + + return ; + } + + const width = text_field.width; + const height = text_field.height; + + let tx = 0; + if (text_field.scrollX > 0) { + tx += text_field.scrollX * (text_field.textWidth - width) / width; + } + + let ty = 0; + if (text_field.scrollY > 0) { + ty += text_field.scrollY * (text_field.textHeight - height) / height; + } + + const point = text_field.globalToLocal(new Point(stage_x, stage_y)); + const x = point.x + tx; + const y = point.y + ty; + + let w = 2; + let yMin = 2; + let yMax = yMin + textData.heightTable[0]; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + switch (textObject.mode) { + + case "break": + case "wrap": + if (x > w && y > yMin + && yMax > y + && width > x + ) { + const index = idx; + if (selected) { + if (text_field.selectIndex !== index) { + text_field.selectIndex = index; + + if (text_field.focusVisible) { + text_field.focusVisible = false; + textFieldBlinkingClearTimeoutService(); + } + + textFieldSelectedFocusMoveUseCase(text_field); + textFieldApplyChangesService(text_field); + } + } else { + if (text_field.focusIndex !== index || text_field.selectIndex > -1) { + text_field.focusIndex = index; + text_field.selectIndex = -1; + textFieldApplyChangesService(text_field); + } + } + + return ; + } + + w = 2; + yMin += textData.heightTable[textObject.line - 1]; + yMax = yMin + textData.heightTable[textObject.line]; + break; + + case "text": + if (x > w && y > yMin + && yMax > y + && w + textObject.w > x + ) { + let index = idx; + if (selected) { + if (text_field.focusIndex > index) { + // left + if (text_field.focusIndex === index + 1) { + if (w + textObject.w / 2 < x) { + index = -1; + } + } else { + if (w + textObject.w / 2 < x) { + index += 1; + } + } + } else { + // right + if (text_field.focusIndex === index) { + if (w + textObject.w / 2 > x) { + index = -1; + } + } else { + if (w + textObject.w / 2 > x) { + index -= 1; + } + } + } + + if (text_field.selectIndex !== index) { + text_field.selectIndex = index; + + if (text_field.selectIndex > -1) { + text_field.focusVisible = false; + textFieldBlinkingClearTimeoutService(); + } + + textFieldSelectedFocusMoveUseCase(text_field); + textFieldApplyChangesService(text_field); + } + } else { + if (w + textObject.w / 2 < x) { + const textObject = textData.textTable[index + 1]; + if (!textObject || textObject.mode === "text") { + index += 1; + } + } + + if (text_field.focusIndex !== index || text_field.selectIndex > -1) { + text_field.focusIndex = index; + text_field.selectIndex = -1; + textFieldApplyChangesService(text_field); + } + } + return ; + } + + if (idx === textData.textTable.length - 1 + && x > w && y > yMin && yMax > y + && width > x + ) { + const index = textData.textTable.length; + if (selected) { + if (text_field.selectIndex !== index) { + text_field.selectIndex = index; + + if (text_field.focusVisible) { + text_field.focusVisible = false; + textFieldBlinkingClearTimeoutService(); + } + + textFieldApplyChangesService(text_field); + } + } else { + if (text_field.focusIndex !== index || text_field.selectIndex > -1) { + text_field.focusIndex = index; + text_field.selectIndex = -1; + textFieldApplyChangesService(text_field); + } + } + return ; + } + + w += textObject.w; + break; + + default: + break; + + } + + if (!selected) { + text_field.focusIndex = textData.textTable.length; + text_field.selectIndex = -1; + if ($getBlinkingTimerId() === undefined) { + textFieldBlinkingUseCase(text_field); + } else { + textFieldApplyChangesService(text_field); + } + } else { + let height = 0; + for (let idx = 0; textData.heightTable.length > idx; ++idx) { + height += textData.heightTable[idx]; + } + + if (y > height || 2 > y) { + text_field.selectIndex = 2 > y + ? 1 + : textData.textTable.length; + + if (text_field.focusVisible) { + text_field.focusVisible = false; + textFieldBlinkingClearTimeoutService(); + } + textFieldApplyChangesService(text_field); + } + } + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.test.ts new file mode 100644 index 00000000..05ff58c2 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.test.ts @@ -0,0 +1,18 @@ +import { execute } from "./TextFieldSetFocusUseCase"; +import { FocusEvent } from "@next2d/events"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldSetFocusUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + textField.changed = false; + expect(textField.changed).toBe(false); + + execute(textField, FocusEvent.FOCUS_IN); + expect(textField.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts new file mode 100644 index 00000000..63d92990 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts @@ -0,0 +1,56 @@ +import type { TextField } from "../../TextField"; +import { FocusEvent } from "@next2d/events"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; +import { execute as textFieldBlinkingClearTimeoutService } from "../service/TextFieldBlinkingClearTimeoutService"; +import { execute as textFieldBlinkingUseCase } from "./TextFieldBlinkingUseCase"; +import { + $textArea, + $getBlinkingTimerId +} from "../../TextUtil"; + +/** + * @description フォーカス + * + * @param {TextField} text_field + * @param {string} name + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, name: string): void => +{ + if (text_field.willTrigger(name)) { + text_field.dispatchEvent(new FocusEvent(name)); + } + + $textArea.value = ""; + if (text_field.focus) { + + setTimeout((): void => + { + $textArea.focus(); + }, 300); + + if ($getBlinkingTimerId() === undefined) { + if (text_field.focusIndex === -1) { + text_field.focusIndex = 1; + text_field.selectIndex = -1; + } + textFieldBlinkingUseCase(text_field); + } + + } else { + // params reset + text_field.focusIndex = -1; + text_field.selectIndex = -1; + text_field.focusVisible = false; + + // clear blinking + textFieldBlinkingClearTimeoutService(); + + // blur event + $textArea.blur(); + } + + textFieldApplyChangesService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.test.ts new file mode 100644 index 00000000..997b8547 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./TextFieldSetScrollXUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldSetScrollXUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + + textField.scrollEnabled = false; + execute(textField, 10); + + expect(textField.$scrollX).toBe(0); + + textField.scrollEnabled = true; + textField.autoSize = "center"; + execute(textField, 10); + + expect(textField.$scrollX).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.ts new file mode 100644 index 00000000..c4775695 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetScrollXUseCase.ts @@ -0,0 +1,95 @@ +import type { TextField } from "../../TextField"; +import type { Shape } from "@next2d/display"; +import { Event } from "@next2d/events"; +import { Tween, Easing, type Job } from "@next2d/ui"; +import { $clamp } from "../../TextUtil"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; + +/** + * @description xスクロール位置を設定します + * x Sets the scroll position. + * + * @param {TextField} text_field + * @param {number} scroll_x + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, scroll_x: number): void => +{ + if (!text_field.scrollEnabled + || text_field.autoSize !== "none" + ) { + return ; + } + + // check y animation + if (text_field.yScrollShape.hasLocalVariable("job")) { + return ; + } + + const width = text_field.width; + scroll_x = $clamp(scroll_x, 0, width, 0); + if (text_field.scrollX === scroll_x) { + return ; + } + + if (text_field.textWidth > width) { + + textFieldApplyChangesService(text_field); + + const xScrollShape = text_field.xScrollShape; + xScrollShape.width = width * width / text_field.textWidth; + + const parent = text_field.parent; + if (parent) { + + // start animation + if (xScrollShape.hasLocalVariable("job")) { + xScrollShape.getLocalVariable("job").stop(); + } + + // view start + xScrollShape.alpha = 0.9; + + // set position + xScrollShape.x = text_field.x + 1 + + (width - 1 - xScrollShape.width) + / (width - 1) + * (scroll_x - 1); + + xScrollShape.y = text_field.y + text_field.height + - xScrollShape.height - 0.5; + + // added sprite + parent.addChildAt( + xScrollShape, + parent.getChildIndex(text_field) + 1 + ); + + const job = Tween.add(xScrollShape, + { "alpha" : 0.9 }, + { "alpha" : 0 }, + 0.5, 0.2, Easing.outQuad + ); + + job.addEventListener(Event.COMPLETE, (event: Event): void => + { + const shape = (event.target as Job).target as Shape; + shape.deleteLocalVariable("job"); + if (shape.parent) { + shape.parent.removeChild(shape); + } + }); + job.start(); + + xScrollShape.setLocalVariable("job", job); + } + + text_field.$scrollX = scroll_x; + } + + if (text_field.willTrigger(Event.SCROLL)) { + text_field.dispatchEvent(new Event(Event.SCROLL, true)); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.test.ts new file mode 100644 index 00000000..d20808c0 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./TextFieldSetScrollYUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldSetScrollYUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.multiline = true; + textField.wordWrap = true; + + textField.scrollEnabled = false; + execute(textField, 10); + expect(textField.scrollY).toBe(0); + + textField.scrollEnabled = true; + textField.autoSize = "center"; + execute(textField, 10); + + expect(textField.scrollY).toBe(0); + + textField.scrollEnabled = true; + textField.autoSize = "none"; + textField.multiline = false; + textField.wordWrap = false; + + execute(textField, 10); + expect(textField.scrollY).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.ts new file mode 100644 index 00000000..59310400 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSetScrollYUseCase.ts @@ -0,0 +1,94 @@ +import type { TextField } from "../../TextField"; +import type { Shape } from "@next2d/display"; +import { Event } from "@next2d/events"; +import { Tween, Easing, type Job } from "@next2d/ui"; +import { $clamp } from "../../TextUtil"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; + +/** + * @description yスクロール位置を設定します + * y Sets the scroll position. + * + * @param {TextField} text_field + * @param {number} scroll_y + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, scroll_y: number): void => +{ + if (!text_field.scrollEnabled + || text_field.autoSize !== "none" + || !text_field.multiline && !text_field.wordWrap + ) { + return ; + } + + // check x animation + if (text_field.xScrollShape.hasLocalVariable("job")) { + return ; + } + + const height = text_field.height; + scroll_y = $clamp(scroll_y, 0, height, 0); + if (text_field.scrollY === scroll_y) { + return ; + } + + if (text_field.textHeight > height) { + + textFieldApplyChangesService(text_field); + + const yScrollShape = text_field.yScrollShape; + yScrollShape.height = height * height / text_field.textHeight; + + const parent = text_field.parent; + if (parent) { + + // start animation + if (yScrollShape.hasLocalVariable("job")) { + yScrollShape.getLocalVariable("job").stop(); + } + + // view start + yScrollShape.alpha = 0.9; + + // set position + yScrollShape.x = text_field.x + text_field.width - yScrollShape.width - 0.5; + yScrollShape.y = text_field.y + 0.5 + + (height - 1 - yScrollShape.height) + / (height - 1) + * (scroll_y - 1); + + // added sprite + parent.addChildAt( + yScrollShape, + parent.getChildIndex(text_field) + 1 + ); + + const job = Tween.add(yScrollShape, + { "alpha" : 0.9 }, + { "alpha" : 0 }, + 0.5, 0.2, Easing.outQuad + ); + + job.addEventListener(Event.COMPLETE, (event: Event): void => + { + const shape = (event.target as Job).target as Shape; + shape.deleteLocalVariable("job"); + if (shape.parent) { + shape.parent.removeChild(shape); + } + }); + job.start(); + + yScrollShape.setLocalVariable("job", job); + + text_field.$scrollY = scroll_y; + } + } + + if (text_field.willTrigger(Event.SCROLL)) { + text_field.dispatchEvent(new Event(Event.SCROLL, true)); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.test.ts new file mode 100644 index 00000000..9d49d871 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./TextFieldUpdateStopIndexUseCase"; +import { TextField } from "../../TextField"; +import { describe, expect, it } from "vitest"; + +describe("TextFieldUpdateStopIndexUseCase.js test", () => +{ + it("execute test case1", () => + { + const textField = new TextField(); + textField.changed = false; + + expect(textField.changed).toBe(false); + + execute(textField, 10); + + expect(textField.scrollX).toBe(0); + expect(textField.scrollY).toBe(0); + + expect(textField.changed).toBe(false); + }); + + it("execute test case2", () => + { + const textField = new TextField(); + textField.text = "testtesttesttesttesttesttesttesttest"; + textField.xMax = 5; + + textField.changed = false; + expect(textField.changed).toBe(false); + + execute(textField, 10); + + expect(textField.scrollX).toBe(1.2903225806451613); + expect(textField.scrollY).toBe(0); + expect(textField.changed).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.ts b/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.ts new file mode 100644 index 00000000..53db26b6 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldUpdateStopIndexUseCase.ts @@ -0,0 +1,102 @@ +import type { TextField } from "../../TextField"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; + +/** + * @description テキストの任意の表示終了位置の設定 + * Setting an arbitrary display end position for text. + * + * @param {TextField} text_field + * @param {number} stop_index + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField, stop_index: number): void => +{ + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + let currentTextWidth = 2; + let targetIndex = 0; + for (let idx = 1; idx < textData.textTable.length; ++idx) { + + const textObject = textData.textTable[idx]; + if (!textObject) { + continue; + } + + let countUp = false; + if (textObject.mode === "text") { + countUp = true; + currentTextWidth += textObject.w; + } + + if (targetIndex >= stop_index) { + targetIndex = idx; + break; + } + + if (textObject.mode === "break") { + countUp = true; + currentTextWidth = 2; + } + + if (countUp) { + targetIndex++; + } + + } + + const textObject = textData.textTable[targetIndex]; + if (!textObject) { + return ; + } + + text_field.$scrollX = text_field.$scrollY = 0; + + const line = textObject.line; + + let currentTextHeight = 0; + for (let idx = 0; idx <= line; ++idx) { + currentTextHeight += textData.heightTable[idx]; + } + + const rawHeight = Math.abs(text_field.yMax - text_field.yMin); + let viewTextHeight = 0; + for (let idx = line; idx > -1; --idx) { + const lineHeight = textData.heightTable[idx]; + if (rawHeight < viewTextHeight + lineHeight) { + break; + } + viewTextHeight += lineHeight; + } + + if (currentTextHeight > rawHeight) { + const scaleY = (text_field.textHeight - rawHeight) / rawHeight; + text_field.$scrollY = Math.max(0, Math.min((currentTextHeight - viewTextHeight) / scaleY, rawHeight)); + } + + const rawWidth = Math.abs(text_field.xMax - text_field.xMin); + let viewTextWidth = 0; + for (let idx = targetIndex; idx > 0; --idx) { + const textObject = textData.textTable[idx]; + if (textObject.mode !== "text") { + continue; + } + + if (rawWidth < viewTextWidth + textObject.w) { + break; + } + viewTextWidth += textObject.w; + } + + if (currentTextWidth > rawWidth) { + const scaleX = (text_field.textWidth - rawWidth) / rawWidth; + text_field.$scrollX = Math.max(0, Math.min((currentTextWidth - viewTextWidth) / scaleX, rawWidth + 0.5)); + } + + textFieldApplyChangesService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextFormat.ts b/packages/text/src/TextFormat.ts index 3fe45102..fbbdc08d 100644 --- a/packages/text/src/TextFormat.ts +++ b/packages/text/src/TextFormat.ts @@ -1,198 +1,19 @@ -import { TextFormatAlignImpl } from "@next2d/interface"; -import { - $clamp, - $intToRGBA, - $toColorInt -} from "@next2d/share"; +import type { ITextFormatAlign } from "./interface/ITextFormatAlign"; +import type { ITextFormatObject } from "./interface/ITextFormatObject"; +import { $toColorInt } from "./TextUtil"; /** - * TextFormat クラスは、文字フォーマット情報を表します。 - * TextFormat クラスを使用して、テキストフィールドに特定のテキストフォーマットを作成します。 + * @description TextFormat クラスは、文字フォーマット情報を表します。 + * TextFormat クラスを使用して、テキストフィールドに特定のテキストフォーマットを作成します。 * - * The TextFormat class represents character formatting information. - * Use the TextFormat class to create specific text formatting for text fields. + * The TextFormat class represents character formatting information. + * Use the TextFormat class to create specific text formatting for text fields. * * @class * @memberOf next2d.text */ export class TextFormat { - private _$font: string | null; - private _$size: number | null; - private _$color: number | null; - private _$bold: boolean | null; - private _$italic: boolean | null; - private _$underline: boolean | null; - private _$align: TextFormatAlignImpl | null; - private _$leftMargin: number | null; - private _$rightMargin: number | null; - private _$leading: number | null; - private _$letterSpacing: number | null; - - /** - * @param {string} [font=null] - * @param {number} [size=null] - * @param {number} [color=null] - * @param {boolean} [bold=null] - * @param {boolean} [italic=null] - * @param {boolean} [underline=null] - * @param {string} [align=null] - * @param {number} [left_margin=null] - * @param {number} [right_margin=null] - * @param {number} [leading=null] - * - * @constructor - * @public - */ - constructor( - font: string | null = null, - size: number | null = null, - color: number | null = null, - bold: boolean | null = null, - italic: boolean | null = null, - underline: boolean | null = null, - align: TextFormatAlignImpl | null = null, - left_margin: number | null = null, - right_margin: number | null = null, - leading: number | null = null - ) { - - /** - * @type {string} - * @default null - * @private - */ - this._$font = font; - - /** - * @type {number} - * @default null - * @private - */ - this._$size = size; - - /** - * @type {number} - * @default null - * @private - */ - this._$color = color === null - ? null - : $clamp($toColorInt(color), 0, 0xffffff, 0); - - /** - * @type {boolean} - * @default null - * @private - */ - this._$bold = bold; - - /** - * @type {boolean} - * @default null - * @private - */ - this._$italic = italic; - - /** - * @type {boolean} - * @default null - * @private - */ - this._$underline = underline; - - /** - * @type {string} - * @default null - * @private - */ - this._$align = align; - - /** - * @type {number} - * @default null - * @private - */ - this._$leftMargin = left_margin; - - /** - * @type {number} - * @default null - * @private - */ - this._$rightMargin = right_margin; - - /** - * @type {number} - * @default null - * @private - */ - this._$leading = leading; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$letterSpacing = 0; - } - - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class TextFormat] - * @method - * @static - */ - static toString (): string - { - return "[class TextFormat]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.text.TextFormat - * @const - * @static - */ - static get namespace (): string - { - return "next2d.text.TextFormat"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object TextFormat] - * @method - * @public - */ - toString (): string - { - return "[object TextFormat]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.text.TextFormat - * @const - * @public - */ - get namespace (): string - { - return "next2d.text.TextFormat"; - } - /** * @description 段落の行揃えの設定を示します。 * Indicates the alignment of the paragraph. @@ -201,14 +22,7 @@ export class TextFormat * @default null * @public */ - get align (): TextFormatAlignImpl | null - { - return this._$align; - } - set align (align: TextFormatAlignImpl | null) - { - this._$align = align; - } + public align: ITextFormatAlign | null; /** * @description テキストをボールドにするかどうかを指定します。 @@ -218,14 +32,7 @@ export class TextFormat * @default null * @public */ - get bold (): boolean | null - { - return this._$bold; - } - set bold (bold: boolean | null) - { - this._$bold = bold !== null ? !!bold : null; - } + public bold: boolean | null; /** * @description テキストの色を示します。 @@ -235,17 +42,7 @@ export class TextFormat * @default null * @public */ - get color (): number | null - { - return this._$color; - } - set color (color: number | null) - { - this._$color = color; - if (color) { - this._$color = $clamp($toColorInt(color), 0, 0xffffff, 0); - } - } + public color: number | null; /** * @description このテキストフォーマットでのテキストフォント名を示すストリングです。 @@ -255,14 +52,7 @@ export class TextFormat * @default null * @public */ - get font (): string | null - { - return this._$font; - } - set font (font: string | null) - { - this._$font = font !== null ? `${font}` : null; - } + public font: string | null; /** * @description このテキストフォーマットのテキストをイタリックにするかどうかを示します。 @@ -272,14 +62,7 @@ export class TextFormat * @default null * @public */ - get italic (): boolean | null - { - return this._$italic; - } - set italic (italic: boolean | null) - { - this._$italic = italic !== null ? !!italic : null; - } + public italic: boolean | null; /** * @description 行間の垂直の行送りを示す整数です。 @@ -290,14 +73,7 @@ export class TextFormat * @default null * @public */ - get leading (): number | null - { - return this._$leading; - } - set leading (leading: number | null) - { - this._$leading = leading; - } + public leading: number | null; /** * @description 段落の左インデントをピクセル単位で示します。 @@ -307,14 +83,7 @@ export class TextFormat * @default null * @public */ - get leftMargin (): number | null - { - return this._$leftMargin; - } - set leftMargin (left_margin: number | null) - { - this._$leftMargin = left_margin; - } + public leftMargin: number | null; /** * @description すべての文字の間に均等に配分されるスペースの量を表す数値です。 @@ -325,14 +94,7 @@ export class TextFormat * @default null * @public */ - get letterSpacing (): number | null - { - return this._$letterSpacing; - } - set letterSpacing (letter_spacing: number | null) - { - this._$letterSpacing = letter_spacing; - } + public letterSpacing: number | null; /** * @description 段落の右インデントをピクセル単位で示します。 @@ -342,14 +104,7 @@ export class TextFormat * @default null * @public */ - get rightMargin (): number | null - { - return this._$rightMargin; - } - set rightMargin (right_margin: number | null) - { - this._$rightMargin = right_margin; - } + public rightMargin: number | null; /** * @description このテキストフォーマットのテキストのサイズ(ピクセル単位)です。 @@ -359,14 +114,7 @@ export class TextFormat * @default null * @public */ - get size (): number | null - { - return this._$size; - } - set size (size: number | null) - { - this._$size = size ? size | 0 : null; - } + public size: number | null; /** * @description このテキストフォーマットを使用するテキストに @@ -378,255 +126,88 @@ export class TextFormat * @default null * @public */ - get underline (): boolean | null - { - return this._$underline; - } - set underline (underline: boolean | null) - { - this._$underline = underline !== null ? !!underline : null; - } - - /** - * @return {string} - * @method - * @private - */ - _$toStyleString (): string - { - let style: string = ""; - - if (this._$font) { - style += `font-family: ${this._$font};`; - } - - if (this._$size) { - style += `font-size: ${this._$size}px;`; - } - - if (this._$color) { - const color = $intToRGBA($toColorInt(this._$color)); - const R: string = color.R.toString(16).padStart(2, "0"); - const G: string = color.G.toString(16).padStart(2, "0"); - const B: string = color.B.toString(16).padStart(2, "0"); - style += `color: #${R}${G}${B};`; - } - - if (this._$bold) { - style += "font-weight: bold;"; - } - - if (this._$italic) { - style += "font-style: italic;"; - } - - if (this._$underline) { - style += "text-decoration: underline;"; - } - - if (this._$align) { - style += `text-align: ${this._$align};`; - } - - if (this._$leftMargin) { - style += `margin-left: ${this._$leftMargin}px;`; - } - - if (this._$rightMargin) { - style += `margin-right: ${this._$rightMargin}px;`; - } - - if (this._$leading) { - style += `margin-bottom: ${this._$leading}px;`; - } - - if (this._$letterSpacing) { - style += `letter-spacing: ${this._$letterSpacing}px;`; - } - - return style; - } + public underline: boolean | null; /** - * @return {boolean} - * @method - * @private + * @param {string} [font=null] + * @param {number} [size=null] + * @param {number} [color=null] + * @param {boolean} [bold=null] + * @param {boolean} [italic=null] + * @param {boolean} [underline=null] + * @param {string} [align=null] + * @param {number} [left_margin=null] + * @param {number} [right_margin=null] + * @param {number} [leading=null] + * + * @constructor + * @public */ - _$isSame (text_format: TextFormat): boolean - { - if (this._$font !== text_format.font) { - return false; - } - - if (this._$size !== text_format.size) { - return false; - } - - if (this._$color !== text_format.color) { - return false; - } - - if (this._$bold !== text_format.bold) { - return false; - } - - if (this._$italic !== text_format.italic) { - return false; - } - - if (this._$underline !== text_format.underline) { - return false; - } - - if (this._$align !== text_format.align) { - return false; - } - - if (this._$leftMargin !== text_format.leftMargin) { - return false; - } - - if (this._$rightMargin !== text_format.rightMargin) { - return false; - } - - if (this._$leading !== text_format.leading) { - return false; - } - - if (this._$letterSpacing !== text_format.letterSpacing) { - return false; - } - - return true; + constructor( + font: string | null = null, + size: number | null = null, + color: number | null = null, + bold: boolean | null = null, + italic: boolean | null = null, + underline: boolean | null = null, + align: ITextFormatAlign | null = null, + left_margin: number | null = null, + right_margin: number | null = null, + leading: number | null = null, + letter_Spacing: number | null = null + ) { + this.font = font; + this.size = size; + this.color = color; + this.bold = bold; + this.italic = italic; + this.underline = underline; + this.align = align; + this.leftMargin = left_margin; + this.rightMargin = right_margin; + this.leading = leading; + this.letterSpacing = letter_Spacing; } /** + * @description この TextFormat オブジェクトのコピーを作成して返します。 + * Creates a copy of the TextFormat object and returns it. + * * @return {next2d.text.TextFormat} * @method - * @private + * @public */ - _$clone (): TextFormat + clone (): TextFormat { - const textFormat = new TextFormat( - this._$font, this._$size, this._$color, this._$bold, - this._$italic, this._$underline, this._$align, - this._$leftMargin, this._$rightMargin, this._$leading + return new TextFormat( + this.font, this.size, this.color, this.bold, + this.italic, this.underline, this.align, + this.leftMargin, this.rightMargin, this.leading, this.letterSpacing ); - - textFormat._$letterSpacing = this._$letterSpacing; - - return textFormat; } /** - * @return {void} - * @method - * @private - */ - _$setDefault (): void - { - this._$align = "left"; - this._$bold = false; - this._$color = 0; - this._$font = "Times New Roman"; - this._$italic = false; - this._$leading = 0; - this._$leftMargin = 0; - this._$letterSpacing = 0; - this._$rightMargin = 0; - this._$size = 12; - this._$underline = false; - } - - /** - * @param {TextFormat} text_format - * @return {void} - * @method - * @private - */ - _$merge (text_format: TextFormat): void - { - if (this._$align === null) { - this._$align = text_format._$align; - } - - if (this._$bold === null) { - this._$bold = text_format._$bold; - } - - if (this._$color === null) { - this._$color = text_format._$color; - } - - if (this._$font === null) { - this._$font = text_format._$font; - } - - if (this._$italic === null) { - this._$italic = text_format._$italic; - } - - if (this._$leading === null) { - this._$leading = text_format._$leading; - } - - if (this._$leftMargin === null) { - this._$leftMargin = text_format._$leftMargin; - } - - if (this._$letterSpacing === null) { - this._$letterSpacing = text_format._$letterSpacing; - } - - if (this._$rightMargin === null) { - this._$rightMargin = text_format._$rightMargin; - } - - if (this._$size === null) { - this._$size = text_format._$size; - } - - if (this._$underline === null) { - this._$underline = text_format._$underline; - } - } - - /** - * @return {number} - * @method - * @private - */ - _$widthMargin (): number - { - let width = 0; - - if (this._$leftMargin) { - width += this._$leftMargin; - } - - if (this._$rightMargin) { - width += this._$rightMargin; - } - - return width; - } - - /** - * @return {string} + * @description この TextFormat オブジェクトのプロパティをオブジェクトに変換します。 + * Converts the properties of this TextFormat object to an object. + * + * @return {ITextFormatObject} * @method - * @private + * @public */ - _$generateFontStyle (): string - { - let fontStyle = ""; - if (this._$italic) { - fontStyle = "italic "; - } - if (this._$bold) { - fontStyle += "bold "; - } - - return `${fontStyle}${this._$size}px '${this._$font}',sans-serif`; - } -} + toObject (): ITextFormatObject + { + return { + "font": this.font, + "size": this.size, + "color": $toColorInt(this.color), + "bold": this.bold, + "italic": this.italic, + "underline": this.underline, + "align": this.align, + "leftMargin": this.leftMargin, + "rightMargin": this.rightMargin, + "leading": this.leading, + "letterSpacing": this.letterSpacing + }; + } +} \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.test.ts b/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.test.ts new file mode 100644 index 00000000..dfbc465d --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.test.ts @@ -0,0 +1,46 @@ +import { execute } from "./TextFormatGenerateFontStyleService"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextFormatGenerateFontStyleService.js length test", () => +{ + it("test case1", () => + { + const textFormat = new TextFormat(); + textFormat.italic = true; + textFormat.bold = true; + textFormat.size = 12; + textFormat.font = "Arial"; + expect(execute(textFormat)).toBe("italic bold 12px 'Arial',sans-serif"); + }); + + it("test case2", () => + { + const textFormat = new TextFormat(); + textFormat.italic = false; + textFormat.bold = true; + textFormat.size = 12; + textFormat.font = "Arial"; + expect(execute(textFormat)).toBe("bold 12px 'Arial',sans-serif"); + }); + + it("test case3", () => + { + const textFormat = new TextFormat(); + textFormat.italic = true; + textFormat.bold = false; + textFormat.size = 12; + textFormat.font = "Arial"; + expect(execute(textFormat)).toBe("italic 12px 'Arial',sans-serif"); + }); + + it("test case4", () => + { + const textFormat = new TextFormat(); + textFormat.italic = false; + textFormat.bold = false; + textFormat.size = 12; + textFormat.font = "Arial"; + expect(execute(textFormat)).toBe("12px 'Arial',sans-serif"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.ts b/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.ts new file mode 100644 index 00000000..29411c33 --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatGenerateFontStyleService.ts @@ -0,0 +1,23 @@ +import type { TextFormat } from "../../TextFormat"; + +/** + * @description テキストフォーマットからフォントスタイルを生成 + * Generate font style from text format + * + * @param {TextFormat} text_frmat + * @return {string} + * @method + * @protected + */ +export const execute = (text_frmat: TextFormat): string => +{ + let fontStyle = ""; + if (text_frmat.italic) { + fontStyle = "italic "; + } + if (text_frmat.bold) { + fontStyle += "bold "; + } + + return `${fontStyle}${text_frmat.size}px '${text_frmat.font}',sans-serif`; +}; \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.test.ts b/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.test.ts new file mode 100644 index 00000000..9d4b1f67 --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./TextFormatGetWidthMarginService"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextFormatGetWidthMarginService.js length test", () => +{ + it("test case1", () => + { + const textFormat = new TextFormat(); + expect(execute(textFormat)).toBe(0); + }); + + it("test case2", () => + { + const textFormat = new TextFormat(); + textFormat.leftMargin = 10; + expect(execute(textFormat)).toBe(10); + }); + + it("test case3", () => + { + const textFormat = new TextFormat(); + textFormat.rightMargin = 20; + expect(execute(textFormat)).toBe(20); + }); + + it("test case4", () => + { + const textFormat = new TextFormat(); + textFormat.leftMargin = 10; + textFormat.rightMargin = 20; + expect(execute(textFormat)).toBe(30); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.ts b/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.ts new file mode 100644 index 00000000..16a4f838 --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatGetWidthMarginService.ts @@ -0,0 +1,25 @@ +import type { TextFormat } from "../../TextFormat"; + +/** + * @description テキストフォーマットから幅のマージンを取得 + * Get width margin from text format + * + * @param {TextFormat} text_format + * @return {number} + * @method + * @protected + */ +export const execute = (text_format: TextFormat): number => +{ + let width = 0; + + if (text_format.leftMargin) { + width += text_format.leftMargin; + } + + if (text_format.rightMargin) { + width += text_format.rightMargin; + } + + return width; +}; \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.test.ts b/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.test.ts new file mode 100644 index 00000000..37ed4465 --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.test.ts @@ -0,0 +1,38 @@ +import { execute } from "./TextFormatHtmlTextGenerateStyleService"; +import { execute as textFormatSetDefaultService } from "./TextFormatSetDefaultService"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextFormatHtmlTextGenerateStyleService.js length test", () => +{ + it("test case1", () => + { + expect(execute(new TextFormat())).toBe(""); + }); + + it("test case2", () => + { + const textFormat = new TextFormat(); + textFormatSetDefaultService(textFormat); + expect(execute(textFormat)).toBe("font-family: Times New Roman;font-size: 12px;text-align: left;"); + }); + + it("test case3", () => + { + const textFormat = new TextFormat(); + + textFormat.font = "Arial"; + textFormat.size = 20; + textFormat.color = 0xFF0000; + textFormat.bold = true; + textFormat.italic = true; + textFormat.underline = true; + textFormat.align = "center"; + textFormat.leftMargin = 10; + textFormat.rightMargin = 15; + textFormat.leading = 5; + textFormat.letterSpacing = 2; + + expect(execute(textFormat)).toBe("font-family: Arial;font-size: 20px;color: #ff0000;font-weight: bold;font-style: italic;text-decoration: underline;text-align: center;margin-left: 10px;margin-right: 15px;margin-bottom: 5px;letter-spacing: 2px;"); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.ts b/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.ts new file mode 100644 index 00000000..c80040fd --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatHtmlTextGenerateStyleService.ts @@ -0,0 +1,69 @@ +import type { TextFormat } from "../../TextFormat"; +import { + $intToRGBA, + $toColorInt +} from "../../TextUtil"; + +/** + * @description テキストフォーマットをHTMLのstyle属性に変換します。 + * Converts text format to the style attribute of HTML. + * + * @param {TextFormat} text_format + * @return {string} + * @method + * @protected + */ +export const execute = (text_format: TextFormat): string => +{ + let style: string = ""; + + if (text_format.font) { + style += `font-family: ${text_format.font};`; + } + + if (text_format.size) { + style += `font-size: ${text_format.size}px;`; + } + + if (text_format.color) { + const color = $intToRGBA($toColorInt(text_format.color)); + const R: string = color.R.toString(16).padStart(2, "0"); + const G: string = color.G.toString(16).padStart(2, "0"); + const B: string = color.B.toString(16).padStart(2, "0"); + style += `color: #${R}${G}${B};`; + } + + if (text_format.bold) { + style += "font-weight: bold;"; + } + + if (text_format.italic) { + style += "font-style: italic;"; + } + + if (text_format.underline) { + style += "text-decoration: underline;"; + } + + if (text_format.align) { + style += `text-align: ${text_format.align};`; + } + + if (text_format.leftMargin) { + style += `margin-left: ${text_format.leftMargin}px;`; + } + + if (text_format.rightMargin) { + style += `margin-right: ${text_format.rightMargin}px;`; + } + + if (text_format.leading) { + style += `margin-bottom: ${text_format.leading}px;`; + } + + if (text_format.letterSpacing) { + style += `letter-spacing: ${text_format.letterSpacing}px;`; + } + + return style; +}; \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatIsSameService.test.ts b/packages/text/src/TextFormat/service/TextFormatIsSameService.test.ts new file mode 100644 index 00000000..1e8ebc0c --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatIsSameService.test.ts @@ -0,0 +1,112 @@ +import { execute } from "./TextFormatIsSameService"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextFormatIsSameService.js length test", () => +{ + it("test case1", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + expect(execute(source, destination)).toBe(true); + }); + + it("test case2", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.font = "Arial"; + destination.font = "Times New Roman"; + expect(execute(source, destination)).toBe(false); + }); + + it("test case3", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.size = 10; + destination.size = 12; + expect(execute(source, destination)).toBe(false); + }); + + it("test case4", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.color = 0x000000; + destination.color = 0xFFFFFF; + expect(execute(source, destination)).toBe(false); + }); + + it("test case5", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.bold = true; + destination.bold = false; + expect(execute(source, destination)).toBe(false); + }); + + it("test case6", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.italic = true; + destination.italic = false; + expect(execute(source, destination)).toBe(false); + }); + + it("test case7", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.underline = true; + destination.underline = false; + expect(execute(source, destination)).toBe(false); + }); + + it("test case8", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.align = "left"; + destination.align = "right"; + expect(execute(source, destination)).toBe(false); + }); + + it("test case9", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.leftMargin = 10; + destination.leftMargin = 20; + expect(execute(source, destination)).toBe(false); + }); + + it("test case10", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.rightMargin = 10; + destination.rightMargin = 20; + expect(execute(source, destination)).toBe(false); + }); + + it("test case11", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.leading = 10; + destination.leading = 20; + expect(execute(source, destination)).toBe(false); + }); + + it("test case12", () => + { + const source = new TextFormat(); + const destination = new TextFormat(); + source.letterSpacing = 10; + destination.letterSpacing = 20; + expect(execute(source, destination)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatIsSameService.ts b/packages/text/src/TextFormat/service/TextFormatIsSameService.ts new file mode 100644 index 00000000..af68118d --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatIsSameService.ts @@ -0,0 +1,34 @@ +import type { TextFormat } from "../../TextFormat"; + +/** + * @description TextFormat の比較 + * TextFormat comparison + * + * @param {TextFormat} source + * @param {TextFormat} destination + * @return {boolean} + * @method + * @protected + */ +export const execute = (source: TextFormat, destination: TextFormat): boolean => +{ + switch (true) { + + case source.font !== destination.font: + case source.size !== destination.size: + case source.color !== destination.color: + case source.bold !== destination.bold: + case source.italic !== destination.italic: + case source.underline !== destination.underline: + case source.align !== destination.align: + case source.leftMargin !== destination.leftMargin: + case source.rightMargin !== destination.rightMargin: + case source.leading !== destination.leading: + case source.letterSpacing !== destination.letterSpacing: + return false; + + default: + return true; + + } +}; \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatSetDefaultService.test.ts b/packages/text/src/TextFormat/service/TextFormatSetDefaultService.test.ts new file mode 100644 index 00000000..70f2ac89 --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatSetDefaultService.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./TextFormatSetDefaultService"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextFormatSetDefaultService.js length test", () => +{ + it("default test case1", () => + { + const textFormat = new TextFormat(); + + expect(textFormat.align).toBeNull(); + expect(textFormat.bold).toBeNull(); + expect(textFormat.color).toBeNull(); + expect(textFormat.font).toBeNull(); + expect(textFormat.italic).toBeNull(); + expect(textFormat.leading).toBeNull(); + expect(textFormat.leftMargin).toBeNull(); + expect(textFormat.letterSpacing).toBeNull(); + expect(textFormat.rightMargin).toBeNull(); + expect(textFormat.size).toBeNull(); + expect(textFormat.underline).toBeNull(); + + execute(textFormat); + + expect(textFormat.align).toBe("left"); + expect(textFormat.bold).toBe(false); + expect(textFormat.color).toBe(0); + expect(textFormat.font).toBe("Times New Roman"); + expect(textFormat.italic).toBe(false); + expect(textFormat.leading).toBe(0); + expect(textFormat.leftMargin).toBe(0); + expect(textFormat.letterSpacing).toBe(0); + expect(textFormat.rightMargin).toBe(0); + expect(textFormat.size).toBe(12); + expect(textFormat.underline).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextFormat/service/TextFormatSetDefaultService.ts b/packages/text/src/TextFormat/service/TextFormatSetDefaultService.ts new file mode 100644 index 00000000..b97c2bbf --- /dev/null +++ b/packages/text/src/TextFormat/service/TextFormatSetDefaultService.ts @@ -0,0 +1,25 @@ +import type { TextFormat } from "../../TextFormat"; + +/** + * @description TextFormat のデフォルト設定を行います + * Set the default settings for TextFormat + * + * @param {TextFormat} text_format + * @return {void} + * @method + * @protected + */ +export const execute = (text_format: TextFormat): void => +{ + text_format.align = "left"; + text_format.bold = false; + text_format.color = 0; + text_format.font = "Times New Roman"; + text_format.italic = false; + text_format.leading = 0; + text_format.leftMargin = 0; + text_format.letterSpacing = 0; + text_format.rightMargin = 0; + text_format.size = 12; + text_format.underline = false; +}; \ No newline at end of file diff --git a/packages/text/src/TextParser.ts b/packages/text/src/TextParser.ts deleted file mode 100644 index c6dc492a..00000000 --- a/packages/text/src/TextParser.ts +++ /dev/null @@ -1,606 +0,0 @@ -import { parseDocument } from "htmlparser2"; -import { TextData } from "./TextData"; -import { TextFormat } from "./TextFormat"; -import { $toColorInt } from "@next2d/share"; -import type { OptionsImpl } from "./interface/OptionsImpl"; -import type { TextObjectImpl } from "./interface/TextObjectImpl"; - -/** - * @type {OffscreenCanvasRenderingContext2D} - * @private - */ -const $context: OffscreenCanvasRenderingContext2D = new OffscreenCanvas(1, 1).getContext("2d") as NonNullable; - -/** - * @type {number} - * @private - */ -let $currentWidth: number = 0; - -/** - * @param {string} texts - * @param {TextFormat} text_format - * @param {TextData} text_data - * @param {object} options - * @return {void} - * @method - * @private - */ -const _$parseText = ( - texts: string, - text_format: TextFormat, - text_data: TextData, - options: OptionsImpl -): void => { - - let line: number = text_data.lineTable.length - 1; - const maxWidth: number = options.width - text_format._$widthMargin() - 4; - - for (let idx: number = 0; idx < texts.length; ++idx) { - - const textFormat: TextFormat = options.textFormats === null - ? text_format - : options.textFormats.shift() as NonNullable; - - const text: string = texts[idx]; - - const object: TextObjectImpl = { - "mode" : "text", - "text" : text, - "x" : 0, - "y" : 0, - "w" : 0, - "h" : 0, - "line" : line, - "textFormat" : textFormat._$clone() - }; - - $context.font = textFormat._$generateFontStyle(); - const mesure: TextMetrics = $context.measureText(text || ""); - - let width: number = mesure.width; - if (textFormat.letterSpacing) { - width += textFormat.letterSpacing; - } - - let height: number = mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent; - if (textFormat.leading) { - height += textFormat.leading; - } - - // setup - object.x = 0; - object.y = mesure.fontBoundingBoxAscent; - object.w = width; - object.h = height; - - $currentWidth += width; - if (options.wordWrap && $currentWidth > maxWidth) { - - $currentWidth = width; - - // update - line++; - object.line = line; - - // break object - const wrapObject: TextObjectImpl = { - "mode" : "wrap", - "text" : "", - "x" : 0, - "y" : 0, - "w" : 0, - "h" : 0, - "line" : line, - "textFormat" : textFormat._$clone() - }; - - let chunkLength: number = 1; - let isSeparated: boolean = true; - const pattern: RegExp = /[0-9a-zA-Z?!;:.,?!。、;:〜]/g; - - for (;;) { - - const index: number = text_data.textTable.length - chunkLength; - if (0 >= index) { - isSeparated = false; - chunkLength = 0; - break; - } - - const prevObj: TextObjectImpl = text_data.textTable[index]; - if (!prevObj) { - isSeparated = false; - chunkLength = 0; - break; - } - - if (prevObj.mode !== "text") { - isSeparated = false; - break; - } - - if (prevObj.text === " ") { - chunkLength--; - break; - } - - if (!prevObj.text.match(pattern)) { - chunkLength--; - break; - } - - chunkLength++; - } - - // new line - text_data.widthTable[line] = 0; - text_data.heightTable[line] = 0; - text_data.ascentTable[line] = 0; - - if (chunkLength > 0 && isSeparated) { - - const insertIdx: number = text_data.textTable.length - chunkLength; - text_data.textTable.splice(insertIdx, 0, wrapObject); - text_data.lineTable.push(wrapObject); - - const prevLine: number = line - 1; - - // reset - text_data.widthTable[prevLine] = 0; - text_data.heightTable[prevLine] = 0; - text_data.ascentTable[prevLine] = 0; - - for (let idx: number = 0; idx < insertIdx; ++idx) { - const textObject: TextObjectImpl = text_data.textTable[idx]; - if (textObject.line !== prevLine) { - continue; - } - - if (textObject.mode !== "text") { - continue; - } - - text_data.widthTable[prevLine] += textObject.w; - text_data.heightTable[prevLine] = Math.max(text_data.heightTable[prevLine], textObject.h); - text_data.ascentTable[prevLine] = Math.max(text_data.ascentTable[prevLine], textObject.y); - } - - // reset - $currentWidth = 0; - for (let idx: number = insertIdx + 1; idx < text_data.textTable.length; ++idx) { - const textObject: TextObjectImpl = text_data.textTable[idx]; - textObject.line = line; - $currentWidth += textObject.w; - } - - } else { - text_data.textTable.push(wrapObject); - text_data.lineTable.push(wrapObject); - } - } - - text_data.widthTable[line] = $currentWidth; - text_data.heightTable[line] = Math.max(text_data.heightTable[line], height); - text_data.ascentTable[line] = Math.max(text_data.ascentTable[line], object.y); - text_data.textTable.push(object); - } -}; - -/** - * @param {string} value - * @param {TextFormat} text_format - * @return {void} - * @method - * @private - */ -const _$parseStyle = ( - value: string, - text_format: TextFormat, - options: OptionsImpl -): void => { - - const values: string[] = value - .trim() - .split(";"); - - const attributes: any[] = []; - for (let idx: number = 0; idx < values.length; ++idx) { - - const styleValue: string = values[idx]; - if (!styleValue) { - continue; - } - - const styles: any[] = styleValue.split(":"); - const name: string = styles[0].trim(); - const value: string = styles[1].trim(); - switch (name) { - - case "font-size": - attributes.push({ - "name": "size", - "value": parseFloat(value) - }); - break; - - case "font-family": - attributes.push({ - "name": "face", - "value": value.replace(/'|"/g, "") - }); - break; - - case "letter-spacing": - attributes.push({ - "name": "letterSpacing", - "value": value - }); - break; - - case "margin-bottom": - attributes.push({ - "name": "leading", - "value": parseFloat(value) - }); - break; - - case "margin-left": - attributes.push({ - "name": "leftMargin", - "value": parseFloat(value) - }); - break; - - case "margin-right": - attributes.push({ - "name": "rightMargin", - "value": parseFloat(value) - }); - break; - - case "color": - case "align": - attributes.push({ - "name": name, - "value": value - }); - break; - - case "text-decoration": - case "font-weight": - case "font-style": - attributes.push({ - "name": value, - "value": true - }); - break; - - default: - break; - - } - } - - // eslint-disable-next-line no-use-before-define - _$setAttributes(attributes, text_format, options); -}; - -/** - * @param {array} attributes - * @param {TextFormat} text_format - * @param {object} options - * @return {void} - * @method - * @private - */ -const _$setAttributes = ( - attributes: any[], - text_format: TextFormat, - options: OptionsImpl -): void => { - - for (let idx = 0; idx < attributes.length; ++idx) { - const object: any = attributes[idx]; - switch (object.name) { - - case "style": - _$parseStyle(object.value, text_format, options); - break; - - case "align": - text_format.align = object.value; - break; - - case "face": - text_format.font = object.value; - break; - - case "size": - text_format.size = +object.value; - if (options.subFontSize) { - text_format.size -= options.subFontSize; - if (1 > text_format.size) { - text_format.size = 1; - } - } - break; - - case "color": - text_format.color = $toColorInt(object.value); - break; - - case "letterSpacing": - text_format.letterSpacing = +object.value; - break; - - case "leading": - text_format.leading = +object.value; - break; - - case "leftMargin": - text_format.leftMargin = +object.value; - break; - - case "rightMargin": - text_format.rightMargin = +object.value; - break; - - case "underline": - text_format.underline = true; - break; - - case "bold": - text_format.bold = true; - break; - - case "italic": - text_format.italic = true; - break; - - default: - break; - - } - } -}; - -/** - * @param {TextData} text_data - * @param {TextFormat} text_format - * @return {void} - * @method - * @private - */ -const _$createNewLine = ( - text_data: TextData, - text_format: TextFormat -): void => { - - $currentWidth = 0; - const line: number = text_data.lineTable.length; - - $context.font = text_format._$generateFontStyle(); - const mesure: TextMetrics = $context.measureText(""); - - const object: TextObjectImpl = { - "mode": "break", - "text": "", - "x": 0, - "y": 0, - "w": 0, - "h": mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent, - "line": line, - "textFormat": text_format._$clone() - }; - - text_data.heightTable[line] = 0; - text_data.ascentTable[line] = 0; - text_data.widthTable[line] = 0; - - // register - text_data.lineTable.push(object); - text_data.textTable.push(object); -}; - -/** - * @param {object} document - * @param {TextFormat} text_format - * @param {TextData} text_data - * @param {object} options - * @return {void} - * @method - * @private - */ -const _$parseTag = ( - document: any, - text_format: TextFormat, - text_data: TextData, - options: OptionsImpl -): void => { - - for (let idx: number = 0; idx < document.children.length; ++idx) { - - const node: any = document.children[idx]; - if (node.nodeType === 3) { - - _$parseText(node.nodeValue || "", text_format, text_data, options); - - continue; - - } - - const tf = text_format._$clone(); - switch (node.name.toUpperCase()) { - - case "DIV": // div tag - case "P": // p tag - _$setAttributes(node.attributes, tf, options); - - if (options.multiline) { - _$createNewLine(text_data, tf); - } - - _$parseTag(node, tf, text_data, options); - - if (options.multiline) { - _$createNewLine(text_data, tf); - } - - continue; - - case "U": // underline - tf.underline = true; - break; - - case "B": // bold - tf.bold = true; - break; - - case "I": // italic - tf.italic = true; - break; - - case "FONT": // FONT tag - case "SPAN": // SPAN tag - _$setAttributes(node.attributes, tf, options); - break; - - case "BR": - if (!options.multiline) { - continue; - } - _$createNewLine(text_data, tf); - break; - - default: - break; - - } - - _$parseTag(node, tf, text_data, options); - } -}; - -/** - * @param {TextData} text_data - * @return {void} - * @method - * @private - */ -const _$adjustmentHeight = (text_data: TextData): void => -{ - const length: number = text_data.heightTable.length - 1; - for (let idx: number = 1; idx < length; ++idx) { - - const height: number = text_data.heightTable[idx]; - if (height > 0) { - continue; - } - - // 改行があって、高さの設定がなければ前の行の高さを設定する - const object: TextObjectImpl = text_data.lineTable[idx]; - text_data.heightTable[idx] = object.h = text_data.heightTable[idx - 1]; - } -}; - -/** - * @description 文字列のテキストを分解・解析して配列戻す - * - * @param {string} text - * @param {object} [options = null] - * @return {TextData} - * @method - * @public - */ -export const parsePlainText = ( - text: string, - text_format: TextFormat, - options: OptionsImpl -): TextData => { - - const textData: TextData = new TextData(); - if (!text) { - return textData; - } - - const lineText: string[] = options.multiline - ? text.split("\n") - : [text.replace("\n", "")]; - - for (let idx: number = 0; idx < lineText.length; ++idx) { - - let textFormat: TextFormat = text_format._$clone(); - if (options.textFormats) { - textFormat = idx === 0 - ? options.textFormats[0] - : options.textFormats.shift() as NonNullable; - } - - if (options.subFontSize - && options.subFontSize > 0 && textFormat.size - ) { - textFormat.size -= options.subFontSize; - if (1 > textFormat.size) { - textFormat.size = 1; - } - } - - if (idx === 0 || options.wordWrap || options.multiline) { - _$createNewLine(textData, textFormat); - } - - const texts: string = lineText[idx]; - if (texts) { - $currentWidth = 0; - _$parseText(texts, textFormat, textData, options); - } - } - - _$adjustmentHeight(textData); - - return textData; -}; - -/** - * @description HTMLを分解・解析して配列戻す - * - * @param {string} html_text - * @param {object} [options = null] - * @return {array} - * @method - * @public - */ -export const parseHtmlText = ( - html_text: string, - text_format: TextFormat, - options: OptionsImpl -): TextData => { - - const textData: TextData = new TextData(); - if (!html_text) { - return textData; - } - - const htmlText: string = html_text - .trim() - .replace(/\r?\n/g, "") - .replace(/\t/g, ""); - - const textFormat: TextFormat = text_format._$clone(); - if (options.subFontSize && options.subFontSize > 0 && textFormat.size) { - textFormat.size -= options.subFontSize; - if (1 > textFormat.size) { - textFormat.size = 1; - } - } - - const document: any = parseDocument(htmlText); - _$createNewLine(textData, textFormat); - - _$parseTag(document, textFormat, textData, options); - - _$adjustmentHeight(textData); - - return textData; -}; \ No newline at end of file diff --git a/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.test.ts b/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.test.ts new file mode 100644 index 00000000..f6a3b728 --- /dev/null +++ b/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.test.ts @@ -0,0 +1,79 @@ +import { execute } from "./TextParserAdjustmentHeightService"; +import { TextData } from "../../TextData"; +import { describe, expect, it } from "vitest"; +import { ITextObject } from "../../interface/ITextObject"; + +describe("TextParserAdjustmentHeightService.js length test", () => +{ + it("test case1", () => + { + const textData = new TextData(); + textData.heightTable.push(10, 20, 0, 10); + textData.lineTable.push( + { + "h": 10 + } as ITextObject, + { + "h": 20 + } as ITextObject, + { + "h": 0 + } as ITextObject, + { + "h": 10 + } as ITextObject + ); + + expect(textData.heightTable[0]).toBe(10); + expect(textData.heightTable[1]).toBe(20); + expect(textData.heightTable[2]).toBe(0); + expect(textData.heightTable[3]).toBe(10); + + execute(textData); + expect(textData.heightTable[0]).toBe(10); + expect(textData.heightTable[1]).toBe(20); + expect(textData.heightTable[2]).toBe(20); + expect(textData.heightTable[3]).toBe(10); + + expect(textData.lineTable[0].h).toBe(10); + expect(textData.lineTable[1].h).toBe(20); + expect(textData.lineTable[2].h).toBe(20); + expect(textData.lineTable[3].h).toBe(10); + }); + + it("test case2", () => + { + const textData = new TextData(); + textData.heightTable.push(10, 20, 30, 0); + textData.lineTable.push( + { + "h": 10 + } as ITextObject, + { + "h": 20 + } as ITextObject, + { + "h": 30 + } as ITextObject, + { + "h": 0 + } as ITextObject + ); + + expect(textData.heightTable[0]).toBe(10); + expect(textData.heightTable[1]).toBe(20); + expect(textData.heightTable[2]).toBe(30); + expect(textData.heightTable[3]).toBe(0); + + execute(textData); + expect(textData.heightTable[0]).toBe(10); + expect(textData.heightTable[1]).toBe(20); + expect(textData.heightTable[2]).toBe(30); + expect(textData.heightTable[3]).toBe(0); + + expect(textData.lineTable[0].h).toBe(10); + expect(textData.lineTable[1].h).toBe(20); + expect(textData.lineTable[2].h).toBe(30); + expect(textData.lineTable[3].h).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.ts b/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.ts new file mode 100644 index 00000000..c9d9fb0c --- /dev/null +++ b/packages/text/src/TextParser/service/TextParserAdjustmentHeightService.ts @@ -0,0 +1,30 @@ +import type { TextData } from "../../TextData"; + +/** + * @description 改行だけの行の高さを調整 + * Adjust the height of a line that contains only line breaks + * + * @param {TextData} text_data + * @return {void} + * @method + * @protected + */ +export const execute = (text_data: TextData): void => +{ + const length = text_data.heightTable.length - 1; + for (let idx = 1; idx < length; ++idx) { + + const height = text_data.heightTable[idx]; + if (height > 0) { + continue; + } + + // 改行があって、高さの設定がなければ前の行の高さを設定する + const object = text_data.lineTable[idx]; + if (!object) { + continue; + } + + text_data.heightTable[idx] = object.h = text_data.heightTable[idx - 1]; + } +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/service/TextParserParseStyleService.test.ts b/packages/text/src/TextParser/service/TextParserParseStyleService.test.ts new file mode 100644 index 00000000..55029cd5 --- /dev/null +++ b/packages/text/src/TextParser/service/TextParserParseStyleService.test.ts @@ -0,0 +1,32 @@ +import { execute } from "./TextParserParseStyleService"; +import { describe, expect, it } from "vitest"; + +describe("TextParserParseStyleService.js length test", () => +{ + it("test case1", () => + { + const value = "font-size: 12px; font-family: 'Arial'; letter-spacing: 1px; margin-bottom: 2px; margin-left: 3px; margin-right: 4px; color: #000000; align: center; text-decoration: underline; font-weight: bold; font-style: italic;"; + + const attributes = execute(value); + + expect(attributes.length).toBe(11); + + const results = [ + { "name": "size", "value": 12 }, + { "name": "face", "value": "Arial" }, + { "name": "letterSpacing", "value": 1 }, + { "name": "leading", "value": 2 }, + { "name": "leftMargin", "value": 3 }, + { "name": "rightMargin", "value": 4 }, + { "name": "color", "value": "#000000" }, + { "name": "align", "value": "center" }, + { "name": "underline", "value": true }, + { "name": "bold", "value": true }, + { "name": "italic", "value": true } + ]; + + for (let idx = 0; idx < results.length; idx++) { + expect(attributes[idx]).toEqual(results[idx]); + } + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/service/TextParserParseStyleService.ts b/packages/text/src/TextParser/service/TextParserParseStyleService.ts new file mode 100644 index 00000000..e4836ecd --- /dev/null +++ b/packages/text/src/TextParser/service/TextParserParseStyleService.ts @@ -0,0 +1,97 @@ +import type { IAttributeObject } from "../../interface/IAttributeObject"; + +/** + * @description スタイルを解析して属性オブジェクトの配列を返す + * Analyze the style and return an array of attribute objects + * + * @param {string} value + * @return {IAttributeObject[]} + * @method + * @protected + */ +export const execute = (value: string): IAttributeObject[] => +{ + const values: string[] = value + .trim() + .split(";"); + + const attributes: IAttributeObject[] = []; + for (let idx = 0; idx < values.length; ++idx) { + + const styleValue = values[idx]; + if (!styleValue) { + continue; + } + + const styles = styleValue.split(":"); + const name = styles[0].trim(); + const value = styles[1].trim(); + switch (name) { + + case "font-size": + attributes.push({ + "name": "size", + "value": parseInt(value) + }); + break; + + case "font-family": + attributes.push({ + "name": "face", + "value": value.replace(/'|"/g, "") + }); + break; + + case "letter-spacing": + attributes.push({ + "name": "letterSpacing", + "value": parseInt(value) + }); + break; + + case "margin-bottom": + attributes.push({ + "name": "leading", + "value": parseInt(value) + }); + break; + + case "margin-left": + attributes.push({ + "name": "leftMargin", + "value": parseInt(value) + }); + break; + + case "margin-right": + attributes.push({ + "name": "rightMargin", + "value": parseInt(value) + }); + break; + + case "color": + case "align": + attributes.push({ + "name": name, + "value": value + }); + break; + + case "text-decoration": + case "font-weight": + case "font-style": + attributes.push({ + "name": value, + "value": true + }); + break; + + default: + break; + + } + } + + return attributes; +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.test.ts b/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.test.ts new file mode 100644 index 00000000..bf0c820f --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./TextParserCreateNewLineUseCase"; +import { TextData } from "../../TextData"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextParserCreateNewLineUseCase.js test", () => +{ + it("test case1", () => + { + const textData = new TextData(); + const textFormat = new TextFormat(); + + textFormat.size = 12; + textFormat.font = "Arial"; + + expect(textData.heightTable.length).toBe(0); + expect(textData.ascentTable.length).toBe(0); + expect(textData.widthTable.length).toBe(0); + expect(textData.lineTable.length).toBe(0); + expect(textData.textTable.length).toBe(0); + + execute(textData, textFormat); + + expect(textData.heightTable.length).toBe(1); + expect(textData.ascentTable.length).toBe(1); + expect(textData.widthTable.length).toBe(1); + expect(textData.lineTable.length).toBe(1); + expect(textData.textTable.length).toBe(1); + + const object = textData.textTable[0]; + expect(object.mode).toBe("break"); + expect(object.text).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.ts b/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.ts new file mode 100644 index 00000000..ece78017 --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserCreateNewLineUseCase.ts @@ -0,0 +1,51 @@ +import type { TextData } from "../../TextData"; +import type { TextFormat } from "../../TextFormat"; +import type { ITextObject } from "../../interface/ITextObject"; +import { execute as textFormatGenerateFontStyleService } from "../../TextFormat/service/TextFormatGenerateFontStyleService"; +import { + $context, + $setCurrentWidth +} from "../../TextUtil"; + +/** + * @description 新しい行を作成 + * Create a new line + * + * @param {TextData} text_data + * @param {TextFormat} text_format + * @return {void} + * @method + * @protected + */ +export const execute = ( + text_data: TextData, + text_format: TextFormat +): void => { + + $setCurrentWidth(0); + + const line = text_data.lineTable.length; + + $context.font = textFormatGenerateFontStyleService(text_format); + const mesure: TextMetrics = $context.measureText(""); + + const object: ITextObject = { + "mode": "break", + "text": "", + "x": 0, + "y": 0, + "w": 0, + "h": mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent, + "line": line, + "textFormat": text_format.toObject() + }; + + // initialize + text_data.heightTable[line] = 0; + text_data.ascentTable[line] = 0; + text_data.widthTable[line] = 0; + + // register + text_data.lineTable.push(object); + text_data.textTable.push(object); +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.test.ts b/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.test.ts new file mode 100644 index 00000000..0f45c776 --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.test.ts @@ -0,0 +1,66 @@ +import { execute } from "./TextParserParseHtmlTextUseCase"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextParserParseHtmlTextUseCase.js test", () => +{ + it("test case1", () => + { + const textFormat = new TextFormat(); + + textFormat.size = 24; + textFormat.font = "Arial"; + + expect(textFormat.align).toBe(null); + expect(textFormat.bold).toBe(null); + expect(textFormat.italic).toBe(null) + expect(textFormat.underline).toBe(null); + expect(textFormat.color).toBe(null); + expect(textFormat.size).toBe(24); + expect(textFormat.font).toBe("Arial"); + expect(textFormat.leftMargin).toBe(null); + expect(textFormat.rightMargin).toBe(null); + expect(textFormat.leading).toBe(null); + expect(textFormat.letterSpacing).toBe(null); + + const htmlText = `

+ + +
+えお順 +

`; + + const textData = execute(htmlText, textFormat, { + "width": 200, + "multiline": true, + "wordWrap": true, + "subFontSize": 0, + "textFormats": null + }); + + + expect(textData.textTable.length).toBe(9); + + const object0 = textData.textTable[0]; + expect(object0.mode).toBe("break"); + + const object1 = textData.textTable[2]; + expect(object1.mode).toBe("text"); + expect(object1.text).toBe("あ"); + expect(object1.textFormat.underline).toBe(true); + + const object2 = textData.textTable[3]; + expect(object2.mode).toBe("text"); + expect(object2.text).toBe("い"); + expect(object2.textFormat.bold).toBe(true); + + const object3 = textData.textTable[4]; + expect(object3.mode).toBe("text"); + expect(object3.text).toBe("う"); + expect(object3.textFormat.italic).toBe(true); + + const object4 = textData.textTable[5]; + expect(object4.mode).toBe("break"); + expect(object4.text).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.ts b/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.ts new file mode 100644 index 00000000..cec8df3b --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseHtmlTextUseCase.ts @@ -0,0 +1,53 @@ +import type { TextFormat } from "../../TextFormat"; +import type { IOptions } from "../../interface/IOptions"; +import { TextData } from "../../TextData"; +import { parseDocument } from "htmlparser2"; +import { execute as textParserCreateNewLineUseCase } from "./TextParserCreateNewLineUseCase"; +import { execute as textParserAdjustmentHeightService } from "../service/TextParserAdjustmentHeightService"; +import { execute as textParserParseTagUseCase } from "./TextParserParseTagUseCase"; + +/** + * @description HTMLテキストを解析してTextDataを生成 + * Analyze HTML text and generate TextData + * + * @param {string} html_text + * @param {TextFormat} text_format + * @param {IOptions} options + * @return {TextData} + * @method + * @protected + */ +export const execute = ( + html_text: string, + text_format: TextFormat, + options: IOptions +): TextData => { + + const textData: TextData = new TextData(); + if (!html_text) { + return textData; + } + + const htmlText: string = html_text + .trim() + .replace(/\r?\n/g, "") + .replace(/\t/g, ""); + + const textFormat = text_format.clone(); + if (options.subFontSize && options.subFontSize > 0 && textFormat.size) { + textFormat.size -= options.subFontSize; + if (1 > textFormat.size) { + textFormat.size = 1; + } + } + + textParserCreateNewLineUseCase(textData, textFormat); + + textParserParseTagUseCase( + parseDocument(htmlText), textFormat, textData, options + ); + + textParserAdjustmentHeightService(textData); + + return textData; +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.test.ts b/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.test.ts new file mode 100644 index 00000000..760d50f2 --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.test.ts @@ -0,0 +1,69 @@ +import { execute } from "./TextParserParseTagUseCase"; +import { TextData } from "../../TextData"; +import { TextFormat } from "../../TextFormat"; +import { parseDocument } from "htmlparser2"; +import { describe, expect, it } from "vitest"; + +describe("TextParserParseTagUseCase.js test", () => +{ + it("test case1", () => + { + const textData = new TextData(); + const textFormat = new TextFormat(); + + textFormat.size = 24; + textFormat.font = "Arial"; + + expect(textFormat.align).toBe(null); + expect(textFormat.bold).toBe(null); + expect(textFormat.italic).toBe(null) + expect(textFormat.underline).toBe(null); + expect(textFormat.color).toBe(null); + expect(textFormat.size).toBe(24); + expect(textFormat.font).toBe("Arial"); + expect(textFormat.leftMargin).toBe(null); + expect(textFormat.rightMargin).toBe(null); + expect(textFormat.leading).toBe(null); + expect(textFormat.letterSpacing).toBe(null); + + const htmlText = `

+ + +
+えお順 +

`; + + execute(parseDocument(htmlText.trim().replace(/\r?\n/g, "").replace(/\t/g, "")), textFormat, textData, { + "width": 200, + "multiline": true, + "wordWrap": true, + "subFontSize": 0, + "textFormats": null + }); + + + expect(textData.textTable.length).toBe(8); + + const object0 = textData.textTable[0]; + expect(object0.mode).toBe("break"); + + const object1 = textData.textTable[1]; + expect(object1.mode).toBe("text"); + expect(object1.text).toBe("あ"); + expect(object1.textFormat.underline).toBe(true); + + const object2 = textData.textTable[2]; + expect(object2.mode).toBe("text"); + expect(object2.text).toBe("い"); + expect(object2.textFormat.bold).toBe(true); + + const object3 = textData.textTable[3]; + expect(object3.mode).toBe("text"); + expect(object3.text).toBe("う"); + expect(object3.textFormat.italic).toBe(true); + + const object4 = textData.textTable[4]; + expect(object4.mode).toBe("break"); + expect(object4.text).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.ts b/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.ts new file mode 100644 index 00000000..98f71c0a --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseTagUseCase.ts @@ -0,0 +1,86 @@ +import type { TextFormat } from "../../TextFormat"; +import type { TextData } from "../../TextData"; +import type { IOptions } from "../../interface/IOptions"; +import type { Document, Element, ChildNode } from "domhandler"; +import { execute as textParserParseTextUseCase } from "./TextParserParseTextUseCase"; +import { execute as textParserCreateNewLineUseCase } from "./TextParserCreateNewLineUseCase"; +import { execute as textParserSetAttributesUseCase } from "./TextParserSetAttributesUseCase"; + +/** + * @description タグを解析してTextDataとTextFormatを設定 + * Analyze tags and set TextData and TextFormat + * + * @param {Document} document + * @param {TextFormat} text_format + * @param {TextData} text_data + * @param {IOptions} options + * @return {void} + * @method + * @protected + */ +export const execute = ( + document: Document, + text_format: TextFormat, + text_data: TextData, + options: IOptions +): void => { + + for (let idx = 0; idx < document.children.length; ++idx) { + + const node = document.children[idx] as ChildNode; + + if (node.nodeType === 3) { + + textParserParseTextUseCase(node.nodeValue || "", text_format, text_data, options); + + continue; + + } + + const textFormat = text_format.clone(); + switch ((node as Element).name.toUpperCase()) { + + case "DIV": // div tag + case "P": // p tag + textParserSetAttributesUseCase((node as Element).attributes, textFormat, options); + + if (options.multiline) { + textParserCreateNewLineUseCase(text_data, textFormat); + } + + execute(node as Document, textFormat, text_data, options); + + continue; + + case "U": // underline + textFormat.underline = true; + break; + + case "B": // bold + textFormat.bold = true; + break; + + case "I": // italic + textFormat.italic = true; + break; + + case "FONT": // FONT tag + case "SPAN": // SPAN tag + textParserSetAttributesUseCase((node as Element).attributes, textFormat, options); + break; + + case "BR": + if (!options.multiline) { + continue; + } + textParserCreateNewLineUseCase(text_data, textFormat); + break; + + default: + break; + + } + + execute(node as Document, textFormat, text_data, options); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.test.ts b/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.test.ts new file mode 100644 index 00000000..3db040e9 --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./TextParserParseTextUseCase"; +import { execute as textParserCreateNewLineUseCase } from "./TextParserCreateNewLineUseCase"; +import { TextData } from "../../TextData"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextParserParseTextUseCase.js test", () => +{ + it("test case1", () => + { + const textData = new TextData(); + const textFormat = new TextFormat(); + + textFormat.size = 24; + textFormat.font = "Arial"; + + textParserCreateNewLineUseCase(textData, textFormat); + + const texts = "あいうえお順"; + execute(texts, textFormat, textData, { + "width": 200, + "multiline": true, + "wordWrap": true, + "subFontSize": 0, + "textFormats": null + }); + + expect(textData.textTable.length).toBe(7); + + for (let idx = 1; idx < textData.textTable.length; ++idx) { + const object = textData.textTable[idx]; + expect(object.text).toBe(texts[idx - 1]); + expect(object.textFormat.size).toBe(24); + expect(object.textFormat.font).toBe("Arial"); + } + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.ts b/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.ts new file mode 100644 index 00000000..6266292c --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserParseTextUseCase.ts @@ -0,0 +1,187 @@ +import type { IOptions } from "../../interface/IOptions"; +import type { ITextObject } from "../../interface/ITextObject"; +import type { TextData } from "../../TextData"; +import type { TextFormat } from "../../TextFormat"; +import { execute as textFormatGetWidthMarginService } from "../../TextFormat/service/TextFormatGetWidthMarginService"; +import { execute as textFormatGenerateFontStyleService } from "../../TextFormat/service/TextFormatGenerateFontStyleService"; +import { + $context, + $setCurrentWidth, + $getCurrentWidth +} from "../../TextUtil"; + +/** + * @description テキストを解析 + * Analyze text + * + * @param {string} texts + * @param {TextFormat} text_format + * @param {TextData} text_data + * @param {IOptions} options + * @return {void} + * @method + * @protected + */ +export const execute = ( + texts: string, + text_format: TextFormat, + text_data: TextData, + options: IOptions +): void => { + + let line = text_data.lineTable.length - 1; + const maxWidth = options.width - textFormatGetWidthMarginService(text_format) - 4; + + for (let idx = 0; idx < texts.length; ++idx) { + + const textFormat = options.textFormats + ? options.textFormats.shift() as NonNullable + : text_format; + + const text = texts[idx]; + + const object: ITextObject = { + "mode" : "text", + "text" : text, + "x" : 0, + "y" : 0, + "w" : 0, + "h" : 0, + "line" : line, + "textFormat" : textFormat.toObject() + }; + + $context.font = textFormatGenerateFontStyleService(textFormat); + const mesure = $context.measureText(text || ""); + + const width = textFormat.letterSpacing + ? mesure.width + textFormat.letterSpacing + : mesure.width; + + const height = textFormat.leading + ? mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent + textFormat.leading + : mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent; + + // setup + object.x = 0; + object.y = mesure.fontBoundingBoxAscent; + object.w = width; + object.h = height; + + $setCurrentWidth($getCurrentWidth() + width); + if (options.wordWrap && $getCurrentWidth() > maxWidth) { + + $setCurrentWidth(width); + + // update + line++; + object.line = line; + + // break object + const wrapObject: ITextObject = { + "mode" : "wrap", + "text" : "", + "x" : 0, + "y" : 0, + "w" : 0, + "h" : 0, + "line" : line, + "textFormat" : textFormat.toObject() + }; + + let chunkLength = 1; + let isSeparated = true; + const pattern: RegExp = /[0-9a-zA-Z?!;:.,?!。、;:〜]/g; + + for (;;) { + + const index = text_data.textTable.length - chunkLength; + if (0 >= index) { + isSeparated = false; + chunkLength = 0; + break; + } + + const prevObj = text_data.textTable[index]; + if (!prevObj) { + isSeparated = false; + chunkLength = 0; + break; + } + + if (prevObj.mode !== "text") { + isSeparated = false; + break; + } + + if (prevObj.text === " ") { + chunkLength--; + break; + } + + if (!prevObj.text.match(pattern)) { + chunkLength--; + break; + } + + chunkLength++; + } + + // new line + text_data.widthTable[line] = 0; + text_data.heightTable[line] = 0; + text_data.ascentTable[line] = 0; + + if (chunkLength > 0 && isSeparated) { + + const insertIdx = text_data.textTable.length - chunkLength; + text_data.textTable.splice(insertIdx, 0, wrapObject); + text_data.lineTable.push(wrapObject); + + const prevLine = line - 1; + + // reset + text_data.widthTable[prevLine] = 0; + text_data.heightTable[prevLine] = 0; + text_data.ascentTable[prevLine] = 0; + + for (let idx = 0; idx < insertIdx; ++idx) { + + const textObject = text_data.textTable[idx]; + if (!textObject) { + continue; + } + + if (textObject.line !== prevLine) { + continue; + } + + if (textObject.mode !== "text") { + continue; + } + + text_data.widthTable[prevLine] += textObject.w; + text_data.heightTable[prevLine] = Math.max(text_data.heightTable[prevLine], textObject.h); + text_data.ascentTable[prevLine] = Math.max(text_data.ascentTable[prevLine], textObject.y); + } + + // reset + $setCurrentWidth(0); + for (let idx = insertIdx + 1; idx < text_data.textTable.length; ++idx) { + const textObject = text_data.textTable[idx]; + textObject.line = line; + $setCurrentWidth($getCurrentWidth() + textObject.w); + } + + } else { + text_data.textTable.push(wrapObject); + text_data.lineTable.push(wrapObject); + } + } + + text_data.widthTable[line] = $getCurrentWidth(); + text_data.heightTable[line] = Math.max(text_data.heightTable[line], height); + text_data.ascentTable[line] = Math.max(text_data.ascentTable[line], object.y); + text_data.textTable.push(object); + } +}; \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.test.ts b/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.test.ts new file mode 100644 index 00000000..ca369f06 --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.test.ts @@ -0,0 +1,48 @@ +import { execute } from "./TextParserSetAttributesUseCase"; +import { execute as textParserParseStyleService } from "../service/TextParserParseStyleService"; +import { TextData } from "../../TextData"; +import { TextFormat } from "../../TextFormat"; +import { describe, expect, it } from "vitest"; + +describe("TextParserSetAttributesUseCase.js test", () => +{ + it("test case1", () => + { + const value = "font-size: 12px; font-family: 'Arial'; letter-spacing: 1px; margin-bottom: 2px; margin-left: 3px; margin-right: 4px; color: #000000; align: center; text-decoration: underline; font-weight: bold; font-style: italic;"; + + const attributes = textParserParseStyleService(value); + const textFormat = new TextFormat(); + + expect(textFormat.size).toBe(null); + expect(textFormat.font).toBe(null); + expect(textFormat.letterSpacing).toBe(null); + expect(textFormat.leading).toBe(null); + expect(textFormat.leftMargin).toBe(null); + expect(textFormat.rightMargin).toBe(null); + expect(textFormat.color).toBe(null); + expect(textFormat.align).toBe(null); + expect(textFormat.underline).toBe(null); + expect(textFormat.bold).toBe(null); + expect(textFormat.italic).toBe(null); + + execute(attributes, textFormat, { + "width": 200, + "multiline": true, + "wordWrap": true, + "subFontSize": 0, + "textFormats": null + }); + + expect(textFormat.size).toBe(12); + expect(textFormat.font).toBe("Arial"); + expect(textFormat.letterSpacing).toBe(1); + expect(textFormat.leading).toBe(2); + expect(textFormat.leftMargin).toBe(3); + expect(textFormat.rightMargin).toBe(4); + expect(textFormat.color).toBe(0x000000); + expect(textFormat.align).toBe("center"); + expect(textFormat.underline).toBe(true); + expect(textFormat.bold).toBe(true); + expect(textFormat.italic).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.ts b/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.ts new file mode 100644 index 00000000..0eacf7fa --- /dev/null +++ b/packages/text/src/TextParser/usecase/TextParserSetAttributesUseCase.ts @@ -0,0 +1,92 @@ +import type { TextFormat } from "../../TextFormat"; +import type { IOptions } from "../../interface/IOptions"; +import type { IAttributeObject } from "../../interface/IAttributeObject"; +import type { ITextFormatAlign } from "../../interface/ITextFormatAlign"; +import { $toColorInt } from "../../TextUtil"; +import { execute as textParserParseStyleService } from "../service/TextParserParseStyleService"; + +/** + * @description 属性を TextFormat に設定 + * Set attributes to TextFormat + * + * @param {IAttributeObject[]} attributes + * @param {TextFormat} text_format + * @param {IOptions} options + * @method + * @protected + */ +export const execute = ( + attributes: IAttributeObject[], + text_format: TextFormat, + options: IOptions +): void => { + + for (let idx = 0; idx < attributes.length; ++idx) { + + const object: IAttributeObject = attributes[idx]; + switch (object.name) { + + case "style": + execute( + textParserParseStyleService(object.value as string), + text_format, + options + ); + break; + + case "align": + text_format.align = object.value as ITextFormatAlign; + break; + + case "face": + text_format.font = object.value as string; + break; + + case "size": + text_format.size = +object.value; + if (options.subFontSize) { + text_format.size -= options.subFontSize; + if (1 > text_format.size) { + text_format.size = 1; + } + } + break; + + case "color": + text_format.color = $toColorInt(object.value); + break; + + case "letterSpacing": + text_format.letterSpacing = parseInt(object.value as string); + break; + + case "leading": + text_format.leading = parseInt(object.value as string); + break; + + case "leftMargin": + text_format.leftMargin = parseInt(object.value as string); + break; + + case "rightMargin": + text_format.rightMargin = parseInt(object.value as string); + break; + + case "underline": + text_format.underline = true; + break; + + case "bold": + text_format.bold = true; + break; + + case "italic": + text_format.italic = true; + break; + + default: + break; + + } + } +}; \ No newline at end of file diff --git a/packages/text/src/TextUtil.ts b/packages/text/src/TextUtil.ts new file mode 100644 index 00000000..1db78af5 --- /dev/null +++ b/packages/text/src/TextUtil.ts @@ -0,0 +1,224 @@ +import type { IRGBA } from "./interface/IRGBA"; +import type { IElementPosition } from "./interface/IElementPosition"; +import type { TextField } from "./TextField"; +import { execute as textAreaRegisterEventUseCase } from "./TextArea/usecase/TextAreaRegisterEventUseCase"; + +/** + * @description 選択中のテキストフィールド + * Selected text field + * + * @type {TextField | null} + * @private + */ +let $selectedTextField: TextField | null = null; + +/** + * @description 選択したテキストフィールドをセット + * Set the selected text field + * + * @param {TextField | null} text_field + * @method + * @public + */ +export const $setSelectedTextField = (text_field: TextField | null): void => +{ + $selectedTextField = text_field; +}; + +/** + * @description 選択中のテキストフィールドを返却 + * Returns the selected text field + * + * @return {TextField | null} + * @method + * @public + */ +export const $getSelectedTextField = (): TextField | null => +{ + return $selectedTextField; +}; + +/** + * @description テキスト変更用の TextArea Element(hidden) + * TextArea Element(hidden) for changing text + * + * @type {HTMLTextAreaElement} + * @protected + */ +export const $textArea: HTMLTextAreaElement = document.createElement("textarea") as HTMLTextAreaElement; +textAreaRegisterEventUseCase($textArea); + +$textArea.tabIndex = -1; + +let style = ""; +style += "position: fixed;"; +style += "top: 0;"; +style += "left: 0;"; +style += "font-size: 16px;"; +style += "border: 0;"; +style += "resize: none;"; +style += "opacity: 0;"; +style += "z-index: -1;"; +style += "pointer-events: none;"; +$textArea.setAttribute("style", style); + +/** + * @type {HTMLCanvasElement} + * @private +*/ +const canvas: HTMLCanvasElement = document.createElement("canvas"); +canvas.width = canvas.height = 1; +export const $context = canvas.getContext("2d") as CanvasRenderingContext2D; + +/** + * @type {number} + * @private + */ +let $currentWidth: number = 0; + +/** + * @description 分解中のテキストの現在の幅を取得 + * Get the current width of the text being decomposed + * + * @return {number} + * @method + * @protected + */ +export const $getCurrentWidth = (): number => +{ + return $currentWidth; +}; + +/** + * @description 分解中のテキストの現在の幅をセット + * Set the current width of the text being decomposed + * + * @return {void} + * @method + * @protected + */ +export const $setCurrentWidth = (width: number): void => +{ + $currentWidth = width; +}; + +/** + * @description カラー文字列を数値に変換 + * Convert color string to number + * + * @param {string} value + * @return {number} + * @method + * @protected + */ +export const $convertColorStringToNumber = (value: string): number => +{ + if (!$context) { + return 0; + } + + $context.fillStyle = value; + return +`0x${$context.fillStyle.slice(1)}`; +}; + +/** + * @description カラー文字列を数値に変換 + * Convert color string to number + * + * @param {*} value + * @return {number} + * @method + * @static + */ +export const $toColorInt = (value: any): number => +{ + return isNaN(+value) + ? $convertColorStringToNumber(`${value}`) + : +value; +}; + +/** + * @param {number} color + * @param {number} [alpha=1] + * @returns {IRGBA} + * @method + * @static + */ +export const $intToRGBA = (color: number, alpha: number = 1): IRGBA => +{ + return { + "R": (color & 0xff0000) >> 16, + "G": (color & 0x00ff00) >> 8, + "B": color & 0x0000ff, + "A": alpha * 255 + }; +}; + +/** + * @description 値が最小値と最大値の間に収まるように調整します。 + * Adjust the value so that it falls between the minimum and maximum values. + * + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @protected + */ +export const $clamp = ( + value: number, + min: number, + max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +/** + * @type {NodeJS.Timeout} + * @private + */ +let $timerId: NodeJS.Timeout | void; + +/** + * @description テキスト点滅のタイマーIDを返却 + * Returns the timer ID for text blinking + * + * @return {NodeJS.Timeout} + * @protected + */ +export const $getBlinkingTimerId = (): NodeJS.Timeout | void => +{ + return $timerId; +}; + +/** + * @description テキスト点滅のタイマーIDをセット + * Set the timer ID for text blinking + * + * @param {NodeJS.Timeout} timer_id + * @return {void} + * @protected + */ +export const $setBlinkingTimerId = (timer_id: NodeJS.Timeout | void): void => +{ + $timerId = timer_id; +}; + +/** + * @description canvasの位置をセット + * Set the position of the canvas + * + * @type {IElementPosition} + * @public + */ +export const $mainCanvasPosition: IElementPosition = { + "x": 0, + "y": 0 +}; \ No newline at end of file diff --git a/packages/text/src/index.ts b/packages/text/src/index.ts index 2b91645e..154c93ad 100644 --- a/packages/text/src/index.ts +++ b/packages/text/src/index.ts @@ -1,3 +1,8 @@ +export * from "./TextField"; export * from "./TextFormat"; -export * from "./TextParser"; -export * from "./TextData"; \ No newline at end of file +export { + $textArea, + $getSelectedTextField, + $setSelectedTextField, + $mainCanvasPosition +} from "./TextUtil"; \ No newline at end of file diff --git a/packages/text/src/interface/IAttributeObject.ts b/packages/text/src/interface/IAttributeObject.ts new file mode 100644 index 00000000..8a8eaecd --- /dev/null +++ b/packages/text/src/interface/IAttributeObject.ts @@ -0,0 +1,4 @@ +export type IAttributeObject = { + name: string; + value: string | number | boolean; +} \ No newline at end of file diff --git a/packages/text/src/interface/IBlendMode.ts b/packages/text/src/interface/IBlendMode.ts new file mode 100644 index 00000000..9480efe7 --- /dev/null +++ b/packages/text/src/interface/IBlendMode.ts @@ -0,0 +1,15 @@ +export type IBlendMode = "copy" + | "add" + | "alpha" + | "darken" + | "difference" + | "erase" + | "hardlight" + | "invert" + | "layer" + | "lighten" + | "multiply" + | "normal" + | "overlay" + | "screen" + | "subtract"; \ No newline at end of file diff --git a/packages/text/src/interface/IBounds.ts b/packages/text/src/interface/IBounds.ts new file mode 100644 index 00000000..b20c1ee3 --- /dev/null +++ b/packages/text/src/interface/IBounds.ts @@ -0,0 +1,6 @@ +export interface IBounds { + xMin: number; + xMax: number; + yMin: number; + yMax: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/ICharacter.ts b/packages/text/src/interface/ICharacter.ts new file mode 100644 index 00000000..1b583b65 --- /dev/null +++ b/packages/text/src/interface/ICharacter.ts @@ -0,0 +1,6 @@ +import type { IMovieClipCharacter } from "./IMovieClipCharacter"; +import type { IVideoCharacter } from "./IVideoCharacter"; +import type { IShapeCharacter } from "./IShapeCharacter"; +import type { ITextFieldCharacter } from "./ITextFieldCharacter"; + +export type ICharacter = IMovieClipCharacter | IVideoCharacter | IShapeCharacter | ITextFieldCharacter; \ No newline at end of file diff --git a/packages/text/src/interface/IDictionaryTag.ts b/packages/text/src/interface/IDictionaryTag.ts new file mode 100644 index 00000000..31e8ba03 --- /dev/null +++ b/packages/text/src/interface/IDictionaryTag.ts @@ -0,0 +1,8 @@ +export interface IDictionaryTag { + characterId: number; + name: string; + startFrame: number; + endFrame: number; + depth: number; + clipDepth: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/IElementPosition.ts b/packages/text/src/interface/IElementPosition.ts new file mode 100644 index 00000000..1657f32b --- /dev/null +++ b/packages/text/src/interface/IElementPosition.ts @@ -0,0 +1,5 @@ +export interface IElementPosition +{ + x: number; + y: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/IFilterArray.ts b/packages/text/src/interface/IFilterArray.ts new file mode 100644 index 00000000..1cc8fd6c --- /dev/null +++ b/packages/text/src/interface/IFilterArray.ts @@ -0,0 +1,23 @@ +import type { + BlurFilter, + BevelFilter, + ColorMatrixFilter, + ConvolutionFilter, + DisplacementMapFilter, + DropShadowFilter, + GlowFilter, + GradientBevelFilter, + GradientGlowFilter +} from "@next2d/filters"; + +export type IFilterArray = Array< + BlurFilter + | BevelFilter + | ColorMatrixFilter + | ConvolutionFilter + | DisplacementMapFilter + | DropShadowFilter + | GlowFilter + | GradientBevelFilter + | GradientGlowFilter +>; \ No newline at end of file diff --git a/packages/text/src/interface/IGrid.ts b/packages/text/src/interface/IGrid.ts new file mode 100644 index 00000000..f95d98fd --- /dev/null +++ b/packages/text/src/interface/IGrid.ts @@ -0,0 +1,6 @@ +export interface IGrid { + x: number; + y: number; + w: number; + h: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/ILoopConfig.ts b/packages/text/src/interface/ILoopConfig.ts new file mode 100644 index 00000000..835d87a4 --- /dev/null +++ b/packages/text/src/interface/ILoopConfig.ts @@ -0,0 +1,9 @@ +import type { ILoopType } from "./ILoopType"; + +export interface ILoopConfig { + type: ILoopType; + frame: number; + start: number; + end: number; + tweenFrame?: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/ILoopType.ts b/packages/text/src/interface/ILoopType.ts new file mode 100644 index 00000000..7ee3e8ad --- /dev/null +++ b/packages/text/src/interface/ILoopType.ts @@ -0,0 +1,5 @@ +export type ILoopType = 0 // REPEAT + | 1 // NO_REPEAT + | 2 // FIXED + | 3 // NO_REPEAT_REVERSAL + | 4 ;// REPEAT_REVERSAL \ No newline at end of file diff --git a/packages/text/src/interface/IMovieClipActionObject.ts b/packages/text/src/interface/IMovieClipActionObject.ts new file mode 100644 index 00000000..a7960988 --- /dev/null +++ b/packages/text/src/interface/IMovieClipActionObject.ts @@ -0,0 +1,5 @@ +export interface IMovieClipActionObject { + frame: number; + action: string; + script?: Function; +} \ No newline at end of file diff --git a/packages/text/src/interface/IMovieClipCharacter.ts b/packages/text/src/interface/IMovieClipCharacter.ts new file mode 100644 index 00000000..305a9f20 --- /dev/null +++ b/packages/text/src/interface/IMovieClipCharacter.ts @@ -0,0 +1,18 @@ +import type { IMovieClipSoundObject } from "./IMovieClipSoundObject"; +import type { IMovieClipActionObject } from "./IMovieClipActionObject"; +import type { IMovieClipLabelObject } from "./IMovieClipLabelObject"; +import type { IPlaceObject } from "./IPlaceObject"; +import type { IDictionaryTag } from "./IDictionaryTag"; + +export interface IMovieClipCharacter { + symbol?: string; + extends: string; + totalFrame: number; + controller: Array>; + dictionary: IDictionaryTag[]; + placeMap: Array>; + placeObjects: IPlaceObject[]; + labels?: IMovieClipLabelObject[]; + actions?: IMovieClipActionObject[]; + sounds?: IMovieClipSoundObject[]; +} \ No newline at end of file diff --git a/packages/text/src/interface/IMovieClipLabelObject.ts b/packages/text/src/interface/IMovieClipLabelObject.ts new file mode 100644 index 00000000..da1d6877 --- /dev/null +++ b/packages/text/src/interface/IMovieClipLabelObject.ts @@ -0,0 +1,4 @@ +export interface IMovieClipLabelObject { + name: string; + frame: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/IMovieClipSoundObject.ts b/packages/text/src/interface/IMovieClipSoundObject.ts new file mode 100644 index 00000000..ab1b961e --- /dev/null +++ b/packages/text/src/interface/IMovieClipSoundObject.ts @@ -0,0 +1,6 @@ +import type { ISoundTag } from "./ISoundTag"; + +export interface IMovieClipSoundObject { + frame: number; + sound: ISoundTag[]; +} \ No newline at end of file diff --git a/packages/text/src/interface/IOptions.ts b/packages/text/src/interface/IOptions.ts new file mode 100644 index 00000000..72f74dd3 --- /dev/null +++ b/packages/text/src/interface/IOptions.ts @@ -0,0 +1,9 @@ +import type { TextFormat } from "../TextFormat"; + +export interface IOptions { + width: number; + multiline: boolean; + wordWrap: boolean; + textFormats?: TextFormat[] | null + subFontSize?: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/IPlaceObject.ts b/packages/text/src/interface/IPlaceObject.ts new file mode 100644 index 00000000..2b0741a3 --- /dev/null +++ b/packages/text/src/interface/IPlaceObject.ts @@ -0,0 +1,15 @@ +import type { ILoopConfig } from "./ILoopConfig"; +import type { ISurfaceFilter } from "./ISurfaceFilter"; +import type { IFilterArray } from "./IFilterArray"; +import type { IBlendMode } from "./IBlendMode"; + +export interface IPlaceObject { + matrix?: number[]; + typedMatrix?: Float32Array; + colorTransform?: number[]; + typedColorTransform?: Float32Array; + blendMode?: IBlendMode; + surfaceFilterList?: ISurfaceFilter[]; + filters?: IFilterArray; + loop?: ILoopConfig; +} \ No newline at end of file diff --git a/packages/text/src/interface/IRGBA.ts b/packages/text/src/interface/IRGBA.ts new file mode 100644 index 00000000..aa6feffc --- /dev/null +++ b/packages/text/src/interface/IRGBA.ts @@ -0,0 +1,6 @@ +export interface IRGBA { + A: number; + R: number; + G: number; + B: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/IShapeCharacter.ts b/packages/text/src/interface/IShapeCharacter.ts new file mode 100644 index 00000000..9f52aca2 --- /dev/null +++ b/packages/text/src/interface/IShapeCharacter.ts @@ -0,0 +1,15 @@ +import type { IGrid } from "./IGrid"; +import type { IBounds } from "./IBounds"; + +export interface IShapeCharacter { + symbol?: string; + extends: string; + bounds: IBounds; + bitmapId: number; + inBitmap?: boolean; + grid?: IGrid | null; + recodes?: any[] | null; + buffer?: number[] | null; + imageBuffer?: Uint8Array; + recodeBuffer?: Float32Array; +} \ No newline at end of file diff --git a/packages/text/src/interface/ISoundTag.ts b/packages/text/src/interface/ISoundTag.ts new file mode 100644 index 00000000..51e955dd --- /dev/null +++ b/packages/text/src/interface/ISoundTag.ts @@ -0,0 +1,6 @@ +export interface ISoundTag { + characterId: number; + volume: number; + autoPlay: boolean; + loopCount: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/ISurfaceFilter.ts b/packages/text/src/interface/ISurfaceFilter.ts new file mode 100644 index 00000000..c761a9d3 --- /dev/null +++ b/packages/text/src/interface/ISurfaceFilter.ts @@ -0,0 +1,14 @@ +type ClassName = "BevelFilter" + | "BlurFilter" + | "ColorMatrixFilter" + | "ConvolutionFilter" + | "DisplacementMapFilter" + | "DropShadowFilter" + | "GlowFilter" + | "GradientBevelFilter" + | "GradientGlowFilter"; + +export interface ISurfaceFilter { + class: ClassName; + params: any[]; +} \ No newline at end of file diff --git a/packages/text/src/interface/ITextFieldAutoSize.ts b/packages/text/src/interface/ITextFieldAutoSize.ts new file mode 100644 index 00000000..e38ed299 --- /dev/null +++ b/packages/text/src/interface/ITextFieldAutoSize.ts @@ -0,0 +1 @@ +export type ITextFieldAutoSize = "center" | "left" | "none" | "right"; \ No newline at end of file diff --git a/packages/text/src/interface/ITextFieldCharacter copy.ts b/packages/text/src/interface/ITextFieldCharacter copy.ts new file mode 100644 index 00000000..f3c9837e --- /dev/null +++ b/packages/text/src/interface/ITextFieldCharacter copy.ts @@ -0,0 +1,27 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; +import type { ITextFieldType } from "./ITextFieldType"; +import type { IBounds } from "./IBounds"; + +export interface ITextFieldCharacter { + symbol?: string; + extends: string; + font: string; + size: number; + align: ITextFormatAlign; + color: number; + leading: number; + letterSpacing: number; + leftMargin: number; + rightMargin: number; + fontType: number; + autoSize: number; + inputType: ITextFieldType; + multiline: boolean; + wordWrap: boolean; + border: boolean; + scroll: boolean; + thickness: number; + thicknessColor: number; + bounds: IBounds; + text: string; +} \ No newline at end of file diff --git a/packages/text/src/interface/ITextFieldCharacter.ts b/packages/text/src/interface/ITextFieldCharacter.ts new file mode 100644 index 00000000..f3c9837e --- /dev/null +++ b/packages/text/src/interface/ITextFieldCharacter.ts @@ -0,0 +1,27 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; +import type { ITextFieldType } from "./ITextFieldType"; +import type { IBounds } from "./IBounds"; + +export interface ITextFieldCharacter { + symbol?: string; + extends: string; + font: string; + size: number; + align: ITextFormatAlign; + color: number; + leading: number; + letterSpacing: number; + leftMargin: number; + rightMargin: number; + fontType: number; + autoSize: number; + inputType: ITextFieldType; + multiline: boolean; + wordWrap: boolean; + border: boolean; + scroll: boolean; + thickness: number; + thicknessColor: number; + bounds: IBounds; + text: string; +} \ No newline at end of file diff --git a/packages/text/src/interface/ITextFieldType copy.ts b/packages/text/src/interface/ITextFieldType copy.ts new file mode 100644 index 00000000..3f92a04b --- /dev/null +++ b/packages/text/src/interface/ITextFieldType copy.ts @@ -0,0 +1 @@ +export type ITextFieldType = "input" | "static"; \ No newline at end of file diff --git a/packages/text/src/interface/ITextFieldType.ts b/packages/text/src/interface/ITextFieldType.ts new file mode 100644 index 00000000..3f92a04b --- /dev/null +++ b/packages/text/src/interface/ITextFieldType.ts @@ -0,0 +1 @@ +export type ITextFieldType = "input" | "static"; \ No newline at end of file diff --git a/packages/text/src/interface/ITextFormatAlign copy.ts b/packages/text/src/interface/ITextFormatAlign copy.ts new file mode 100644 index 00000000..aa48dea1 --- /dev/null +++ b/packages/text/src/interface/ITextFormatAlign copy.ts @@ -0,0 +1 @@ +export type ITextFormatAlign = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/text/src/interface/ITextFormatAlign.ts b/packages/text/src/interface/ITextFormatAlign.ts new file mode 100644 index 00000000..aa48dea1 --- /dev/null +++ b/packages/text/src/interface/ITextFormatAlign.ts @@ -0,0 +1 @@ +export type ITextFormatAlign = "center" | "left" | "right"; \ No newline at end of file diff --git a/packages/text/src/interface/ITextFormatObject.ts b/packages/text/src/interface/ITextFormatObject.ts new file mode 100644 index 00000000..df67bab8 --- /dev/null +++ b/packages/text/src/interface/ITextFormatObject.ts @@ -0,0 +1,15 @@ +import type { ITextFormatAlign } from "./ITextFormatAlign"; + +export interface ITextFormatObject { + align: ITextFormatAlign | null; + bold: boolean | null; + color: number | null; + font: string | null; + italic: boolean | null; + leading: number | null; + leftMargin: number | null; + letterSpacing: number | null; + rightMargin: number | null; + size: number | null; + underline: boolean | null; +} \ No newline at end of file diff --git a/packages/text/src/interface/ITextObject.ts b/packages/text/src/interface/ITextObject.ts new file mode 100644 index 00000000..811db0dd --- /dev/null +++ b/packages/text/src/interface/ITextObject.ts @@ -0,0 +1,13 @@ +import type { ITextFormatObject } from "./ITextFormatObject"; +import type { ITextObjectMode } from "./ITextObjectMode"; + +export interface ITextObject { + mode: ITextObjectMode; + text: string; + textFormat: ITextFormatObject; + x: number; + y: number; + w: number; + h: number; + line: number; +} \ No newline at end of file diff --git a/packages/text/src/interface/ITextObjectMode.ts b/packages/text/src/interface/ITextObjectMode.ts new file mode 100644 index 00000000..0c41854e --- /dev/null +++ b/packages/text/src/interface/ITextObjectMode.ts @@ -0,0 +1 @@ +export type ITextObjectMode = "break" | "wrap" | "image" | "text"; \ No newline at end of file diff --git a/packages/text/src/interface/IVideoCharacter.ts b/packages/text/src/interface/IVideoCharacter.ts new file mode 100644 index 00000000..da241a86 --- /dev/null +++ b/packages/text/src/interface/IVideoCharacter.ts @@ -0,0 +1,12 @@ +import type { IBounds } from "./IBounds"; + +export interface IVideoCharacter { + symbol?: string; + extends: string; + buffer: number[] | null; + videoData: Uint8Array; + volume: number; + loop: boolean; + autoPlay: boolean; + bounds: IBounds; +} \ No newline at end of file diff --git a/packages/text/src/interface/OptionsImpl.ts b/packages/text/src/interface/OptionsImpl.ts deleted file mode 100644 index 7bdb5251..00000000 --- a/packages/text/src/interface/OptionsImpl.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TextFormat } from "../TextFormat"; - -export interface OptionsImpl { - width: number; - multiline: boolean; - wordWrap: boolean; - textFormats: TextFormat[] | null - subFontSize?: number; -} \ No newline at end of file diff --git a/packages/text/src/interface/TextObjectImpl.ts b/packages/text/src/interface/TextObjectImpl.ts deleted file mode 100644 index 7c718f89..00000000 --- a/packages/text/src/interface/TextObjectImpl.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { TextFormat } from "../TextFormat"; -import { TextObjectModeImpl } from "./TextObjectModeImpl"; - -export interface TextObjectImpl { - mode: TextObjectModeImpl; - text: string; - textFormat: TextFormat; - x: number; - y: number; - w: number; - h: number; - line: number; -} \ No newline at end of file diff --git a/packages/text/src/interface/TextObjectModeImpl.ts b/packages/text/src/interface/TextObjectModeImpl.ts deleted file mode 100644 index 3f558522..00000000 --- a/packages/text/src/interface/TextObjectModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextObjectModeImpl = "break" | "wrap" | "image" | "text"; \ No newline at end of file diff --git a/packages/util/LICENSE b/packages/texture-packer/LICENSE similarity index 100% rename from packages/util/LICENSE rename to packages/texture-packer/LICENSE diff --git a/packages/texture-packer/README.md b/packages/texture-packer/README.md new file mode 100644 index 00000000..e06461d4 --- /dev/null +++ b/packages/texture-packer/README.md @@ -0,0 +1,11 @@ +@next2d/texture-packer +============= + +## Installation + +``` +npm install @next2d/texture-packer +``` + +## License +This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/texture-packer/package.json b/packages/texture-packer/package.json new file mode 100644 index 00000000..de2cbfa3 --- /dev/null +++ b/packages/texture-packer/package.json @@ -0,0 +1,26 @@ +{ + "name": "@next2d/texture-packer", + "version": "*", + "description": "Next2D Texture Packer Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "license": "MIT", + "homepage": "https://next2d.app", + "bugs": "https://github.com/Next2D/Player/issues", + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.js" + } + }, + "keywords": [ + "Next2D", + "Next2D Texture Packer" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Next2D/Player.git" + } +} diff --git a/packages/texture-packer/src/Node.ts b/packages/texture-packer/src/Node.ts new file mode 100644 index 00000000..40aa44b1 --- /dev/null +++ b/packages/texture-packer/src/Node.ts @@ -0,0 +1,133 @@ +import { execute as nodeInsertService } from "./Node/service/NodeInsertService"; +import { execute as nodeDisposeService } from "./Node/service/NodeDisposeService"; + +/** + * @description テクスチャパッキングのノードクラス + * Node class for texture + * + * @class + * @public + */ +export class Node +{ + /** + * @description パッキングされたテクスチャの識別番号 + * Identification number of packed texture + * + * @type {number} + * @public + */ + public index: number; + + /** + * @description パッキングされたテクスチャのx座標 + * x coordinate of packed texture + * + * @type {number} + * @public + */ + public x: number; + + /** + * @description パッキングされたテクスチャのy座標 + * y coordinate of packed texture + * + * @type {number} + * @public + */ + public y: number; + + /** + * @description パッキングされたテクスチャの幅 + * Width of packed texture + * + * @type {number} + * @public + */ + public w: number; + + /** + * @description パッキングされたテクスチャの高さ + * Height of packed texture + * + * @type {number} + * @public + */ + public h: number; + + /** + * @description 左のノード + * Left node + * + * @type {Node} + * @public + */ + public left: Node | null; + + /** + * @description 右のノード + * Right node + * + * @type {Node} + * @public + */ + public right: Node | null; + + /** + * @description 使用済みフラグ + * Used flag + * + * @type {boolean} + * @public + */ + public used: boolean; + + /** + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @constructor + * @public + */ + constructor (index:number, x: number, y: number, w: number, h: number) + { + this.index = index; + this.x = x; + this.y = y; + this.w = w; + this.h = h; + + this.left = null; + this.right = null; + this.used = false; + } + + /** + * @description ノードにテクスチャを挿入、挿入したノードを返却します。 + * Insert a texture into the node and return the inserted node. + * + * @param {number} width + * @param {number} height + * @return {Node | null} + * @method + * @public + */ + insert (width: number, height: number): Node | null + { + return nodeInsertService(this, width, height); + } + + /** + * @description ノードを削除します。 + * Remove the node. + * + * @return {boolean} + * @method + * @public + */ + dispose (x: number, y: number, width: number, height: number): boolean + { + return nodeDisposeService(this, x, y, width, height); + } +} \ No newline at end of file diff --git a/packages/texture-packer/src/Node/service/NodeDisposeService.test.ts b/packages/texture-packer/src/Node/service/NodeDisposeService.test.ts new file mode 100644 index 00000000..4cd06b00 --- /dev/null +++ b/packages/texture-packer/src/Node/service/NodeDisposeService.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { Node } from "../../Node"; + +describe("NodeDisposeService.js method test", () => +{ + it("test case1", () => + { + const rootNode = new Node(0, 0, 0, 4096, 4096); + + const node1 = rootNode.insert(300, 120);; + const node2 = rootNode.insert(100, 240); + const node3 = rootNode.insert(400, 240); + + if (!node1 || !node2 || !node3) { + throw new Error("node is null"); + } + + rootNode.dispose(node1.x, node1.y, node1.w, node1.h); + expect(rootNode.left?.used).toBe(false); + + rootNode.dispose(node2.x, node2.y, node2.w, node2.h); + expect(rootNode.right?.left?.used).toBe(false); + + rootNode.dispose(node3.x, node3.y, node3.w, node3.h); + expect(rootNode.left).toBe(null); + expect(rootNode.right).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/texture-packer/src/Node/service/NodeDisposeService.ts b/packages/texture-packer/src/Node/service/NodeDisposeService.ts new file mode 100644 index 00000000..5c63e39b --- /dev/null +++ b/packages/texture-packer/src/Node/service/NodeDisposeService.ts @@ -0,0 +1,50 @@ +import { Node } from "../../Node"; + +/** + * @description ノード解放ロジック + * Node release logic + * + * @param {Node} node + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @protected + */ +export const execute = ( + node: Node, + x: number, + y: number, + width: number, + height: number +): boolean => { + + if (node.left?.dispose(x, y, width, height)) { + if (!node.left.used && !node.right?.used) { + node.left = node.right = null; + node.used = false; + } + return true; + } + + if (node.right?.dispose(x, y, width, height)) { + if (!node.right.used && !node.left?.used) { + node.left = node.right = null; + node.used = false; + } + return true; + } + + if (x === node.x + && y === node.y + && width === node.w + && height === node.h + ) { + node.used = false; + return true; + } + + return false; +}; \ No newline at end of file diff --git a/packages/texture-packer/src/Node/service/NodeInsertService.test.ts b/packages/texture-packer/src/Node/service/NodeInsertService.test.ts new file mode 100644 index 00000000..a6afb2e0 --- /dev/null +++ b/packages/texture-packer/src/Node/service/NodeInsertService.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { Node } from "../../Node"; + +describe("NodeInsertService.js method test", () => +{ + it("test case1", () => + { + const rootNode = new Node(0, 0, 0, 4096, 4096); + const node1 = rootNode.insert(300, 120);; + const node2 = rootNode.insert(100, 240); + const node3 = rootNode.insert(400, 240); + + if (!node1 || !node2 || !node3) { + throw new Error("node is null"); + } + + expect(node1.x).toBe(0); + expect(node1.y).toBe(0); + expect(node1.w).toBe(300); + expect(node1.h).toBe(120); + + expect(node2.x).toBe(0); + expect(node2.y).toBe(121); + expect(node2.w).toBe(100); + expect(node2.h).toBe(240); + + expect(node3.x).toBe(101); + expect(node3.y).toBe(121); + expect(node3.w).toBe(400); + expect(node3.h).toBe(240); + }); +}); \ No newline at end of file diff --git a/packages/texture-packer/src/Node/service/NodeInsertService.ts b/packages/texture-packer/src/Node/service/NodeInsertService.ts new file mode 100644 index 00000000..466aa8fc --- /dev/null +++ b/packages/texture-packer/src/Node/service/NodeInsertService.ts @@ -0,0 +1,43 @@ +import { Node } from "../../Node"; + +/** + * @description ノード挿入ロジック + * Node insertion logic + * + * @param {Node} node + * @param {number} width + * @param {number} height + * @return {Node | null} + * @method + * @protected + */ +export const execute = (node: Node, width: number, height: number): Node | null => +{ + if (node.used) { + const newNode = node.left?.insert(width, height); + return newNode ? newNode : node.right?.insert(width, height) || null; + } + + if (width > node.w || height > node.h) { + return null; + } + + if (width === node.w && height === node.h) { + node.used = true; + return node; + } + + const dw = node.w - width; + const dh = node.h - height; + + if (dw > dh) { + node.left = new Node(node.index, node.x, node.y, width, height); + node.right = new Node(node.index, node.x + width + 1, node.y, dw - 1, node.h); + } else { + node.left = new Node(node.index, node.x, node.y, node.w, height); + node.right = new Node(node.index, node.x, node.y + height + 1, node.w, dh - 1); + } + + node.used = true; + return node.left.insert(width, height); +}; \ No newline at end of file diff --git a/packages/texture-packer/src/TexturePacker.ts b/packages/texture-packer/src/TexturePacker.ts new file mode 100644 index 00000000..dd55873a --- /dev/null +++ b/packages/texture-packer/src/TexturePacker.ts @@ -0,0 +1,64 @@ +import { Node } from "./Node"; + +/** + * @description テクスチャパッカー + * Texture packer + * + * @class + * @public + */ +export class TexturePacker +{ + /** + * @description ルートノード + * Root node + * + * @type {Node} + * @private + */ + private readonly _$root: Node; + + /** + * @param {number} index + * @param {number} width + * @param {number} height + * @constructor + * @public + */ + constructor (index: number, width: number, height: number) + { + this._$root = new Node(index, 0, 0, width, height); + } + + /** + * @description テクスチャをパック、挿入したノードを返却します。 + * Pack the texture and return the inserted node. + * + * @param {number} width + * @param {number} height + * @return {Node | null} + * @method + * @public + */ + insert (width: number, height: number): Node | null + { + return this._$root.insert(width, height); + } + + /** + * @description Nodeを破棄します。 + * Dispose of the Node. + * + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {boolean} + * @method + * @public + */ + dispose (x: number, y: number, width: number, height: number): boolean + { + return this._$root.dispose(x, y, width, height); + } +} \ No newline at end of file diff --git a/packages/texture-packer/src/index.ts b/packages/texture-packer/src/index.ts new file mode 100644 index 00000000..972cb127 --- /dev/null +++ b/packages/texture-packer/src/index.ts @@ -0,0 +1,2 @@ +export * from "./TexturePacker"; +export * from "./Node"; \ No newline at end of file diff --git a/packages/ui/package.json b/packages/ui/package.json index a7ff7c29..3d59e0c5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/ui", "version": "*", - "description": "Next2D UI Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D UI Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -32,7 +24,6 @@ "url": "git+https://github.com/Next2D/Player.git" }, "peerDependencies": { - "@next2d/events": "file:../events", - "@next2d/share": "file:../share" + "@next2d/events": "file:../events" } } diff --git a/packages/ui/src/Easing.ts b/packages/ui/src/Easing.ts index 25009707..729de6ce 100644 --- a/packages/ui/src/Easing.ts +++ b/packages/ui/src/Easing.ts @@ -1,70 +1,44 @@ -import { $Math } from "@next2d/share"; +import { execute as easingLinearService } from "./Easing/service/EasingLinearService"; +import { execute as easingInQuadService } from "./Easing/service/EasingInQuadService"; +import { execute as easingOutQuadService } from "./Easing/service/EasingOutQuadService"; +import { execute as easingInOutQuadService } from "./Easing/service/EasingInOutQuadService"; +import { execute as easingInCubicService } from "./Easing/service/EasingInCubicService"; +import { execute as easingOutCubicService } from "./Easing/service/EasingOutCubicService"; +import { execute as easingInOutCubicService } from "./Easing/service/EasingInOutCubicService"; +import { execute as easingInQuartService } from "./Easing/service/EasingInQuartService"; +import { execute as easingOutQuartService } from "./Easing/service/EasingOutQuartService"; +import { execute as easingInOutQuartService } from "./Easing/service/EasingInOutQuartService"; +import { execute as easingInQuintService } from "./Easing/service/EasingInQuintService"; +import { execute as easingOutQuintService } from "./Easing/service/EasingOutQuintService"; +import { execute as easingInOutQuintService } from "./Easing/service/EasingInOutQuintService"; +import { execute as easingInSineService } from "./Easing/service/EasingInSineService"; +import { execute as easingOutSineService } from "./Easing/service/EasingOutSineService"; +import { execute as easingInOutSineService } from "./Easing/service/EasingInOutSineService"; +import { execute as easingInExpoService } from "./Easing/service/EasingInExpoService"; +import { execute as easingOutExpoService } from "./Easing/service/EasingOutExpoService"; +import { execute as easingInOutExpoService } from "./Easing/service/EasingInOutExpoService"; +import { execute as easingInCircService } from "./Easing/service/EasingInCircService"; +import { execute as easingOutCircService } from "./Easing/service/EasingOutCircService"; +import { execute as easingInOutCircService } from "./Easing/service/EasingInOutCircService"; +import { execute as easingInBackService } from "./Easing/service/EasingInBackService"; +import { execute as easingOutBackService } from "./Easing/service/EasingOutBackService"; +import { execute as easingInOutBackService } from "./Easing/service/EasingInOutBackService"; +import { execute as easingInElasticService } from "./Easing/service/EasingInElasticService"; +import { execute as easingOutElasticService } from "./Easing/service/EasingOutElasticService"; +import { execute as easingInOutElasticService } from "./Easing/service/EasingInOutElasticService"; +import { execute as easingOutBounceService } from "./Easing/service/EasingOutBounceService"; +import { execute as easingInBounceService } from "./Easing/service/EasingInBounceService"; +import { execute as easingInOutBounceService } from "./Easing/service/EasingInOutBounceService"; /** - * Easeクラスは、イージング機能の関数を提供します。 - * The Ease class provides a collection of easing functions + * @description Easeクラスは、イージング機能の関数を提供します。 + * The Ease class provides a collection of easing functions * * @class * @memberOf next2d.ui */ export class Easing { - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Easing] - * @method - * @static - */ - static toString (): string - { - return "[class Easing]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.ui.Easing - * @const - * @static - */ - static get namespace (): string - { - return "next2d.ui.Easing"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object Easing] - * @method - * @public - */ - toString (): string - { - return "[object Easing]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.ui.Easing - * @const - * @public - */ - get namespace (): string - { - return "next2d.ui.Easing"; - } - /** * @param {number} t * @param {number} b @@ -76,7 +50,7 @@ export class Easing */ static linear (t: number, b: number, c: number, d: number): number { - return t / d * c + b; + return easingLinearService(t, b, c, d); } /** @@ -90,7 +64,7 @@ export class Easing */ static inQuad (t: number, b: number, c: number, d: number): number { - return (t /= d) * t * c + b; + return easingInQuadService(t, b, c, d); } /** @@ -104,7 +78,7 @@ export class Easing */ static outQuad (t: number, b: number, c: number, d: number): number { - return -(t /= d) * (t - 2) * c + b; + return easingOutQuadService(t, b, c, d); } /** @@ -118,9 +92,7 @@ export class Easing */ static inOutQuad (t: number, b: number, c: number, d: number): number { - return (t /= d / 2) < 1 - ? t * t * c / 2 + b - : -((t -= 1) * (t - 2) - 1) * c / 2 + b; + return easingInOutQuadService(t, b, c, d); } /** @@ -134,7 +106,7 @@ export class Easing */ static inCubic (t: number, b: number, c: number, d: number): number { - return (t /= d) * t * t * c + b; + return easingInCubicService(t, b, c, d); } /** @@ -148,8 +120,7 @@ export class Easing */ static outCubic (t: number, b: number, c: number, d: number): number { - t /= d; - return (--t * t * t + 1) * c + b; + return easingOutCubicService(t, b, c, d); } /** @@ -163,9 +134,7 @@ export class Easing */ static inOutCubic (t: number, b: number, c: number, d: number): number { - return (t /= d / 2) < 1 - ? t * t * t * c / 2 + b - : ((t -= 2) * t * t + 2) * c / 2 + b; + return easingInOutCubicService(t, b, c, d); } /** @@ -179,7 +148,7 @@ export class Easing */ static inQuart (t: number, b: number, c: number, d: number): number { - return (t /= d) * t * t * t * c + b; + return easingInQuartService(t, b, c, d); } /** @@ -193,8 +162,7 @@ export class Easing */ static outQuart (t: number, b: number, c: number, d: number): number { - t /= d; - return (--t * t * t * t - 1) * -c + b; + return easingOutQuartService(t, b, c, d); } /** @@ -208,9 +176,7 @@ export class Easing */ static inOutQuart (t: number, b: number, c: number, d: number): number { - return (t /= d / 2) < 1 - ? t * t * t * t * c / 2 + b - : ((t -= 2) * t * t * t - 2) * -c / 2 + b; + return easingInOutQuartService(t, b, c, d); } /** @@ -224,7 +190,7 @@ export class Easing */ static inQuint (t: number, b: number, c: number, d: number): number { - return (t /= d) * t * t * t * t * c + b; + return easingInQuintService(t, b, c, d); } /** @@ -238,8 +204,7 @@ export class Easing */ static outQuint (t: number, b: number, c: number, d: number): number { - t /= d; - return (--t * t * t * t * t + 1) * c + b; + return easingOutQuintService(t, b, c, d); } /** @@ -253,9 +218,7 @@ export class Easing */ static inOutQuint (t: number, b: number, c: number, d: number): number { - return (t /= d / 2) < 1 - ? t * t * t * t * t * c / 2 + b - : ((t -= 2) * t * t * t * t + 2) * c / 2 + b; + return easingInOutQuintService(t, b, c, d); } /** @@ -269,7 +232,7 @@ export class Easing */ static inSine (t: number, b: number, c: number, d: number): number { - return -c * $Math.cos(t / d * ($Math.PI / 2)) + c + b; + return easingInSineService(t, b, c, d); } /** @@ -283,7 +246,7 @@ export class Easing */ static outSine (t: number, b: number, c: number, d: number): number { - return c * $Math.sin(t / d * ($Math.PI / 2)) + b; + return easingOutSineService(t, b, c, d); } /** @@ -297,7 +260,7 @@ export class Easing */ static inOutSine (t: number, b: number, c: number, d: number): number { - return -c / 2 * ($Math.cos($Math.PI * t / d) - 1) + b; + return easingInOutSineService(t, b, c, d); } /** @@ -311,7 +274,7 @@ export class Easing */ static inExpo (t: number, b: number, c: number, d: number): number { - return c * $Math.pow(2, 10 * (t / d - 1) ) + b; + return easingInExpoService(t, b, c, d); } /** @@ -325,7 +288,7 @@ export class Easing */ static outExpo (t: number, b: number, c: number, d: number): number { - return c * (-$Math.pow(2, -10 * t / d) + 1) + b; + return easingOutExpoService(t, b, c, d); } /** @@ -339,9 +302,7 @@ export class Easing */ static inOutExpo (t: number, b: number, c: number, d: number): number { - return (t /= d / 2) < 1 - ? c / 2 * $Math.pow(2, 10 * (t - 1)) + b - : c / 2 * (-$Math.pow(2, -10 * (t - 1)) + 2) + b; + return easingInOutExpoService(t, b, c, d); } /** @@ -355,7 +316,7 @@ export class Easing */ static inCirc (t: number, b: number, c: number, d: number): number { - return (1 - $Math.sqrt(1 - (t /= d) * t)) * c + b; + return easingInCircService(t, b, c, d); } /** @@ -369,8 +330,7 @@ export class Easing */ static outCirc (t: number, b: number, c: number, d: number): number { - t /= d; - return $Math.sqrt(1 - --t * t) * c + b; + return easingOutCircService(t, b, c, d); } /** @@ -384,9 +344,7 @@ export class Easing */ static inOutCirc (t: number, b: number, c: number, d: number): number { - return (t /= d * 2) < 1 - ? ($Math.sqrt(1 - t * t) - 1) / -2 * c + b - : ($Math.sqrt(1 - (t -= 2) * t) + 1) / 2 * c + b; + return easingInOutCircService(t, b, c, d); } /** @@ -400,7 +358,7 @@ export class Easing */ static inBack (t: number, b: number, c: number, d: number): number { - return (2.70158 * (t /= d) * t * t - 1.70158 * t * t) * c + b; + return easingInBackService(t, b, c, d); } /** @@ -414,10 +372,7 @@ export class Easing */ static outBack (t: number, b: number, c: number, d: number): number { - return (1 + 2.70158 - * $Math.pow((t /= d) - 1, 3) + 1.70158 - * $Math.pow(t - 1, 2) - ) * c + b; + return easingOutBackService(t, b, c, d); } /** @@ -431,11 +386,7 @@ export class Easing */ static inOutBack (t: number, b: number, c: number, d: number): number { - let s = 1.70158; - if ((t /= d / 2) < 1) { - return t * t * (((s *= 1.525) + 1) * t - s) * c / 2 + b; - } - return ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) * c / 2 + b; + return easingInOutBackService(t, b, c, d); } /** @@ -449,13 +400,7 @@ export class Easing */ static inElastic (t: number, b: number, c: number, d: number): number { - return (t /= d) === 0 - ? b - : t === 1 - ? c + b - : -$Math.pow(2, (t *= 10) - 10) - * $Math.sin((t - 10.75) * (2 * $Math.PI / 3)) - * c + b; + return easingInElasticService(t, b, c, d); } /** @@ -469,13 +414,7 @@ export class Easing */ static outElastic (t: number, b: number, c: number, d: number): number { - return (t /= d) === 0 - ? b - : t === 1 - ? c + b - : ($Math.pow(2, -10 * t) - * $Math.sin((t * 10 - 0.75) * (2 * $Math.PI / 3)) + 1) - * c + b; + return easingOutElasticService(t, b, c, d); } /** @@ -489,13 +428,7 @@ export class Easing */ static inOutElastic (t: number, b: number, c: number, d: number): number { - return (t /= d) === 0 - ? b - : t === 1 - ? c + b - : t < 0.5 - ? -($Math.pow(2, 20 * t - 10) * $Math.sin((20 * t - 11.125) * (2 * $Math.PI / 4.5))) / 2 * c + b - : ($Math.pow(2, -20 * t + 10) * $Math.sin((20 * t - 11.125) * (2 * $Math.PI / 4.5)) / 2 + 1) * c + b; + return easingInOutElasticService(t, b, c, d); } /** @@ -509,16 +442,7 @@ export class Easing */ static outBounce (t: number, b: number, c: number, d: number): number { - if ((t /= d) < 1 / 2.75) { - return 7.5625 * t * t * c + b; - } - if (t < 2 / 2.75) { - return (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) * c + b; - } - if (t < 2.5 / 2.75) { - return (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) * c + b; - } - return (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) * c + b; + return easingOutBounceService(t, b, c, d); } /** @@ -532,7 +456,7 @@ export class Easing */ static inBounce (t: number, b: number, c: number, d: number): number { - return c - Easing.outBounce(d - t, 0, c, d) + b; + return easingInBounceService(t, b, c, d); } /** @@ -546,8 +470,6 @@ export class Easing */ static inOutBounce (t: number, b: number, c: number, d: number): number { - return t < d / 2 - ? Easing.inBounce(t * 2, b, c / 2, d) - : Easing.outBounce(t * 2 - d, b + c / 2, c / 2, d); + return easingInOutBounceService(t, b, c, d); } -} +} \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInBackService.test.ts b/packages/ui/src/Easing/service/EasingInBackService.test.ts new file mode 100644 index 00000000..db53ee37 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInBackService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inCubic method test", () => + { + expect(Easing.inCirc(0.1, 0.5, 0.5, 1)).toBe(0.5025062814466901); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInBackService.ts b/packages/ui/src/Easing/service/EasingInBackService.ts new file mode 100644 index 00000000..58dd6f1c --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInBackService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing in back service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (2.70158 * (t /= d) * t * t - 1.70158 * t * t) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInBounceService.test.ts b/packages/ui/src/Easing/service/EasingInBounceService.test.ts new file mode 100644 index 00000000..1d81c078 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInBounceService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inBounce method test", () => + { + expect(Easing.inBounce(0.1, 0.5, 0.5, 1)).toBe(0.5059375); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInBounceService.ts b/packages/ui/src/Easing/service/EasingInBounceService.ts new file mode 100644 index 00000000..ab8daa34 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInBounceService.ts @@ -0,0 +1,17 @@ +import { Easing } from "../../Easing"; + +/** + * @description Easing out bounce function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return c - Easing.outBounce(d - t, 0, c, d) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInCircService.test.ts b/packages/ui/src/Easing/service/EasingInCircService.test.ts new file mode 100644 index 00000000..7af705af --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInCircService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inBack method test", () => + { + expect(Easing.inBack(0.1, 0.5, 0.5, 1)).toBe(0.49284289); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInCircService.ts b/packages/ui/src/Easing/service/EasingInCircService.ts new file mode 100644 index 00000000..7204efa6 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInCircService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing in circ service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (1 - Math.sqrt(1 - (t /= d) * t)) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInCubicService.test.ts b/packages/ui/src/Easing/service/EasingInCubicService.test.ts new file mode 100644 index 00000000..009df901 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInCubicService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inCubic method test", () => + { + expect(Easing.inCubic(0.1, 0.5, 0.5, 1)).toBe(0.5005); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInCubicService.ts b/packages/ui/src/Easing/service/EasingInCubicService.ts new file mode 100644 index 00000000..cc0ff288 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInCubicService.ts @@ -0,0 +1,15 @@ +/** + * @description Cubic easing in function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) * t * t * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInElasticService.test.ts b/packages/ui/src/Easing/service/EasingInElasticService.test.ts new file mode 100644 index 00000000..7301764e --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInElasticService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inElastic method test", () => + { + expect(Easing.inElastic(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInElasticService.ts b/packages/ui/src/Easing/service/EasingInElasticService.ts new file mode 100644 index 00000000..9670366d --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInElasticService.ts @@ -0,0 +1,21 @@ +/** + * @description Easing in elastic service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) === 0 + ? b + : t === 1 + ? c + b + : -Math.pow(2, (t *= 10) - 10) + * Math.sin((t - 10.75) * (2 * Math.PI / 3)) + * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInExpoService.test.ts b/packages/ui/src/Easing/service/EasingInExpoService.test.ts new file mode 100644 index 00000000..4f1d9957 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInExpoService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inExpo method test", () => + { + expect(Easing.inExpo(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInExpoService.ts b/packages/ui/src/Easing/service/EasingInExpoService.ts new file mode 100644 index 00000000..86b39361 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInExpoService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing in expo function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return c * Math.pow(2, 10 * (t / d - 1) ) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutBackService.test.ts b/packages/ui/src/Easing/service/EasingInOutBackService.test.ts new file mode 100644 index 00000000..e65b306f --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutBackService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutBack method test", () => + { + expect(Easing.inOutBack(0.1, 0.5, 0.5, 1)).toBe(0.481240724); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutBackService.ts b/packages/ui/src/Easing/service/EasingInOutBackService.ts new file mode 100644 index 00000000..ca524eb8 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutBackService.ts @@ -0,0 +1,18 @@ +/** + * @description Easing in out back service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + let s = 1.70158; + return (t /= d / 2) < 1 + ? t * t * (((s *= 1.525) + 1) * t - s) * c / 2 + b + : ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) * c / 2 + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutBounceService.test.ts b/packages/ui/src/Easing/service/EasingInOutBounceService.test.ts new file mode 100644 index 00000000..9d38bd59 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutBounceService.test.ts @@ -0,0 +1,11 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutBounce method test", () => + { + expect(Easing.inOutBounce(0.1, 0.5, 0.5, 1)).toBe(0.515); + }); + +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutBounceService.ts b/packages/ui/src/Easing/service/EasingInOutBounceService.ts new file mode 100644 index 00000000..a6a9c9d5 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutBounceService.ts @@ -0,0 +1,19 @@ +import { Easing } from "../../Easing"; + +/** + * @description Easing in out bounce function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return t < d / 2 + ? Easing.inBounce(t * 2, b, c / 2, d) + : Easing.outBounce(t * 2 - d, b + c / 2, c / 2, d); +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutCircService.test.ts b/packages/ui/src/Easing/service/EasingInOutCircService.test.ts new file mode 100644 index 00000000..5365e622 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutCircService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutCirc method test", () => + { + expect(Easing.inOutCirc(0.1, 0.5, 0.5, 1)).toBe(0.5003126955570227); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutCircService.ts b/packages/ui/src/Easing/service/EasingInOutCircService.ts new file mode 100644 index 00000000..de00be80 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutCircService.ts @@ -0,0 +1,17 @@ +/** + * @description Easing in out circ service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d * 2) < 1 + ? (Math.sqrt(1 - t * t) - 1) / -2 * c + b + : (Math.sqrt(1 - (t -= 2) * t) + 1) / 2 * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutCubicService.test.ts b/packages/ui/src/Easing/service/EasingInOutCubicService.test.ts new file mode 100644 index 00000000..7073f8e9 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutCubicService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutCubic method test", () => + { + expect(Easing.inOutCubic(0.1, 0.5, 0.5, 1)).toBe(0.502); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutCubicService.ts b/packages/ui/src/Easing/service/EasingInOutCubicService.ts new file mode 100644 index 00000000..c2e4453b --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutCubicService.ts @@ -0,0 +1,17 @@ +/** + * @description Cubic easing in/out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d / 2) < 1 + ? t * t * t * c / 2 + b + : ((t -= 2) * t * t + 2) * c / 2 + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutElasticService.test.ts b/packages/ui/src/Easing/service/EasingInOutElasticService.test.ts new file mode 100644 index 00000000..508f8cc3 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutElasticService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutElastic method test", () => + { + expect(Easing.inOutElastic(0.1, 0.5, 0.5, 1)).toBe(0.5001695782985028); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutElasticService.ts b/packages/ui/src/Easing/service/EasingInOutElasticService.ts new file mode 100644 index 00000000..4865777c --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutElasticService.ts @@ -0,0 +1,21 @@ +/** + * @description Easing in out elastic service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) === 0 + ? b + : t === 1 + ? c + b + : t < 0.5 + ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * (2 * Math.PI / 4.5))) / 2 * c + b + : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * (2 * Math.PI / 4.5)) / 2 + 1) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutExpoService.test.ts b/packages/ui/src/Easing/service/EasingInOutExpoService.test.ts new file mode 100644 index 00000000..836186f8 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutExpoService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutExpo method test", () => + { + expect(Easing.inOutExpo(0.1, 0.5, 0.5, 1)).toBe(0.5009765625); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutExpoService.ts b/packages/ui/src/Easing/service/EasingInOutExpoService.ts new file mode 100644 index 00000000..9689b0e4 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutExpoService.ts @@ -0,0 +1,17 @@ +/** + * @description Easing in out expo function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d / 2) < 1 + ? c / 2 * Math.pow(2, 10 * (t - 1)) + b + : c / 2 * (-Math.pow(2, -10 * (t - 1)) + 2) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuadService.test.ts b/packages/ui/src/Easing/service/EasingInOutQuadService.test.ts new file mode 100644 index 00000000..b3299861 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuadService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutQuad method test", () => + { + expect(Easing.inOutQuad(0.1, 0.5, 0.5, 1)).toBe(0.51); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuadService.ts b/packages/ui/src/Easing/service/EasingInOutQuadService.ts new file mode 100644 index 00000000..b2c27d85 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuadService.ts @@ -0,0 +1,17 @@ +/** + * @description Quad easing in/out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d / 2) < 1 + ? t * t * c / 2 + b + : -((t -= 1) * (t - 2) - 1) * c / 2 + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuartService.test.ts b/packages/ui/src/Easing/service/EasingInOutQuartService.test.ts new file mode 100644 index 00000000..c55815e6 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuartService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutQuart method test", () => + { + expect(Easing.inOutQuart(0.1, 0.5, 0.5, 1)).toBe(0.5004); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuartService.ts b/packages/ui/src/Easing/service/EasingInOutQuartService.ts new file mode 100644 index 00000000..d617c230 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuartService.ts @@ -0,0 +1,17 @@ +/** + * @description Quart easing in/out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d / 2) < 1 + ? t * t * t * t * c / 2 + b + : ((t -= 2) * t * t * t - 2) * -c / 2 + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuintService.test.ts b/packages/ui/src/Easing/service/EasingInOutQuintService.test.ts new file mode 100644 index 00000000..c457b049 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuintService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutQuint method test", () => + { + expect(Easing.inOutQuint(0.1, 0.5, 0.5, 1)).toBe(0.50008); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutQuintService.ts b/packages/ui/src/Easing/service/EasingInOutQuintService.ts new file mode 100644 index 00000000..85422f39 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutQuintService.ts @@ -0,0 +1,17 @@ +/** + * @description Quint easing in/out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d / 2) < 1 + ? t * t * t * t * t * c / 2 + b + : ((t -= 2) * t * t * t * t + 2) * c / 2 + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutSineService.test.ts b/packages/ui/src/Easing/service/EasingInOutSineService.test.ts new file mode 100644 index 00000000..d2397bf5 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutSineService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inOutSine method test", () => + { + expect(Easing.inOutSine(0.1, 0.5, 0.5, 1)).toBe(0.5122358709262116); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInOutSineService.ts b/packages/ui/src/Easing/service/EasingInOutSineService.ts new file mode 100644 index 00000000..946eaf7d --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInOutSineService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing in out sine function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuadService.test.ts b/packages/ui/src/Easing/service/EasingInQuadService.test.ts new file mode 100644 index 00000000..ff46a417 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuadService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inQuad method test", () => + { + expect(Easing.inQuad(0.1, 0.5, 0.5, 1)).toBe(0.505); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuadService.ts b/packages/ui/src/Easing/service/EasingInQuadService.ts new file mode 100644 index 00000000..726d2378 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuadService.ts @@ -0,0 +1,15 @@ +/** + * @description Quad easing in function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) * t * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuartService.test.ts b/packages/ui/src/Easing/service/EasingInQuartService.test.ts new file mode 100644 index 00000000..397f3ccf --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuartService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inQuart method test", () => + { + expect(Easing.inQuart(0.1, 0.5, 0.5, 1)).toBe(0.50005); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuartService.ts b/packages/ui/src/Easing/service/EasingInQuartService.ts new file mode 100644 index 00000000..8d59fb50 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuartService.ts @@ -0,0 +1,15 @@ +/** + * @description Quart easing in function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) * t * t * t * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuintService.test.ts b/packages/ui/src/Easing/service/EasingInQuintService.test.ts new file mode 100644 index 00000000..c7643fd5 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuintService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inQuint method test", () => + { + expect(Easing.inQuint(0.1, 0.5, 0.5, 1)).toBe(0.500005); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInQuintService.ts b/packages/ui/src/Easing/service/EasingInQuintService.ts new file mode 100644 index 00000000..4f1566bd --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInQuintService.ts @@ -0,0 +1,15 @@ +/** + * @description Quint easing in function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) * t * t * t * t * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInSineService.test.ts b/packages/ui/src/Easing/service/EasingInSineService.test.ts new file mode 100644 index 00000000..894aa1cd --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInSineService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("inSine method test", () => + { + expect(Easing.inSine(0.1, 0.5, 0.5, 1)).toBe(0.5061558297024311); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingInSineService.ts b/packages/ui/src/Easing/service/EasingInSineService.ts new file mode 100644 index 00000000..928a3e80 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingInSineService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing in sine function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingLinearService.test.ts b/packages/ui/src/Easing/service/EasingLinearService.test.ts new file mode 100644 index 00000000..fd423166 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingLinearService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("linear method test", () => + { + expect(Easing.linear(0.1, 0.5, 0.5, 1)).toBe(0.55); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingLinearService.ts b/packages/ui/src/Easing/service/EasingLinearService.ts new file mode 100644 index 00000000..9cdaf57f --- /dev/null +++ b/packages/ui/src/Easing/service/EasingLinearService.ts @@ -0,0 +1,15 @@ +/** + * @description Linear easing function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return t / d * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutBackService.test.ts b/packages/ui/src/Easing/service/EasingOutBackService.test.ts new file mode 100644 index 00000000..720db6fc --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutBackService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outBack method test", () => + { + expect(Easing.outBack(0.1, 0.5, 0.5, 1)).toBe(0.7044139899999999); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutBackService.ts b/packages/ui/src/Easing/service/EasingOutBackService.ts new file mode 100644 index 00000000..098b9146 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutBackService.ts @@ -0,0 +1,18 @@ +/** + * @description Easing out back service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (1 + 2.70158 + * Math.pow((t /= d) - 1, 3) + 1.70158 + * Math.pow(t - 1, 2) + ) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutBounceService.test.ts b/packages/ui/src/Easing/service/EasingOutBounceService.test.ts new file mode 100644 index 00000000..d9f8bab7 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutBounceService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outBounce method test", () => + { + expect(Easing.outBounce(0.1, 0.5, 0.5, 1)).toBe(0.5378125); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutBounceService.ts b/packages/ui/src/Easing/service/EasingOutBounceService.ts new file mode 100644 index 00000000..60b06d71 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutBounceService.ts @@ -0,0 +1,24 @@ +/** + * @description Easing out bounce function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + if ((t /= d) < 1 / 2.75) { + return 7.5625 * t * t * c + b; + } + if (t < 2 / 2.75) { + return (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) * c + b; + } + if (t < 2.5 / 2.75) { + return (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) * c + b; + } + return (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutCircService.test.ts b/packages/ui/src/Easing/service/EasingOutCircService.test.ts new file mode 100644 index 00000000..91454a67 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutCircService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outCirc method test", () => + { + expect(Easing.outCirc(0.1, 0.5, 0.5, 1)).toBe(0.7179449471770336); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutCircService.ts b/packages/ui/src/Easing/service/EasingOutCircService.ts new file mode 100644 index 00000000..7543372a --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutCircService.ts @@ -0,0 +1,16 @@ +/** + * @description Easing out circ service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + t /= d; + return Math.sqrt(1 - --t * t) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutCubicService.test.ts b/packages/ui/src/Easing/service/EasingOutCubicService.test.ts new file mode 100644 index 00000000..4b6c880d --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutCubicService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outCubic method test", () => + { + expect(Easing.outCubic(0.1, 0.5, 0.5, 1)).toBe(0.6355); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutCubicService.ts b/packages/ui/src/Easing/service/EasingOutCubicService.ts new file mode 100644 index 00000000..155e8d35 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutCubicService.ts @@ -0,0 +1,16 @@ +/** + * @description Cubic easing out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + t /= d; + return (--t * t * t + 1) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutElasticService.test.ts b/packages/ui/src/Easing/service/EasingOutElasticService.test.ts new file mode 100644 index 00000000..39351c4d --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutElasticService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outElastic method test", () => + { + expect(Easing.outElastic(0.1, 0.5, 0.5, 1)).toBe(1.125); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutElasticService.ts b/packages/ui/src/Easing/service/EasingOutElasticService.ts new file mode 100644 index 00000000..8ce59483 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutElasticService.ts @@ -0,0 +1,21 @@ +/** + * @description Easing out elastic service + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return (t /= d) === 0 + ? b + : t === 1 + ? c + b + : (Math.pow(2, -10 * t) + * Math.sin((t * 10 - 0.75) * (2 * Math.PI / 3)) + 1) + * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutExpoService.test.ts b/packages/ui/src/Easing/service/EasingOutExpoService.test.ts new file mode 100644 index 00000000..884f8661 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutExpoService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outExpo method test", () => + { + expect(Easing.outExpo(0.1, 0.5, 0.5, 1)).toBe(0.75); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutExpoService.ts b/packages/ui/src/Easing/service/EasingOutExpoService.ts new file mode 100644 index 00000000..924ddc2f --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutExpoService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing out expo function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return c * (-Math.pow(2, -10 * t / d) + 1) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuadService.test.ts b/packages/ui/src/Easing/service/EasingOutQuadService.test.ts new file mode 100644 index 00000000..664b2333 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuadService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outQuad method test", () => + { + expect(Easing.outQuad(0.1, 0.5, 0.5, 1)).toBe(0.595); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuadService.ts b/packages/ui/src/Easing/service/EasingOutQuadService.ts new file mode 100644 index 00000000..37fc5921 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuadService.ts @@ -0,0 +1,15 @@ +/** + * @description Quad easing out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return -(t /= d) * (t - 2) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuartService.test.ts b/packages/ui/src/Easing/service/EasingOutQuartService.test.ts new file mode 100644 index 00000000..95ca9656 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuartService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outQuart method test", () => + { + expect(Easing.outQuart(0.1, 0.5, 0.5, 1)).toBe(0.6719499999999999); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuartService.ts b/packages/ui/src/Easing/service/EasingOutQuartService.ts new file mode 100644 index 00000000..9a3065a7 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuartService.ts @@ -0,0 +1,16 @@ +/** + * @description Quart easing out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + t /= d; + return (--t * t * t * t - 1) * -c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuintService.test.ts b/packages/ui/src/Easing/service/EasingOutQuintService.test.ts new file mode 100644 index 00000000..d77121b5 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuintService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outQuint method test", () => + { + expect(Easing.outQuint(0.1, 0.5, 0.5, 1)).toBe(0.7047549999999999); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutQuintService.ts b/packages/ui/src/Easing/service/EasingOutQuintService.ts new file mode 100644 index 00000000..7908f551 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutQuintService.ts @@ -0,0 +1,16 @@ +/** + * @description Quint easing out function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + t /= d; + return (--t * t * t * t * t + 1) * c + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutSineService.test.ts b/packages/ui/src/Easing/service/EasingOutSineService.test.ts new file mode 100644 index 00000000..cf4eb974 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutSineService.test.ts @@ -0,0 +1,10 @@ +import { Easing } from "../../Easing"; +import { describe, expect, it } from "vitest"; + +describe("Easing.js method test", () => +{ + it("outSine method test", () => + { + expect(Easing.outSine(0.1, 0.5, 0.5, 1)).toBe(0.5782172325201155); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Easing/service/EasingOutSineService.ts b/packages/ui/src/Easing/service/EasingOutSineService.ts new file mode 100644 index 00000000..c58bd644 --- /dev/null +++ b/packages/ui/src/Easing/service/EasingOutSineService.ts @@ -0,0 +1,15 @@ +/** + * @description Easing out sine function + * + * @param {number} t + * @param {number} b + * @param {number} c + * @param {number} d + * @return {number} + * @method + * @public + */ +export const execute = (t: number, b: number, c: number, d: number): number => +{ + return c * Math.sin(t / d * (Math.PI / 2)) + b; +}; \ No newline at end of file diff --git a/packages/ui/src/Job.ts b/packages/ui/src/Job.ts index 900b5ba4..870c002e 100644 --- a/packages/ui/src/Job.ts +++ b/packages/ui/src/Job.ts @@ -1,13 +1,9 @@ +import type { IObject } from "./interface/IObject"; +import type { IEntriesObject } from "./interface/IEntriesObject"; import { Easing } from "./Easing"; -import { - EventDispatcher, - Event -} from "@next2d/events"; -import { - $setTimeout, - $performance, - $cancelAnimationFrame -} from "@next2d/share"; +import { execute as jobStopService } from "./Job/service/JobStopService"; +import { execute as jobStartUseCase } from "./Job/usecase/JobStartUseCase"; +import { EventDispatcher } from "@next2d/events"; /** * @class @@ -16,464 +12,201 @@ import { */ export class Job extends EventDispatcher { - private readonly _$target: any; - private _$delay: number; - private _$duration: number; - private _$ease: Function; - private _$from: any; - private _$names: any[] | null; - private _$startTime: number; - private _$stopFlag: boolean; - private _$forceStop: boolean; - private _$to: any; - private _$currentTime: number; - private _$timerId: number; - // eslint-disable-next-line no-use-before-define - private _$nextJob: Job | null; - /** - * @param {object} target - * @param {object} [from=null] - * @param {object} [to=null] - * @param {number} [delay=0] - * @param {number} [duration=1] - * @param {function} [ease=null] + * @description イージングの対象オブジェクト + * Target object of easing * - * @constructor + * @type {object} + * @default null * @public */ - constructor ( - target: any, - from: any = null, to: any = null, - delay: number = 0, duration: number = 1, - ease: Function | null = null - ) { - - super(); - - /** - * @type {object} - * @private - */ - this._$target = target; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$delay = delay; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$duration = duration; - - /** - * @type {function} - * @default Easing.linear - * @private - */ - this._$ease = ease || Easing.linear; - - /** - * @type {object} - * @default null - * @private - */ - this._$from = from; - - /** - * @type {array} - * @default null - * @private - */ - this._$names = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$startTime = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$stopFlag = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$forceStop = false; - - /** - * @type {object} - * @default null - * @private - */ - this._$to = to; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$currentTime = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$timerId = 0; - - /** - * @type {Job} - * @default null - * @private - */ - this._$nextJob = null; - } + public readonly target: any; /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. + * @description イージングのエントリーオブジェクト + * Entry object of easing * - * @return {string} - * @default [class Job] - * @method - * @static + * @type {array} + * @default null + * @public */ - static toString (): string - { - return "[class Job]"; - } + public entries: IEntriesObject[] | null; /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. + * @description イージングの開始時間 + * Start time of easing * - * @return {string} - * @default next2d.ui.Job - * @const - * @static + * @type {number} + * @default 0 + * @public */ - static get namespace (): string - { - return "next2d.ui.Job"; - } + public startTime: number; /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. + * @description イージングの強制停止フラグ + * Forced stop flag of easing * - * @return {string} - * @default [object Job] - * @method + * @type {boolean} + * @default false * @public */ - toString (): string - { - return "[object Job]"; - } + public stopFlag: boolean; /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. + * @description イージングの遅延実行のタイマーID + * Timer ID for delayed execution of easing * - * @return {string} - * @default next2d.ui.Job - * @const - * @public + * @type {number} + * @default -1 + * @protected */ - get namespace (): string - { - return "next2d.ui.Job"; - } + public $timerId: number; /** - * @member {function} - * @default Easing.linear + * @description イージングの開始までの遅延時間を返します。 + * Returns the delay time until the start of the easing. + * + * @type {number} + * @default 0 * @public */ - get ease (): Function - { - return this._$ease; - } - set ease (ease: Function) - { - if (typeof ease === "function") { - this._$ease = ease; - } - } + public delay: number; /** - * @member {number} - * @default 0 + * @description イージング完了時間を返します。 + * Returns the easing completion time. + * + * @type {number} + * @default 1 * @public */ - get delay (): number - { - return this._$delay; - } - set delay (delay: number) - { - this._$delay = delay; - } + public duration: number; /** - * @member {number} - * @default 1 + * @description イージングの計算関数を返します。 + * Returns the calculation function of the easing. + * + * @see Easing + * @type {function} + * @default Easing.linear * @public */ - get duration (): number - { - return this._$duration; - } - set duration (duration: number) - { - this._$duration = duration; - } + public ease: Function; /** - * @member {object} - * @default null + * @description イージングの開始オブジェクトを返します。 + * Returns the start object of the easing. + * + * @type {object} * @public */ - get from (): any - { - return this._$from; - } - set from (from: any) - { - this._$from = from; - } + public from: IObject; /** - * @member {object} - * @default null + * @description イージングの終了オブジェクトを返します。 + * Returns the end object of the easing. + * + * @type {object} * @public */ - get to (): any - { - return this._$to; - } - set to (to: any) - { - this._$to = to; - } + public to: IObject; /** - * @member {object} - * @readonly + * @description イージングの現在時間を返します。 + * Returns the current time of the easing. + * + * @type {number} + * @default 0 * @public */ - get target (): any - { - return this._$target; - } + public currentTime: number; /** - * @description 指定したjobを次に開始します。nullで解消 - * Starts the next specified job, resolved by null + * @description イージングの次のjobを返します。 + * Returns the next job of the easing. * * @member {Job | null} * @default null + * @readonly * @public */ - chain (job: Job | null): Job | null - { - this._$nextJob = job; - return job; - } + + public nextJob: Job | null; /** - * @return {void} - * @method + * @param {object} target + * @param {object} [from=null] + * @param {object} [to=null] + * @param {number} [delay=0] + * @param {number} [duration=1] + * @param {function} [ease=null] + * + * @constructor * @public */ - initialize (): void - { - if (this._$forceStop) { - return ; - } - - // setup - this._$stopFlag = false; - this._$startTime = $performance.now(); + constructor ( + target: any, + from: IObject, + to: IObject, + delay: number = 0, + duration: number = 1, + ease: Function | null = null + ) { + super(); - this._$names = this._$entries(this._$from); + this.target = target; + this.from = from; + this.to = to; + this.delay = delay; + this.duration = duration; + this.ease = ease || Easing.linear; - // start - this._$update(); + // default value + this.currentTime = 0; + this.nextJob = null; + this.entries = null; + this.startTime = 0; + this.stopFlag = false; + this.$timerId = -1; } /** - * @param {object} object - * @return {array} + * @description 指定したjobを次に開始します。nullで解消 + * Starts the next specified job, resolved by null + * + * @return {Job | null} * @method - * @private + * @public */ - _$entries (object: any): any[] + chain (job: Job | null): Job | null { - const entries: any[] = Object.entries(object); - - for (let idx = 0; idx < entries.length; ++idx) { - - const values = entries[idx]; - - const value: any = values[1]; - if (value && typeof value === "object") { - values[1] = this._$entries(value); - } - } - - return entries; + this.nextJob = job; + return job; } /** + * @description イージングを開始します。 + * Starts the easing. + * * @return {void} * @method * @public */ start (): void { - if (this._$timerId) { - $cancelAnimationFrame(this._$timerId); - } - - this._$forceStop = false; - - if (this._$delay) { - - $setTimeout((): void => - { - this.initialize(); - }, this._$delay * 1000); - - return ; - } - - this.initialize(); + jobStartUseCase(this); } /** + * @description イージングを停止します。 + * Stops the easing. + * * @return {void} * @method * @public */ stop (): void { - if (this._$timerId) { - $cancelAnimationFrame(this._$timerId); - } - - if (this.hasEventListener(Event.STOP)) { - this.dispatchEvent(new Event(Event.STOP)); - this.removeAllEventListener(Event.STOP); - } - - this._$names = null; - this._$forceStop = true; - this._$stopFlag = true; - } - - /** - * @return {void} - * @method - * @private - */ - _$update (): void - { - if (this._$stopFlag) { - return ; - } - - if (!this._$names) { - return this.stop(); - } - - // update current time - this._$currentTime = ($performance.now() - this._$startTime) * 0.001; - - this._$updateProperty( - this._$target, this._$from, this._$to, this._$names - ); - - if (this.hasEventListener(Event.UPDATE)) { - this.dispatchEvent(new Event(Event.UPDATE)); - } - - if (this._$currentTime >= this._$duration) { - if (this.hasEventListener(Event.COMPLETE)) { - this.dispatchEvent(new Event(Event.COMPLETE)); - } - - if (this._$nextJob) { - this._$nextJob.start(); - } - } else { - this._$timerId = requestAnimationFrame(() => { - this._$update(); - }); - } - } - - /** - * @param {object} target - * @param {object} from - * @param {object} to - * @param {array} names - * @return {void} - * @method - * @private - */ - _$updateProperty (target: any, from: any, to: any, names: any[]): void - { - for (let idx = 0; idx < names.length; ++idx) { - - const values = names[idx]; - - const name = values[0]; - if (name === "__proto__" - || name === "constructor" - || name === "prototype" - ) { - continue; - } - - const value = values[1]; - if (value && typeof value === "object") { - this._$updateProperty(target[name], from[name], to[name], value); - continue; - } - - if (!(name in target)) { - continue; - } - - // update - const fromValue = from[name]; - if (this._$duration > this._$currentTime) { - - target[name] = this._$ease( - this._$currentTime, - fromValue, to[name] - fromValue, - this._$duration - ); - - } else { - - target[name] = to[name]; - - } - } + jobStopService(this); } -} +} \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobEntriesService.test.ts b/packages/ui/src/Job/service/JobEntriesService.test.ts new file mode 100644 index 00000000..dccf65cb --- /dev/null +++ b/packages/ui/src/Job/service/JobEntriesService.test.ts @@ -0,0 +1,80 @@ +import type { IEntriesObject } from "../../interface/IEntriesObject"; +import { execute } from "./JobEntriesService"; +import { describe, expect, it } from "vitest"; + +describe("JobEntriesService.js test", () => +{ + it("execute test case1", () => + { + const entries = execute({ "x": 100, "y": 200 }); + expect(entries.length).toBe(2); + expect(entries[0].name).toBe("x"); + expect(entries[0].value).toBe(100); + expect(entries[1].name).toBe("y"); + expect(entries[1].value).toBe(200); + }); + + it("execute test case2", () => + { + const entries = execute({ + "x": 100, + "y": 200, + "matrix": { + "a": 1, + "b": 2 + } + }); + + expect(entries.length).toBe(3); + expect(entries[0].name).toBe("x"); + expect(entries[0].value).toBe(100); + expect(entries[1].name).toBe("y"); + expect(entries[1].value).toBe(200); + + const matrix = entries[2].value; + expect(matrix[0].name).toBe("a"); + expect(matrix[0].value).toBe(1); + expect(matrix[1].name).toBe("b"); + expect(matrix[1].value).toBe(2); + }); + + it("execute test case3", () => + { + const entries = execute({ + "x": 100, + "y": 200, + "transform": { + "matrix": { + "a": 1, + "b": 2 + }, + "color": { + "red": 255, + "green": 255 + } + } + + }); + + expect(entries.length).toBe(3); + expect(entries[0].name).toBe("x"); + expect(entries[0].value).toBe(100); + expect(entries[1].name).toBe("y"); + expect(entries[1].value).toBe(200); + + const transform = entries[2].value as IEntriesObject[]; + expect(transform.length).toBe(2); + + const matrix = transform[0].value; + expect(matrix[0].name).toBe("a"); + expect(matrix[0].value).toBe(1); + expect(matrix[1].name).toBe("b"); + expect(matrix[1].value).toBe(2); + + const color = transform[1].value; + expect(color[0].name).toBe("red"); + expect(color[0].value).toBe(255); + expect(color[1].name).toBe("green"); + expect(color[1].value).toBe(255); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobEntriesService.ts b/packages/ui/src/Job/service/JobEntriesService.ts new file mode 100644 index 00000000..2c2cf352 --- /dev/null +++ b/packages/ui/src/Job/service/JobEntriesService.ts @@ -0,0 +1,24 @@ +import type { IEntriesObject } from "../../interface/IEntriesObject"; +import type { IObject } from "../../interface/IObject"; + +/** + * @description Tweenの開始/終了のオブジェクトを配列に変換 + * Convert tween start/end objects to arrays + * + * @param {object} object + * @return {array} + * @method + * @private + */ +export const execute = (object: IObject): IEntriesObject[] => +{ + const entries: IEntriesObject[] = []; + for (const [name, value] of Object.entries(object)) + { + entries.push({ + "name": name, + "value": typeof value === "number" ? value : execute(value) + }); + } + return entries; +}; \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobStopService.ts b/packages/ui/src/Job/service/JobStopService.ts new file mode 100644 index 00000000..fec88fcb --- /dev/null +++ b/packages/ui/src/Job/service/JobStopService.ts @@ -0,0 +1,21 @@ +import type { Job } from "../../Job"; +import { JobEvent } from "@next2d/events"; + +/** + * @description ジョブの停止処理関数 + * Stop process function + * + * @param {Job} job + * @return {void} + * @method + * @protected + */ +export const execute = (job: Job): void => +{ + cancelAnimationFrame(job.$timerId); + if (job.hasEventListener(JobEvent.STOP)) { + job.dispatchEvent(new JobEvent(JobEvent.STOP)); + } + job.entries = null; + job.stopFlag = true; +}; \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobUpdateFrameService.test.ts b/packages/ui/src/Job/service/JobUpdateFrameService.test.ts new file mode 100644 index 00000000..d597eaee --- /dev/null +++ b/packages/ui/src/Job/service/JobUpdateFrameService.test.ts @@ -0,0 +1,33 @@ +import { execute } from "./JobUpdateFrameService"; +import { Job } from "../../Job"; +import { describe, expect, it, vi } from "vitest"; + +describe("JobUpdateFrameService.js test", () => +{ + it("execute test case1", () => + { + const MockJob = vi.fn().mockImplementation(() => + { + return { + "stopFlag": true + } as unknown as Job; + }); + + const job = new MockJob(); + expect(execute(job, 0)).toBe(-1); + }); + + it("execute test case2", () => + { + const MockJob = vi.fn().mockImplementation(() => + { + return { + "stopFlag": false, + "entries": null + } as unknown as Job; + }); + + const job = new MockJob(); + expect(execute(job, 0)).toBe(-1); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobUpdateFrameService.ts b/packages/ui/src/Job/service/JobUpdateFrameService.ts new file mode 100644 index 00000000..4bb67a61 --- /dev/null +++ b/packages/ui/src/Job/service/JobUpdateFrameService.ts @@ -0,0 +1,57 @@ +import type { IEntriesObject } from "../../interface/IEntriesObject"; +import type { Job } from "../../Job"; +import { execute as jobUpdatePropertyService } from "./JobUpdatePropertyService"; +import { JobEvent } from "@next2d/events"; + +/** + * @description 繰り返しのアップデート処理関数 + * Update process function + * + * @param {Job} job + * @param {number} timestamp + * @return {array} + * @method + * @private + */ +export const execute = (job: Job, timestamp: number): number => +{ + if (job.stopFlag || !job.entries) { + return -1; + } + + // update current time + job.currentTime = (timestamp - job.startTime) / 1000; + + // update property + jobUpdatePropertyService( + job, job.target, + job.from, job.to, + job.entries as IEntriesObject[] + ); + + // update event + if (job.hasEventListener(JobEvent.UPDATE)) { + job.dispatchEvent(new JobEvent(JobEvent.UPDATE)); + } + + // complete logic + if (job.currentTime >= job.duration) { + + // complete event + if (job.hasEventListener(JobEvent.COMPLETE)) { + job.dispatchEvent(new JobEvent(JobEvent.COMPLETE)); + } + + // next job + if (job.nextJob) { + job.nextJob.start(); + } + + return -1; + } + + return requestAnimationFrame((timestamp: number): void => + { + execute(job, timestamp); + }); +}; \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobUpdatePropertyService.test.ts b/packages/ui/src/Job/service/JobUpdatePropertyService.test.ts new file mode 100644 index 00000000..3cb79e84 --- /dev/null +++ b/packages/ui/src/Job/service/JobUpdatePropertyService.test.ts @@ -0,0 +1,64 @@ +import { execute } from "./JobUpdatePropertyService"; +import { execute as jobEntriesService } from "./JobEntriesService"; +import { Job } from "../../Job"; +import { describe, expect, it } from "vitest"; + +describe("JobUpdatePropertyService.js test", () => +{ + it("execute test case1", () => + { + const target = { "a": 1, "b": 2 }; + const from = { "a": 0, "b": 0 }; + const to = { "a": 10, "b": 20 }; + const job = new Job(target, from, to); + const entries = jobEntriesService(from); + + execute(job, target, from, to, entries); + + expect(target.a).toBe(0); + expect(target.b).toBe(0); + }); + + it("execute test case2", () => + { + const target = { "a": 1, "b": 2 }; + const from = { "a": 0, "b": 0 }; + const to = { "a": 10, "b": 20 }; + const job = new Job(target, from, to); + const entries = jobEntriesService(from); + + job.duration = -1; + + execute(job, target, from, to, entries); + expect(target.a).toBe(10); + expect(target.b).toBe(20); + }); + + it("execute test case3", () => + { + const target = { + "a": 1, + "b": 2, + "color": { + "red": 128 + } + }; + const from = { "a": 0, "b": 0, "color": { "red": 0 } }; + const to = { "a": 10, "b": 20, "color": { "red": 255 } }; + const job = new Job(target, from, to); + const entries = jobEntriesService(from); + + execute(job, target, from, to, entries); + + expect(target.a).toBe(0); + expect(target.b).toBe(0); + expect(target.color.red).toBe(0); + + job.duration = -1; + + execute(job, target, from, to, entries); + expect(target.a).toBe(10); + expect(target.b).toBe(20); + expect(target.color.red).toBe(255); + }); +}); \ No newline at end of file diff --git a/packages/ui/src/Job/service/JobUpdatePropertyService.ts b/packages/ui/src/Job/service/JobUpdatePropertyService.ts new file mode 100644 index 00000000..645e2cfd --- /dev/null +++ b/packages/ui/src/Job/service/JobUpdatePropertyService.ts @@ -0,0 +1,66 @@ +import type { IEntriesObject } from "../../interface/IEntriesObject"; +import type { IObject } from "../../interface/IObject"; +import type { Job } from "../../Job"; + +/** + * @description fromのオブジェクトのプロパティを元に、targetのプロパティの値を更新 + * Update the value of the target property based on the properties of the from object + * + * @param {Job} job + * @param {object} target + * @param {object} from + * @param {object} to + * @param {array} entries + * @return {void} + * @method + * @private + */ +export const execute = ( + job: Job, + target: any, + from: IObject, + to: IObject, + entries: IEntriesObject[] +): void => { + + for (let idx = 0; idx < entries.length; ++idx) { + + const entry = entries[idx]; + if (!entry) { + continue; + } + + const name = entry.name; + if (!(name in target) || !(name in to)) { + continue; + } + + if (typeof entry.value !== "number") { + execute( + job, + target[name], + from[name] as IObject, + to[name] as IObject, + entry.value as IEntriesObject[] + ); + continue; + } + + // update + const fromValue = from[name] as number; + if (job.duration > job.currentTime) { + + target[name] = job.ease( + job.currentTime, + fromValue, to[name] as number - fromValue, + job.duration + ); + + } else { + + // Easing end + target[name] = to[name] as number; + + } + } +}; \ No newline at end of file diff --git a/packages/ui/src/Job/usecase/JobBootUseCase.ts b/packages/ui/src/Job/usecase/JobBootUseCase.ts new file mode 100644 index 00000000..4b5f96c0 --- /dev/null +++ b/packages/ui/src/Job/usecase/JobBootUseCase.ts @@ -0,0 +1,31 @@ +import type { Job } from "../../Job"; +import { execute as jobEntriesService } from "../service/JobEntriesService"; +import { execute as jobUpdateFrameService } from "../service/JobUpdateFrameService"; + +/** + * @description ジョブの実行処理関数 + * Execution process function + * + * @param {Job} job + * @return {void} + * @method + * @protected + */ +export const execute = (job: Job): void => +{ + if (job.stopFlag) { + return ; + } + + // create entries + job.entries = jobEntriesService(job.from); + if (!job.entries) { + return ; + } + + // setup + job.startTime = performance.now(); + + // start + job.$timerId = jobUpdateFrameService(job, job.startTime); +}; \ No newline at end of file diff --git a/packages/ui/src/Job/usecase/JobStartUseCase.ts b/packages/ui/src/Job/usecase/JobStartUseCase.ts new file mode 100644 index 00000000..b14cc43f --- /dev/null +++ b/packages/ui/src/Job/usecase/JobStartUseCase.ts @@ -0,0 +1,30 @@ +import type { Job } from "../../Job"; +import { execute as jobBootUseCase } from "./JobBootUseCase"; + +/** + * @description ジョブの開始処理関数 + * Start process function + * + * @param {Job} job + * @return {void} + * @method + * @protected + */ +export const execute = (job: Job): void => +{ + // stop job + cancelAnimationFrame(job.$timerId); + + // reset + job.stopFlag = false; + + // delayed start + if (job.delay) { + setTimeout(() => + { + jobBootUseCase(job); + }, job.delay * 1000); + } else { + jobBootUseCase(job); + } +}; \ No newline at end of file diff --git a/packages/ui/src/Tween.ts b/packages/ui/src/Tween.ts index 480ccf4a..816eb4a1 100644 --- a/packages/ui/src/Tween.ts +++ b/packages/ui/src/Tween.ts @@ -1,3 +1,4 @@ +import type { IObject } from "./interface/IObject"; import { Job } from "./Job"; /** @@ -6,62 +7,6 @@ import { Job } from "./Job"; */ export class Tween { - /** - * @description 指定されたクラスのストリングを返します。 - * Returns the string representation of the specified class. - * - * @return {string} - * @default [class Tween] - * @method - * @static - */ - static toString (): string - { - return "[class Tween]"; - } - - /** - * @description 指定されたクラスの空間名を返します。 - * Returns the space name of the specified class. - * - * @return {string} - * @default next2d.ui.Tween - * @const - * @static - */ - static get namespace (): string - { - return "next2d.ui.Tween"; - } - - /** - * @description 指定されたオブジェクトのストリングを返します。 - * Returns the string representation of the specified object. - * - * @return {string} - * @default [object Tween] - * @method - * @public - */ - toString (): string - { - return "[object Tween]"; - } - - /** - * @description 指定されたオブジェクトの空間名を返します。 - * Returns the space name of the specified object. - * - * @return {string} - * @default next2d.ui.Tween - * @const - * @public - */ - get namespace (): string - { - return "next2d.ui.Tween"; - } - /** * @description 新しいJobクラスを追加します * Add a new Job class @@ -77,7 +22,7 @@ export class Tween * @static */ static add ( - target: any, from: any, to: any, + target: any, from: IObject, to: IObject, delay: number = 0, duration: number = 1, ease: Function | null = null ): Job { diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 59e47a36..14de8cb4 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,3 +1,3 @@ export * from "./Easing"; -export * from "./Job"; -export * from "./Tween"; \ No newline at end of file +export * from "./Tween"; +export * from "./Job"; \ No newline at end of file diff --git a/packages/ui/src/interface/IEntriesObject.ts b/packages/ui/src/interface/IEntriesObject.ts new file mode 100644 index 00000000..b7c8dafb --- /dev/null +++ b/packages/ui/src/interface/IEntriesObject.ts @@ -0,0 +1,4 @@ +export interface IEntriesObject { + name: string; + value: number | IEntriesObject[]; +} \ No newline at end of file diff --git a/packages/ui/src/interface/IObject.ts b/packages/ui/src/interface/IObject.ts new file mode 100644 index 00000000..b042bf88 --- /dev/null +++ b/packages/ui/src/interface/IObject.ts @@ -0,0 +1,3 @@ +export interface IObject { + [key: string]: number | IObject +} \ No newline at end of file diff --git a/packages/util/README.md b/packages/util/README.md deleted file mode 100644 index af865cd8..00000000 --- a/packages/util/README.md +++ /dev/null @@ -1,11 +0,0 @@ -@next2d/util -============= - -## Installation - -``` -npm install @next2d/util -``` - -## License -This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details. diff --git a/packages/util/package.json b/packages/util/package.json deleted file mode 100644 index a091ff5e..00000000 --- a/packages/util/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@next2d/util", - "version": "*", - "description": "Next2D Util Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", - "license": "MIT", - "homepage": "https://next2d.app", - "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - } - }, - "keywords": [ - "Next2D", - "Next2D Util" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/Next2D/Player.git" - }, - "peerDependencies": { - "@next2d/geom": "file:../geom", - "@next2d/net": "file:../net", - "@next2d/core": "file:../core", - "@next2d/events": "file:../events", - "@next2d/display": "file:../display", - "@next2d/text": "file:../text", - "@next2d/media": "file:../media", - "@next2d/interface": "file:../interface" - } -} diff --git a/packages/util/src/Global.ts b/packages/util/src/Global.ts deleted file mode 100644 index 8e6eff34..00000000 --- a/packages/util/src/Global.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { LoaderInfo } from "@next2d/display"; - -/** - * @type {number} - */ -let instanceId: number = 0; - -/** - * @return {number} - * @method - * @public - */ -export const $getInstanceId = (): number => -{ - return instanceId++; -}; - -/** - * @type {number} - */ -let loaderInfoId: number = 0; - -/** - * @return {number} - * @method - * @public - */ -export const $getLoaderInfoId = (): number => -{ - return loaderInfoId++; -}; - -/** - * @type {LoaderInfo | null} - * @public - */ -let $currentLoaderInfo: LoaderInfo | null = null; - -export const $getCurrentLoaderInfo = (): LoaderInfo | null => -{ - return $currentLoaderInfo; -}; - -export const $setCurrentLoaderInfo = (loader_info: LoaderInfo | null = null) => -{ - $currentLoaderInfo = loader_info; -}; - -let $eventType: string = ""; - -export const $getEventType = (): string => -{ - return $eventType; -}; - -export const $setEventType = (event_type: string) => -{ - $eventType = event_type; -}; - -/** - * @type {Event | null} - * @public - */ -let $event: MouseEvent | TouchEvent | Event | null = null; - -export const $getEvent = (): MouseEvent | TouchEvent | Event | null => -{ - return $event; -}; - -export const $setEvent = (event: MouseEvent | TouchEvent | Event | null = null) => -{ - $event = event; -}; - -let $soundMixerVolume: number = 1; - -export const $getSoundMixerVolume = (): number => -{ - return $soundMixerVolume; -}; - -export const $setSoundMixerVolume = (volume: number) => -{ - $soundMixerVolume = volume; -}; \ No newline at end of file diff --git a/packages/util/src/Shortcut.ts b/packages/util/src/Shortcut.ts deleted file mode 100644 index 0e67f911..00000000 --- a/packages/util/src/Shortcut.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @shortcut - * @type {Window} - * @const - * @static - */ -export const $window: Window = window; - -/** - * @shortcut - * @type {Document} - * @const - * @static - */ -export const $document: Document = $window.document; - -/** - * @shortcut - * @type {RegExp} - * @const - * @static - */ -export const $RegExp: typeof RegExp = RegExp; diff --git a/packages/util/src/Util.ts b/packages/util/src/Util.ts deleted file mode 100644 index b8c37a02..00000000 --- a/packages/util/src/Util.ts +++ /dev/null @@ -1,1284 +0,0 @@ -import { URLRequestHeader } from "@next2d/net"; -import { Player } from "@next2d/core"; -import { Event as Next2DEvent } from "@next2d/events"; -import { - Matrix, - ColorTransform, - Point -} from "@next2d/geom"; -import { - Stage, - Sprite, - MovieClip, - Shape -} from "@next2d/display"; -import { TextField } from "@next2d/display"; -import { - Video, - Sound -} from "@next2d/media"; -import { - DisplayObjectImpl, - DragRulesImpl, - AjaxOptionImpl, - ImageTypeImpl, - DropTargetImpl, - ParentImpl, - BitmapDrawObjectImpl, - NoCodeDataZlibImpl, - PropertyMessageMapImpl, - BlendModeImpl -} from "@next2d/interface"; -import { $getEvent } from "./Global"; -import { - $document, - $window -} from "./Shortcut"; -import { - $getArray, - $poolArray, - $Math, - $clearTimeout, - $setTimeout, - $devicePixelRatio -} from "@next2d/share"; - -/** - * @type {string} - * @const - * @static - */ -export const $PREFIX: string = "__next2d__"; - -/** - * @type {number} - * @const - * @static - */ -export const $HIGH_SAMPLES: number = 4; - -/** - * @type {number} - * @const - * @static - */ -export const $MEDIUM_SAMPLES: number = 2; - -/** - * @type {number} - * @const - * @static - */ -export const $LOW_SAMPLES: number = 0; - -/** - * @type {string} - * @const - * @static - */ -export const $LOAD_START: string = "loadstart"; - -/** - * @type {string} - * @const - * @static - */ -export const $PROGRESS: string = "progress"; - -/** - * @type {string} - * @const - * @static - */ -export const $LOADEND: string = "loadend"; - -/** - * @type {string} - * @const - * @static - */ -export const $TOUCH_START: string = "touchstart"; - -/** - * @type {string} - * @const - * @static - */ -export const $TOUCH_MOVE: string = "touchmove"; - -/** - * @type {string} - * @const - * @static - */ -export const $TOUCH_END: string = "touchend"; - -/** - * @type {string} - * @const - * @static - */ -export const $MOUSE_DOWN: string = "mousedown"; - -/** - * @type {string} - * @const - * @static - */ -export const $MOUSE_MOVE: string = "mousemove"; - -/** - * @type {string} - * @const - * @static - */ -export const $MOUSE_UP: string = "mouseup"; - -/** - * @type {string} - * @const - * @static - */ -export const $MOUSE_WHEEL: string = "wheel"; - -/** - * @type {string} - * @const - * @static - */ -export const $DOUBLE_CLICK: string = "dblclick"; - -/** - * @type {string} - * @const - * @static - */ -export const $MOUSE_LEAVE: string = "mouseleave"; - -/** - * @type {string} - * @const - * @static - */ -export const $KEY_DOWN: string = "keydown"; - -/** - * @type {string} - * @const - * @static - */ -export const $KEY_UP: string = "keyup"; - -/** - * @type {string} - * @const - * @static - */ -export const $SCROLL: string = "scroll"; - -/** - * @type {number} - * @static - */ -// eslint-disable-next-line -export let $soundMixerVolume: number = 1; - -/** - * @type {AudioContext} - * @static - */ -// eslint-disable-next-line -export let $audioContext: AudioContext|null = null; - -/** - * @type {Map} - * @const - * @static - */ -export const $variables: Map = new Map(); - -/** - * @type {DisplayObject|null} - * @default null - * @static - */ -// eslint-disable-next-line -export let $dropTarget: DropTargetImpl | null = null; - -/** - * @param {DisplayObject} drop_target - * @return {void} - * @method - * @public - */ -export const $setDropTarget = (drop_target: DropTargetImpl | null): void => -{ - $dropTarget = drop_target; -}; - -/** - * @type {{bounds: null, lock: boolean, position: {x: number, y: number}}} - * @const - * @static - */ -export const $dragRules: DragRulesImpl = { - "lock": false, - "position": { - "x": 0, - "y": 0 - }, - "bounds": null -}; - -/** - * @description RGB to Linear Table - * @type {Float32Array} - * @const - * @static - */ -export const $rgbToLinearTable: Float32Array = new Float32Array(256); - -/** - * @description RGB to Linear Table - * @type {Float32Array} - * @const - * @static - */ -export const $rgbIdentityTable: Float32Array = new Float32Array(256); -for (let idx = 0; idx < 256; ++idx) { - $rgbToLinearTable[idx] = $Math.pow(idx / 255, 2.23333333); - $rgbToLinearTable[idx] = idx / 255; -} - -/** - * @type {Float32Array} - * @const - * @static - */ -export const $MATRIX_HIT_ARRAY_IDENTITY: Float32Array = new Float32Array([1, 0, 0, 1, 0, 0]); - -/** - * @type {array} - * @const - * @static - */ -export const $audios: Sound[] = []; - -/** - * 使用済みになったMatrix Objectをプール - * Pool Matrix objects that are no longer in use. - * - * @type {Matrix[]} - * @const - * @static - */ -const $matrices: Matrix[] = []; - -/** - * 使用済みになったColorTransform Objectをプール - * Pool ColorTransform objects that are no longer in use. - * - * @type {ColorTransform[]} - * @const - * @static - */ -const $colors: ColorTransform[] = []; - -/** - * @type {Map} - * @const - * @static - */ -export const $bitmapDrawMap: Map = new Map(); - -// eslint-disable-next-line -export let $isChrome: boolean = false; -// eslint-disable-next-line -export let $isSafari: boolean = false; -// eslint-disable-next-line -export let $isFireFox: boolean = false; -// eslint-disable-next-line -export let $isAndroid: boolean = false; -// eslint-disable-next-line -export let $isiOS: boolean = false; -// eslint-disable-next-line -export let $isTouch: boolean = false; - -/** - * @type {HTMLTextAreaElement} - * @const - */ -export const $textArea: HTMLTextAreaElement = $document.createElement("textarea"); - -let style = ""; -style += "position: fixed;"; -style += "top: 0;"; -style += "left: 0;"; -style += "font-size: 16px;"; -style += "border: 0;"; -style += "resize: none;"; -style += "opacity: 0;"; -style += "z-index: -1;"; -style += "pointer-events: none;"; -$textArea.setAttribute("style", style); - -$textArea.tabIndex = -1; - -$textArea.addEventListener("compositionstart", (): void => -{ - const player: Player = $window.next2d.player; - const textField: TextField | null = player._$textField; - if (textField) { - textField.compositionStart(); - } -}); - -$textArea.addEventListener("compositionupdate", (event: CompositionEvent): void => -{ - const player: Player = $window.next2d.player; - const textField: TextField | null = player._$textField; - if (textField) { - textField.compositionUpdate(event.data); - } -}); - -$textArea.addEventListener("compositionend", (): void => -{ - const player: Player = $window.next2d.player; - const textField: TextField | null = player._$textField; - if (textField) { - textField.compositionEnd(); - } -}); - -$textArea.addEventListener("input", (event: InputEvent): void => -{ - if (!event.data) { - return ; - } - - const player: Player = $window.next2d.player; - const textField: TextField | null = player._$textField; - if (textField) { - textField.insertText(event.data); - } -}); - -$textArea.addEventListener("keydown", (event: KeyboardEvent): void => -{ - const player: Player = $window.next2d.player; - const textField: TextField | null = player._$textField; - if (!textField) { - return ; - } - - switch (event.key) { - - case "Backspace": - case "Delete": - textField.deleteText(); - break; - - case "Enter": - textField.insertText("\n"); - break; - - case "ArrowLeft": - textField.arrowLeft(); - break; - - case "ArrowRight": - textField.arrowRight(); - break; - - case "ArrowUp": - textField.arrowUp(); - break; - - case "ArrowDown": - textField.arrowDown(); - break; - - case "a": - if (event.metaKey || event.ctrlKey) { - event.preventDefault(); - textField.selectAll(); - } - break; - - case "v": - if (event.metaKey || event.ctrlKey) { - event.preventDefault(); - textField.paste(); - } - break; - - case "c": - if (event.metaKey || event.ctrlKey) { - event.preventDefault(); - textField.copy(); - } - break; - - } -}); - -/** - * @type {HTMLCanvasElement} - * @const - */ -const hitCanvas: HTMLCanvasElement = $document.createElement("canvas"); -hitCanvas.width = 1; -hitCanvas.height = 1; - -const hitContext: CanvasRenderingContext2D | null = hitCanvas.getContext("2d"); -if (!hitContext) { - throw new Error("the CanvasRenderingContext2D is null."); -} - -hitContext.globalAlpha = 0; -hitContext.imageSmoothingEnabled = false; - -export const $hitContext: CanvasRenderingContext2D = hitContext; - -/** - * @type {Float32Array[]} - * @private - */ -const $renderBufferArray: Float32Array[] = []; - -/** - * @type {array} - * @private - */ -const $renderMessageArray: PropertyMessageMapImpl[] = []; - -/** - * @return {Float32Array} - * @method - * @public - */ -export const $getRenderBufferArray = (): Float32Array => -{ - return $renderBufferArray.length - ? $renderBufferArray.pop() as NonNullable - : new Float32Array(64); -}; - -/** - * @param {Float32Array} buffer - * @return {void} - * @method - * @public - */ -export const $poolRenderBufferArray = (buffer: Float32Array): void => -{ - $renderBufferArray.push(buffer); - console.log("renderBufferArray: ", $renderBufferArray); -}; - -/** - * @return {object} - * @method - * @public - */ -export const $getRenderMessageObject = (): PropertyMessageMapImpl => -{ - return $renderMessageArray.length - ? $renderMessageArray.pop() as NonNullable> - : { "command": "" }; -}; - -/** - * @param {object} object - * @return {void} - * @method - * @public - */ -export const $poolRenderMessageObject = (object: PropertyMessageMapImpl): void => -{ - object.buffer = null; - $renderMessageArray.push(object); - console.log("renderMessageArray: ", $renderMessageArray); -}; - -/** - * @return {Player} - * @method - * @static - */ -export const $currentPlayer = (): Player => -{ - return $window.next2d.player; -}; - -/** - * @return {Point} - * @method - * @static - */ -export const $currentMousePoint = (): Point => -{ - const event: MouseEvent | TouchEvent | Event | null = $getEvent(); - if (!event) { - return new Point(); - } - - // setup - const player: Player = $currentPlayer(); - - let x: number = $window.scrollX; - let y: number = $window.scrollY; - - const div: HTMLElement | null = $document - .getElementById(player.contentElementId); - - if (div) { - const rect: DOMRect = div.getBoundingClientRect(); - x += rect.left; - y += rect.top; - } - - let touchX: number = 0; - let touchY: number = 0; - if ("changedTouches" in event) { - const changedTouche: Touch = event.changedTouches[0]; - touchX = changedTouche.pageX; - touchY = changedTouche.pageY; - } else if ("pageX" in event) { - touchX = event.pageX; - touchY = event.pageY; - } - - const pointX: number = (touchX - x) / player._$scale - player.x / player._$scale / $devicePixelRatio; - const pointY: number = (touchY - y) / player._$scale - player.y / player._$scale / $devicePixelRatio; - - return new Point(pointX, pointY); -}; - -/** - * @param {number} [a=1] - * @param {number} [b=0] - * @param {number} [c=0] - * @param {number} [d=1] - * @param {number} [tx=0] - * @param {number} [ty=0] - * @return {Matrix} - */ -export const $getMatrix = ( - a: number = 1, b: number = 0, - c: number = 0, d: number = 1, - tx: number = 0, ty: number = 0 -): Matrix => { - - const matrix = $matrices.pop(); - if (!matrix) { - return new Matrix(a, b, c, d, tx, ty); - } - - matrix.setTo(a, b, c, d, tx, ty); - - return matrix; -}; - -/** - * @param {Matrix} matrix - * @return {void} - * @method - * @static - */ -export const $poolMatrix = (matrix: Matrix): void => -{ - $matrices.push(matrix); -}; - -/** - * @param {number} [red_multiplier=1] - * @param {number} [green_multiplier=1] - * @param {number} [blue_multiplier=1] - * @param {number} [alpha_multiplier=1] - * @param {number} [red_offset=0] - * @param {number} [green_offset=0] - * @param {number} [blue_offset=0] - * @param {number} [alpha_offset=0] - * @return {ColorTransform} - * @method - * @public - */ -export const $getColorTransform = ( - red_multiplier: number = 1, green_multiplier: number = 1, - blue_multiplier: number = 1, alpha_multiplier: number = 1, - red_offset: number = 0, green_offset: number = 0, - blue_offset: number = 0, alpha_offset: number = 0 -): ColorTransform => { - - const colorTransform = $colors.length ? $colors.pop() : null; - if (!colorTransform) { - return new ColorTransform( - red_multiplier, green_multiplier, blue_multiplier, alpha_multiplier, - red_offset, green_offset, blue_offset, alpha_offset - ); - } - - colorTransform.redMultiplier = red_multiplier; - colorTransform.greenMultiplier = green_multiplier; - colorTransform.blueMultiplier = blue_multiplier; - colorTransform.alphaMultiplier = alpha_multiplier; - colorTransform.redOffset = red_offset; - colorTransform.greenOffset = green_offset; - colorTransform.blueOffset = blue_offset; - colorTransform.alphaOffset = alpha_offset; - - return colorTransform; - -}; - -/** - * @param {ColorTransform} color_transform - * @return {void} - * @method - * @static - */ -export const $poolColorTransform = (color_transform: ColorTransform): void => -{ - $colors.push(color_transform); -}; - -/** - * @param {Sound} sound - * @param {AudioBuffer} audio_buffer - * @return {void} - * @method - * @static - */ -const $decodeAudioSuccess = (sound: Sound, audio_buffer: AudioBuffer): void => -{ - if (sound._$character) { - - sound._$character.audioBuffer = audio_buffer; - - } else { - - sound._$audioBuffer = audio_buffer; - - } -}; - -/** - * @param {Sound} sound - * @param {ArrayBuffer} array_buffer - * @return {Promise} - * @method - * @static - */ -const $decodeAudioFailed = ( - sound: Sound, - array_buffer: ArrayBuffer -): Promise => { - - if (!$audioContext) { - throw new Error("the Audio Context is null."); - } - - const buffer: Uint8Array = new Uint8Array(array_buffer); - - let idx: number = 0; - for(;;) { - - idx = buffer.indexOf(0xff, idx); - - if (idx === -1 || (buffer[idx + 1] & 0xe0) === 0xe0) { - break; - } - - ++idx; - - } - - if (idx > -1) { - - return $audioContext - .decodeAudioData(buffer.subarray(idx).buffer) - .then((audio_buffer: AudioBuffer) => - { - $decodeAudioSuccess(sound, audio_buffer); - return Promise.resolve(sound); - }) - .catch(() => - { - throw new Error("This voice data is not available."); - }); - - } - - throw new Error("This voice data is not available."); -}; - -/** - * @param {Sound} sound - * @return {Promise} - * @method - * @static - */ -export const $decodeAudioData = (sound: Sound): Promise => -{ - if (!$audioContext) { - throw new Error("the AudioContext is null."); - } - - let buffer: ArrayBuffer | null = null; - if (sound._$character) { - const array: number[] | null = sound._$character.buffer; - if (array) { - buffer = new Uint8Array(array).buffer; - $poolArray(array); - sound._$character.buffer = null; - } - } else { - buffer = sound._$arrayBuffer; - } - - if (!buffer) { - return Promise.resolve(sound); - } - - return $audioContext - .decodeAudioData(buffer) - .then((audio_buffer: AudioBuffer) => - { - $decodeAudioSuccess(sound, audio_buffer); - return Promise.resolve(sound); - }) - .catch(() => - { - if (!buffer) { - throw new Error(); - } - - return $decodeAudioFailed(sound, buffer); - }); -}; - -/** - * @return {void} - * @method - * @static - */ -export const $loadAudioData = (): void => -{ - // create AudioContext - if (!$audioContext) { - - $audioContext = new AudioContext(); - $audioContext.resume(); - - } - - if ($audioContext) { - - const promises: Promise[] = $getArray(); - for (let idx: number = 0; idx < $audios.length; ++idx) { - - const sound = $audios[idx]; - - if (sound._$character && sound._$character.audioBuffer) { - promises.push(Promise.resolve(sound)); - } - - if (sound._$audioBuffer) { - promises.push(Promise.resolve(sound)); - } - - promises.push($decodeAudioData(sound)); - } - - Promise - .all(promises) - .then((sounds) => - { - // reset - $audios.length = 0; - - const player: Player = $currentPlayer(); - player._$loaders.push(...sounds); - }); - } - -}; - -/** - * @param {Uint8Array} buffer - * @return {string} - * @method - * @static - */ -export const $getImageType = (buffer: Uint8Array): ImageTypeImpl => -{ - if (buffer[0] === 0xff && buffer[1] === 0xd8) { - return "jpeg"; - } - - if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) { - return "gif"; - } - - if (buffer[0] === 0x89 && buffer[1] === 0x50 && - buffer[2] === 0x4E && buffer[3] === 0x47 && - buffer[4] === 0x0D && buffer[5] === 0x0A && - buffer[6] === 0x1A && buffer[7] === 0x0A - ) { - return "png"; - } - - if (buffer[0] === 0x42 && buffer[1] === 0x4d) { - return "bmp"; - } - - throw new Error("Unsupported image formats."); -}; - -/** - * @type {number} - * @static - */ -let $resizeTimerId: number = -1; - -/** - * @return {void} - * @method - * @static - */ -const $resizeExecute = (): void => -{ - const player: Player = $currentPlayer(); - if (player._$loadStatus === Player.LOAD_END) { - - player._$resize(); - - const stage: Stage = player.stage; - if (stage.willTrigger(Next2DEvent.RESIZE)) { - stage.dispatchEvent(new Next2DEvent(Next2DEvent.RESIZE)); - } - } -}; - -/** - * added resize event - */ -$window.addEventListener("resize", (): void => -{ - $clearTimeout($resizeTimerId); - // @ts-ignore - $resizeTimerId = $setTimeout($resizeExecute, 300); -}); - -/** - * @param {AjaxOptionImpl} option - * @return void - * @method - * @public - */ -export const $ajax = (option: AjaxOptionImpl) => -{ - // get or post - let postData: string | null = null; - switch (option.method.toUpperCase()) { - - case "GET": - if (option.data) { - const urls = option.url.split("?"); - - urls[1] = urls.length === 1 - ? option.data.toString() - : `${urls[1]}&${option.data.toString()}`; - - option.url = urls.join("?"); - } - break; - - case "PUT": - case "POST": - if (option.data) { - postData = option.data.toString(); - } - break; - - default: - break; - - } - - // start - const xmlHttpRequest: XMLHttpRequest = new XMLHttpRequest(); - - // init - xmlHttpRequest.open(option.method, option.url, true); - - // set mimeType - xmlHttpRequest.responseType = option.format; - - // use cookie - xmlHttpRequest.withCredentials = option.withCredentials; - - // add event - if (option.event) { - - const keys: string[] = Object.keys(option.event); - for (let idx = 0; idx < keys.length; ++idx) { - - const name: string = keys[idx]; - - // @ts-ignore - xmlHttpRequest.addEventListener(name, option.event[name]); - } - - $poolArray(keys); - } - - // set request header - for (let idx: number = 0; idx < option.headers.length; ++idx) { - const header: URLRequestHeader = option.headers[idx]; - xmlHttpRequest.setRequestHeader(header.name, header.value); - } - - xmlHttpRequest.send(postData); -}; - -/** - * @param {string} header - * @return {array} - */ -export const $headerToArray = (header: string) => -{ - const results: URLRequestHeader[] = $getArray(); - if (header) { - - const headers = header.trim().split("\n"); - - const length = headers.length; - for (let idx = 0; idx < length; ++idx) { - - const values = headers[idx].split(":"); - - results.push(new URLRequestHeader( - `${values[0].trim()}`, - `${values[1].trim()}` - )); - - } - - } - return results; -}; - -/** - * @type {Map} - * @private - */ -const blendMap: Map = new Map([ - ["normal", 1], - ["layer", 2], - ["multiply", 3], - ["screen", 4], - ["lighten", 5], - ["darken", 6], - ["difference", 7], - ["add", 8], - ["subtract", 9], - ["invert", 10], - ["alpha", 11], - ["erase", 12], - ["overlay", 13], - ["hardlight", 14] -]); - -/** - * @param {string} blend - * @return {number} - * @method - * @public - */ -export const $blendToNumber = (blend: BlendModeImpl): number => -{ - return blendMap.has(blend) ? blendMap.get(blend) || 1 : 1; -}; - -/** - * @param {string} character_extends - * @return {DisplayObject} - * @method - * @public - */ -export const $createInstance = (character_extends: string): DisplayObjectImpl => -{ - switch (character_extends) { - - case MovieClip.namespace: - return new MovieClip; - - case Shape.namespace: - return new Shape; - - case TextField.namespace: - return new TextField(); - - case Sprite.namespace: - return new Sprite; - - case Video.namespace: - return new Video(); - - } -}; - -/** - * @type {Worker|null} - */ -export let $rendererWorker: Worker | null = null; - -/** - * @type {Function|null} - */ -export let $removeContainerWorker: Function|null = null; - -/** - * @type {Function|null} - */ -export let $postContainerWorker: Function|null = null; - -/** - * @type {string} - * @static - */ -const $unzipURL: string = URL.createObjectURL(new Blob(["(()=>{\"use strict\";var r=Uint8Array,n=Uint16Array,e=Int32Array,a=new r([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),t=new r([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),i=new r([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),o=function(r,a){for(var t=new n(31),i=0;i<31;++i)t[i]=a+=1<>1|(21845&s)<<1;w=(61680&(w=(52428&w)>>2|(13107&w)<<2))>>4|(3855&w)<<4,d[s]=((65280&w)>>8|(255&w)<<8)>>1}var h=function(r,e,a){for(var t=r.length,i=0,o=new n(e);i>v]=c}else for(f=new n(t),i=0;i>15-r[i]);return f},y=new r(288);for(s=0;s<144;++s)y[s]=8;for(s=144;s<256;++s)y[s]=9;for(s=256;s<280;++s)y[s]=7;for(s=280;s<288;++s)y[s]=8;var b=new r(32);for(s=0;s<32;++s)b[s]=5;var g=h(y,9,1),p=h(b,5,1),m=function(r){for(var n=r[0],e=1;en&&(n=r[e]);return n},k=function(r,n,e){var a=n/8|0;return(r[a]|r[a+1]<<8)>>(7&n)&e},x=function(r,n){var e=n/8|0;return(r[e]|r[e+1]<<8|r[e+2]<<16)>>(7&n)},T=[\"unexpected EOF\",\"invalid block type\",\"invalid length/literal\",\"invalid distance\",\"stream finished\",\"no stream handler\",,\"no callback\",\"invalid UTF-8 data\",\"extra field too long\",\"date not in range 1980-2099\",\"filename too long\",\"stream finishing\",\"invalid zip data\"],z=function(r,n,e){var a=new Error(n||T[r]);if(a.code=r,Error.captureStackTrace&&Error.captureStackTrace(a,z),!e)throw a;return a},E=function(n,e,o,f){var v=n.length,c=f?f.length:0;if(!v||e.f&&!e.l)return o||new r(0);var d=!o,s=d||2!=e.i,w=e.i;d&&(o=new r(3*v));var y,b=function(n){var e=o.length;if(n>e){var a=new r(Math.max(2*e,n));a.set(o),o=a}},T=e.f||0,E=e.p||0,M=e.b||0,S=e.l,U=e.d,A=e.m,C=e.n,q=8*v;do{if(!S){T=k(n,E,1);var D=k(n,E+1,3);if(E+=3,!D){var F=n[(y=E,(H=4+((y+7)/8|0))-4)]|n[H-3]<<8,I=H+F;if(I>v){w&&z(0);break}s&&b(M+F),o.set(n.subarray(H,I),M),e.b=M+=F,e.p=E=8*I,e.f=T;continue}if(1==D)S=g,U=p,A=9,C=5;else if(2==D){var O=k(n,E,31)+257,J=k(n,E+10,15)+4,L=O+k(n,E+5,31)+1;E+=14;for(var N=new r(L),P=new r(19),R=0;R>4)<16)N[R++]=H;else{var Q=0,V=0;for(16==H?(V=3+k(n,E,3),E+=2,Q=N[R-1]):17==H?(V=3+k(n,E,7),E+=3):18==H&&(V=11+k(n,E,127),E+=7);V--;)N[R++]=Q}}var W=N.subarray(0,O),X=N.subarray(O);A=m(W),C=m(X),S=h(W,A,1),U=h(X,C,1)}else z(1);if(E>q){w&&z(0);break}}s&&b(M+131072);for(var Y=(1<>4;if((E+=15&Q)>q){w&&z(0);break}if(Q||z(2),_<256)o[M++]=_;else{if(256==_){$=E,S=null;break}var rr=_-254;if(_>264){var nr=a[R=_-257];rr=k(n,E,(1<>4;if(er||z(3),E+=15&er,X=l[ar],ar>3&&(nr=t[ar],X+=x(n,E)&(1<q){w&&z(0);break}s&&b(M+131072);var tr=M+rr;if(Mn.length)&&(a=n.length),new r(n.subarray(e,a))}(o,0,M):o.subarray(0,M)},M=new r(0);function S(n,e){var a,t,i=function(r){31==r[0]&&139==r[1]&&8==r[2]||z(6,\"invalid gzip data\");var n=r[3],e=10;4&n&&(e+=2+(r[10]|r[11]<<8));for(var a=(n>>3&1)+(n>>4&1);a>0;a-=!r[e++]);return e+(2&n)}(n);return i+8>n.length&&z(6,\"invalid gzip data\"),E(n.subarray(i,-8),{i:2},e&&e.out||new r((t=(a=n).length,(a[t-4]|a[t-3]<<8|a[t-2]<<16|a[t-1]<<24)>>>0)),e&&e.dictionary)}function U(r,n){return E(r.subarray((e=r,a=n&&n.dictionary,(8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31)&&z(6,\"invalid zlib data\"),(e[1]>>5&1)==+!a&&z(6,\"invalid zlib data: \"+(32&e[1]?\"need\":\"unexpected\")+\" dictionary\"),2+(e[1]>>3&4)),-4),{i:2},n&&n.out,n&&n.dictionary);var e,a}var A=\"undefined\"!=typeof TextDecoder&&new TextDecoder;try{A.decode(M,{stream:!0})}catch(r){}\"function\"==typeof queueMicrotask?queueMicrotask:\"function\"==typeof setTimeout&&setTimeout;self.addEventListener(\"message\",(r=>{return n=void 0,e=void 0,t=function*(){const n=31==(e=r.data)[0]&&139==e[1]&&8==e[2]?S(e,a):8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31?function(r,n){return E(r,{i:2},n&&n.out,n&&n.dictionary)}(e,a):U(e,a);var e,a;let t=\"\";for(let r=0;r -{ - if (!$unzipWorker) { - $unzipWorker = new Worker($unzipURL); - } - return $unzipWorker; -}; - -/** - * @type {boolean} - * @static - */ -let $unzipWorkerActive: boolean = false; -export const $updateUnzipWorkerStatus = (status: boolean): void => -{ - $unzipWorkerActive = status; -}; -export const $useUnzipWorker = (): boolean => -{ - return $unzipWorkerActive; -}; - -/** - * @type {string} - * @public - */ -const $renderURL: string = "(()=>{\"use strict\";let t=1,e=0,i=!1;const s=1/0,r=Math,n=Array,a=Map,h=Number,o=Float32Array,_=Int32Array,l=Int16Array,c=OffscreenCanvas,$=isNaN,u=requestAnimationFrame,d=setTimeout,g=clearTimeout,f=new o([1,0,0,1,0,0]),m=new o([1,1,1,1,0,0,0,0]),p=r.PI/180,x=(r.PI,[]),b=[],v=[],T=[],A=[],M=[],y=[],E=[],C=[],S=new c(1,1).getContext(\"2d\"),F=(t=0,e=0,i=0,s=0)=>{const r=C.pop()||{xMin:0,xMax:0,yMin:0,yMax:0};return r.xMin=t,r.xMax=e,r.yMin=i,r.yMax=s,r},B=t=>{C.push(t)},w=(t=0,e=0,i=0,s=0)=>{const r=v.pop()||new o(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},R=t=>{v.push(t)},I=(t=0,e=0,i=0,s=0)=>{const r=b.pop()||new _(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},P=(t=0,e=0,i=0,s=0,r=0,n=0)=>{const a=T.pop()||new o(6);return a[0]=t,a[1]=e,a[2]=i,a[3]=s,a[4]=r,a[5]=n,a},N=t=>{T.push(t)},k=(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0)=>{const _=A.pop()||new o(8);return _[0]=t,_[1]=e,_[2]=i,_[3]=s,_[4]=r,_[5]=n,_[6]=a,_[7]=h,_},L=t=>{A.push(t)},O=(t=0,e=0,i=0,s=0,r=0,n=0,a=0,h=0,_=0)=>{const l=M.pop()||new o(9);return l[0]=t,l[1]=e,l[2]=i,l[3]=s,l[4]=r,l[5]=n,l[6]=a,l[7]=h,l[8]=_,l},U=(...t)=>{const e=y.pop()||[];return t.length&&e.push(...t),e},D=(t=null)=>{t&&(t.length&&(t.length=0),y.push(t))},X=t=>{t.size&&t.clear(),E.push(t)},V=()=>E.pop()||new a,Y=t=>(t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t),z=t=>{const e=1/(t[0]*t[4]-t[3]*t[1]),i=t[3]*t[7]-t[4]*t[6],s=t[1]*t[6]-t[0]*t[7];return O(t[4]*e,0-t[1]*e,0,0-t[3]*e,t[0]*e,0,i*e,s*e,1)},G=(t,e,i,s=null)=>{const n=+t;return $(n)&&null!==s?s:r.min(r.max(e,$(n)?0:n),i)},H=(t,e)=>P(t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]),W=(t,e)=>k(t[0]*e[0],t[1]*e[1],t[2]*e[2],t[3]*e[3],t[0]*e[4]+t[4],t[1]*e[5]+t[5],t[2]*e[6]+t[6],t[3]*e[7]+t[7]),q=(t,e)=>{const i=t.xMax*e[0]+t.yMax*e[2]+e[4],s=t.xMax*e[0]+t.yMin*e[2]+e[4],n=t.xMin*e[0]+t.yMax*e[2]+e[4],a=t.xMin*e[0]+t.yMin*e[2]+e[4],o=t.xMax*e[1]+t.yMax*e[3]+e[5],_=t.xMax*e[1]+t.yMin*e[3]+e[5],l=t.xMin*e[1]+t.yMax*e[3]+e[5],c=t.xMin*e[1]+t.yMin*e[3]+e[5],$=r.min(h.MAX_VALUE,i,s,n,a),u=r.max(0-h.MAX_VALUE,i,s,n,a),d=r.min(h.MAX_VALUE,o,_,l,c),g=r.max(0-h.MAX_VALUE,o,_,l,c);return F($,u,d,g)},j=t=>$(+t)?(t=>{if(!S)return 0;S.fillStyle=t;const e=+`0x${S.fillStyle.slice(1)}`;return S.fillStyle=\"rgba(0, 0, 0, 1)\",e})(`${t}`):+t,K=(t,e,i)=>(t>>16)*(i?e:1)/255,Q=(t,e,i)=>(t>>8&255)*(i?e:1)/255,J=(t,e,i)=>(255&t)*(i?e:1)/255,Z=(t,e=1)=>({R:(16711680&t)>>16,G:(65280&t)>>8,B:255&t,A:255*e}),tt=(t,e,i=!1,s=!1)=>{let r=\"\";return i&&(r=\"italic \"),s&&(r+=\"bold \"),`${r}${e}px '${t}','sans-serif'`},et=t=>{t.color&&L(t.color),t.isLayer=!1,t.isUpdated=null,t.canApply=null,t.matrix=null,t.color=null,t.filters=null,t.blendMode=\"normal\",t.sw=0,t.sh=0,x.push(t)},it=new Map([[1,\"normal\"],[2,\"layer\"],[3,\"multiply\"],[4,\"screen\"],[5,\"lighten\"],[6,\"darken\"],[7,\"difference\"],[8,\"add\"],[9,\"subtract\"],[10,\"invert\"],[11,\"alpha\"],[12,\"erase\"],[13,\"overlay\"],[14,\"hardlight\"]]),st=t=>it.has(t)&&it.get(t)||\"normal\",rt=new class{constructor(){this._$pool=[],this._$store=new Map,this._$timerMap=new Map,this._$context=null}set context(t){this._$context=t}reset(){for(const t of this._$store.values()){for(const e of t.values())this.destroy(e);X(t)}this._$store.clear(),this._$context&&this._$context.frameBuffer.clearCache()}destroy(t=null){if(t&&\"object\"==typeof t)if(t instanceof WebGLTexture)u((()=>{this._$context&&this._$context.frameBuffer.releaseTexture(t)}));else{if(\"canvas\"in t&&t instanceof CanvasRenderingContext2D){const e=t.canvas,i=e.width,s=e.height;t.clearRect(0,0,i+1,s+1),e.width=e.height=1,this._$pool.push(e)}this._$context&&\"index\"in t&&this._$context.frameBuffer.textureManager.releasePosition(t)}}getCanvas(){return this._$pool.pop()||document.createElement(\"canvas\")}remove(t,e){if(!this._$store.has(t))return;const i=this._$store.get(t);i.has(e)&&(i.delete(e),i.size||(X(i),this._$store.delete(t)))}stopTimer(t){t=`${t}`,this._$timerMap.has(t)&&(g(this._$timerMap.get(t)),this._$timerMap.delete(t))}removeCache(t){if(t=`${t}`,this._$store.has(t)){const e=this._$store.get(t);for(const t of e.values())this.destroy(t);e.clear(),X(e),this._$store.delete(t)}this._$timerMap.delete(t)}setRemoveTimer(t){if(t=`${t}`,this.stopTimer(t),this._$store.has(t)){const e=d((()=>{this.removeCache(t)}),5e3);this._$timerMap.set(t,e)}}get(t){const e=`${t[0]}`,i=`${t[1]}`;if(this._$store.has(e)){this.stopTimer(e);const t=this._$store.get(e);if(t.has(i))return t.get(i)}return null}set(t,e=null){const i=`${t[0]}`,s=`${t[1]}`;this._$store.has(i)||this._$store.set(i,V());const r=this._$store.get(i);if(null===e){if(!r.has(s))return;return this.destroy(r.get(s)),r.delete(s),void(r.size||(X(r),this._$store.delete(i)))}r.set(s,e)}has(t){const e=`${t[0]}`;return!!this._$store.has(e)&&this._$store.get(e).has(`${t[1]}`)}generateKeys(t,e=null,i=null){let s=\"\";e&&e.length&&(s+=`${e[0]}_${e[1]}`),i&&i.length&&(s+=0===i[7]?\"\":`_${i[7]}`);const r=U();if(s){let t=0;const e=s.length;for(let i=0;i{i=t})()}}class at extends nt{constructor(t=4,e=4,i=1){super(),this._$blurX=4,this._$blurY=4,this._$quality=1,this.blurX=t,this.blurY=e,this.quality=i}static toString(){return\"[class BlurFilter]\"}static get namespace(){return\"next2d.filters.BlurFilter\"}toString(){return\"[object BlurFilter]\"}get namespace(){return\"next2d.filters.BlurFilter\"}static get STEP(){return[.5,1.05,1.4,1.55,1.75,1.9,2,2.15,2.2,2.3,2.5,3,3,3.5,3.5]}get blurX(){return this._$blurX}set blurX(t){(t=G(+t,0,255,0))!==this._$blurX&&(this._$blurX=t,this._$doChanged())}get blurY(){return this._$blurY}set blurY(t){(t=G(+t,0,255,0))!==this._$blurY&&(this._$blurY=t,this._$doChanged())}get quality(){return this._$quality}set quality(t){(t=G(0|t,0,15,1))!==this._$quality&&(this._$quality=t,this._$doChanged())}clone(){return new at(this._$blurX,this._$blurY,this._$quality)}_$toArray(){return U(1,this._$blurX,this._$blurY,this._$quality)}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$quality)return s;const n=at.STEP[this._$quality-1];let a=0>=this._$blurX?1:this._$blurX*n,h=0>=this._$blurY?1:this._$blurY*n;return e?a*=e:a=r.round(a),i?h*=i:h=r.round(h),s.xMin-=a,s.xMax+=2*a,s.yMin-=h,s.yMax+=2*h,s}_$canApply(){return 0!==this._$blurX&&0!==this._$blurY}_$applyFilter(e,i,s=!0){this._$updated=!1;const n=e.frameBuffer,a=n.currentAttachment,h=n.getTextureFromCurrentAttachment();if(!this._$canApply())return s?h:n.createTextureFromCurrentAttachment();let o=r.sqrt(i[0]*i[0]+i[1]*i[1]),_=r.sqrt(i[2]*i[2]+i[3]*i[3]);o/=t,_/=t,o*=2,_*=2;const l=F(0,h.width,0,h.height),c=this._$generateFilterRect(l,o,_);B(l);const $=0|r.ceil(c.xMax),u=0|r.ceil(c.yMax),d=r.ceil(r.abs(c.xMin)+.5*r.abs($-c.xMax)),g=r.ceil(r.abs(c.yMin)+.5*r.abs(u-c.yMax));e._$offsetX=d+e._$offsetX,e._$offsetY=g+e._$offsetY;const f=this._$blurX*o,m=this._$blurY*_;let p=1,x=1;f>128?p=.0625:f>64?p=.125:f>32?p=.25:f>16&&(p=.5),m>128?x=.0625:m>64?x=.125:m>32?x=.25:m>16&&(x=.5);const b=f*p,v=m*x,T=r.ceil($*p),A=r.ceil(u*x),M=n.createTextureAttachment(T,A),y=[M,n.createTextureAttachment(T,A)];let E=0;e._$bind(M),e.reset(),e.setTransform(p,0,0,x,0,0),e.drawImage(h,d,g,h.width,h.height),e.blend.toOneZero();let C=n.getTextureFromCurrentAttachment();for(let t=0;t0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!0,b),C=n.getTextureFromCurrentAttachment()}if(this._$blurY>0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!1,v),C=n.getTextureFromCurrentAttachment()}}if(e.blend.reset(),1!==p||1!==x){const t=n.createTextureAttachment($,u);e._$bind(t),e.reset(),e.imageSmoothingEnabled=!0,e.setTransform(1/p,0,0,1/x,0,0),e.drawImage(C,0,0,T,A),C=n.getTextureFromCurrentAttachment(),e.reset(),e.setTransform(1,0,0,1,0,0),n.releaseAttachment(y[0],!0),n.releaseAttachment(y[1],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(t,!1)}else n.releaseAttachment(y[(E+1)%2],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(y[E],!1);return C}}class ht extends nt{constructor(t=4,e=45,i=16777215,s=1,r=0,n=1,a=4,h=4,o=1,_=1,l=\"inner\",c=!1){super(),this._$blurFilter=new at(a,h,_),this._$distance=4,this._$angle=45,this._$highlightColor=16777215,this._$highlightAlpha=1,this._$shadowColor=0,this._$shadowAlpha=1,this._$strength=1,this._$type=\"inner\",this._$knockout=!1,this.distance=t,this.angle=e,this.highlightColor=i,this.highlightAlpha=s,this.shadowColor=r,this.shadowAlpha=n,this.strength=o,this.type=l,this.knockout=c}static toString(){return\"[class BevelFilter]\"}static get namespace(){return\"next2d.filters.BevelFilter\"}toString(){return\"[object BevelFilter]\"}get namespace(){return\"next2d.filters.BevelFilter\"}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get highlightAlpha(){return this._$highlightAlpha}set highlightAlpha(t){(t=G(+t,0,1,0))!==this._$highlightAlpha&&(this._$highlightAlpha=t,this._$doChanged())}get highlightColor(){return this._$highlightColor}set highlightColor(t){(t=G(j(t),0,16777215,16777215))!==this._$highlightColor&&(this._$highlightColor=t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get shadowAlpha(){return this._$shadowAlpha}set shadowAlpha(t){(t=G(+t,0,1,0))!==this._$shadowAlpha&&(this._$shadowAlpha=t,this._$doChanged())}get shadowColor(){return this._$shadowColor}set shadowColor(t){(t=G(j(t),0,16777215,0))!==this._$shadowColor&&(this._$shadowColor=t,this._$doChanged())}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}get type(){return this._$type}set type(t){(t=`${t}`)!==this._$type&&(this._$type=t,this._$doChanged())}clone(){return new ht(this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$toArray(){return U(0,this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.abs(r.cos(n)*this._$distance),h=r.abs(r.sin(n)*this._$distance);return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&0!==this._$distance&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error(\"the current attachment is null.\");e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply())return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=this._$angle*p,d=r.cos(u)*this._$distance*c,g=r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation=\"erase\",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A=\"inner\"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),s.releaseAttachment(f,!0),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,null,null,null,K(this._$highlightColor,this._$highlightAlpha,!0),Q(this._$highlightColor,this._$highlightAlpha,!0),J(this._$highlightColor,this._$highlightAlpha,!0),this._$highlightAlpha,K(this._$shadowColor,this._$shadowAlpha,!0),Q(this._$shadowColor,this._$shadowAlpha,!0),J(this._$shadowColor,this._$shadowAlpha,!0),this._$shadowAlpha),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseTexture(m),s.getTextureFromCurrentAttachment()}}class ot extends nt{constructor(t=null){super(),this._$matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],this.matrix=t}static toString(){return\"[class ColorMatrixFilter]\"}static get namespace(){return\"next2d.filters.ColorMatrixFilter\"}toString(){return\"[object ColorMatrixFilter]\"}get namespace(){return\"next2d.filters.ColorMatrixFilter\"}get matrix(){return this._$matrix}set matrix(t){if(t&&n.isArray(t)&&20===t.length){for(let e=0;e<20;++e)if(t[e]!==this._$matrix[e]){this._$doChanged();break}this._$matrix=t}}clone(){return new ot(this._$matrix)}_$toArray(){return U(2,this._$matrix)}_$generateFilterRect(t){return t}_$canApply(){return!0}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment(),r=s.width,n=s.height,a=e.createTextureAttachment(r,n);return t._$bind(a),t.reset(),t._$applyColorMatrixFilter(s,this._$matrix),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()}}class _t extends nt{constructor(t=0,e=0,i=null,s=1,r=0,n=!0,a=!0,h=0,o=0){super(),this._$matrixX=0,this._$matrixY=0,this._$matrix=null,this._$divisor=1,this._$bias=0,this._$preserveAlpha=!0,this._$clamp=!0,this._$color=0,this._$alpha=0,this.matrixX=t,this.matrixY=e,this.matrix=i,this.divisor=s,this.bias=r,this.preserveAlpha=n,this.clamp=a,this.color=h,this.alpha=o}static toString(){return\"[class ConvolutionFilter]\"}static get namespace(){return\"next2d.filters.ConvolutionFilter\"}toString(){return\"[object ConvolutionFilter]\"}get namespace(){return\"next2d.filters.ConvolutionFilter\"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get bias(){return this._$bias}set bias(t){t!==this._$bias&&(this._$bias=0|t,this._$doChanged())}get clamp(){return this._$clamp}set clamp(t){t!==this._$clamp&&(this._$clamp=!!t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get divisor(){return this._$divisor}set divisor(t){t!==this._$divisor&&(this._$divisor=0|t,this._$doChanged())}get matrix(){return this._$matrix}set matrix(t){n.isArray(this._$matrix)&&D(this._$matrix),this._$matrix=n.isArray(t)?t:null,this._$doChanged()}get matrixX(){return this._$matrixX}set matrixX(t){(t=0|G(0|t,0,15,0))!==this._$matrixX&&(this._$matrixX=t,this._$doChanged())}get matrixY(){return this._$matrixY}set matrixY(t){(t=0|G(0|t,0,15,0))!==this._$matrixY&&(this._$matrixY=t,this._$doChanged())}get preserveAlpha(){return this._$preserveAlpha}set preserveAlpha(t){t!==this._$preserveAlpha&&(this._$preserveAlpha=!!t,this._$doChanged())}clone(){return new _t(this._$matrixX,this._$matrixY,this._$matrix?this._$matrix.slice():null,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$toArray(){return U(3,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$matrix&&this._$matrixX*this._$matrixY===this._$matrix.length}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment();return this._$canApply()&&this._$matrix?(t._$applyConvolutionFilter(s,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,K(this._$color,this._$alpha,!1),Q(this._$color,this._$alpha,!1),J(this._$color,this._$alpha,!1),this._$alpha),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()):s}}class lt extends nt{constructor(t=null,e=null,i=0,s=0,r=0,n=0,a=\"wrap\",h=0,o=0){super(),this._$mapBitmap=null,this._$mapPoint=null,this._$componentX=0,this._$componentY=0,this._$scaleX=0,this._$scaleY=0,this._$mode=\"wrap\",this._$color=0,this._$alpha=0,this.mapBitmap=t,this.mapPoint=e,this.componentX=i,this.componentY=s,this.scaleX=r,this.scaleY=n,this.mode=a,this.color=h,this.alpha=o}static toString(){return\"[class DisplacementMapFilter]\"}static get namespace(){return\"next2d.filters.DisplacementMapFilter\"}toString(){return\"[object DisplacementMapFilter]\"}get namespace(){return\"next2d.filters.DisplacementMapFilter\"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get componentX(){return this._$componentX}set componentX(t){t!==this._$componentX&&(this._$componentX=t,this._$doChanged())}get componentY(){return this._$componentY}set componentY(t){t!==this._$componentY&&(this._$componentY=t,this._$doChanged())}get mapBitmap(){return this._$mapBitmap}set mapBitmap(t){t!==this._$mapBitmap&&(this._$mapBitmap=t,this._$doChanged())}get mapPoint(){return this._$mapPoint}set mapPoint(t){t!==this._$mapPoint&&(this._$mapPoint=t,this._$doChanged())}get mode(){return this._$mode}set mode(t){t!==this._$mode&&(this._$mode=t,this._$doChanged())}get scaleX(){return this._$scaleX}set scaleX(t){(t=G(+t,-65535,65535,0))!==this._$scaleX&&(this._$scaleX=t,this._$doChanged())}get scaleY(){return this._$scaleY}set scaleY(t){(t=G(+t,-65535,65535,0))!==this._$scaleY&&(this._$scaleY=t,this._$doChanged())}clone(){return new lt(this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$toArray(){return U(4,this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$mapBitmap&&this._$componentX>0&&this._$componentY>0&&0!==this._$scaleX&&0!==this._$scaleY}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;t.setTransform(1,0,0,1,0,0);const n=i.getTextureFromCurrentAttachment();if(!this._$canApply()||!s||!this._$mapBitmap)return n;const a=r.sqrt(e[0]*e[0]+e[1]*e[1]),h=r.sqrt(e[2]*e[2]+e[3]*e[3]);return t._$applyDisplacementMapFilter(n,this._$mapBitmap,n.width/a,n.height/h,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha),i.releaseAttachment(s,!0),i.getTextureFromCurrentAttachment()}}class ct extends nt{constructor(t=4,e=45,i=0,s=1,r=4,n=4,a=1,h=1,o=!1,_=!1,l=!1){super(),this._$blurFilter=new at(r,n,h),this._$distance=4,this._$angle=45,this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this._$hideObject=!1,this.distance=t,this.angle=e,this.color=i,this.alpha=s,this.strength=a,this.inner=o,this.knockout=_,this.hideObject=l}static toString(){return\"[class DropShadowFilter]\"}static get namespace(){return\"next2d.filters.DropShadowFilter\"}toString(){return\"[object DropShadowFilter]\"}get namespace(){return\"next2d.filters.DropShadowFilter\"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get hideObject(){return this._$hideObject}set hideObject(t){t!==this._$hideObject&&(this._$hideObject=!!t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new ct(this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$toArray(){return U(5,this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.cos(n)*this._$distance,h=r.sin(n)*this._$distance;return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(e,i){const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error(\"the current attachment is null.\");if(e.setTransform(1,0,0,1,0,0),!this._$canApply())return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=this._$angle*p,v=r.cos(b)*this._$distance*m,T=r.sin(b)*this._$distance*x,A=this._$inner?a:c+r.max(0,r.abs(v)-g),M=this._$inner?h:$+r.max(0,r.abs(T)-f),y=r.ceil(A),E=r.ceil(M),C=(y-A)/2,S=(E-M)/2,F=this._$inner?0:r.max(0,g-v)+C,B=this._$inner?0:r.max(0,f-T)+S,w=this._$inner?v-u:(v>0?r.max(0,v-g):0)+C,R=this._$inner?T-d:(T>0?r.max(0,T-f):0)+S;let I,P;return this._$inner?(I=\"inner\",P=this._$knockout||this._$hideObject):!this._$knockout&&this._$hideObject?(I=\"full\",P=!0):(I=\"outer\",P=this._$knockout),e._$bind(n),e._$applyBitmapFilter(l,y,E,a,h,F,B,c,$,w,R,!0,I,P,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),e._$offsetX=o+F,e._$offsetY=_+B,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class $t extends nt{constructor(t=0,e=1,i=4,s=4,r=1,n=1,a=!1,h=!1){super(),this._$blurFilter=new at(i,s,n),this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this.color=t,this.alpha=e,this.strength=r,this.inner=a,this.knockout=h}static toString(){return\"[class GlowFilter]\"}static get namespace(){return\"next2d.filters.GlowFilter\"}toString(){return\"[object GlowFilter]\"}get namespace(){return\"next2d.filters.GlowFilter\"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,4))!==this._$color&&(this._$color=t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new $t(this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$toArray(){return U(6,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);return this._$canApply()?this._$blurFilter._$generateFilterRect(s,e,i):s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(t,e){const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error(\"the current attachment is null.\");if(this._$updated=!1,t.setTransform(1,0,0,1,0,0),!this._$canApply())return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),_=o.width,l=o.height,c=t._$offsetX,$=t._$offsetY,u=this._$inner?r:_,d=this._$inner?n:l,g=this._$inner?0:c-a,f=this._$inner?0:$-h,m=this._$inner?-c:0,p=this._$inner?-$:0,x=this._$inner?\"inner\":\"outer\";return t._$bind(s),t._$applyBitmapFilter(o,u,d,r,n,g,f,_,l,m,p,!0,x,this._$knockout,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),t._$offsetX=a+g,t._$offsetY=h+f,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class ut extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_=\"inner\",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type=\"inner\",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return\"[class GradientBevelFilter]\"}static get namespace(){return\"next2d.filters.GradientBevelFilter\"}toString(){return\"[object GradientBevelFilter]\"}get namespace(){return\"next2d.filters.GradientBevelFilter\"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply()||!n)return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=+this._$angle*p,d=+r.cos(u)*this._$distance*c,g=+r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation=\"erase\",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A=\"inner\"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseAttachment(f,!0),s.getTextureFromCurrentAttachment()}}class dt extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_=\"inner\",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type=\"inner\",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return\"[class GradientGlowFilter]\"}static get namespace(){return\"next2d.filters.GradientGlowFilter\"}toString(){return\"[object GradientGlowFilter]\"}get namespace(){return\"next2d.filters.GradientGlowFilter\"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(e.setTransform(1,0,0,1,0,0),!this._$canApply()||!n)return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=+this._$angle*p,v=+r.cos(b)*this._$distance*m,T=+r.sin(b)*this._$distance*x,A=\"inner\"===this.type,M=A?a:c+r.max(0,r.abs(v)-g),y=A?h:$+r.max(0,r.abs(T)-f),E=r.ceil(M),C=r.ceil(y),S=(E-M)/2,F=(C-y)/2,B=A?0:r.max(0,g-v)+S,w=A?0:r.max(0,f-T)+F,R=A?v-u:(v>0?r.max(0,v-g):0)+S,I=A?T-d:(T>0?r.max(0,T-f):0)+F;return e._$bind(n),e._$applyBitmapFilter(l,E,C,a,h,B,w,c,$,R,I,!0,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=o+B,e._$offsetY=_+w,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class gt{constructor(){this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$clipDepth=0,this._$depth=0,this._$isMask=!1,this._$updated=!0,this._$matrix=P(1,0,0,1,0,0),this._$colorTransform=k(1,1,1,1,0,0,0,0),this._$blendMode=\"normal\",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$maskMatrix=null,this._$isMask=!1,this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$scale9Grid=null,this._$matrixBase=null}_$shouldClip(t){const e=this._$getBounds(t),i=r.abs(e.xMax-e.xMin),s=r.abs(e.yMax-e.yMin);return B(e),!(!i||!s)}_$getLayerBounds(e){const i=this._$getBounds(),s=q(i,e);B(i);const n=this._$filters;if(!n||!n.length)return s;let a=F(0,r.abs(s.xMax-s.xMin),0,r.abs(s.yMax-s.yMin));B(s);let h=+r.sqrt(e[0]*e[0]+e[1]*e[1]),o=+r.sqrt(e[2]*e[2]+e[3]*e[3]);h/=t,o/=t,h*=2,o*=2;for(let t=0;t-1){const t=le.instances;if(!t.has(this._$parentId))return;const e=t.get(this._$parentId);e._$updated||e._$doChanged()}}_$update(t){if(this._$doChanged(),this._$visible=t.visible,\"depth\"in t&&(this._$depth=t.depth),\"isMask\"in t&&(this._$isMask=t.isMask),\"clipDepth\"in t&&(this._$clipDepth=t.clipDepth),\"maskId\"in t&&(this._$maskId=t.maskId,this._$maskId>-1&&t.maskMatrix&&(this._$maskMatrix=t.maskMatrix)),this._$matrix[0]=\"a\"in t?t.a:1,this._$matrix[1]=\"b\"in t?t.b:0,this._$matrix[2]=\"c\"in t?t.c:0,this._$matrix[3]=\"d\"in t?t.d:1,this._$matrix[4]=\"tx\"in t?t.tx:0,this._$matrix[5]=\"ty\"in t?t.ty:0,this._$colorTransform[0]=\"f0\"in t?t.f0:1,this._$colorTransform[1]=\"f1\"in t?t.f1:1,this._$colorTransform[2]=\"f2\"in t?t.f2:1,this._$colorTransform[3]=\"f3\"in t?t.f3:1,this._$colorTransform[4]=\"f4\"in t?t.f4:0,this._$colorTransform[5]=\"f5\"in t?t.f5:0,this._$colorTransform[6]=\"f6\"in t?t.f6:0,this._$colorTransform[7]=\"f7\"in t?t.f7:0,this._$blendMode=t.blendMode||\"normal\",this._$filters=null,t.filters&&t.filters.length){this._$filters=U();for(let e=0;e-1&&this._$characterId&&rt.setRemoveTimer(`${this._$loaderInfoId}@${this._$characterId}`),t.instances.delete(this._$instanceId),this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$blendMode=\"normal\",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$isMask=!1,this._$depth=0,this._$clipDepth=0,this._$scale9Grid=null}_$isUpdated(){return this._$updated}_$isFilterUpdated(t,e=null,i=!1){if(this._$isUpdated())return!0;if(i&&e)for(let t=0;tc||s._$clipDepth>0)&&(t.restore(),l&&t._$leaveClip(),c=0,l=!0),!l)continue;if(s._$clipDepth>0){c=s._$clipDepth,l=s._$shouldClip(o),l&&(t.save(),l=s._$startClip(t,o));continue}const a=s._$maskId>-1&&$.has(s._$maskId)?$.get(s._$maskId):null;if(a){let e;if(a._$updated=!1,this._$instanceId===a._$parentId)e=o;else{e=f;let i=$.get(a._$parentId);for(;i||i._$instanceId!==i._$parentId;)e=H(i._$matrix,e),i=$.get(i._$parentId);const s=le.scaleX,r=P(s,0,0,s,0,0);if(e=H(r,e),N(r),t.isLayer){const i=t.getCurrentPosition();e[4]-=i.xMin,e[5]-=i.yMin}}if(!a._$shouldClip(e))continue;const i=a._$startClip(t,e);if(t.save(),!i){t.restore();continue}}s._$draw(t,o,_),s._$updated=!1,a&&(t.restore(),t._$leaveClip())}if(c&&(t.restore(),l&&t._$leaveClip()),h.isLayer)return this._$postDraw(t,e,s,h);h.matrix!==e&&N(h.matrix),s!==i&&L(s),et(h)}_$getLayerBounds(e){const i=this._$children;if(!i.length)return F(0,0,0,0);const s=h.MAX_VALUE;let n=s,a=-s,o=s,_=-s;const l=le.instances;for(let t=0;t0){const s=this._$getBounds(null),o=q(s,i);B(s);const _=+o.xMax,l=+o.xMin,c=+o.yMax,$=+o.yMin;B(o);const u=r.ceil(r.abs(_-l)),d=r.ceil(r.abs(c-$));if(0>=u||0>=d)return et(n),i!==e&&N(i),null;let g=+r.sqrt(i[0]*i[0]+i[1]*i[1]);if(!h.isInteger(g)){const t=g.toString(),e=t.indexOf(\"e\");-1!==e&&(g=+t.slice(0,e)),g=+g.toFixed(4)}let f=+r.sqrt(i[2]*i[2]+i[3]*i[3]);if(!h.isInteger(f)){const t=f.toString(),e=t.indexOf(\"e\");-1!==e&&(f=+t.slice(0,e)),f=+f.toFixed(4)}n.canApply=this._$canApply(this._$filters);let m=F(0,u,0,d);if(n.canApply&&this._$filters)for(let t=0;tp.width||$-m.yMin>p.height)return B(m),et(n),i!==e&&N(i),null;if(0>l+m.xMax||0>$+m.yMax)return B(m),et(n),i!==e&&N(i),null;let x=i[4]-l,b=i[5]-$;t._$startLayer(F(l,_,$,c));const v=this._$isFilterUpdated(i,this._$filters,n.canApply),T=this._$getLayerBounds(i),A=r.ceil(r.abs(T.xMax-T.xMin)),M=r.ceil(r.abs(T.yMax-T.yMin));B(T);const y=A-m.xMax+m.xMin,E=M-m.yMax+m.yMin;x+=y,b+=E,n.sw=y,n.sh=E,v&&t._$saveAttachment(r.ceil(u+y),r.ceil(d+E),!0),n.isLayer=!0,n.isUpdated=v,n.filters=this._$filters,n.blendMode=a,n.color=k(),n.matrix=P(i[0],i[1],i[2],i[3],x,b),i!==e&&N(i),B(m)}return n}_$postDraw(t,e,i,s){t.drawInstacedArray();const r=U(this._$instanceId,\"f\"),n=t.frameBuffer,a=s.matrix;let h=0,o=0,_=rt.get(r);if(!_||s.isUpdated){_&&rt.set(r,null),_=n.getTextureFromCurrentAttachment();const i=s.filters;let l=!1;if(i&&i.length){for(let s=0;s{switch(!0){case t[0]>e[0]:return 1;case e[0]>t[0]:return-1;default:return 0}})),this._$stops}linear(t,e,i,s,r=\"rgb\",n=\"pad\"){return this._$type=\"linear\",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$rgb=r,this._$mode=n,this._$stops.length&&(this._$stops.length=0),this}radial(t,e,i,s,r,n,a=\"rgb\",h=\"pad\",o=0){return this._$type=\"radial\",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$points[4]=r,this._$points[5]=n,this._$rgb=a,this._$mode=h,this._$focalPointRatio=G(o,-.975,.975,0),this._$stops.length&&(this._$stops.length=0),this}addColorStop(t,e){this._$stops.push(U(t,e))}}class pt{constructor(t,e,i){this._$texture=t,this._$repeat=e,this._$colorTransform=i}get texture(){return this._$texture}get repeat(){return this._$repeat}get colorTransform(){return this._$colorTransform}}class xt{constructor(){this._$fillStyle=w(1,1,1,1),this._$strokeStyle=w(1,1,1,1),this._$lineWidth=1,this._$lineCap=\"round\",this._$lineJoin=\"round\",this._$miterLimit=5}get miterLimit(){return this._$miterLimit}set miterLimit(t){this._$miterLimit=t}get lineWidth(){return this._$lineWidth}set lineWidth(t){this._$lineWidth=t}get lineCap(){return this._$lineCap}set lineCap(t){this._$lineCap=t}get lineJoin(){return this._$lineJoin}set lineJoin(t){this._$lineJoin=t}get fillStyle(){return this._$fillStyle}set fillStyle(t){this._$fillStyle instanceof o&&R(this._$fillStyle),this._$fillStyle=t}get strokeStyle(){return this._$strokeStyle}set strokeStyle(t){this._$strokeStyle instanceof o&&R(this._$strokeStyle),this._$strokeStyle=t}clear(){this._$lineWidth=1,this._$lineCap=\"round\",this._$lineJoin=\"round\",this._$miterLimit=5,this._$clearFill(),this._$clearStroke()}_$clearFill(){if(this._$fillStyle instanceof mt)return this._$fillStyle.dispose(),void(this._$fillStyle=w(1,1,1,1));this._$fillStyle instanceof pt?this._$fillStyle=w(1,1,1,1):this._$fillStyle.fill(1)}_$clearStroke(){if(this._$strokeStyle instanceof mt)return this._$strokeStyle.dispose(),void(this._$strokeStyle=w(1,1,1,1));this._$strokeStyle instanceof pt?this._$strokeStyle=w(1,1,1,1):this._$strokeStyle.fill(1)}}let bt=2048;class vt{constructor(t){t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this._$gl=t,this._$objectPool=[],this._$objectPoolArea=0,this._$activeTexture=-1,this._$boundTextures=[null,null,null],this._$maxWidth=0,this._$maxHeight=0,this._$atlasTextures=[],this._$atlasCacheMap=new Map,this._$positionObjectArray=[],this._$nodeObjectArray=[],this._$atlasNodes=new Map}createTextureAtlas(){const t=this._$gl.createTexture();t.width=bt,t.height=bt,this._$gl.activeTexture(this._$gl.TEXTURE3),this._$gl.bindTexture(this._$gl.TEXTURE_2D,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,this._$gl.NEAREST),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,this._$gl.NEAREST),this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,bt,bt),this._$gl.bindTexture(this._$gl.TEXTURE_2D,null),this._$activeTexture>-1&&this._$gl.activeTexture(this._$activeTexture);const e=this._$atlasTextures.length;this._$atlasNodes.set(e,[]),this._$atlasCacheMap.set(e,[]),this._$atlasTextures.push(t)}getAtlasTexture(t){return this._$atlasTextures[t]}getNode(t,e,i,s){const r=this._$nodeObjectArray.length?this._$nodeObjectArray.pop():{x:0,y:0,w:0,h:0};return r.x=t,r.y=e,r.w=i,r.h=s,r}createCachePosition(t,e){const i=this._$positionObjectArray.length?this._$positionObjectArray.pop():{index:0,x:0,y:0,w:0,h:0};i.x=i.y=0,i.w=t,i.h=e;for(const[s,r]of this._$atlasNodes){if(!r.length)return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i;const n=r.length;for(let a=0;an.w||e>n.h))return i.index=s,i.x=n.x,i.y=n.y,this._$atlasCacheMap.get(i.index).push(i),n.w!==t||n.h!==e?t>e?(n.h-e-1>0&&r.push(this.getNode(n.x,n.y+e+1,n.w,n.h-e-1)),n.w-t-1>0?(n.x=n.x+t+1,n.w=n.w-t-1,n.h=e):(r.splice(a,1),this._$nodeObjectArray.push(n))):(n.w-t-1>0&&r.push(this.getNode(n.x+t+1,n.y,n.w-t-1,n.h)),n.h-e-1>0?(n.y=n.y+e+1,n.w=t,n.h=n.h-e-1):(r.splice(a,1),this._$nodeObjectArray.push(n))):(r.splice(a,1),this._$nodeObjectArray.push(n)),i}}const s=this._$atlasTextures.length;this.createTextureAtlas();const r=this._$atlasNodes.get(s);return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i}releasePosition(t){var e;this._$atlasNodes.has(t.index)&&(null===(e=this._$atlasNodes.get(t.index))||void 0===e||e.unshift(this.getNode(t.x,t.y,t.w,t.h)),this._$positionObjectArray.push(t))}clearCache(){for(const t of this._$atlasCacheMap.values())t.length=0;for(const t of this._$atlasNodes.values())t.length=0}_$createTexture(t,e){const i=this._$gl.createTexture();return i.width=0,i.height=0,i.area=0,i.dirty=!0,i.smoothing=!0,this.bind0(i,!1),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,t,e),i}_$getTexture(t,e){for(let i=0;ithis._$maxWidth*this._$maxHeight*2)this._$gl.deleteTexture(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPool.length&&this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();this._$objectPoolArea-=t.area,this._$gl.deleteTexture(t)}}bind0(t,e=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,e)}bind01(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,e,i),this._$bindTexture(0,this._$gl.TEXTURE0,t,i)}bind012(t,e,i,s=null){this._$bindTexture(2,this._$gl.TEXTURE2,i,s),this._$bindTexture(1,this._$gl.TEXTURE1,e,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}bind02(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,e,i),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}_$bindTexture(t,e,i=null,s=null){const r=i!==this._$boundTextures[t],n=null!==s&&null!==i&&s!==i.smoothing;if((r||n||e===this._$gl.TEXTURE0)&&e!==this._$activeTexture&&(this._$activeTexture=e,this._$gl.activeTexture(e)),r&&(this._$boundTextures[t]=i,this._$gl.bindTexture(this._$gl.TEXTURE_2D,i)),n){i&&(i.smoothing=!!s);const t=s?this._$gl.LINEAR:this._$gl.NEAREST;this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,t)}}}class Tt{constructor(t){this._$gl=t,this._$objectPool=U(),this._$objectPoolArea=0,this._$maxWidth=0,this._$maxHeight=0}set maxWidth(t){this._$maxWidth=t}set maxHeight(t){this._$maxHeight=t}_$createStencilBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error(\"the stencil buffer is null.\");return t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getStencilBuffer(t,e){const i=this._$objectPool.length;for(let s=0;s100){const t=this._$objectPool.shift();if(t)return this._$objectPoolArea-=t.area,t}return this._$createStencilBuffer()}create(t,e){const i=this._$getStencilBuffer(t,e);return i.width===t&&i.height===e||(i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,i),this._$gl.renderbufferStorage(this._$gl.RENDERBUFFER,this._$gl.STENCIL_INDEX8,t,e)),i}release(t){if(t.area>this._$maxWidth*this._$maxHeight*2)this._$gl.deleteRenderbuffer(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();t&&(this._$objectPoolArea-=t.area,this._$gl.deleteRenderbuffer(t))}}}class At{constructor(t,e){this._$gl=t,this._$samples=e,this._$objectPool=U()}set samples(t){this._$samples=t}_$createColorBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error(\"the color buffer is null.\");const e=this._$gl.createRenderbuffer();if(!e)throw new Error(\"the stencil buffer is null.\");return t.stencil=e,t.samples=0,t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getColorBuffer(t){if(!this._$objectPool.length)return this._$createColorBuffer();const e=this._$bsearch(t);if(e1;){const s=r.floor((i+e)/2);t<=this._$objectPool[s].area?i=s:e=s}return i}}class Mt{constructor(t,e){this._$gl=t,this._$objectPool=[],this._$frameBuffer=t.createFramebuffer(),t.bindFramebuffer(t.READ_FRAMEBUFFER,this._$frameBuffer),this._$frameBufferTexture=t.createFramebuffer(),this._$currentAttachment=null,this._$isBinding=!1,this._$textureManager=new vt(t),this._$stencilBufferPool=new Tt(t),this._$colorBufferPool=new At(t,e),this._$isRenderBinding=!1,this._$colorBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.RGBA8,bt,bt),this._$stencilBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.STENCIL_INDEX8,bt,bt)}bindRenderBuffer(){this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),this._$isRenderBinding||(this._$isRenderBinding=!0,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,this._$stencilBuffer))}get currentAttachment(){return this._$currentAttachment}get textureManager(){return this._$textureManager}createCacheAttachment(t,e,i=!1,s=0){const r=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},n=this._$textureManager.create(t,e);return r.width=t,r.height=e,i?(r.color=this._$colorBufferPool.create(t,e,s),r.texture=n,r.msaa=!0,r.stencil=r.color.stencil):(r.color=n,r.texture=n,r.msaa=!1,r.stencil=this._$stencilBufferPool.create(t,e)),r.mask=!1,r.clipLevel=0,r.isActive=!0,r}clearCache(){this._$textureManager.clearCache()}setMaxSize(t,e){this._$stencilBufferPool._$maxWidth=t,this._$stencilBufferPool._$maxHeight=e,this._$textureManager._$maxWidth=t,this._$textureManager._$maxHeight=e}createTextureAttachment(t,e){const i=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},s=this._$textureManager.create(t,e);return i.width=t,i.height=e,i.color=s,i.texture=s,i.msaa=!1,i.stencil=null,i.mask=!1,i.clipLevel=0,i.isActive=!0,i}createTextureAttachmentFrom(t){const e=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!0};return e.width=t.width,e.height=t.height,e.color=t,e.texture=t,e.msaa=!1,e.stencil=null,e.mask=!1,e.clipLevel=0,e.isActive=!0,e}releaseAttachment(t=null,e=!1){t&&t.isActive&&(t.msaa?t.color instanceof WebGLRenderbuffer&&this._$colorBufferPool.release(t.color):t.stencil&&this._$stencilBufferPool.release(t.stencil),e&&t.texture&&this._$textureManager.release(t.texture),t.color=null,t.texture=null,t.stencil=null,t.isActive=!1,this._$objectPool.push(t))}bind(t){this._$currentAttachment=t,this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),t.msaa?t.color instanceof WebGLRenderbuffer&&(this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.color),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,t.color)):t.color instanceof WebGLTexture&&(t.color&&this._$textureManager.bind0(t.color),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,t.color,0)),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.stencil),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,t.stencil),this._$isRenderBinding=!1}unbind(){this._$currentAttachment=null,this._$isBinding&&(this._$isBinding=!1,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,null))}transferToMainTexture(){if(!this._$currentAttachment)throw new Error(\"the current attachment is null.\");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error(\"the texture is null.\");this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,null),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBuffer)}createCachePosition(t,e){return this._$textureManager.createCachePosition(t,e)}transferTexture(t){this._$gl.disable(this._$gl.SCISSOR_TEST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture);const e=this._$textureManager.getAtlasTexture(t.index);this._$textureManager.bind0(e),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,e,0);const i=r.max(0,t.x-1),s=r.max(0,t.y-1),n=r.min(bt,t.x+t.w+1),a=r.min(bt,t.y+t.h+1);this._$gl.blitFramebuffer(i,s,n,a,i,s,n,a,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)}getTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error(\"the current attachment is null.\");if(!this._$currentAttachment.msaa&&this._$currentAttachment.texture)return this._$currentAttachment.texture;const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error(\"the texture is null.\");return i.dirty=!1,this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer),i}createTextureFromPixels(t,e,i=null,s=!1,r=!0){return this._$textureManager.create(t,e,i,s,r)}createTextureFromCanvas(t){return this._$textureManager.createFromCanvas(t)}createTextureFromImage(t,e=!1){return this._$textureManager.createFromImage(t,e)}createTextureFromVideo(t,e=!1){return this._$textureManager.createFromVideo(t,e)}createTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error(\"the current attachment is null.\");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$textureManager.create(t,e);return this._$textureManager.bind0(i),this._$gl.copyTexSubImage2D(this._$gl.TEXTURE_2D,0,0,0,0,0,t,e),i}releaseTexture(t){this._$textureManager.release(t)}}class yt{constructor(){this._$bezierConverterBuffer=new o(32)}cubicToQuad(t,e,i,s,r,n,a,h){this._$split2Cubic(t,e,i,s,r,n,a,h,0,16),this._$split2Cubic(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0,8),this._$split2Cubic(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16,24),this._$split2Quad(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0),this._$split2Quad(this._$bezierConverterBuffer[8],this._$bezierConverterBuffer[9],this._$bezierConverterBuffer[10],this._$bezierConverterBuffer[11],this._$bezierConverterBuffer[12],this._$bezierConverterBuffer[13],this._$bezierConverterBuffer[14],this._$bezierConverterBuffer[15],8),this._$split2Quad(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16),this._$split2Quad(this._$bezierConverterBuffer[24],this._$bezierConverterBuffer[25],this._$bezierConverterBuffer[26],this._$bezierConverterBuffer[27],this._$bezierConverterBuffer[28],this._$bezierConverterBuffer[29],this._$bezierConverterBuffer[30],this._$bezierConverterBuffer[31],24)}_$split2Cubic(t,e,i,s,r,n,a,h,o,_){const l=.125*(t+3*(i+r)+a),c=.125*(e+3*(s+n)+h),$=.125*(a+r-i-t),u=.125*(h+n-s-e);this._$bezierConverterBuffer[o]=t,this._$bezierConverterBuffer[o+1]=e,this._$bezierConverterBuffer[o+2]=.5*(t+i),this._$bezierConverterBuffer[o+3]=.5*(e+s),this._$bezierConverterBuffer[o+4]=l-$,this._$bezierConverterBuffer[o+5]=c-u,this._$bezierConverterBuffer[o+6]=l,this._$bezierConverterBuffer[o+7]=c,this._$bezierConverterBuffer[_]=l,this._$bezierConverterBuffer[_+1]=c,this._$bezierConverterBuffer[_+2]=l+$,this._$bezierConverterBuffer[_+3]=c+u,this._$bezierConverterBuffer[_+4]=.5*(r+a),this._$bezierConverterBuffer[_+5]=.5*(n+h),this._$bezierConverterBuffer[_+6]=a,this._$bezierConverterBuffer[_+7]=h}_$split2Quad(t,e,i,s,r,n,a,h,o){const _=.125*(t+3*(i+r)+a),l=.125*(e+3*(s+n)+h);this._$bezierConverterBuffer[o]=.25*t+.75*i,this._$bezierConverterBuffer[o+1]=.25*e+.75*s,this._$bezierConverterBuffer[o+2]=_,this._$bezierConverterBuffer[o+3]=l,this._$bezierConverterBuffer[o+4]=.75*r+.25*a,this._$bezierConverterBuffer[o+5]=.75*n+.25*h,this._$bezierConverterBuffer[o+6]=a,this._$bezierConverterBuffer[o+7]=h}}class Et{constructor(){this._$currentPath=U(),this._$vertices=U(),this._$bezierConverter=new yt}get vertices(){return this._$pushCurrentPathToVertices(),this._$vertices}begin(){for(this._$currentPath.length=0;this._$vertices.length;)D(this._$vertices.pop())}moveTo(t,e){this._$currentPath.length?this._$equalsToLastPoint(t,e)||(this._$pushCurrentPathToVertices(),this._$pushPointToCurrentPath(t,e,!1)):this._$pushPointToCurrentPath(t,e,!1)}lineTo(t,e){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}quadTo(t,e,i,s){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(i,s)||(this._$pushPointToCurrentPath(t,e,!0),this._$pushPointToCurrentPath(i,s,!1))}cubicTo(t,e,i,s,r,n){if(this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(r,n))return;const a=+this._$currentPath[this._$currentPath.length-3],h=+this._$currentPath[this._$currentPath.length-2];this._$bezierConverter.cubicToQuad(a,h,t,e,i,s,r,n);const o=this._$bezierConverter._$bezierConverterBuffer;for(let t=0;t<32;)this.quadTo(o[t++],o[t++],o[t++],o[t++])}drawCircle(t,e,i){const s=i,r=.5522847498307936*i;this.cubicTo(t+s,e+r,t+r,e+s,t,e+s),this.cubicTo(t-r,e+s,t-s,e+r,t-s,e),this.cubicTo(t-s,e-r,t-r,e-s,t,e-s),this.cubicTo(t+r,e-s,t+s,e-r,t+s,e)}close(){if(this._$currentPath.length<=6)return;const t=+this._$currentPath[0],e=+this._$currentPath[1];this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}_$equalsToLastPoint(t,e){const i=+this._$currentPath[this._$currentPath.length-3],s=+this._$currentPath[this._$currentPath.length-2];return t===i&&e===s}_$pushPointToCurrentPath(t,e,i){this._$currentPath.push(t,e,i)}_$pushCurrentPathToVertices(){this._$currentPath.length<4?this._$currentPath.length=0:(this._$vertices.push(this._$currentPath),this._$currentPath=U())}createRectVertices(t,e,i,s){return U(U(t,e,!1,t+i,e,!1,t+i,e+s,!1,t,e+s,!1))}}class Ct{constructor(){this.enabled=!1,this.parentMatrixA=1,this.parentMatrixB=0,this.parentMatrixC=0,this.parentMatrixD=0,this.parentMatrixE=1,this.parentMatrixF=0,this.parentMatrixG=0,this.parentMatrixH=0,this.parentMatrixI=1,this.ancestorMatrixA=1,this.ancestorMatrixB=0,this.ancestorMatrixC=0,this.ancestorMatrixD=0,this.ancestorMatrixE=1,this.ancestorMatrixF=0,this.ancestorMatrixG=0,this.ancestorMatrixH=0,this.ancestorMatrixI=1,this.parentViewportX=0,this.parentViewportY=0,this.parentViewportW=0,this.parentViewportH=0,this.minXST=1e-5,this.minYST=1e-5,this.minXPQ=1e-5,this.minYPQ=1e-5,this.maxXST=.99999,this.maxYST=.99999,this.maxXPQ=.99999,this.maxYPQ=.99999}enable(t,e,i,s,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x){const b=n.xMax-n.xMin,v=n.yMax-n.yMin,T=a.w,A=a.h,M=r.abs(r.ceil(b*h)),y=r.abs(r.ceil(v*h)),E=T>0?(a.x-n.xMin)/b:1e-5,C=A>0?(a.y-n.yMin)/v:1e-5,S=T>0?(a.x+a.w-n.xMin)/b:.99999,F=A>0?(a.y+a.h-n.yMin)/v:.99999;let B=M*E/i,w=y*C/s,R=(i-M*(1-S))/i,I=(s-y*(1-F))/s;if(B>=R){const t=E/(E+(1-S));B=r.max(t-1e-5,0),R=r.min(t+1e-5,1)}if(w>=I){const t=C/(C+(1-F));w=r.max(t-1e-5,0),I=r.min(t+1e-5,1)}this.enabled=!0,this.parentMatrixA=o,this.parentMatrixB=_,this.parentMatrixD=l,this.parentMatrixE=c,this.parentMatrixG=$,this.parentMatrixH=u,this.ancestorMatrixA=d,this.ancestorMatrixB=g,this.ancestorMatrixD=f,this.ancestorMatrixE=m,this.ancestorMatrixG=p,this.ancestorMatrixH=x,this.parentViewportX=t,this.parentViewportY=e,this.parentViewportW=i,this.parentViewportH=s,this.minXST=E,this.minYST=C,this.minXPQ=B,this.minYPQ=w,this.maxXST=S,this.maxYST=F,this.maxXPQ=R,this.maxYPQ=I}disable(){this.enabled=!1}}class St{constructor(t,e){this._$gl=t,this._$array=[],this._$map=V();const i=this._$gl.getProgramParameter(e,this._$gl.ACTIVE_UNIFORMS);for(let t=0;t0&&(t.assign--,t.method(t.array)))}}}class Ft{constructor(){this._$attributes=[],this._$count=0}get attributes(){return this._$attributes}get count(){return this._$count}set count(t){this._$count=t}clear(){this._$attributes.length=0,this._$count=0}}class Bt{constructor(t,e,i,s){this._$gl=t,this._$context=e,this._$program=this._$createProgram(i,s),this._$uniform=new St(t,this._$program),this._$instance=null}get instance(){return this._$instance||(this._$instance=new Ft),this._$instance}get uniform(){return this._$uniform}_$createProgram(t,i){const s=this._$gl.createProgram();s.id=e++;const r=this._$gl.createShader(this._$gl.VERTEX_SHADER);this._$gl.shaderSource(r,t),this._$gl.compileShader(r);const n=this._$gl.createShader(this._$gl.FRAGMENT_SHADER);return this._$gl.shaderSource(n,i),this._$gl.compileShader(n),this._$gl.attachShader(s,r),this._$gl.attachShader(s,n),this._$gl.linkProgram(s),this._$gl.detachShader(s,r),this._$gl.detachShader(s,n),this._$gl.deleteShader(r),this._$gl.deleteShader(n),s}_$attachProgram(){const t=this._$context.shaderList;t.currentProgramId!==this._$program.id&&(t.currentProgramId=this._$program.id,this._$gl.useProgram(this._$program))}drawArraysInstanced(t){this._$attachProgram(),this._$context.vao.bindInstnceArray(t),this._$gl.drawArraysInstanced(this._$gl.TRIANGLE_STRIP,0,4,t.count)}_$drawImage(){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindCommonVertexArray(),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$drawGradient(t,e){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindGradientVertexArray(t,e),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$stroke(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawElements(this._$gl.TRIANGLES,t.indexCount,this._$gl.UNSIGNED_SHORT,0)}_$fill(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t);const e=t.indexRanges,i=e[e.length-1];this._$gl.drawArrays(this._$gl.TRIANGLES,0,i.first+i.count)}_$containerClip(t,e,i){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.TRIANGLES,e,i)}_$drawPoints(t,e,i){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.POINTS,e,i)}}class wt{static FUNCTION_GRID_OFF(){return\"\\n\\nvec2 applyMatrix(in vec2 vertex) {\\n mat3 matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n\\n vec2 position = (matrix * vec3(vertex, 1.0)).xy;\\n\\n return position;\\n}\\n\\n\"}static FUNCTION_GRID_ON(t){return`\\n\\nvec2 applyMatrix(in vec2 vertex) {\\n mat3 parent_matrix = mat3(\\n u_highp[${t}].xyz,\\n u_highp[${t+1}].xyz,\\n u_highp[${t+2}].xyz\\n );\\n mat3 ancestor_matrix = mat3(\\n u_highp[${t+3}].xyz,\\n u_highp[${t+4}].xyz,\\n u_highp[${t+5}].xyz\\n );\\n vec2 parent_offset = vec2(u_highp[${t+2}].w, u_highp[${t+3}].w);\\n vec2 parent_size = vec2(u_highp[${t+4}].w, u_highp[${t+5}].w);\\n vec4 grid_min = u_highp[${t+6}];\\n vec4 grid_max = u_highp[${t+7}];\\n\\n vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy;\\n position = (position - parent_offset) / parent_size;\\n\\n vec4 ga = grid_min;\\n vec4 gb = grid_max - grid_min;\\n vec4 gc = vec4(1.0) - grid_max;\\n\\n vec2 pa = position;\\n vec2 pb = position - grid_min.st;\\n vec2 pc = position - grid_max.st;\\n\\n position = (ga.pq / ga.st) * min(pa, ga.st)\\n + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st)\\n + (gc.pq / gc.st) * max(vec2(0.0), pc);\\n\\n position = position * parent_size + parent_offset;\\n position = (ancestor_matrix * vec3(position, 1.0)).xy;\\n\\n return position;\\n}\\n\\n`}}class Rt{static TEMPLATE(t,e,i,s){const r=e-1,n=i?this.VARYING_UV_ON():\"\",a=i?this.STATEMENT_UV_ON():\"\";return`#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\nlayout (location = 1) in vec2 a_option1;\\nlayout (location = 2) in vec2 a_option2;\\nlayout (location = 3) in float a_type;\\n\\nuniform vec4 u_highp[${t}];\\n\\n${n}\\n\\n${s?wt.FUNCTION_GRID_ON(i?5:0):wt.FUNCTION_GRID_OFF()}\\n\\nfloat crossVec2(in vec2 v1, in vec2 v2) {\\n return v1.x * v2.y - v2.x * v1.y;\\n}\\n\\nvec2 perpendicularVec2(in vec2 v1) {\\n float face = u_highp[${r}][1];\\n\\n return face * vec2(v1.y, -v1.x);\\n}\\n\\nvec2 calculateNormal(in vec2 direction) {\\n vec2 normalized = normalize(direction);\\n return perpendicularVec2(normalized);\\n}\\n\\nvec2 calculateIntersection(in vec2 v1, in vec2 v2, in vec2 o1, in vec2 o2) {\\n float t = crossVec2(o2 - o1, v2) / crossVec2(v1, v2);\\n return (o1 + t * v1);\\n}\\n\\nvec2 calculateAnchor(in vec2 position, in float convex, out vec2 v1, out vec2 v2, out vec2 o1, out vec2 o2) {\\n float miter_limit = u_highp[${r}][2];\\n\\n vec2 a = applyMatrix(a_option1);\\n vec2 b = applyMatrix(a_option2);\\n\\n v1 = convex * (position - a);\\n v2 = convex * (b - position);\\n o1 = calculateNormal(v1) + a;\\n o2 = calculateNormal(v2) + position;\\n\\n vec2 anchor = calculateIntersection(v1, v2, o1, o2) - position;\\n return normalize(anchor) * min(length(anchor), miter_limit);\\n}\\n\\nvoid main() {\\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\\n float half_width = u_highp[${r}][0];\\n\\n vec2 position = applyMatrix(a_vertex);\\n vec2 offset = vec2(0.0);\\n vec2 v1, v2, o1, o2;\\n\\n if (a_type == 1.0 || a_type == 2.0) { // 線分\\n offset = calculateNormal(a_option2 * (applyMatrix(a_option1) - position));\\n } else if (a_type == 10.0) { // スクエア線端\\n offset = normalize(position - applyMatrix(a_option1));\\n offset += a_option2 * perpendicularVec2(offset);\\n } else if (a_type == 21.0) { // マイター結合(線分Bの凸側)\\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\\n } else if (a_type == 22.0) { // マイター結合(線分Aの凸側)\\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\\n } else if (a_type == 23.0) { // マイター結合(線分Aの凹側)\\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\\n } else if (a_type == 24.0) { // マイター結合(線分Bの凹側)\\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\\n } else if (a_type >= 30.0) { // ラウンド結合\\n float face = u_highp[${r}][1];\\n float rad = face * (a_type - 30.0) * 0.3488888889; /* 0.3488888889 = PI / 9.0 */\\n offset = mat2(cos(rad), sin(rad), -sin(rad), cos(rad)) * vec2(1.0, 0.0);\\n }\\n \\n offset *= half_width;\\n position += offset;\\n ${a}\\n\\n position /= viewport;\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n`}static VARYING_UV_ON(){return\"\\nout vec2 v_uv;\\n\"}static STATEMENT_UV_ON(){return\"\\n mat3 uv_matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n mat3 inverse_matrix = mat3(\\n u_highp[3].xyz,\\n u_highp[4].xyz,\\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\\n );\\n\\n v_uv = (uv_matrix * vec3(a_vertex, 1.0)).xy;\\n v_uv += offset;\\n v_uv = (inverse_matrix * vec3(v_uv, 1.0)).xy;\\n\"}}class It{static TEMPLATE(t,e,i,s){const r=i?this.ATTRIBUTE_BEZIER_ON():\"\",n=i?this.VARYING_BEZIER_ON():e?this.VARYING_UV_ON():\"\",a=i?this.STATEMENT_BEZIER_ON():e?this.STATEMENT_UV_ON():\"\";return`#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n${r}\\n\\nuniform vec4 u_highp[${t}];\\n\\n${n}\\n\\n${s?wt.FUNCTION_GRID_ON(e?5:0):wt.FUNCTION_GRID_OFF()}\\n\\nvoid main() {\\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\\n\\n ${a}\\n\\n vec2 pos = applyMatrix(a_vertex) / viewport;\\n pos = pos * 2.0 - 1.0;\\n gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\\n}\\n\\n`}static ATTRIBUTE_BEZIER_ON(){return\"\\nlayout (location = 1) in vec2 a_bezier;\\n\"}static VARYING_UV_ON(){return\"\\nout vec2 v_uv;\\n\"}static VARYING_BEZIER_ON(){return\"\\nout vec2 v_bezier;\\n\"}static STATEMENT_UV_ON(){return\"\\n mat3 uv_matrix = mat3(\\n u_highp[0].xyz,\\n u_highp[1].xyz,\\n u_highp[2].xyz\\n );\\n mat3 inverse_matrix = mat3(\\n u_highp[3].xyz,\\n u_highp[4].xyz,\\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\\n );\\n\\n v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy;\\n\"}static STATEMENT_BEZIER_ON(){return\"\\n v_bezier = a_bezier;\\n\"}}class Pt{static FUNCTION_IS_INSIDE(){return\"\\n\\nfloat isInside(in vec2 uv) {\\n return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0)));\\n}\\n\\n\"}static STATEMENT_INSTANCED_COLOR_TRANSFORM_ON(){return\"\\n src.rgb /= max(0.0001, src.a);\\n src = clamp(src * mul + add, 0.0, 1.0);\\n src.rgb *= src.a;\\n\"}static STATEMENT_COLOR_TRANSFORM_ON(t){return`\\n vec4 mul = u_mediump[${t}];\\n vec4 add = u_mediump[${t+1}];\\n${Pt.STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()}\\n`}}class Nt{static SOLID_COLOR(){return\"#version 300 es\\nprecision mediump float;\\n\\nuniform vec4 u_mediump;\\n\\nout vec4 o_color;\\n\\nvoid main() {\\n o_color = vec4(u_mediump.rgb * u_mediump.a, u_mediump.a);\\n}\\n\\n\"}static BITMAP_CLIPPED(){return`#version 300 es\\nprecision mediump float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_mediump[3];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy;\\n\\n vec4 src = texture(u_texture, uv);\\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\\n o_color = src;\\n}`}static BITMAP_PATTERN(){return`#version 300 es\\nprecision mediump float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_mediump[3];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy);\\n \\n vec4 src = texture(u_texture, uv);\\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\\n o_color = src;\\n}`}static MASK(){return\"#version 300 es\\nprecision mediump float;\\n\\nin vec2 v_bezier;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 px = dFdx(v_bezier);\\n vec2 py = dFdy(v_bezier);\\n\\n vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y);\\n float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f);\\n\\n if (alpha > 0.0) {\\n o_color = vec4(min(alpha, 1.0));\\n } else {\\n discard;\\n } \\n}\\n\\n\"}}class kt{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getSolidColorShapeShader(t,e){const i=`s${t?\"y\":\"n\"}${e?\"y\":\"n\"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!1,e);const a=new Bt(this._$gl,this._$context,n,Nt.SOLID_COLOR());return this._$collection.set(i,a),a}getBitmapShapeShader(t,e,i){const s=`b${t?\"y\":\"n\"}${e?\"y\":\"n\"}${i?\"y\":\"n\"}`;if(this._$collection.has(s)){const t=this._$collection.get(s);if(t)return t}const r=(i?13:5)+(t?1:0),n=r;let a;a=t?Rt.TEMPLATE(r,n,!0,i):It.TEMPLATE(r,!0,!1,i);const h=e?Nt.BITMAP_PATTERN():Nt.BITMAP_CLIPPED(),o=new Bt(this._$gl,this._$context,a,h);return this._$collection.set(s,o),o}getMaskShapeShader(t,e){const i=`m${t?\"y\":\"n\"}${e?\"y\":\"n\"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!0,e);const a=new Bt(this._$gl,this._$context,n,Nt.MASK());return this._$collection.set(i,a),a}setSolidColorShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.highp;let u;n?($[0]=_.parentMatrixA,$[1]=_.parentMatrixB,$[2]=_.parentMatrixC,$[4]=_.parentMatrixD,$[5]=_.parentMatrixE,$[6]=_.parentMatrixF,$[8]=_.parentMatrixG,$[9]=_.parentMatrixH,$[10]=_.parentMatrixI,$[12]=_.ancestorMatrixA,$[13]=_.ancestorMatrixB,$[14]=_.ancestorMatrixC,$[16]=_.ancestorMatrixD,$[17]=_.ancestorMatrixE,$[18]=_.ancestorMatrixF,$[20]=_.ancestorMatrixG,$[21]=_.ancestorMatrixH,$[22]=_.ancestorMatrixI,$[3]=h,$[7]=o,$[11]=_.parentViewportX,$[15]=_.parentViewportY,$[19]=_.parentViewportW,$[23]=_.parentViewportH,$[24]=_.minXST,$[25]=_.minYST,$[26]=_.minXPQ,$[27]=_.minYPQ,$[28]=_.maxXST,$[29]=_.maxYST,$[30]=_.maxXPQ,$[31]=_.maxYPQ,u=32):($[0]=a[0],$[1]=a[1],$[2]=a[2],$[4]=a[3],$[5]=a[4],$[6]=a[5],$[8]=a[6],$[9]=a[7],$[10]=a[8],$[3]=h,$[7]=o,u=12),e&&($[u]=i,$[u+1]=s,$[u+2]=r);const d=t.mediump;d[0]=l[0],d[1]=l[1],d[2]=l[2],d[3]=l[3]*c}setBitmapShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x,b){const v=t.highp;let T;v[0]=a[0],v[1]=a[1],v[2]=a[2],v[4]=a[3],v[5]=a[4],v[6]=a[5],v[8]=a[6],v[9]=a[7],v[10]=a[8],v[12]=h[0],v[13]=h[1],v[14]=h[2],v[16]=h[3],v[17]=h[4],v[18]=h[5],v[11]=h[6],v[15]=h[7],v[19]=h[8],v[3]=o,v[7]=_,T=20,n&&(v[T]=l.parentMatrixA,v[T+1]=l.parentMatrixB,v[T+2]=l.parentMatrixC,v[T+4]=l.parentMatrixD,v[T+5]=l.parentMatrixE,v[T+6]=l.parentMatrixF,v[T+8]=l.parentMatrixG,v[T+9]=l.parentMatrixH,v[T+10]=l.parentMatrixI,v[T+12]=l.ancestorMatrixA,v[T+13]=l.ancestorMatrixB,v[T+14]=l.ancestorMatrixC,v[T+16]=l.ancestorMatrixD,v[T+17]=l.ancestorMatrixE,v[T+18]=l.ancestorMatrixF,v[T+20]=l.ancestorMatrixG,v[T+21]=l.ancestorMatrixH,v[T+22]=l.ancestorMatrixI,v[T+11]=l.parentViewportX,v[T+15]=l.parentViewportY,v[T+19]=l.parentViewportW,v[T+23]=l.parentViewportH,v[T+24]=l.minXST,v[T+25]=l.minYST,v[T+26]=l.minXPQ,v[T+27]=l.minYPQ,v[T+28]=l.maxXST,v[T+29]=l.maxYST,v[T+30]=l.maxXPQ,v[T+31]=l.maxYPQ,T=52),e&&(v[T]=i,v[T+1]=s,v[T+2]=r);const A=t.mediump;A[0]=c,A[1]=$,A[4]=u,A[5]=d,A[6]=g,A[7]=f,A[8]=m,A[9]=p,A[10]=x,A[11]=b}setMaskShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u=null){const d=t.highp;e&&u?(d[0]=u.parentMatrixA,d[1]=u.parentMatrixB,d[2]=u.parentMatrixC,d[4]=u.parentMatrixD,d[5]=u.parentMatrixE,d[6]=u.parentMatrixF,d[8]=u.parentMatrixG,d[9]=u.parentMatrixH,d[10]=u.parentMatrixI,d[12]=u.ancestorMatrixA,d[13]=u.ancestorMatrixB,d[14]=u.ancestorMatrixC,d[16]=u.ancestorMatrixD,d[17]=u.ancestorMatrixE,d[18]=u.ancestorMatrixF,d[20]=u.ancestorMatrixG,d[21]=u.ancestorMatrixH,d[22]=u.ancestorMatrixI,d[3]=c,d[7]=$,d[11]=u.parentViewportX,d[15]=u.parentViewportY,d[19]=u.parentViewportW,d[23]=u.parentViewportH,d[24]=u.minXST,d[25]=u.minYST,d[26]=u.minXPQ,d[27]=u.minYPQ,d[28]=u.maxXST,d[29]=u.maxYST,d[30]=u.maxXPQ,d[31]=u.maxYPQ):(d[0]=i,d[1]=s,d[2]=r,d[4]=n,d[5]=a,d[6]=h,d[8]=o,d[9]=_,d[10]=l,d[3]=c,d[7]=$)}setMaskShapeUniformIdentity(t,e,i){const s=t.highp;s[0]=1,s[1]=0,s[2]=0,s[4]=0,s[5]=1,s[6]=0,s[8]=0,s[9]=0,s[10]=1,s[3]=e,s[7]=i}}class Lt{static TEMPLATE(t,e,i,s,r){const n=i?this.STATEMENT_GRADIENT_TYPE_RADIAL(e,s):this.STATEMENT_GRADIENT_TYPE_LINEAR(e);let a;switch(r){case\"reflect\":a=\"1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)\";break;case\"repeat\":a=\"fract(t)\";break;default:a=\"clamp(t, 0.0, 1.0)\"}return`#version 300 es\\nprecision highp float;\\n\\nuniform sampler2D u_texture;\\nuniform vec4 u_highp[${t}];\\n\\nin vec2 v_uv;\\nout vec4 o_color;\\n\\nvoid main() {\\n vec2 p = v_uv;\\n ${n}\\n t = ${a};\\n o_color = texture(u_texture, vec2(t, 0.5));\\n}\\n\\n`}static STATEMENT_GRADIENT_TYPE_LINEAR(t){return`\\n vec2 a = u_highp[${t}].xy;\\n vec2 b = u_highp[${t}].zw;\\n\\n vec2 ab = b - a;\\n vec2 ap = p - a;\\n\\n float t = dot(ab, ap) / dot(ab, ab);\\n`}static STATEMENT_GRADIENT_TYPE_RADIAL(t,e){return`\\n float radius = u_highp[${t}][0];\\n\\n vec2 coord = p / radius;\\n ${e?this.STATEMENT_FOCAL_POINT_ON(t):this.STATEMENT_FOCAL_POINT_OFF()}\\n`}static STATEMENT_FOCAL_POINT_OFF(){return\"\\n float t = length(coord);\\n\"}static STATEMENT_FOCAL_POINT_ON(t){return`\\n vec2 focal = vec2(u_highp[${t}][1], 0.0);\\n\\n vec2 dir = normalize(coord - focal);\\n\\n float a = dot(dir, dir);\\n float b = 2.0 * dot(dir, focal);\\n float c = dot(focal, focal) - 1.0;\\n float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);\\n\\n float t = distance(focal, coord) / distance(focal, focal + dir * x);\\n`}}class Ot{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getGradientShapeShader(t,e,i,s,r){const n=this.createCollectionKey(t,e,i,s,r);if(this._$collection.has(n)){const t=this._$collection.get(n);if(t)return t}const a=(e?13:5)+(t?1:0)+1,h=a-1;let o;o=t?Rt.TEMPLATE(a,h,!0,e):It.TEMPLATE(a,!0,!1,e);const _=new Bt(this._$gl,this._$context,o,Lt.TEMPLATE(a,h,i,s,r));return this._$collection.set(n,_),_}createCollectionKey(t,e,i,s,r){const n=t?\"y\":\"n\",a=e?\"y\":\"n\",h=i?\"y\":\"n\",o=i&&s?\"y\":\"n\";let _=0;switch(r){case\"reflect\":_=1;break;case\"repeat\":_=2}return`${n}${a}${h}${o}${_}`}setGradientShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.highp;d[0]=a[0],d[1]=a[1],d[2]=a[2],d[4]=a[3],d[5]=a[4],d[6]=a[5],d[8]=a[6],d[9]=a[7],d[10]=a[8],d[12]=h[0],d[13]=h[1],d[14]=h[2],d[16]=h[3],d[17]=h[4],d[18]=h[5],d[11]=h[6],d[15]=h[7],d[19]=h[8],d[3]=o,d[7]=_;let g=20;n&&(d[g]=l.parentMatrixA,d[g+1]=l.parentMatrixB,d[g+2]=l.parentMatrixC,d[g+4]=l.parentMatrixD,d[g+5]=l.parentMatrixE,d[g+6]=l.parentMatrixF,d[g+8]=l.parentMatrixG,d[g+9]=l.parentMatrixH,d[g+10]=l.parentMatrixI,d[g+12]=l.ancestorMatrixA,d[g+13]=l.ancestorMatrixB,d[g+14]=l.ancestorMatrixC,d[g+16]=l.ancestorMatrixD,d[g+17]=l.ancestorMatrixE,d[g+18]=l.ancestorMatrixF,d[g+20]=l.ancestorMatrixG,d[g+21]=l.ancestorMatrixH,d[g+22]=l.ancestorMatrixI,d[g+11]=l.parentViewportX,d[g+15]=l.parentViewportY,d[g+19]=l.parentViewportW,d[g+23]=l.parentViewportH,d[g+24]=l.minXST,d[g+25]=l.minYST,d[g+26]=l.minXPQ,d[g+27]=l.minYPQ,d[g+28]=l.maxXST,d[g+29]=l.maxYST,d[g+30]=l.maxXPQ,d[g+31]=l.maxYPQ,g=52),e&&(d[g]=i,d[g+1]=s,d[g+2]=r,g+=4),c?(d[g]=$[5],d[g+1]=u):(d[g]=$[0],d[g+1]=$[1],d[g+2]=$[2],d[g+3]=$[3])}}class Ut{static TEXTURE(){return\"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 position = a_vertex * 2.0 - 1.0;\\n gl_Position = vec4(position, 0.0, 1.0);\\n}\\n\\n\"}static BLEND(){return\"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[4];\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 offset = u_highp[0].xy;\\n vec2 size = u_highp[0].zw;\\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * size + offset;\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= viewport;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n\"}static INSTANCE_BLEND(){return\"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[5];\\n\\nout vec2 v_src_coord;\\nout vec2 v_dst_coord;\\n\\nvoid main() {\\n vec4 rect = vec4(u_highp[0].x, u_highp[0].y, u_highp[0].z, u_highp[0].w);\\n vec2 size = vec2(u_highp[4].x, u_highp[4].y);\\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n v_src_coord = a_vertex * rect.zw + rect.xy;\\n v_dst_coord = a_vertex;\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * size;\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= viewport;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n\"}static INSTANCE(){return\"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\nlayout (location = 1) in vec4 a_rect;\\nlayout (location = 2) in vec4 a_size;\\nlayout (location = 3) in vec2 a_offset;\\nlayout (location = 4) in vec4 a_matrix;\\nlayout (location = 5) in vec4 a_mul;\\nlayout (location = 6) in vec4 a_add;\\n\\nout vec2 v_coord;\\nout vec4 mul;\\nout vec4 add;\\n\\nvoid main() {\\n v_coord = a_vertex * a_rect.zw + a_rect.xy;\\n mul = a_mul;\\n add = a_add;\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position = position * a_size.xy;\\n mat3 matrix = mat3(a_matrix.x, a_matrix.y, 0.0, a_matrix.z, a_matrix.w, 0.0, a_offset.x, a_offset.y, 1.0);\\n position = (matrix * vec3(position, 1.0)).xy;\\n position /= a_size.zw;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n\"}static BLEND_CLIP(){return\"#version 300 es\\n\\nlayout (location = 0) in vec2 a_vertex;\\n\\nuniform vec4 u_highp[4];\\n\\nout vec2 v_coord;\\n\\nvoid main() {\\n v_coord = a_vertex;\\n\\n vec2 offset = u_highp[0].xy;\\n vec2 size = u_highp[0].zw;\\n mat3 inv_matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\\n\\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\\n position *= viewport;\\n position = (inv_matrix * vec3(position, 1.0)).xy;\\n position = (position - offset) / size;\\n\\n position = position * 2.0 - 1.0;\\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\\n}\\n\\n\"}}class Dt{static TEMPLATE(t,e,i){let s=\"\";for(let t=1;t>16)/255,h[a++]=(e>>8&255)/255,h[a++]=(255&e)/255,h[a++]=s[t]}for(let t=r;tthis._$vertexBufferData.length){const t=new o(2*this._$vertexBufferData.length);t.set(this._$vertexBufferData),this._$vertexBufferData=t}}static _$expandIndexBufferIfNeeded(t){if(this._$indexBufferPos+t>this._$indexBufferData.length){const t=new l(2*this._$indexBufferData.length);t.set(this._$indexBufferData),this._$indexBufferData=t}}static _$generateLineSegment(t){const e=t.length-5;for(let i=0;it*s-i*e;class ee{constructor(t){this._$gl=t,this._$fillVertexArrayPool=[],this._$strokeVertexArrayPool=[],this._$boundVertexArray=null,this._$fillAttrib_vertex=0,this._$fillAttrib_bezier=1,this._$strokeAttrib_vertex=0,this._$strokeAttrib_option1=1,this._$strokeAttrib_option2=2,this._$strokeAttrib_type=3,this._$vertexBufferData=new Float32Array([0,0,0,1,1,0,1,1]),this._$attributeVertexBuffer=t.createBuffer(),this._$attributeBuffer=new Float32Array(22),this._$instanceVertexArray=this._$getCommonVertexArray(),this._$commonVertexArray=this._$getVertexArray(0,1)}_$getCommonVertexArray(){const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,this._$attributeVertexBuffer),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(1,4,this._$gl.FLOAT,!1,88,0),this._$gl.vertexAttribDivisor(1,1),this._$gl.enableVertexAttribArray(2),this._$gl.vertexAttribPointer(2,4,this._$gl.FLOAT,!1,88,16),this._$gl.vertexAttribDivisor(2,1),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(3,2,this._$gl.FLOAT,!1,88,32),this._$gl.vertexAttribDivisor(3,1),this._$gl.enableVertexAttribArray(4),this._$gl.vertexAttribPointer(4,4,this._$gl.FLOAT,!1,88,40),this._$gl.vertexAttribDivisor(4,1),this._$gl.enableVertexAttribArray(5),this._$gl.vertexAttribPointer(5,4,this._$gl.FLOAT,!1,88,56),this._$gl.vertexAttribDivisor(5,1),this._$gl.enableVertexAttribArray(6),this._$gl.vertexAttribPointer(6,4,this._$gl.FLOAT,!1,88,72),this._$gl.vertexAttribDivisor(6,1),t}_$getVertexArray(t,e){const i=this._$gl.createVertexArray();this.bind(i);const s=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s),this._$vertexBufferData[0]=t,this._$vertexBufferData[2]=t,this._$vertexBufferData[4]=e,this._$vertexBufferData[6]=e,this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$vertexBufferData,this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),i}_$getFillVertexArray(){if(this._$fillVertexArrayPool.length){const t=this._$fillVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(this._$fillAttrib_vertex,2,this._$gl.FLOAT,!1,16,0),this._$gl.vertexAttribPointer(this._$fillAttrib_bezier,2,this._$gl.FLOAT,!1,16,8),t}_$getStrokeVertexArray(){if(this._$strokeVertexArrayPool.length){const t=this._$strokeVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e);const i=this._$gl.createBuffer();return t.indexBuffer=i,t.indexLength=0,this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER,i),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.enableVertexAttribArray(2),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(this._$strokeAttrib_vertex,2,this._$gl.FLOAT,!1,28,0),this._$gl.vertexAttribPointer(this._$strokeAttrib_option1,2,this._$gl.FLOAT,!1,28,8),this._$gl.vertexAttribPointer(this._$strokeAttrib_option2,2,this._$gl.FLOAT,!1,28,16),this._$gl.vertexAttribPointer(this._$strokeAttrib_type,1,this._$gl.FLOAT,!1,28,24),t}createFill(t){const e=Zt.generate(t),i=e.vertexBufferData,s=this._$getFillVertexArray();return s.indexRanges=e.indexRanges,this.bind(s),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s.vertexBuffer),s.vertexLengththis._$attributeBuffer.length&&(this._$attributeBuffer=new Float32Array(t.attributes.length),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW)),this._$attributeBuffer.set(t.attributes),this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER,0,this._$attributeBuffer.subarray(0,t.attributes.length))}bindCommonVertexArray(){this.bind(this._$commonVertexArray)}bindGradientVertexArray(t,e){const i=this._$getVertexArray(t,e);this.bind(i)}}class ie{constructor(t,e){this._$context=t,this._$gl=e,this._$clips=[],this._$poolClip=[],this._$clipStatus=!1,this._$containerClip=!1,this._$currentClip=!1}get containerClip(){return this._$containerClip}set containerClip(t){this._$containerClip=t}_$onClear(t){t&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0)}_$onBind(t){!t&&this._$currentClip?(this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1):t&&!this._$currentClip&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0,this._$endClipDef())}_$onClearRect(){this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1}_$enterClip(){this._$currentClip||(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0);const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error(\"mask currentAttachment is null.\");t.mask=!0,++t.clipLevel}_$beginClipDef(){const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error(\"mask currentAttachment is null.\");this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.stencilMask(1<7&&(this._$unionStencilMask(e,a,h),n=e)}n>e+1&&this._$unionStencilMask(e,a,h)}_$unionStencilMask(t,e,i){const s=this._$context.path.createRectVertices(0,0,e,i),r=this._$context.vao.createFill(s);D(s.pop()),D(s);const n=this._$context.shaderList.shapeShaderVariants,a=n.getMaskShapeShader(!1,!1),h=a.uniform;n.setMaskShapeUniformIdentity(h,e,i);const o=r.indexRanges[0];this._$gl.stencilFunc(this._$gl.LEQUAL,1<this._$maxTextureSize?this._$maxTextureSize/i:1}drawInstacedArray(){this.blend.drawInstacedArray()}clearInstacedArray(){this.blend.clearInstacedArray()}bindRenderBuffer(t){this._$frameBufferManager.bindRenderBuffer(),this._$gl.clearColor(0,0,0,0),this._$gl.clear(this._$gl.COLOR_BUFFER_BIT|this._$gl.STENCIL_BUFFER_BIT),this._$viewportWidth=t.w,this._$viewportHeight=t.h,this._$gl.viewport(t.x,t.y,t.w,t.h),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(t.x,t.y,t.w,t.h)}getTextureFromRect(t){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t.index),s=e.currentAttachment,r=e.createTextureAttachment(t.w,t.h);this._$bind(r),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(i,-t.x,-i.height+t.h+t.y,i.width,i.height),this.restore();const n=r.texture;return e.releaseAttachment(r),this._$bind(s),n}drawBitmap(t){const e=this._$shaderList.blendShaderVariants,i=e.getNormalBlendShader(!1);e.setNormalBlendUniform(i.uniform,0,0,t.width,t.height,this._$matrix,this._$viewportWidth,this._$viewportHeight,!1,1,1,1,1,0,0,0,0),this._$frameBufferManager.textureManager.bind0(t,this._$imageSmoothingEnabled),this.blend.toOperation(\"normal\"),i._$drawImage()}drawTextureFromRect(t,e){const i=this._$frameBufferManager,s=i.currentAttachment;this.bindRenderBuffer(e),i.transferTexture(e);const r=i.textureManager.getAtlasTexture(e.index),n=i.createTextureAttachmentFrom(r);this._$bind(n),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(e.x,e.y,e.w,e.h),this._$gl.clearColor(0,0,0,0),this._$gl.disable(this._$gl.SCISSOR_TEST),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(t,e.x,r.height-e.h-e.y,t.width,t.height),this.restore(),i.releaseAttachment(n),this._$bind(s),i.textureManager.release(t)}stopStencil(){this._$mask._$onClearRect()}_$bind(t=null){if(!t)return;this._$frameBufferManager.bind(t);const e=t.color,i=t.stencil,s=t.width,r=t.height;this._$viewportWidth===s&&this._$viewportHeight===r||(this._$viewportWidth=s,this._$viewportHeight=r,this._$gl.viewport(0,0,s,r)),(e&&e.dirty||i&&i.dirty)&&(e&&(e.dirty=!1),i&&(i.dirty=!1),this._$gl.clearColor(0,0,0,0),this.clearRect(0,0,this._$viewportWidth,this._$viewportHeight),this._$gl.clearColor(this._$clearColorR,this._$clearColorG,this._$clearColorB,this._$clearColorA),this._$mask._$onClear(t.mask)),this._$mask._$onBind(t.mask)}setTransform(t,e,i,s,r,n){this._$matrix[0]=t,this._$matrix[1]=e,this._$matrix[3]=i,this._$matrix[4]=s,this._$matrix[6]=r,this._$matrix[7]=n}setMaxSize(t,e){this._$frameBufferManager.setMaxSize(t,e)}transform(t,e,i,s,r,n){const a=this._$matrix[0],h=this._$matrix[1],o=this._$matrix[3],_=this._$matrix[4],l=this._$matrix[6],c=this._$matrix[7];this._$matrix[0]=t*a+e*o,this._$matrix[1]=t*h+e*_,this._$matrix[3]=i*a+s*o,this._$matrix[4]=i*h+s*_,this._$matrix[6]=r*a+n*o+l,this._$matrix[7]=r*h+n*_+c}debug(t=0){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t),s=e.currentAttachment,r=e.createTextureAttachmentFrom(i);this._$bind(r);const n=new Uint8Array(i.width*i.height*4);this._$gl.readPixels(0,0,i.width,i.height,this._$gl.RGBA,this._$gl.UNSIGNED_BYTE,n);const a=document.createElement(\"canvas\");a.width=i.width,a.height=i.height;const h=a.getContext(\"2d\"),o=new ImageData(i.width,i.height);for(let t=0;ts.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this.fillStyle;let r,n,a,h=this._$matrix;const o=this._$grid.enabled;if(s instanceof mt){const t=s.stops,e=\"linearRGB\"===s.rgb;if(r=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(r,!0),this._$frameBufferManager.bindRenderBuffer(),n=this._$shaderList.gradientShapeShaderVariants,\"linear\"===s.type)a=n.getGradientShapeShader(!1,o,!1,!1,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,s.points,0);else{h=this._$stack[this._$stack.length-1];const t=0!==s.focalPointRatio;a=n.getGradientShapeShader(!1,o,!0,t,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,s.points,s.focalPointRatio)}}else if(s instanceof pt){h=this._$stack[this._$stack.length-1];const t=s.colorTransform;r=s.texture,this._$frameBufferManager.textureManager.bind0(r,this._$imageSmoothingEnabled),n=this._$shaderList.shapeShaderVariants,a=n.getBitmapShapeShader(!1,s.repeat,o),t?n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,1,1,1,this._$globalAlpha,0,0,0,0)}else n=this._$shaderList.shapeShaderVariants,a=n.getSolidColorShapeShader(!1,this._$grid.enabled),n.setSolidColorShapeUniform(a.uniform,!1,0,0,0,o,h,this._$viewportWidth,this._$viewportHeight,this._$grid,s,this._$globalAlpha);const _=this._$shaderList.shapeShaderVariants,l=_.getMaskShapeShader(!1,o);_.setMaskShapeUniform(l.uniform,o,h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],this._$viewportWidth,this._$viewportHeight,this._$grid),this._$gl.enable(this._$gl.STENCIL_TEST),this._$gl.stencilMask(255),this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.colorMask(!1,!1,!1,!1),l._$fill(i),this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.NOTEQUAL,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.ZERO,this._$gl.ZERO),this._$gl.colorMask(!0,!0,!0,!0),a._$fill(i),this._$gl.disable(this._$gl.STENCIL_TEST),this.releaseFillVertexArray(i)}releaseFillVertexArray(t){this._$vao.releaseFill(t);const e=t.indexRanges;for(let t=0;ta.width||i>a.height||0>e&&0>=s+e||0>i&&0>=n+i||(this._$maskBounds.xMin=r.max(0,r.min(this._$maskBounds.xMin,e)),this._$maskBounds.yMin=r.max(0,r.min(this._$maskBounds.yMin,i)),this._$maskBounds.xMax=r.min(a.width,r.min(this._$maskBounds.xMax,s)),this._$maskBounds.yMax=r.min(a.height,r.min(this._$maskBounds.yMax,n)),0))}_$endClipDef(){this._$mask._$endClipDef()}_$leaveClip(){this.drawInstacedArray(),this._$mask._$leaveClip()}_$drawContainerClip(){this._$mask._$drawContainerClip()}closePath(){this._$path.close()}stroke(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createStroke(t,this.lineCap,this.lineJoin);let s=this._$matrix;const n=this.strokeStyle;let a=r.sign(s[0]*s[4]);a>0&&0!==s[1]&&0!==s[3]&&(a=-r.sign(s[1]*s[3]));let h,o,_=.5*this.lineWidth;this._$grid.enabled?(h=r.abs(this._$grid.ancestorMatrixA+this._$grid.ancestorMatrixD),o=r.abs(this._$grid.ancestorMatrixB+this._$grid.ancestorMatrixE)):(h=r.abs(s[0]+s[3]),o=r.abs(s[1]+s[4]));const l=r.min(h,o),c=r.max(h,o);_*=c*(1-.3*r.cos(.5*r.PI*(l/c))),_=r.max(1,_);const $=this._$grid.enabled;let u,d,g;if(n instanceof mt){\"radial\"===n.type&&(s=this._$stack[this._$stack.length-1]);const t=n.stops,e=\"linearRGB\"===n.rgb;if(u=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(u,!0),d=this._$shaderList.gradientShapeShaderVariants,\"linear\"===n.type)g=d.getGradientShapeShader(!0,$,!1,!1,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,n.points,0);else{s=this._$stack[this._$stack.length-1];const t=0!==n.focalPointRatio;g=d.getGradientShapeShader(!0,$,!0,t,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,n.points,n.focalPointRatio)}}else if(n instanceof pt){s=this._$stack[this._$stack.length-1];const t=n.colorTransform;u=n.texture,this._$frameBufferManager.textureManager.bind0(u),d=this._$shaderList.shapeShaderVariants,g=d.getBitmapShapeShader(!0,n.repeat,this._$grid.enabled),t?d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,1,1,1,this._$globalAlpha,0,0,0,0)}else d=this._$shaderList.shapeShaderVariants,g=d.getSolidColorShapeShader(!0,this._$grid.enabled),d.setSolidColorShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,this._$viewportWidth,this._$viewportHeight,this._$grid,n,this._$globalAlpha);g._$stroke(i),this._$vao.releaseStroke(i)}arc(t,e,i){this._$path.drawCircle(t,e,i)}clip(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this._$shaderList.shapeShaderVariants,r=s.getMaskShapeShader(!1,!1),n=r.uniform;s.setMaskShapeUniform(n,!1,this._$matrix[0],this._$matrix[1],this._$matrix[2],this._$matrix[3],this._$matrix[4],this._$matrix[5],this._$matrix[6],this._$matrix[7],this._$matrix[8],this._$viewportWidth,this._$viewportHeight,null),this._$mask._$onClip(i,this._$matrix,this._$viewportWidth,this._$viewportHeight)||(r._$fill(i),this.beginPath())}save(){const t=this._$matrix;this._$stack.push(O(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])),this._$mask._$onSave()}restore(){var t;this._$stack.length&&(t=this._$matrix,M.push(t),this._$matrix=this._$stack.pop()||O()),this._$mask._$onRestore()}createPattern(t,e,i){return new pt(t,e,i)}createLinearGradient(t,e,i,s,r=\"rgb\",n=\"pad\"){return(new mt).linear(t,e,i,s,r,n)}createRadialGradient(t,e,i,s,r,n,a=\"rgb\",h=\"pad\",o=0){return(new mt).radial(t,e,i,s,r,n,a,h,o)}_$applyBlurFilter(t,e,i){const s=this._$frameBufferManager,n=s.currentAttachment;if(!n)throw new Error(\"the current attachment is null.\");const a=n.width,h=n.height;s.textureManager.bind0(t,!0);const o=r.ceil(.5*i),_=1-(o-.5*i),l=1+i,c=this._$shaderList.filterShaderVariants,$=c.getBlurFilterShader(o);c.setBlurFilterUniform($.uniform,a,h,e,_,l),$._$drawImage()}_$applyBitmapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g=null,f=null,m=null,p=0,x=0,b=0,v=0,T=0,A=0,M=0,y=0){const E=this._$frameBufferManager,C=\"inner\"===$,S=E.currentAttachment,F=E.getTextureFromCurrentAttachment();let B=null;const w=null!==g&&null!==f&&null!==m;let R;null!==g&&null!==f&&null!==m&&(B=this._$gradientLUT.generateForFilter(g,f,m)),C?w&&B?E.textureManager.bind02(t,B,!0):E.textureManager.bind0(t):(R=this._$frameBufferManager.createTextureAttachment(e,i),this._$bind(R),w&&B?E.textureManager.bind012(t,F,B,!0):E.textureManager.bind01(t,F));const I=!(C||\"full\"===$&&u),P=!(e===h&&i===o&&0===_&&0===l),N=!(1===d),k=this._$shaderList.filterShaderVariants,L=k.getBitmapFilterShader(I,P,c,$,u,N,w);k.setBitmapFilterUniform(L.uniform,e,i,s,r,n,a,h,o,_,l,c,d,p,x,b,v,T,A,M,y,I,P,N,w),C?u?this.blend.toSourceIn():this.blend.toSourceAtop():this.blend.toOneZero(),L._$drawImage(),C||E.releaseAttachment(S,!0)}_$applyColorMatrixFilter(t,e){this._$frameBufferManager.textureManager.bind0(t,!0);const i=this._$shaderList.filterShaderVariants,s=i.getColorMatrixFilterShader();i.setColorMatrixFilterUniform(s.uniform,e),this.blend.reset(),s._$drawImage()}_$applyConvolutionFilter(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.width,u=t.height,d=this._$frameBufferManager.createTextureAttachment($,u);this._$bind(d),this._$frameBufferManager.textureManager.bind0(t,!0);const g=this._$shaderList.filterShaderVariants,f=g.getConvolutionFilterShader(e,i,a,h);g.setConvolutionFilterUniform(f.uniform,$,u,s,r,n,h,o,_,l,c),this.blend.reset(),f._$drawImage()}_$applyDisplacementMapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.width,g=t.height,f=this._$frameBufferManager.createTextureAttachment(d,g);this._$bind(f),r||(r={x:0,y:0});const m=this._$frameBufferManager.createTextureFromImage(e);this._$frameBufferManager.textureManager.bind01(t,m);const p=this._$shaderList.filterShaderVariants,x=p.getDisplacementMapFilterShader(n,a,_);p.setDisplacementMapFilterUniform(x.uniform,e.width,e.height,i,s,r.x,r.y,h,o,_,l,c,$,u),this.blend.reset(),x._$drawImage(),this._$frameBufferManager.releaseTexture(m)}_$startLayer(t){this._$positions.push(t),this._$blends.push(this._$isLayer),this._$isLayer=!0}_$endLayer(){const t=this._$positions.pop();t&&B(t),this._$isLayer=!!this._$blends.pop()}_$saveAttachment(t,e,i=!1){this.drawInstacedArray();const s=this._$frameBufferManager;this._$attachmentArray.push(s.currentAttachment),this._$bind(s.createCacheAttachment(t,e,i))}_$restoreAttachment(t=!1){const e=this._$frameBufferManager;e.releaseAttachment(e.currentAttachment,t),this._$bind(this._$attachmentArray.pop())}getCurrentPosition(){return this._$positions[this._$positions.length-1]}textureScale(t,e){const i=r.max(t,e);return i>this._$maxTextureSize?this._$maxTextureSize/i:1}}class ne extends gt{constructor(){super(),this._$recodes=null,this._$maxAlpha=0,this._$canDraw=!1,this._$uniqueKey=\"\",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0),this._$bitmapId=0,this._$mode=\"shape\"}_$clip(t,e){if(!this._$recodes)return;const i=this._$getBounds(),n=q(i,e);B(i);const a=r.ceil(r.abs(n.xMax-n.xMin)),h=r.ceil(r.abs(n.yMax-n.yMin));switch(B(n),!0){case 0===a:case 0===h:case a===-1/0:case h===-1/0:case a===s:case h===s:return}t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),this._$runCommand(t,this._$recodes,null,!0),t.clip()}_$createCacheKey(){if(!this._$recodes)return\"\";let t=0;for(let e=0;e0&&this._$canApply(a);let T=F(0,m,0,p);if(v&&a)for(let t=0;tM.width||f-T.yMin>M.height)return void B(T);if(0>d+T.xMax||0>f+T.yMax)return void B(T);if(B(T),\"\"===this._$uniqueKey&&(!l&&this._$loaderInfoId>-1&&this._$characterId>-1?this._$uniqueKey=`${this._$loaderInfoId}@${this._$characterId}`:this._$uniqueKey=this._$createCacheKey()),\"bitmap\"===this._$mode)this._$cacheKeys.length||(this._$cacheKeys=rt.generateKeys(this._$uniqueKey));else if(!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$uniqueKey,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=A.currentAttachment;s&&s.mask&&t.stopStencil();let n=0,a=0;if(\"shape\"===this._$mode){n=r.ceil(r.abs(c.xMax-c.xMin)*x),a=r.ceil(r.abs(c.yMax-c.yMin)*b);const e=t._$getTextureScale(n,a);e<1&&(n*=e,a*=e)}else n=r.ceil(r.abs(c.xMax-c.xMin)),a=r.ceil(r.abs(c.yMax-c.yMin));if(t.cachePosition=A.createCachePosition(n,a),t.bindRenderBuffer(t.cachePosition),t.reset(),\"shape\"===this._$mode?t.setTransform(x,0,0,b,-c.xMin*x,-c.yMin*b):t.setTransform(1,0,0,1,-c.xMin,-c.yMin),l){const i=le.scaleX,s=P(i,0,0,i,0,0),n=H(s,_);N(s);const a=this._$matrixBase,h=P(a[0],a[1],a[2],a[3],a[4]*i-d,a[5]*i-f),o=H(h,n),l=o[4]-(e[4]-d),$=o[5]-(e[5]-f);N(o);const u=q(c,n),g=+u.xMax,m=+u.xMin,p=+u.yMax,x=+u.yMin,b=r.ceil(r.abs(g-m)),v=r.ceil(r.abs(p-x));B(u),t.grid.enable(m,x,b,v,c,this._$scale9Grid,i,n[0],n[1],n[2],n[3],n[4],n[5],h[0],h[1],h[2],h[3],h[4]-l,h[5]-$),N(n),N(h)}this._$runCommand(t,this._$recodes,i,!1),l&&t.grid.disable(),A.transferTexture(t.cachePosition),rt.set(this._$cacheKeys,t.cachePosition),t._$bind(s)}let y=0,E=0;if(v&&a){const i=this._$createBitmapTexture(t,t.cachePosition,x,b,m,p),s=this._$drawFilter(t,e,a,m,p,i);s.offsetX&&(y=s.offsetX),s.offsetY&&(E=s.offsetY),t.cachePosition=s}if(v||\"bitmap\"!==this._$mode){const i=r.atan2(e[1],e[0]),s=r.atan2(-e[2],e[3]);if(v||!i&&!s)t.setTransform(1,0,0,1,d-y,f-E);else{const n=c.xMin*x,a=c.yMin*b,h=r.cos(i),o=r.sin(i),_=r.cos(s),l=r.sin(s);t.setTransform(h,o,-l,_,n*h-a*l+e[4],n*o+a*_+e[5])}}else t.setTransform(e[0],e[1],e[2],e[3],c.xMin*e[0]+c.yMin*e[2]+e[4],c.xMin*e[1]+c.yMin*e[3]+e[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=\"shape\"===this._$mode,t.globalCompositeOperation=n,t.drawInstance(d-y,f-E,u,g,i),t.cachePosition=null),B(c)}setupStroke(t,e,i,s,r){switch(t.lineWidth=e,i){case 0:t.lineCap=\"none\";break;case 1:t.lineCap=\"round\";break;case 2:t.lineCap=\"square\"}switch(s){case 0:t.lineJoin=\"bevel\";break;case 1:t.lineJoin=\"miter\";break;case 2:t.lineJoin=\"round\"}t.miterLimit=r}createGradientStyle(t,e,i,s,n,a,h,o=null){let _,l=\"pad\";switch(n){case 0:l=\"reflect\";break;case 1:l=\"repeat\"}if(0===e){const e=(t=>{const e=-819.2*t[0]-819.2*t[2]+t[4],i=819.2*t[0]-819.2*t[2]+t[4],s=-819.2*t[0]+819.2*t[2]+t[4],n=-819.2*t[1]-819.2*t[3]+t[5],a=819.2*t[1]-819.2*t[3]+t[5];let h=s-e,o=-819.2*t[1]+819.2*t[3]+t[5]-n;const _=r.sqrt(h*h+o*o);_?(h/=_,o/=_):(h=0,o=0);const l=(i-e)*h+(a-n)*o;return w(e+l*h,n+l*o,i,a)})(s);_=t.createLinearGradient(e[0],e[1],e[2],e[3],a?\"rgb\":\"linearRGB\",l)}else t.save(),t.transform(s[0],s[1],s[2],s[3],s[4],s[5]),_=t.createRadialGradient(0,0,0,0,0,819.2,a?\"rgb\":\"linearRGB\",l,h);for(let t=0;t-1&&this._$characterId>-1&&rt.removeCache(`${this._$loaderInfoId}@${this._$characterId}`))}}class ae extends ne{_$clip(t,e){let i=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(i=H(e,n));const a=this._$getBounds(),h=q(a,i);B(a);const o=r.ceil(r.abs(h.xMax-h.xMin)),_=r.ceil(r.abs(h.yMax-h.yMin));switch(B(h),!0){case 0===o:case 0===_:case o===-1/0:case _===-1/0:case o===s:case _===s:return}super._$clip(t,i),i!==e&&N(i)}_$draw(t,e,i){if(!this._$visible||!this._$maxAlpha||!this._$canDraw)return;let s=i;const r=this._$colorTransform;if(1===r[0]&&1===r[1]&&1===r[2]&&1===r[3]&&0===r[4]&&0===r[5]&&0===r[6]&&0===r[7]||(s=W(i,r)),!G(s[3]+s[7]/255,0,1,0))return void(s!==i&&L(s));let n=e;const a=this._$matrix;1===a[0]&&0===a[1]&&0===a[2]&&1===a[3]&&0===a[4]&&0===a[5]||(n=H(e,a)),super._$draw(t,n,s,this._$blendMode,this._$filters),n!==e&&N(n),s!==i&&L(s)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$recodes=null,super._$remove(),ce.push(this)}}class he extends gt{constructor(){super(),this._$background=!1,this._$backgroundColor=16777215,this._$border=!1,this._$borderColor=0,this._$wordWrap=!1,this._$textData=U(),this._$textAreaActive=!1,this._$thickness=0,this._$thicknessColor=0,this._$limitWidth=0,this._$limitHeight=0,this._$autoSize=\"none\",this._$widthTable=U(),this._$heightTable=U(),this._$objectTable=U(),this._$textHeightTable=U(),this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$maxScrollV=null,this._$scrollV=1,this._$textHeight=0,this._$verticalAlign=\"top\",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}get width(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.xMax-t.xMin);switch(B(t),!0){case 0===e:case e===s:case e===-1/0:return 0;default:return e}}get height(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.yMax-t.yMin);switch(B(t),e){case 0:case s:case-1/0:return 0;default:return e}}get maxScrollV(){if(null===this._$maxScrollV){this._$maxScrollV=1;const t=this._$textHeightTable.length,e=this.height;if(e>this._$textHeight)return this._$maxScrollV;let i=0,s=0;for(;t>s&&(i+=this._$textHeightTable[s++],!(i>e));)this._$maxScrollV++}return this._$maxScrollV}_$clip(t,e){const i=this._$getBounds(),s=i.xMax,n=i.xMin,a=i.yMax,h=i.yMin;B(i);const o=r.ceil(r.abs(s-n)),_=r.ceil(r.abs(a-h));if(!o||!_)return;let l=e;const c=this._$matrix;1===c[0]&&0===c[1]&&0===c[2]&&1===c[3]&&0===c[4]&&0===c[5]||(l=H(e,c)),t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(o,0),t.lineTo(o,_),t.lineTo(0,_),t.lineTo(0,0),t.clip(),l!==e&&N(l)}_$draw(t,e,i){if(!this._$visible||this._$textAreaActive)return;if(!this._$background&&!this._$border&&2>this._$textData.length)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1);if(!o)return;let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds(null);c.xMin-=this._$thickness,c.xMax+=this._$thickness,c.yMin-=this._$thickness,c.yMax+=this._$thickness;const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf(\"e\");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf(\"e\");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),this._$isUpdated()&&(rt.removeCache(this._$instanceId),t.cachePosition=null,this._$cacheKeys.length=0),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U(x,b);this._$cacheKeys=rt.generateKeys(this._$instanceId,t),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=r.min(1,r.max(x,b)),a=r.ceil(r.abs(c.xMax-c.xMin)*x),h=r.ceil(r.abs(c.yMax-c.yMin)*b);n[3]=1;const o=new OffscreenCanvas(a+2*s,h+2*s).getContext(\"2d\");if(!o)return;if(this._$background||this._$border){if(o.beginPath(),o.moveTo(0,0),o.lineTo(a,0),o.lineTo(a,h),o.lineTo(0,h),o.lineTo(0,0),this._$background){const t=Z(this._$backgroundColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.fillStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.fill()}if(this._$border){const t=Z(this._$borderColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.lineWidth=s,o.strokeStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.stroke()}}o.save(),o.beginPath(),o.moveTo(2,2),o.lineTo(a-2,2),o.lineTo(a-2,h-2),o.lineTo(2,h-2),o.lineTo(2,2),o.clip(),o.beginPath(),o.setTransform(x,0,0,b,0,0),this._$doDraw(o,e,i,a/x),o.restore();const _=M.createCachePosition(m,p),l=M.createTextureFromCanvas(o.canvas);t.drawTextureFromRect(l,_),t.cachePosition=_,rt.set(this._$cacheKeys,_)}let E=!1,C=0,S=0;if(v&&v.length&&this._$canApply(v)){E=!0;const e=this._$drawFilter(t,_,v,m,p);e.offsetX&&(C=e.offsetX),e.offsetY&&(S=e.offsetY),t.cachePosition=e}const w=r.atan2(_[1],_[0]),R=r.atan2(-_[2],_[3]);if(E||!w&&!R)t.setTransform(1,0,0,1,d-C,f-S);else{const e=c.xMin*x,i=c.yMin*b,s=r.cos(w),n=r.sin(w),a=r.cos(R),h=r.sin(R);t.setTransform(s,n,-h,a,e*s-i*h+_[4],e*n+i*a+_[5])}t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),B(c),_!==e&&N(_),n!==i&&L(n)}_$doDraw(t,e,i,s){const n=this.width,a=this.height;let h=0,o=0,_=0,l=0;if(\"top\"!==this._$verticalAlign&&this.height>this._$textHeight)switch(this._$verticalAlign){case\"middle\":l=(this.height-this._$textHeight+2)/2;break;case\"bottom\":l=this.height-this._$textHeight+2}const c=this._$textData.length;for(let $=0;$a||u>n))continue;const d=c.textFormat,g=Z(c.textFormat._$color),f=r.max(0,r.min(255*g.A*i[3]+i[7],255))/255;if(t.fillStyle=`rgba(${g.R},${g.G},${g.B},${f})`,this._$thickness){const e=Z(this._$thicknessColor),s=r.max(0,r.min(255*e.A*i[3]+i[7],255))/255;t.lineWidth=this._$thickness,t.strokeStyle=`rgba(${e.R},${e.G},${e.B},${s})`}const m=c.yIndex;switch(c.mode){case\"break\":case\"wrap\":if(_++,this._$scrollV>_)continue;if(o+=this._$textHeightTable[m],h=this._$getAlignOffset(this._$objectTable[m],s),d._$underline){const s=c.textFormat._$size/12,n=Z(d._$color),a=r.max(0,r.min(255*n.A*i[3]+i[7],255))/255;t.lineWidth=r.max(1,1/r.min(e[0],e[3])),t.strokeStyle=`rgba(${n.R},${n.G},${n.B},${a})`,t.beginPath(),t.moveTo(h,l+o-s),t.lineTo(h+this._$widthTable[m],l+o-s),t.stroke()}break;case\"text\":{if(this._$scrollV>_)continue;let e=o-this._$heightTable[0];_e||(e+=c.textFormat._$size/12*2),t.beginPath(),t.textBaseline=\"top\",t.font=tt(d._$font,d._$size,d._$italic,d._$bold),this._$thickness&&t.strokeText(c.text,u,l+e),t.fillText(c.text,u,l+e)}break;case\"image\":if(!c.loaded)continue;t.beginPath(),t.drawImage(c.image,c.hspace,l+c.y,c.width,c.height)}}}_$getAlignOffset(t,e){const i=this._$widthTable[t.yIndex],s=t.textFormat,n=s._$blockIndent+s._$leftMargin>0?s._$blockIndent+s._$leftMargin:0;switch(!0){case!this._$wordWrap&&i>e:return r.max(0,n);case\"center\"===s._$align:case\"center\"===this._$autoSize:return r.max(0,e/2-n-s._$rightMargin-i/2);case\"right\"===s._$align:case\"right\"===this._$autoSize:return r.max(0,e-n-i-s._$rightMargin-2);default:return r.max(0,n+2)}}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textAreaActive=!1,super._$remove(),$e.push(this)}_$updateProperty(t){this._$textAreaActive=!!t.textAreaActive,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textData.push(...t.textData),this._$widthTable.push(...t.widthTable),this._$heightTable.push(...t.heightTable),this._$objectTable.push(...t.objectTable),this._$textHeightTable.push(...t.textHeightTable),this._$wordWrap=t.wordWrap,this._$limitWidth=t.limitWidth,this._$limitHeight=t.limitHeight,this._$autoSize=t.autoSize,this._$scrollV=t.scrollV,this._$textHeight=t.textHeight,this._$verticalAlign=t.verticalAlign,this._$border=t.border,this._$border&&(this._$borderColor=t.borderColor),this._$background=t.background,this._$background&&(this._$backgroundColor=t.backgroundColor),\"thickness\"in t&&(this._$thickness=t.thickness,this._$thicknessColor=t.thicknessColor)}_$update(t){super._$update(t),this._$textAreaActive=!!t.textAreaActive,this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,t.textData&&this._$updateProperty(t)}}class oe extends gt{constructor(){super(),this._$imageBitmap=null,this._$context=null,this._$smoothing=!0,this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}_$clip(t,e){const i=this._$xMax,s=this._$yMax;if(!i||!s)return;let r=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(r=H(e,n)),t.reset(),t.setTransform(r[0],r[1],r[2],r[3],r[4],r[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(i,0),t.lineTo(i,s),t.lineTo(0,s),t.lineTo(0,0),t.clip(),r!==e&&N(r)}_$draw(t,e,i){if(!this._$visible||!this._$imageBitmap||!this._$context)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1,0);if(!o)return void(n!==i&&L(n));let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds();B(c);const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf(\"e\");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf(\"e\");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$instanceId,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const e=r.ceil(r.abs(this._$xMax-this._$xMin)),i=r.ceil(r.abs(this._$yMax-this._$yMin)),s=M.createCachePosition(e,i);t.cachePosition=s,rt.set(this._$cacheKeys,s)}this._$context.drawImage(this._$imageBitmap,0,0);const E=M.textureManager._$createFromElement(this._$imageBitmap.width,this._$imageBitmap.height,this._$context.canvas,this._$smoothing);let C=0,S=0;if(T&&v){const e=M.currentAttachment,i=M.createCacheAttachment(m,p);t._$bind(i),t.reset();const s=P(x,0,0,b,m/2,p/2),r=P(1,0,0,1,-E.width/2,-E.height/2),n=H(s,r);N(s),N(r),t.setTransform(n[0],n[1],n[2],n[3],n[4],n[5]),t.drawImage(E,0,0,E.width,E.height);const a=M.getTextureFromCurrentAttachment();t._$bind(e),M.releaseAttachment(i),t.drawTextureFromRect(E,t.cachePosition);const h=this._$drawFilter(t,_,v,m,p,a);h.offsetX&&(C=h.offsetX),h.offsetY&&(S=h.offsetY),t.cachePosition=h,t.setTransform(1,0,0,1,d-C,f-S)}else t.drawTextureFromRect(E,t.cachePosition),t.setTransform(_[0],_[1],_[2],_[3],_[4],_[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),_!==e&&N(_),n!==i&&L(n)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$context=null,this._$imageBitmap=null,this._$smoothing=!0,super._$remove(),de.push(this)}_$updateProperty(t){if(this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,this._$imageBitmap=t.imageBitmap,this._$smoothing=t.smoothing,!this._$context&&this._$imageBitmap){const t=new c(this._$imageBitmap.width,this._$imageBitmap.height);this._$context=t.getContext(\"2d\")}}_$update(t){super._$update(t),this._$updateProperty(t)}}let _e=!1;const le=new class{constructor(){this._$instances=new Map,this._$matrix=P(1,0,0,1,0,0),this._$width=0,this._$height=0,this._$stage=new ft,this._$canvas=null,this._$context=null,this._$attachment=null}get instances(){return this._$instances}get context(){return this._$context}get scaleX(){return this._$matrix[0]}stop(){rt.reset()}_$initialize(e,i){let s=0;var r,n;this._$setStage(e[s++]),n=1===e[s++],_e=n,r=e[s++],t=r,this._$canvas=i;const a=i.getContext(\"webgl2\",{stencil:!0,premultipliedAlpha:!0,antialias:!1,depth:!1,preserveDrawingBuffer:!0});if(a){const t=new re(a,e[s++]);this._$context=t,rt.context=t}}_$setBackgroundColor(t){if(!this._$context)return;const e=t[0];if(-1===e)this._$context._$setColor(0,0,0,0);else{const t={A:(i=e)>>>24,R:(16711680&i)>>16,G:(65280&i)>>8,B:255&i};this._$context._$setColor(t.R/255,t.G/255,t.B/255,1)}var i}_$bitmapDraw(t,e,i,s){const r=this._$context;if(!r)return;r._$bind(this._$attachment),r.reset(),r.setTransform(1,0,0,1,0,0),r.clearRect(0,0,this._$width,this._$height),r.beginPath(),t._$draw(r,e,i),r.frameBuffer.transferToMainTexture();const n=s.getContext(\"2d\");n&&this._$canvas&&n.drawImage(this._$canvas,0,0)}_$draw(){if(!this._$width||!this._$height)return;const t=this._$context;t&&(t.reset(),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,this._$width,this._$height),t.beginPath(),this._$stage._$draw(t,this._$matrix,m),this._$stage._$updated=!1,t.drawInstacedArray(),t.frameBuffer.transferToMainTexture())}_$resize(t){let e=0;const i=t[e++],s=t[e++];if(this._$width=i,this._$height=s,!this._$canvas)return;if(this._$canvas.width===i&&this._$canvas.height===s)return;const r=this._$context;if(!r)return;const n=t[e++];this._$matrix[0]=n,this._$matrix[3]=n,this._$matrix[4]=t[e++],this._$matrix[5]=t[e++],this._$stage._$updated=!0,rt.reset(),r.clearInstacedArray(),this._$canvas.width=i,this._$canvas.height=s,r._$gl.viewport(0,0,i,s);const a=r.frameBuffer;this._$attachment&&(a.unbind(),a.releaseAttachment(this._$attachment,!0)),this._$attachment=a.createCacheAttachment(i,s,!0),r.setMaxSize(i,s),r._$bind(this._$attachment)}_$setStage(t){this._$stage._$instanceId=t,this._$instances.set(t,this._$stage)}_$updateStage(){this._$stage._$updated=!0}_$createDisplayObjectContainer(t){const e=ge();let i=0;e._$instanceId=t[i++],e._$parentId=t[i++],this._$setProperty(e,t,2),this._$instances.set(e._$instanceId,e)}_$setProperty(t,e,i){t._$visible=1===e[i++],t._$depth=e[i++],t._$clipDepth=e[i++],t._$isMask=1===e[i++],1===e[i++]?(t._$maskId=e[i++],t._$maskMatrix||(t._$maskMatrix=P()),t._$maskMatrix[0]=e[i++],t._$maskMatrix[1]=e[i++],t._$maskMatrix[2]=e[i++],t._$maskMatrix[3]=e[i++],t._$maskMatrix[4]=e[i++],t._$maskMatrix[5]=e[i++]):(t._$maskId=-1,t._$maskMatrix&&(N(t._$maskMatrix),t._$maskMatrix=null),i+=7),t._$visible?(t._$matrix[0]=e[i++],t._$matrix[1]=e[i++],t._$matrix[2]=e[i++],t._$matrix[3]=e[i++],t._$matrix[4]=e[i++],t._$matrix[5]=e[i++],t._$colorTransform[0]=e[i++],t._$colorTransform[1]=e[i++],t._$colorTransform[2]=e[i++],t._$colorTransform[3]=e[i++],t._$colorTransform[4]=e[i++],t._$colorTransform[5]=e[i++],t._$colorTransform[6]=e[i++],t._$colorTransform[7]=e[i++]):(i+=6,i+=8),t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null,t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null}_$registerShapeRecodes(t,e){this._$instances.has(t)||this._$instances.set(t,pe()),this._$instances.get(t)._$recodes=e}_$createShape(t){let e=0;const i=t[e++];this._$instances.has(i)||this._$instances.set(i,pe());const s=this._$instances.get(i);s._$instanceId=i,s._$parentId=t[e++],s._$maxAlpha=t[e++],s._$canDraw=1===t[e++],s._$xMin=t[e++],s._$yMin=t[e++],s._$xMax=t[e++],s._$yMax=t[e++],s._$characterId=t[e++],s._$loaderInfoId=t[e++],this._$setProperty(s,t,10)}_$createVideo(t){const e=me();t.characterId&&(e._$characterId=t.characterId),\"loaderInfoId\"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}_$createTextField(t){const e=fe();e._$xMin=t.xMin||0,e._$yMin=t.yMin||0,e._$xMax=t.xMax||0,e._$yMax=t.yMax||0,t.characterId&&(e._$characterId=t.characterId),\"loaderInfoId\"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}},ce=[],$e=[],ue=[],de=[],ge=()=>ue.pop()||new ft,fe=()=>$e.pop()||new he,me=()=>de.pop()||new oe,pe=()=>ce.pop()||new ae;const xe=new class{constructor(){this.state=\"deactivate\",this.queue=[],this._$options=[]}execute(){this.state=\"active\";let t=!0;for(;this.queue.length;){const e=this.queue.shift();if(console.log(e),e){switch(t=!0,e.command){case\"draw\":le._$draw();break;case\"setProperty\":if(!le.instances.has(e.instanceId))continue;break;case\"setChildren\":{t=!1;const i=e.buffer,s=le.instances;if(!s.has(i[0]))continue;const r=s.get(i[0]);r._$doChanged(),r._$children=i.subarray(1)}break;case\"remove\":{const t=le.instances;if(!t.has(e.instanceId))continue;t.get(e.instanceId)._$remove(),t.delete(e.instanceId)}break;case\"createShape\":le._$createShape(e.buffer);break;case\"createDisplayObjectContainer\":le._$createDisplayObjectContainer(e.buffer);break;case\"createTextField\":le._$createTextField(e);break;case\"createVideo\":le._$createVideo(e);break;case\"resize\":le._$resize(e.buffer);break;case\"initialize\":le._$initialize(e.buffer,e.canvas);break;case\"setBackgroundColor\":le._$setBackgroundColor(e.buffer);break;case\"stop\":le.stop();break;case\"removeCache\":rt.removeCache(e.id);break;case\"bitmapDraw\":{const t=le.instances;if(!t.has(e.sourceId))continue;const i=t.get(e.sourceId),s=new c(e.width,e.height);le._$bitmapDraw(i,e.matrix||f,e.colorTransform||m,s);const r=s.transferToImageBitmap();globalThis.postMessage({command:\"bitmapDraw\",sourceId:e.sourceId,imageBitmap:r},[r])}break;default:if(e.command.indexOf(\"shapeRecodes\")>-1){t=!1;const i=+e.command.split(\"@\")[1];le._$registerShapeRecodes(i,e.buffer)}}e.buffer&&t&&(this._$options.length=0)}}this.state=\"deactivate\"}};self.addEventListener(\"message\",(t=>{return e=void 0,i=void 0,r=function*(){xe.queue.push(t.data),\"deactivate\"===xe.state&&xe.execute()},new((s=void 0)||(s=Promise))((function(t,n){function a(t){try{o(r.next(t))}catch(t){n(t)}}function h(t){try{o(r.throw(t))}catch(t){n(t)}}function o(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,h)}o((r=r.apply(e,i||[])).next())}));var e,i,s,r}))})();"; - -/** - * @method - * @public - */ -export const $initialize = (): Promise => -{ - if ("OffscreenCanvas" in window) { - - const offscreen: OffscreenCanvas = new OffscreenCanvas(0, 0); - const context: WebGL2RenderingContext | null = offscreen.getContext("webgl2"); - - /** - * @default null - * @type {Worker} - * @static - */ - $rendererWorker = context !== null - ? new Worker(URL.createObjectURL(new Blob([$renderURL], { "type": "text/javascript" }))) - : null; - - $rendererWorker = null; - if ($rendererWorker) { - - /** - * @param {DisplayObjectContainer} source - * @return {void} - * @method - * @private - */ - $postContainerWorker = (source: ParentImpl): void => - { - source._$createWorkerInstance(); - source._$postProperty(); - - const children: DisplayObjectImpl[] = source._$needsChildren - ? source._$getChildren() - : source._$children; - - const childrenIds: number[] = $getArray(); - - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - if (!instance) { - continue; - } - - childrenIds.push(instance._$instanceId); - - if ("_$children" in instance) { - // @ts-ignore - $postContainerWorker(instance); - continue; - } - - instance._$createWorkerInstance(); - instance._$postProperty(); - } - - source._$postChildrenIds(childrenIds); - - $poolArray(childrenIds); - }; - - /** - * @param {DisplayObjectContainer} source - * @return {void} - * @method - * @static - */ - $removeContainerWorker = (source: ParentImpl): void => - { - source._$removeWorkerInstance(); - - const children: DisplayObjectImpl[] = source._$needsChildren - ? source._$getChildren() - : source._$children; - - for (let idx: number = 0; idx < children.length; ++idx) { - - const instance: DisplayObjectImpl = children[idx]; - if (!instance) { - continue; - } - - if ("_$children" in instance) { - - // @ts-ignore - $removeContainerWorker(instance); - - } else { - instance._$removeWorkerInstance(); - } - } - }; - - // $rendererWorker.onmessage = (event: MessageEvent) => - // { - - // switch (event.data.command) { - - // case "renderBuffer": - // $poolRenderBufferArray(event.data.buffer); - // break; - - // case "bitmapDraw": - // { - // const sourceId: number = event.data.sourceId; - // const object: BitmapDrawObjectImpl | void = $bitmapDrawMap - // .get(sourceId); - - // $bitmapDrawMap.delete(sourceId); - // if (!object) { - // return ; - // } - - // // reset - // const source: DisplayObjectImpl = object.source; - // if ("_$children" in source) { - // // @ts-ignore - // $removeContainerWorker(source); - // } else { - // source._$removeWorkerInstance(); - // } - - // if (object.callback) { - // const context = object.context; - // context.drawImage(event.data.imageBitmap, 0, 0); - // object.callback(context.canvas); - // } - // } - // break; - - // default: - // break; - - // } - // }; - } - } - - return new Promise((resolve): void => - { - // @ts-ignore - const userAgentData: any = navigator.userAgentData; - if (userAgentData) { - - userAgentData - .getHighEntropyValues(["platform", "mobile"]) - .then((object: any) => - { - const brands: any = object.brands; - for (let idx: number = 0; idx < brands.length; ++idx) { - if (brands[idx].brand.indexOf("Chrome") === -1) { - continue; - } - - $isChrome = true; - break; - } - - $isAndroid = object.platform === "Android"; - $isiOS = object.platform === "iOS"; - $isTouch = $isAndroid || $isiOS; - - resolve(); - }); - - } else { - - const userAgent = navigator.userAgent; - - $isAndroid = userAgent.indexOf("Android") > -1; - - $isiOS = userAgent.indexOf("iPhone") > -1 - || userAgent.indexOf("iPod") > -1; - - $isChrome = userAgent.indexOf("Chrome") > -1; - - $isFireFox = userAgent.indexOf("Firefox") > -1; - - $isSafari = userAgent.indexOf("Chrome") === -1 - && userAgent.indexOf("Safari") > -1; - - $isTouch = $isAndroid || $isiOS; - - resolve(); - } - }); -}; \ No newline at end of file diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts deleted file mode 100644 index 19bf7ad4..00000000 --- a/packages/util/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./Global"; -export * from "./Shortcut"; -export * from "./Util"; \ No newline at end of file diff --git a/packages/webgl/package.json b/packages/webgl/package.json index e43c2ed7..842e138e 100644 --- a/packages/webgl/package.json +++ b/packages/webgl/package.json @@ -1,26 +1,18 @@ { "name": "@next2d/webgl", "version": "*", - "description": "Next2D Webgl Packages", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", + "description": "Next2D Webgl Package", + "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", "license": "MIT", "homepage": "https://next2d.app", "bugs": "https://github.com/Next2D/Player/issues", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], + "main": "src/index.js", + "types": "src/index.d.ts", + "type": "module", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } + "import": "./src/index.js", + "require": "./src/index.js" } }, "keywords": [ @@ -32,6 +24,7 @@ "url": "git+https://github.com/Next2D/Player.git" }, "peerDependencies": { - "@next2d/share": "file:../share" + "@next2d/texture-packer": "file:../texture-packer", + "@next2d/render-queue": "file:../render-queue" } } diff --git a/packages/webgl/src/AtlasManager.ts b/packages/webgl/src/AtlasManager.ts new file mode 100644 index 00000000..c2ec3e80 --- /dev/null +++ b/packages/webgl/src/AtlasManager.ts @@ -0,0 +1,267 @@ +import type { IAttachmentObject } from "./interface/IAttachmentObject"; +import type { TexturePacker } from "@next2d/texture-packer"; +import type { ITextureObject } from "./interface/ITextureObject"; +import { execute as textureManagerCreateAtlasTextureUseCase } from "./TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "./FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { $RENDER_MAX_SIZE } from "./WebGLUtil"; + +/** + * @description アクティブなアトラスインデックス + * Active atlas index + * + * @type {number} + * @private + */ +let $activeAtlasIndex: number = 0; + +/** + * @description アクティブなアトラスインデックスをセット + * Set the active atlas index + * + * @param {number} index + * @return {void} + * @method + * @protected + */ +export const $setActiveAtlasIndex = (index: number): void => +{ + $activeAtlasIndex = index; +}; + +/** + * @description アクティブなアトラスインデックスを返却 + * Return the active atlas index + * + * @returns {number} + * @method + * @protected + */ +export const $getActiveAtlasIndex = (): number => +{ + return $activeAtlasIndex; +}; + +/** + * @description アトラステクスチャのアタッチメントオブジェクト + * Attachment object of atlas texture + * + * @type {IAttachmentObject[]} + * @private + */ +const $atlasAttachmentObjects: IAttachmentObject[] = []; + +/** + * @description アトラス専用のフレームバッファ配列 + * Array of frame buffers dedicated to the atlas + * + * @return {IAttachmentObject[]} + * @method + * @protected + */ +export const $getAtlasAttachmentObjects = (): IAttachmentObject[] => +{ + return $atlasAttachmentObjects; +}; + +/** + * @description アトラステクスチャオブジェクトをセット + * Set the atlas texture object + * + * @param {IAttachmentObject} attachment_object + * @return {void} + * @method + * @protected + */ +export const $setAtlasAttachmentObject = (attachment_object: IAttachmentObject): void => +{ + $atlasAttachmentObjects[$activeAtlasIndex] = attachment_object; +}; + +/** + * @description アトラステクスチャオブジェクトを返却 + * Return the atlas texture object + * + * @returns {IAttachmentObject} + * @method + * @protected + */ +export const $getAtlasAttachmentObject = (): IAttachmentObject => +{ + if (!($activeAtlasIndex in $atlasAttachmentObjects)) { + $setAtlasAttachmentObject( + frameBufferManagerGetAttachmentObjectUseCase($RENDER_MAX_SIZE, $RENDER_MAX_SIZE, true) + ); + } + return $atlasAttachmentObjects[$activeAtlasIndex]; +}; + +/** + * @description アトラステクスチャオブジェクトが存在するか + * Does the atlas texture object exist? + * + * @return {boolean} + * @method + * @protected + */ +export const $hasAtlasAttachmentObject = (): boolean => +{ + return $activeAtlasIndex in $atlasAttachmentObjects; +}; + +/** + * @description ルートノードの配列 + * Array of root nodes + * + * @type {TexturePacker[]} + * @protected + */ +export const $rootNodes: TexturePacker[] = []; + +/** + * @description アトラス専用のテクスチャ + * Texture for atlas only + * + * @type {ITextureObject | null} + * @private + */ +export let $atlasTexture: ITextureObject | null = null; + +/** + * @description アトラステクスチャオブジェクトを返却 + * Return the atlas texture object + * + * @return {ITextureObject} + * @method + * @protected + */ +export const $getAtlasTextureObject = (): ITextureObject => +{ + if (!$atlasTexture) { + $atlasTexture = textureManagerCreateAtlasTextureUseCase(); + } + return $atlasTexture as ITextureObject; +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $transferBounds: Float32Array[] = []; + +/** + * @description アトラステクスチャの転送範囲を返却 + * Return the transfer range of the atlas texture + * + * @param {number} index + * @return {Float32Array} + * @method + * @protected + */ +export const $getActiveTransferBounds = (index: number): Float32Array => +{ + if (!(index in $transferBounds)) { + $transferBounds[index] = new Float32Array([ + Number.MAX_VALUE, + Number.MAX_VALUE, + -Number.MAX_VALUE, + -Number.MAX_VALUE + ]); + } + return $transferBounds[index]; +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $allTransferBounds: Float32Array[] = []; + +/** + * @description アトラステクスチャの切り替え時の転送範囲を返却 + * Return the transfer range when switching the atlas texture + * + * @param {number} index + * @return {Float32Array} + * @method + * @protected + */ +export const $getActiveAllTransferBounds = (index: number): Float32Array => +{ + if (!(index in $allTransferBounds)) { + $allTransferBounds[index] = new Float32Array([ + Number.MAX_VALUE, + Number.MAX_VALUE, + -Number.MAX_VALUE, + -Number.MAX_VALUE + ]); + } + return $allTransferBounds[index]; +}; + +/** + * @description アトラステクスチャの転送範囲をクリア + * Clear the transfer range of the atlas texture + * + * @return {void} + * @method + * @protected + */ +export const $clearTransferBounds = (): void => +{ + for (let idx = 0; idx < $transferBounds.length; ++idx) { + const bounds = $transferBounds[idx]; + if (!bounds) { + continue; + } + + bounds[0] = bounds[1] = Number.MAX_VALUE; + bounds[2] = bounds[3] = -Number.MAX_VALUE; + } + + for (let idx = 0; idx < $allTransferBounds.length; ++idx) { + const bounds = $allTransferBounds[idx]; + if (!bounds) { + continue; + } + + bounds[0] = bounds[1] = Number.MAX_VALUE; + bounds[2] = bounds[3] = -Number.MAX_VALUE; + } +}; + +/** + * @description 現在設定されているアトラスアタッチメントオブジェクトのインデックス値 + * Index value of the currently set atlas attachment object + * + * @type {number} + * @default 0 + * @private + */ +let $currentAtlasIndex: number = 0; + +/** + * @description 現在設定されているアトラスアタッチメントオブジェクトのインデックス値をセット + * Set the index value of the currently set atlas attachment object + * + * @param {number} index + * @return {void} + * @method + * @protected + */ +export const $setCurrentAtlasIndex = (index: number): void => +{ + $currentAtlasIndex = index; +}; + +/** + * @description 現在設定されているアトラスアタッチメントオブジェクトのインデックス値を返却 + * Returns the index value of the currently set atlas attachment object + * + * @return {number} + * @method + * @protected + */ +export const $getCurrentAtlasIndex = (): number => +{ + return $currentAtlasIndex; +}; \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.test.ts b/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.test.ts new file mode 100644 index 00000000..2eea9d72 --- /dev/null +++ b/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./AtlasManagerCreateNodeService"; +import { $setActiveAtlasIndex } from "../../AtlasManager"; +import { describe, expect, it } from "vitest"; + +describe("AtlasManagerCreateNodeService.js method test", () => +{ + it("test case", () => + { + const node1 = execute(100, 200); + expect(node1.index).toBe(0); + expect(node1.x).toBe(0); + expect(node1.y).toBe(0); + expect(node1.w).toBe(100); + expect(node1.h).toBe(200); + + $setActiveAtlasIndex(1); + const node2 = execute(100, 200); + expect(node2.index).toBe(1); + expect(node1.x).toBe(0); + expect(node1.y).toBe(0); + expect(node1.w).toBe(100); + expect(node1.h).toBe(200); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.ts b/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.ts new file mode 100644 index 00000000..b810be4f --- /dev/null +++ b/packages/webgl/src/AtlasManager/service/AtlasManagerCreateNodeService.ts @@ -0,0 +1,36 @@ +import type { Node } from "@next2d/texture-packer"; +import { $rootNodes } from "../../AtlasManager"; +import { $RENDER_MAX_SIZE } from "../../WebGLUtil"; +import { TexturePacker } from "@next2d/texture-packer"; +import { + $getActiveAtlasIndex, + $setActiveAtlasIndex +} from "../../AtlasManager"; + +/** + * @description 指定サイズのキャッシュ座標を生成、二分木構造を利用して座標を取得します。 + * Generate cache coordinates of the specified size and get the coordinates using a binary tree structure. + * + * @param {number} width + * @param {number} height + * @return {Node} + * @method + * @protected + */ +export const execute = (width: number, height: number): Node => +{ + const index = $getActiveAtlasIndex(); + if (!$rootNodes[index]) { + $rootNodes[index] = new TexturePacker(index, $RENDER_MAX_SIZE, $RENDER_MAX_SIZE); + } + + const rootNode = $rootNodes[index] as NonNullable; + const node = rootNode.insert(width, height); + + if (!node) { + $setActiveAtlasIndex(index + 1); + return execute(width, height); + } + + return node; +}; \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.test.ts b/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.test.ts new file mode 100644 index 00000000..8f48d478 --- /dev/null +++ b/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.test.ts @@ -0,0 +1,17 @@ +import { execute } from "./AtlasManagerRemoveNodeService"; +import { execute as atlasManagerCreateNodeService } from "./AtlasManagerCreateNodeService"; +import { $rootNodes } from "../../AtlasManager"; +import { describe, expect, it } from "vitest"; + +describe("AtlasManagerRemoveNodeService.js method test", () => +{ + it("test case", () => + { + const node = atlasManagerCreateNodeService(100, 200); + expect(node.left === null).toBe(true); + expect(node.right === null).toBe(true); + execute(node); + expect(node.left).toBe(null); + expect(node.right).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.ts b/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.ts new file mode 100644 index 00000000..df1b2acd --- /dev/null +++ b/packages/webgl/src/AtlasManager/service/AtlasManagerRemoveNodeService.ts @@ -0,0 +1,20 @@ +import type { Node } from "@next2d/texture-packer"; +import { $rootNodes } from "../../AtlasManager"; + +/** + * @description アトラスの座標情報ノードを削除 + * Remove the coordinate information node of the atlas + * + * @param {Node} node + * @return {void} + * @method + * @protected + */ +export const execute = (node: Node): void => +{ + const rootNode = $rootNodes[node.index]; + if (!rootNode) { + return ; + } + rootNode.dispose(node.x, node.y, node.w, node.h); +}; \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.test.ts b/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.test.ts new file mode 100644 index 00000000..7d35b7e5 --- /dev/null +++ b/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.test.ts @@ -0,0 +1,23 @@ +import { execute } from "./AtlasManagerResetUseCase"; +import { execute as atlasManagerCreateNodeService } from "../service/AtlasManagerCreateNodeService"; +import { $rootNodes, $getActiveAtlasIndex, $setActiveAtlasIndex } from "../../AtlasManager"; +import { describe, expect, it } from "vitest"; + +describe("AtlasManagerResetUseCase.js method test", () => +{ + it("test case", () => + { + $rootNodes.length = 0; + atlasManagerCreateNodeService(100, 200); + + expect($rootNodes.length).toBe(1); + expect($getActiveAtlasIndex()).toBe(0); + + $setActiveAtlasIndex(1); + expect($getActiveAtlasIndex()).toBe(1); + + execute(); + expect($getActiveAtlasIndex()).toBe(0); + expect($rootNodes.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.ts b/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.ts new file mode 100644 index 00000000..fbc1dadd --- /dev/null +++ b/packages/webgl/src/AtlasManager/usecase/AtlasManagerResetUseCase.ts @@ -0,0 +1,28 @@ +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { + $rootNodes, + $setActiveAtlasIndex, + $getAtlasAttachmentObjects +} from "../../AtlasManager"; + +/** + * @description アトラス専用の座標マップ、フレームバッファをリセット + * Reset the coordinate map and frame buffer dedicated to the atlas + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $rootNodes.length = 0; + $setActiveAtlasIndex(0); + + const atlasAttachmentObjects = $getAtlasAttachmentObjects(); + for (let idx = 0; idx < atlasAttachmentObjects.length; idx++) { + frameBufferManagerReleaseAttachmentObjectUseCase( + atlasAttachmentObjects[idx] + ); + } + atlasAttachmentObjects.length = 0; +}; \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter.ts b/packages/webgl/src/BezierConverter.ts index d3d5f38d..9f800409 100644 --- a/packages/webgl/src/BezierConverter.ts +++ b/packages/webgl/src/BezierConverter.ts @@ -1,163 +1,8 @@ -import { $Float32Array } from "@next2d/share"; - /** - * @class + * @description ベジェ曲線の変換後の値を格納するバッファ + * Buffer to store the converted values of the Bezier curve + * + * @type {Float32Array} + * @public */ -export class BezierConverter -{ - public readonly _$bezierConverterBuffer: Float32Array; - - /** - * @constructor - * @public - */ - constructor () - { - this._$bezierConverterBuffer = new $Float32Array(32); - } - - /** - * @param {number} from_x - * @param {number} from_y - * @param {number} cx1 - * @param {number} cy1 - * @param {number} cx2 - * @param {number} cy2 - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - cubicToQuad ( - from_x: number, from_y: number, - cx1: number, cy1: number, - cx2: number, cy2: number, - x: number, y: number - ): void { - - this._$split2Cubic(from_x, from_y, cx1, cy1, cx2, cy2, x, y, 0, 16); - this._$split2Cubic( - this._$bezierConverterBuffer[0], this._$bezierConverterBuffer[1], this._$bezierConverterBuffer[2], this._$bezierConverterBuffer[3], - this._$bezierConverterBuffer[4], this._$bezierConverterBuffer[5], this._$bezierConverterBuffer[6], this._$bezierConverterBuffer[7], - 0, 8 - ); - this._$split2Cubic( - this._$bezierConverterBuffer[16], this._$bezierConverterBuffer[17], this._$bezierConverterBuffer[18], this._$bezierConverterBuffer[19], - this._$bezierConverterBuffer[20], this._$bezierConverterBuffer[21], this._$bezierConverterBuffer[22], this._$bezierConverterBuffer[23], - 16, 24 - ); - this._$split2Quad( - this._$bezierConverterBuffer[0], this._$bezierConverterBuffer[1], this._$bezierConverterBuffer[2], this._$bezierConverterBuffer[3], - this._$bezierConverterBuffer[4], this._$bezierConverterBuffer[5], this._$bezierConverterBuffer[6], this._$bezierConverterBuffer[7], - 0 - ); - this._$split2Quad( - this._$bezierConverterBuffer[8], this._$bezierConverterBuffer[9], this._$bezierConverterBuffer[10], this._$bezierConverterBuffer[11], - this._$bezierConverterBuffer[12], this._$bezierConverterBuffer[13], this._$bezierConverterBuffer[14], this._$bezierConverterBuffer[15], - 8 - ); - this._$split2Quad( - this._$bezierConverterBuffer[16], this._$bezierConverterBuffer[17], this._$bezierConverterBuffer[18], this._$bezierConverterBuffer[19], - this._$bezierConverterBuffer[20], this._$bezierConverterBuffer[21], this._$bezierConverterBuffer[22], this._$bezierConverterBuffer[23], - 16 - ); - this._$split2Quad( - this._$bezierConverterBuffer[24], this._$bezierConverterBuffer[25], this._$bezierConverterBuffer[26], this._$bezierConverterBuffer[27], - this._$bezierConverterBuffer[28], this._$bezierConverterBuffer[29], this._$bezierConverterBuffer[30], this._$bezierConverterBuffer[31], - 24 - ); - } - - /** - * @description 3次ベジェを、2つの3次ベジェに分割する - * @param {number} p0 - * @param {number} p1 - * @param {number} p2 - * @param {number} p3 - * @param {number} p4 - * @param {number} p5 - * @param {number} p6 - * @param {number} p7 - * @param {number} offset1 - * @param {number} offset2 - * @return void - * @method - * @private - */ - _$split2Cubic ( - p0: number, p1: number, - p2: number, p3: number, - p4: number, p5: number, - p6: number, p7: number, - offset1: number, offset2: number - ): void { - - const mx: number = (p0 + 3 * (p2 + p4) + p6) * 0.125; - const my: number = (p1 + 3 * (p3 + p5) + p7) * 0.125; - const dx: number = (p6 + p4 - p2 - p0) * 0.125; - const dy: number = (p7 + p5 - p3 - p1) * 0.125; - - this._$bezierConverterBuffer[offset1 ] = p0; - this._$bezierConverterBuffer[offset1 + 1] = p1; - this._$bezierConverterBuffer[offset1 + 2] = (p0 + p2) * 0.5; - this._$bezierConverterBuffer[offset1 + 3] = (p1 + p3) * 0.5; - this._$bezierConverterBuffer[offset1 + 4] = mx - dx; - this._$bezierConverterBuffer[offset1 + 5] = my - dy; - this._$bezierConverterBuffer[offset1 + 6] = mx; - this._$bezierConverterBuffer[offset1 + 7] = my; - - this._$bezierConverterBuffer[offset2 ] = mx; - this._$bezierConverterBuffer[offset2 + 1] = my; - this._$bezierConverterBuffer[offset2 + 2] = mx + dx; - this._$bezierConverterBuffer[offset2 + 3] = my + dy; - this._$bezierConverterBuffer[offset2 + 4] = (p4 + p6) * 0.5; - this._$bezierConverterBuffer[offset2 + 5] = (p5 + p7) * 0.5; - this._$bezierConverterBuffer[offset2 + 6] = p6; - this._$bezierConverterBuffer[offset2 + 7] = p7; - } - - /** - * @description 3次ベジェを、2つの2次ベジェに変換する - * - * @param {number} p0 - * @param {number} p1 - * @param {number} p2 - * @param {number} p3 - * @param {number} p4 - * @param {number} p5 - * @param {number} p6 - * @param {number} p7 - * @param {number} offset - * @return {void} - * @method - * @private - */ - _$split2Quad ( - p0: number, p1: number, - p2: number, p3: number, - p4: number, p5: number, - p6: number, p7: number, - offset: number - ): void { - - const mx: number = (p0 + 3 * (p2 + p4) + p6) * 0.125; - const my: number = (p1 + 3 * (p3 + p5) + p7) * 0.125; - - // 2次ベジェの始点の値は不要なので含めない - - // this.result[offset - 2] = p0; - // this.result[offset - 1] = p1; - this._$bezierConverterBuffer[offset ] = p0 * 0.25 + p2 * 0.75; - this._$bezierConverterBuffer[offset + 1] = p1 * 0.25 + p3 * 0.75; - this._$bezierConverterBuffer[offset + 2] = mx; - this._$bezierConverterBuffer[offset + 3] = my; - - // this.result[offset + 2] = mx; - // this.result[offset + 3] = my; - this._$bezierConverterBuffer[offset + 4] = p4 * 0.75 + p6 * 0.25; - this._$bezierConverterBuffer[offset + 5] = p5 * 0.75 + p7 * 0.25; - this._$bezierConverterBuffer[offset + 6] = p6; - this._$bezierConverterBuffer[offset + 7] = p7; - } -} +export const $bezierBuffer: Float32Array = new Float32Array(32); \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.test.ts b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.test.ts new file mode 100644 index 00000000..ce6c7fee --- /dev/null +++ b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.test.ts @@ -0,0 +1,141 @@ +import { execute } from "./BezierConverterSplit2CubicService"; +import { describe, expect, it } from "vitest"; +import { $bezierBuffer } from "../../BezierConverter"; + +describe("BezierConverterSplit2CubicService.js method test", () => +{ + it("test case offset 0 8", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 0, 8 + ); + + expect($bezierBuffer[0]).toBe(10); + expect($bezierBuffer[1]).toBe(10); + expect($bezierBuffer[2]).toBe(15); + expect($bezierBuffer[3]).toBe(15); + expect($bezierBuffer[4]).toBe(20); + expect($bezierBuffer[5]).toBe(20); + expect($bezierBuffer[6]).toBe(25); + expect($bezierBuffer[7]).toBe(25); + expect($bezierBuffer[8]).toBe(25); + expect($bezierBuffer[9]).toBe(25); + expect($bezierBuffer[10]).toBe(30); + expect($bezierBuffer[11]).toBe(30); + expect($bezierBuffer[12]).toBe(35); + expect($bezierBuffer[13]).toBe(35); + expect($bezierBuffer[14]).toBe(40); + expect($bezierBuffer[15]).toBe(40); + expect($bezierBuffer[16]).toBe(0); + expect($bezierBuffer[17]).toBe(0); + expect($bezierBuffer[18]).toBe(0); + expect($bezierBuffer[19]).toBe(0); + expect($bezierBuffer[20]).toBe(0); + expect($bezierBuffer[21]).toBe(0); + expect($bezierBuffer[22]).toBe(0); + expect($bezierBuffer[23]).toBe(0); + expect($bezierBuffer[24]).toBe(0); + expect($bezierBuffer[25]).toBe(0); + expect($bezierBuffer[26]).toBe(0); + expect($bezierBuffer[27]).toBe(0); + expect($bezierBuffer[28]).toBe(0); + expect($bezierBuffer[29]).toBe(0); + expect($bezierBuffer[30]).toBe(0); + expect($bezierBuffer[31]).toBe(0); + }); + + it("test case offset 0 16", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 0, 16 + ); + + expect($bezierBuffer[0]).toBe(10); + expect($bezierBuffer[1]).toBe(10); + expect($bezierBuffer[2]).toBe(15); + expect($bezierBuffer[3]).toBe(15); + expect($bezierBuffer[4]).toBe(20); + expect($bezierBuffer[5]).toBe(20); + expect($bezierBuffer[6]).toBe(25); + expect($bezierBuffer[7]).toBe(25); + expect($bezierBuffer[8]).toBe(0); + expect($bezierBuffer[9]).toBe(0); + expect($bezierBuffer[10]).toBe(0); + expect($bezierBuffer[11]).toBe(0); + expect($bezierBuffer[12]).toBe(0); + expect($bezierBuffer[13]).toBe(0); + expect($bezierBuffer[14]).toBe(0); + expect($bezierBuffer[15]).toBe(0); + expect($bezierBuffer[16]).toBe(25); + expect($bezierBuffer[17]).toBe(25); + expect($bezierBuffer[18]).toBe(30); + expect($bezierBuffer[19]).toBe(30); + expect($bezierBuffer[20]).toBe(35); + expect($bezierBuffer[21]).toBe(35); + expect($bezierBuffer[22]).toBe(40); + expect($bezierBuffer[23]).toBe(40); + expect($bezierBuffer[24]).toBe(0); + expect($bezierBuffer[25]).toBe(0); + expect($bezierBuffer[26]).toBe(0); + expect($bezierBuffer[27]).toBe(0); + expect($bezierBuffer[28]).toBe(0); + expect($bezierBuffer[29]).toBe(0); + expect($bezierBuffer[30]).toBe(0); + expect($bezierBuffer[31]).toBe(0); + }); + + it("test case offset 16 24", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 16, 24 + ); + + expect($bezierBuffer[0]).toBe(0); + expect($bezierBuffer[1]).toBe(0); + expect($bezierBuffer[2]).toBe(0); + expect($bezierBuffer[3]).toBe(0); + expect($bezierBuffer[4]).toBe(0); + expect($bezierBuffer[5]).toBe(0); + expect($bezierBuffer[6]).toBe(0); + expect($bezierBuffer[7]).toBe(0); + expect($bezierBuffer[8]).toBe(0); + expect($bezierBuffer[9]).toBe(0); + expect($bezierBuffer[10]).toBe(0); + expect($bezierBuffer[11]).toBe(0); + expect($bezierBuffer[12]).toBe(0); + expect($bezierBuffer[13]).toBe(0); + expect($bezierBuffer[14]).toBe(0); + expect($bezierBuffer[15]).toBe(0); + expect($bezierBuffer[16]).toBe(10); + expect($bezierBuffer[17]).toBe(10); + expect($bezierBuffer[18]).toBe(15); + expect($bezierBuffer[19]).toBe(15); + expect($bezierBuffer[20]).toBe(20); + expect($bezierBuffer[21]).toBe(20); + expect($bezierBuffer[22]).toBe(25); + expect($bezierBuffer[23]).toBe(25); + expect($bezierBuffer[24]).toBe(25); + expect($bezierBuffer[25]).toBe(25); + expect($bezierBuffer[26]).toBe(30); + expect($bezierBuffer[27]).toBe(30); + expect($bezierBuffer[28]).toBe(35); + expect($bezierBuffer[29]).toBe(35); + expect($bezierBuffer[30]).toBe(40); + expect($bezierBuffer[31]).toBe(40); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.ts b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.ts new file mode 100644 index 00000000..329f1f08 --- /dev/null +++ b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2CubicService.ts @@ -0,0 +1,51 @@ +import { $bezierBuffer } from "../../BezierConverter"; + +/** + * @description 3次ベジェに分割 + * Split cubic Bezier + * + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} p4 + * @param {number} p5 + * @param {number} p6 + * @param {number} p7 + * @param {number} offset1 + * @param {number} offset2 + * @return {void} + * @method + * @protected + */ +export const execute = ( + p0: number, p1: number, + p2: number, p3: number, + p4: number, p5: number, + p6: number, p7: number, + offset1: number, offset2: number +): void => { + + const mx: number = (p0 + 3 * (p2 + p4) + p6) * 0.125; + const my: number = (p1 + 3 * (p3 + p5) + p7) * 0.125; + const dx: number = (p6 + p4 - p2 - p0) * 0.125; + const dy: number = (p7 + p5 - p3 - p1) * 0.125; + + $bezierBuffer[offset1 ] = p0; + $bezierBuffer[offset1 + 1] = p1; + $bezierBuffer[offset1 + 2] = (p0 + p2) * 0.5; + $bezierBuffer[offset1 + 3] = (p1 + p3) * 0.5; + $bezierBuffer[offset1 + 4] = mx - dx; + $bezierBuffer[offset1 + 5] = my - dy; + $bezierBuffer[offset1 + 6] = mx; + $bezierBuffer[offset1 + 7] = my; + + $bezierBuffer[offset2 ] = mx; + $bezierBuffer[offset2 + 1] = my; + $bezierBuffer[offset2 + 2] = mx + dx; + $bezierBuffer[offset2 + 3] = my + dy; + $bezierBuffer[offset2 + 4] = (p4 + p6) * 0.5; + $bezierBuffer[offset2 + 5] = (p5 + p7) * 0.5; + $bezierBuffer[offset2 + 6] = p6; + $bezierBuffer[offset2 + 7] = p7; +}; \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.test.ts b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.test.ts new file mode 100644 index 00000000..7f88539d --- /dev/null +++ b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.test.ts @@ -0,0 +1,186 @@ +import { execute } from "./BezierConverterSplit2QuadService"; +import { describe, expect, it } from "vitest"; +import { $bezierBuffer } from "../../BezierConverter"; + +describe("BezierConverterSplit2QuadService.js method test", () => +{ + it("test case offset 0", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 0 + ); + + expect($bezierBuffer[0]).toBe(17.5); + expect($bezierBuffer[1]).toBe(17.5); + expect($bezierBuffer[2]).toBe(25); + expect($bezierBuffer[3]).toBe(25); + expect($bezierBuffer[4]).toBe(32.5); + expect($bezierBuffer[5]).toBe(32.5); + expect($bezierBuffer[6]).toBe(40); + expect($bezierBuffer[7]).toBe(40); + expect($bezierBuffer[8]).toBe(0); + expect($bezierBuffer[9]).toBe(0); + expect($bezierBuffer[10]).toBe(0); + expect($bezierBuffer[11]).toBe(0); + expect($bezierBuffer[12]).toBe(0); + expect($bezierBuffer[13]).toBe(0); + expect($bezierBuffer[14]).toBe(0); + expect($bezierBuffer[15]).toBe(0); + expect($bezierBuffer[16]).toBe(0); + expect($bezierBuffer[17]).toBe(0); + expect($bezierBuffer[18]).toBe(0); + expect($bezierBuffer[19]).toBe(0); + expect($bezierBuffer[20]).toBe(0); + expect($bezierBuffer[21]).toBe(0); + expect($bezierBuffer[22]).toBe(0); + expect($bezierBuffer[23]).toBe(0); + expect($bezierBuffer[24]).toBe(0); + expect($bezierBuffer[25]).toBe(0); + expect($bezierBuffer[26]).toBe(0); + expect($bezierBuffer[27]).toBe(0); + expect($bezierBuffer[28]).toBe(0); + expect($bezierBuffer[29]).toBe(0); + expect($bezierBuffer[30]).toBe(0); + expect($bezierBuffer[31]).toBe(0); + }); + + it("test case offset 8", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 8 + ); + + expect($bezierBuffer[0]).toBe(0); + expect($bezierBuffer[1]).toBe(0); + expect($bezierBuffer[2]).toBe(0); + expect($bezierBuffer[3]).toBe(0); + expect($bezierBuffer[4]).toBe(0); + expect($bezierBuffer[5]).toBe(0); + expect($bezierBuffer[6]).toBe(0); + expect($bezierBuffer[7]).toBe(0); + expect($bezierBuffer[8]).toBe(17.5); + expect($bezierBuffer[9]).toBe(17.5); + expect($bezierBuffer[10]).toBe(25); + expect($bezierBuffer[11]).toBe(25); + expect($bezierBuffer[12]).toBe(32.5); + expect($bezierBuffer[13]).toBe(32.5); + expect($bezierBuffer[14]).toBe(40); + expect($bezierBuffer[15]).toBe(40); + expect($bezierBuffer[16]).toBe(0); + expect($bezierBuffer[17]).toBe(0); + expect($bezierBuffer[18]).toBe(0); + expect($bezierBuffer[19]).toBe(0); + expect($bezierBuffer[20]).toBe(0); + expect($bezierBuffer[21]).toBe(0); + expect($bezierBuffer[22]).toBe(0); + expect($bezierBuffer[23]).toBe(0); + expect($bezierBuffer[24]).toBe(0); + expect($bezierBuffer[25]).toBe(0); + expect($bezierBuffer[26]).toBe(0); + expect($bezierBuffer[27]).toBe(0); + expect($bezierBuffer[28]).toBe(0); + expect($bezierBuffer[29]).toBe(0); + expect($bezierBuffer[30]).toBe(0); + expect($bezierBuffer[31]).toBe(0); + }); + + it("test case offset 16", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 16 + ); + + expect($bezierBuffer[0]).toBe(0); + expect($bezierBuffer[1]).toBe(0); + expect($bezierBuffer[2]).toBe(0); + expect($bezierBuffer[3]).toBe(0); + expect($bezierBuffer[4]).toBe(0); + expect($bezierBuffer[5]).toBe(0); + expect($bezierBuffer[6]).toBe(0); + expect($bezierBuffer[7]).toBe(0); + expect($bezierBuffer[8]).toBe(0); + expect($bezierBuffer[9]).toBe(0); + expect($bezierBuffer[10]).toBe(0); + expect($bezierBuffer[11]).toBe(0); + expect($bezierBuffer[12]).toBe(0); + expect($bezierBuffer[13]).toBe(0); + expect($bezierBuffer[14]).toBe(0); + expect($bezierBuffer[15]).toBe(0); + expect($bezierBuffer[16]).toBe(17.5); + expect($bezierBuffer[17]).toBe(17.5); + expect($bezierBuffer[18]).toBe(25); + expect($bezierBuffer[19]).toBe(25); + expect($bezierBuffer[20]).toBe(32.5); + expect($bezierBuffer[21]).toBe(32.5); + expect($bezierBuffer[22]).toBe(40); + expect($bezierBuffer[23]).toBe(40); + expect($bezierBuffer[24]).toBe(0); + expect($bezierBuffer[25]).toBe(0); + expect($bezierBuffer[26]).toBe(0); + expect($bezierBuffer[27]).toBe(0); + expect($bezierBuffer[28]).toBe(0); + expect($bezierBuffer[29]).toBe(0); + expect($bezierBuffer[30]).toBe(0); + expect($bezierBuffer[31]).toBe(0); + }); + + it("test case offset 24", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + 24 + ); + + expect($bezierBuffer[0]).toBe(0); + expect($bezierBuffer[1]).toBe(0); + expect($bezierBuffer[2]).toBe(0); + expect($bezierBuffer[3]).toBe(0); + expect($bezierBuffer[4]).toBe(0); + expect($bezierBuffer[5]).toBe(0); + expect($bezierBuffer[6]).toBe(0); + expect($bezierBuffer[7]).toBe(0); + expect($bezierBuffer[8]).toBe(0); + expect($bezierBuffer[9]).toBe(0); + expect($bezierBuffer[10]).toBe(0); + expect($bezierBuffer[11]).toBe(0); + expect($bezierBuffer[12]).toBe(0); + expect($bezierBuffer[13]).toBe(0); + expect($bezierBuffer[14]).toBe(0); + expect($bezierBuffer[15]).toBe(0); + expect($bezierBuffer[16]).toBe(0); + expect($bezierBuffer[17]).toBe(0); + expect($bezierBuffer[18]).toBe(0); + expect($bezierBuffer[19]).toBe(0); + expect($bezierBuffer[20]).toBe(0); + expect($bezierBuffer[21]).toBe(0); + expect($bezierBuffer[22]).toBe(0); + expect($bezierBuffer[23]).toBe(0); + expect($bezierBuffer[24]).toBe(17.5); + expect($bezierBuffer[25]).toBe(17.5); + expect($bezierBuffer[26]).toBe(25); + expect($bezierBuffer[27]).toBe(25); + expect($bezierBuffer[28]).toBe(32.5); + expect($bezierBuffer[29]).toBe(32.5); + expect($bezierBuffer[30]).toBe(40); + expect($bezierBuffer[31]).toBe(40); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.ts b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.ts new file mode 100644 index 00000000..6f916983 --- /dev/null +++ b/packages/webgl/src/BezierConverter/service/BezierConverterSplit2QuadService.ts @@ -0,0 +1,42 @@ +import { $bezierBuffer } from "../../BezierConverter"; + +/** + * @description 2次ベジェを分割 + * Split quadratic Bezier + * + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} p4 + * @param {number} p5 + * @param {number} p6 + * @param {number} p7 + * @param {number} offset + * @return {void} + * @method + * @protected + */ +export const execute = ( + p0: number, p1: number, + p2: number, p3: number, + p4: number, p5: number, + p6: number, p7: number, + offset: number +): void => { + + const mx: number = (p0 + 3 * (p2 + p4) + p6) * 0.125; + const my: number = (p1 + 3 * (p3 + p5) + p7) * 0.125; + + // 2次ベジェの始点の値は不要なので含めない + + $bezierBuffer[offset ] = p0 * 0.25 + p2 * 0.75; + $bezierBuffer[offset + 1] = p1 * 0.25 + p3 * 0.75; + $bezierBuffer[offset + 2] = mx; + $bezierBuffer[offset + 3] = my; + + $bezierBuffer[offset + 4] = p4 * 0.75 + p6 * 0.25; + $bezierBuffer[offset + 5] = p5 * 0.75 + p7 * 0.25; + $bezierBuffer[offset + 6] = p6; + $bezierBuffer[offset + 7] = p7; +}; \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.test.ts b/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.test.ts new file mode 100644 index 00000000..c256a6f2 --- /dev/null +++ b/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.test.ts @@ -0,0 +1,50 @@ +import { execute } from "./BezierConverterCubicToQuadUseCase"; +import { describe, expect, it } from "vitest"; +import { $bezierBuffer } from "../../BezierConverter"; + +describe("BezierConverterCubicToQuadUseCase.js method test", () => +{ + it("test case", () => + { + $bezierBuffer.fill(0); + execute( + 10, 10, + 20, 20, + 30, 30, + 40, 40, + ); + + expect($bezierBuffer[0]).toBe(11.875); + expect($bezierBuffer[1]).toBe(11.875); + expect($bezierBuffer[2]).toBe(13.75); + expect($bezierBuffer[3]).toBe(13.75); + expect($bezierBuffer[4]).toBe(15.625); + expect($bezierBuffer[5]).toBe(15.625); + expect($bezierBuffer[6]).toBe(17.5); + expect($bezierBuffer[7]).toBe(17.5); + expect($bezierBuffer[8]).toBe(19.375); + expect($bezierBuffer[9]).toBe(19.375); + expect($bezierBuffer[10]).toBe(21.25); + expect($bezierBuffer[11]).toBe(21.25); + expect($bezierBuffer[12]).toBe(23.125); + expect($bezierBuffer[13]).toBe(23.125); + expect($bezierBuffer[14]).toBe(25); + expect($bezierBuffer[15]).toBe(25); + expect($bezierBuffer[16]).toBe(26.875); + expect($bezierBuffer[17]).toBe(26.875); + expect($bezierBuffer[18]).toBe(28.75); + expect($bezierBuffer[19]).toBe(28.75); + expect($bezierBuffer[20]).toBe(30.625); + expect($bezierBuffer[21]).toBe(30.625); + expect($bezierBuffer[22]).toBe(32.5); + expect($bezierBuffer[23]).toBe(32.5); + expect($bezierBuffer[24]).toBe(34.375); + expect($bezierBuffer[25]).toBe(34.375); + expect($bezierBuffer[26]).toBe(36.25); + expect($bezierBuffer[27]).toBe(36.25); + expect($bezierBuffer[28]).toBe(38.125); + expect($bezierBuffer[29]).toBe(38.125); + expect($bezierBuffer[30]).toBe(40); + expect($bezierBuffer[31]).toBe(40); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.ts b/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.ts new file mode 100644 index 00000000..b82db341 --- /dev/null +++ b/packages/webgl/src/BezierConverter/usecase/BezierConverterCubicToQuadUseCase.ts @@ -0,0 +1,66 @@ +import { execute as bezierConverterSplit2CubicService } from "../service/BezierConverterSplit2CubicService"; +import { execute as bezierConverterSplit2QuadService } from "../service/BezierConverterSplit2QuadService"; +import { $bezierBuffer } from "../../BezierConverter"; + +/** + * @description 3次ベジェ曲線を2次ベジェ曲線に分割 + * Split cubic Bezier curve into quadratic Bezier curve + * + * @param {number} from_x + * @param {number} from_y + * @param {number} cx1 + * @param {number} cy1 + * @param {number} cx2 + * @param {number} cy2 + * @param {number} x + * @param {number} y + * @return {Float32Array} + * @method + * @protected + */ +export const execute = ( + from_x: number, from_y: number, + cx1: number, cy1: number, + cx2: number, cy2: number, + x: number, y: number +): Float32Array => { + + bezierConverterSplit2CubicService( + from_x, from_y, cx1, cy1, cx2, cy2, x, y, + 0, 16 + ); + + bezierConverterSplit2CubicService( + $bezierBuffer[0], $bezierBuffer[1], $bezierBuffer[2], $bezierBuffer[3], + $bezierBuffer[4], $bezierBuffer[5], $bezierBuffer[6], $bezierBuffer[7], + 0, 8 + ); + bezierConverterSplit2CubicService( + $bezierBuffer[16], $bezierBuffer[17], $bezierBuffer[18], $bezierBuffer[19], + $bezierBuffer[20], $bezierBuffer[21], $bezierBuffer[22], $bezierBuffer[23], + 16, 24 + ); + + bezierConverterSplit2QuadService( + $bezierBuffer[0], $bezierBuffer[1], $bezierBuffer[2], $bezierBuffer[3], + $bezierBuffer[4], $bezierBuffer[5], $bezierBuffer[6], $bezierBuffer[7], + 0 + ); + bezierConverterSplit2QuadService( + $bezierBuffer[8], $bezierBuffer[9], $bezierBuffer[10], $bezierBuffer[11], + $bezierBuffer[12], $bezierBuffer[13], $bezierBuffer[14], $bezierBuffer[15], + 8 + ); + bezierConverterSplit2QuadService( + $bezierBuffer[16], $bezierBuffer[17], $bezierBuffer[18], $bezierBuffer[19], + $bezierBuffer[20], $bezierBuffer[21], $bezierBuffer[22], $bezierBuffer[23], + 16 + ); + bezierConverterSplit2QuadService( + $bezierBuffer[24], $bezierBuffer[25], $bezierBuffer[26], $bezierBuffer[27], + $bezierBuffer[28], $bezierBuffer[29], $bezierBuffer[30], $bezierBuffer[31], + 24 + ); + + return $bezierBuffer; +}; \ No newline at end of file diff --git a/packages/webgl/src/Bitmap.ts b/packages/webgl/src/Bitmap.ts new file mode 100644 index 00000000..c234357c --- /dev/null +++ b/packages/webgl/src/Bitmap.ts @@ -0,0 +1,8 @@ +/** + * @description Bitmapの設定データ + * Bitmap setting data + * + * @type {Array} + * @protected + */ +export const $bitmapData: Array = []; \ No newline at end of file diff --git a/packages/webgl/src/Blend.ts b/packages/webgl/src/Blend.ts new file mode 100644 index 00000000..0c97c2ac --- /dev/null +++ b/packages/webgl/src/Blend.ts @@ -0,0 +1,75 @@ +import type { IBlendMode } from "./interface/IBlendMode"; + +/** + * @description 現在設定されているブレンドモード + * The currently set blend mode + * + * @type {IBlendMode} + * @default "normal" + * @private + */ +let $currentBlendMode: IBlendMode = "normal"; + +/** + * @description ブレンドモード情報を更新 + * Update blend mode information + * + * @param {string} blend_mode + * @return {void} + * @method + * @protected + */ +export const $setCurrentBlendMode = (blend_mode: IBlendMode): void => +{ + $currentBlendMode = blend_mode; +}; + +/** + * @description 現在設定されているブレンドモードを返却 + * Returns the currently set blend mode + * + * @return {IBlendMode} + * @method + * @protected + */ +export const $getCurrentBlendMode = (): IBlendMode => +{ + return $currentBlendMode; +}; + +/** + * @description ブレンドモードの設定コード + * Blend mode setting code + * + * @type {number} + * @default 600 + * @private + */ +let $funcCode: number = 600; + +/** + * @description ブレンドモードの設定コードを更新 + * Update the blend mode setting code + * + * @param {number} func_code + * @return {void} + * @method + * @protected + */ +export const $setFuncCode = (func_code: number): void => +{ + $funcCode = func_code; +}; + +/** + * @description ブレンドモードの設定コードを返却 + * Returns the blend mode setting code + * + * @return {number} + * @method + * @protected + */ +export const $getFuncCode = (): number => +{ + return $funcCode; +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendAddService.test.ts b/packages/webgl/src/Blend/service/BlendAddService.test.ts new file mode 100644 index 00000000..a661d3ef --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendAddService.test.ts @@ -0,0 +1,33 @@ +import { execute } from "./BlendAddService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendAddService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ONE"); + expect(dfactor).toBe("ONE"); + }), + "ONE": "ONE", + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(611); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendAddService.ts b/packages/webgl/src/Blend/service/BlendAddService.ts new file mode 100644 index 00000000..a2466c2a --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendAddService.ts @@ -0,0 +1,21 @@ +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ブレンドモードを加算に設定します。 + * Set the blend mode to add. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 611) { + $setFuncCode(611); + $gl.blendFunc($gl.ONE, $gl.ONE); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendAlphaService.test.ts b/packages/webgl/src/Blend/service/BlendAlphaService.test.ts new file mode 100644 index 00000000..93002524 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendAlphaService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendAlphaService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendAlphaService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ZERO"); + expect(dfactor).toBe("SRC_ALPHA"); + }), + "ZERO": "ZERO", + "SRC_ALPHA": "SRC_ALPHA" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(606); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendAlphaService.ts b/packages/webgl/src/Blend/service/BlendAlphaService.ts new file mode 100644 index 00000000..fc68d89c --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendAlphaService.ts @@ -0,0 +1,21 @@ +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ブレンドモードをアルファに設定します。 + * Set the blend mode to alpha. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 606) { + $setFuncCode(606); + $gl.blendFunc($gl.ZERO, $gl.SRC_ALPHA); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendEraseService.test.ts b/packages/webgl/src/Blend/service/BlendEraseService.test.ts new file mode 100644 index 00000000..eb75c01a --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendEraseService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendEraseService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendEraseService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ZERO"); + expect(dfactor).toBe("ONE_MINUS_SRC_ALPHA"); + }), + "ZERO": "ZERO", + "ONE_MINUS_SRC_ALPHA": "ONE_MINUS_SRC_ALPHA" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(603); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendEraseService.ts b/packages/webgl/src/Blend/service/BlendEraseService.ts new file mode 100644 index 00000000..dfadff0f --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendEraseService.ts @@ -0,0 +1,21 @@ +import { $gl } from "../../WebGLUtil"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +/** + * @description ブレンドモードを消去に設定します。 + * Set the blend mode to erase. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 603) { + $setFuncCode(603); + $gl.blendFunc($gl.ZERO, $gl.ONE_MINUS_SRC_ALPHA); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendOneZeroService.test.ts b/packages/webgl/src/Blend/service/BlendOneZeroService.test.ts new file mode 100644 index 00000000..8bd47490 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendOneZeroService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendOneZeroService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendOneZeroService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ONE"); + expect(dfactor).toBe("ZERO"); + }), + "ONE": "ONE", + "ZERO": "ZERO" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(610); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendOneZeroService.ts b/packages/webgl/src/Blend/service/BlendOneZeroService.ts new file mode 100644 index 00000000..5bf1fe32 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendOneZeroService.ts @@ -0,0 +1,21 @@ +import { $gl } from "../../WebGLUtil"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +/** + * @description ブレンドモードをOne/Zeroに設定します。 + * Set the blend mode to One/Zero. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 610) { + $setFuncCode(610); + $gl.blendFunc($gl.ONE, $gl.ZERO); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendResetService.test.ts b/packages/webgl/src/Blend/service/BlendResetService.test.ts new file mode 100644 index 00000000..35be4f11 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendResetService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendResetService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendResetService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ONE"); + expect(dfactor).toBe("ONE_MINUS_SRC_ALPHA"); + }), + "ONE": "ONE", + "ONE_MINUS_SRC_ALPHA": "ONE_MINUS_SRC_ALPHA" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(613); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendResetService.ts b/packages/webgl/src/Blend/service/BlendResetService.ts new file mode 100644 index 00000000..ad58ed9b --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendResetService.ts @@ -0,0 +1,21 @@ +import { $gl } from "../../WebGLUtil"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +/** + * @description ブレンドモードをリセット + * Reset the blend mode + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 613) { + $setFuncCode(613); + $gl.blendFunc($gl.ONE, $gl.ONE_MINUS_SRC_ALPHA); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendScreenService.test.ts b/packages/webgl/src/Blend/service/BlendScreenService.test.ts new file mode 100644 index 00000000..ea0c5d51 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendScreenService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendScreenService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendScreenService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("ONE_MINUS_DST_COLOR"); + expect(dfactor).toBe("ONE"); + }), + "ONE_MINUS_DST_COLOR": "ONE_MINUS_DST_COLOR", + "ONE": "ONE" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(641); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendScreenService.ts b/packages/webgl/src/Blend/service/BlendScreenService.ts new file mode 100644 index 00000000..f17b8850 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendScreenService.ts @@ -0,0 +1,21 @@ +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ブレンドモードをスクリーンに設定します。 + * Set the blend mode to screen. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 641) { + $setFuncCode(641); + $gl.blendFunc($gl.ONE_MINUS_DST_COLOR, $gl.ONE); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendSourceAtopService.test.ts b/packages/webgl/src/Blend/service/BlendSourceAtopService.test.ts new file mode 100644 index 00000000..17183511 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendSourceAtopService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendSourceAtopService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendSourceAtopService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("DST_ALPHA"); + expect(dfactor).toBe("ONE_MINUS_SRC_ALPHA"); + }), + "DST_ALPHA": "DST_ALPHA", + "ONE_MINUS_SRC_ALPHA": "ONE_MINUS_SRC_ALPHA" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(673); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendSourceAtopService.ts b/packages/webgl/src/Blend/service/BlendSourceAtopService.ts new file mode 100644 index 00000000..2d977fb0 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendSourceAtopService.ts @@ -0,0 +1,21 @@ +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ブレンドモードをソースアトップに設定します。 + * Set the blend mode to source atop. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 673) { + $setFuncCode(673); + $gl.blendFunc($gl.DST_ALPHA, $gl.ONE_MINUS_SRC_ALPHA); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendSourceInService.test.ts b/packages/webgl/src/Blend/service/BlendSourceInService.test.ts new file mode 100644 index 00000000..32a4bbdf --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendSourceInService.test.ts @@ -0,0 +1,34 @@ +import { execute } from "./BlendSourceInService"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +describe("BlendSourceInService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn((sfactor, dfactor) => + { + expect(sfactor).toBe("DST_ALPHA"); + expect(dfactor).toBe("ZERO"); + }), + "DST_ALPHA": "DST_ALPHA", + "ZERO": "ZERO" + } + } + }); + + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute(); + expect($getFuncCode()).toBe(670); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/service/BlendSourceInService.ts b/packages/webgl/src/Blend/service/BlendSourceInService.ts new file mode 100644 index 00000000..e16086a0 --- /dev/null +++ b/packages/webgl/src/Blend/service/BlendSourceInService.ts @@ -0,0 +1,21 @@ +import { $gl } from "../../WebGLUtil"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +/** + * @description ブレンドモードをソースインに設定します。 + * Set the blend mode to source in. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($getFuncCode() !== 670) { + $setFuncCode(670); + $gl.blendFunc($gl.DST_ALPHA, $gl.ZERO); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlendBootUseCase.ts b/packages/webgl/src/Blend/usecase/BlendBootUseCase.ts new file mode 100644 index 00000000..8c877a64 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlendBootUseCase.ts @@ -0,0 +1,16 @@ +import { execute as blendResetService } from "../service/BlendResetService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ブレンドモードを起動する + * Start the blend mode. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $gl.enable($gl.BLEND); + blendResetService(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlendDrawFilterToMainUseCase.ts b/packages/webgl/src/Blend/usecase/BlendDrawFilterToMainUseCase.ts new file mode 100644 index 00000000..bacbed93 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlendDrawFilterToMainUseCase.ts @@ -0,0 +1,115 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { $context } from "../../WebGLUtil"; +import { execute as blendOperationUseCase } from "./BlendOperationUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsBlendMatrixTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as frameBufferManagerGetTextureFromBoundsUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetTextureFromBoundsUseCase"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind01UseCase } from "../../TextureManager/usecase/TextureManagerBind01UseCase"; +import { execute as variantsBlendDrawShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendDrawShaderService"; +import { execute as shaderManagerSetBlendWithColorTransformUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetBlendWithColorTransformUniformService"; +import { execute as shaderManagerSetMatrixTextureWithColorTransformUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMatrixTextureWithColorTransformUniformService"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as blendResetService } from "../service/BlendResetService"; +import { execute as frameBufferManagerTransferTextureFromRectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerTransferTextureFromRectUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; + +/** + * @description 指定のTextureObjectを描画します。 + * Draw the specified TextureObject. + * + * @param {ITextureObject} texture_object + * @param {Float32Array} color_transform + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + color_transform: Float32Array, + x: number, + y: number +): void => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const width = texture_object.width; + const height = texture_object.height; + switch ($context.globalCompositeOperation) { + + case "normal": + case "layer": + case "add": + case "screen": + case "alpha": + case "erase": + case "copy": + { + // メインのAttachmentObjectに描画して終了 + $context.bind($context.$mainAttachmentObject as IAttachmentObject); + $context.setTransform(1, 0, 0, 1, x, y); + + const shaderManager = variantsBlendMatrixTextureShaderService(true); + shaderManagerSetMatrixTextureWithColorTransformUniformService( + shaderManager, color_transform, width, height + ); + + // テクスチャをバインド + textureManagerBind0UseCase(texture_object); + + blendOperationUseCase($context.globalCompositeOperation); + shaderManagerDrawTextureUseCase(shaderManager); + } + break; + + default: + { + // 書き込み先の矩形を取得 + const dstTextureObject = frameBufferManagerGetTextureFromBoundsUseCase(x, y, width, height); + + // ブレンド用のフレームバッファをバインド + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, false); + $context.bind(attachmentObject); + + // ブレンドするテクスチャをバインド + textureManagerBind01UseCase(dstTextureObject, texture_object); + + const shaderManager = variantsBlendDrawShaderService( + $context.globalCompositeOperation, true + ); + + shaderManagerSetBlendWithColorTransformUniformService( + shaderManager, + color_transform[0], color_transform[1], color_transform[2], color_transform[3], + color_transform[4], color_transform[5], color_transform[6], color_transform[7] + ); + + shaderManagerDrawTextureUseCase(shaderManager); + + // メインのAttachmentObjectに描画して終了 + $context.bind($context.$mainAttachmentObject as IAttachmentObject); + $context.reset(); + $context.setTransform(1, 0, 0, 1, x, y); + + // ブレンドしたtextureを元の座標に描画 + frameBufferManagerTransferTextureFromRectUseCase( + attachmentObject.texture as ITextureObject + ); + + textureManagerReleaseTextureObjectUseCase(dstTextureObject); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject); + } + break; + + } + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + blendResetService(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlendOperationUseCase.test.ts b/packages/webgl/src/Blend/usecase/BlendOperationUseCase.test.ts new file mode 100644 index 00000000..6b61b7a6 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlendOperationUseCase.test.ts @@ -0,0 +1,73 @@ +import { execute } from "./BlendOperationUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { + $setFuncCode, + $getFuncCode +} from "../../Blend"; + +vi.mock("../../WebGLUtil.ts", async (importOriginal) => +{ + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "blendFunc": vi.fn(() => "blendFunc"), + "ONE_MINUS_SRC_ALPHA": "ONE_MINUS_SRC_ALPHA", + "ONE_MINUS_DST_COLOR": "ONE_MINUS_DST_COLOR", + "ONE": "ONE", + "SRC_ALPHA": "SRC_ALPHA", + "ZERO": "ZERO", + } + } +}); + +describe("BlendOperationUseCase.js method test", () => +{ + it("test case add", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("add"); + expect($getFuncCode()).toBe(611); + }); + + it("test case screen", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("screen"); + expect($getFuncCode()).toBe(641); + }); + + it("test case alpha", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("alpha"); + expect($getFuncCode()).toBe(606); + }); + + it("test case erase", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("erase"); + expect($getFuncCode()).toBe(603); + }); + + it("test case copy", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("copy"); + expect($getFuncCode()).toBe(610); + }); + + it("test case normal", () => + { + $setFuncCode(600); + expect($getFuncCode()).toBe(600); + execute("normal"); + expect($getFuncCode()).toBe(613); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlendOperationUseCase.ts b/packages/webgl/src/Blend/usecase/BlendOperationUseCase.ts new file mode 100644 index 00000000..13794045 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlendOperationUseCase.ts @@ -0,0 +1,47 @@ +import type { IBlendMode } from "../../interface/IBlendMode"; +import { execute as blendAddService } from "../service/BlendAddService"; +import { execute as blendResetService } from "../service/BlendResetService"; +import { execute as blendScreenService } from "../service/BlendScreenService"; +import { execute as blendAlphaService } from "../service/BlendAlphaService"; +import { execute as blendEraseService } from "../service/BlendEraseService"; +import { execute as blendOneZeroService } from "../service/BlendOneZeroService"; + +/** + * @description 設定されたブレンドモードへ切り替える + * Switch to the set blend mode + * + * @param {IBlendMode} operation + * @return {void} + * @method + * @protected + */ +export const execute = (operation: IBlendMode): void => +{ + switch (operation) { + + case "add": + blendAddService(); + break; + + case "screen": + blendScreenService(); + break; + + case "alpha": + blendAlphaService(); + break; + + case "erase": + blendEraseService(); + break; + + case "copy": + blendOneZeroService(); + break; + + default: + blendResetService(); + break; + + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.test.ts b/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.test.ts new file mode 100644 index 00000000..581a6944 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./BlnedClearArraysInstancedUseCase"; +import { execute as variantsBlendInstanceShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendInstanceShaderService.ts"; +import { describe, expect, it, vi } from "vitest"; + +vi.mock("../../WebGLUtil.ts", async (importOriginal) => +{ + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createProgram": vi.fn(() => "createProgram"), + "createShader": vi.fn(() => "createShader"), + "shaderSource": vi.fn(() => "shaderSource"), + "compileShader": vi.fn(() => "compileShader"), + "attachShader": vi.fn(() => "attachShader"), + "linkProgram": vi.fn(() => "linkProgram"), + "detachShader": vi.fn(() => "detachShader"), + "deleteShader": vi.fn(() => "deleteShader"), + "getProgramParameter": vi.fn(() => "getProgramParameter"), + } + } +}); + +describe("BlnedClearArraysInstancedUseCase.js method test", () => +{ + it("test case", () => + { + const shaderInstancedManager = variantsBlendInstanceShaderService(); + shaderInstancedManager.count++; + + expect(shaderInstancedManager.count).toBe(1); + + execute(); + + expect(shaderInstancedManager.count).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.ts b/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.ts new file mode 100644 index 00000000..10c32dbd --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlnedClearArraysInstancedUseCase.ts @@ -0,0 +1,15 @@ +import { execute as variantsBlendInstanceShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendInstanceShaderService"; + +/** + * @description リサイズなどのイベント発火時には描画情報を初期化します + * Initialize drawing information when an event such as resizing is fired + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const shaderInstancedManager = variantsBlendInstanceShaderService(); + shaderInstancedManager.clear(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlnedDrawArraysInstancedUseCase.ts b/packages/webgl/src/Blend/usecase/BlnedDrawArraysInstancedUseCase.ts new file mode 100644 index 00000000..b00a6865 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlnedDrawArraysInstancedUseCase.ts @@ -0,0 +1,31 @@ +import { execute as variantsBlendInstanceShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendInstanceShaderService"; +import { execute as shaderInstancedManagerDrawArraysInstancedUseCase } from "../../Shader/ShaderInstancedManager/usecase/ShaderInstancedManagerDrawArraysInstancedUseCase"; +import { execute as blendOperationUseCase } from "../../Blend/usecase/BlendOperationUseCase"; +import { execute as frameBufferManagerTransferAtlasTextureService } from "../../FrameBufferManager/service/FrameBufferManagerTransferAtlasTextureService"; +import { $context } from "../../WebGLUtil"; + +/** + * @description インスタンス描画を実行します。 + * Execute instance drawing. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const shaderInstancedManager = variantsBlendInstanceShaderService(); + if (!shaderInstancedManager.count) { + return ; + } + + // Transfer to atlas texture. + frameBufferManagerTransferAtlasTextureService(); + + blendOperationUseCase($context.globalCompositeOperation); + shaderInstancedManagerDrawArraysInstancedUseCase( + shaderInstancedManager + ); + + shaderInstancedManager.clear(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Blend/usecase/BlnedDrawDisplayObjectUseCase.ts b/packages/webgl/src/Blend/usecase/BlnedDrawDisplayObjectUseCase.ts new file mode 100644 index 00000000..684e0da2 --- /dev/null +++ b/packages/webgl/src/Blend/usecase/BlnedDrawDisplayObjectUseCase.ts @@ -0,0 +1,252 @@ +import type { Node } from "@next2d/texture-packer"; +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as variantsBlendInstanceShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendInstanceShaderService"; +import { execute as variantsBlendDrawShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendDrawShaderService"; +import { $getFloat32Array6, $RENDER_MAX_SIZE } from "../../WebGLUtil"; +import { $setActiveAtlasIndex } from "../../AtlasManager"; +import { execute as frameBufferManagerGetTextureFromNodeUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetTextureFromNodeUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as textureManagerBind01UseCase } from "../../TextureManager/usecase/TextureManagerBind01UseCase"; +import { execute as frameBufferManagerGetTextureFromBoundsUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetTextureFromBoundsUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as frameBufferManagerTransferTextureFromRectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerTransferTextureFromRectUseCase"; +import { execute as shaderManagerSetBlendWithColorTransformUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetBlendWithColorTransformUniformService"; +import { execute as variantsBlendMatrixTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService"; +import { execute as shaderManagerSetMatrixTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService"; +import { + $context, + $getViewportHeight, + $getViewportWidth +} from "../../WebGLUtil"; +import { + $getCurrentBlendMode, + $setCurrentBlendMode +} from "../../Blend"; +import { + $getCurrentAtlasIndex, + $setCurrentAtlasIndex +} from "../../AtlasManager"; +import { renderQueue } from "@next2d/render-queue"; + +/** + * @description DisplayObject単体の描画を実行 + * Execute drawing of a single DisplayObject + * + * @param {Node} node + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @param {Float32Array} color_transform + * @return {void} + * @method + * @protected + */ +export const execute = ( + node: Node, + x_min: number, + y_min: number, + x_max: number, + y_max: number, + color_transform: Float32Array +): void => { + + const ct0 = color_transform[0]; + const ct1 = color_transform[1]; + const ct2 = color_transform[2]; + const ct3 = $context.globalAlpha; + const ct4 = color_transform[4] / 255; + const ct5 = color_transform[5] / 255; + const ct6 = color_transform[6] / 255; + const ct7 = 0; + + const matrix = $context.$matrix; + switch ($context.globalCompositeOperation) { + + case "normal": + case "layer": + case "add": + case "screen": + case "alpha": + case "erase": + case "copy": + { + if ($getCurrentBlendMode() !== $context.globalCompositeOperation + || $getCurrentAtlasIndex() !== node.index + ) { + // 異なるフレームバッファになるので、切り替え前にメインバッファに描画を実行 + $setActiveAtlasIndex($getCurrentAtlasIndex()); + + const currentOperation = $context.globalCompositeOperation; + $context.globalCompositeOperation = $getCurrentBlendMode(); + $context.drawArraysInstanced(); + + // ブレンドモードをセット + $context.globalCompositeOperation = currentOperation; + $setCurrentBlendMode($context.globalCompositeOperation); + + // indexをセット + $setCurrentAtlasIndex(node.index); + $setActiveAtlasIndex(node.index); + } + + // 描画するまで配列に変数を保持 + const shaderInstancedManager = variantsBlendInstanceShaderService(); + + renderQueue.push( + // texture rectangle (vec4) + node.x / $RENDER_MAX_SIZE, node.y / $RENDER_MAX_SIZE, + node.w / $RENDER_MAX_SIZE, node.h / $RENDER_MAX_SIZE, + // texture width, height and viewport width, height (vec4) + node.w, node.h, $getViewportWidth(), $getViewportHeight(), + // matrix tx, ty (vec2) + matrix[6], matrix[7], + // matrix scale0, rotate0, scale1, rotate1 (vec4) + matrix[0], matrix[1], matrix[3], matrix[4], + // mulColor (vec4) + ct0, ct1, ct2, ct3, + // addColor (vec4) + ct4, ct5, ct6, ct7 + ); + shaderInstancedManager.count++; + } + break; + + default: + { + const currentAttachmentObject = $context.currentAttachmentObject; + + // これまでの描画を実行 + $context.drawArraysInstanced(); + + const width = Math.ceil(Math.abs(x_max - x_min)); + const height = Math.ceil(Math.abs(y_max - y_min)); + + let offsetX = 0; + let offsetY = 0; + + let srcTextureObject = frameBufferManagerGetTextureFromNodeUseCase(node); + if (matrix[0] !== 1 || matrix[1] !== 0 + || matrix[3] !== 0 || matrix[4] !== 1 + ) { + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase( + width, height, false + ); + + const a = $getFloat32Array6( + matrix[0], matrix[1], + matrix[3], matrix[4], + width / 2, height / 2 + ); + + const b = $getFloat32Array6( + 1, 0, 0, 1, + -node.w / 2, + -node.h / 2 + ); + + const tMatrix = $getFloat32Array6( + a[0] * b[0] + a[2] * b[1], + a[1] * b[0] + a[3] * b[1], + a[0] * b[2] + a[2] * b[3], + a[1] * b[2] + a[3] * b[3], + a[0] * b[4] + a[2] * b[5] + a[4], + a[1] * b[4] + a[3] * b[5] + a[5] + ); + + $context.save(); + $context.bind(attachmentObject); + $context.setTransform( + tMatrix[0], tMatrix[1], + tMatrix[2], tMatrix[3], + tMatrix[4], tMatrix[5] + ); + + offsetX = tMatrix[4]; + offsetY = tMatrix[5]; + + // 元の描画をフィルター用のテクスチャに描画 + const shaderManager = variantsBlendMatrixTextureShaderService(); + shaderManagerSetMatrixTextureUniformService( + shaderManager, srcTextureObject.width, srcTextureObject.height + ); + + textureManagerBind0UseCase(srcTextureObject, true); + shaderManagerDrawTextureUseCase(shaderManager); + + // 元のテクスチャを解放 + textureManagerReleaseTextureObjectUseCase(srcTextureObject); + + // フィルター用のテクスチャをセットしてコピー用のAttachmentObjectをリリース + srcTextureObject = attachmentObject.texture as ITextureObject; + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + $context.restore(); + } + + // offset値があれば、座標を移動 + if (offsetX || offsetY) { + matrix[6] -= offsetX; + matrix[7] -= offsetY; + } + + const dstTextureObject = frameBufferManagerGetTextureFromBoundsUseCase( + matrix[6], matrix[7], width, height + ); + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, false); + $context.bind(attachmentObject); + + // ブレンドするテクスチャをバインド + textureManagerBind01UseCase(dstTextureObject, srcTextureObject); + + // blend用のシェーダーを取得 + const shaderManager = variantsBlendDrawShaderService($context.globalCompositeOperation, true); + shaderManagerSetBlendWithColorTransformUniformService( + shaderManager, + ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7 + ); + + shaderManagerDrawTextureUseCase(shaderManager); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + // ブレンドしたtextureを元の座標に描画 + frameBufferManagerTransferTextureFromRectUseCase( + attachmentObject.texture as ITextureObject + ); + + // matrixを元に戻す + if (offsetX || offsetY) { + matrix[6] += offsetX; + matrix[7] += offsetY; + } + + // テクスチャを解放 + textureManagerReleaseTextureObjectUseCase(srcTextureObject); + textureManagerReleaseTextureObjectUseCase(dstTextureObject); + + // フレームバッファを解放 + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject); + + // ブレンドモードをセット + $setCurrentBlendMode($context.globalCompositeOperation); + + // indexをセット + $setCurrentAtlasIndex(node.index); + $setActiveAtlasIndex(node.index); + } + break; + + } +}; \ No newline at end of file diff --git a/packages/webgl/src/CanvasGradientToWebGL.ts b/packages/webgl/src/CanvasGradientToWebGL.ts deleted file mode 100644 index 19ad0489..00000000 --- a/packages/webgl/src/CanvasGradientToWebGL.ts +++ /dev/null @@ -1,244 +0,0 @@ -import type { InterpolationMethodImpl } from "./interface/InterpolationMethodImpl"; -import type { SpreadMethodImpl } from "./interface/SpreadMethodImpl"; -import type { GradientTypeImpl } from "./interface/GradientTypeImpl"; -import { - $getFloat32Array6, - $getArray, - $clamp, - $poolInt32Array4, - $poolFloat32Array6 -} from "@next2d/share"; - -/** - * @class - */ -export class CanvasGradientToWebGL -{ - private _$rgb: InterpolationMethodImpl; - private _$mode: SpreadMethodImpl; - private _$type: GradientTypeImpl; - private _$focalPointRatio: number; - private readonly _$points: Float32Array; - private readonly _$stops: any[]; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {string} - * @default InterpolationMethod.RGB - * @private - */ - this._$rgb = "rgb"; - - /** - * @type {string} - * @default SpreadMethod.PAD - * @private - */ - this._$mode = "pad"; - - /** - * @type {string} - * @default GradientType.LINEAR - * @private - */ - this._$type = "linear"; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$focalPointRatio = 0; - - /** - * @type {Float32Array} - * @private - */ - this._$points = $getFloat32Array6(); - - /** - * @type {array} - * @private - */ - this._$stops = $getArray(); - } - - /** - * @return {void} - * @method - * @public - */ - dispose (): void - { - const stops = this._$stops; - for (let idx: number = 0; idx < stops.length; ++idx) { - $poolInt32Array4(stops[idx][1]); - } - $poolFloat32Array6(this._$points); - } - - /** - * @member {string} - * @readonly - * @public - */ - get mode (): SpreadMethodImpl - { - return this._$mode; - } - - /** - * @member {string} - * @readonly - * @public - */ - get type (): GradientTypeImpl - { - return this._$type; - } - - /** - * @member {string} - * @readonly - * @public - */ - get rgb (): InterpolationMethodImpl - { - return this._$rgb; - } - - /** - * @member {Float32Array} - * @readonly - * @public - */ - get points (): Float32Array - { - return this._$points; - } - - /** - * @member {number} - * @readonly - * @public - */ - get focalPointRatio (): number - { - return this._$focalPointRatio; - } - - /** - * @member {array} - * @readonly - * @public - */ - get stops (): any[] - { - // sort - this._$stops.sort((a, b) => - { - switch (true) { - - case a[0] > b[0]: - return 1; - - case b[0] > a[0]: - return -1; - - default: - return 0; - - } - }); - - return this._$stops; - } - - /** - * @param {number} x0 - * @param {number} y0 - * @param {number} x1 - * @param {number} y1 - * @param {string} [rgb=InterpolationMethod.RGB] - * @param {string} [mode=SpreadMethod.PAD] - * @return {CanvasGradientToWebGL} - * @method - * @public - */ - linear ( - x0: number, y0: number, x1: number, y1: number, - rgb: InterpolationMethodImpl = "rgb", - mode: SpreadMethodImpl = "pad" - ): CanvasGradientToWebGL { - - this._$type = "linear"; - this._$points[0] = x0; - this._$points[1] = y0; - this._$points[2] = x1; - this._$points[3] = y1; - this._$rgb = rgb; - this._$mode = mode; - - if (this._$stops.length) { - this._$stops.length = 0; - } - - return this; - } - - /** - * @param {number} x0 - * @param {number} y0 - * @param {number} r0 - * @param {number} x1 - * @param {number} y1 - * @param {number} r1 - * @param {string} [rgb=InterpolationMethod.RGB] - * @param {string} [mode=SpreadMethod.PAD] - * @param {number} [focal_point_ratio=0] - * @return {CanvasGradientToWebGL} - * @method - * @public - */ - radial ( - x0: number, y0: number, r0: number, x1: number, y1: number, r1: number, - rgb: InterpolationMethodImpl = "rgb", - mode: SpreadMethodImpl = "pad", - focal_point_ratio: number = 0 - ): CanvasGradientToWebGL { - - this._$type = "radial"; - this._$points[0] = x0; - this._$points[1] = y0; - this._$points[2] = r0; - this._$points[3] = x1; - this._$points[4] = y1; - this._$points[5] = r1; - this._$rgb = rgb; - this._$mode = mode; - this._$focalPointRatio = $clamp(focal_point_ratio, -0.975, 0.975, 0); - - if (this._$stops.length) { - this._$stops.length = 0; - } - - return this; - } - - /** - * @param {number} offset - * @param {Int32Array} color - * @return {void} - * @method - * @public - */ - addColorStop (offset: number, color: Int32Array): void - { - this._$stops.push($getArray(offset, color)); - } -} diff --git a/packages/webgl/src/CanvasPatternToWebGL.ts b/packages/webgl/src/CanvasPatternToWebGL.ts deleted file mode 100644 index 436f8ef7..00000000 --- a/packages/webgl/src/CanvasPatternToWebGL.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @class - */ -export class CanvasPatternToWebGL -{ - private readonly _$texture: WebGLTexture; - private readonly _$repeat: boolean; - private readonly _$colorTransform: Float32Array; - - /** - * @constructor - * @public - */ - constructor ( - texture: WebGLTexture, - repeat: boolean, - color_transform: Float32Array - ) { - - /** - * @type {WebGLTexture} - * @private - */ - this._$texture = texture; - - /** - * @type {string} - * @private - */ - this._$repeat = repeat; - - /** - * @type {Float32Array} - * @private - */ - this._$colorTransform = color_transform; - } - - /** - * @member {WebGLTexture} - * @readonly - * @public - */ - get texture (): WebGLTexture - { - return this._$texture; - } - - /** - * @member {boolean} - * @readonly - * @public - */ - get repeat (): boolean - { - return this._$repeat; - } - - /** - * @member {Float32Array} - * @readonly - * @public - */ - get colorTransform (): Float32Array - { - return this._$colorTransform; - } -} \ No newline at end of file diff --git a/packages/webgl/src/CanvasToWebGLContext.ts b/packages/webgl/src/CanvasToWebGLContext.ts deleted file mode 100644 index d39e0627..00000000 --- a/packages/webgl/src/CanvasToWebGLContext.ts +++ /dev/null @@ -1,2349 +0,0 @@ -import { CanvasToWebGLContextStyle } from "./CanvasToWebGLContextStyle"; -import { FrameBufferManager } from "./FrameBufferManager"; -import { CanvasToWebGLContextPath } from "./CanvasToWebGLContextPath"; -import { CanvasToWebGLContextGrid } from "./CanvasToWebGLContextGrid"; -import { CanvasToWebGLShaderList } from "./shader/CanvasToWebGLShaderList"; -import { GradientLUTGenerator } from "./shader/GradientLUTGenerator"; -import { VertexArrayObjectManager } from "./VertexArrayObjectManager"; -import { CanvasToWebGLContextMask } from "./CanvasToWebGLContextMask"; -import { CanvasToWebGLContextBlend } from "./CanvasToWebGLContextBlend"; -import { CanvasPatternToWebGL } from "./CanvasPatternToWebGL"; -import { CanvasGradientToWebGL } from "./CanvasGradientToWebGL"; -import { WebGLFillMeshGenerator } from "./WebGLFillMeshGenerator"; -import { $setRenderSize } from "./Const"; -import type { CanvasToWebGLShader } from "./shader/CanvasToWebGLShader"; -import type { GradientShapeShaderVariantCollection } from "./shader/variants/GradientShapeShaderVariantCollection"; -import type { ShapeShaderVariantCollection } from "./shader/variants/ShapeShaderVariantCollection"; -import type { WebGLShaderUniform } from "./shader/WebGLShaderUniform"; -import type { FilterShaderVariantCollection } from "./shader/variants/FilterShaderVariantCollection"; -import type { BlendShaderVariantCollection } from "./shader/variants/BlendShaderVariantCollection"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { BlendModeImpl } from "./interface/BlendModeImpl"; -import type { IndexRangeImpl } from "./interface/IndexRangeImpl"; -import type { PointImpl } from "./interface/PointImpl"; -import type { VerticesImpl } from "./interface/VerticesImpl"; -import type { InterpolationMethodImpl } from "./interface/InterpolationMethodImpl"; -import type { SpreadMethodImpl } from "./interface/SpreadMethodImpl"; -import type { CapsStyleImpl } from "./interface/CapsStyleImpl"; -import type { JointStyleImpl } from "./interface/JointStyleImpl"; -import type { CachePositionImpl } from "./interface/CachePositionImpl"; -import { - $Math, - $getFloat32Array9, - $getArray, - $clamp, - $poolArray, - $inverseMatrix, - $poolFloat32Array9, - $poolBoundsObject, - $getBoundsObject -} from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContext -{ - public _$offsetX: number; - public _$offsetY: number; - public readonly _$gl: WebGL2RenderingContext; - private _$cacheBounds: BoundsImpl; - private _$matrix: Float32Array; - private _$cacheAttachment: AttachmentImpl|null; - private _$globalAlpha: number; - private _$imageSmoothingEnabled: boolean; - private _$globalCompositeOperation: BlendModeImpl; - private _$clearColorR: number; - private _$clearColorG: number; - private _$clearColorB: number; - private _$clearColorA: number; - private _$viewportWidth: number; - private _$viewportHeight: number; - private _$cachePosition: CachePositionImpl | null; - private _$isLayer: boolean; - private readonly _$maxTextureSize: number; - private readonly _$contextStyle: CanvasToWebGLContextStyle; - private readonly _$stack: Float32Array[]; - private readonly _$blends: boolean[]; - private readonly _$positions: BoundsImpl[]; - private readonly _$frameBufferManager: FrameBufferManager; - private readonly _$path: CanvasToWebGLContextPath; - private readonly _$grid: CanvasToWebGLContextGrid; - private readonly _$shaderList: CanvasToWebGLShaderList; - private readonly _$gradientLUT: GradientLUTGenerator; - private readonly _$vao: VertexArrayObjectManager; - private readonly _$mask: CanvasToWebGLContextMask; - private readonly _$blend: CanvasToWebGLContextBlend; - private readonly _$maskBounds: BoundsImpl; - private readonly _$attachmentArray: Array; - - /** - * @param {WebGL2RenderingContext} gl - * @param {number} sample - * @constructor - * @public - */ - constructor (gl: WebGL2RenderingContext, sample: number) - { - $setRenderSize(gl.getParameter(gl.MAX_TEXTURE_SIZE)); - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {number} - * @private - */ - const samples: number = $Math.min( - sample, - gl.getParameter(gl.MAX_SAMPLES) - ); - - /** - * @type {number} - * @private - */ - this._$maxTextureSize = $Math.min(8192, - gl.getParameter(gl.MAX_TEXTURE_SIZE) - ) - 2; - - /** - * @type {CanvasToWebGLContextStyle} - * @private - */ - this._$contextStyle = new CanvasToWebGLContextStyle(); - - /** - * @type {BoundsImpl} - * @private - */ - this._$cacheBounds = $getBoundsObject(); - - /** - * @type {Float32Array} - * @private - */ - this._$matrix = $getFloat32Array9(1, 0, 0, 0, 1, 0, 0, 0, 1); - - /** - * @type {AttachmentImpl} - * @default null - * @private - */ - this._$cacheAttachment = null; - - /** - * @type {array} - * @private - */ - this._$stack = []; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$globalAlpha = 1; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$imageSmoothingEnabled = false; - - /** - * @type {string} - * @default "normal" - * @private - */ - this._$globalCompositeOperation = "normal"; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$clearColorR = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$clearColorG = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$clearColorB = 1; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$clearColorA = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$viewportWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$viewportHeight = 0; - - /** - * @type {FrameBufferManager} - * @private - */ - this._$frameBufferManager = new FrameBufferManager(gl, samples); - - /** - * @type {CanvasToWebGLContextPath} - * @private - */ - this._$path = new CanvasToWebGLContextPath(); - - /** - * @type {CanvasToWebGLContextGrid} - * @private - */ - this._$grid = new CanvasToWebGLContextGrid(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$offsetX = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$offsetY = 0; - - /** - * @type {array} - * @private - */ - this._$blends = $getArray(); - - /** - * @type {array} - * @private - */ - this._$positions = $getArray(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isLayer = false; - - /** - * @type {CanvasToWebGLShaderList} - * @private - */ - this._$shaderList = new CanvasToWebGLShaderList(this, gl); - - /** - * @type {GradientLUTGenerator} - * @private - */ - this._$gradientLUT = new GradientLUTGenerator(this, gl); - - /** - * @type {VertexArrayObjectManager} - * @private - */ - this._$vao = new VertexArrayObjectManager(gl); - - /** - * @type {CanvasToWebGLContextMask} - * @private - */ - this._$mask = new CanvasToWebGLContextMask(this, gl); - - /** - * @type {CanvasToWebGLContextBlend} - * @private - */ - this._$blend = new CanvasToWebGLContextBlend(this, gl); - - /** - * @type {array} - * @private - */ - this._$attachmentArray = []; - - /** - * @type {object} - * @private - */ - this._$maskBounds = $getBoundsObject(0, 0, 0, 0); - - /** - * @type {object} - * @default null - * @private - */ - this._$cachePosition = null; - } - - /** - * @member {object} - * @public - */ - get cachePosition (): CachePositionImpl | null - { - return this._$cachePosition; - } - set cachePosition (cache_position: CachePositionImpl | null) - { - this._$cachePosition = cache_position; - } - - /** - * @return {void} - * @method - * @public - */ - reset (): void - { - // reset - this._$globalAlpha = 1; - this._$globalCompositeOperation = "normal"; - this._$imageSmoothingEnabled = false; - - // reset color - this._$contextStyle.clear(); - } - - /** - * @return {boolean} - * @public - */ - get isLayer (): boolean - { - return this._$isLayer; - } - - /** - * @memberof CanvasToWebGLContext# - * @return {HTMLCanvasElement} - * @public - */ - get canvas (): HTMLCanvasElement | OffscreenCanvas - { - return this._$gl.canvas; - } - - /** - * @return {AttachmentImpl|null} - * @public - */ - get cacheAttachment (): AttachmentImpl | null - { - return this._$cacheAttachment; - } - set cacheAttachment (attachment: AttachmentImpl | null) - { - this._$cacheAttachment = attachment; - } - - /** - * @return {BoundsImpl} - * @readonly - * @public - */ - get cacheBounds (): BoundsImpl - { - return this._$cacheBounds; - } - - /** - * @member {Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL} - * @public - */ - get fillStyle (): Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL - { - return this._$contextStyle.fillStyle; - } - set fillStyle (fill_style: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL) - { - this._$contextStyle.fillStyle = fill_style; - } - - /** - * @member {Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL} - * @public - */ - get strokeStyle (): Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL - { - return this._$contextStyle.strokeStyle; - } - set strokeStyle (stroke_style: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL) - { - this._$contextStyle.strokeStyle = stroke_style; - } - - /** - * @member {number} - * @public - */ - get lineWidth (): number - { - return this._$contextStyle.lineWidth; - } - set lineWidth (line_width: number) - { - this._$contextStyle.lineWidth = line_width; - } - - /** - * @member {string} - * @public - */ - get lineCap (): CapsStyleImpl - { - return this._$contextStyle.lineCap; - } - set lineCap (line_cap: CapsStyleImpl) - { - this._$contextStyle.lineCap = line_cap; - } - - /** - * @member {string} - * @public - */ - get lineJoin (): JointStyleImpl - { - return this._$contextStyle.lineJoin; - } - set lineJoin (line_join: JointStyleImpl) - { - this._$contextStyle.lineJoin = line_join; - } - - /** - * @member {number} - * @public - */ - get miterLimit (): number - { - return this._$contextStyle.miterLimit; - } - set miterLimit (miter_limit: number) - { - this._$contextStyle.miterLimit = miter_limit; - } - - /** - * @member {number} - * @public - */ - get globalAlpha (): number - { - return this._$globalAlpha; - } - set globalAlpha (global_alpha: number) - { - this._$globalAlpha = $clamp(global_alpha, 0, 1, 1); - } - - /** - * @member {boolean} - * @public - */ - get imageSmoothingEnabled (): boolean - { - return this._$imageSmoothingEnabled; - } - set imageSmoothingEnabled (image_smoothing_enabled: boolean) - { - this._$imageSmoothingEnabled = image_smoothing_enabled; - } - - /** - * @member {BlendModeImpl} - * @public - */ - get globalCompositeOperation (): BlendModeImpl - { - return this._$globalCompositeOperation; - } - set globalCompositeOperation (global_composite_operation: BlendModeImpl) - { - this._$globalCompositeOperation = global_composite_operation; - } - - /** - * @member {FrameBufferManager} - * @readonly - * @public - */ - get frameBuffer (): FrameBufferManager - { - return this._$frameBufferManager; - } - - /** - * @member {CanvasToWebGLShaderList} - * @readonly - * @public - */ - get shaderList (): CanvasToWebGLShaderList - { - return this._$shaderList; - } - - /** - * @member {CanvasToWebGLContextPath} - * @readonly - * @public - */ - get path (): CanvasToWebGLContextPath - { - return this._$path; - } - - /** - * @member {CanvasToWebGLContextGrid} - * @readonly - * @public - */ - get grid (): CanvasToWebGLContextGrid - { - return this._$grid; - } - - /** - * @member {VertexArrayObjectManager} - * @readonly - * @public - */ - get vao (): VertexArrayObjectManager - { - return this._$vao; - } - - /** - * @member {CanvasToWebGLContextBlend} - * @readonly - * @public - */ - get blend (): CanvasToWebGLContextBlend - { - return this._$blend; - } - - /** - * @return {object} - * @private - */ - _$getCurrentPosition (): BoundsImpl - { - return this._$positions[this._$positions.length - 1]; - } - - /** - * @description textureの最大描画可能サイズからリサイズの比率を算出して返す - * Calculate and return the resize ratio from the maximum drawable size of texture - * - * @param {number} width - * @param {number} height - * @return {number} - * @method - * @public - */ - _$getTextureScale (width: number, height: number): number - { - const maxSize = $Math.max(width, height); - if (maxSize > this._$maxTextureSize) { - return this._$maxTextureSize / maxSize; - } - return 1; - } - - /** - * @description 描画用のbufferをbind - * - * @return {void} - * @method - * @public - */ - drawInstacedArray (): void - { - this._$blend.drawInstacedArray(); - } - - /** - * @return {void} - * @method - * @public - */ - clearInstacedArray (): void - { - this._$blend.clearInstacedArray(); - } - - /** - * @description 描画用のbufferをbind - * - * @param {object} position - * @return {void} - * @method - * @public - */ - bindRenderBuffer (position: CachePositionImpl): void - { - this - ._$frameBufferManager - .bindRenderBuffer(); - - // 初期化 - this._$gl.clearColor(0, 0, 0, 0); - this._$gl.clear(this._$gl.COLOR_BUFFER_BIT | this._$gl.STENCIL_BUFFER_BIT); - - // 描画領域をあらためて設定 - this._$viewportWidth = position.w; - this._$viewportHeight = position.h; - this._$gl.viewport(position.x, position.y, position.w, position.h); - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor( - position.x, position.y, - position.w, position.h - ); - } - - /** - * @param {object} position - * @return {WebGLTexture} - * @method - * @public - */ - getTextureFromRect (position: CachePositionImpl): WebGLTexture - { - const manager: FrameBufferManager = this._$frameBufferManager; - - const atlasTexture: WebGLTexture = manager - .textureManager - .getAtlasTexture(position.index); - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createTextureAttachment( - position.w, position.h - ); - - this._$bind(attachment); - - this.save(); - this.setTransform(1, 0, 0, 1, 0, 0); - - this.reset(); - this.drawImage( - atlasTexture, - -position.x, -atlasTexture.height + position.h + position.y, - atlasTexture.width, atlasTexture.height - ); - - this.restore(); - - const texture: WebGLTexture = attachment.texture as NonNullable; - manager.releaseAttachment(attachment); - - // reset - this._$bind(currentAttachment); - - return texture; - } - - /** - * @param {WebGLTexture} texture - * @return {void} - * @method - * @public - */ - drawBitmap (texture: WebGLTexture): void - { - const variants: BlendShaderVariantCollection = this - ._$shaderList - .blendShaderVariants; - - const shader: CanvasToWebGLShader = variants - .getNormalBlendShader(false); - - variants.setNormalBlendUniform( - shader.uniform, 0, 0, texture.width, texture.height, - this._$matrix, - this._$viewportWidth, this._$viewportHeight, - false, 1, 1, 1, 1, 0, 0, 0, 0 - ); - - this - ._$frameBufferManager - .textureManager - .bind0(texture, this._$imageSmoothingEnabled); - - this._$blend.toOperation("normal"); - shader._$drawImage(); - } - - /** - * @return {void} - * @method - * @public - */ - drawTextureFromRect (texture: WebGLTexture, position: CachePositionImpl): void - { - const manager: FrameBufferManager = this._$frameBufferManager; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - this.bindRenderBuffer(position); - manager.transferTexture(position); - - const atlasTexture: WebGLTexture = manager - .textureManager - .getAtlasTexture(position.index); - - const attachment: AttachmentImpl = manager - .createTextureAttachmentFrom(atlasTexture); - this._$bind(attachment); - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor( - position.x, position.y, - position.w, position.h - ); - this._$gl.clearColor(0, 0, 0, 0); - this._$gl.disable(this._$gl.SCISSOR_TEST); - - this.save(); - this.setTransform(1, 0, 0, 1, 0, 0); - - this.reset(); - this.drawImage( - texture, - position.x, atlasTexture.height - position.h - position.y, - texture.width, texture.height - ); - - this.restore(); - manager.releaseAttachment(attachment); - - // reset - this._$bind(currentAttachment); - - manager.textureManager.release(texture); - } - - /** - * @return {void} - * @method - * @public - */ - stopStencil (): void - { - this._$mask._$onClearRect(); - } - - /** - * @param {object} [attachment = null] - * @return {void} - * @method - * @public - */ - _$bind (attachment: AttachmentImpl | null = null): void - { - if (!attachment) { - return; - } - - this._$frameBufferManager.bind(attachment); - - const colorBuffer: WebGLTexture | WebGLRenderbuffer | null = attachment.color; - const stencilBuffer: WebGLRenderbuffer | null = attachment.stencil; - - const width: number = attachment.width; - const height: number = attachment.height; - - if (this._$viewportWidth !== width || this._$viewportHeight !== height) { - this._$viewportWidth = width; - this._$viewportHeight = height; - this._$gl.viewport(0, 0, width, height); - } - - // カラーバッファorステンシルバッファが、未初期化の場合はクリアする - if (colorBuffer && colorBuffer.dirty - || stencilBuffer && stencilBuffer.dirty - ) { - - if (colorBuffer) { - colorBuffer.dirty = false; - } - - if (stencilBuffer) { - stencilBuffer.dirty = false; - } - - this._$gl.clearColor(0, 0, 0, 0); - this.clearRect(0, 0, this._$viewportWidth, this._$viewportHeight); - this._$gl.clearColor(this._$clearColorR, this._$clearColorG, this._$clearColorB, this._$clearColorA); - - this._$mask._$onClear(attachment.mask); - } - - this._$mask._$onBind(attachment.mask); - } - - /** - * @param {number} a - * @param {number} b - * @param {number} c - * @param {number} d - * @param {number} e - * @param {number} f - * @return {void} - * @method - * @public - */ - setTransform ( - a: number, b: number, c: number, - d: number, e: number, f: number - ): void { - this._$matrix[0] = a; - this._$matrix[1] = b; - this._$matrix[3] = c; - this._$matrix[4] = d; - this._$matrix[6] = e; - this._$matrix[7] = f; - } - - /** - * @param {number} width - * @param {number} height - * @return {void} - * @method - * @public - */ - setMaxSize (width: number, height: number): void - { - this._$frameBufferManager.setMaxSize(width, height); - } - - /** - * @param {number} a - * @param {number} b - * @param {number} c - * @param {number} d - * @param {number} e - * @param {number} f - * @return {void} - * @method - * @public - */ - transform ( - a: number, b: number, c: number, - d: number, e: number, f: number - ): void { - - const a00: number = this._$matrix[0]; - const a01: number = this._$matrix[1]; - const a10: number = this._$matrix[3]; - const a11: number = this._$matrix[4]; - const a20: number = this._$matrix[6]; - const a21: number = this._$matrix[7]; - - this._$matrix[0] = a * a00 + b * a10; - this._$matrix[1] = a * a01 + b * a11; - this._$matrix[3] = c * a00 + d * a10; - this._$matrix[4] = c * a01 + d * a11; - this._$matrix[6] = e * a00 + f * a10 + a20; - this._$matrix[7] = e * a01 + f * a11 + a21; - } - - debug (index: number = 0) - { - const manager: FrameBufferManager = this._$frameBufferManager; - const atlasTexture: WebGLTexture = manager - .textureManager - .getAtlasTexture(index); - - const currentAttachment = manager.currentAttachment; - - const attachment: AttachmentImpl = manager.createTextureAttachmentFrom(atlasTexture); - this._$bind(attachment); - - const pixels = new Uint8Array(atlasTexture.width * atlasTexture.height * 4); - this._$gl.readPixels( - 0, 0, atlasTexture.width, atlasTexture.height, - this._$gl.RGBA, this._$gl.UNSIGNED_BYTE, pixels - ); - - const canvas = document.createElement("canvas"); - canvas.width = atlasTexture.width; - canvas.height = atlasTexture.height; - const ctx = canvas.getContext("2d"); - - const imageData = new ImageData(atlasTexture.width, atlasTexture.height); - for (let idx = 0; idx < pixels.length; ++idx) { - imageData.data[idx] = pixels[idx]; - } - - ctx?.putImageData(imageData, 0, 0); - console.log(canvas.toDataURL()); - - this._$bind(currentAttachment); - manager.releaseAttachment(attachment); - } - - /** - * @param {number} x_min - * @param {number} y_min - * @param {number} x_max - * @param {number} y_max - * @param {Float32Array} color_transform - * @return {void} - * @method - * @public - */ - drawInstance ( - x_min: number, y_min: number, x_max: number, y_max: number, - color_transform: Float32Array - ): void { - - let ct0: number = 1; - let ct1: number = 1; - let ct2: number = 1; - let ct4: number = 0; - let ct5: number = 0; - let ct6: number = 0; - - const ct3: number = this._$globalAlpha; - const ct7: number = 0; - - if (color_transform) { - ct0 = color_transform[0]; - ct1 = color_transform[1]; - ct2 = color_transform[2]; - ct4 = color_transform[4] / 255; - ct5 = color_transform[5] / 255; - ct6 = color_transform[6] / 255; - } - - const position: CachePositionImpl | null = this._$cachePosition; - if (position) { - - switch (this._$globalCompositeOperation) { - - case "normal": - case "layer": - case "add": - case "screen": - case "alpha": - case "erase": - case "copy": - this._$blend.drawInstance( - position, - ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7, - this._$globalCompositeOperation, - this._$viewportWidth, this._$viewportHeight, - this._$matrix, - this._$imageSmoothingEnabled - ); - break; - - default: - { - const atlasTexture: WebGLTexture = this - ._$frameBufferManager - .textureManager - .getAtlasTexture(position.index); - - this._$blend.drawInstanceBlend( - atlasTexture, x_min, y_min, x_max, y_max, - ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7, - position, - this._$globalCompositeOperation, - this._$viewportWidth, this._$viewportHeight, - this._$matrix, - this._$imageSmoothingEnabled - ); - } - break; - - } - } - } - - /** - * @param {WebGLTexture} image - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} [color_transform=null] - * @return {void} - * @method - * @public - */ - drawImage ( - image: WebGLTexture, - x: number, y: number, w: number, h: number, - color_transform: Float32Array | null = null - ): void { - - let ct0: number = 1; - let ct1: number = 1; - let ct2: number = 1; - let ct4: number = 0; - let ct5: number = 0; - let ct6: number = 0; - - const ct3: number = this._$globalAlpha; - const ct7: number = 0; - - if (color_transform) { - ct0 = color_transform[0]; - ct1 = color_transform[1]; - ct2 = color_transform[2]; - ct4 = color_transform[4] / 255; - ct5 = color_transform[5] / 255; - ct6 = color_transform[6] / 255; - } - - this._$blend.drawImage( - image, x, y, w, h, - ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7, - this._$globalCompositeOperation, - this._$viewportWidth, this._$viewportHeight, - this._$matrix, - this._$imageSmoothingEnabled - ); - } - - /** - * @param {number} r - * @param {number} g - * @param {number} b - * @param {number} a - * @return {void} - * @method - * @public - */ - _$setColor ( - r: number = 0, g: number = 0, - b: number = 0, a: number = 0 - ): void { - this._$clearColorR = r; - this._$clearColorG = g; - this._$clearColorB = b; - this._$clearColorA = a; - this._$gl.clearColor(r, g, b, a); - } - - /** - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @return {void} - * @method - * @public - */ - clearRect ( - x: number, y: number, - w: number, h: number - ): void { - this._$mask._$onClearRect(); - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor(x, y, w, h); - this._$gl.clear(this._$gl.COLOR_BUFFER_BIT | this._$gl.STENCIL_BUFFER_BIT); - this._$gl.disable(this._$gl.SCISSOR_TEST); - } - - /** - * @return {void} - * @method - * @public - */ - _$clearRectStencil (): void - { - // stencilを終了 - this._$mask._$onClearRect(); - - // マスクの描画領域に限定してstencil情報をクリア - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor( - this._$maskBounds.xMin, - this._$maskBounds.yMin, - this._$maskBounds.xMax, - this._$maskBounds.yMax - ); - this._$gl.clear(this._$gl.STENCIL_BUFFER_BIT); - this._$gl.disable(this._$gl.SCISSOR_TEST); - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - moveTo (x: number, y: number): void - { - this._$path.moveTo(x, y); - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - lineTo (x: number, y: number): void - { - this._$path.lineTo(x, y); - } - - /** - * @return {void} - * @method - * @public - */ - beginPath (): void - { - this._$path.begin(); - } - - /** - * @param {number} cx - * @param {number} cy - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - quadraticCurveTo ( - cx: number, cy: number, - x: number ,y: number - ): void { - this._$path.quadTo(cx, cy, x, y); - } - - /** - * @param {number} cp1x - * @param {number} cp1y - * @param {number} cp2x - * @param {number} cp2y - * @param {number} dx - * @param {number} dy - * @return {void} - * @method - * @public - */ - bezierCurveTo ( - cp1x: number, cp1y: number, - cp2x: number, cp2y: number, - dx: number, dy: number - ): void { - this._$path.cubicTo(cp1x, cp1y, cp2x, cp2y, dx, dy); - } - - /** - * @return {void} - * @method - * @public - */ - fill (): void - { - // to triangle - const fillVertices: VerticesImpl = this._$path.vertices; - if (!fillVertices.length) { - return ; - } - - const checkVertices: any[] = $getArray(); - for (let idx: number = 0; idx < fillVertices.length; ++idx) { - - const vertices: any[] = fillVertices[idx]; - if (10 > vertices.length) { - continue; - } - - checkVertices.push(vertices); - } - - if (!checkVertices.length) { - $poolArray(checkVertices); - return ; - } - - const fillVertexArrayObject: WebGLVertexArrayObject = this._$vao.createFill(checkVertices); - - const fillStyle: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL = this.fillStyle; - - let matrix: Float32Array = this._$matrix; - let texture: WebGLTexture; - let variants: GradientShapeShaderVariantCollection | ShapeShaderVariantCollection; - let shader: CanvasToWebGLShader; - - const hasGrid: boolean = this._$grid.enabled; - if (fillStyle instanceof CanvasGradientToWebGL) { - - const stops: any[] = fillStyle.stops; - const isLinearSpace: boolean = fillStyle.rgb === "linearRGB"; - - texture = this - ._$gradientLUT - .generateForShape(stops, isLinearSpace); - - this - ._$frameBufferManager - .textureManager - .bind0(texture, true); - - this - ._$frameBufferManager - .bindRenderBuffer(); - - variants = this - ._$shaderList - .gradientShapeShaderVariants; - - if (fillStyle.type === "linear") { - - shader = variants - .getGradientShapeShader( - false, hasGrid, false, false, fillStyle.mode - ); - - variants.setGradientShapeUniform( - shader.uniform, false, 0, 0, 0, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - false, fillStyle.points, 0 - ); - - } else { - - matrix = this._$stack[this._$stack.length - 1]; - - const hasFocalPoint: boolean = fillStyle.focalPointRatio !== 0; - - shader = variants - .getGradientShapeShader( - false, hasGrid, true, hasFocalPoint, fillStyle.mode - ); - - variants.setGradientShapeUniform( - shader.uniform, false, 0, 0, 0, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - true, fillStyle.points, fillStyle.focalPointRatio - ); - - } - - } else if (fillStyle instanceof CanvasPatternToWebGL) { - - matrix = this._$stack[this._$stack.length - 1]; - - const pct: Float32Array = fillStyle.colorTransform; - - texture = fillStyle.texture; - - this - ._$frameBufferManager - .textureManager - .bind0(texture, this._$imageSmoothingEnabled); - - variants = this - ._$shaderList - .shapeShaderVariants; - - shader = variants - .getBitmapShapeShader( - false, fillStyle.repeat, hasGrid - ); - - if (pct) { - - variants.setBitmapShapeUniform( - shader.uniform, false, 0, 0, 0, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - texture.width, texture.height, - pct[0], pct[1], pct[2], this._$globalAlpha, - pct[4] / 255, pct[5] / 255, pct[6] / 255, 0 - ); - - } else { - - variants.setBitmapShapeUniform( - shader.uniform, false, 0, 0, 0, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - texture.width, texture.height, - 1, 1, 1, this._$globalAlpha, - 0, 0, 0, 0 - ); - - } - - } else { - - variants = this - ._$shaderList - .shapeShaderVariants; - - shader = variants - .getSolidColorShapeShader(false, this._$grid.enabled); - - variants.setSolidColorShapeUniform( - shader.uniform, false, 0, 0, 0, - hasGrid, matrix, - this._$viewportWidth, this._$viewportHeight, this._$grid, - fillStyle, this._$globalAlpha - ); - - } - - const coverageVariants: ShapeShaderVariantCollection = this - ._$shaderList - .shapeShaderVariants; - - const coverageShader: CanvasToWebGLShader = coverageVariants - .getMaskShapeShader(false, hasGrid); - - coverageVariants.setMaskShapeUniform( - coverageShader.uniform, hasGrid, - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5], - matrix[6], matrix[7], matrix[8], - this._$viewportWidth, this._$viewportHeight, this._$grid - ); - - // mask on - this._$gl.enable(this._$gl.STENCIL_TEST); - this._$gl.stencilMask(0xff); - - // draw shape - this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - this._$gl.stencilFunc(this._$gl.ALWAYS, 0, 0xff); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.INVERT, this._$gl.INVERT); - this._$gl.colorMask(false, false, false, false); - coverageShader._$fill(fillVertexArrayObject); - this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - - // draw shape range - this._$gl.stencilFunc(this._$gl.NOTEQUAL, 0, 0xff); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.ZERO, this._$gl.ZERO); - this._$gl.colorMask(true, true, true, true); - shader._$fill(fillVertexArrayObject); - - // mask off - this._$gl.disable(this._$gl.STENCIL_TEST); - - // release vertex array - this.releaseFillVertexArray(fillVertexArrayObject); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @return {void} - * @method - * @public - */ - releaseFillVertexArray (vertex_array: WebGLVertexArrayObject): void - { - // release shape vertex array object - this._$vao.releaseFill(vertex_array); - - const indexRanges: IndexRangeImpl[] = vertex_array.indexRanges; - for (let idx = 0; idx < indexRanges.length; ++idx) { - WebGLFillMeshGenerator - .indexRangePool - .push(indexRanges[idx]); - } - - $poolArray(indexRanges); - } - - /** - * @return {void} - * @method - * @public - */ - _$enterClip (): void - { - this._$mask._$enterClip(); - } - - /** - * @return {void} - * @method - * @public - */ - _$beginClipDef (): void - { - this._$mask._$beginClipDef(); - } - - /** - * @param {boolean} flag - * @return {void} - * @method - * @private - */ - _$updateContainerClipFlag (flag: boolean): void - { - this._$mask.containerClip = flag; - } - - /** - * @description マスク処理の開始関数 - * Mask processing start function - * - * @param {object} bounds - * @return {void} - * @method - * @public - */ - _$startClip (bounds: BoundsImpl): boolean - { - const x: number = bounds.xMin; - const y: number = bounds.yMin; - const width: number = Math.abs(bounds.xMax - bounds.xMin); - const height: number = Math.abs(bounds.yMax - bounds.yMin); - - // resize - const manager: FrameBufferManager = this._$frameBufferManager; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment) { - throw new Error("the current Attachment is null."); - } - - if (x > currentAttachment.width - || y > currentAttachment.height - ) { - return false; - } - - if (0 > x && 0 >= width + x) { - return false; - } - - if (0 > y && 0 >= height + y) { - return false; - } - - this._$maskBounds.xMin = $Math.max(0, $Math.min(this._$maskBounds.xMin, x)); - this._$maskBounds.yMin = $Math.max(0, $Math.min(this._$maskBounds.yMin, y)); - this._$maskBounds.xMax = $Math.min(currentAttachment.width, $Math.min(this._$maskBounds.xMax, width)); - this._$maskBounds.yMax = $Math.min(currentAttachment.height, $Math.min(this._$maskBounds.yMax, height)); - - return true; - } - - /** - * @return {void} - * @method - * @public - */ - _$endClipDef (): void - { - this._$mask._$endClipDef(); - } - - /** - * @return {void} - * @method - * @public - */ - _$leaveClip (): void - { - this.drawInstacedArray(); - this._$mask._$leaveClip(); - } - - /** - * @return {void} - * @method - * @public - */ - _$drawContainerClip (): void - { - this._$mask._$drawContainerClip(); - } - - /** - * @return {void} - * @method - * @public - */ - closePath (): void - { - this._$path.close(); - } - - /** - * @return {void} - * @method - * @public - */ - stroke (): void - { - - const strokeVertices: any[] = this._$path.vertices; - if (!strokeVertices.length) { - return ; - } - - const checkVertices = $getArray(); - for (let idx = 0; idx < strokeVertices.length; ++idx) { - - const vertices = strokeVertices[idx]; - if (6 > vertices.length) { - continue; - } - - checkVertices.push(vertices); - } - - if (!checkVertices.length) { - $poolArray(checkVertices); - return ; - } - - const strokeBuffer: WebGLVertexArrayObject = this._$vao.createStroke( - strokeVertices, - this.lineCap, - this.lineJoin - ); - - let matrix: Float32Array = this._$matrix; - - const strokeStyle: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL = this.strokeStyle; - - let face: number = $Math.sign(matrix[0] * matrix[4]); - if (face > 0 && matrix[1] !== 0 && matrix[3] !== 0) { - face = -$Math.sign(matrix[1] * matrix[3]); - } - - let lineWidth: number = this.lineWidth * 0.5; - let scaleX: number; - let scaleY: number; - if (this._$grid.enabled) { - // lineWidth *= $getSameScaleBase(); - scaleX = $Math.abs(this._$grid.ancestorMatrixA + this._$grid.ancestorMatrixD); - scaleY = $Math.abs(this._$grid.ancestorMatrixB + this._$grid.ancestorMatrixE); - } else { - scaleX = $Math.abs(matrix[0] + matrix[3]); - scaleY = $Math.abs(matrix[1] + matrix[4]); - } - - const scaleMin: number = $Math.min(scaleX, scaleY); - const scaleMax: number = $Math.max(scaleX, scaleY); - lineWidth *= scaleMax * (1 - 0.3 * $Math.cos($Math.PI * 0.5 * (scaleMin / scaleMax))); - lineWidth = $Math.max(1, lineWidth); - - const hasGrid: boolean = this._$grid.enabled; - - let texture: WebGLTexture; - let variants: GradientShapeShaderVariantCollection | ShapeShaderVariantCollection; - let shader: CanvasToWebGLShader; - if (strokeStyle instanceof CanvasGradientToWebGL) { - - if (strokeStyle.type === "radial") { - matrix = this._$stack[this._$stack.length - 1]; - } - - const stops: any[] = strokeStyle.stops; - const isLinearSpace: boolean = strokeStyle.rgb === "linearRGB"; - - texture = this - ._$gradientLUT - .generateForShape(stops, isLinearSpace); - - this - ._$frameBufferManager - .textureManager - .bind0(texture, true); - - variants = this - ._$shaderList - .gradientShapeShaderVariants; - - if (strokeStyle.type === "linear") { - - shader = variants - .getGradientShapeShader( - true, hasGrid, false, false, strokeStyle.mode - ); - - variants.setGradientShapeUniform( - shader.uniform, true, lineWidth, face, this.miterLimit, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - false, strokeStyle.points, 0 - ); - - } else { - - matrix = this._$stack[this._$stack.length - 1]; - - const hasFocalPoint: boolean = strokeStyle.focalPointRatio !== 0; - - shader = variants - .getGradientShapeShader( - true, hasGrid, true, hasFocalPoint, strokeStyle.mode - ); - - variants.setGradientShapeUniform( - shader.uniform, true, lineWidth, face, this.miterLimit, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - true, strokeStyle.points, strokeStyle.focalPointRatio - ); - } - - } else if (strokeStyle instanceof CanvasPatternToWebGL) { - - matrix = this._$stack[this._$stack.length - 1]; - - const pct = strokeStyle.colorTransform; - - texture = strokeStyle.texture; - - this - ._$frameBufferManager - .textureManager - .bind0(texture); - - variants = this - ._$shaderList - .shapeShaderVariants; - - shader = variants - .getBitmapShapeShader( - true, strokeStyle.repeat, this._$grid.enabled - ); - - if (pct) { - - variants.setBitmapShapeUniform( - shader.uniform, true, lineWidth, face, this.miterLimit, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - texture.width, texture.height, - pct[0], pct[1], pct[2], this._$globalAlpha, - pct[4] / 255, pct[5] / 255, pct[6] / 255, 0 - ); - - } else { - - variants.setBitmapShapeUniform( - shader.uniform, true, lineWidth, face, this.miterLimit, - hasGrid, matrix, $inverseMatrix(this._$matrix), - this._$viewportWidth, this._$viewportHeight, this._$grid, - texture.width, texture.height, - 1, 1, 1, this._$globalAlpha, - 0, 0, 0, 0 - ); - } - - } else { - - variants = this - ._$shaderList - .shapeShaderVariants; - - shader = variants - .getSolidColorShapeShader(true, this._$grid.enabled); - - variants.setSolidColorShapeUniform( - shader.uniform, true, lineWidth, face, this.miterLimit, - hasGrid, matrix, - this._$viewportWidth, this._$viewportHeight, this._$grid, - strokeStyle, this._$globalAlpha - ); - - } - - shader._$stroke(strokeBuffer); - - this._$vao.releaseStroke(strokeBuffer); - } - - /** - * @param {number} x - * @param {number} y - * @param {number} radius - * @return {void} - * @method - * @public - */ - arc (x: number, y: number, radius: number): void - { - this._$path.drawCircle(x, y, radius); - } - - /** - * @return {void} - * @method - * @public - */ - clip (): void - { - // to triangle - const fillVertices: any[] = this._$path.vertices; - if (!fillVertices.length) { - return ; - } - - const checkVertices: any[] = $getArray(); - for (let idx: number = 0; idx < fillVertices.length; ++idx) { - - const vertices: any[] = fillVertices[idx]; - if (10 > vertices.length) { - continue; - } - - checkVertices.push(vertices); - } - - if (!checkVertices.length) { - $poolArray(checkVertices); - return ; - } - - const fillVertexArrayObject: WebGLVertexArrayObject = this._$vao.createFill(checkVertices); - - // mask render - const variants: ShapeShaderVariantCollection = this - ._$shaderList - .shapeShaderVariants; - - const shader: CanvasToWebGLShader = variants.getMaskShapeShader(false, false); - - const uniform: WebGLShaderUniform = shader.uniform; - - variants.setMaskShapeUniform( - uniform, false, - this._$matrix[0], this._$matrix[1], this._$matrix[2], - this._$matrix[3], this._$matrix[4], this._$matrix[5], - this._$matrix[6], this._$matrix[7], this._$matrix[8], - this._$viewportWidth, this._$viewportHeight, null - ); - - if (this._$mask._$onClip(fillVertexArrayObject, - this._$matrix, - this._$viewportWidth, - this._$viewportHeight - )) { - return; - } - - shader._$fill(fillVertexArrayObject); - - this.beginPath(); - } - - /** - * @return {void} - * @method - * @public - */ - save (): void - { - // matrix - const matrix: Float32Array = this._$matrix; - - this._$stack.push($getFloat32Array9( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5], - matrix[6], matrix[7], matrix[8] - )); - - // mask - this._$mask._$onSave(); - } - - /** - * @return {void} - * @method - * @public - */ - restore (): void - { - //matrix - if (this._$stack.length) { - $poolFloat32Array9(this._$matrix); - this._$matrix = this._$stack.pop() || $getFloat32Array9(); - } - - // mask - this._$mask._$onRestore(); - } - - /** - * @param {WebGLTexture} texture - * @param {boolean} repeat - * @param {Float32Array} color_transform - * @return {CanvasPatternToWebGL} - * @method - * @public - */ - createPattern ( - texture: WebGLTexture, - repeat: boolean, - color_transform: Float32Array - ): CanvasPatternToWebGL { - return new CanvasPatternToWebGL(texture, repeat, color_transform); - } - - /** - * @param {number} x0 - * @param {number} y0 - * @param {number} x1 - * @param {number} y1 - * @param {string} [rgb=InterpolationMethod.RGB] - * @param {string} [mode=SpreadMethod.PAD] - * @return {CanvasGradientToWebGL} - * @method - * @public - */ - createLinearGradient ( - x0: number, y0: number, x1: number, y1: number, - rgb: InterpolationMethodImpl = "rgb", - mode: SpreadMethodImpl = "pad" - ): CanvasGradientToWebGL { - return new CanvasGradientToWebGL() - .linear(x0, y0, x1, y1, rgb, mode); - } - - /** - * @param {number} x0 - * @param {number} y0 - * @param {number} r0 - * @param {number} x1 - * @param {number} y1 - * @param {number} r1 - * @param {string} [rgb=InterpolationMethod.RGB] - * @param {string} [mode=SpreadMethod.PAD] - * @param {number} [focal_point_ratio=0] - * @return {CanvasGradientToWebGL} - * @method - * @public - */ - createRadialGradient ( - x0: number, y0: number, r0: number, - x1: number, y1: number, r1: number, - rgb: InterpolationMethodImpl = "rgb", - mode: SpreadMethodImpl = "pad", - focal_point_ratio: number = 0 - ): CanvasGradientToWebGL { - return new CanvasGradientToWebGL() - .radial( - x0, y0, r0, x1, y1, r1, - rgb, mode, focal_point_ratio - ); - } - - /** - * @param {WebGLTexture} texture - * @param {boolean} is_horizontal - * @param {number} blur - * @return {void} - * @method - * @public - */ - _$applyBlurFilter ( - texture: WebGLTexture, - is_horizontal: boolean, - blur: number - ): void { - - const manager: FrameBufferManager = this._$frameBufferManager; - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment) { - throw new Error("the current attachment is null."); - } - - const width: number = currentAttachment.width; - const height: number = currentAttachment.height; - - manager - .textureManager - .bind0(texture, true); - - const halfBlur: number = $Math.ceil(blur * 0.5); - const fraction: number = 1 - (halfBlur - blur * 0.5); - const samples: number = 1 + blur; - - const variants: FilterShaderVariantCollection = this - ._$shaderList - .filterShaderVariants; - - const shader: CanvasToWebGLShader = variants - .getBlurFilterShader(halfBlur); - - variants - .setBlurFilterUniform( - shader.uniform, - width, height, - is_horizontal, fraction, samples - ); - - shader._$drawImage(); - } - - /** - * @param {WebGLTexture} texture - * @param {number} width - * @param {number} height - * @param {number} base_width - * @param {number} base_height - * @param {number} base_offset_x - * @param {number} base_offset_y - * @param {number} blur_width - * @param {number} blur_height - * @param {number} blur_offset_x - * @param {number} blur_offset_y - * @param {boolean} is_glow - * @param {string} type - * @param {boolean} knockout - * @param {number} strength - * @param {array | null} [ratios = null] - * @param {array | null} [colors = null] - * @param {array | null} [alphas = null] - * @param {number} [color_r1 = 0] - * @param {number} [color_g1 = 0] - * @param {number} [color_b1 = 0] - * @param {number} [color_a1 = 0] - * @param {number} [color_r2 = 0] - * @param {number} [color_g2 = 0] - * @param {number} [color_b2 = 0] - * @param {number} [color_a2 = 0] - * @return {void} - * @method - * @public - */ - _$applyBitmapFilter ( - texture: WebGLTexture, - width: number, height: number, - base_width: number, base_height: number, - base_offset_x: number, base_offset_y: number, - blur_width: number, blur_height: number, - blur_offset_x: number, blur_offset_y: number, - is_glow: boolean, type: string, - knockout: boolean, strength: number, - ratios: number[] | null = null, - colors: number[] | null = null, - alphas: number[] | null = null, - color_r1: number = 0, color_g1: number = 0, - color_b1: number = 0, color_a1: number = 0, - color_r2: number = 0, color_g2: number = 0, - color_b2: number = 0, color_a2: number = 0 - ): void { - - const manager: FrameBufferManager = this._$frameBufferManager; - const isInner: boolean = type === "inner"; - - const baseAttachment = manager.currentAttachment; - const baseTexture = manager.getTextureFromCurrentAttachment(); - - let lut: WebGLTexture | null = null; - - const isGradient: boolean = ratios !== null && colors !== null && alphas !== null; - - if (ratios !== null && colors !== null && alphas !== null) { - lut = this - ._$gradientLUT - .generateForFilter(ratios, colors, alphas); - } - - let targetTextureAttachment; - if (isInner) { - - if (isGradient && lut) { - - manager - .textureManager - .bind02(texture, lut, true); - - } else { - - manager - .textureManager - .bind0(texture); - - } - - } else { - - targetTextureAttachment = this - ._$frameBufferManager - .createTextureAttachment(width, height); - - this._$bind(targetTextureAttachment); - - if (isGradient && lut) { - - manager - .textureManager - .bind012(texture, baseTexture, lut, true); - - } else { - - manager - .textureManager - .bind01(texture, baseTexture); - - } - } - - const transformsBase: boolean = !(isInner || type === "full" && knockout); - const transformsBlur: boolean = !(width === blur_width && height === blur_height && blur_offset_x === 0 && blur_offset_y === 0); - const appliesStrength: boolean = !(strength === 1); - - const variants: FilterShaderVariantCollection = this - ._$shaderList - .filterShaderVariants; - - const shader: CanvasToWebGLShader = variants.getBitmapFilterShader( - transformsBase, transformsBlur, - is_glow, type, knockout, - appliesStrength, isGradient - ); - - variants.setBitmapFilterUniform( - shader.uniform, width, height, - base_width, base_height, base_offset_x, base_offset_y, - blur_width, blur_height, blur_offset_x, blur_offset_y, - is_glow, strength, - color_r1, color_g1, color_b1, color_a1, - color_r2, color_g2, color_b2, color_a2, - transformsBase, transformsBlur, appliesStrength, isGradient - ); - - if (!isInner) { - - this._$blend.toOneZero(); - - } else if (knockout) { - - this._$blend.toSourceIn(); - - } else { - - this._$blend.toSourceAtop(); - - } - - shader._$drawImage(); - - if (!isInner) { - manager - .releaseAttachment(baseAttachment, true); - } - } - - /** - * @param {WebGLTexture} texture - * @param {array} matrix - * @return {void} - * @method - * @public - */ - _$applyColorMatrixFilter ( - texture: WebGLTexture, - matrix: number[] - ): void { - - this - ._$frameBufferManager - .textureManager - .bind0(texture, true); - - const variants: FilterShaderVariantCollection = this - ._$shaderList - .filterShaderVariants; - - const shader = variants - .getColorMatrixFilterShader(); - - variants - .setColorMatrixFilterUniform( - shader.uniform, matrix - ); - - this._$blend.reset(); - - shader._$drawImage(); - } - - /** - * @param {WebGLTexture} texture - * @param {number} matrix_x - * @param {number} matrix_y - * @param {Float32Array} matrix - * @param {number} divisor - * @param {number} bias - * @param {boolean} preserve_alpha - * @param {boolean} clamp - * @param {number} color_r - * @param {number} color_g - * @param {number} color_b - * @param {number} color_a - * @return {void} - * @method - * @public - */ - _$applyConvolutionFilter ( - texture: WebGLTexture, - matrix_x: number, matrix_y: number, - matrix: number[], - divisor: number, bias: number, - preserve_alpha: boolean, clamp: boolean, - color_r: number, color_g: number, - color_b: number, color_a: number - ): void { - - const width: number = texture.width; - const height: number = texture.height; - - const targetTextureAttachment: AttachmentImpl = this - ._$frameBufferManager - .createTextureAttachment(width, height); - - this._$bind(targetTextureAttachment); - - this - ._$frameBufferManager - .textureManager - .bind0(texture, true); - - const variants: FilterShaderVariantCollection = this - ._$shaderList - .filterShaderVariants; - - const shader = variants - .getConvolutionFilterShader( - matrix_x, matrix_y, preserve_alpha, clamp - ); - - variants.setConvolutionFilterUniform( - shader.uniform, - width, height, matrix, divisor, bias, clamp, - color_r, color_g, color_b, color_a - ); - - this._$blend.reset(); - - shader._$drawImage(); - } - - /** - * @param {WebGLTexture} texture - * @param {HTMLImageElement} map - * @param {number} base_width - * @param {number} base_height - * @param {PointImpl} [point=null] - * @param {number} component_x - * @param {number} component_y - * @param {number} scale_x - * @param {number} scale_y - * @param {string} mode - * @param {number} color_r - * @param {number} color_g - * @param {number} color_b - * @param {number} color_a - * @return {void} - * @method - * @private - */ - _$applyDisplacementMapFilter ( - texture: WebGLTexture, - map: HTMLImageElement, - base_width: number, base_height: number, - point: PointImpl | null, - component_x: number, component_y: number, - scale_x: number, scale_y: number, - mode: string, - color_r: number, color_g: number, - color_b: number, color_a: number - ): void { - - const width: number = texture.width; - const height: number = texture.height; - - const targetTextureAttachment: AttachmentImpl = this - ._$frameBufferManager - .createTextureAttachment(width, height); - - this._$bind(targetTextureAttachment); - - if (!point) { - point = { "x": 0, "y": 0 }; - } - - const mapTexture: WebGLTexture = this - ._$frameBufferManager - .createTextureFromImage(map); - - this - ._$frameBufferManager - .textureManager - .bind01(texture, mapTexture); - - const variants: FilterShaderVariantCollection = this - ._$shaderList - .filterShaderVariants; - - const shader: CanvasToWebGLShader = variants - .getDisplacementMapFilterShader( - component_x, component_y, mode - ); - - variants.setDisplacementMapFilterUniform( - shader.uniform, map.width, map.height, base_width, base_height, - point.x, point.y, scale_x, scale_y, mode, - color_r, color_g, color_b, color_a - ); - - this._$blend.reset(); - - shader._$drawImage(); - - this - ._$frameBufferManager - .releaseTexture(mapTexture); - } - - /** - * @param {BoundsImpl} position - * @return {void} - * @method - * @private - */ - _$startLayer (position: BoundsImpl): void - { - this._$positions.push(position); - this._$blends.push(this._$isLayer); - this._$isLayer = true; - } - - /** - * @return {void} - * @method - * @private - */ - _$endLayer (): void - { - const bounds: BoundsImpl | void = this._$positions.pop(); - if (bounds) { - $poolBoundsObject(bounds); - } - - this._$isLayer = !!this._$blends.pop(); - } - - /** - * @param {number} width - * @param {number} height - * @param {boolean} [multisample=false] - * @return {void} - * @method - * @private - */ - _$saveAttachment ( - width: number, height: number, - multisample: boolean = false - ): void { - - this.drawInstacedArray(); - - const manager: FrameBufferManager = this._$frameBufferManager; - - this - ._$attachmentArray - .push(manager.currentAttachment); - - this._$bind( - manager.createCacheAttachment(width, height, multisample) - ); - } - - /** - * @param {boolean} [release_texture = false] - * @return {void} - * @method - * @private - */ - _$restoreAttachment (release_texture: boolean = false): void - { - const manager: FrameBufferManager = this._$frameBufferManager; - - manager.releaseAttachment( - manager.currentAttachment, release_texture - ); - - this._$bind( - this._$attachmentArray.pop() - ); - } - - /** - * @return {object} - * @method - * @private - */ - getCurrentPosition (): BoundsImpl - { - return this._$positions[this._$positions.length - 1]; - } - - /** - * @description 最大テクスチャサイズを超えないスケール値を取得する - * @param {number} width - * @param {number} height - * @return {number} - * @method - * @public - */ - textureScale (width: number, height: number): number - { - const maxSize = $Math.max(width, height); - if (maxSize > this._$maxTextureSize) { - return this._$maxTextureSize / maxSize; - } - return 1; - } -} diff --git a/packages/webgl/src/CanvasToWebGLContextBlend.ts b/packages/webgl/src/CanvasToWebGLContextBlend.ts deleted file mode 100644 index bac6c241..00000000 --- a/packages/webgl/src/CanvasToWebGLContextBlend.ts +++ /dev/null @@ -1,684 +0,0 @@ -import type { CanvasToWebGLContext } from "./CanvasToWebGLContext"; -import type { FrameBufferManager } from "./FrameBufferManager"; -import type { CanvasToWebGLShader } from "./shader/CanvasToWebGLShader"; -import type { WebGLShaderUniform } from "./shader/WebGLShaderUniform"; -import type { WebGLShaderInstance } from "./shader/WebGLShaderInstance"; -import type { BlendShaderVariantCollection } from "./shader/variants/BlendShaderVariantCollection"; -import type { BlendModeImpl } from "./interface/BlendModeImpl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { CachePositionImpl } from "./interface/CachePositionImpl"; -import { - $Math, - $Number, - $inverseMatrix -} from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContextBlend -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private _$enabled: boolean; - private _$funcCode: number; - private _$currentOperation: BlendModeImpl; - private _$currentIndex: number; - private _$currentSmoothing: boolean | null; - private _$variants: BlendShaderVariantCollection; - private _$instanceShader: CanvasToWebGLShader; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$enabled = false; - - /** - * @type {number} - * @default 600 - * @private - */ - this._$funcCode = 600; - - /** - * @type {string} - * @private - */ - this._$currentOperation = "normal"; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$currentIndex = -1; - - /** - * @type {boolean} - * @default null - * @private - */ - this._$currentSmoothing = null; - - /** - * @type {BlendShaderVariantCollection} - * @private - */ - this._$variants = context.shaderList.blendShaderVariants; - - /** - * @type {CanvasToWebGLShader} - * @private - */ - this._$instanceShader = this._$variants.getInstanceShader(); - - // start - this.enable(); - } - - /** - * @return {void} - * @method - * @public - */ - enable (): void - { - if (!this._$enabled) { - this._$enabled = true; - this._$gl.enable(this._$gl.BLEND); - } - - this.reset(); - } - - /** - * @return {void} - * @method - * @public - */ - disable (): void - { - if (this._$enabled) { - this._$enabled = false; - this._$gl.disable(this._$gl.BLEND); - } - } - - /** - * @return {void} - * @method - * @public - */ - reset (): void - { - if (this._$funcCode !== 613) { - this._$funcCode = 613; - this._$gl.blendFunc(this._$gl.ONE, this._$gl.ONE_MINUS_SRC_ALPHA); - } - } - - /** - * @return {void} - * @method - * @public - */ - toOneZero (): void - { - if (this._$funcCode !== 610) { - this._$funcCode = 610; - this._$gl.blendFunc(this._$gl.ONE, this._$gl.ZERO); - } - } - - /** - * @return {void} - * @method - * @public - */ - toZeroOne (): void - { - if (this._$funcCode !== 601) { - this._$funcCode = 601; - this._$gl.blendFuncSeparate( - this._$gl.ZERO, this._$gl.ONE, - this._$gl.ONE, this._$gl.ZERO - ); - } - } - - /** - * @return {void} - * @method - * @public - */ - toAdd (): void - { - if (this._$funcCode !== 611) { - this._$funcCode = 611; - this._$gl.blendFunc(this._$gl.ONE, this._$gl.ONE); - } - } - - /** - * @return {void} - * @method - * @public - */ - toScreen (): void - { - if (this._$funcCode !== 641) { - this._$funcCode = 641; - this._$gl.blendFunc(this._$gl.ONE_MINUS_DST_COLOR, this._$gl.ONE); - } - } - - /** - * @return {void} - * @method - * @public - */ - toAlpha (): void - { - if (this._$funcCode !== 606) { - this._$funcCode = 606; - this._$gl.blendFunc(this._$gl.ZERO, this._$gl.SRC_ALPHA); - } - } - - /** - * @return {void} - * @method - * @public - */ - toErase (): void - { - if (this._$funcCode !== 603) { - this._$funcCode = 603; - this._$gl.blendFunc(this._$gl.ZERO, this._$gl.ONE_MINUS_SRC_ALPHA); - } - } - - /** - * @return {void} - * @method - * @public - */ - toSourceAtop (): void - { - if (this._$funcCode !== 673) { - this._$funcCode = 673; - this._$gl.blendFunc(this._$gl.DST_ALPHA, this._$gl.ONE_MINUS_SRC_ALPHA); - } - } - - /** - * @return {void} - * @method - * @public - */ - toSourceIn (): void - { - if (this._$funcCode !== 670) { - this._$funcCode = 670; - this._$gl.blendFunc(this._$gl.DST_ALPHA, this._$gl.ZERO); - } - } - - /** - * @param {string} operation - * @return {void} - * @method - * @public - */ - toOperation (operation: BlendModeImpl): void - { - switch (operation) { - - case "add": - this.toAdd(); - break; - - case "screen": - this.toScreen(); - break; - - case "alpha": - this.toAlpha(); - break; - - case "erase": - this.toErase(); - break; - - case "copy": - this.toOneZero(); - break; - - default: - this.reset(); - break; - - } - } - - /** - * @return {void} - * @method - * @public - */ - clearInstacedArray (): void - { - const instance: WebGLShaderInstance = this._$instanceShader.instance; - if (!instance.count) { - return ; - } - - instance.clear(); - } - - /** - * @return {void} - * @method - * @public - */ - drawInstacedArray (): void - { - const instance: WebGLShaderInstance = this._$instanceShader.instance; - if (!instance.count) { - return ; - } - - const manager: FrameBufferManager = this._$context.frameBuffer; - const texture: WebGLTexture = manager - .textureManager - .getAtlasTexture(this._$currentIndex); - - manager - .textureManager - .bind0(texture, this._$currentSmoothing); - - this.toOperation(this._$currentOperation); - this - ._$instanceShader - .drawArraysInstanced(instance); - - instance.clear(); - } - - /** - * @param {object} position - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @param {string} operation - * @param {number} render_width - * @param {number} render_height - * @param {Float32Array} matrix - * @param {boolean} smoothing - * @return {void} - * @method - * @public - */ - drawInstance ( - position: CachePositionImpl, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number, - operation: BlendModeImpl, - render_width: number, render_height: number, - matrix: Float32Array, smoothing: boolean - ): void { - - if (!this._$currentOperation) { - this._$currentOperation = operation; - } - - if (this._$currentIndex === -1) { - this._$currentIndex = position.index; - } - - if (this._$currentSmoothing === null) { - this._$currentSmoothing = smoothing; - } - - if (this._$currentOperation !== operation - || this._$currentIndex !== position.index - || this._$currentSmoothing !== smoothing - ) { - - // execute - this.drawInstacedArray(); - - // update - this._$currentOperation = operation; - this._$currentIndex = position.index; - this._$currentSmoothing = smoothing; - } - - this._$variants.pushNormalBlend( - this._$instanceShader.instance, - position.x, position.y, position.w, position.h, - matrix, render_width, render_height, - ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7 - ); - } - - /** - * @param {WebGLTexture} atlas_texture - * @param {number} x_min - * @param {number} y_min - * @param {number} x_max - * @param {number} y_max - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @param {object} cache_position - * @param {string} operation - * @param {number} render_width - * @param {number} render_height - * @param {Float32Array} matrix - * @param {boolean} smoothing - */ - drawInstanceBlend ( - atlas_texture: WebGLTexture, - x_min: number, y_min: number, x_max: number, y_max: number, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number, - cache_position: CachePositionImpl, - operation: BlendModeImpl, - render_width: number, render_height: number, - matrix: Float32Array, smoothing: boolean - ): void { - - this.drawInstacedArray(); - - const manager: FrameBufferManager = this._$context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const withCT: boolean = - ct0 !== 1 || ct1 !== 1 || ct2 !== 1 || ct3 !== 1 || - ct4 !== 0 || ct5 !== 0 || ct6 !== 0 || ct7 !== 0 - ; - - const texture: WebGLTexture = manager - .getTextureFromCurrentAttachment(); - - const backTextureAttachment: AttachmentImpl = this - ._$context - .frameBuffer - .createTextureAttachment(cache_position.w, cache_position.h); - - this - ._$context - ._$bind(backTextureAttachment); - - manager - .textureManager - .bind0(texture); - - const clipShader: CanvasToWebGLShader = this._$variants.getClipShader(); - const clipUniform: WebGLShaderUniform = clipShader.uniform; - this._$variants.setClipUniform( - clipUniform, 0, 0, cache_position.w, cache_position.h, - $inverseMatrix(matrix), render_width, render_height - ); - - this.reset(); - clipShader._$drawImage(); - - const backTexture = manager - .getTextureFromCurrentAttachment(); - - this._$context._$bind(currentAttachment); - - manager - .textureManager - .bind01(backTexture, atlas_texture, smoothing); - - const shader: CanvasToWebGLShader = this - ._$variants - .getInstanceBlendShader(operation, withCT); - - this._$variants.setInstanceBlendUniform( - shader.uniform, - cache_position.x / atlas_texture.width, - cache_position.y / atlas_texture.height, - cache_position.w / atlas_texture.width, - cache_position.h / atlas_texture.height, - cache_position.w, cache_position.h, - matrix, render_width, render_height, - withCT, ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7 - ); - - const width: number = $Math.abs(x_max - x_min); - const height: number = $Math.abs(y_max - y_min); - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor(x_min, render_height - (y_min + height), width, height); - - this.toOneZero(); - shader._$drawImage(); - - this._$gl.disable(this._$gl.SCISSOR_TEST); - - manager.releaseAttachment(backTextureAttachment, true); - } - - /** - * @param {WebGLTexture} image - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @param {string} operation - * @param {number} renderWidth - * @param {number} renderHeight - * @param {Float32Array} matrix - * @param {boolean} imageSmoothingEnabled - * @return {void} - * @method - * @public - */ - drawImage ( - image: WebGLTexture, - x: number, y: number, w: number, h: number, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number, - operation: BlendModeImpl, - renderWidth: number, renderHeight: number, - matrix: Float32Array, - imageSmoothingEnabled: boolean - ): void { - - const manager: FrameBufferManager = this._$context.frameBuffer; - const currentBuffer: AttachmentImpl | null = manager.currentAttachment; - - const withCT: boolean = - ct0 !== 1 || ct1 !== 1 || ct2 !== 1 || ct3 !== 1 || - ct4 !== 0 || ct5 !== 0 || ct6 !== 0 || ct7 !== 0 - ; - - switch (operation) { - - case "normal": - case "layer": - case "add": - case "screen": - case "alpha": - case "erase": - case "copy": - { - manager.textureManager.bind0(image, imageSmoothingEnabled); - - const shader: CanvasToWebGLShader = this._$variants.getNormalBlendShader(withCT); - this._$variants.setNormalBlendUniform( - shader.uniform, x, y, w, h, matrix, renderWidth, renderHeight, - withCT, ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7 - ); - - const a: number = matrix[0]; - const b: number = matrix[1]; - const c: number = matrix[3]; - const d: number = matrix[4]; - const tx: number = matrix[6]; - const ty: number = matrix[7]; - - if (a !== 1 || b !== 0 || c !== 0 || d !== 1) { - - const left: number = x; - const right: number = x + w; - const top: number = y; - const bottom: number = y + h; - - const x0: number = +(right * a + bottom * c + tx); - const x1: number = +(right * a + top * c + tx); - const x2: number = +(left * a + bottom * c + tx); - const x3: number = +(left * a + top * c + tx); - const y0: number = +(right * b + bottom * d + ty); - const y1: number = +(right * b + top * d + ty); - const y2: number = +(left * b + bottom * d + ty); - const y3: number = +(left * b + top * d + ty); - - const no: number = $Number.MAX_VALUE; - const xMin: number = +$Math.min($Math.min($Math.min($Math.min( no, x0), x1), x2), x3); - const xMax: number = +$Math.max($Math.max($Math.max($Math.max(-no, x0), x1), x2), x3); - const yMin: number = +$Math.min($Math.min($Math.min($Math.min( no, y0), y1), y2), y3); - const yMax: number = +$Math.max($Math.max($Math.max($Math.max(-no, y0), y1), y2), y3); - - const sx: number = $Math.max(0, xMin); - const sy: number = $Math.max(0, yMin); - const sw: number = $Math.min($Math.max(0, renderWidth - sx), $Math.ceil($Math.abs(xMax - xMin))); - const sh: number = $Math.min($Math.max(0, renderHeight - sy), $Math.ceil($Math.abs(yMax - yMin))); - - if (!sw || !sh) { - return ; - } - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor(sx, $Math.max(0, renderHeight - (sy + sh)), sw + 1, sh + 1); - - } else { - - const sx: number = $Math.max(0, x + tx); - const sy: number = $Math.max(0, y + ty); - const sw: number = $Math.min($Math.max(0, renderWidth - sx), w); - const sh: number = $Math.min($Math.max(0, renderHeight - sy), h); - - if (!sw || !sh) { - return ; - } - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor(sx, $Math.max(0, renderHeight - (sy + sh)), sw + 1, sh + 1); - } - - this.toOperation(operation); - shader._$drawImage(); - - this._$gl.disable(this._$gl.SCISSOR_TEST); - } - break; - - default: - { - const sx: number = $Math.max(0, x + matrix[6]); - const sy: number = $Math.max(0, y + matrix[7]); - const sw: number = $Math.min($Math.max(0, renderWidth - sx), w); - const sh: number = $Math.min($Math.max(0, renderHeight - sy), h); - - if (!sw || !sh) { - return ; - } - - const texture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - - const backTextureAttachment: AttachmentImpl = this - ._$context - .frameBuffer - .createTextureAttachment(w, h); - - this._$context._$bind(backTextureAttachment); - manager.textureManager.bind0(texture); - - const clipShader: CanvasToWebGLShader = this._$variants.getClipShader(); - const clipUniform: WebGLShaderUniform = clipShader.uniform; - this._$variants.setClipUniform( - clipUniform, x, y, w, h, - $inverseMatrix(matrix), renderWidth, renderHeight - ); - - this.reset(); - clipShader._$drawImage(); - - const backTexture = manager - .getTextureFromCurrentAttachment(); - - this._$context._$bind(currentBuffer); - - manager.textureManager.bind01(backTexture, image, imageSmoothingEnabled); - - const shader: CanvasToWebGLShader = this - ._$variants - .getBlendShader(operation, withCT); - - this._$variants.setBlendUniform( - shader.uniform, x, y, w, h, - matrix, renderWidth, renderHeight, - withCT, ct0, ct1, ct2, ct3, ct4, ct5, ct6, ct7 - ); - - this._$gl.enable(this._$gl.SCISSOR_TEST); - this._$gl.scissor(sx, $Math.max(0, renderHeight - (sy + sh)), sw, sh); - - this.toOneZero(); - shader._$drawImage(); - - this._$gl.disable(this._$gl.SCISSOR_TEST); - - manager.releaseAttachment(backTextureAttachment, true); - - } - break; - - } - } -} \ No newline at end of file diff --git a/packages/webgl/src/CanvasToWebGLContextGrid.ts b/packages/webgl/src/CanvasToWebGLContextGrid.ts deleted file mode 100644 index 4ae654a1..00000000 --- a/packages/webgl/src/CanvasToWebGLContextGrid.ts +++ /dev/null @@ -1,372 +0,0 @@ -import type { BoundsImpl } from "./interface/BoundsImpl"; -import type { GridImpl } from "./interface/GridImpl"; -import { $Math } from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContextGrid -{ - public enabled: boolean; - public parentMatrixA: number; - public parentMatrixB: number; - public parentMatrixC: number; - public parentMatrixD: number; - public parentMatrixE: number; - public parentMatrixF: number; - public parentMatrixG: number; - public parentMatrixH: number; - public parentMatrixI: number; - public ancestorMatrixA: number; - public ancestorMatrixB: number; - public ancestorMatrixC: number; - public ancestorMatrixD: number; - public ancestorMatrixE: number; - public ancestorMatrixF: number; - public ancestorMatrixG: number; - public ancestorMatrixH: number; - public ancestorMatrixI: number; - public parentViewportX: number; - public parentViewportY: number; - public parentViewportW: number; - public parentViewportH: number; - public minXST: number; - public minYST: number; - public minXPQ: number; - public minYPQ: number; - public maxXST: number; - public maxYST: number; - public maxXPQ: number; - public maxYPQ: number; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {boolean} - * @default false - * @public - */ - this.enabled = false; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixA = 1; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixB = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixC = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixD = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixE = 1; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixF = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixG = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixH = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.parentMatrixI = 1; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixA = 1; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixB = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixC = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixD = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixE = 1; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixF = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixG = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixH = 0; - - /** - * @type {number} - * @default 1 - * @public - */ - this.ancestorMatrixI = 1; - - /** - * @type {number} - * @default 0 - * @public - */ - this.parentViewportX = 0; - - /** - * @type {number} - * @default 0 - * @public - */ - this.parentViewportY = 0; - - /** - * @type {number} - * @default 0 - * @public - */ - this.parentViewportW = 0; - - /** - * @type {number} - * @default 0 - * @public - */ - this.parentViewportH = 0; - - /** - * @type {number} - * @default 0.00001 - * @public - */ - this.minXST = 0.00001; - - /** - * @type {number} - * @default 0.00001 - * @public - */ - this.minYST = 0.00001; - - /** - * @type {number} - * @default 0.00001 - * @public - */ - this.minXPQ = 0.00001; - - /** - * @type {number} - * @default 0.00001 - * @public - */ - this.minYPQ = 0.00001; - - /** - * @type {number} - * @default 0.99999 - * @public - */ - this.maxXST = 0.99999; - - /** - * @type {number} - * @default 0.99999 - * @public - */ - this.maxYST = 0.99999; - - /** - * @type {number} - * @default 0.99999 - * @public - */ - this.maxXPQ = 0.99999; - - /** - * @type {number} - * @default 0.99999 - * @public - */ - this.maxYPQ = 0.99999; - } - - /** - * @param {number} x - * @param {number} y - * @param {number} width - * @param {number} height - * @param {object} bounds - * @param {object} grid - * @param {number} same_scale - * @param {number} parentA - * @param {number} parentB - * @param {number} parentC - * @param {number} parentD - * @param {number} parentE - * @param {number} parentF - * @param {number} ancestorA - * @param {number} ancestorB - * @param {number} ancestorC - * @param {number} ancestorD - * @param {number} ancestorE - * @param {number} ancestorF - * @return {void} - * @method - * @public - */ - enable ( - x: number, y: number, width: number, height: number, - bounds: BoundsImpl, grid: GridImpl, same_scale: number, - parentA: number, parentB: number, parentC: number, - parentD: number, parentE: number, parentF: number, - ancestorA: number, ancestorB: number, ancestorC: number, - ancestorD: number, ancestorE: number, ancestorF: number - ): void { - - const boundsWidth: number = bounds.xMax - bounds.xMin; - const boundsHeight: number = bounds.yMax - bounds.yMin; - const gridWidth: number = grid.w; - const gridHeight: number = grid.h; - - const sameWidth: number = $Math.abs($Math.ceil(boundsWidth * same_scale)); - const sameHeight: number = $Math.abs($Math.ceil(boundsHeight * same_scale)); - - // 等倍サイズでの正規化grid - const minXST: number = gridWidth > 0 ? (grid.x - bounds.xMin) / boundsWidth : 0.00001; - const minYST: number = gridHeight > 0 ? (grid.y - bounds.yMin) / boundsHeight : 0.00001; - const maxXST: number = gridWidth > 0 ? (grid.x + grid.w - bounds.xMin) / boundsWidth : 0.99999; - const maxYST: number = gridHeight > 0 ? (grid.y + grid.h - bounds.yMin) / boundsHeight : 0.99999; - - // 現在サイズでの正規化grid - let minXPQ: number = sameWidth * minXST / width; - let minYPQ: number = sameHeight * minYST / height; - let maxXPQ: number = (width - sameWidth * (1 - maxXST)) / width; - let maxYPQ: number = (height - sameHeight * (1 - maxYST)) / height; - - if (minXPQ >= maxXPQ) { - const m: number = minXST / (minXST + (1 - maxXST)); - minXPQ = $Math.max(m - 0.00001, 0); - maxXPQ = $Math.min(m + 0.00001, 1); - } - - if (minYPQ >= maxYPQ) { - const m: number = minYST / (minYST + (1 - maxYST)); - minYPQ = $Math.max(m - 0.00001, 0); - maxYPQ = $Math.min(m + 0.00001, 1); - } - - this.enabled = true; - - this.parentMatrixA = parentA; - this.parentMatrixB = parentB; - this.parentMatrixD = parentC; - this.parentMatrixE = parentD; - this.parentMatrixG = parentE; - this.parentMatrixH = parentF; - - this.ancestorMatrixA = ancestorA; - this.ancestorMatrixB = ancestorB; - this.ancestorMatrixD = ancestorC; - this.ancestorMatrixE = ancestorD; - this.ancestorMatrixG = ancestorE; - this.ancestorMatrixH = ancestorF; - - this.parentViewportX = x; - this.parentViewportY = y; - this.parentViewportW = width; - this.parentViewportH = height; - - this.minXST = minXST; - this.minYST = minYST; - this.minXPQ = minXPQ; - this.minYPQ = minYPQ; - - this.maxXST = maxXST; - this.maxYST = maxYST; - this.maxXPQ = maxXPQ; - this.maxYPQ = maxYPQ; - } - - /** - * @return void - * @method - * @public - */ - disable (): void - { - this.enabled = false; - } -} \ No newline at end of file diff --git a/packages/webgl/src/CanvasToWebGLContextMask.ts b/packages/webgl/src/CanvasToWebGLContextMask.ts deleted file mode 100644 index e3cabd8f..00000000 --- a/packages/webgl/src/CanvasToWebGLContextMask.ts +++ /dev/null @@ -1,542 +0,0 @@ -import { WebGLFillMeshGenerator } from "./WebGLFillMeshGenerator"; -import type { CanvasToWebGLContext } from "./CanvasToWebGLContext"; -import type { ShapeShaderVariantCollection } from "./shader/variants/ShapeShaderVariantCollection"; -import type { CanvasToWebGLShader } from "./shader/CanvasToWebGLShader"; -import type { WebGLShaderUniform } from "./shader/WebGLShaderUniform"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { ClipObjectImpl } from "./interface/ClipObjectImpl"; -import type { IndexRangeImpl } from "./interface/IndexRangeImpl"; -import { $poolArray } from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContextMask -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$clips: boolean[]; - private readonly _$poolClip: ClipObjectImpl[]; - private _$clipStatus: boolean; - private _$containerClip: boolean; - private _$currentClip: boolean; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$clips = []; - - /** - * @type {array} - * @private - */ - this._$poolClip = []; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$clipStatus = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$containerClip = false; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$currentClip = false; - } - - /** - * @member {boolean} - * @public - */ - get containerClip (): boolean - { - return this._$containerClip; - } - set containerClip (flag: boolean) - { - this._$containerClip = flag; - } - - /** - * @param {boolean} mask - * @return {void} - * @method - * @private - */ - _$onClear (mask: boolean): void - { - if (mask) { - this._$gl.enable(this._$gl.STENCIL_TEST); - this._$currentClip = true; - } - } - - /** - * @param {boolean} mask - * @return {void} - * @method - * @private - */ - _$onBind (mask: boolean): void - { - if (!mask && this._$currentClip) { - // キャッシュ作成前は、一旦マスクを無効にする - this._$gl.disable(this._$gl.STENCIL_TEST); - this._$currentClip = false; - } else if (mask && !this._$currentClip) { - // キャッシュ作成後は、マスクの状態を復元する - this._$gl.enable(this._$gl.STENCIL_TEST); - this._$currentClip = true; - this._$endClipDef(); - } - } - - /** - * @return {void} - * @method - * @private - */ - _$onClearRect (): void - { - this._$gl.disable(this._$gl.STENCIL_TEST); - this._$currentClip = false; - } - - /** - * @return {void} - * @method - * @private - */ - _$enterClip () - { - if (!this._$currentClip) { - this._$gl.enable(this._$gl.STENCIL_TEST); - this._$currentClip = true; - } - - // buffer mask on - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - if (!currentAttachment) { - throw new Error("mask currentAttachment is null."); - } - - currentAttachment.mask = true; - ++currentAttachment.clipLevel; - } - - /** - * @return {void} - * @method - * @private - */ - _$beginClipDef () - { - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - if (!currentAttachment) { - throw new Error("mask currentAttachment is null."); - } - - this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - this._$gl.stencilFunc(this._$gl.ALWAYS, 0, 0xff); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.INVERT, this._$gl.INVERT); - this._$gl.stencilMask(1 << currentAttachment.clipLevel - 1); - this._$gl.colorMask(false, false, false, false); - } - - /** - * @return {void} - * @method - * @private - */ - _$endClipDef (): void - { - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - if (!currentAttachment) { - throw new Error("mask currentAttachment is null."); - } - - const clipLevel: number = currentAttachment.clipLevel; - - let mask: number = 0; - for (let idx: number = 0; idx < clipLevel; ++idx) { - mask |= (1 << clipLevel - idx) - 1; - } - - this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - this._$gl.stencilFunc(this._$gl.EQUAL, mask & 0xff, mask); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.KEEP, this._$gl.KEEP); - this._$gl.stencilMask(0xff); - this._$gl.colorMask(true, true, true, true); - } - - /** - * @return {void} - * @method - * @private - */ - _$leaveClip (): void - { - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - if (!currentAttachment) { - throw new Error("mask currentAttachment is null."); - } - - --currentAttachment.clipLevel; - currentAttachment.mask = !!currentAttachment.clipLevel; - - // end clip - if (!currentAttachment.clipLevel) { - this._$context._$clearRectStencil(); - return; - } - - // replace - const w: number = currentAttachment.width; - const h: number = currentAttachment.height; - - // create buffer - const vertices: any[] = this - ._$context - .path - .createRectVertices(0, 0, w, h); - - const object: WebGLVertexArrayObject = this - ._$context - .vao - .createFill(vertices); - - $poolArray(vertices.pop()); - $poolArray(vertices); - - const variants: ShapeShaderVariantCollection = this - ._$context - .shaderList - .shapeShaderVariants; - - const shader: CanvasToWebGLShader = variants.getMaskShapeShader(false, false); - const uniform: WebGLShaderUniform = shader.uniform; - variants.setMaskShapeUniformIdentity(uniform, w, h); - - const range: IndexRangeImpl = object.indexRanges[0]; - - // deny - if (!this._$currentClip) { - this._$currentClip = true; - this._$gl.enable(this._$gl.STENCIL_TEST); - } - - this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - this._$gl.stencilFunc(this._$gl.ALWAYS, 0, 0xff); - this._$gl.stencilOp(this._$gl.REPLACE, this._$gl.REPLACE, this._$gl.REPLACE); - this._$gl.stencilMask(1 << currentAttachment.clipLevel); - this._$gl.colorMask(false, false, false, false); - - shader._$containerClip(object, range.first, range.count); - - // object pool - const indexRanges: IndexRangeImpl[] = object.indexRanges; - for (let idx = 0; idx < indexRanges.length; ++idx) { - WebGLFillMeshGenerator - .indexRangePool - .push(indexRanges[idx]); - } - - $poolArray(object.indexRanges); - - this._$context.vao.releaseFill(object); - - this._$endClipDef(); - } - - /** - * @return {void} - * @method - * @private - */ - _$drawContainerClip (): void - { - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - if (!currentAttachment) { - throw new Error("mask currentAttachment is null."); - } - - const currentClipLevel: number = currentAttachment.clipLevel; - - const variants: ShapeShaderVariantCollection = this - ._$context - .shaderList - .shapeShaderVariants; - - const shader: CanvasToWebGLShader = variants - .getMaskShapeShader(false, false); - - const uniform: WebGLShaderUniform = shader.uniform; - - let useLevel: number = currentClipLevel; - - // create buffer - const w: number = currentAttachment.width; - const h: number = currentAttachment.height; - - this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE); - this._$gl.stencilFunc(this._$gl.ALWAYS, 0, 0xff); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.INVERT, this._$gl.INVERT); - this._$gl.colorMask(false, false, false, false); - - const length: number = this._$poolClip.length; - for (let idx: number = 0; idx < length; ++idx) { - - const object: ClipObjectImpl | void = this._$poolClip.shift(); // fixed - if (!object) { - continue; - } - - variants.setMaskShapeUniform( - uniform, false, - object.matrixA, object.matrixB, object.matrixC, - object.matrixD, object.matrixE, object.matrixF, - object.matrixG, object.matrixH, object.matrixI, - object.viewportWidth, object.viewportHeight, null - ); - - const indexRanges: IndexRangeImpl[] = object.vertexArrayObject.indexRanges; - for (let idx: number = 0; idx < indexRanges.length; ++idx) { - - const range: IndexRangeImpl = indexRanges[idx]; - - this._$gl.stencilMask(1 << useLevel - 1); - shader - ._$containerClip( - object.vertexArrayObject, - range.first, - range.count - ); - - WebGLFillMeshGenerator - .indexRangePool - .push(range); - } - $poolArray(indexRanges); - - this._$context.vao.releaseFill(object.vertexArrayObject); - - ++useLevel; - - // union - if (useLevel > 7) { - - // union - this._$unionStencilMask(currentClipLevel, w, h); - - // reset - useLevel = currentClipLevel; - - } - - } - - // last union - if (useLevel > currentClipLevel + 1) { - this._$unionStencilMask(currentClipLevel, w, h); - } - } - - /** - * @param {number} level - * @param {number} w - * @param {number} h - * @return {void} - * @method - * @private - */ - _$unionStencilMask (level: number, w: number, h: number): void - { - // create buffer - const vertices: any[] = this - ._$context - .path - .createRectVertices(0, 0, w, h); - - const object: WebGLVertexArrayObject = this - ._$context - .vao - .createFill(vertices); - - $poolArray(vertices.pop()); - $poolArray(vertices); - - const variants: ShapeShaderVariantCollection = this - ._$context - .shaderList - .shapeShaderVariants; - - const shader: CanvasToWebGLShader = variants - .getMaskShapeShader(false, false); - - const uniform: WebGLShaderUniform = shader.uniform; - - variants - .setMaskShapeUniformIdentity(uniform, w, h); - - const range: IndexRangeImpl = object.indexRanges[0]; - - // 例として level=4 の場合 - // - // ステンシルバッファの4ビット目以上を4ビット目に統合する。 - // |?|?|?|?|?|*|*|*| -> | | | | |?|*|*|*| - // - // このとき、4ビット目以上に1のビットが1つでもあれば4ビット目を1、 - // そうでなければ4ビット目を0とする。 - // - // 00000*** -> 00000*** - // 00001*** -> 00001*** - // 00010*** -> 00001*** - // 00011*** -> 00001*** - // 00100*** -> 00001*** - // ... - // 11101*** -> 00001*** - // 11110*** -> 00001*** - // 11111*** -> 00001*** - // - // したがってステンシルの現在の値を 00001000 と比較すればよい。 - // 比較して 00001000 以上であれば 00001*** で更新し、そうでなければ 00000*** で更新する。 - // 下位3ビットは元の値を保持する必要があるので 11111000 でマスクする。 - - this._$gl.stencilFunc(this._$gl.LEQUAL, 1 << level - 1, 0xff); - this._$gl.stencilOp(this._$gl.ZERO, this._$gl.REPLACE, this._$gl.REPLACE); - this._$gl.stencilMask(~((1 << level - 1) - 1)); - - shader._$containerClip(object, range.first, range.count); - - // reset - if (this._$poolClip.length) { - this._$gl.stencilFunc(this._$gl.ALWAYS, 0, 0xff); - this._$gl.stencilOp(this._$gl.KEEP, this._$gl.INVERT, this._$gl.INVERT); - } - - // object pool - const indexRanges: IndexRangeImpl[] = object.indexRanges; - for (let idx = 0; idx < indexRanges.length; ++idx) { - WebGLFillMeshGenerator - .indexRangePool - .push(indexRanges[idx]); - } - - $poolArray(object.indexRanges); - - this._$context.vao.releaseFill(object); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @param {Float32Array} matrix - * @param {number} width - * @param {number} height - * @return {boolean} - * @method - * @private - */ - _$onClip ( - vertex_array: WebGLVertexArrayObject, - matrix: Float32Array, - width: number, height: number - ): boolean { - - this._$clipStatus = true; - - if (this._$containerClip) { - - this._$poolClip.push({ - "vertexArrayObject": vertex_array, - "matrixA": matrix[0], - "matrixB": matrix[1], - "matrixC": matrix[2], - "matrixD": matrix[3], - "matrixE": matrix[4], - "matrixF": matrix[5], - "matrixG": matrix[6], - "matrixH": matrix[7], - "matrixI": matrix[8], - "viewportWidth": width, - "viewportHeight": height - }); - - return true; - } - - return false; - } - - /** - * @return {void} - * @method - * @public - */ - _$onSave (): void - { - this._$clips.push(this._$clipStatus); - } - - /** - * @return {void} - * @method - * @public - */ - _$onRestore (): void - { - if (this._$clips.length) { - this._$clipStatus = !!this._$clips.pop(); - } - } -} diff --git a/packages/webgl/src/CanvasToWebGLContextPath.ts b/packages/webgl/src/CanvasToWebGLContextPath.ts deleted file mode 100644 index 93d034e7..00000000 --- a/packages/webgl/src/CanvasToWebGLContextPath.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { BezierConverter } from "./BezierConverter"; -import type { VerticesImpl } from "./interface/VerticesImpl"; -import { - $getArray, - $poolArray -} from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContextPath -{ - private _$currentPath: Array; - private readonly _$vertices: VerticesImpl; - private readonly _$bezierConverter: BezierConverter; - - /** - * @constructor - */ - constructor () - { - /** - * @type {array} - * @private - */ - this._$currentPath = $getArray(); - - /** - * @type {array} - * @private - */ - this._$vertices = $getArray(); - - /** - * @type {BezierConverter} - * @private - */ - this._$bezierConverter = new BezierConverter(); - } - - /** - * @member {array} - * @readonly - * @public - */ - get vertices (): VerticesImpl - { - this._$pushCurrentPathToVertices(); - return this._$vertices; - } - - /** - * @return {void} - * @method - * @public - */ - begin (): void - { - this._$currentPath.length = 0; - - while (this._$vertices.length) { - $poolArray(this._$vertices.pop()); - } - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - moveTo (x: number, y: number): void - { - if (!this._$currentPath.length) { - this._$pushPointToCurrentPath(x, y, false); - return; - } - - if (this._$equalsToLastPoint(x, y)) { - return; - } - - this._$pushCurrentPathToVertices(); - this._$pushPointToCurrentPath(x, y, false); - } - - /** - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - lineTo (x: number, y: number): void - { - if (!this._$currentPath.length) { - this.moveTo(0, 0); - } - - if (this._$equalsToLastPoint(x, y)) { - return; - } - - this._$pushPointToCurrentPath(x, y, false); - } - - /** - * @param {number} cx - * @param {number} cy - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - quadTo (cx: number, cy: number, x: number, y: number): void - { - if (!this._$currentPath.length) { - this.moveTo(0, 0); - } - - if (this._$equalsToLastPoint(x, y)) { - return; - } - - this._$pushPointToCurrentPath(cx, cy, true); - this._$pushPointToCurrentPath(x, y, false); - } - - /** - * @param {number} cx1 - * @param {number} cy1 - * @param {number} cx2 - * @param {number} cy2 - * @param {number} x - * @param {number} y - * @return {void} - * @method - * @public - */ - cubicTo ( - cx1: number, cy1: number, - cx2: number, cy2: number, - x: number, y: number - ): void { - - if (!this._$currentPath.length) { - this.moveTo(0, 0); - } - - if (this._$equalsToLastPoint(x, y)) { - return; - } - - const fromX: number = +this._$currentPath[this._$currentPath.length - 3]; - const fromY: number = +this._$currentPath[this._$currentPath.length - 2]; - - this - ._$bezierConverter - .cubicToQuad(fromX, fromY, cx1, cy1, cx2, cy2, x, y); - - const buffer: Float32Array = this - ._$bezierConverter - ._$bezierConverterBuffer; - - for (let i: number = 0; i < 32; ) { - this.quadTo( - buffer[i++], - buffer[i++], - buffer[i++], - buffer[i++] - ); - } - } - - /** - * @param {number} x - * @param {number} y - * @param {number} radius - * @return {void} - * @method - * @public - */ - drawCircle (x: number, y: number, radius: number): void - { - const r: number = radius; - const k: number = radius * 0.5522847498307936; - this.cubicTo(x + r, y + k, x + k, y + r, x, y + r); - this.cubicTo(x - k, y + r, x - r, y + k, x - r, y); - this.cubicTo(x - r, y - k, x - k, y - r, x, y - r); - this.cubicTo(x + k, y - r, x + r, y - k, x + r, y); - } - - /** - * @return {void} - * @method - * @public - */ - close () - { - if (this._$currentPath.length <= 6) { - return; - } - - const x: number = +this._$currentPath[0]; - const y: number = +this._$currentPath[1]; - - if (this._$equalsToLastPoint(x, y)) { - return; - } - - this._$pushPointToCurrentPath(x, y, false); - } - - /** - * @param {number} x - * @param {number} y - * @return {boolean} - * @method - * @private - */ - _$equalsToLastPoint (x: number, y: number): boolean - { - const lastX: number = +this._$currentPath[this._$currentPath.length - 3]; - const lastY: number = +this._$currentPath[this._$currentPath.length - 2]; - return x === lastX && y === lastY; - } - - /** - * @param {number} x - * @param {number} y - * @param {boolean} is_control_point - * @return {void} - * @method - * @private - */ - _$pushPointToCurrentPath (x: number, y: number, is_control_point: boolean) - { - this._$currentPath.push(x, y, is_control_point); - } - - /** - * @return {void} - * @method - * @private - */ - _$pushCurrentPathToVertices (): void - { - if (this._$currentPath.length < 4) { - this._$currentPath.length = 0; - return; - } - - this._$vertices.push(this._$currentPath); - this._$currentPath = $getArray(); - } - - /** - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @return {array} - * @method - * @public - */ - createRectVertices ( - x: number, y: number, - w: number, h: number - ): VerticesImpl { - return $getArray($getArray( - x, y, false, - x + w, y, false, - x + w, y + h, false, - x, y + h, false - )); - } -} diff --git a/packages/webgl/src/CanvasToWebGLContextStyle.ts b/packages/webgl/src/CanvasToWebGLContextStyle.ts deleted file mode 100644 index 2a14f3fe..00000000 --- a/packages/webgl/src/CanvasToWebGLContextStyle.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { CanvasGradientToWebGL } from "./CanvasGradientToWebGL"; -import { CanvasPatternToWebGL } from "./CanvasPatternToWebGL"; -import type { CapsStyleImpl } from "./interface/CapsStyleImpl"; -import type { JointStyleImpl } from "./interface/JointStyleImpl"; -import { - $getFloat32Array4, - $poolFloat32Array4, - $Float32Array -} from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLContextStyle -{ - private _$fillStyle: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL; - private _$strokeStyle: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL; - private _$lineWidth: number; - private _$lineCap: CapsStyleImpl; - private _$lineJoin: JointStyleImpl; - private _$miterLimit: number; - - /** - * @constructor - * @public - */ - constructor () - { - this._$fillStyle = $getFloat32Array4(1, 1, 1, 1); // fixed size 4 - this._$strokeStyle = $getFloat32Array4(1, 1, 1, 1); // fixed size 4 - this._$lineWidth = 1; - this._$lineCap = "round"; - this._$lineJoin = "round"; - this._$miterLimit = 5; - } - - /** - * @member {number} - * @default 5 - * @public - */ - get miterLimit (): number - { - return this._$miterLimit; - } - set miterLimit (miter_limit: number) - { - this._$miterLimit = miter_limit; - } - - /** - * @member {number} - * @default 1 - * @public - */ - get lineWidth (): number - { - return this._$lineWidth; - } - set lineWidth (line_width: number) - { - this._$lineWidth = line_width; - } - - /** - * @member {string} - * @default "round" - * @public - */ - get lineCap (): CapsStyleImpl - { - return this._$lineCap; - } - set lineCap (line_cap: CapsStyleImpl) - { - this._$lineCap = line_cap; - } - - /** - * @member {string} - * @default "round" - * @public - */ - get lineJoin (): JointStyleImpl - { - return this._$lineJoin; - } - set lineJoin (line_join: JointStyleImpl) - { - this._$lineJoin = line_join; - } - - /** - * @member {Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL} - * @public - */ - get fillStyle (): Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL - { - return this._$fillStyle; - } - set fillStyle (style: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL) - { - if (this._$fillStyle instanceof $Float32Array) { - $poolFloat32Array4(this._$fillStyle); - } - this._$fillStyle = style; - } - - /** - * @member {Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL} - * @public - */ - get strokeStyle (): Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL - { - return this._$strokeStyle; - } - set strokeStyle (style: Float32Array|CanvasGradientToWebGL|CanvasPatternToWebGL) - { - if (this._$strokeStyle instanceof $Float32Array) { - $poolFloat32Array4(this._$strokeStyle); - } - this._$strokeStyle = style; - } - - /** - * @return {void} - * @method - * @public - */ - clear (): void - { - this._$lineWidth = 1; - this._$lineCap = "round"; - this._$lineJoin = "round"; - this._$miterLimit = 5; - - this._$clearFill(); - this._$clearStroke(); - } - - /** - * @return {void} - * @method - * @public - */ - _$clearFill (): void - { - if (this._$fillStyle instanceof CanvasGradientToWebGL) { - this._$fillStyle.dispose(); - this._$fillStyle = $getFloat32Array4(1, 1, 1, 1); - return ; - } - - if (this._$fillStyle instanceof CanvasPatternToWebGL) { - this._$fillStyle = $getFloat32Array4(1, 1, 1, 1); - return ; - } - - this._$fillStyle.fill(1); - } - - /** - * @return {void} - * @method - * @public - */ - _$clearStroke (): void - { - if (this._$strokeStyle instanceof CanvasGradientToWebGL) { - this._$strokeStyle.dispose(); - this._$strokeStyle = $getFloat32Array4(1, 1, 1, 1); - return ; - } - - if (this._$strokeStyle instanceof CanvasPatternToWebGL) { - this._$strokeStyle = $getFloat32Array4(1, 1, 1, 1); - return ; - } - - this._$strokeStyle.fill(1); - } -} \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject.ts b/packages/webgl/src/ColorBufferObject.ts new file mode 100644 index 00000000..82db7b8f --- /dev/null +++ b/packages/webgl/src/ColorBufferObject.ts @@ -0,0 +1,10 @@ +import type { IColorBufferObject } from "./interface/IColorBufferObject"; + +/** + * @description ColorBufferObjectの再利用のための配列のオブジェクトプール、 + * Object pool of array for reusing ColorBufferObject + * + * @type {IColorBufferObject[]} + * @private + */ +export const $objectPool: IColorBufferObject[] = []; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.test.ts b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.test.ts new file mode 100644 index 00000000..60ab5116 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.test.ts @@ -0,0 +1,27 @@ +import { execute } from "./ColorBufferObjectCreateService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ColorBufferObjectCreateService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }) + } + } + }); + + const colorBufferObject = execute(); + expect(colorBufferObject.resource).toBe("createRenderbuffer"); + expect(colorBufferObject.stencil.resource).toBe("createRenderbuffer"); + expect(colorBufferObject.width).toBe(0); + expect(colorBufferObject.height).toBe(0); + expect(colorBufferObject.area).toBe(0); + expect(colorBufferObject.dirty).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.ts b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.ts new file mode 100644 index 00000000..340674cd --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectCreateService.ts @@ -0,0 +1,32 @@ +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description 新規のColorBufferObjectを生成する + * Create a new ColorBufferObject + * + * @return {object} + * @method + * @protected + */ +export const execute = (): IColorBufferObject => +{ + const stencilBufferObject: IStencilBufferObject = { + "id": 0, + "resource": $gl.createRenderbuffer() as NonNullable, + "width": 0, + "height": 0, + "area": 0, + "dirty": false + }; + + return { + "resource": $gl.createRenderbuffer() as NonNullable, + "stencil": stencilBufferObject, + "width": 0, + "height": 0, + "area": 0, + "dirty": false + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.test.ts b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.test.ts new file mode 100644 index 00000000..0c6ee25d --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.test.ts @@ -0,0 +1,76 @@ +import { execute } from "./ColorBufferObjectMeguruBinarySearchService"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../ColorBufferObject"; + +describe("ColorBufferObjectMeguruBinarySearchService.js method test", () => +{ + it("test case", () => + { + $objectPool.length = 0; + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + + expect(execute(255 * 255)).toBe(0); + expect(execute(257 * 257)).toBe(1); + expect(execute(511 * 511)).toBe(1); + expect(execute(513 * 513)).toBe(2); + expect(execute(1200 * 1200)).toBe(3); + expect(execute(2200 * 2200)).toBe(4); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.ts b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.ts new file mode 100644 index 00000000..ba61cd5f --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/service/ColorBufferObjectMeguruBinarySearchService.ts @@ -0,0 +1,27 @@ +import { $objectPool } from "../../ColorBufferObject"; + +/** + * @description めぐる式二分探索法でobject_poolを検索する + * Search object_pool with Meguru binary search method + * + * @param {number} area + * @return {number} + * @method + * @protected + */ +export const execute = (area: number): number => +{ + let ng: number = -1; + let ok: number = $objectPool.length; + + while (Math.abs(ok - ng) > 1) { + const mid: number = Math.floor((ok + ng) / 2); + if (area <= $objectPool[mid].area) { + ok = mid; + } else { + ng = mid; + } + } + + return ok; +}; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.test.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.test.ts new file mode 100644 index 00000000..22c606ad --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.test.ts @@ -0,0 +1,101 @@ +import { execute } from "./ColorBufferObjectAcquireObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../ColorBufferObject"; + +describe("ColorBufferObjectAcquireObjectUseCase.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }) + } + } + }); + + // new + $objectPool.length = 0; + const newColorBufferObject = execute(255 * 255) + expect(newColorBufferObject.width).toBe(0); + expect(newColorBufferObject.area).toBe(0); + expect(newColorBufferObject.height).toBe(0); + + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + + // hit + expect($objectPool.length).toBe(4); + const poolColorBufferObject = execute(500 * 500) + expect(poolColorBufferObject.width).toBe(512); + expect(poolColorBufferObject.area).toBe(512 * 512); + expect(poolColorBufferObject.height).toBe(512); + expect($objectPool.length).toBe(3); + + // array shift + const oldColorBufferObject = execute(2500 * 2500) + expect(oldColorBufferObject.width).toBe(256); + expect(oldColorBufferObject.area).toBe(256 * 256); + expect(oldColorBufferObject.height).toBe(256); + expect($objectPool.length).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.ts new file mode 100644 index 00000000..8fe06796 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectAcquireObjectUseCase.ts @@ -0,0 +1,34 @@ +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import { execute as colorBufferObjectCreateService } from "../service/ColorBufferObjectCreateService"; +import { execute as colorBufferObjectMeguruBinarySearchService } from "../service/ColorBufferObjectMeguruBinarySearchService"; +import { $objectPool } from "../../ColorBufferObject"; + +/** + * @description 指定サイズのobjectがpoolにあれば再利用、なければ新規作成する + * If an object of the specified size is in the pool, reuse it, otherwise create a new one + * + * @param {number} area + * @return {object} + * @method + * @protected + */ +export const execute = (area: number): IColorBufferObject => +{ + if (!$objectPool.length) { + return colorBufferObjectCreateService(); + } + + const index = colorBufferObjectMeguruBinarySearchService(area); + if (index < $objectPool.length) { + const colorBuffer = $objectPool[index]; + $objectPool.splice(index, 1); + return colorBuffer; + } + + const colorBuffer = $objectPool.shift(); + if (!colorBuffer) { + throw new Error("the color buffer is void."); + } + + return colorBuffer; +}; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.test.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.test.ts new file mode 100644 index 00000000..71a5d054 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.test.ts @@ -0,0 +1,89 @@ +import { execute } from "./ColorBufferObjectGetColorBufferObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../ColorBufferObject"; + +describe("ColorBufferObjectGetColorBufferObjectUseCase.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "renderbufferStorageMultisample": vi.fn(() => { return "renderbufferStorageMultisample" }), + } + } + }); + + // new + $objectPool.length = 0; + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + expect($objectPool.length).toBe(4); + + const colorBufferObject = execute(620, 480); + expect($objectPool.length).toBe(3); + expect(colorBufferObject.width).toBe(1024); + expect(colorBufferObject.height).toBe(1024); + expect(colorBufferObject.area).toBe(1024 * 1024); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.ts new file mode 100644 index 00000000..0001b966 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase.ts @@ -0,0 +1,56 @@ +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import { execute as colorBufferObjectAcquireObjectUseCase } from "./ColorBufferObjectAcquireObjectUseCase"; +import { + $gl, + $samples, + $upperPowerOfTwo +} from "../../WebGLUtil"; + +/** + * @description 指定サイズのColorBufferObjectを取得する + * Get a ColorBufferObject of the specified size + * + * @param {number} width + * @param {number} height + * @return {object} + * @method + * @protected + */ +export const execute = (width: number, height: number): IColorBufferObject => +{ + // 128以下で描画崩れが発生する場合がある?ため、256を最小サイズにする + width = Math.max(256, $upperPowerOfTwo(width)); + height = Math.max(256, $upperPowerOfTwo(height)); + + const colorBufferObject = colorBufferObjectAcquireObjectUseCase(width * height); + if (colorBufferObject.width < width + || colorBufferObject.height < height + ) { + + width = Math.max(width, colorBufferObject.width); + height = Math.max(height, colorBufferObject.height); + + colorBufferObject.width = width; + colorBufferObject.height = height; + colorBufferObject.area = width * height; + + $gl.bindRenderbuffer($gl.RENDERBUFFER, colorBufferObject.resource); + $gl.renderbufferStorageMultisample( + $gl.RENDERBUFFER, + $samples, + $gl.RGBA8, + width, height + ); + + $gl.bindRenderbuffer($gl.RENDERBUFFER, colorBufferObject.stencil.resource); + $gl.renderbufferStorageMultisample( + $gl.RENDERBUFFER, + $samples, + $gl.STENCIL_INDEX8, + width, height + ); + } + + colorBufferObject.dirty = true; + return colorBufferObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.test.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.test.ts new file mode 100644 index 00000000..9d24e6f4 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.test.ts @@ -0,0 +1,92 @@ +import { execute } from "./ColorBufferObjectReleaseColorBufferObjectUseCase"; +import { describe, expect, it } from "vitest"; +import { $objectPool } from "../../ColorBufferObject"; + +describe("ColorBufferObjectReleaseColorBufferObjectUseCase.js method test", () => +{ + it("test case", () => + { + $objectPool.length = 0; + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + expect($objectPool.length).toBe(4); + + const colorBufferObject = { + "resource": {} as unknown as WebGLRenderbuffer, + "stencil": { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0, + "dirty": false, + }, + "width": 620, + "height": 480, + "area": 620 * 480, + "dirty": false, + }; + execute(colorBufferObject); + expect($objectPool.length).toBe(5); + expect($objectPool[2]).toBe(colorBufferObject); + + // 重複チェック + execute(colorBufferObject); + expect($objectPool.length).toBe(5); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.ts b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.ts new file mode 100644 index 00000000..8f7bc9c7 --- /dev/null +++ b/packages/webgl/src/ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase.ts @@ -0,0 +1,23 @@ +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import { execute as colorBufferObjectMeguruBinarySearchService } from "../service/ColorBufferObjectMeguruBinarySearchService"; +import { $objectPool } from "../../ColorBufferObject"; + +/** + * @description 利用済みのColorBufferObjectを再利用するためにプールに保管する + * Store the used ColorBufferObject in the pool for reuse + * + * @param {object} color_buffer_object + * @return {void} + * @method + * @protected + */ +export const execute = (color_buffer_object: IColorBufferObject): void => +{ + if ($objectPool.indexOf(color_buffer_object) > -1) { + return ; + } + + const index = colorBufferObjectMeguruBinarySearchService(color_buffer_object.area); + color_buffer_object.dirty = true; + $objectPool.splice(index, 0, color_buffer_object); +}; \ No newline at end of file diff --git a/packages/webgl/src/ColorBufferPool.ts b/packages/webgl/src/ColorBufferPool.ts deleted file mode 100644 index ca8d83d4..00000000 --- a/packages/webgl/src/ColorBufferPool.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - $getArray, - $Math, - $upperPowerOfTwo -} from "@next2d/share"; - -/** - * @class - */ -export class ColorBufferPool -{ - private readonly _$gl: WebGL2RenderingContext; - private readonly _$objectPool: WebGLRenderbuffer[]; - private _$samples: number; - - /** - * @param {WebGL2RenderingContext} gl - * @param {number} samples - * @constructor - */ - constructor (gl: WebGL2RenderingContext, samples: number) - { - this._$gl = gl; - this._$samples = samples; - this._$objectPool = $getArray(); - } - - /** - * @member {number} - * @param {number} samples - * @public - */ - set samples (samples: number) - { - this._$samples = samples; - } - - /** - * @return {WebGLRenderbuffer} - * @method - * @private - */ - _$createColorBuffer (): WebGLRenderbuffer - { - const colorBuffer: WebGLRenderbuffer | null = this._$gl.createRenderbuffer(); - if (!colorBuffer) { - throw new Error("the color buffer is null."); - } - - const stencilBuffer: WebGLRenderbuffer | null = this._$gl.createRenderbuffer(); - if (!stencilBuffer) { - throw new Error("the stencil buffer is null."); - } - - colorBuffer.stencil = stencilBuffer; - colorBuffer.samples = 0; - colorBuffer.width = 0; - colorBuffer.height = 0; - colorBuffer.area = 0; - colorBuffer.dirty = true; - - return colorBuffer; - } - - /** - * @param {number} area - * @return {WebGLRenderbuffer} - * @method - * @private - */ - _$getColorBuffer (area: number): WebGLRenderbuffer - { - if (!this._$objectPool.length) { - return this._$createColorBuffer(); - } - - const index: number = this._$bsearch(area); - if (index < this._$objectPool.length) { - const colorBuffer: WebGLRenderbuffer = this._$objectPool[index]; - this._$objectPool.splice(index, 1); - return colorBuffer; - } - - const colorBuffer: WebGLRenderbuffer | void = this._$objectPool.shift(); - if (!colorBuffer) { - throw new Error("the color buffer is void."); - } - - return colorBuffer; - } - - /** - * @param {number} width - * @param {number} height - * @param {number} [samples=0] - * @return {WebGLRenderbuffer} - * @method - * @public - */ - create ( - width: number, height: number, - samples: number = 0 - ): WebGLRenderbuffer { - - // 128以下で描画崩れが発生する場合がある?ため、256を最小サイズにする - width = $Math.max(256, $upperPowerOfTwo(width)); - height = $Math.max(256, $upperPowerOfTwo(height)); - - const colorBuffer: WebGLRenderbuffer = this._$getColorBuffer(width * height); - - if (!samples) { - samples = this._$samples; - } - - if (colorBuffer.width < width - || colorBuffer.height < height - || colorBuffer.samples !== samples - ) { - - width = $Math.max(width, colorBuffer.width); - height = $Math.max(height, colorBuffer.height); - - colorBuffer.samples = samples; - colorBuffer.width = width; - colorBuffer.height = height; - colorBuffer.area = width * height; - colorBuffer.dirty = false; - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, colorBuffer); - this._$gl.renderbufferStorageMultisample( - this._$gl.RENDERBUFFER, - samples, - this._$gl.RGBA8, - width, height - ); - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, colorBuffer.stencil); - this._$gl.renderbufferStorageMultisample( - this._$gl.RENDERBUFFER, - samples, - this._$gl.STENCIL_INDEX8, - width, height - ); - } - - return colorBuffer; - } - - /** - * @param {WebGLRenderbuffer} [colorBuffer = null] - * @return {void} - * @method - * @public - */ - release (colorBuffer: WebGLRenderbuffer): void - { - colorBuffer.dirty = true; - - const index: number = this._$bsearch(colorBuffer.area); - this._$objectPool.splice(index, 0, colorBuffer); - } - - /** - * @description 「めぐる式二分探索法」で面積が引数以上の要素のインデックスを求める - * - * @param {number} area - * @return {number} - * @method - * @private - */ - _$bsearch (area: number): number - { - let ng: number = -1; - let ok: number = this._$objectPool.length; - - while ($Math.abs(ok - ng) > 1) { - const mid: number = $Math.floor((ok + ng) / 2); - if (area <= this._$objectPool[mid].area) { - ok = mid; - } else { - ng = mid; - } - } - - return ok; - } -} \ No newline at end of file diff --git a/packages/webgl/src/Const.ts b/packages/webgl/src/Const.ts deleted file mode 100644 index 64a7a362..00000000 --- a/packages/webgl/src/Const.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @type {number} - * @public - */ -export let $RENDER_SIZE: number = 2048; - -/** - * @param {number} size - * @return {void} - * @method - * @public - */ -export const $setRenderSize = (size: number): void => -{ - $RENDER_SIZE = Math.min(4096, size / 2); -}; \ No newline at end of file diff --git a/packages/webgl/src/Context.ts b/packages/webgl/src/Context.ts new file mode 100644 index 00000000..81a2b1d7 --- /dev/null +++ b/packages/webgl/src/Context.ts @@ -0,0 +1,1129 @@ +import type { IAttachmentObject } from "./interface/IAttachmentObject"; +import type { IBlendMode } from "./interface/IBlendMode"; +import type { IBounds } from "./interface/IBounds"; +import type { Node } from "@next2d/texture-packer"; +import { execute as beginPath } from "./PathCommand/service/PathCommandBeginPathService"; +import { execute as moveTo } from "./PathCommand/usecase/PathCommandMoveToUseCase"; +import { execute as lineTo } from "./PathCommand/usecase/PathCommandLineToUseCase"; +import { execute as quadraticCurveTo } from "./PathCommand/usecase/PathCommandQuadraticCurveToUseCase"; +import { execute as closePath } from "./PathCommand/usecase/PathCommandClosePathUseCase"; +import { execute as arc } from "./PathCommand/usecase/PathCommandArcUseCase"; +import { execute as bezierCurveTo } from "./PathCommand/usecase/PathCommandBezierCurveToUseCase"; +import { execute as contextUpdateBackgroundColorService } from "./Context/service/ContextUpdateBackgroundColorService"; +import { execute as contextFillBackgroundColorService } from "./Context/service/ContextFillBackgroundColorService"; +import { execute as contextResizeUseCase } from "./Context/usecase/ContextResizeUseCase"; +import { execute as contextClearRectUseCase } from "./Context/usecase/ContextClearRectUseCase"; +import { execute as contextBindUseCase } from "./Context/usecase/ContextBindUseCase"; +import { execute as contextSaveService } from "./Context/service/ContextSaveService"; +import { execute as contextRestoreService } from "./Context/service/ContextRestoreService"; +import { execute as contextSetTransformService } from "./Context/service/ContextSetTransformService"; +import { execute as contextTransformService } from "./Context/service/ContextTransformService"; +import { execute as contextResetService } from "./Context/service/ContextResetService"; +import { execute as contextResetStyleService } from "./Context/service/ContextResetStyleService"; +import { execute as contextBeginNodeRenderingService } from "./Context/service/ContextBeginNodeRenderingService"; +import { execute as contextEndNodeRenderingService } from "./Context/service/ContextEndNodeRenderingService"; +import { execute as contextFillUseCase } from "./Context/usecase/ContextFillUseCase"; +import { execute as contextGradientFillUseCase } from "./Context/usecase/ContextGradientFillUseCase"; +import { execute as contextGradientStrokeUseCase } from "./Context/usecase/ContextGradientStrokeUseCase"; +import { execute as contextUseGridService } from "./Context/service/ContextUseGridService"; +import { execute as contextClipUseCase } from "./Context/usecase/ContextClipUseCase"; +import { execute as atlasManagerCreateNodeService } from "./AtlasManager/service/AtlasManagerCreateNodeService"; +import { execute as atlasManagerRemoveNodeService } from "./AtlasManager/service/AtlasManagerRemoveNodeService"; +import { execute as blnedDrawDisplayObjectUseCase } from "./Blend/usecase/BlnedDrawDisplayObjectUseCase"; +import { execute as blnedClearArraysInstancedUseCase } from "./Blend/usecase/BlnedClearArraysInstancedUseCase"; +import { execute as blnedDrawArraysInstancedUseCase } from "./Blend/usecase/BlnedDrawArraysInstancedUseCase"; +import { execute as vertexArrayObjectBootUseCase } from "./VertexArrayObject/usecase/VertexArrayObjectBootUseCase"; +import { execute as frameBufferManagerTransferMainCanvasService } from "./FrameBufferManager/service/FrameBufferManagerTransferMainCanvasService"; +import { execute as blendBootUseCase } from "./Blend/usecase/BlendBootUseCase"; +import { execute as maskBeginMaskService } from "./Mask/service/MaskBeginMaskService"; +import { execute as maskSetMaskBoundsService } from "./Mask/service/MaskSetMaskBoundsService"; +import { execute as maskEndMaskService } from "./Mask/service/MaskEndMaskService"; +import { execute as maskLeaveMaskUseCase } from "./Mask/usecase/MaskLeaveMaskUseCase"; +import { execute as contextDrawPixelsUseCase } from "./Context/usecase/ContextDrawPixelsUseCase"; +import { execute as contextDrawElementUseCase } from "./Context/usecase/ContextDrawElementUseCase"; +import { execute as contextBitmapFillUseCase } from "./Context/usecase/ContextBitmapFillUseCase"; +import { execute as contextBitmapStrokeUseCase } from "./Context/usecase/ContextBitmapStrokeUseCase"; +import { execute as contextStrokeUseCase } from "./Context/usecase/ContextStrokeUseCase"; +import { execute as contextApplyFilterUseCase } from "./Context/usecase/ContextApplyFilterUseCase"; +import { execute as contextUpdateTransferBoundsService } from "./Context/service/ContextUpdateTransferBoundsService"; +import { execute as contextUpdateAllTransferBoundsService } from "./Context/service/ContextUpdateAllTransferBoundsService"; +import { execute as contextDrawFillUseCase } from "./Context/usecase/ContextDrawFillUseCase"; +import { execute as contextCreateImageBitmapService } from "./Context/service/ContextCreateImageBitmapService"; +import { $setGradientLUTGeneratorMaxLength } from "./Shader/GradientLUTGenerator"; +import { + $getAtlasAttachmentObject, + $clearTransferBounds +} from "./AtlasManager"; +import { + $setReadFrameBuffer, + $setDrawFrameBuffer, + $getCurrentAttachment, + $setAtlasFrameBuffer, + $setBitmapFrameBuffer +} from "./FrameBufferManager"; +import { + $setRenderMaxSize, + $setWebGL2RenderingContext, + $setSamples, + $getFloat32Array9, + $getArray, + $setContext, + $setDevicePixelRatio +} from "./WebGLUtil"; + +/** + * @description WebGL版、Next2Dのコンテキスト + * WebGL version, Next2D context + * + * @class + */ +export class Context +{ + /** + * @description matrixのデータを保持するスタック + * Stack to hold matrix data + * + * @type {Float32Array[]} + * @protected + */ + public readonly $stack: Float32Array[]; + + /** + * @description 2D変換行列 + * 2D transformation matrix + * + * @type {Float32Array} + * @protected + */ + public readonly $matrix: Float32Array; + + /** + * @description 背景色のR + * Background color R + * + * @type {number} + * @protected + */ + public $clearColorR: number; + + /** + * @description 背景色のG + * Background color G + * + * @type {number} + * @protected + */ + public $clearColorG: number; + + /** + * @description 背景色のB + * Background color B + * + * @type {number} + * @protected + */ + public $clearColorB: number; + + /** + * @description 背景色のA + * Background color A + * + * @type {number} + * @protected + */ + public $clearColorA: number; + + /** + * @description メインのアタッチメントオブジェクト + * Main attachment object + * + * @type {IAttachmentObject} + * @protected + */ + public $mainAttachmentObject: IAttachmentObject | null; + + /** + * @description アタッチメントオブジェクトを保持するスタック + * Stack to hold attachment objects + * + * @type {IAttachmentObject[]} + * @protected + */ + public readonly $stackAttachmentObject: IAttachmentObject[]; + + /** + * @description グローバルアルファ + * Global alpha + * + * @type {number} + * @default 1 + * @public + */ + public globalAlpha: number; + + /** + * @description 合成モード + * composite mode + * + * @type {IBlendMode} + * @default "normal" + * @public + */ + public globalCompositeOperation: IBlendMode; + + /** + * @description イメージのスムージング設定 + * Image smoothing setting + * + * @type {boolean} + * @default false + * @public + */ + public imageSmoothingEnabled: boolean; + + /** + * @description 塗りつぶしのRGBAを保持するFloat32Array + * Float32Array that holds the RGBA of the fill + * + * @type {Float32Array} + * @protected + */ + public $fillStyle: Float32Array; + + /** + * @description 線のRGBAを保持するFloat32Array + * Float32Array that holds the RGBA of the line + * + * @type {Float32Array} + * @protected + */ + public $strokeStyle: Float32Array; + + /** + * @description マスクの描画範囲 + * Drawing range of the mask + * + * @type {IBounds} + * @protected + */ + public readonly maskBounds: IBounds; + + /** + * @description ストロークの太さ + * Stroke thickness + * + * @type {number} + * @default 1 + * @public + */ + public thickness: number; + + /** + * @description ストロークのキャップ + * Stroke cap + * + * @type {number} + * @default 1 + * @public + */ + public caps: number; + + /** + * @description ストロークのジョイント + * Stroke joint + * + * @type {number} + * @default 2 + * @public + */ + public joints: number; + + /** + * @description ストロークのマイターリミット + * Stroke miter limit + * + * @type {number} + * @default 0 + * @public + */ + public miterLimit: number; + + /** + * @param {WebGL2RenderingContext} gl + * @param {number} samples + * @param {number} [device_pixel_ratio=1] + * @constructor + * @public + */ + constructor ( + gl: WebGL2RenderingContext, + samples: number, + device_pixel_ratio: number = 1 + ) { + + $setWebGL2RenderingContext(gl); + $setRenderMaxSize(gl.getParameter(gl.MAX_TEXTURE_SIZE)); + $setSamples(samples); + $setDevicePixelRatio(device_pixel_ratio); + + this.$stack = $getArray(); + this.$stackAttachmentObject = $getArray(); + this.$matrix = $getFloat32Array9(1, 0, 0, 0, 1, 0, 0, 0, 1); + + // bakground color + this.$clearColorR = 0; + this.$clearColorG = 0; + this.$clearColorB = 0; + this.$clearColorA = 0; + + // stroke + this.thickness = 1; + this.caps = 1; + this.joints = 2; + this.miterLimit = 0; + + // メインのアタッチメントオブジェクト + this.$mainAttachmentObject = null; + + // グローバルアルファ、合成モード、イメージのスムージング設定 + this.globalAlpha = 1; + this.globalCompositeOperation = "normal"; + this.imageSmoothingEnabled = false; + + // 塗りつぶしタイプ、ストロークタイプ + this.$fillStyle = new Float32Array([1, 1, 1, 1]); + this.$strokeStyle = new Float32Array([1, 1, 1, 1]); + + // マスクの描画範囲 + this.maskBounds = { + "xMin": 0, + "yMin": 0, + "xMax": 0, + "yMax": 0 + }; + + // WebTextureの設定 + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + + // FrameBufferManagerの初期起動 + $setReadFrameBuffer(gl); + $setDrawFrameBuffer(gl); + $setAtlasFrameBuffer(gl); + $setBitmapFrameBuffer(gl); + + // VertexArrayObjectの初期起動 + vertexArrayObjectBootUseCase(gl); + + // ブレンドモードを起動する + blendBootUseCase(); + + // グラデーションの最大長を設定 + $setGradientLUTGeneratorMaxLength(gl); + + // コンテキストをセット + $setContext(this); + } + + /** + * @description 転送範囲をリセット + * Reset the transfer range + * + * @return {void} + * @method + * @public + */ + clearTransferBounds (): void + { + $clearTransferBounds(); + } + + /** + * @description 背景色を更新 + * Update background color + * + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {void} + * @method + * @public + */ + updateBackgroundColor (red: number, green: number, blue: number, alpha: number): void + { + contextUpdateBackgroundColorService(this, red, green, blue, alpha); + } + + /** + * @description 背景色を指定カラーで塗りつぶす + * Fill the background color with the specified color + * + * @return {void} + * @method + * @public + */ + fillBackgroundColor (): void + { + contextFillBackgroundColorService( + this.$clearColorR, + this.$clearColorG, + this.$clearColorB, + this.$clearColorA + ); + } + + /** + * @description メインcanvasのサイズを変更 + * Change the size of the main canvas + * + * @param {number} width + * @param {number} height + * @param {boolean} [cache_clear=true] + * @return {void} + * @method + * @public + */ + resize (width: number, height: number, cache_clear: boolean = true): void + { + contextResizeUseCase(this, width, height, cache_clear); + } + + /** + * @description 指定範囲をクリアする + * Clear the specified range + * + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @return {void} + * @method + * @purotected + */ + clearRect (x: number, y: number, w: number, h: number): void + { + contextClearRectUseCase(x, y, w, h); + } + + /** + * @description アタッチメントオブジェクトをバインド + * Bind the attachment object + * + * @param {IAttachmentObject} attachment_object + * @return {void} + * @method + * @public + */ + bind (attachment_object: IAttachmentObject): void + { + contextBindUseCase(this, attachment_object); + } + + /** + * @description 現在の2D変換行列を保存 + * Save the current 2D transformation matrix + * + * @return {void} + * @method + * @public + */ + save (): void + { + contextSaveService(this); + } + + /** + * @description 2D変換行列を復元 + * Restore 2D transformation matrix + * + * @return {void} + * @method + * @public + */ + restore (): void + { + contextRestoreService(this); + } + + /** + * @description 2D変換行列を設定 + * Set 2D transformation matrix + * + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} e + * @param {number} f + * @return {void} + * @method + * @public + */ + setTransform ( + a: number, b: number, c: number, + d: number, e: number, f: number + ): void { + contextSetTransformService(this.$matrix, a, b, c, d, e, f); + } + + /** + * @description 現在の2D変換行列に対して乗算を行います。 + * Multiply the current 2D transformation matrix. + * + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} e + * @param {number} f + * @return {void} + * @method + * @public + */ + transform ( + a: number, b: number, c: number, + d: number, e: number, f: number + ): void { + contextTransformService(this, a, b, c, d, e, f); + } + + /** + * @description コンテキストの値を初期化する + * Initialize the values of the context + * + * @return {void} + * @method + * @public + */ + reset (): void + { + contextResetService(this); + } + + /** + * @description パスを開始 + * Start the path + * + * @return {void} + * @method + * @public + */ + beginPath (): void + { + // reset color style + contextResetStyleService(this); + + // begin path + beginPath(); + } + + /** + * @description パスを移動 + * Move the path + * + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @public + */ + moveTo (x: number, y: number): void + { + moveTo(x, y); + } + + /** + * @description パスを線で結ぶ + * Connect the path with a line + * + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @public + */ + lineTo (x: number, y: number): void + { + lineTo(x, y); + } + + /** + * @description 二次ベジェ曲線を描画 + * Draw a quadratic Bezier curve + * + * @param {number} cx + * @param {number} cy + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @public + */ + quadraticCurveTo (cx: number, cy: number, x: number, y: number): void + { + quadraticCurveTo(cx, cy, x, y); + } + + /** + * @description 塗りつぶしスタイルを設定 + * Set fill style + * + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {void} + * @method + * @public + */ + fillStyle (red: number, green: number, blue: number, alpha: number): void + { + this.$fillStyle[0] = red; + this.$fillStyle[1] = green; + this.$fillStyle[2] = blue; + this.$fillStyle[3] = alpha; + } + + /** + * @description 線のスタイルを設定 + * Set line style + * + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {void} + * @method + * @public + */ + strokeStyle (red: number, green: number, blue: number, alpha: number): void + { + this.$strokeStyle[0] = red; + this.$strokeStyle[1] = green; + this.$strokeStyle[2] = blue; + this.$strokeStyle[3] = alpha; + } + + /** + * @description パスを閉じる + * Close the path + * + * @return {void} + * @method + * @public + */ + closePath (): void + { + closePath(); + } + + /** + * @description 円弧を描画 + * Draw an arc + * + * @param {number} x + * @param {number} y + * @param {number} radius + * @return {void} + * @method + * @public + */ + arc (x: number, y: number, radius: number): void + { + arc(x, y, radius); + } + + /** + * @description 3次ベジェ曲線を描画 + * Draw a cubic Bezier curve + * + * @param {number} cx1 + * @param {number} cy1 + * @param {number} cx2 + * @param {number} cy2 + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @public + */ + bezierCurveTo (cx1: number, cy1: number, cx2: number, cy2: number, x: number, y: number): void + { + bezierCurveTo(cx1, cy1, cx2, cy2, x, y); + } + + /** + * @description 塗りつぶしを実行 + * Perform fill + * + * @return {void} + * @method + * @public + */ + fill (): void + { + contextFillUseCase("fill"); + } + + /** + * @description グラデーションの塗りつぶしを実行 + * Perform gradient fill + * + * @param {number} type + * @param {array} stops + * @param {Float32Array} matrix + * @param {number} spread + * @param {number} interpolation + * @param {number} focal + * @return {void} + * @method + * @public + */ + gradientFill ( + type: number, + stops: number[], + matrix: Float32Array, + spread: number, + interpolation: number, + focal: number + ): void { + contextGradientFillUseCase( + type, stops, matrix, + spread, interpolation, focal + ); + } + + /** + * @description 塗りのピクセルデータを描画 + * Draw pixel data of the fill + * + * @param {Uint8Array} pixels + * @param {Float32Array} matrix + * @param {number} width + * @param {number} height + * @param {boolean} repeat + * @param {boolean} smooth + * @return {void} + * @method + * @public + */ + bitmapFill ( + pixels: Uint8Array, + matrix: Float32Array, + width: number, + height: number, + repeat: boolean, + smooth: boolean + ): void { + contextBitmapFillUseCase( + pixels, matrix, width, height, repeat, smooth + ); + } + + /** + * @description 線の描画を実行 + * Perform line drawing + * + * @return {void} + * @method + * @public + */ + stroke (): void + { + contextStrokeUseCase(); + } + + /** + * @description 線のグラデーションを実行 + * Perform gradient of the line + * + * @param {number} type + * @param {array} stops + * @param {Float32Array} matrix + * @param {number} spread + * @param {number} interpolation + * @param {number} focal + * @return {void} + * @method + * @public + */ + gradientStroke ( + type: number, + stops: number[], + matrix: Float32Array, + spread: number, + interpolation: number, + focal: number + ): void { + contextGradientStrokeUseCase( + type, stops, matrix, + spread, interpolation, focal + ); + } + + /** + * @description 線のピクセルデータを描画 + * Draw pixel data of the line + * + * @param {Uint8Array} pixels + * @param {Float32Array} matrix + * @param {number} width + * @param {number} height + * @param {boolean} repeat + * @param {boolean} smooth + * @return {void} + * @method + * @public + */ + bitmapStroke ( + pixels: Uint8Array, + matrix: Float32Array, + width: number, + height: number, + repeat: boolean, + smooth: boolean + ): void { + contextBitmapStrokeUseCase( + pixels, matrix, width, height, repeat, smooth + ); + } + + /** + * @description マスク処理を実行 + * Perform mask processing + * + * @return {void} + * @method + * @public + */ + clip (): void + { + contextClipUseCase(); + } + + /** + * @description 現在のアタッチメントオブジェクトを取得 + * Get the current attachment object + * + * @return {IAttachmentObject | null} + * @readonly + * @public + */ + get currentAttachmentObject (): IAttachmentObject | null + { + return $getCurrentAttachment(); + } + + /** + * @description アトラス専用のアタッチメントオブジェクトを取得 + * Get the attachment object for the atlas + * + * @return {IAttachmentObject} + * @readonly + * @public + */ + get atlasAttachmentObject (): IAttachmentObject + { + return $getAtlasAttachmentObject(); + } + + /** + * @description キャッシュするポジションのノードを作成 + * Create a node for the position to cache + * + * @param {number} width + * @param {number} height + * @return {Node} + * @method + * @public + */ + createNode (width: number, height: number): Node + { + return atlasManagerCreateNodeService(width, height); + } + + /** + * @description 指定のノードを削除 + * Remove the specified node + * + * @param {Node} node + * @return {void} + * @method + * @public + */ + removeNode (node: Node): void + { + atlasManagerRemoveNodeService(node); + } + + /** + * @description 指定のノード範囲で描画を開始 + * Start drawing in the specified node range + * + * @param {Node} node + * @return {void} + * @method + * @public + */ + beginNodeRendering (node: Node): void + { + // 転送範囲を更新 + contextUpdateTransferBoundsService(node); + + // ノードの描画を開始 + contextBeginNodeRenderingService(node.x, node.y, node.w, node.h); + } + + /** + * @description 指定のノード範囲で描画を終了 + * End drawing in the specified node range + * + * @return {void} + * @method + * @public + */ + endNodeRendering (): void + { + contextEndNodeRenderingService(); + } + + /** + * @description 塗りの描画を実行 + * Perform fill drawing + * + * @return {void} + * @method + * @public + */ + drawFill (): void + { + contextDrawFillUseCase(); + } + + /** + * @description インスタンスを描画 + * Draw an instance + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @param {Float32Array} color_transform + * @return {void} + * @method + * @public + */ + drawDisplayObject ( + node: Node, + x_min: number, + y_min: number, + x_max: number, + y_max: number, + color_transform: Float32Array + ): void { + contextUpdateAllTransferBoundsService(node); + blnedDrawDisplayObjectUseCase( + node, x_min, y_min, x_max, y_max, color_transform + ); + } + + /** + * @description インスタンス配列を描画 + * Draw an instance array + * + * @return {void} + * @method + * @public + */ + drawArraysInstanced (): void + { + blnedDrawArraysInstancedUseCase(); + } + + /** + * @description インスタンス配列をクリア + * Clear the instance array + * + * @return {void} + * @method + * @public + */ + clearArraysInstanced (): void + { + blnedClearArraysInstancedUseCase(); + } + + /** + * @description フレームバッファの描画情報をキャンバスに転送 + * Transfer the drawing information of the frame buffer to the canvas + * + * @return {void} + * @method + * @public + */ + transferMainCanvas (): void + { + frameBufferManagerTransferMainCanvasService(); + } + + /** + * @description ピクセルバッファをNodeの指定箇所に転送 + * Transfer the pixel buffer to the specified location of the Node + * + * @param {Node} node + * @param {Uint8Array} pixels + * @return {void} + * @method + * @public + */ + drawPixels (node: Node, pixels: Uint8Array): void + { + contextDrawPixelsUseCase(node, pixels); + } + + /** + * @description OffscreenCanvasをNodeの指定箇所に転送 + * Transfer the OffscreenCanvas to the specified location of the Node + * + * @param {Node} node + * @param {OffscreenCanvas | ImageBitmap} element + * @return {void} + * @method + * @public + */ + drawElement (node: Node, element: OffscreenCanvas | ImageBitmap): void + { + contextDrawElementUseCase(node, element); + } + + /** + * @description マスクを開始準備 + * Prepare to start drawing the mask + * + * @return {void} + * @method + * @public + */ + beginMask (): void + { + maskBeginMaskService(); + } + + /** + * @description マスクの描画を開始 + * Start drawing the mask + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @return {void} + * @method + * @public + */ + setMaskBounds ( + x_min: number, + y_min: number, + x_max: number, + y_max: number + ): void { + maskSetMaskBoundsService(x_min, y_min, x_max, y_max); + } + + /** + * @description マスクの描画を終了 + * End mask drawing + * + * @return {void} + * @method + * @public + */ + endMask (): void + { + maskEndMaskService(); + } + + /** + * @description マスクの終了処理 + * Mask end processing + * + * @return {void} + * @method + * @public + */ + leaveMask (): void + { + this.drawArraysInstanced(); + maskLeaveMaskUseCase(); + } + + /** + * @description グリッドの描画データをセット + * Set the grid drawing data + * + * @param {Float32Array} grid_data + * @return {void} + * @method + * @public + */ + useGrid (grid_data: Float32Array | null): void + { + contextUseGridService(grid_data); + } + + /** + * @description フィルターを適用 + * Apply the filter + * + * @param {Node} node + * @param {string} unique_key + * @param {boolean} updated + * @param {number} width + * @param {number} height + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {IBlendMode} blend_mode + * @param {Float32Array} bounds + * @param {Float32Array} params + * @return {void} + * @method + * @public + */ + applyFilter ( + node: Node, + unique_key: string, + updated: boolean, + width: number, + height: number, + is_bitmap: boolean, + matrix: Float32Array, + color_transform: Float32Array, + blend_mode: IBlendMode, + bounds: Float32Array, + params: Float32Array + ): void { + this.drawArraysInstanced(); + contextApplyFilterUseCase( + node, unique_key, updated, + width, height, is_bitmap, + matrix, color_transform, blend_mode, + bounds, params + ); + } + + /** + * @description 現在のメインのframe bufferからImageBitmapを生成 + * Generate an ImageBitmap from the current main frame buffer + * + * @param {number} width + * @param {number} height + * @return {Promise} + * @method + * @public + */ + async createImageBitmap (width: number, height: number): Promise + { + return await contextCreateImageBitmapService(width, height); + } +} \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.test.ts b/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.test.ts new file mode 100644 index 00000000..6d7152f0 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./ContextBeginNodeRenderingService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextBeginNodeRenderingService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "SCISSOR_TEST": "SCISSOR_TEST", + "enable": vi.fn((v) => { + expect(v).toBe("SCISSOR_TEST"); + }), + "scissor": vi.fn((x, y, w, h) => { + expect(x).toBe(1); + expect(y).toBe(2); + if (w === 3) { + expect(w).toBe(3); + expect(h).toBe(4); + } else { + expect(w).toBe(4); + expect(h).toBe(5); + } + + }), + "clear": vi.fn((v) => { return "clear"; }), + "viewport": vi.fn((x, y, w, h) => { + expect(x).toBe(1); + expect(y).toBe(2); + expect(w).toBe(3); + expect(h).toBe(4); + }) + } + } + }); + }); + + execute(1, 2, 3, 4); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.ts b/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.ts new file mode 100644 index 00000000..a077f308 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextBeginNodeRenderingService.ts @@ -0,0 +1,27 @@ + +import { $gl } from "../../WebGLUtil"; + +/** + * @description 描画範囲を設定 + * Set the drawing range. + * + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number, w: number, h: number): void => +{ + // 初期化範囲を設定 + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor(x, y, w + 1, h + 1); + + // 初期化 + $gl.clear($gl.COLOR_BUFFER_BIT | $gl.STENCIL_BUFFER_BIT); + + // 描画領域をあらためて設定 + $gl.scissor(x, y, w, h); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextCreateImageBitmapService.ts b/packages/webgl/src/Context/service/ContextCreateImageBitmapService.ts new file mode 100644 index 00000000..56107cee --- /dev/null +++ b/packages/webgl/src/Context/service/ContextCreateImageBitmapService.ts @@ -0,0 +1,88 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as textureManagerGetMainTextureFromBoundsUseCase } from "../../TextureManager/usecase/TextureManagerGetMainTextureFromBoundsUseCase"; +import { + $readFrameBuffer, + $getPixelFrameBuffer +} from "../../FrameBufferManager"; +import { + $context, + $gl, + $upperPowerOfTwo +} from "../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +let $byteLength: number = 0; + +/** + * @description OffscreenCanvas に描画して返却 + * Draw to OffscreenCanvas and return + * + * @param {number} width + * @param {number} height + * @return {Promise} + * @method + * @protected + */ +export const execute = async (width: number, height: number): Promise => +{ + // fixed logic + const mainTextureObject = textureManagerGetMainTextureFromBoundsUseCase(0, 0, width, height); + textureManagerBind0UseCase(mainTextureObject); + + $gl.bindFramebuffer($gl.FRAMEBUFFER, $getPixelFrameBuffer()); + $gl.framebufferTexture2D( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.TEXTURE_2D, mainTextureObject.resource, 0 + ); + + const pixels = new Uint8Array(width * height * 4); + const $mainAttachmentObject = $context.$mainAttachmentObject as IAttachmentObject; + + if (pixels.byteLength > $byteLength) { + $byteLength = $upperPowerOfTwo(pixels.byteLength); + $gl.bufferData($gl.PIXEL_PACK_BUFFER, $byteLength, $gl.STREAM_READ); + } + + $gl.readPixels( + 0, $mainAttachmentObject.height - height, + width, height, $gl.RGBA, $gl.UNSIGNED_BYTE, 0 + ); + + const sync = $gl.fenceSync($gl.SYNC_GPU_COMMANDS_COMPLETE, 0) as WebGLSync; + + // 非同期 readPixels 用 PBO の設定 + await new Promise((resolve): void => + { + const wait = (): void => + { + const status = $gl.clientWaitSync(sync, $gl.SYNC_FLUSH_COMMANDS_BIT, 0); + if (status === $gl.TIMEOUT_EXPIRED) { + requestAnimationFrame(wait); + return ; + } + + $gl.deleteSync(sync); + $gl.getBufferSubData($gl.PIXEL_PACK_BUFFER, 0, pixels); + + return resolve(); + }; + wait(); + }); + $gl.bindFramebuffer($gl.FRAMEBUFFER, $readFrameBuffer); + + // 描画用の OffscreenCanvas に pixelsを描画 + const offscreenCanvas = new OffscreenCanvas(width, height); + const context = offscreenCanvas.getContext("2d") as OffscreenCanvasRenderingContext2D; + + const imageData = new ImageData(new Uint8ClampedArray(pixels), width, height); + context.putImageData(imageData, 0, 0); + + // 反転してImageBitmapを返却 + return await createImageBitmap(offscreenCanvas, { + "imageOrientation": "flipY" + }); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextEndNodeRenderingService.test.ts b/packages/webgl/src/Context/service/ContextEndNodeRenderingService.test.ts new file mode 100644 index 00000000..75ee2084 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextEndNodeRenderingService.test.ts @@ -0,0 +1,24 @@ +import { execute } from "./ContextEndNodeRenderingService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextEndNodeRenderingService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "SCISSOR_TEST": "SCISSOR_TEST", + "disable": vi.fn((v) => { + expect(v).toBe("SCISSOR_TEST"); + }) + } + } + }); + }); + + execute(); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextEndNodeRenderingService.ts b/packages/webgl/src/Context/service/ContextEndNodeRenderingService.ts new file mode 100644 index 00000000..b06be847 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextEndNodeRenderingService.ts @@ -0,0 +1,15 @@ + +import { $gl } from "../../WebGLUtil"; + +/** + * @description アトラスへの描画終了 + * End drawing to the atlas + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $gl.disable($gl.SCISSOR_TEST); +}; diff --git a/packages/webgl/src/Context/service/ContextFillBackgroundColorService.test.ts b/packages/webgl/src/Context/service/ContextFillBackgroundColorService.test.ts new file mode 100644 index 00000000..60ad1f4b --- /dev/null +++ b/packages/webgl/src/Context/service/ContextFillBackgroundColorService.test.ts @@ -0,0 +1,49 @@ +import { execute } from "./ContextFillBackgroundColorService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextFillBackgroundColorService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindFramebuffer": vi.fn(() => { return "bindFramebuffer" }), + "clear": vi.fn(() => { return "clear" }), + "clearColor": vi.fn((red, green, blue, alpha) => + { + if (red === 1) { + expect(red).toBe(1); + expect(green).toBe(2); + expect(blue).toBe(3); + expect(alpha).toBe(4); + } else { + expect(red).toBe(0); + expect(green).toBe(0); + expect(blue).toBe(0); + expect(alpha).toBe(0); + } + }), + } + } + }); + + execute(1, 2, 3, 4); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextFillBackgroundColorService.ts b/packages/webgl/src/Context/service/ContextFillBackgroundColorService.ts new file mode 100644 index 00000000..5439c3b7 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextFillBackgroundColorService.ts @@ -0,0 +1,20 @@ +import { $gl } from "../../WebGLUtil"; + +/** + * @description 背景色を更新 + * Update background color + * + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {void} + * @method + * @protected + */ +export const execute = (red: number, green: number, blue: number, alpha: number): void => +{ + $gl.clearColor(red, green, blue, alpha); + $gl.clear($gl.COLOR_BUFFER_BIT | $gl.STENCIL_BUFFER_BIT); + $gl.clearColor(0, 0, 0, 0); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextResetService.test.ts b/packages/webgl/src/Context/service/ContextResetService.test.ts new file mode 100644 index 00000000..9a5ba039 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextResetService.test.ts @@ -0,0 +1,55 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextResetService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextResetService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + + context.globalAlpha = 0.5; + context.globalCompositeOperation = "multiply"; + context.imageSmoothingEnabled = true; + + expect(context.globalAlpha).toBe(0.5); + expect(context.globalCompositeOperation).toBe("multiply"); + expect(context.imageSmoothingEnabled).toBe(true); + + execute(context); + + expect(context.globalAlpha).toBe(1); + expect(context.globalCompositeOperation).toBe("normal"); + expect(context.imageSmoothingEnabled).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextResetService.ts b/packages/webgl/src/Context/service/ContextResetService.ts new file mode 100644 index 00000000..1e8e10cd --- /dev/null +++ b/packages/webgl/src/Context/service/ContextResetService.ts @@ -0,0 +1,17 @@ +import type { Context } from "../../Context"; + +/** + * @description コンテキストの値を初期化する + * Initialize the values of the context + * + * @param {Context} context + * @return {void} + * @method + * @protected + */ +export const execute = (context: Context): void => +{ + context.globalAlpha = 1; + context.globalCompositeOperation = "normal"; + context.imageSmoothingEnabled = false; +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextResetStyleService.test.ts b/packages/webgl/src/Context/service/ContextResetStyleService.test.ts new file mode 100644 index 00000000..d49d9e37 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextResetStyleService.test.ts @@ -0,0 +1,63 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextResetStyleService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextResetStyleService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + context.$fillStyle.fill(0.5); + context.$strokeStyle.fill(0.5); + + expect(context.$fillStyle[0]).toBe(0.5); + expect(context.$fillStyle[1]).toBe(0.5); + expect(context.$fillStyle[2]).toBe(0.5); + expect(context.$fillStyle[3]).toBe(0.5); + expect(context.$strokeStyle[0]).toBe(0.5); + expect(context.$strokeStyle[1]).toBe(0.5); + expect(context.$strokeStyle[2]).toBe(0.5); + expect(context.$strokeStyle[3]).toBe(0.5); + + execute(context); + + expect(context.$fillStyle[0]).toBe(1); + expect(context.$fillStyle[1]).toBe(1); + expect(context.$fillStyle[2]).toBe(1); + expect(context.$fillStyle[3]).toBe(1); + expect(context.$strokeStyle[0]).toBe(1); + expect(context.$strokeStyle[1]).toBe(1); + expect(context.$strokeStyle[2]).toBe(1); + expect(context.$strokeStyle[3]).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextResetStyleService.ts b/packages/webgl/src/Context/service/ContextResetStyleService.ts new file mode 100644 index 00000000..581e9c3f --- /dev/null +++ b/packages/webgl/src/Context/service/ContextResetStyleService.ts @@ -0,0 +1,16 @@ +import type { Context } from "../../Context"; + +/** + * @description コンテキストのカラースタイル設定を初期化する + * Initialize the color style settings of the context + * + * @param {Context} context + * @return {void} + * @method + * @protected + */ +export const execute = (context: Context): void => +{ + context.$fillStyle.fill(1); + context.$strokeStyle.fill(1); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextRestoreService.test.ts b/packages/webgl/src/Context/service/ContextRestoreService.test.ts new file mode 100644 index 00000000..b2d4812e --- /dev/null +++ b/packages/webgl/src/Context/service/ContextRestoreService.test.ts @@ -0,0 +1,51 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextRestoreService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextRestoreService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + + context.$stack.length = 0; + context.$stack.push(new Float32Array([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 0 + ])) + expect(context.$stack.length).toBe(1); + execute(context); + expect(context.$stack.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextRestoreService.ts b/packages/webgl/src/Context/service/ContextRestoreService.ts new file mode 100644 index 00000000..6d47fea4 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextRestoreService.ts @@ -0,0 +1,28 @@ +import type { Context } from "../../Context"; +import { $poolFloat32Array9 } from "../../WebGLUtil"; + +/** + * @description 2D変換行列をスタックから復元します + * Restore the 2D transformation matrix from the stack + * + * @param {Context} context + * @return {void} + * @method + * @protected + */ +export const execute = (context: Context): void => +{ + if (!context.$stack.length) { + return ; + } + + const matrix = context.$stack.pop() as Float32Array; + context.$matrix[0] = matrix[0]; + context.$matrix[1] = matrix[1]; + context.$matrix[3] = matrix[3]; + context.$matrix[4] = matrix[4]; + context.$matrix[6] = matrix[6]; + context.$matrix[7] = matrix[7]; + + $poolFloat32Array9(matrix); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextSaveService.test.ts b/packages/webgl/src/Context/service/ContextSaveService.test.ts new file mode 100644 index 00000000..469e1de1 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextSaveService.test.ts @@ -0,0 +1,46 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextSaveService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextSaveService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + + context.$stack.length = 0; + expect(context.$stack.length).toBe(0); + execute(context); + expect(context.$stack.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextSaveService.ts b/packages/webgl/src/Context/service/ContextSaveService.ts new file mode 100644 index 00000000..0bc13baa --- /dev/null +++ b/packages/webgl/src/Context/service/ContextSaveService.ts @@ -0,0 +1,16 @@ +import type { Context } from "../../Context"; +import { $getFloat32Array9 } from "../../WebGLUtil"; + +/** + * @description 2D変換行列をスタックに保存します + * Save the 2D transformation matrix to the stack + * + * @param {Context} context + * @return {void} + * @method + * @protected + */ +export const execute = (context: Context): void => +{ + context.$stack.push($getFloat32Array9(...context.$matrix)); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextSetTransformService.test.ts b/packages/webgl/src/Context/service/ContextSetTransformService.test.ts new file mode 100644 index 00000000..20c6d806 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextSetTransformService.test.ts @@ -0,0 +1,33 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextSetTransformService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextSetTransformService.js method test", () => +{ + it("test case", () => + { + const matrix = new Float32Array(9); + + expect(matrix[0]).toBe(0); + expect(matrix[1]).toBe(0); + expect(matrix[2]).toBe(0); + expect(matrix[3]).toBe(0); + expect(matrix[4]).toBe(0); + expect(matrix[5]).toBe(0); + expect(matrix[6]).toBe(0); + expect(matrix[7]).toBe(0); + expect(matrix[8]).toBe(0); + + execute(matrix, 1, 2, 3, 4, 5, 6); + + expect(matrix[0]).toBe(1); + expect(matrix[1]).toBe(2); + expect(matrix[2]).toBe(0); + expect(matrix[3]).toBe(3); + expect(matrix[4]).toBe(4); + expect(matrix[5]).toBe(0); + expect(matrix[6]).toBe(5); + expect(matrix[7]).toBe(6); + expect(matrix[8]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextSetTransformService.ts b/packages/webgl/src/Context/service/ContextSetTransformService.ts new file mode 100644 index 00000000..ffdf8f54 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextSetTransformService.ts @@ -0,0 +1,27 @@ +/** + * @description 2D変換行列を設定します + * Set the 2D transformation matrix + * + * @param {Float32Array} matrix + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} e + * @param {number} f + * @return {void} + * @method + * @protected + */ +export const execute = ( + matrix: Float32Array, + a: number, b: number, c: number, + d: number, e: number, f: number +): void => { + matrix[0] = a; + matrix[1] = b; + matrix[3] = c; + matrix[4] = d; + matrix[6] = e; + matrix[7] = f; +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextTransformService.test.ts b/packages/webgl/src/Context/service/ContextTransformService.test.ts new file mode 100644 index 00000000..d0228345 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextTransformService.test.ts @@ -0,0 +1,66 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextTransformService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextTransformService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + + context.$matrix.fill(3); + context.$matrix[6] = 100; + context.$matrix[7] = 200; + expect(context.$matrix[0]).toBe(3); + expect(context.$matrix[1]).toBe(3); + expect(context.$matrix[2]).toBe(3); + expect(context.$matrix[3]).toBe(3); + expect(context.$matrix[4]).toBe(3); + expect(context.$matrix[5]).toBe(3); + expect(context.$matrix[6]).toBe(100); + expect(context.$matrix[7]).toBe(200); + expect(context.$matrix[8]).toBe(3); + + execute(context, 9, 3, 5, 2, 50, 300); + + expect(context.$matrix[0]).toBe(36); + expect(context.$matrix[1]).toBe(36); + expect(context.$matrix[2]).toBe(3); + expect(context.$matrix[3]).toBe(21); + expect(context.$matrix[4]).toBe(21); + expect(context.$matrix[5]).toBe(3); + expect(context.$matrix[6]).toBe(1150); + expect(context.$matrix[7]).toBe(1250); + expect(context.$matrix[8]).toBe(3); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextTransformService.ts b/packages/webgl/src/Context/service/ContextTransformService.ts new file mode 100644 index 00000000..a27e3403 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextTransformService.ts @@ -0,0 +1,37 @@ +import type { Context } from "../../Context"; + +/** + * @description 2D変換行列の乗算を行います + * Multiply the 2D transformation matrix + * + * @param {Context} context + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} e + * @param {number} f + * @return {void} + * @method + * @protected + */ +export const execute = ( + context: Context, + a: number, b: number, c: number, + d: number, e: number, f: number +): void => { + + const a00: number = context.$matrix[0]; + const a01: number = context.$matrix[1]; + const a10: number = context.$matrix[3]; + const a11: number = context.$matrix[4]; + const a20: number = context.$matrix[6]; + const a21: number = context.$matrix[7]; + + context.$matrix[0] = a * a00 + b * a10; + context.$matrix[1] = a * a01 + b * a11; + context.$matrix[3] = c * a00 + d * a10; + context.$matrix[4] = c * a01 + d * a11; + context.$matrix[6] = e * a00 + f * a10 + a20; + context.$matrix[7] = e * a01 + f * a11 + a21; +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextUpdateAllTransferBoundsService.ts b/packages/webgl/src/Context/service/ContextUpdateAllTransferBoundsService.ts new file mode 100644 index 00000000..108de9e4 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextUpdateAllTransferBoundsService.ts @@ -0,0 +1,25 @@ +import type { Node } from "@next2d/texture-packer"; +import { $getActiveAllTransferBounds } from "../../AtlasManager"; + +/** + * @description 切り替え時の転写範囲を更新します。 + * Update the transfer range when switching. + * + * @param {Node} node + * @return {void} + * @method + * @protected + */ +export const execute = (node: Node): void => +{ + const bounds = $getActiveAllTransferBounds(node.index); + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + + bounds[0] = Math.min(node.x, xMin); + bounds[1] = Math.min(node.y, yMin); + bounds[2] = Math.max(node.x + node.w, xMax); + bounds[3] = Math.max(node.y + node.h, yMax); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.test.ts b/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.test.ts new file mode 100644 index 00000000..0f602be5 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.test.ts @@ -0,0 +1,50 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextUpdateBackgroundColorService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextUpdateBackgroundColorService.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + expect(context.$clearColorR).toBe(0); + expect(context.$clearColorG).toBe(0); + expect(context.$clearColorB).toBe(0); + expect(context.$clearColorA).toBe(0); + execute(context, 1, 1, 1, 1); + expect(context.$clearColorR).toBe(1); + expect(context.$clearColorG).toBe(1); + expect(context.$clearColorB).toBe(1); + expect(context.$clearColorA).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.ts b/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.ts new file mode 100644 index 00000000..93778b3f --- /dev/null +++ b/packages/webgl/src/Context/service/ContextUpdateBackgroundColorService.ts @@ -0,0 +1,28 @@ +import type { Context } from "../../Context"; +import { $clamp } from "../../WebGLUtil"; + +/** + * @description 背景色を設定を更新 + * Update background color setting + * + * @param {Context} context + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {void} + * @method + * @protected + */ +export const execute = ( + context: Context, + red: number, + green: number, + blue: number, + alpha: number +): void => { + context.$clearColorR = $clamp(red, 0, 1, 0); + context.$clearColorG = $clamp(green, 0, 1, 0); + context.$clearColorB = $clamp(blue, 0, 1, 0); + context.$clearColorA = $clamp(alpha, 0, 1, 0); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextUpdateTransferBoundsService.ts b/packages/webgl/src/Context/service/ContextUpdateTransferBoundsService.ts new file mode 100644 index 00000000..4ac4112f --- /dev/null +++ b/packages/webgl/src/Context/service/ContextUpdateTransferBoundsService.ts @@ -0,0 +1,25 @@ +import type { Node } from "@next2d/texture-packer"; +import { $getActiveTransferBounds } from "../../AtlasManager"; + +/** + * @description 転写範囲を更新します。 + * Update the transfer range. + * + * @param {Node} node + * @return {void} + * @method + * @protected + */ +export const execute = (node: Node): void => +{ + const bounds = $getActiveTransferBounds(node.index); + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + + bounds[0] = Math.min(node.x, xMin); + bounds[1] = Math.min(node.y, yMin); + bounds[2] = Math.max(node.x + node.w, xMax); + bounds[3] = Math.max(node.y + node.h, yMax); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/service/ContextUseGridService.ts b/packages/webgl/src/Context/service/ContextUseGridService.ts new file mode 100644 index 00000000..50e88d63 --- /dev/null +++ b/packages/webgl/src/Context/service/ContextUseGridService.ts @@ -0,0 +1,16 @@ +import { $gridDataMap } from "../../Grid"; +import { $fillBufferIndexes } from "../../Mesh"; + +/** + * @description Gridデータを設定 + * Set Grid data + * + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @protected + */ +export const execute = (grid_data: Float32Array | null): void => +{ + $gridDataMap.set($fillBufferIndexes.length, grid_data); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextApplyFilterUseCase.ts b/packages/webgl/src/Context/usecase/ContextApplyFilterUseCase.ts new file mode 100644 index 00000000..99963e2b --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextApplyFilterUseCase.ts @@ -0,0 +1,328 @@ +import type { Node } from "@next2d/texture-packer"; +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IBlendMode } from "../../interface/IBlendMode"; +import { execute as frameBufferManagerGetTextureFromNodeUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetTextureFromNodeUseCase"; +import { execute as filterApplyBlurFilterUseCase } from "../../Filter/BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsBlendMatrixTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService"; +import { execute as shaderManagerSetMatrixTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as filterApplyColorMatrixFilterUseCase } from "../../Filter/ColorMatrixFilter/usecase/FilterApplyColorMatrixFilterUseCase"; +import { execute as filterApplyGlowFilterUseCase } from "../../Filter/GlowFilter/usecase/FilterApplyGlowFilterUseCase"; +import { execute as filterApplyDropShadowFilterUseCase } from "../../Filter/DropShadowFilter/usecase/FilterApplyDropShadowFilterUseCase"; +import { execute as filterApplyBevelFilterUseCase } from "../../Filter/BevelFilter/usecase/FilterApplyBevelFilterUseCase"; +import { execute as filterApplyGradientBevelFilterUseCase } from "../../Filter/GradientBevelFilter/usecase/FilterApplyGradientBevelFilterUseCase"; +import { execute as filterApplyGradientGlowFilterUseCase } from "../../Filter/GradientGlowFilter/usecase/FilterApplyGradientGlowFilterUseCase"; +import { execute as filterApplyConvolutionFilterUseCase } from "../../Filter/ConvolutionFilter/usecase/FilterApplyConvolutionFilterUseCase"; +import { execute as filterApplyDisplacementMapFilterUseCase } from "../../Filter/DisplacementMapFilter/usecase/FilterApplyDisplacementMapFilterUseCase"; +import { execute as blendDrawFilterToMainUseCase } from "../../Blend/usecase/BlendDrawFilterToMainUseCase"; +import { $cacheStore } from "@next2d/cache"; +import { $offset } from "../../Filter"; +import { + $context, + $getFloat32Array6, + $getDevicePixelRatio, + $multiplyMatrices, + $poolFloat32Array6 +} from "../../WebGLUtil"; + +/** + * @description フィルターを適用します。 + * Apply the filter. + * + * @param {Node} node + * @param {string} unique_key + * @param {boolean} updated + * @param {number} width + * @param {number} height + * @param {Float32Array} matrix + * @param {Float32Array} color_transform + * @param {IBlendMode} blend_mode + * @param {Float32Array} bounds + * @param {Float32Array} params + * @return {void} + * @method + * @protected + */ +export const execute = ( + node: Node, + unique_key: string, + updated: boolean, + width: number, + height: number, + is_bitmap: boolean, + matrix: Float32Array, + color_transform: Float32Array, + blend_mode: IBlendMode, + bounds: Float32Array, + params: Float32Array +): void => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + let textureObject: ITextureObject | null = null; + const scaleX = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const scaleY = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + const key = $cacheStore.generateFilterKeys( + matrix[0], matrix[1], matrix[2], matrix[3] + ); + + let useCache = false; + const fKey = $cacheStore.get(unique_key, "fKey"); + if (fKey === key) { + const cacheTextureObject = $cacheStore.get(unique_key, "fTexture") as ITextureObject; + if (updated) { + textureManagerReleaseTextureObjectUseCase(cacheTextureObject); + } else { + useCache = true; + textureObject = cacheTextureObject; + } + } + + let offsetX = 0; + let offsetY = 0; + if (!useCache) { + + // 描画元のテクスチャを取得 + textureObject = frameBufferManagerGetTextureFromNodeUseCase(node); + + const radianX = Math.atan2(matrix[1], matrix[0]); + const radianY = Math.atan2(-matrix[2], matrix[3]); + + const a0 = is_bitmap ? scaleX * Math.cos(radianX) : Math.cos(radianX); + const b1 = is_bitmap ? scaleX * Math.sin(radianX) : Math.sin(radianX); + const c2 = is_bitmap ? -scaleY * Math.sin(radianY) : -Math.sin(radianY); + const d3 = is_bitmap ? scaleY * Math.cos(radianY) : Math.cos(radianY); + const a = $getFloat32Array6( + a0, b1, c2, d3, + width / 2, + height / 2 + ); + + const b = $getFloat32Array6( + 1, 0, 0, 1, + -node.w / 2, + -node.h / 2 + ); + + const tMatrix = $multiplyMatrices(a, b); + $poolFloat32Array6(a); + $poolFloat32Array6(b); + if (tMatrix[0] !== 1 || tMatrix[1] !== 0 || tMatrix[2] !== 0 || tMatrix[3] !== 1) { + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase( + width, height, false + ); + + $context.bind(attachmentObject); + + $context.reset(); + $context.setTransform( + tMatrix[0], tMatrix[1], + tMatrix[2], tMatrix[3], + tMatrix[4], tMatrix[5] + ); + + offsetX = tMatrix[4]; + offsetY = tMatrix[5]; + + textureManagerBind0UseCase(textureObject); + + // 元の描画をフィルター用のテクスチャに描画 + const shaderManager = variantsBlendMatrixTextureShaderService(); + shaderManagerSetMatrixTextureUniformService( + shaderManager, textureObject.width, textureObject.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + // 元のテクスチャを解放 + textureManagerReleaseTextureObjectUseCase(textureObject); + + // フィルター用のテクスチャをセットしてコピー用のAttachmentObjectをリリース + textureObject = attachmentObject.texture as ITextureObject; + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + } + + $poolFloat32Array6(tMatrix); + + // オフセットを初期化 + $offset.x = 0; + $offset.y = 0; + + // フィルターを適用 + for (let idx = 0; params.length > idx; ) { + + const type = params[idx++]; + switch (type) { + + case 0: // BevelFilter + textureObject = filterApplyBevelFilterUseCase( + textureObject, matrix, + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], Boolean(params[idx++]) + ); + break; + + case 1: // BlurFilter + textureObject = filterApplyBlurFilterUseCase( + textureObject, matrix, params[idx++], params[idx++], params[idx++] + ); + break; + + case 2: // ColorMatrixFilter + textureObject = filterApplyColorMatrixFilterUseCase( + textureObject, + new Float32Array([ + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++] + ]) + ); + break; + + case 3: // ConvolutionFilter + { + const matrix_x = params[idx++]; + const matrix_y = params[idx++]; + + const length = matrix_x * matrix_y; + const matrix = params.subarray(idx, idx + length); + idx += length; + + textureObject = filterApplyConvolutionFilterUseCase( + textureObject, matrix_x, matrix_y, matrix, + params[idx++], params[idx++], Boolean(params[idx++]), Boolean(params[idx++]), + params[idx++], params[idx++] + ); + } + break; + + case 4: // DisplacementMapFilter + { + const length = params[idx++]; + const buffer = new Uint8Array(length); + buffer.set(params.subarray(idx, idx + length)); + idx += length; + + textureObject = filterApplyDisplacementMapFilterUseCase( + textureObject, matrix, buffer, params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++] + ); + } + break; + + case 5: // DropShadowFilter + textureObject = filterApplyDropShadowFilterUseCase( + textureObject, matrix, + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], params[idx++], params[idx++], + Boolean(params[idx++]), Boolean(params[idx++]), Boolean(params[idx++]) + ); + break; + + case 6: // GlowFilter + textureObject = filterApplyGlowFilterUseCase( + textureObject, matrix, + params[idx++], params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], Boolean(params[idx++]), Boolean(params[idx++]) + ); + break; + + case 7: // GradientBevelFilter + { + const distance = params[idx++]; + const angle = params[idx++]; + + let length = params[idx++]; + const colors = params.subarray(idx, idx + length); + idx += length; + + length = params[idx++]; + const alphas = params.subarray(idx, idx + length); + idx += length; + + length = params[idx++]; + const ratios = params.subarray(idx, idx + length); + idx += length; + + textureObject = filterApplyGradientBevelFilterUseCase( + textureObject, matrix, + distance, angle, colors, alphas, ratios, + params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], Boolean(params[idx++]) + ); + } + break; + + case 8: // GradientGlowFilter + { + const distance = params[idx++]; + const angle = params[idx++]; + + let length = params[idx++]; + const colors = params.subarray(idx, idx + length); + idx += length; + + length = params[idx++]; + const alphas = params.subarray(idx, idx + length); + idx += length; + + length = params[idx++]; + const ratios = params.subarray(idx, idx + length); + idx += length; + + textureObject = filterApplyGradientGlowFilterUseCase( + textureObject, matrix, + distance, angle, colors, alphas, ratios, + params[idx++], params[idx++], params[idx++], + params[idx++], params[idx++], Boolean(params[idx++]) + ); + } + break; + + } + } + } else { + offsetX = $cacheStore.get(unique_key, "offsetX"); + offsetY = $cacheStore.get(unique_key, "offsetY"); + } + + if (textureObject) { + + const devicePixelRatio = $getDevicePixelRatio(); + const xMin = bounds[0] * (scaleX / devicePixelRatio); + const yMin = bounds[1] * (scaleY / devicePixelRatio); + + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + $context.globalCompositeOperation = blend_mode; + blendDrawFilterToMainUseCase( + textureObject, color_transform, + -offsetX + xMin + matrix[4], + -offsetY + yMin + matrix[5] + ); + } + + if (!useCache) { + $cacheStore.set(unique_key, "fKey", key); + $cacheStore.set(unique_key, "fTexture", textureObject); + $cacheStore.set(unique_key, "offsetX", offsetX); + $cacheStore.set(unique_key, "offsetY", offsetY); + } + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextBindUseCase.test.ts b/packages/webgl/src/Context/usecase/ContextBindUseCase.test.ts new file mode 100644 index 00000000..61386234 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextBindUseCase.test.ts @@ -0,0 +1,84 @@ +import { Context } from "../../Context"; +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { execute } from "./ContextBindUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { + $setCurrentAttachment, + $getCurrentAttachment +} from "../../FrameBufferManager"; + +describe("ContextBindUseCase.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + "enable": vi.fn(() => "enable"), + "scissor": vi.fn(() => "scissor"), + "clear": vi.fn(() => "clear"), + "disable": vi.fn(() => "disable"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "blendFunc": vi.fn(() => "blendFunc"), + "viewport": vi.fn((x, y, w, h) => + { + expect(x).toBe(0); + expect(y).toBe(0); + expect(w).toBe(100); + expect(h).toBe(200); + }), + } as unknown as WebGL2RenderingContext; + + $setCurrentAttachment(null); + expect($getCurrentAttachment()).toBe(null); + + const context = new Context(mockGL, 4); + const attachment_object: IAttachmentObject = { + "width": 100, + "height": 200, + "clipLevel": 0, + "msaa": false, + "mask": false, + "texture": { + "resource": "createRenderbuffer", + "width": 0, + "height": 0, + "area": 0 + }, + "color": null, + "stencil": { + "resource": "createRenderbuffer", + "width": 0, + "height": 0, + "area": 0, + "dirty": true, + } + }; + + execute(context, attachment_object); + + expect($getCurrentAttachment()).toBe(attachment_object); + expect(attachment_object.stencil?.dirty).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextBindUseCase.ts b/packages/webgl/src/Context/usecase/ContextBindUseCase.ts new file mode 100644 index 00000000..4e28e5e2 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextBindUseCase.ts @@ -0,0 +1,60 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import type { Context } from "../../Context"; +import { $getCurrentAttachment } from "../../FrameBufferManager"; +import { execute as frameBufferManagerBindAttachmentObjectService } from "../../FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService"; +import { execute as maskBindUseCase } from "../../Mask/usecase/MaskBindUseCase"; +import { + $gl, + $setViewportSize +} from "../../WebGLUtil"; + +/** + * @description アタッチメントオブジェクトのサイズに変更・初期化し、フレームバッファにアタッチメントオブジェクトをバインドする + * Change and initialize to the size of the attachment object and bind the attachment object to the frame buffer + * + * @param {Context} context + * @param {IAttachmentObject} attachment_object + * @return {void} + * @method + * @protected + */ +export const execute = (context: Context, attachment_object: IAttachmentObject): void => +{ + // fixed logic + const currentAttachment = $getCurrentAttachment(); + if (currentAttachment + && attachment_object.id === currentAttachment.id + ) { + return ; + } + + // フレームバッファにアタッチメントオブジェクトをバインドする + frameBufferManagerBindAttachmentObjectService(attachment_object); + + if (!currentAttachment + || currentAttachment.width !== attachment_object.width + || currentAttachment.height !== attachment_object.height) + { + $setViewportSize(attachment_object.width, attachment_object.height); + $gl.viewport(0, 0, attachment_object.width, attachment_object.height); + } + + // カラーバッファorステンシルバッファが、未初期化の場合はクリアする + const object = attachment_object.msaa + ? attachment_object.color as IColorBufferObject + : attachment_object.stencil as IStencilBufferObject; + + // 再利用のオブジェクトの場合は、描画情報をクリアする + if (object.dirty) { + + object.dirty = false; + + // 無色透明で初期化 + context.clearRect(0, 0, attachment_object.width, attachment_object.height); + } + + // mask bind + maskBindUseCase(attachment_object.mask); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextBitmapFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextBitmapFillUseCase.ts new file mode 100644 index 00000000..fbe6d383 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextBitmapFillUseCase.ts @@ -0,0 +1,50 @@ +import { $getVertices } from "../../PathCommand"; +import { execute as meshFillGenerateUseCase } from "../../Mesh/usecase/MeshFillGenerateUseCase"; +import { $bitmapData } from "../../Bitmap"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description パスコマンドのビットマップ塗り実行します。 + * Execute bitmap painting of path commands. + * + * @param {Uint8Array} pixels + * @param {Float32Array} matrix + * @param {number} width + * @param {number} height + * @param {boolean} repeat + * @param {boolean} smooth + * @return {void} + * @method + * @protected + */ +export const execute = ( + pixels: Uint8Array, + matrix: Float32Array, + width: number, + height: number, + repeat: boolean, + smooth: boolean +): void => { + + const vertices = $getVertices(); + if (!vertices.length) { + return ; + } + + // 塗りの種類を追加 + $fillTypes.push("bitmap"); + + const fillMesh = meshFillGenerateUseCase(vertices); + $addFillBuffer(fillMesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(fillMesh.indexCount); + + $bitmapData.push( + pixels, matrix, width, height, repeat, smooth + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextBitmapStrokeUseCase.ts b/packages/webgl/src/Context/usecase/ContextBitmapStrokeUseCase.ts new file mode 100644 index 00000000..e806991d --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextBitmapStrokeUseCase.ts @@ -0,0 +1,50 @@ +import { $getVertices } from "../../PathCommand"; +import { execute as meshStrokeGenerateUseCase } from "../../Mesh/usecase/MeshStrokeGenerateUseCase"; +import { $bitmapData } from "../../Bitmap"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description パスコマンドの線のビットマップの描画を実行します。 + * Execute drawing of bitmap of line of path command. + * + * @param {Uint8Array} pixels + * @param {Float32Array} matrix + * @param {number} width + * @param {number} height + * @param {boolean} repeat + * @param {boolean} smooth + * @return {void} + * @method + * @protected + */ +export const execute = ( + pixels: Uint8Array, + matrix: Float32Array, + width: number, + height: number, + repeat: boolean, + smooth: boolean +): void => { + + const vertices = $getVertices(true); + if (!vertices.length) { + return ; + } + + // 塗りの種類を追加 + $fillTypes.push("bitmap"); + + const mesh = meshStrokeGenerateUseCase(vertices); + $addFillBuffer(mesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(mesh.indexCount); + + $bitmapData.push( + pixels, matrix, width, height, repeat, smooth + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextClearRectUseCase.test.ts b/packages/webgl/src/Context/usecase/ContextClearRectUseCase.test.ts new file mode 100644 index 00000000..a6f20d78 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextClearRectUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./ContextClearRectUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextClearRectUseCase.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "enable": vi.fn(() => { return "enable" }), + "clear": vi.fn(() => { return "enable" }), + "disable": vi.fn(() => { return "enable" }), + "scissor": vi.fn((x, y, w, h) => + { + expect(x).toBe(1); + expect(y).toBe(2); + expect(w).toBe(3); + expect(h).toBe(4); + }), + } + } + }); + + execute(1, 2, 3, 4); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextClearRectUseCase.ts b/packages/webgl/src/Context/usecase/ContextClearRectUseCase.ts new file mode 100644 index 00000000..70431621 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextClearRectUseCase.ts @@ -0,0 +1,22 @@ +import { $gl } from "../../WebGLUtil"; + +/** + * @description 指定範囲をクリアする + * Clear the specified range + * + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number, w: number, h: number): void => +{ + // 指定範囲をクリア + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor(x, y, w, h); + $gl.clear($gl.COLOR_BUFFER_BIT | $gl.STENCIL_BUFFER_BIT); + $gl.disable($gl.SCISSOR_TEST); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextClipUseCase.ts b/packages/webgl/src/Context/usecase/ContextClipUseCase.ts new file mode 100644 index 00000000..f657bf9b --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextClipUseCase.ts @@ -0,0 +1,102 @@ +import { execute as vertexArrayObjectCreateFillObjectUseCase } from "../../VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase"; +import { execute as vertexArrayObjectReleaseVertexArrayObjectService } from "../../VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService"; +import { execute as variantsShapeMaskShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeMaskShaderService"; +import { execute as shaderManagerSetMaskUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMaskUniformService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as maskUnionMaskService } from "../../Mask/service/MaskUnionMaskService"; +import { + $clipLevels, + $clipBounds +} from "../../Mask"; +import { + $gl, + $context +} from "../../WebGLUtil"; +import { + $fillBufferIndexes, + $clearFillBufferSetting +} from "../../Mesh"; +import { + $terminateGrid, + $gridDataMap +} from "../../Grid"; + +/** + * @description Contextのパスコマンドのマスク描画を実行します。 + * Execute Context path command mask drawing. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return ; + } + + // レベルと描画範囲をセット + const bounds = $clipBounds.get(currentAttachmentObject.clipLevel) as Float32Array; + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + xMin, + currentAttachmentObject.height - yMin - height, + width, + height + ); + + const vertexArrayObject = vertexArrayObjectCreateFillObjectUseCase(); + let level = $clipLevels.get(currentAttachmentObject.clipLevel) as number; + + let offset = 0; + let gridData: Float32Array | null = null; + + const length = $fillBufferIndexes.length; + for (let idx = 0; idx < length; idx++) { + + $gl.stencilMask(1 << level - 1); + + const indexCount = $fillBufferIndexes[idx]; + + if ($gridDataMap.has(idx)) { + gridData = $gridDataMap.get(idx) as Float32Array | null; + } + const useGrid = !!gridData; + + const shaderManager = variantsShapeMaskShaderService(useGrid); + if (gridData) { + shaderManagerSetMaskUniformService(shaderManager, gridData); + } + shaderManagerFillUseCase( + shaderManager, vertexArrayObject, offset, indexCount + ); + + offset += indexCount; + + ++level; + if (level > 7) { + maskUnionMaskService(); + level = currentAttachmentObject.clipLevel + 1; + } + } + + // update clip level + $clipLevels.set(currentAttachmentObject.clipLevel, level); + + // release vertex array + vertexArrayObjectReleaseVertexArrayObjectService(vertexArrayObject); + + // clear fill buffer setting + $clearFillBufferSetting(); + $terminateGrid(); + + $gl.disable($gl.SCISSOR_TEST); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextDrawElementUseCase.ts b/packages/webgl/src/Context/usecase/ContextDrawElementUseCase.ts new file mode 100644 index 00000000..5629ecdf --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextDrawElementUseCase.ts @@ -0,0 +1,32 @@ +import type { Node } from "@next2d/texture-packer"; +import { execute as textureManagerCreateFromCanvasUseCase } from "../../TextureManager/usecase/TextureManagerCreateFromCanvasUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as variantsBlendTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as blendResetService } from "../../Blend/service/BlendResetService"; + +/** + * @description OffscreenCanvasを描画します。 + * Draw OffscreenCanvas. + * + * @param {Node} node + * @param {OffscreenCanvas | ImageBitmap} element + * @return {void} + * @method + * @protected + */ +export const execute = (node: Node, element: OffscreenCanvas | ImageBitmap): void => +{ + const textureObject = textureManagerCreateFromCanvasUseCase(node.w, node.h, element); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService(shaderManager, node.w, node.h); + + // テクスチャを描画 + blendResetService(); + shaderManagerDrawTextureUseCase(shaderManager); + + // テクスチャオブジェクトを解放 + textureManagerReleaseTextureObjectUseCase(textureObject); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextDrawFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextDrawFillUseCase.ts new file mode 100644 index 00000000..cf0bbf0c --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextDrawFillUseCase.ts @@ -0,0 +1,98 @@ +import { execute as vertexArrayObjectBindFillMeshUseCase } from "../../VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase"; +import { execute as vertexArrayObjectReleaseVertexArrayObjectService } from "../../VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService"; +import { execute as contextNormalFillUseCase } from "./ContextNormalFillUseCase"; +import { execute as contextLinearGradientFillUseCase } from "./ContextLinearGradientFillUseCase"; +import { execute as contextRadialGradientFillUseCase } from "./ContextRadialGradientFillUseCase"; +import { execute as contextPatternBitmapFillUseCase } from "./ContextPatternBitmapFillUseCase"; +import { $gl } from "../../WebGLUtil"; +import { + $terminateGrid, + $gridDataMap +} from "../../Grid"; +import { + $fillBufferIndexes, + $fillTypes, + $clearFillBufferSetting +} from "../../Mesh"; + +/** + * @description Contextのパスコマンドの塗り実行します。 + * Execute Context path command painting. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const fillVertexArrayObject = vertexArrayObjectBindFillMeshUseCase(); + + // mask on + $gl.enable($gl.STENCIL_TEST); + $gl.frontFace($gl.CCW); + $gl.stencilMask(0xff); + + let fillOffset = 0; + let gridData: Float32Array | null = null; + for (let idx = 0; idx < $fillTypes.length; idx++) { + + if ($gridDataMap.has(idx)) { + gridData = $gridDataMap.get(idx) as Float32Array | null; + } + + const type = $fillTypes[idx]; + switch (type) { + + case "fill": // 通常のShapeの塗り + { + const count = $fillBufferIndexes.shift() as number; + contextNormalFillUseCase( + fillVertexArrayObject, fillOffset, count, gridData + ); + fillOffset += count; + } + break; + + case "linear": // 線形グラデーションの塗り + { + const count = $fillBufferIndexes.shift() as number; + contextLinearGradientFillUseCase( + fillVertexArrayObject, fillOffset, count, gridData + ); + fillOffset += count; + } + break; + + case "radial": // 円形グラデーションの塗り + { + const count = $fillBufferIndexes.shift() as number; + contextRadialGradientFillUseCase( + fillVertexArrayObject, fillOffset, count, gridData + ); + fillOffset += count; + } + break; + + case "bitmap": // 画像の塗りつぶし + { + const count = $fillBufferIndexes.shift() as number; + contextPatternBitmapFillUseCase( + fillVertexArrayObject, fillOffset, count, gridData + ); + fillOffset += count; + } + break; + + } + } + + // mask off + $gl.disable($gl.STENCIL_TEST); + + // release vertex array + vertexArrayObjectReleaseVertexArrayObjectService(fillVertexArrayObject); + + // 設定値を初期化 + $clearFillBufferSetting(); + $terminateGrid(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextDrawPixelsUseCase.ts b/packages/webgl/src/Context/usecase/ContextDrawPixelsUseCase.ts new file mode 100644 index 00000000..ad960d82 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextDrawPixelsUseCase.ts @@ -0,0 +1,32 @@ +import type { Node } from "@next2d/texture-packer"; +import { execute as textureManagerCreateFromPixelsUseCase } from "../../TextureManager/usecase/TextureManagerCreateFromPixelsUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as variantsBlendTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as blendResetService } from "../../Blend/service/BlendResetService"; + +/** + * @description ピクセルを描画します。 + * Draw pixels. + * + * @param {Node} node + * @param {Uint8Array} pixels + * @return {void} + * @method + * @protected + */ +export const execute = (node: Node, pixels: Uint8Array): void => +{ + const textureObject = textureManagerCreateFromPixelsUseCase(node.w, node.h, pixels); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService(shaderManager, node.w, node.h); + + // テクスチャを描画 + blendResetService(); + shaderManagerDrawTextureUseCase(shaderManager); + + // テクスチャオブジェクトを解放 + textureManagerReleaseTextureObjectUseCase(textureObject); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextFillUseCase.ts new file mode 100644 index 00000000..643db3d8 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextFillUseCase.ts @@ -0,0 +1,33 @@ +import type { IFillType } from "../../interface/IFillType"; +import { $getVertices } from "../../PathCommand"; +import { execute as meshFillGenerateUseCase } from "../../Mesh/usecase/MeshFillGenerateUseCase"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description Contextのパスコマンドの塗り実行します。 + * Execute Context path command painting. + * + * @return {void} + * @method + * @protected + */ +export const execute = (type: IFillType): void => +{ + const vertices = $getVertices(); + if (!vertices.length) { + return ; + } + + // 塗りの種類を追加 + $fillTypes.push(type); + + const mesh = meshFillGenerateUseCase(vertices); + $addFillBuffer(mesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(mesh.indexCount); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextGradientFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextGradientFillUseCase.ts new file mode 100644 index 00000000..ce1862cc --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextGradientFillUseCase.ts @@ -0,0 +1,51 @@ +import { $getVertices } from "../../PathCommand"; +import { execute as meshFillGenerateUseCase } from "../../Mesh/usecase/MeshFillGenerateUseCase"; +import { $gradientData } from "../../Gradient"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description グラデーション塗りを描画 + * Draw a gradient fill + * + * @param {number} type + * @param {array} stops + * @param {Float32Array} matrix + * @param {number} spread + * @param {number} interpolation + * @param {number} focal + * @return {void} + * @method + * @protected + */ +export const execute = ( + type: number, + stops: number[], + matrix: Float32Array, + spread: number, + interpolation: number, + focal: number +): void => { + + const vertices = $getVertices(); + if (!vertices.length) { + return ; + } + + // 塗りの種類を追加 + $fillTypes.push(type === 0 ? "linear" : "radial"); + + const fillMesh = meshFillGenerateUseCase(vertices); + $addFillBuffer(fillMesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(fillMesh.indexCount); + + $gradientData.push(stops, matrix, spread, interpolation); + if (type === 1) { + $gradientData.push(focal); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextGradientStrokeUseCase.ts b/packages/webgl/src/Context/usecase/ContextGradientStrokeUseCase.ts new file mode 100644 index 00000000..9b86deec --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextGradientStrokeUseCase.ts @@ -0,0 +1,51 @@ +import { $getVertices } from "../../PathCommand"; +import { execute as meshStrokeGenerateUseCase } from "../../Mesh/usecase/MeshStrokeGenerateUseCase"; +import { $gradientData } from "../../Gradient"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description 線のグラデーションを実行 + * Execute gradient of line + * + * @param {number} type + * @param {array} stops + * @param {Float32Array} matrix + * @param {number} spread + * @param {number} interpolation + * @param {number} focal + * @return {void} + * @method + * @protected + */ +export const execute = ( + type: number, + stops: number[], + matrix: Float32Array, + spread: number, + interpolation: number, + focal: number +): void => { + + const vertices = $getVertices(); + if (!vertices.length) { + return ; + } + + // 塗りの種類を追加 + $fillTypes.push(type === 0 ? "linear" : "radial"); + + const mesh = meshStrokeGenerateUseCase(vertices); + $addFillBuffer(mesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(mesh.indexCount); + + $gradientData.push(stops, matrix, spread, interpolation); + if (type === 1) { + $gradientData.push(focal); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextLinearGradientFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextLinearGradientFillUseCase.ts new file mode 100644 index 00000000..6b6f679f --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextLinearGradientFillUseCase.ts @@ -0,0 +1,90 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as gradientLUTGenerateShapeTextureUseCase } from "../../Shader/GradientLUTGenerator/usecase/GradientLUTGenerateShapeTextureUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsShapeMaskShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeMaskShaderService"; +import { execute as shaderManagerSetMaskUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMaskUniformService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as variantsGradientShapeShaderUseCase } from "../../Shader/Variants/Gradient/usecase/VariantsGradientShapeShaderUseCase"; +import { execute as shaderManagerSetGradientFillUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetGradientFillUniformService"; +import { $gradientData } from "../../Gradient"; +import { + $gl, + $linearGradientXY, + $inverseMatrix, + $context, + $poolFloat32Array6, + $poolFloat32Array4 +} from "../../WebGLUtil"; + +/** + * @description 線形グラデーションのシェーダーを実行します。 + * Execute the linear gradient shader. + * + * @param {IVertexArrayObject} vertex_array_object + * @param {number} offset + * @param {number} index_count + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertex_array_object: IVertexArrayObject, + offset: number, + index_count: number, + grid_data: Float32Array | null +): void => { + + const stops = $gradientData.shift() as number[]; + const matrix = $gradientData.shift() as Float32Array; + const spread = $gradientData.shift() as number; + const interpolation = $gradientData.shift() as number; + + $gl.disable($gl.STENCIL_TEST); + const textureObject = gradientLUTGenerateShapeTextureUseCase(stops, interpolation); + textureManagerBind0UseCase(textureObject); + + $gl.enable($gl.STENCIL_TEST); + $gl.frontFace($gl.CCW); + $gl.stencilMask(0xff); + + // mask setting + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOpSeparate($gl.FRONT, $gl.KEEP, $gl.KEEP, $gl.INCR_WRAP); + $gl.stencilOpSeparate($gl.BACK, $gl.KEEP, $gl.KEEP, $gl.DECR_WRAP); + $gl.colorMask(false, false, false, false); + + const useGrid = !!grid_data; + const coverageShader = variantsShapeMaskShaderService(useGrid); + if (grid_data) { + shaderManagerSetMaskUniformService(coverageShader, grid_data); + } + + $gl.enable($gl.SAMPLE_ALPHA_TO_COVERAGE); + shaderManagerFillUseCase( + coverageShader, vertex_array_object, offset, index_count + ); + $gl.disable($gl.SAMPLE_ALPHA_TO_COVERAGE); + + $gl.stencilFunc($gl.NOTEQUAL, 0, 0xff); + $gl.stencilOp($gl.KEEP, $gl.ZERO, $gl.ZERO); + $gl.colorMask(true, true, true, true); + + const shaderManager = variantsGradientShapeShaderUseCase( + false, false, spread, useGrid + ); + + const points = $linearGradientXY(matrix); + const inverseMatrix = $inverseMatrix($context.$matrix); + shaderManagerSetGradientFillUniformService( + shaderManager, 0, $context.$matrix, + inverseMatrix, 0, points, grid_data + ); + + $poolFloat32Array4(points); + $poolFloat32Array6(inverseMatrix); + + shaderManagerFillUseCase( + shaderManager, vertex_array_object, offset, index_count + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextNormalFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextNormalFillUseCase.ts new file mode 100644 index 00000000..391ad575 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextNormalFillUseCase.ts @@ -0,0 +1,58 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as variantsShapeMaskShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeMaskShaderService"; +import { execute as shaderManagerSetMaskUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMaskUniformService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as variantsShapeSolidColorShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeSolidColorShaderService"; +import { execute as shaderManagerSetFillUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetFillUniformService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description 塗りのシェーダーを実行します。 + * Execute the fill shader. + * + * @param {IVertexArrayObject} vertex_array_object + * @param {number} offset + * @param {number} index_count + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertex_array_object: IVertexArrayObject, + offset: number, + index_count: number, + grid_data: Float32Array | null +): void => +{ + // mask setting + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOpSeparate($gl.FRONT, $gl.KEEP, $gl.KEEP, $gl.INCR_WRAP); + $gl.stencilOpSeparate($gl.BACK, $gl.KEEP, $gl.KEEP, $gl.DECR_WRAP); + $gl.colorMask(false, false, false, false); + + const useGrid = !!grid_data; + const coverageShader = variantsShapeMaskShaderService(useGrid); + if (grid_data) { + shaderManagerSetMaskUniformService(coverageShader, grid_data); + } + + $gl.enable($gl.SAMPLE_ALPHA_TO_COVERAGE); + shaderManagerFillUseCase( + coverageShader, vertex_array_object, offset, index_count + ); + $gl.disable($gl.SAMPLE_ALPHA_TO_COVERAGE); + + // draw shape setting + $gl.stencilFunc($gl.NOTEQUAL, 0, 0xff); + $gl.stencilOp($gl.KEEP, $gl.ZERO, $gl.ZERO); + $gl.colorMask(true, true, true, true); + + const shaderManager = variantsShapeSolidColorShaderService(useGrid); + if (grid_data) { + shaderManagerSetFillUniformService(shaderManager, grid_data); + } + shaderManagerFillUseCase( + shaderManager, vertex_array_object, offset, index_count + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextPatternBitmapFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextPatternBitmapFillUseCase.ts new file mode 100644 index 00000000..803a9e72 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextPatternBitmapFillUseCase.ts @@ -0,0 +1,86 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as variantsShapeMaskShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeMaskShaderService"; +import { execute as shaderManagerSetMaskUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMaskUniformService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as variantsBitmapShaderService } from "../../Shader/Variants/Bitmap/service/VariantsBitmapShaderService"; +import { execute as shaderManagerSetBitmapFillUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetBitmapFillUniformService"; +import { execute as textureManagerCreateFromPixelsUseCase } from "../../TextureManager/usecase/TextureManagerCreateFromPixelsUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { $bitmapData } from "../../Bitmap"; +import { + $gl, + $context +} from "../../WebGLUtil"; + +/** + * @description 放射状グラデーションのシェーダーを実行します。 + * Execute the radial gradient shader. + * + * @param {IVertexArrayObject} vertex_array_object + * @param {number} offset + * @param {number} index_count + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertex_array_object: IVertexArrayObject, + offset: number, + index_count: number, + grid_data: Float32Array | null +): void => { + + // mask setting + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOpSeparate($gl.FRONT, $gl.KEEP, $gl.KEEP, $gl.INCR_WRAP); + $gl.stencilOpSeparate($gl.BACK, $gl.KEEP, $gl.KEEP, $gl.DECR_WRAP); + $gl.colorMask(false, false, false, false); + + const useGrid = !!grid_data; + const coverageShader = variantsShapeMaskShaderService(useGrid); + if (grid_data) { + shaderManagerSetMaskUniformService(coverageShader, grid_data); + } + + $gl.enable($gl.SAMPLE_ALPHA_TO_COVERAGE); + shaderManagerFillUseCase( + coverageShader, vertex_array_object, offset, index_count + ); + $gl.disable($gl.SAMPLE_ALPHA_TO_COVERAGE); + + // bitmap setting + const pixels = $bitmapData.shift() as Uint8Array; + const matrix = $bitmapData.shift() as Float32Array; + const width = $bitmapData.shift() as number; + const height = $bitmapData.shift() as number; + const repeat = $bitmapData.shift() as boolean; + const smooth = $bitmapData.shift() as boolean; + + const textureObject = textureManagerCreateFromPixelsUseCase( + width, height, pixels, smooth + ); + + $context.save(); + $context.transform( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); + + // draw shape setting + $gl.stencilFunc($gl.NOTEQUAL, 0, 0xff); + $gl.stencilOp($gl.KEEP, $gl.ZERO, $gl.ZERO); + $gl.colorMask(true, true, true, true); + + const shaderManager = variantsBitmapShaderService(repeat, useGrid); + shaderManagerSetBitmapFillUniformService( + shaderManager, width, height, grid_data + ); + shaderManagerFillUseCase( + shaderManager, vertex_array_object, offset, index_count + ); + + $context.restore(); + + textureManagerReleaseTextureObjectUseCase(textureObject); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextRadialGradientFillUseCase.ts b/packages/webgl/src/Context/usecase/ContextRadialGradientFillUseCase.ts new file mode 100644 index 00000000..ec0df740 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextRadialGradientFillUseCase.ts @@ -0,0 +1,97 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as variantsShapeMaskShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeMaskShaderService"; +import { execute as shaderManagerSetMaskUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetMaskUniformService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as gradientLUTGenerateShapeTextureUseCase } from "../../Shader/GradientLUTGenerator/usecase/GradientLUTGenerateShapeTextureUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsGradientShapeShaderUseCase } from "../../Shader/Variants/Gradient/usecase/VariantsGradientShapeShaderUseCase"; +import { execute as shaderManagerSetGradientFillUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetGradientFillUniformService"; +import { $gradientData } from "../../Gradient"; +import { + $gl, + $inverseMatrix, + $context, + $poolFloat32Array6 +} from "../../WebGLUtil"; + +/** + * @description 放射状グラデーションのシェーダーを実行します。 + * Execute the radial gradient shader. + * + * @param {IVertexArrayObject} vertex_array_object + * @param {number} offset + * @param {number} index_count + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertex_array_object: IVertexArrayObject, + offset: number, + index_count: number, + grid_data: Float32Array | null +): void => { + + const stops = $gradientData.shift() as number[]; + const matrix = $gradientData.shift() as Float32Array; + const spread = $gradientData.shift() as number; + const interpolation = $gradientData.shift() as number; + const focal = $gradientData.shift() as number; + + $gl.disable($gl.STENCIL_TEST); + const textureObject = gradientLUTGenerateShapeTextureUseCase(stops, interpolation); + textureManagerBind0UseCase(textureObject); + + $gl.enable($gl.STENCIL_TEST); + $gl.frontFace($gl.CCW); + $gl.stencilMask(0xff); + + // mask setting + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOpSeparate($gl.FRONT, $gl.KEEP, $gl.KEEP, $gl.INCR_WRAP); + $gl.stencilOpSeparate($gl.BACK, $gl.KEEP, $gl.KEEP, $gl.DECR_WRAP); + $gl.colorMask(false, false, false, false); + + const useGrid = !!grid_data; + const coverageShader = variantsShapeMaskShaderService(useGrid); + if (grid_data) { + shaderManagerSetMaskUniformService(coverageShader, grid_data); + } + + $gl.enable($gl.SAMPLE_ALPHA_TO_COVERAGE); + shaderManagerFillUseCase( + coverageShader, vertex_array_object, offset, index_count + ); + $gl.disable($gl.SAMPLE_ALPHA_TO_COVERAGE); + + $gl.stencilFunc($gl.NOTEQUAL, 0, 0xff); + $gl.stencilOp($gl.KEEP, $gl.ZERO, $gl.ZERO); + $gl.colorMask(true, true, true, true); + + $context.save(); + $context.transform( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); + + const prevMatrix = $context.$stack[$context.$stack.length - 1]; + + const inverseMatrix = $inverseMatrix($context.$matrix); + + const shaderManager = variantsGradientShapeShaderUseCase( + true, Boolean(focal), spread, useGrid + ); + shaderManagerSetGradientFillUniformService( + shaderManager, 1, prevMatrix, + inverseMatrix, focal, grid_data + ); + + $context.restore(); + + $poolFloat32Array6(inverseMatrix); + + shaderManagerFillUseCase( + shaderManager, vertex_array_object, offset, index_count + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextResizeUseCase.test.ts b/packages/webgl/src/Context/usecase/ContextResizeUseCase.test.ts new file mode 100644 index 00000000..9045c364 --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextResizeUseCase.test.ts @@ -0,0 +1,73 @@ +import { Context } from "../../Context"; +import { execute } from "./ContextResizeUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("ContextResizeUseCase.js method test", () => +{ + it("test case", () => + { + const mockGL = { + "createTexture": vi.fn(() => "createTexture"), + "activeTexture": vi.fn(() => "activeTexture"), + "bindTexture": vi.fn(() => "bindTexture"), + "texParameteri": vi.fn(() => "texParameteri"), + "texStorage2D": vi.fn(() => "texStorage2D"), + "getParameter": vi.fn(() => "getParameter"), + "pixelStorei": vi.fn(() => "pixelStorei"), + "createFramebuffer": vi.fn(() => "createFramebuffer"), + "bindFramebuffer": vi.fn(() => "bindFramebuffer"), + "clearColor": vi.fn(() => "clearColor"), + "createRenderbuffer": vi.fn(() => "createRenderbuffer"), + "bindRenderbuffer": vi.fn(() => "bindRenderbuffer"), + "renderbufferStorageMultisample": vi.fn(() => "renderbufferStorageMultisample"), + "framebufferRenderbuffer": vi.fn(() => "framebufferRenderbuffer"), + "viewport": vi.fn(() => "viewport"), + "renderbufferStorage": vi.fn(() => "renderbufferStorage"), + "bindBuffer": vi.fn(() => "bindBuffer"), + "createBuffer": vi.fn(() => "createBuffer"), + "createVertexArray": vi.fn(() => "createVertexArray"), + "bindVertexArray": vi.fn(() => "bindVertexArray"), + "bufferData": vi.fn(() => "bufferData"), + "enableVertexAttribArray": vi.fn(() => "enableVertexAttribArray"), + "vertexAttribPointer": vi.fn(() => "vertexAttribPointer"), + "vertexAttribDivisor": vi.fn(() => "vertexAttribDivisor"), + "enable": vi.fn(() => "enable"), + "blendFunc": vi.fn(() => "blendFunc"), + "framebufferTexture2D": vi.fn(() => "framebufferTexture2D"), + "scissor": vi.fn(() => "scissor"), + "clear": vi.fn(() => "clear"), + "disable": vi.fn(() => "disable"), + "createProgram": vi.fn(() => "createProgram"), + "createShader": vi.fn(() => "createShader"), + "shaderSource": vi.fn(() => "shaderSource"), + "compileShader": vi.fn(() => "compileShader"), + "attachShader": vi.fn(() => "attachShader"), + "linkProgram": vi.fn(() => "linkProgram"), + "detachShader": vi.fn(() => "detachShader"), + "deleteShader": vi.fn(() => "deleteShader"), + "getProgramParameter": vi.fn(() => "getProgramParameter"), + } as unknown as WebGL2RenderingContext; + + const context = new Context(mockGL, 4); + expect(context.$mainAttachmentObject).toBe(null); + execute(context, 100, 200); + + if (!context.$mainAttachmentObject) + { + throw new Error("context.$mainAttachment is null"); + } + + expect(context.$mainAttachmentObject.width).toBe(100); + expect(context.$mainAttachmentObject.height).toBe(200); + expect(context.$mainAttachmentObject.clipLevel).toBe(0); + expect(context.$mainAttachmentObject.msaa).toBe(true); + expect(context.$mainAttachmentObject.mask).toBe(false); + expect(context.$mainAttachmentObject.color?.resource).toBe("createRenderbuffer"); + expect(context.$mainAttachmentObject.color?.area).toBe(65536); + expect(context.$mainAttachmentObject.color?.width).toBe(256); + expect(context.$mainAttachmentObject.color?.height).toBe(256); + expect(context.$mainAttachmentObject.stencil?.resource).toBe("createRenderbuffer"); + expect(context.$mainAttachmentObject.stencil?.width).toBe(0); + expect(context.$mainAttachmentObject.stencil?.height).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextResizeUseCase.ts b/packages/webgl/src/Context/usecase/ContextResizeUseCase.ts new file mode 100644 index 00000000..d76de01d --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextResizeUseCase.ts @@ -0,0 +1,54 @@ +import type { Context } from "../../Context"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as frameBufferManagerUnBindAttachmentObjectService } from "../../FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService"; +import { execute as atlasManagerResetUseCase } from "../../AtlasManager/usecase/AtlasManagerResetUseCase"; + +/** + * @description メインのアタッチメントオブジェクトをリサイズする + * Resize the main attachment object + * + * @param {Context} context + * @param {number} width + * @param {number} height + * @param {boolean} [cache_clear=true] + * @return {void} + * @method + * @protected + */ +export const execute = ( + context: Context, + width: number, + height: number, + cache_clear: boolean = true +): void => { + + // clear InstacedArray + context.clearArraysInstanced(); + + if (context.$stackAttachmentObject.length) { + for (let idx = 0; idx < context.$stackAttachmentObject.length; ++idx) { + const attachmentObject = context.$stackAttachmentObject[idx]; + if (!attachmentObject) { + continue; + } + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject); + } + } else { + if (context.$mainAttachmentObject) { + frameBufferManagerReleaseAttachmentObjectUseCase(context.$mainAttachmentObject); + } + } + + // reset node + if (cache_clear) { + atlasManagerResetUseCase(); + } + + // unbind + frameBufferManagerUnBindAttachmentObjectService(); + + // new attachment object + context.$mainAttachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, true); + context.bind(context.$mainAttachmentObject); +}; \ No newline at end of file diff --git a/packages/webgl/src/Context/usecase/ContextStrokeUseCase.ts b/packages/webgl/src/Context/usecase/ContextStrokeUseCase.ts new file mode 100644 index 00000000..2532ab6b --- /dev/null +++ b/packages/webgl/src/Context/usecase/ContextStrokeUseCase.ts @@ -0,0 +1,30 @@ +import { $getVertices } from "../../PathCommand"; +import { execute as meshStrokeGenerateUseCase } from "../../Mesh/usecase/MeshStrokeGenerateUseCase"; +import { + $addFillBuffer, + $fillTypes, + $fillBufferIndexes +} from "../../Mesh"; + +/** + * @description ストロークを描画 + * Draw a stroke + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const vertices = $getVertices(true); + if (!vertices.length) { + return ; + } + + $fillTypes.push("fill"); + const mesh = meshStrokeGenerateUseCase(vertices); + $addFillBuffer(mesh.buffer); + + // 塗りのインデックスを追加 + $fillBufferIndexes.push(mesh.indexCount); +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter.ts b/packages/webgl/src/Filter.ts new file mode 100644 index 00000000..c7b64663 --- /dev/null +++ b/packages/webgl/src/Filter.ts @@ -0,0 +1,61 @@ +import type { IPoint } from "./interface/IPoint"; + +/** + * @description フィルター適用におけるオフセット値 + * Offset value in filter application + * + * @type {IPoint} + * @protected + */ +export const $offset: IPoint = { + "x": 0, + "y": 0 +}; + +/** + * @description カラー値から赤成分を取得します。 + * Get the red component from the color value. + * + * @param {number} color + * @param {number} alpha + * @param {boolean} premultiplied + * @return {number} + * @method + * @protected + */ +export const $intToR = (color: number, alpha: number, premultiplied: boolean): number => +{ + return (color >> 16) * (premultiplied ? alpha : 1) / 255; +}; + +/** + * @description カラー値から緑成分を取得します。 + * Get the green component from the color value. + * + * @param {number} color + * @param {number} alpha + * @param {boolean} premultiplied + * @return {number} + * @method + * @protected + */ +export const $intToG = (color: number, alpha: number, premultiplied: boolean): number => +{ + return (color >> 8 & 0xFF) * (premultiplied ? alpha : 1) / 255; +}; + +/** + * @description カラー値から青成分を取得します。 + * Get the red component from the color value. + * + * @param {number} color + * @param {number} alpha + * @param {boolean} premultiplied + * @return {number} + * @method + * @protected + */ +export const $intToB = (color: number, alpha: number, premultiplied: boolean): number => +{ + return (color & 0xFF) * (premultiplied ? alpha : 1) / 255; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/BevelFilter/usecase/FilterApplyBevelFilterUseCase.ts b/packages/webgl/src/Filter/BevelFilter/usecase/FilterApplyBevelFilterUseCase.ts new file mode 100644 index 00000000..5918816f --- /dev/null +++ b/packages/webgl/src/Filter/BevelFilter/usecase/FilterApplyBevelFilterUseCase.ts @@ -0,0 +1,176 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as variantsBlendTextureShaderService } from "../../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as blendEraseService } from "../../../Blend/service/BlendEraseService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as filterApplyBlurFilterUseCase } from "../../BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as filterApplyBitmapFilterUseCase } from "../../BitmapFilter/usecase/FilterApplyBitmapFilterUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { + $offset, + $intToR, + $intToG, + $intToB +} from "../../../Filter"; +import { + $getDevicePixelRatio, + $context +} from "../../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +const $Deg2Rad: number = Math.PI / 180; + +/** + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} distance + * @param {number} angle + * @param {number} highlight_color + * @param {number} highlight_alpha + * @param {number} shadow_color + * @param {number} shadow_alpha + * @param {number} blur_x + * @param {number} blur_y + * @param {number} strength + * @param {number} quality + * @param {number} type + * @param {boolean} knockout + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + distance: number = 4, + angle: number = 45, + highlight_color: number = 0xffffff, + highlight_alpha: number = 1, + shadow_color: number = 0, + shadow_alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: number = 1, + type: number = 0, + knockout: boolean = false +): ITextureObject => { + + const baseWidth = texture_object.width; + const baseHeight = texture_object.height; + const baseOffsetX = $offset.x; + const baseOffsetY = $offset.y; + + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + // pointer + const devicePixelRatio = $getDevicePixelRatio(); + const radian = angle * $Deg2Rad; + const x = Math.cos(radian) * distance * (xScale / devicePixelRatio); + const y = Math.sin(radian) * distance * (yScale / devicePixelRatio); + + const currentAttachmentObject = $context.currentAttachmentObject; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(baseWidth, baseHeight, false); + $context.bind(attachmentObject); + + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + + textureManagerBind0UseCase(texture_object); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + $context.setTransform(1, 0, 0, 1, x * 2, y * 2); + blendEraseService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + blendResetService(); + + const bevelBaseTextureObject = attachmentObject.texture as ITextureObject; + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + const blurTextureObject = filterApplyBlurFilterUseCase( + bevelBaseTextureObject, matrix, blur_x, blur_y, quality, false + ); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject); + + const blurWidth = blurTextureObject.width; + const blurHeight = blurTextureObject.height; + const bevelWidth = Math.ceil(blurWidth + Math.abs(x) * 2); + const bevelHeight = Math.ceil(blurHeight + Math.abs(y) * 2); + + // bevel filter buffer + let typeName = ""; + switch (type) { + + case 0: + typeName = "full"; + break; + + case 1: + typeName = "inner"; + break; + + case 2: + typeName = "outer"; + break; + + } + + const isInner = typeName === "inner"; + const width = isInner ? baseWidth : bevelWidth; + const height = isInner ? baseHeight : bevelHeight; + + const absX = Math.abs(x); + const absY = Math.abs(y); + const blurOffsetX = (blurWidth - baseWidth) / 2; + const blurOffsetY = (blurHeight - baseHeight) / 2; + + const baseTextureX = isInner ? 0 : absX + blurOffsetX; + const baseTextureY = isInner ? 0 : absY + blurOffsetY; + const blurTextureX = isInner ? -blurOffsetX - x : absX - x; + const blurTextureY = isInner ? -blurOffsetY - y : absY - y; + + const textureObject = filterApplyBitmapFilterUseCase( + texture_object, blurTextureObject, width, height, + baseWidth, baseHeight, baseTextureX, baseTextureY, + blurWidth, blurHeight, blurTextureX, blurTextureY, + false, typeName, knockout, + strength, null, null, null, + $intToR(highlight_color, highlight_alpha, true), + $intToG(highlight_color, highlight_alpha, true), + $intToB(highlight_color, highlight_alpha, true), + highlight_alpha, + $intToR(shadow_color, shadow_alpha, true), + $intToG(shadow_color, shadow_alpha, true), + $intToB(shadow_color, shadow_alpha, true), + shadow_alpha + ); + + $offset.x = baseOffsetX + baseTextureX; + $offset.y = baseOffsetY + baseTextureY; + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(blurTextureObject); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/BitmapFilter/usecase/FilterApplyBitmapFilterUseCase.ts b/packages/webgl/src/Filter/BitmapFilter/usecase/FilterApplyBitmapFilterUseCase.ts new file mode 100644 index 00000000..e2431f30 --- /dev/null +++ b/packages/webgl/src/Filter/BitmapFilter/usecase/FilterApplyBitmapFilterUseCase.ts @@ -0,0 +1,188 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind01UseCase } from "../../../TextureManager/usecase/TextureManagerBind01UseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as textureManagerBind02UseCase } from "../../../TextureManager/usecase/TextureManagerBind02UseCase"; +import { execute as textureManagerBind012UseCase } from "../../../TextureManager/usecase/TextureManagerBind012UseCase"; +import { execute as blendOneZeroService } from "../../../Blend/service/BlendOneZeroService"; +import { execute as blendSourceInService } from "../../../Blend/service/BlendSourceInService"; +import { execute as blendSourceAtopService } from "../../../Blend/service/BlendSourceAtopService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as variantsBitmapFilterShaderService } from "../../../Shader/Variants/Filter/service/VariantsBitmapFilterShaderService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as shaderManagerSetBitmapFilterUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetBitmapFilterUniformService"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as variantsBlendTextureShaderService } from "../../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as gradientLUTGenerateFilterTextureUseCase } from "../../../Shader/GradientLUTGenerator/usecase/GradientLUTGenerateFilterTextureUseCase"; +import { $context } from "../../../WebGLUtil"; + +/** + * @description BitmapFilterを適用する + * Apply BitmapFilter + * + * @param {ITextureObject} texture_object + * @param {ITextureObject} blur_texture_object + * @param {number} width + * @param {number} height + * @param {number} base_width + * @param {number} base_height + * @param {number} base_offset_x + * @param {number} base_offset_y + * @param {number} blur_width + * @param {number} blur_height + * @param {number} blur_offset_x + * @param {number} blur_offset_y + * @param {boolean} is_glow + * @param {string} type + * @param {boolean} knockout + * @param {number} strength + * @param {Float32Array | null} ratios + * @param {Float32Array | null} colors + * @param {Float32Array | null} alphas + * @param {number} color_r1 + * @param {number} color_g1 + * @param {number} color_b1 + * @param {number} color_a1 + * @param {number} color_r2 + * @param {number} color_g2 + * @param {number} color_b2 + * @param {number} color_a2 + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + blur_texture_object: ITextureObject, + width: number, + height: number, + base_width: number, + base_height: number, + base_offset_x: number, + base_offset_y: number, + blur_width: number, + blur_height: number, + blur_offset_x: number, + blur_offset_y: number, + is_glow: boolean, + type: string, + knockout: boolean, + strength: number, + ratios: Float32Array | null = null, + colors: Float32Array | null = null, + alphas: Float32Array | null = null, + color_r1: number = 0, + color_g1: number = 0, + color_b1: number = 0, + color_a1: number = 0, + color_r2: number = 0, + color_g2: number = 0, + color_b2: number = 0, + color_a2: number = 0 +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, false); + $context.bind(attachmentObject); + + const isInner = type === "inner"; + const isGradient = ratios !== null && colors !== null && alphas !== null; + + let gradientTextureObject: ITextureObject | null = null; + if (isGradient) { + gradientTextureObject = gradientLUTGenerateFilterTextureUseCase( + ratios as Float32Array, colors as Float32Array, alphas as Float32Array + ); + } + + if (isInner) { + + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + + textureManagerBind0UseCase(texture_object, true); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + if (isGradient && gradientTextureObject) { + textureManagerBind02UseCase( + blur_texture_object, + gradientTextureObject, + true + ); + } else { + textureManagerBind0UseCase(blur_texture_object, true); + } + + } else { + + if (isGradient && gradientTextureObject) { + textureManagerBind012UseCase( + blur_texture_object, + texture_object, + gradientTextureObject, + true + ); + } else { + textureManagerBind01UseCase( + blur_texture_object, + texture_object, + true + ); + } + } + + const transformsBase = !(isInner || type === "full" && knockout); + const transformsBlur = !(width === blur_width && height === blur_height && blur_offset_x === 0 && blur_offset_y === 0); + const appliesStrength = !(strength === 1); + + if (!isInner) { + + blendOneZeroService(); + + } else if (knockout) { + + blendSourceInService(); + + } else { + + blendSourceAtopService(); + + } + + const shaderManager = variantsBitmapFilterShaderService( + transformsBase, transformsBlur, + is_glow, type, knockout, + appliesStrength, isGradient + ); + + shaderManagerSetBitmapFilterUniformService( + shaderManager, width, height, + base_width, base_height, base_offset_x, base_offset_y, + blur_width, blur_height, blur_offset_x, blur_offset_y, + is_glow, strength, + color_r1, color_g1, color_b1, color_a1, + color_r2, color_g2, color_b2, color_a2, + transformsBase, transformsBlur, appliesStrength, isGradient + ); + + shaderManagerDrawTextureUseCase(shaderManager); + + const textureObject = attachmentObject.texture as ITextureObject; + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + // ブレンドモードをリセット + blendResetService(); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyBlurFilterUseCase.ts b/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyBlurFilterUseCase.ts new file mode 100644 index 00000000..5166d865 --- /dev/null +++ b/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyBlurFilterUseCase.ts @@ -0,0 +1,192 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import type { IAttachmentObject } from "../../../interface/IAttachmentObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as variantsBlendMatrixTextureShaderService } from "../../../Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService"; +import { execute as shaderManagerSetMatrixTextureUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as filterApplyDirectionalBlurFilterUseCase } from "../../../Filter/BlurFilter/usecase/FilterApplyDirectionalBlurFilterUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as blendOneZeroService } from "../../../Blend/service/BlendOneZeroService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { $offset } from "../../../Filter"; +import { + $context, + $getDevicePixelRatio +} from "../../../WebGLUtil"; + +/** + * @type {number[]} + * @constant + */ +const $STEP: number[] = [0.5, 1.05, 1.4, 1.55, 1.75, 1.9, 2, 2.15, 2.2, 2.3, 2.5, 3, 3, 3.5, 3.5]; + +/** + * @description ぼかしフィルターを適用します。 + * Apply the blur filter. + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} [blur_x=4] + * @param {number} [blur_y=4] + * @param {number} [quality=1] + * @param {boolean} [removed=false] + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + blur_x: number = 4, + blur_y: number = 4, + quality: number = 1, + removed: boolean = true +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + const devicePixelRatio = $getDevicePixelRatio(); + const baseBlurX = blur_x * (xScale / devicePixelRatio); + const baseBlurY = blur_y * (yScale / devicePixelRatio); + + const step = $STEP[quality - 1]; + const dx = Math.round(baseBlurX * step); + const dy = Math.round(baseBlurY * step); + + $offset.x += dx; + $offset.y += dy; + + const width = texture_object.width + dx * 2; + const height = texture_object.height + dy * 2; + + let bufferScaleX = 1; + let bufferScaleY = 1; + + if (baseBlurX > 128) { + bufferScaleX = 0.0625; + } else if (baseBlurX > 64) { + bufferScaleX = 0.125; + } else if (baseBlurX > 32) { + bufferScaleX = 0.25; + } else if (baseBlurX > 16) { + bufferScaleX = 0.5; + } + + if (baseBlurY > 128) { + bufferScaleY = 0.0625; + } else if (baseBlurY > 64) { + bufferScaleY = 0.125; + } else if (baseBlurY > 32) { + bufferScaleY = 0.25; + } else if (baseBlurY > 16) { + bufferScaleY = 0.5; + } + + const bufferWidth = Math.ceil(width * bufferScaleX); + const bufferHeight = Math.ceil(height * bufferScaleY); + + const attachmentObject0 = frameBufferManagerGetAttachmentObjectUseCase(bufferWidth, bufferHeight, false); + const attachmentObject1 = frameBufferManagerGetAttachmentObjectUseCase(bufferWidth, bufferHeight, false); + + const attachments: IAttachmentObject[] = [attachmentObject0, attachmentObject1]; + + // 描画元のテクスチャをattachmentObject0に描画 + $context.bind(attachmentObject0); + $context.reset(); + $context.setTransform( + bufferScaleX, 0, 0, bufferScaleY, + dx * bufferScaleX, + dy * bufferScaleY + ); + + textureManagerBind0UseCase(texture_object); + + const shaderManager = variantsBlendMatrixTextureShaderService(); + shaderManagerSetMatrixTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + // 描画元のテクスチャを解放 + if (removed) { + textureManagerReleaseTextureObjectUseCase(texture_object); + } + + const bufferBlurX = baseBlurX * bufferScaleX; + const bufferBlurY = baseBlurY * bufferScaleY; + + let attachmentIndex = 0; + let textureObject = attachmentObject0.texture as ITextureObject; + textureManagerBind0UseCase(textureObject, true); + + blendOneZeroService(); + for (let q = 0; q < quality; ++q) { + + if (blur_x > 0) { + attachmentIndex = (attachmentIndex + 1) % 2; + + const attachmentObject = attachments[attachmentIndex]; + $context.bind(attachmentObject); + + filterApplyDirectionalBlurFilterUseCase( + textureObject, true, bufferBlurX + ); + + textureObject = attachmentObject.texture as ITextureObject; + } + + if (blur_y > 0) { + attachmentIndex = (attachmentIndex + 1) % 2; + + const attachmentObject = attachments[attachmentIndex]; + $context.bind(attachmentObject); + + filterApplyDirectionalBlurFilterUseCase( + textureObject, false, bufferBlurY + ); + + textureObject = attachmentObject.texture as ITextureObject; + } + } + blendResetService(); + + if (bufferScaleX !== 1 || bufferScaleY !== 1) { + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, false); + $context.bind(attachmentObject); + + $context.setTransform( + 1 / bufferScaleX, 0, 0, 1 / bufferScaleY, 0, 0 + ); + + textureManagerBind0UseCase(textureObject, true); + + shaderManagerSetMatrixTextureUniformService( + shaderManager, textureObject.width, textureObject.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + textureObject = attachmentObject.texture as ITextureObject; + + frameBufferManagerReleaseAttachmentObjectUseCase(attachments[0]); + frameBufferManagerReleaseAttachmentObjectUseCase(attachments[1]); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + } else { + attachmentIndex = (attachmentIndex + 1) % 2; + frameBufferManagerReleaseAttachmentObjectUseCase(attachments[attachmentIndex]); + + attachmentIndex = (attachmentIndex + 1) % 2; + frameBufferManagerReleaseAttachmentObjectUseCase(attachments[attachmentIndex], false); + } + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyDirectionalBlurFilterUseCase.ts b/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyDirectionalBlurFilterUseCase.ts new file mode 100644 index 00000000..11b127d2 --- /dev/null +++ b/packages/webgl/src/Filter/BlurFilter/usecase/FilterApplyDirectionalBlurFilterUseCase.ts @@ -0,0 +1,37 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsBlurFilterShaderService } from "../../../Shader/Variants/Filter/service/VariantsBlurFilterShaderService"; +import { execute as shaderManagerSetBlurFilterUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetBlurFilterUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; + +/** + * @description 指定方向にぼかしフィルターを適用します。 + * Apply the blur filter in the specified direction. + * + * @param {ITextureObject} texture_object + * @param {boolean} is_horizontal + * @param {number} blur + * @return {void} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + is_horizontal: boolean, + blur: number +): void => { + + textureManagerBind0UseCase(texture_object, true); + + const halfBlur = Math.ceil(blur * 0.5); + const fraction = 1 - (halfBlur - blur * 0.5); + const samples = 1 + blur; + + const shaderManager = variantsBlurFilterShaderService(halfBlur); + shaderManagerSetBlurFilterUniformService( + shaderManager, + texture_object.width, texture_object.height, + is_horizontal, fraction, samples + ); + shaderManagerDrawTextureUseCase(shaderManager); +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/ColorMatrixFilter/usecase/FilterApplyColorMatrixFilterUseCase.ts b/packages/webgl/src/Filter/ColorMatrixFilter/usecase/FilterApplyColorMatrixFilterUseCase.ts new file mode 100644 index 00000000..4e617f90 --- /dev/null +++ b/packages/webgl/src/Filter/ColorMatrixFilter/usecase/FilterApplyColorMatrixFilterUseCase.ts @@ -0,0 +1,50 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { $context } from "../../../WebGLUtil"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as variantsColorMatrixFilterShaderService } from "../../../Shader/Variants/Filter/service/VariantsColorMatrixFilterShaderService"; +import { execute as shaderManagerSetColorMatrixFilterUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetColorMatrixFilterUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; + +/** + * @description カラーマトリックスフィルターを適用します。 + * Apply the color matrix filter. + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (texture_object: ITextureObject, matrix: Float32Array): ITextureObject => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase( + texture_object.width, texture_object.height, false + ); + $context.bind(attachmentObject); + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + + textureManagerBind0UseCase(texture_object); + blendResetService(); + + const shaderManager = variantsColorMatrixFilterShaderService(); + shaderManagerSetColorMatrixFilterUniformService(shaderManager, matrix); + shaderManagerDrawTextureUseCase(shaderManager); + + const textureObject = attachmentObject.texture as ITextureObject; + + textureManagerReleaseTextureObjectUseCase(texture_object); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/ConvolutionFilter/usecase/FilterApplyConvolutionFilterUseCase.ts b/packages/webgl/src/Filter/ConvolutionFilter/usecase/FilterApplyConvolutionFilterUseCase.ts new file mode 100644 index 00000000..787130a8 --- /dev/null +++ b/packages/webgl/src/Filter/ConvolutionFilter/usecase/FilterApplyConvolutionFilterUseCase.ts @@ -0,0 +1,86 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as variantsConvolutionFilterShaderService } from "../../../Shader/Variants/Filter/service/VariantsConvolutionFilterShaderService"; +import { execute as shaderManagerSetConvolutionFilterUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetConvolutionFilterUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { $context } from "../../../WebGLUtil"; +import { + $intToR, + $intToG, + $intToB +} from "../../../Filter"; + +/** + * @description コンボリューション・フィルタを適用する + * Apply convolution filter + * + * @param {ITextureObject} texture_object + * @param {number} matrix_x + * @param {number} matrix_y + * @param {Float32Array} matrix + * @param {number} divisor + * @param {number} bias + * @param {boolean} preserve_alpha + * @param {boolean} clamp + * @param {number} color + * @param {number} alpha + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix_x: number = 0, + matrix_y: number = 0, + matrix: Float32Array, + divisor: number = 1, + bias: number = 0, + preserve_alpha: boolean = true, + clamp: boolean = true, + color: number = 0, + alpha: number = 0 +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const width = texture_object.width; + const height = texture_object.height; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase( + width, height, false + ); + $context.bind(attachmentObject); + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + + textureManagerBind0UseCase(texture_object, true); + blendResetService(); + + const shaderManager = variantsConvolutionFilterShaderService( + matrix_x, matrix_y, preserve_alpha, clamp + ); + + shaderManagerSetConvolutionFilterUniformService( + shaderManager, width, height, matrix, divisor, bias, clamp, + $intToR(color, alpha, false), + $intToG(color, alpha, false), + $intToB(color, alpha, false), + alpha + ); + shaderManagerDrawTextureUseCase(shaderManager); + + const textureObject = attachmentObject.texture as ITextureObject; + + textureManagerReleaseTextureObjectUseCase(texture_object); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/DisplacementMapFilter/usecase/FilterApplyDisplacementMapFilterUseCase.ts b/packages/webgl/src/Filter/DisplacementMapFilter/usecase/FilterApplyDisplacementMapFilterUseCase.ts new file mode 100644 index 00000000..2691a75e --- /dev/null +++ b/packages/webgl/src/Filter/DisplacementMapFilter/usecase/FilterApplyDisplacementMapFilterUseCase.ts @@ -0,0 +1,114 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerCreateFromPixelsUseCase } from "../../../TextureManager/usecase/TextureManagerCreateFromPixelsUseCase"; +import { execute as textureManagerBind01UseCase } from "../../../TextureManager/usecase/TextureManagerBind01UseCase"; +import { execute as variantsDisplacementMapFilterShaderService } from "../../../Shader/Variants/Filter/service/VariantsDisplacementMapFilterShaderService"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as shaderManagerSetDisplacementMapFilterUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetDisplacementMapFilterUniformService"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { + $context, + $getDevicePixelRatio +} from "../../../WebGLUtil"; +import { + $intToR, + $intToG, + $intToB +} from "../../../Filter"; + +/** + * @description ディスプレイスメントマップ・フィルタを適用する + * Apply displacement map filter + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {Uint8Array} bitmap_buffer + * @param {number} bitmap_width + * @param {number} bitmap_height + * @param {number} map_point_x + * @param {number} map_point_y + * @param {number} component_x + * @param {number} component_y + * @param {number} scale_x + * @param {number} scale_y + * @param {number} mode + * @param {number} color + * @param {number} alpha + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + bitmap_buffer: Uint8Array, + bitmap_width: number = 0, + bitmap_height: number = 0, + map_point_x: number = 0, + map_point_y: number = 0, + component_x: number = 0, + component_y: number = 0, + scale_x: number = 0, + scale_y: number = 0, + mode: number = 2, + color: number = 0, + alpha: number = 0 +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const devicePixelRatio = $getDevicePixelRatio(); + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]) / devicePixelRatio; + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]) / devicePixelRatio; + + const width = texture_object.width; + const height = texture_object.height; + + const baseWidth = width / xScale; + const baseHeight = height / yScale; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase( + width, height, false + ); + $context.bind(attachmentObject); + + const pixelTextureObject = textureManagerCreateFromPixelsUseCase( + bitmap_width, bitmap_height, bitmap_buffer + ); + + textureManagerBind01UseCase( + texture_object, + pixelTextureObject + ); + + const shaderManager = variantsDisplacementMapFilterShaderService( + component_x, component_y, mode + ); + + shaderManagerSetDisplacementMapFilterUniformService( + shaderManager, + width, height, baseWidth, baseHeight, + map_point_x, map_point_y, scale_x, scale_y, mode, + $intToR(color, alpha, true), + $intToG(color, alpha, true), + $intToB(color, alpha, true), + alpha + ); + + blendResetService(); + shaderManagerDrawTextureUseCase(shaderManager); + + const textureObject = attachmentObject.texture as ITextureObject; + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(pixelTextureObject); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/DropShadowFilter/usecase/FilterApplyDropShadowFilterUseCase.ts b/packages/webgl/src/Filter/DropShadowFilter/usecase/FilterApplyDropShadowFilterUseCase.ts new file mode 100644 index 00000000..39612aa7 --- /dev/null +++ b/packages/webgl/src/Filter/DropShadowFilter/usecase/FilterApplyDropShadowFilterUseCase.ts @@ -0,0 +1,134 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as filterApplyBlurFilterUseCase } from "../../BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as filterApplyBitmapFilterUseCase } from "../../BitmapFilter/usecase/FilterApplyBitmapFilterUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { + $getDevicePixelRatio, + $context +} from "../../../WebGLUtil"; +import { + $offset, + $intToR, + $intToG, + $intToB +} from "../../../Filter"; + +/** + * @type {number} + * @private + */ +const $Deg2Rad: number = Math.PI / 180; + +/** + * @description ドロップシャドウフィルターを適用します。 + * Apply the drop shadow filter. + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} distance + * @param {number} angle + * @param {number} color + * @param {number} alpha + * @param {number} blur_x + * @param {number} blur_y + * @param {number} strength + * @param {number} quality + * @param {number} inner + * @param {number} knockout + * @param {number} hide_object + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + distance: number = 4, + angle: number = 45, + color: number = 0, + alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: number = 1, + inner: boolean = false, + knockout: boolean = false, + hide_object: boolean = false +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const baseWidth = texture_object.width; + const baseHeight = texture_object.height; + const baseOffsetX = $offset.x; + const baseOffsetY = $offset.y; + + const blurTextureObject = filterApplyBlurFilterUseCase( + texture_object, matrix, blur_x, blur_y, quality, false + ); + + const blurWidth = blurTextureObject.width; + const blurHeight = blurTextureObject.height; + const blurOffsetX = $offset.x; + const blurOffsetY = $offset.y; + + const offsetDiffX = blurOffsetX - baseOffsetX; + const offsetDiffY = blurOffsetY - baseOffsetY; + + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + // shadow point + const devicePixelRatio = $getDevicePixelRatio(); + const radian = angle * $Deg2Rad; + const x = Math.cos(radian) * distance * (xScale / devicePixelRatio); + const y = Math.sin(radian) * distance * (yScale / devicePixelRatio); + + // dropShadow canvas + const w = inner ? baseWidth : blurWidth + Math.max(0, Math.abs(x) - offsetDiffX); + const h = inner ? baseHeight : blurHeight + Math.max(0, Math.abs(y) - offsetDiffY); + const width = Math.ceil(w); + const height = Math.ceil(h); + const fractionX = (width - w) / 2; + const fractionY = (height - h) / 2; + + const baseTextureX = inner ? 0 : Math.max(0, offsetDiffX - x) + fractionX; + const baseTextureY = inner ? 0 : Math.max(0, offsetDiffY - y) + fractionY; + const blurTextureX = inner ? x - blurOffsetX : (x > 0 ? Math.max(0, x - offsetDiffX) : 0) + fractionX; + const blurTextureY = inner ? y - blurOffsetY : (y > 0 ? Math.max(0, y - offsetDiffY) : 0) + fractionY; + + let typeName: string = ""; + let knockoutState: boolean; + if (inner) { + typeName = "inner"; + knockoutState = knockout || hide_object; + } else if (!knockout && hide_object) { + typeName = "full"; + knockoutState = true; + } else { + typeName = "outer"; + knockoutState = knockout; + } + + const textureObject = filterApplyBitmapFilterUseCase( + texture_object, blurTextureObject, width, height, + baseWidth, baseHeight, baseTextureX, baseTextureY, + blurWidth, blurHeight, blurTextureX, blurTextureY, + true, typeName, knockoutState, + strength, null, null, null, + $intToR(color, alpha, true), + $intToG(color, alpha, true), + $intToB(color, alpha, true), + alpha, + 0, 0, 0, 0 + ); + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(blurTextureObject); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/GlowFilter/usecase/FilterApplyGlowFilterUseCase.ts b/packages/webgl/src/Filter/GlowFilter/usecase/FilterApplyGlowFilterUseCase.ts new file mode 100644 index 00000000..f96d2364 --- /dev/null +++ b/packages/webgl/src/Filter/GlowFilter/usecase/FilterApplyGlowFilterUseCase.ts @@ -0,0 +1,94 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as filterApplyBlurFilterUseCase } from "../../BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as filterApplyBitmapFilterUseCase } from "../../BitmapFilter/usecase/FilterApplyBitmapFilterUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { $context } from "../../../WebGLUtil"; +import { + $offset, + $intToR, + $intToG, + $intToB +} from "../../../Filter"; + +/** + * @description GlowFilterを適用する + * Apply GlowFilter + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} color + * @param {number} alpha + * @param {number} blur_x + * @param {number} blur_y + * @param {number} strength + * @param {number} quality + * @param {number} inner + * @param {number} knockout + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + color: number = 0, + alpha: number = 1, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: number = 1, + inner: boolean = false, + knockout: boolean = false +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject; + + const baseWidth = texture_object.width; + const baseHeight = texture_object.height; + const baseOffsetX = $offset.x; + const baseOffsetY = $offset.y; + + const blurTextureObject = filterApplyBlurFilterUseCase( + texture_object, matrix, blur_x, blur_y, quality, false + ); + + const blurWidth = blurTextureObject.width; + const blurHeight = blurTextureObject.height; + const blurOffsetX = $offset.x; + const blurOffsetY = $offset.y; + + const width = inner ? baseWidth : blurWidth; + const height = inner ? baseHeight : blurHeight; + + const offsetX = blurOffsetX - baseOffsetX; + const offsetY = blurOffsetY - baseOffsetY; + + const baseTextureX = inner ? 0 : offsetX; + const baseTextureY = inner ? 0 : offsetY; + const blurTextureX = inner ? -offsetX : 0; + const blurTextureY = inner ? -offsetY : 0; + + const type = inner ? "inner" : "outer"; + + const textureObject = filterApplyBitmapFilterUseCase( + texture_object, blurTextureObject, width, height, + baseWidth, baseHeight, baseTextureX, baseTextureY, + blurWidth, blurHeight, blurTextureX, blurTextureY, + true, type, knockout, + strength, null, null, null, + $intToR(color, alpha, true), + $intToG(color, alpha, true), + $intToB(color, alpha, true), + alpha, + 0, 0, 0, 0 + ); + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(blurTextureObject); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/GradientBevelFilter/usecase/FilterApplyGradientBevelFilterUseCase.ts b/packages/webgl/src/Filter/GradientBevelFilter/usecase/FilterApplyGradientBevelFilterUseCase.ts new file mode 100644 index 00000000..e84c6dc9 --- /dev/null +++ b/packages/webgl/src/Filter/GradientBevelFilter/usecase/FilterApplyGradientBevelFilterUseCase.ts @@ -0,0 +1,163 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as shaderManagerDrawTextureUseCase } from "../../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as variantsBlendTextureShaderService } from "../../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as blendEraseService } from "../../../Blend/service/BlendEraseService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as filterApplyBlurFilterUseCase } from "../../BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as filterApplyBitmapFilterUseCase } from "../../BitmapFilter/usecase/FilterApplyBitmapFilterUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { $offset } from "../../../Filter"; +import { + $getDevicePixelRatio, + $context +} from "../../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +const $Deg2Rad: number = Math.PI / 180; + +/** + * @description GradientBevelFilterを適用する + * Apply GradientBevelFilter + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} distance + * @param {number} angle + * @param {number[]} colors + * @param {number[]} alphas + * @param {number[]} ratios + * @param {number} blur_x + * @param {number} blur_y + * @param {number} strength + * @param {number} quality + * @param {number} type + * @param {boolean} knockout + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + distance: number = 4, + angle: number = 45, + colors: Float32Array, + alphas: Float32Array, + ratios: Float32Array, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: number = 1, + type: number = 0, + knockout: boolean = false +): ITextureObject => { + + const baseWidth = texture_object.width; + const baseHeight = texture_object.height; + const baseOffsetX = $offset.x; + const baseOffsetY = $offset.y; + + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + const devicePixelRatio = $getDevicePixelRatio(); + const radian = angle * $Deg2Rad; + const x = Math.cos(radian) * distance * (xScale / devicePixelRatio); + const y = Math.sin(radian) * distance * (yScale / devicePixelRatio); + + const currentAttachmentObject = $context.currentAttachmentObject; + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(baseWidth, baseHeight, false); + $context.bind(attachmentObject); + + $context.reset(); + $context.setTransform(1, 0, 0, 1, 0, 0); + + textureManagerBind0UseCase(texture_object); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + $context.setTransform(1, 0, 0, 1, x * 2, y * 2); + blendEraseService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + shaderManagerDrawTextureUseCase(shaderManager); + + blendResetService(); + + const bevelBaseTextureObject = attachmentObject.texture as ITextureObject; + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + const blurTextureObject = filterApplyBlurFilterUseCase( + bevelBaseTextureObject, matrix, blur_x, blur_y, quality, false + ); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject); + + const blurWidth = blurTextureObject.width; + const blurHeight = blurTextureObject.height; + const bevelWidth = Math.ceil(blurWidth + Math.abs(x) * 2); + const bevelHeight = Math.ceil(blurHeight + Math.abs(y) * 2); + + // bevel filter buffer + let typeName = ""; + switch (type) { + + case 0: + typeName = "full"; + break; + + case 1: + typeName = "inner"; + break; + + case 2: + typeName = "outer"; + break; + + } + + const isInner = typeName === "inner"; + const width = isInner ? baseWidth : bevelWidth; + const height = isInner ? baseHeight : bevelHeight; + + const absX = Math.abs(x); + const absY = Math.abs(y); + const blurOffsetX = (blurWidth - baseWidth) / 2; + const blurOffsetY = (blurHeight - baseHeight) / 2; + + const baseTextureX = isInner ? 0 : absX + blurOffsetX; + const baseTextureY = isInner ? 0 : absY + blurOffsetY; + const blurTextureX = isInner ? -blurOffsetX - x : absX - x; + const blurTextureY = isInner ? -blurOffsetY - y : absY - y; + + const textureObject = filterApplyBitmapFilterUseCase( + texture_object, blurTextureObject, width, height, + baseWidth, baseHeight, baseTextureX, baseTextureY, + blurWidth, blurHeight, blurTextureX, blurTextureY, + false, typeName, knockout, + strength, ratios, colors, alphas, + 0, 0, 0, 0, 0, 0, 0, 0 + ); + + $offset.x = baseOffsetX + baseTextureX; + $offset.y = baseOffsetY + baseTextureY; + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(blurTextureObject); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Filter/GradientGlowFilter/usecase/FilterApplyGradientGlowFilterUseCase.ts b/packages/webgl/src/Filter/GradientGlowFilter/usecase/FilterApplyGradientGlowFilterUseCase.ts new file mode 100644 index 00000000..a4aa0723 --- /dev/null +++ b/packages/webgl/src/Filter/GradientGlowFilter/usecase/FilterApplyGradientGlowFilterUseCase.ts @@ -0,0 +1,124 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as filterApplyBlurFilterUseCase } from "../../BlurFilter/usecase/FilterApplyBlurFilterUseCase"; +import { execute as filterApplyBitmapFilterUseCase } from "../../BitmapFilter/usecase/FilterApplyBitmapFilterUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { $offset } from "../../../Filter"; +import { $getDevicePixelRatio } from "../../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +const $Deg2Rad: number = Math.PI / 180; + +/** + * @description GradientGlowFilterを適用する + * Apply GradientGlowFilter + * + * @param {ITextureObject} texture_object + * @param {Float32Array} matrix + * @param {number} distance + * @param {number} angle + * @param {number[]} colors + * @param {number[]} alphas + * @param {number[]} ratios + * @param {number} blur_x + * @param {number} blur_y + * @param {number} strength + * @param {number} quality + * @param {number} type + * @param {boolean} knockout + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + texture_object: ITextureObject, + matrix: Float32Array, + distance: number = 4, + angle: number = 45, + colors: Float32Array, + alphas: Float32Array, + ratios: Float32Array, + blur_x: number = 4, + blur_y: number = 4, + strength: number = 1, + quality: number = 1, + type: number = 0, + knockout: boolean = false +): ITextureObject => { + + const baseWidth = texture_object.width; + const baseHeight = texture_object.height; + const baseOffsetX = $offset.x; + const baseOffsetY = $offset.y; + + const blurTextureObject = filterApplyBlurFilterUseCase( + texture_object, matrix, blur_x, blur_y, quality, false + ); + + const blurWidth = blurTextureObject.width; + const blurHeight = blurTextureObject.height; + const blurOffsetX = $offset.x; + const blurOffsetY = $offset.y; + + const offsetDiffX = blurOffsetX - baseOffsetX; + const offsetDiffY = blurOffsetY - baseOffsetY; + + const xScale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); + const yScale = Math.sqrt(matrix[2] * matrix[2] + matrix[3] * matrix[3]); + + // shadow point + const devicePixelRatio = $getDevicePixelRatio(); + const radian = angle * $Deg2Rad; + const x = Math.cos(radian) * distance * (xScale / devicePixelRatio); + const y = Math.sin(radian) * distance * (yScale / devicePixelRatio); + + const isInner = type === 1; + const w = isInner ? baseWidth : blurWidth + Math.max(0, Math.abs(x) - offsetDiffX); + const h = isInner ? baseHeight : blurHeight + Math.max(0, Math.abs(y) - offsetDiffY); + const width = Math.ceil(w); + const height = Math.ceil(h); + const fractionX = (width - w) / 2; + const fractionY = (height - h) / 2; + + const baseTextureX = isInner ? 0 : Math.max(0, offsetDiffX - x) + fractionX; + const baseTextureY = isInner ? 0 : Math.max(0, offsetDiffY - y) + fractionY; + const blurTextureX = isInner ? x - blurOffsetX : (x > 0 ? Math.max(0, x - offsetDiffX) : 0) + fractionX; + const blurTextureY = isInner ? y - blurOffsetY : (y > 0 ? Math.max(0, y - offsetDiffY) : 0) + fractionY; + + // bevel filter buffer + let typeName = ""; + switch (type) { + + case 0: + typeName = "full"; + break; + + case 1: + typeName = "inner"; + break; + + case 2: + typeName = "outer"; + break; + + } + + const textureObject = filterApplyBitmapFilterUseCase( + texture_object, blurTextureObject, width, height, + baseWidth, baseHeight, baseTextureX, baseTextureY, + blurWidth, blurHeight, blurTextureX, blurTextureY, + true, typeName, knockout, + strength, ratios, colors, alphas, + 0, 0, 0, 0, 0, 0, 0, 0 + ); + + $offset.x = baseOffsetX + baseTextureX; + $offset.y = baseOffsetY + baseTextureY; + + textureManagerReleaseTextureObjectUseCase(texture_object); + textureManagerReleaseTextureObjectUseCase(blurTextureObject); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager.ts b/packages/webgl/src/FrameBufferManager.ts index 07bdbf4d..5d761b17 100644 --- a/packages/webgl/src/FrameBufferManager.ts +++ b/packages/webgl/src/FrameBufferManager.ts @@ -1,672 +1,271 @@ -import { TextureManager } from "./TextureManager"; -import { StencilBufferPool } from "./StencilBufferPool"; -import { ColorBufferPool } from "./ColorBufferPool"; -import { $Math } from "@next2d/share"; -import { $RENDER_SIZE } from "./Const"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { CachePositionImpl } from "./interface/CachePositionImpl"; +import type { IAttachmentObject } from "./interface/IAttachmentObject"; +import { $getAtlasTextureObject } from "./AtlasManager"; /** - * @class + * @description 生成したFrameBufferの管理オブジェクトを配列にプールして再利用します。 + * Pool the management object of the generated FrameBuffer in an array and reuse it. + * + * @type {array} + * @protected */ -export class FrameBufferManager -{ - private _$gl: WebGL2RenderingContext; - private _$objectPool: AttachmentImpl[]; - private _$currentAttachment: AttachmentImpl | null; - private _$isBinding: boolean; - private _$isRenderBinding: boolean; - private readonly _$frameBuffer: WebGLFramebuffer | null; - private readonly _$frameBufferTexture: WebGLFramebuffer | null; - private readonly _$textureManager: TextureManager; - private readonly _$stencilBufferPool: StencilBufferPool; - private readonly _$colorBufferPool: ColorBufferPool; - private readonly _$stencilBuffer: WebGLRenderbuffer; - private readonly _$colorBuffer: WebGLRenderbuffer; - - /** - * @param {WebGL2RenderingContext} gl - * @param {number} samples - * @constructor - */ - constructor (gl: WebGL2RenderingContext, samples: number) - { - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$objectPool = []; - - /** - * @type {WebGLFramebuffer} - * @private - */ - this._$frameBuffer = gl.createFramebuffer(); - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._$frameBuffer); - - /** - * @type {WebGLFramebuffer} - * @private - */ - this._$frameBufferTexture = gl.createFramebuffer(); - - /** - * @type {AttachmentImpl} - * @default null - * @private - */ - this._$currentAttachment = null; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isBinding = false; - - /** - * @type {TextureManager} - * @private - */ - this._$textureManager = new TextureManager(gl); - - /** - * @type {StencilBufferPool} - * @private - */ - this._$stencilBufferPool = new StencilBufferPool(gl); - - /** - * @type {ColorBufferPool} - * @private - */ - this._$colorBufferPool = new ColorBufferPool(gl, samples); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isRenderBinding = false; - - /** - * @type {WebGLRenderbuffer} - * @private - */ - this._$colorBuffer = this._$gl.createRenderbuffer() as NonNullable; - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, this._$colorBuffer); - this._$gl.renderbufferStorageMultisample( - this._$gl.RENDERBUFFER, - samples, - this._$gl.RGBA8, - $RENDER_SIZE, $RENDER_SIZE - ); - - /** - * @type {WebGLRenderbuffer} - * @private - */ - this._$stencilBuffer = this._$gl.createRenderbuffer() as NonNullable; - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, this._$stencilBuffer); - this._$gl.renderbufferStorageMultisample( - this._$gl.RENDERBUFFER, - samples, - this._$gl.STENCIL_INDEX8, - $RENDER_SIZE, $RENDER_SIZE - ); - } - - /** - * @description 描画用のbufferをbind - * - * @return {void} - * @method - * @public - */ - bindRenderBuffer (): void - { - if (!this._$isBinding) { - this._$isBinding = true; - this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER, this._$frameBuffer); - } - - if (!this._$isRenderBinding) { - this._$isRenderBinding = true; - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, this._$colorBuffer); - this._$gl.framebufferRenderbuffer( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.RENDERBUFFER, this._$colorBuffer - ); - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, this._$stencilBuffer); - this._$gl.framebufferRenderbuffer( - this._$gl.FRAMEBUFFER, this._$gl.STENCIL_ATTACHMENT, - this._$gl.RENDERBUFFER, this._$stencilBuffer - ); - } - } - - /** - * @member {AttachmentImpl|null} - * @readonly - * @public - */ - get currentAttachment (): AttachmentImpl|null - { - return this._$currentAttachment; - } - - /** - * @member {TextureManager} - * @readonly - * @public - */ - get textureManager (): TextureManager - { - return this._$textureManager; - } - - /** - * @param {number} width - * @param {number} height - * @param {boolean} [multisample=false] - * @param {number} [samples=0] - * @return {AttachmentImpl} - * @method - * @public - */ - createCacheAttachment ( - width: number, height: number, - multisample: boolean = false, samples: number = 0 - ): AttachmentImpl { - - const attachment: AttachmentImpl = this._$objectPool.pop() || { - "width": 0, - "height": 0, - "color": null, - "texture": null, - "msaa": false, - "stencil": null, - "mask": false, - "clipLevel": 0, - "isActive": false - }; - - const texture: WebGLTexture = this._$textureManager.create(width, height); - - attachment.width = width; - attachment.height = height; - - if (multisample) { - attachment.color = this._$colorBufferPool.create(width, height, samples); - attachment.texture = texture; - attachment.msaa = true; - attachment.stencil = attachment.color.stencil; - } else { - attachment.color = texture; - attachment.texture = texture; - attachment.msaa = false; - attachment.stencil = this._$stencilBufferPool.create(width, height); - } - - attachment.mask = false; - attachment.clipLevel = 0; - attachment.isActive = true; - - return attachment; - } - - /** - * @return {void} - * @method - * @public - */ - clearCache (): void - { - this._$textureManager.clearCache(); - } - - /** - * @param {number} width - * @param {number} height - * @return {void} - * @method - * @public - */ - setMaxSize (width: number, height: number): void - { - this._$stencilBufferPool._$maxWidth = width; - this._$stencilBufferPool._$maxHeight = height; - this._$textureManager._$maxWidth = width; - this._$textureManager._$maxHeight = height; - } - - /** - * @param {number} width - * @param {number} height - * @return {AttachmentImpl} - * @method - * @public - */ - createTextureAttachment ( - width: number, height: number - ): AttachmentImpl { - - const attachment: AttachmentImpl = this._$objectPool.pop() || { - "width": 0, - "height": 0, - "color": null, - "texture": null, - "msaa": false, - "stencil": null, - "mask": false, - "clipLevel": 0, - "isActive": false - }; - - const texture: WebGLTexture = this._$textureManager.create(width, height); - - attachment.width = width; - attachment.height = height; - attachment.color = texture; - attachment.texture = texture; - attachment.msaa = false; - attachment.stencil = null; - attachment.mask = false; - attachment.clipLevel = 0; - attachment.isActive = true; - - return attachment; - } - - /** - * @param {WebGLTexture} texture - * @return {AttachmentImpl} - * @method - * @public - */ - createTextureAttachmentFrom (texture: WebGLTexture): AttachmentImpl - { - const attachment: AttachmentImpl = this._$objectPool.pop() || { - "width": 0, - "height": 0, - "color": null, - "texture": null, - "msaa": false, - "stencil": null, - "mask": false, - "clipLevel": 0, - "isActive": true - }; - - attachment.width = texture.width; - attachment.height = texture.height; - attachment.color = texture; - attachment.texture = texture; - attachment.msaa = false; - attachment.stencil = null; - attachment.mask = false; - attachment.clipLevel = 0; - attachment.isActive = true; - - return attachment; - } - - /** - * @param {AttachmentImpl} [attachment = null] - * @param {boolean} [should_release_texture=false] - * @return {void} - * @method - * @public - */ - releaseAttachment ( - attachment: AttachmentImpl | null = null, - should_release_texture: boolean = false - ): void { - - if (!attachment || !attachment.isActive) { - return; - } - - if (attachment.msaa) { - if (attachment.color instanceof WebGLRenderbuffer) { - this._$colorBufferPool.release(attachment.color); - } - } else if (attachment.stencil) { - this._$stencilBufferPool.release(attachment.stencil); - } - - if (should_release_texture && attachment.texture) { - this._$textureManager.release(attachment.texture); - } - - attachment.color = null; - attachment.texture = null; - attachment.stencil = null; - attachment.isActive = false; - - this._$objectPool.push(attachment); - } - - /** - * @param {object} attachment - * @return {void} - * @method - * @public - */ - bind (attachment: AttachmentImpl): void - { - this._$currentAttachment = attachment; - if (!this._$isBinding) { - this._$isBinding = true; - this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER, this._$frameBuffer); - } - - if (attachment.msaa) { - if (attachment.color instanceof WebGLRenderbuffer) { - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, attachment.color); - this._$gl.framebufferRenderbuffer( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.RENDERBUFFER, attachment.color - ); - } - } else { - - if (attachment.color instanceof WebGLTexture) { - - if (attachment.color) { - this._$textureManager.bind0(attachment.color); - } - - this._$gl.framebufferTexture2D( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.TEXTURE_2D, attachment.color, 0 - ); - } - } - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, attachment.stencil); - this._$gl.framebufferRenderbuffer( - this._$gl.FRAMEBUFFER, this._$gl.STENCIL_ATTACHMENT, - this._$gl.RENDERBUFFER, attachment.stencil - ); - - this._$isRenderBinding = false; - } - - /** - * @return void - * @method - * @public - */ - unbind (): void - { - this._$currentAttachment = null; - if (this._$isBinding) { - this._$isBinding = false; - this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER, null); - } - } - - /** - * @return {void} - * @method - * @public - */ - transferToMainTexture (): void - { - if (!this._$currentAttachment) { - throw new Error("the current attachment is null."); - } - - const width: number = this._$currentAttachment.width; - const height: number = this._$currentAttachment.height; - - const texture: WebGLTexture | null = this._$currentAttachment.texture; - if (!texture) { - throw new Error("the texture is null."); - } - - this._$gl.bindFramebuffer( - this._$gl.DRAW_FRAMEBUFFER, - this._$frameBufferTexture - ); - - this._$textureManager.bind0(texture); - - this._$gl.framebufferTexture2D( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.TEXTURE_2D, texture, 0 - ); - - // use main Framebuffer - this._$gl.bindFramebuffer( - this._$gl.DRAW_FRAMEBUFFER, - null - ); - - // execute - this._$gl.blitFramebuffer( - 0, 0, width, height, - 0, 0, width, height, - this._$gl.COLOR_BUFFER_BIT, - this._$gl.NEAREST - ); - - // reset - this._$gl.bindFramebuffer( - this._$gl.DRAW_FRAMEBUFFER, - this._$frameBuffer - ); - } - - /** - * @param {number} width - * @param {number} height - * @return {object} - * @method - * @public - */ - createCachePosition (width: number, height: number): CachePositionImpl - { - return this - ._$textureManager - .createCachePosition(width, height); - } - - /** - * @param {object} position - * @return {void} - * @method - * @public - */ - transferTexture (position: CachePositionImpl): void - { - this._$gl.disable(this._$gl.SCISSOR_TEST); - this._$gl.bindFramebuffer( - this._$gl.DRAW_FRAMEBUFFER, - this._$frameBufferTexture - ); +export const $objectPool: IAttachmentObject[] = []; - const texture: WebGLTexture = this - ._$textureManager - .getAtlasTexture(position.index); - - this._$textureManager.bind0(texture); - - this._$gl.framebufferTexture2D( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.TEXTURE_2D, texture, 0 - ); - - const x0: number = $Math.max(0, position.x - 1); - const y0: number = $Math.max(0, position.y - 1); - const x1: number = $Math.min($RENDER_SIZE, position.x + position.w + 1); - const y1: number = $Math.min($RENDER_SIZE, position.y + position.h + 1); - - this._$gl.blitFramebuffer( - x0, y0, x1, y1, - x0, y0, x1, y1, - this._$gl.COLOR_BUFFER_BIT, - this._$gl.NEAREST - ); +/** + * @description READ_FRAMEBUFFER専用のFrameBufferオブジェクト + * FrameBuffer object for READ_FRAMEBUFFER only + * + * @class + * @public + */ +export let $readFrameBuffer: WebGLFramebuffer; - this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER, this._$frameBuffer); - } +/** + * @description READ_FRAMEBUFFER専用のFrameBufferオブジェクトを設定 + * Set the FrameBuffer object for READ_FRAMEBUFFER only + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setReadFrameBuffer = (gl: WebGL2RenderingContext): void => +{ + $readFrameBuffer = gl.createFramebuffer() as NonNullable; +}; - /** - * @return {WebGLTexture} - * @method - * @public - */ - getTextureFromCurrentAttachment (): WebGLTexture - { - if (!this._$currentAttachment) { - throw new Error("the current attachment is null."); - } +/** + * @description DRAW_FRAMEBUFFER専用のFrameBufferオブジェクト + * FrameBuffer object for DRAW_FRAMEBUFFER only + * + * @class + * @public + */ +export let $drawFrameBuffer: WebGLFramebuffer | null = null; - if (!this._$currentAttachment.msaa - && this._$currentAttachment.texture - ) { - return this._$currentAttachment.texture; - } +/** + * @description DRAW_FRAMEBUFFER専用のFrameBufferオブジェクトを設定 + * Set the FrameBuffer object for DRAW_FRAMEBUFFER only + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setDrawFrameBuffer = (gl: WebGL2RenderingContext): void => +{ + $drawFrameBuffer = gl.createFramebuffer() as NonNullable; +}; - const width: number = this._$currentAttachment.width; - const height: number = this._$currentAttachment.height; +/** + * @description アトラス専用のFrameBufferオブジェクト + * FrameBuffer object for atlas only + * + * @class + * @public + */ +export let $atlasFrameBuffer: WebGLFramebuffer | null = null; - const texture: WebGLTexture | null = this._$currentAttachment.texture; - if (!texture) { - throw new Error("the texture is null."); - } +/** + * @description アトラス専用のFrameBufferオブジェクトを設定 + * Set the FrameBuffer object for atlas only + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setAtlasFrameBuffer = (gl: WebGL2RenderingContext): void => +{ + $atlasFrameBuffer = gl.createFramebuffer() as NonNullable; + gl.bindFramebuffer(gl.FRAMEBUFFER, $atlasFrameBuffer); - texture.dirty = false; + const textureObject = $getAtlasTextureObject(); - this._$gl.bindFramebuffer( - this._$gl.DRAW_FRAMEBUFFER, - this._$frameBufferTexture - ); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, textureObject.resource, 0 + ); - this._$textureManager.bind0(texture); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, $atlasFrameBuffer); +}; - this._$gl.framebufferTexture2D( - this._$gl.FRAMEBUFFER, this._$gl.COLOR_ATTACHMENT0, - this._$gl.TEXTURE_2D, texture, 0 - ); +/** + * @description 現在アタッチされてるAttachmentObject + * Currently attached AttachmentObject + * + * @type {IAttachmentObject|null} + * @private + */ +let $currentAttachment: IAttachmentObject | null = null; - this._$gl.blitFramebuffer( - 0, 0, width, height, - 0, 0, width, height, - this._$gl.COLOR_BUFFER_BIT, - this._$gl.NEAREST - ); +/** + * @description 現在アタッチされてるAttachmentObjectを設定 + * Set the currently attached AttachmentObject + * + * @param {IAttachmentObject | null} attachment_object + * @return {void} + * @method + * @protected + */ +export const $setCurrentAttachment = (attachment_object: IAttachmentObject | null): void => +{ + $currentAttachment = attachment_object; +}; - this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER, this._$frameBuffer); +/** + * @description 現在アタッチされてるAttachmentObjectを返却 + * Returns the currently attached AttachmentObject + * + * @return {IAttachmentObject | null} + * @method + * @protected + */ +export const $getCurrentAttachment = (): IAttachmentObject | null => +{ + return $currentAttachment; +}; - return texture; - } +/** + * @description FrameBufferがバインドされているかどうかのフラグ + * Flag to check if FrameBuffer is bound + * + * @type {boolean} + * @protected + */ +let $isFramebufferBound: boolean = false; - /** - * @param {number} width - * @param {number} height - * @param {Uint8Array} [pixels=null] - * @param {boolean} [premultipliedAlpha=false] - * @param {boolean} [flip_y=true] - * @return {WebGLTexture} - * @public - */ - createTextureFromPixels ( - width: number, height: number, - pixels: Uint8Array|null = null, - premultipliedAlpha: boolean = false, - flip_y: boolean = true - ): WebGLTexture { - return this - ._$textureManager - .create(width, height, pixels, premultipliedAlpha, flip_y); - } +/** + * @description FrameBufferがバインドされているかどうかのフラグの値を更新 + * Update the value of the flag to check if FrameBuffer is bound + * + * @param {boolean} state + * @return {void} + * @method + * @protected + */ +export const $setFramebufferBound = (state: boolean): void => +{ + $isFramebufferBound = state; +}; - /** - * @param {HTMLCanvasElement} canvas - * @return {WebGLTexture} - * @public - */ - createTextureFromCanvas (canvas: HTMLCanvasElement | OffscreenCanvas): WebGLTexture - { - return this - ._$textureManager - .createFromCanvas(canvas); - } +/** + * @description FrameBufferがバインドされているかどうかのフラグの値を返却 + * Returns the value of the flag to check if FrameBuffer is bound + * + * @return {boolean} + * @method + * @protected + */ +export const $useFramebufferBound = (): boolean => +{ + return $isFramebufferBound; +}; - /** - * @param {HTMLImageElement} image - * @param {boolean} [smoothing=false] - * @return {WebGLTexture} - * @public - */ - createTextureFromImage ( - image: HTMLImageElement, - smoothing: boolean = false - ): WebGLTexture { - return this - ._$textureManager - .createFromImage(image, smoothing); - } +/** + * @description ビットマップの読み込み専用のFrameBufferオブジェクト + * FrameBuffer object for reading bitmaps only + * + * @type {WebGLFramebuffer|null} + * @default null + * @private + */ +let $readBitmapFramebuffer: WebGLFramebuffer | null = null; - /** - * @param {HTMLVideoElement} video - * @param {boolean} [smoothing=false] - * @return {WebGLTexture} - * @public - */ - createTextureFromVideo ( - video: HTMLVideoElement, - smoothing: boolean = false - ): WebGLTexture { - return this - ._$textureManager - .createFromVideo(video, smoothing); - } +/** + * @description ビットマップの書き込み専用のFrameBufferオブジェクト + * FrameBuffer object for writing bitmaps only + * + * @type {WebGLFramebuffer|null} + * @default null + * @private + */ +let $drawBitmapFramebuffer: WebGLFramebuffer | null = null; - /** - * @return {WebGLTexture} - * @public - */ - createTextureFromCurrentAttachment (): WebGLTexture - { - if (!this._$currentAttachment) { - throw new Error("the current attachment is null."); - } +/** + * @type {WebGLFramebuffer} + * @private + */ +let $pixelFrameBuffer: WebGLFramebuffer | null = null; - const width: number = this._$currentAttachment.width; - const height: number = this._$currentAttachment.height; +/** + * @description PBO + * Pixel Buffer Object + * + * @type {WebGLBuffer} + * @private + */ +let $pixelBufferObject: WebGLBuffer | null = null; - const texture: WebGLTexture = this._$textureManager.create(width, height); +/** + * @description ビットマップの読み込み専用のFrameBufferオブジェクトを設定 + * Set the FrameBuffer object for reading bitmaps only + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setBitmapFrameBuffer = (gl: WebGL2RenderingContext): void => +{ + // blitFramebuffer + $drawBitmapFramebuffer = gl.createFramebuffer() as NonNullable; + $readBitmapFramebuffer = gl.createFramebuffer() as NonNullable; - this._$textureManager.bind0(texture); + // PBO + $pixelFrameBuffer = gl.createFramebuffer() as NonNullable; + $pixelBufferObject = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, $pixelBufferObject); +}; - this._$gl.copyTexSubImage2D( - this._$gl.TEXTURE_2D, 0, - 0, 0, 0, 0, width, height - ); +/** + * @description ビットマップの読み込み専用のFrameBufferオブジェクトを返却 + * Returns the FrameBuffer object for reading bitmaps only + * + * @return {WebGLFramebuffer} + * @method + * @protected + */ +export const $getReadBitmapFrameBuffer = (): WebGLFramebuffer => +{ + return $readBitmapFramebuffer as NonNullable; +}; - return texture; - } +/** + * @description ビットマップの書き込み専用のFrameBufferオブジェクトを返却 + * Returns the FrameBuffer object for writing bitmaps only + * + * @return {WebGLFramebuffer} + * @method + * @protected + */ +export const $getDrawBitmapFrameBuffer = (): WebGLFramebuffer => +{ + return $drawBitmapFramebuffer as NonNullable; +}; - /** - * @param {WebGLTexture} texture - * @return void - * @public - */ - releaseTexture (texture: WebGLTexture): void - { - this._$textureManager.release(texture); - } +/** + * @description PBO用のFrameBufferオブジェクトを返却 + * Returns the FrameBuffer object for PBO + * + * @return {WebGLFramebuffer} + * @method + * @protected + */ +export const $getPixelFrameBuffer = (): WebGLFramebuffer => +{ + return $pixelFrameBuffer as NonNullable; +}; -} \ No newline at end of file +/** + * @description PBO + * Pixel Buffer Object + * + * @return {WebGLBuffer} + * @method + * @protected + */ +export const $getPixelBufferObject = (): WebGLBuffer => +{ + return $pixelBufferObject as NonNullable; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.test.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.test.ts new file mode 100644 index 00000000..5543698f --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.test.ts @@ -0,0 +1,142 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import { + $setCurrentAttachment, + $getCurrentAttachment, + $setFramebufferBound, + $useFramebufferBound +} from "../../FrameBufferManager"; +import { + $activeTextureUnit, + $setActiveTextureUnit, +} from "../../TextureManager"; +import { execute } from "./FrameBufferManagerBindAttachmentObjectService"; +import { describe, expect, it, vi } from "vitest"; + +describe("FrameBufferManagerBindAttachmentObjectService.js method test", () => +{ + it("test case1", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "TEXTURE0": 0, + "TEXTURE1": 1, + "TEXTURE2": 2, + "bindTexture": vi.fn(() => { return "bindTexture" }), + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindFramebuffer": vi.fn(() => { return "bindFramebuffer" }), + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "framebufferTexture2D": vi.fn(() => { return "framebufferTexture2D" }), + "framebufferRenderbuffer": vi.fn(() => { return "framebufferRenderbuffer" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + } + } + }); + + const attachmentObject: IAttachmentObject = { + "id": 0, + "width": 100, + "height": 120, + "msaa": false, + "mask": false, + "clipLevel": 0, + "color": null, + "texture": { + "id": 0, + "resource": {} as WebGLTexture, + "width": 100, + "height": 120, + "area": 100 * 120, + "smooth": false, + }, + "stencil": { + "id": 0, + "resource": {} as WebGLRenderbuffer, + "width": 100, + "height": 120, + "area": 100 * 120, + "dirty": false + } + }; + + $setFramebufferBound(false); + expect($useFramebufferBound()).toBe(false); + $setCurrentAttachment(null); + expect($getCurrentAttachment()).toBe(null); + $setActiveTextureUnit(-1); + expect($activeTextureUnit).toBe(-1); + + execute(attachmentObject); + + expect($getCurrentAttachment()).toBe(attachmentObject); + expect($useFramebufferBound()).toBe(true); + expect($activeTextureUnit).toBe(0); + }); + + it("test case2", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "TEXTURE0": 0, + "TEXTURE1": 1, + "TEXTURE2": 2, + "bindTexture": vi.fn(() => { return "bindTexture" }), + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindFramebuffer": vi.fn(() => { return "bindFramebuffer" }), + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "framebufferTexture2D": vi.fn(() => { return "framebufferTexture2D" }), + "framebufferRenderbuffer": vi.fn(() => { return "framebufferRenderbuffer" }), + } + } + }); + + const colorBufferObject: IColorBufferObject = { + "resource": {} as WebGLRenderbuffer, + "stencil": { + "id": 0, + "resource": {} as WebGLRenderbuffer, + "width": 0, + "height": 0, + "area": 0 * 0, + "dirty": false + }, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false + }; + + const attachmentObject: IAttachmentObject = { + "id": 0, + "width": 100, + "height": 120, + "msaa": true, + "mask": false, + "clipLevel": 0, + "color": colorBufferObject, + "texture": null, + "stencil": colorBufferObject.stencil + }; + + $setFramebufferBound(false); + expect($useFramebufferBound()).toBe(false); + $setCurrentAttachment(null); + expect($getCurrentAttachment()).toBe(null); + $setActiveTextureUnit(-1); + expect($activeTextureUnit).toBe(-1); + + execute(attachmentObject); + + expect($getCurrentAttachment()).toBe(attachmentObject); + expect($useFramebufferBound()).toBe(true); + expect($activeTextureUnit).toBe(-1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.ts new file mode 100644 index 00000000..558f0547 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerBindAttachmentObjectService.ts @@ -0,0 +1,52 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { $gl } from "../../WebGLUtil"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { + $setFramebufferBound, + $setCurrentAttachment, + $readFrameBuffer, + $useFramebufferBound +} from "../../FrameBufferManager"; + +/** + * @description フレームバッファにアタッチメントオブジェクトをバインドする + * Bind the attachment object to the frame buffer + * + * @param {IAttachmentObject} attachment_object + * @return {void} + * @method + * @protected + */ +export const execute = (attachment_object: IAttachmentObject): void => +{ + $setCurrentAttachment(attachment_object); + + if (!$useFramebufferBound()) { + $setFramebufferBound(true); + $gl.bindFramebuffer($gl.FRAMEBUFFER, $readFrameBuffer); + } + + if (attachment_object.msaa) { + $gl.bindRenderbuffer($gl.RENDERBUFFER, (attachment_object.color as IColorBufferObject).resource); + $gl.framebufferRenderbuffer( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.RENDERBUFFER, (attachment_object.color as IColorBufferObject).resource + ); + } else { + textureManagerBind0UseCase(attachment_object.texture as ITextureObject); + + $gl.framebufferTexture2D( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.TEXTURE_2D, (attachment_object.texture as ITextureObject).resource, 0 + ); + } + + $gl.bindRenderbuffer($gl.RENDERBUFFER, (attachment_object.stencil as IStencilBufferObject).resource); + $gl.framebufferRenderbuffer( + $gl.FRAMEBUFFER, $gl.STENCIL_ATTACHMENT, + $gl.RENDERBUFFER, (attachment_object.stencil as IStencilBufferObject).resource + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.test.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.test.ts new file mode 100644 index 00000000..64397363 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.test.ts @@ -0,0 +1,18 @@ +import { execute } from "./FrameBufferManagerCreateAttachmentObjectService"; +import { describe, expect, it } from "vitest"; + +describe("FrameBufferManagerCreateAttachmentObjectService.js method test", () => +{ + it("test case", () => + { + const attachmentObject = execute(); + expect(attachmentObject.width).toBe(0); + expect(attachmentObject.height).toBe(0); + expect(attachmentObject.clipLevel).toBe(0); + expect(attachmentObject.msaa).toBe(false); + expect(attachmentObject.mask).toBe(false); + expect(attachmentObject.color).toBe(null); + expect(attachmentObject.texture).toBe(null); + expect(attachmentObject.stencil).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.ts new file mode 100644 index 00000000..39048033 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerCreateAttachmentObjectService.ts @@ -0,0 +1,30 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; + +/** + * @type {number} + * @private + */ +let $id: number = 0; + +/** + * @description FrameBufferManagerのアタッチメントオブジェクトを新規作成 + * Create a new attachment object for FrameBufferManager + * + * @return {IAttachmentObject} + * @method + * @protected + */ +export const execute = (): IAttachmentObject => +{ + return { + "id": $id++, + "width": 0, + "height": 0, + "clipLevel": 0, + "msaa": false, + "mask": false, + "color": null, + "texture": null, + "stencil": null + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferAtlasTextureService.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferAtlasTextureService.ts new file mode 100644 index 00000000..71a66091 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferAtlasTextureService.ts @@ -0,0 +1,84 @@ +import { + $getActiveTransferBounds, + $getActiveAtlasIndex, + $getActiveAllTransferBounds +} from "../../AtlasManager"; +import { + $gl, + $context +} from "../../WebGLUtil"; +import { + $atlasFrameBuffer, + $setFramebufferBound +} from "../../FrameBufferManager"; + +/** + * @type {number} + * @private + */ +let $currentIndex: number = 0; + +/** + * @description アトラステクスチャに転写します。 + * Transfer to atlas texture. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + + const atlasAttachmentObject = $context.atlasAttachmentObject; + $context.bind(atlasAttachmentObject); + + $gl.bindFramebuffer( + $gl.DRAW_FRAMEBUFFER, + $atlasFrameBuffer + ); + $setFramebufferBound(false); + + if ($currentIndex === $getActiveAtlasIndex()) { + const bounds = $getActiveTransferBounds($getActiveAtlasIndex()); + if (bounds[2] !== -Number.MAX_VALUE + && bounds[3] !== -Number.MAX_VALUE + ) { + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + bounds[0], bounds[1], + bounds[2], bounds[3] + ); + + $gl.blitFramebuffer( + 0, 0, atlasAttachmentObject.width, atlasAttachmentObject.height, + 0, 0, atlasAttachmentObject.width, atlasAttachmentObject.height, + $gl.COLOR_BUFFER_BIT, + $gl.NEAREST + ); + $gl.disable($gl.SCISSOR_TEST); + } + } else { + const bounds = $getActiveAllTransferBounds($getActiveAtlasIndex()); + + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + bounds[0], bounds[1], + bounds[2], bounds[3] + ); + + $gl.blitFramebuffer( + 0, 0, atlasAttachmentObject.width, atlasAttachmentObject.height, + 0, 0, atlasAttachmentObject.width, atlasAttachmentObject.height, + $gl.COLOR_BUFFER_BIT, + $gl.NEAREST + ); + $gl.disable($gl.SCISSOR_TEST); + + $currentIndex = $getActiveAtlasIndex(); + } + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferMainCanvasService.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferMainCanvasService.ts new file mode 100644 index 00000000..f56ef258 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerTransferMainCanvasService.ts @@ -0,0 +1,46 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { + $readFrameBuffer, + $drawFrameBuffer +} from "../../FrameBufferManager"; +import { + $gl, + $context +} from "../../WebGLUtil"; + +/** + * @description メインのアタッチメントオブジェクトをメインキャンバスに転送します。 + * Transfer the main attachment object to the main canvas. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const mainAttachmentObject = $context.$mainAttachmentObject as IAttachmentObject; + $context.bind(mainAttachmentObject); + + // fixed logic + $gl.bindFramebuffer($gl.READ_FRAMEBUFFER, $readFrameBuffer); + + // use main Framebuffer + $gl.bindFramebuffer( + $gl.DRAW_FRAMEBUFFER, + null + ); + + const width = mainAttachmentObject.width; + const height = mainAttachmentObject.height; + + $gl.blitFramebuffer( + 0, 0, width, height, + 0, 0, width, height, + $gl.COLOR_BUFFER_BIT, + $gl.NEAREST + ); + + // fixed logic + $gl.bindFramebuffer($gl.DRAW_FRAMEBUFFER, $drawFrameBuffer); + $gl.bindFramebuffer($gl.FRAMEBUFFER, $readFrameBuffer); +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.test.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.test.ts new file mode 100644 index 00000000..79468133 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.test.ts @@ -0,0 +1,58 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject.ts"; +import { execute } from "./FrameBufferManagerUnBindAttachmentObjectService.ts"; +import { describe, expect, it, vi } from "vitest"; +import { + $setCurrentAttachment, + $getCurrentAttachment, + $useFramebufferBound, + $setFramebufferBound +} from "../../FrameBufferManager.ts"; + +describe("FrameBufferManagerUnBindAttachmentObjectService.js method test", () => +{ + it("test case1", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "bindFramebuffer": vi.fn(() => { return "bindFramebuffer" }), + } + } + }); + + const attachmentObject: IAttachmentObject = { + "width": 100, + "height": 100, + "clipLevel": 0, + "msaa": false, + "mask": false, + "color": null, + "texture": { + "resource": "createTexture", + "width": 100, + "height": 100, + "area": 100 * 100, + }, + "stencil": { + "resource": "createRenderbuffer", + "width": 100, + "height": 100, + "area": 100 * 100, + "dirty": false, + } + } + + $setCurrentAttachment(attachmentObject) + expect($getCurrentAttachment()).toBe(attachmentObject); + + $setFramebufferBound(true); + expect($useFramebufferBound()).toBe(true); + + execute(); + expect($useFramebufferBound()).toBe(false); + expect($getCurrentAttachment()).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.ts b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.ts new file mode 100644 index 00000000..19a163fa --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/service/FrameBufferManagerUnBindAttachmentObjectService.ts @@ -0,0 +1,24 @@ +import { $gl } from "../../WebGLUtil"; +import { + $setCurrentAttachment, + $useFramebufferBound, + $setFramebufferBound +} from "../../FrameBufferManager"; + +/** + * @description 現在アタッチされているFramebufferのバインド解除 + * Unbind the currently attached Framebuffer + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $setCurrentAttachment(null); + + if ($useFramebufferBound()) { + $setFramebufferBound(false); + $gl.bindFramebuffer($gl.FRAMEBUFFER, null); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.test.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.test.ts new file mode 100644 index 00000000..bdffcba9 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.test.ts @@ -0,0 +1,81 @@ +import { execute } from "./FrameBufferManagerGetAttachmentObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("FrameBufferManagerGetAttachmentObjectUseCase.js method test", () => +{ + it("test case1", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createTexture": vi.fn(() => { return "createTexture" }), + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }), + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }), + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "renderbufferStorage": vi.fn(() => { return "renderbufferStorage" }), + } + } + }); + + const attachmentObject = execute(100, 100, false); + expect(attachmentObject.width).toBe(100); + expect(attachmentObject.height).toBe(100); + expect(attachmentObject.clipLevel).toBe(0); + expect(attachmentObject.msaa).toBe(false); + expect(attachmentObject.mask).toBe(false); + expect(attachmentObject.color).toBe(null); + expect(attachmentObject.texture?.resource).toBe("createTexture"); + expect(attachmentObject.texture?.width).toBe(100); + expect(attachmentObject.texture?.height).toBe(100); + expect(attachmentObject.texture?.area).toBe(100 * 100); + expect(attachmentObject.stencil?.resource).toBe("createRenderbuffer"); + expect(attachmentObject.stencil?.width).toBe(100); + expect(attachmentObject.stencil?.height).toBe(100); + expect(attachmentObject.stencil?.area).toBe(100 * 100); + }); + + it("test case2", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createTexture": vi.fn(() => { return "createTexture" }), + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }), + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }), + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "renderbufferStorage": vi.fn(() => { return "renderbufferStorage" }), + "renderbufferStorageMultisample": vi.fn(() => { return "renderbufferStorageMultisample" }), + } + } + }); + + const attachmentObject = execute(100, 100, true); + expect(attachmentObject.width).toBe(100); + expect(attachmentObject.height).toBe(100); + expect(attachmentObject.clipLevel).toBe(0); + expect(attachmentObject.msaa).toBe(true); + expect(attachmentObject.mask).toBe(false); + expect(attachmentObject.color?.resource).toBe("createRenderbuffer"); + expect(attachmentObject.color?.width).toBe(256); + expect(attachmentObject.color?.height).toBe(256); + expect(attachmentObject.color?.area).toBe(256 * 256); + expect(attachmentObject.texture).toBe(null); + expect(attachmentObject.stencil?.resource).toBe("createRenderbuffer"); + expect(attachmentObject.stencil?.width).toBe(0); + expect(attachmentObject.stencil?.height).toBe(0); + expect(attachmentObject.stencil?.area).toBe(0); + + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.ts new file mode 100644 index 00000000..38d00570 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase.ts @@ -0,0 +1,52 @@ + +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { $objectPool } from "../../FrameBufferManager"; +import { execute as frameBufferManagerCreateAttachmentObjectService } from "../service/FrameBufferManagerCreateAttachmentObjectService"; +import { execute as colorBufferObjectGetColorBufferObjectUseCase } from "../../ColorBufferObject/usecase/ColorBufferObjectGetColorBufferObjectUseCase"; +import { execute as stencilBufferObjectGetStencilBufferObjectUseCase } from "../../StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase"; +import { execute as textureManagerGetTextureUseCase } from "../../TextureManager/usecase/TextureManagerGetTextureUseCase"; + +/** + * @description FrameBufferManagerのアタッチメントオブジェクトを取得 + * Get the attachment object of FrameBufferManager + * + * @param {number} width + * @param {number} height + * @param {boolean} [multisample=false] + * @return {IAttachmentObject} + * @method + * @protected + */ +export const execute = ( + width: number, + height: number, + multisample: boolean = false +): IAttachmentObject => { + + // キャッシュがあれば再利用する + const attachmentObject = $objectPool.length + ? $objectPool.shift() as IAttachmentObject + : frameBufferManagerCreateAttachmentObjectService(); + + // テクスチャを取得 + attachmentObject.width = width; + attachmentObject.height = height; + + if (multisample) { + attachmentObject.msaa = true; + attachmentObject.texture = null; + attachmentObject.color = colorBufferObjectGetColorBufferObjectUseCase(width, height); + attachmentObject.stencil = attachmentObject.color.stencil; + } else { + attachmentObject.msaa = false; + attachmentObject.texture = textureManagerGetTextureUseCase(width, height); + attachmentObject.color = null; + attachmentObject.stencil = stencilBufferObjectGetStencilBufferObjectUseCase(width, height); + } + + // reset + attachmentObject.mask = false; + attachmentObject.clipLevel = 0; + + return attachmentObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromBoundsUseCase.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromBoundsUseCase.ts new file mode 100644 index 00000000..d1b83914 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromBoundsUseCase.ts @@ -0,0 +1,61 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { execute as variantsBlendTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as frameBufferManagerReleaseAttachmentObjectUseCase } from "../../FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as textureManagerGetMainTextureFromBoundsUseCase } from "../../TextureManager/usecase/TextureManagerGetMainTextureFromBoundsUseCase"; +import { execute as blendResetService } from "../../Blend/service/BlendResetService"; +import { $context } from "../../WebGLUtil"; + +/** + * @description 現在のアタッチメントオブジェクトから指定の範囲のtextureを取得します。 + * Get a texture from the specified range of the current attachment object. + * + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {IAttachmentObject} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + width: number, + height: number +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject as IAttachmentObject; + + const mainTextureObject = textureManagerGetMainTextureFromBoundsUseCase(x, y, width, height); + + const attachmentObject = frameBufferManagerGetAttachmentObjectUseCase(width, height, false); + $context.bind(attachmentObject); + + $context.save(); + $context.setTransform(1, 0, 0, 1, -x, -y); + + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService( + shaderManager, mainTextureObject.width, mainTextureObject.height + ); + + blendResetService(); + textureManagerBind0UseCase(mainTextureObject); + shaderManagerDrawTextureUseCase(shaderManager); + + const textureObject = attachmentObject.texture as ITextureObject; + + $context.restore(); + frameBufferManagerReleaseAttachmentObjectUseCase(attachmentObject, false); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromNodeUseCase.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromNodeUseCase.ts new file mode 100644 index 00000000..2daeab79 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerGetTextureFromNodeUseCase.ts @@ -0,0 +1,69 @@ +import type { Node } from "@next2d/texture-packer"; +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerGetTextureUseCase } from "../../TextureManager/usecase/TextureManagerGetTextureUseCase"; +import { execute as frameBufferManagerTransferAtlasTextureService } from "../../FrameBufferManager/service/FrameBufferManagerTransferAtlasTextureService"; +import { + $getDrawBitmapFrameBuffer, + $getReadBitmapFrameBuffer, + $readFrameBuffer +} from "../../FrameBufferManager"; +import { $gl } from "../../WebGLUtil"; +import { + $getActiveAtlasIndex, + $setActiveAtlasIndex, + $getAtlasTextureObject +} from "../../AtlasManager"; + +/** + * @description アトラスフレームバッファからテクスチャを取得します。 + * Get a texture from the atlas frame buffer. + * + * @param {Node} node + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (node: Node): ITextureObject => +{ + // 指定されたindexのフレームバッファの描画をアトラステクスチャに転送 + const activeIndex = $getActiveAtlasIndex(); + $setActiveAtlasIndex(node.index); + frameBufferManagerTransferAtlasTextureService(); + + const readBitmapFrameBuffer = $getReadBitmapFrameBuffer(); + $gl.bindFramebuffer($gl.FRAMEBUFFER, readBitmapFrameBuffer); + + const atlasTextureObject = $getAtlasTextureObject(); + $gl.framebufferTexture2D( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.TEXTURE_2D, atlasTextureObject.resource, 0 + ); + + const drawBitmapFrameBuffer = $getDrawBitmapFrameBuffer(); + $gl.bindFramebuffer($gl.FRAMEBUFFER, drawBitmapFrameBuffer); + + const textureObject = textureManagerGetTextureUseCase(node.w, node.h); + $gl.framebufferTexture2D( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.TEXTURE_2D, textureObject.resource, 0 + ); + + $gl.bindFramebuffer($gl.FRAMEBUFFER, null); + $gl.bindFramebuffer($gl.READ_FRAMEBUFFER, readBitmapFrameBuffer); + $gl.bindFramebuffer($gl.DRAW_FRAMEBUFFER, drawBitmapFrameBuffer); + + // execute + $gl.blitFramebuffer( + node.x, node.y, node.x + node.w, node.y + node.h, + 0, 0, node.w, node.h, + $gl.COLOR_BUFFER_BIT, + $gl.NEAREST + ); + + // restore + $setActiveAtlasIndex(activeIndex); + $gl.bindFramebuffer($gl.READ_FRAMEBUFFER, $readFrameBuffer); + $gl.bindFramebuffer($gl.FRAMEBUFFER, $readFrameBuffer); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.test.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.test.ts new file mode 100644 index 00000000..5b3a34dc --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.test.ts @@ -0,0 +1,126 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { execute } from "./FrameBufferManagerReleaseAttachmentObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../FrameBufferManager"; +import { $objectPool as $stencilBufferObjectPool } from "../../StencilBufferObject"; +import { $objectPool as $colorBufferObjectPool } from "../../ColorBufferObject"; + +describe("FrameBufferManagerReleaseAttachmentObjectUseCase.js method test", () => +{ + it("test case1", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "deleteTexture": vi.fn(() => { return "deleteTexture" }) + } + } + }); + + const attachmentObject: IAttachmentObject = { + "id": 0, + "width": 100, + "height": 100, + "clipLevel": 0, + "msaa": false, + "mask": false, + "color": null, + "texture": { + "id": 0, + "resource": "createTexture", + "width": 100, + "height": 100, + "area": 100 * 100, + "smooth": false + }, + "stencil": { + "id": 0, + "resource": "createRenderbuffer", + "width": 100, + "height": 100, + "area": 100 * 100, + "dirty": false, + } + } + + $objectPool.length = 0; + expect($objectPool.length).toBe(0); + + $stencilBufferObjectPool.length = 0; + expect($stencilBufferObjectPool.length).toBe(0); + + $colorBufferObjectPool.length = 0; + expect($colorBufferObjectPool.length).toBe(0); + + execute(attachmentObject); + expect($objectPool.length).toBe(1); + expect($stencilBufferObjectPool.length).toBe(1); + expect($colorBufferObjectPool.length).toBe(0); + }); + + it("test case2", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "deleteTexture": vi.fn(() => { return "deleteTexture" }), + } + } + }); + + const colorBufferObject = { + "resource": "createRenderbuffer", + "width": 100, + "height": 100, + "area": 100 * 100, + "dirty": false, + "stencil": { + "id": 0, + "resource": "createRenderbuffer", + "width": 0, + "height": 0, + "area": 0, + "dirty": false + } + }; + + const attachmentObject: IAttachmentObject = { + "id": 0, + "width": 100, + "height": 100, + "clipLevel": 0, + "msaa": true, + "mask": false, + "color": colorBufferObject, + "texture": { + "id": 0, + "resource": "createTexture", + "width": 100, + "height": 100, + "area": 100 * 100, + "smooth": false + }, + "stencil": colorBufferObject.stencil + } + + $objectPool.length = 0; + expect($objectPool.length).toBe(0); + + $stencilBufferObjectPool.length = 0; + expect($stencilBufferObjectPool.length).toBe(0); + + $colorBufferObjectPool.length = 0; + expect($colorBufferObjectPool.length).toBe(0); + + execute(attachmentObject); + expect($objectPool.length).toBe(1); + expect($stencilBufferObjectPool.length).toBe(0); + expect($colorBufferObjectPool.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.ts new file mode 100644 index 00000000..0aa027bb --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerReleaseAttachmentObjectUseCase.ts @@ -0,0 +1,44 @@ +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import type { IColorBufferObject } from "../../interface/IColorBufferObject"; +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { execute as colorBufferObjectReleaseColorBufferObjectUseCase } from "../../ColorBufferObject/usecase/ColorBufferObjectReleaseColorBufferObjectUseCase"; +import { execute as textureManagerReleaseTextureObjectUseCase } from "../../TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase"; +import { execute as stencilBufferObjectReleaseColorBufferObjectUseCase } from "../../StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService"; +import { $objectPool } from "../../FrameBufferManager"; + +/** + * @description 各オブジェクトを再利用するためにプールを行い、アタッチメントオブジェクトは初期化してプールに戻す + * Pool each object for reuse, and initialize and return the attachment object to the pool + * + * @param {IAttachmentObject} attachment_object + * @param {boolean} [release_texture=true] + * @return {void} + * @method + * @protected + */ +export const execute = ( + attachment_object: IAttachmentObject, + release_texture: boolean = true +): void => { + if (attachment_object.msaa) { + colorBufferObjectReleaseColorBufferObjectUseCase( + attachment_object.color as IColorBufferObject + ); + attachment_object.color = null; + attachment_object.stencil = null; + } else { + if (release_texture) { + textureManagerReleaseTextureObjectUseCase( + attachment_object.texture as ITextureObject + ); + } + attachment_object.texture = null; + stencilBufferObjectReleaseColorBufferObjectUseCase( + attachment_object.stencil as IStencilBufferObject + ); + attachment_object.stencil = null; + } + + $objectPool.push(attachment_object); +}; \ No newline at end of file diff --git a/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerTransferTextureFromRectUseCase.ts b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerTransferTextureFromRectUseCase.ts new file mode 100644 index 00000000..3fb78b13 --- /dev/null +++ b/packages/webgl/src/FrameBufferManager/usecase/FrameBufferManagerTransferTextureFromRectUseCase.ts @@ -0,0 +1,33 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerBind0UseCase } from "../../TextureManager/usecase/TextureManagerBind0UseCase"; +import { execute as variantsBlendTextureShaderService } from "../../Shader/Variants/Blend/service/VariantsBlendTextureShaderService"; +import { execute as shaderManagerSetTextureUniformService } from "../../Shader/ShaderManager/service/ShaderManagerSetTextureUniformService"; +import { execute as shaderManagerDrawTextureUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase"; +import { execute as blendOneZeroService } from "../../Blend/service/BlendOneZeroService"; +import { execute as blendResetService } from "../../Blend/service/BlendResetService"; + +/** + * @description メインのアタッチメントオブジェクトをメインキャンバスに転送します。 + * Transfer the main attachment object to the main canvas. + * + * @param {ITextureObject} texture_object + * @return {void} + * @method + * @protected + */ +export const execute = (texture_object: ITextureObject): void => +{ + // ブレンドするテクスチャをバインド + textureManagerBind0UseCase(texture_object); + + blendOneZeroService(); + + // blend用のシェーダーを取得 + const shaderManager = variantsBlendTextureShaderService(); + shaderManagerSetTextureUniformService( + shaderManager, texture_object.width, texture_object.height + ); + + shaderManagerDrawTextureUseCase(shaderManager); + blendResetService(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Gradient.ts b/packages/webgl/src/Gradient.ts new file mode 100644 index 00000000..fd618ba9 --- /dev/null +++ b/packages/webgl/src/Gradient.ts @@ -0,0 +1,8 @@ +/** + * @description グラデーションの設定データ + * Gradient setting data + * + * @type {Array} + * @protected + */ +export const $gradientData: Array = []; \ No newline at end of file diff --git a/packages/webgl/src/Grid.ts b/packages/webgl/src/Grid.ts new file mode 100644 index 00000000..0383af4d --- /dev/null +++ b/packages/webgl/src/Grid.ts @@ -0,0 +1,18 @@ +/** + * @type {Map} + * @private + */ +export const $gridDataMap: Map = new Map(); + +/** + * @description グリッド情報を初期化 + * Initialize grid information + * + * @return {void} + * @method + * @protected + */ +export const $terminateGrid = (): void => +{ + $gridDataMap.clear(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask.ts b/packages/webgl/src/Mask.ts new file mode 100644 index 00000000..b03b65b5 --- /dev/null +++ b/packages/webgl/src/Mask.ts @@ -0,0 +1,48 @@ +/** + * @description マスク描画中かどうか + * Whether the mask is being drawn + * + * @type {boolean} + * @default false + * @private + */ +let $maskDrawingState: boolean = false; + +/** + * @description マスク描画の状態をセット + * Set the state of mask drawing + * + * @param {boolean} state + * @return {void} + * @method + * @public + */ +export const $setMaskDrawing = (state: boolean): void => +{ + $maskDrawingState = state; +}; + +/** + * @description マスク描画中かどうかを返却 + * Returns whether the mask is being drawn + * + * @return {boolean} + * @method + * @public + */ +export const $isMaskDrawing = (): boolean => +{ + return $maskDrawingState; +}; + +/** + * @type {Map} + * @private + */ +export const $clipBounds: Map = new Map(); + +/** + * @type {Map} + * @private + */ +export const $clipLevels: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Mask/service/MaskBeginMaskService.ts b/packages/webgl/src/Mask/service/MaskBeginMaskService.ts new file mode 100644 index 00000000..da083f5a --- /dev/null +++ b/packages/webgl/src/Mask/service/MaskBeginMaskService.ts @@ -0,0 +1,43 @@ +import { + $isMaskDrawing, + $setMaskDrawing, + $clipLevels +} from "../../Mask"; +import { + $context, + $gl +} from "../../WebGLUtil"; + +/** + * @description マスク描画の開始準備 + * Prepare to start drawing the mask + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return; + } + + currentAttachmentObject.mask = true; + currentAttachmentObject.clipLevel++; + $clipLevels.set( + currentAttachmentObject.clipLevel, + currentAttachmentObject.clipLevel + ); + + if (!$isMaskDrawing()) { + $setMaskDrawing(true); + + $gl.enable($gl.STENCIL_TEST); + $gl.enable($gl.SAMPLE_ALPHA_TO_COVERAGE); + + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOp($gl.ZERO, $gl.INVERT, $gl.INVERT); + $gl.colorMask(false, false, false, false); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask/service/MaskEndMaskService.ts b/packages/webgl/src/Mask/service/MaskEndMaskService.ts new file mode 100644 index 00000000..062580f9 --- /dev/null +++ b/packages/webgl/src/Mask/service/MaskEndMaskService.ts @@ -0,0 +1,35 @@ +import { + $gl, + $context +} from "../../WebGLUtil"; + +/** + * @description マスクの描画を終了 + * End mask drawing + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return ; + } + + const clipLevel = currentAttachmentObject.clipLevel; + + let mask = 0; + for (let idx = 0; idx < clipLevel; ++idx) { + mask |= (1 << clipLevel - idx) - 1; + } + + $gl.stencilMask(0xff); + $gl.stencilFunc($gl.EQUAL, mask & 0xff, mask); + $gl.stencilOp($gl.KEEP, $gl.KEEP, $gl.KEEP); + $gl.colorMask(true, true, true, true); + + $gl.disable($gl.SAMPLE_ALPHA_TO_COVERAGE); + $gl.disable($gl.SCISSOR_TEST); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask/service/MaskSetMaskBoundsService.ts b/packages/webgl/src/Mask/service/MaskSetMaskBoundsService.ts new file mode 100644 index 00000000..b32fb5dc --- /dev/null +++ b/packages/webgl/src/Mask/service/MaskSetMaskBoundsService.ts @@ -0,0 +1,47 @@ +import { $clipBounds } from "../../Mask"; +import { + $gl, + $context, + $getFloat32Array4 +} from "../../WebGLUtil"; + +/** + * @description マスクの描画範囲をセット + * Set the drawing range of the mask. + * + * @param {number} x_min + * @param {number} y_min + * @param {number} x_max + * @param {number} y_max + * @return {void} + * @method + * @protected + */ +export const execute = ( + x_min: number, + y_min: number, + x_max: number, + y_max: number +): void => { + + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return ; + } + + // レベルと描画範囲をセット + $clipBounds.set( + currentAttachmentObject.clipLevel, + $getFloat32Array4(x_min, y_min, x_max, y_max) + ); + + const width = Math.ceil(Math.abs(x_max - x_min)); + const height = Math.ceil(Math.abs(y_max - y_min)); + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + x_min, + currentAttachmentObject.height - y_min - height, + width, + height + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask/service/MaskUnionMaskService.ts b/packages/webgl/src/Mask/service/MaskUnionMaskService.ts new file mode 100644 index 00000000..ee327d97 --- /dev/null +++ b/packages/webgl/src/Mask/service/MaskUnionMaskService.ts @@ -0,0 +1,48 @@ +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as variantsShapeRectShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeRectShaderService"; +import { $getRectVertexArrayObject } from "../../VertexArrayObject"; +import { + $gl, + $context +} from "../../WebGLUtil"; + +/** + * @description マスクの合成処理 + * Mask synthesis processing + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return ; + } + + // レベル以上のステンシルバッファをマージ + const mask = 1 << currentAttachmentObject.clipLevel - 1; + $gl.stencilMask(~mask - 1); + $gl.stencilFunc($gl.LEQUAL, mask, 0xff); + $gl.stencilOp($gl.ZERO, $gl.REPLACE, $gl.REPLACE); + shaderManagerFillUseCase( + variantsShapeRectShaderService(), + $getRectVertexArrayObject(), + 0, 6 + ); + + // レベル以上のステンシルバッファをクリア + $gl.stencilMask(1 << currentAttachmentObject.clipLevel); + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOp($gl.REPLACE, $gl.REPLACE, $gl.REPLACE); + shaderManagerFillUseCase( + variantsShapeRectShaderService(), + $getRectVertexArrayObject(), + 0, 6 + ); + + // base mask setting + $gl.stencilMask(0xff); + $gl.stencilOp($gl.ZERO, $gl.INVERT, $gl.INVERT); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask/usecase/MaskBindUseCase.ts b/packages/webgl/src/Mask/usecase/MaskBindUseCase.ts new file mode 100644 index 00000000..623cb397 --- /dev/null +++ b/packages/webgl/src/Mask/usecase/MaskBindUseCase.ts @@ -0,0 +1,32 @@ + +import { execute as maskEndMaskService } from "../service/MaskEndMaskService"; +import { $gl } from "../../WebGLUtil"; +import { + $isMaskDrawing, + $setMaskDrawing +} from "../../Mask"; + +/** + * @description マスクOn/Offに合わせたバインド処理 + * Binding process according to mask On/Off + * + * @param {boolean} mask + * @return {void} + * @method + * @protected + */ +export const execute = (mask: boolean): void => +{ + if (!mask && $isMaskDrawing()) { + $setMaskDrawing(false); + + $gl.disable($gl.STENCIL_TEST); + $gl.disable($gl.SCISSOR_TEST); + } else if (mask && !$isMaskDrawing()) { + $setMaskDrawing(true); + + // キャッシュ作成後は、マスクの状態を復元する + $gl.enable($gl.STENCIL_TEST); + maskEndMaskService(); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Mask/usecase/MaskLeaveMaskUseCase.ts b/packages/webgl/src/Mask/usecase/MaskLeaveMaskUseCase.ts new file mode 100644 index 00000000..28a50ff1 --- /dev/null +++ b/packages/webgl/src/Mask/usecase/MaskLeaveMaskUseCase.ts @@ -0,0 +1,81 @@ +import { execute as maskEndMaskService } from "../service/MaskEndMaskService"; +import { execute as shaderManagerFillUseCase } from "../../Shader/ShaderManager/usecase/ShaderManagerFillUseCase"; +import { execute as variantsShapeRectShaderService } from "../../Shader/Variants/Shape/service/VariantsShapeRectShaderService"; +import { $getRectVertexArrayObject } from "../../VertexArrayObject"; +import { + $setMaskDrawing, + $clipBounds, + $clipLevels +} from "../../Mask"; +import { + $gl, + $context, + $poolFloat32Array4 +} from "../../WebGLUtil"; + +/** + * @description マスクの終了処理 + * End mask processing + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + const currentAttachmentObject = $context.currentAttachmentObject; + if (!currentAttachmentObject) { + return ; + } + + const clipLevel = currentAttachmentObject.clipLevel; + const bounds = $clipBounds.get(clipLevel) as Float32Array; + const xMin = bounds[0]; + const yMin = bounds[1]; + const xMax = bounds[2]; + const yMax = bounds[3]; + + // レベルと描画範囲を削除 + $clipBounds.delete(clipLevel); + $clipLevels.delete(clipLevel); + $poolFloat32Array4(bounds); + + const width = Math.ceil(Math.abs(xMax - xMin)); + const height = Math.ceil(Math.abs(yMax - yMin)); + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + xMin, + currentAttachmentObject.height - yMin - height, + width, + height + ); + + // 単体のマスクであれば終了 + --currentAttachmentObject.clipLevel; + if (!currentAttachmentObject.clipLevel) { + currentAttachmentObject.mask = false; + $setMaskDrawing(false); + + $gl.clear($gl.STENCIL_BUFFER_BIT); + $gl.disable($gl.STENCIL_TEST); + $gl.disable($gl.SCISSOR_TEST); + + $clipLevels.clear(); + $clipBounds.clear(); + return ; + } + + // 上位レベルのステンシルバッファをクリア + $gl.stencilMask(1 << currentAttachmentObject.clipLevel); + $gl.stencilFunc($gl.ALWAYS, 0, 0xff); + $gl.stencilOp($gl.REPLACE, $gl.REPLACE, $gl.REPLACE); + $gl.colorMask(false, false, false, false); + + shaderManagerFillUseCase( + variantsShapeRectShaderService(), + $getRectVertexArrayObject(), + 0, 6 + ); + + maskEndMaskService(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh.ts b/packages/webgl/src/Mesh.ts new file mode 100644 index 00000000..3b3dd89d --- /dev/null +++ b/packages/webgl/src/Mesh.ts @@ -0,0 +1,120 @@ +import type { IFillType } from "./interface/IFillType"; + +/** + * @description 指定された値を2の累乗に切り上げます。 + * Rounds the specified value up to a power of two. + * + * @param {number} v + * @return {number} + * @method + * @protected + */ +export const $upperPowerOfTwo = (v: number): number => +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +}; + +/** + * @type {Float32Array} + * @private + */ +let $fillBuffer: Float32Array = new Float32Array(128); + +/** + * @type {number} + * @private + */ +let $fillBufferOffset: number = 0; + +/** + * @description バッファを埋めるデータを返却 + * Returns the data to fill the buffer + * + * @return {Float32Array} + * @method + * @protected + */ +export const $getFillBuffer = (): Float32Array => +{ + return $fillBuffer; +}; + +/** + * @description バッファを埋めるデータのオフセットを返却 + * Returns the offset of the data to fill the buffer + * + * @return {number} + * @method + * @protected + */ +export const $getFillBufferOffset = (): number => +{ + return $fillBufferOffset; +}; + +/** + * @description バッファを埋めるデータを設定 + * Set the data to fill the buffer + * + * @param {Float32Array} buffer + * @return {void} + * @method + * @protected + */ +export const $addFillBuffer = (buffer: Float32Array): void => +{ + const length = buffer.length + $fillBufferOffset; + if (length > $fillBuffer.length) { + const newBuffer = new Float32Array($upperPowerOfTwo(length)); + newBuffer.set($fillBuffer); + newBuffer.set(buffer, $fillBufferOffset); + + $fillBuffer = newBuffer; + } else { + $fillBuffer.set(buffer, $fillBufferOffset); + } + + $fillBufferOffset += buffer.length; +}; + +/** + * @description バッファを頂点数の配列 + * Array of the number of vertices in the buffer + * + * @type {number[]} + * @protected + */ +export const $fillBufferIndexes: number[] = []; + +/** + * @description 塗りの種類の配列 + * Array of fill types + * + * @type {IFillType[]} + * @protected + */ +export const $fillTypes: IFillType[] = []; + +/** + * @description 頂点情報の設定値を初期化 + * Initializes the setting value of the vertex information + * + * @return {void} + * @method + * @protected + */ +export const $clearFillBufferSetting = (): void => +{ + $fillTypes.length = 0; + + // fill + $fillBufferOffset = 0; + $fillBufferIndexes.length = 0; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshCalculateNormalVectorService.ts b/packages/webgl/src/Mesh/service/MeshCalculateNormalVectorService.ts new file mode 100644 index 00000000..fec12de3 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshCalculateNormalVectorService.ts @@ -0,0 +1,21 @@ +import type { IPoint } from "../../interface/IPoint"; + +/** + * @description 直線の法線ベクトルを計算 + * Calculate the normal vector of a line + * + * @param {number} x + * @param {number} y + * @param {number} thickness + * @return {IPoint} + * @method + * @protected + */ +export const execute = (x: number, y: number, thickness: number): IPoint => +{ + const magnitude = Math.sqrt(x * x + y * y); + return { + "x": -(y / magnitude) * thickness, + "y": x / magnitude * thickness + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshCalculateNormalizeBezierService.ts b/packages/webgl/src/Mesh/service/MeshCalculateNormalizeBezierService.ts new file mode 100644 index 00000000..6ca58672 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshCalculateNormalizeBezierService.ts @@ -0,0 +1,18 @@ +import type { IPoint } from "../../interface/IPoint"; + +/** + * @description ベクトルの正規化を行う + * Normalize the vector + * + * @param {IPoint} point + * @return {IPoint} + * @method + * @protected + */ +export const execute = (point: IPoint): IPoint => +{ + const length = Math.sqrt(point.x * point.x + point.y * point.y); + return length === 0 + ? { "x": 0, "y": 0 } + : { "x": point.x / length, "y": point.y / length }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshFillGenerateService.test.ts b/packages/webgl/src/Mesh/service/MeshFillGenerateService.test.ts new file mode 100644 index 00000000..354e63a9 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshFillGenerateService.test.ts @@ -0,0 +1,155 @@ +import { execute } from "./MeshFillGenerateService"; +import { $setViewportSize } from "../../WebGLUtil.ts"; +import { describe, expect, it, vi } from "vitest"; + +describe("MeshFillGenerateService.js method test", () => +{ + it("test case", async () => + { + + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + "$context": { + "$fillStyle": new Float32Array([0, 0, 0, 1]), + "$matrix": new Float32Array([1, 0, 0, 1, 0, 0, 0, 0, 1]), + } + } + }); + + $setViewportSize(10, 10); + const vertices = [ + -0.75, 0.8999999761581421, false, + 0.15000000596046448, 1.149999976158142, false, + 0.949999988079071, 0.699999988079071, false, + 1.25, 0.25, true, + 1.149999976158142, -0.30000001192092896, false, + 0.8999999761581421, -1.25, true, + -1, -1.149999976158142, false, + -1.149999976158142, -1.100000023841858, true, + -1.2000000476837158, 0, false, + -1.25, 0.550000011920929, true, + -0.75, 0.8999999761581421, false + ]; + + const buffer = new Float32Array((vertices.length / 3 - 2) * 51); + const index = execute( + vertices, buffer, 0, + 1, 0, 0, 1, 0, 0, + 0, 0, 0, 1 + ); + expect(index).toBe(27); + expect(buffer.length).toBe(459); + + expect(buffer[0]).toBe(-0.75); + expect(buffer[1]).toBe(0.8999999761581421); + expect(buffer[2]).toBe(0.5); + expect(buffer[3]).toBe(0.5); + expect(buffer[4]).toBe(0); + expect(buffer[5]).toBe(0); + expect(buffer[6]).toBe(0); + expect(buffer[7]).toBe(1); + expect(buffer[8]).toBe(1); + expect(buffer[9]).toBe(0); + expect(buffer[10]).toBe(0); + expect(buffer[11]).toBe(0); + expect(buffer[12]).toBe(1); + expect(buffer[13]).toBe(0); + expect(buffer[14]).toBe(0); + expect(buffer[15]).toBe(0); + expect(buffer[16]).toBe(0); + expect(buffer[17]).toBe(0.15000000596046448); + expect(buffer[18]).toBe(1.149999976158142); + expect(buffer[19]).toBe(0.5); + expect(buffer[20]).toBe(0.5); + expect(buffer[21]).toBe(0); + expect(buffer[22]).toBe(0); + expect(buffer[23]).toBe(0); + expect(buffer[24]).toBe(1); + expect(buffer[25]).toBe(1); + expect(buffer[26]).toBe(0); + expect(buffer[27]).toBe(0); + expect(buffer[28]).toBe(0); + expect(buffer[29]).toBe(1); + expect(buffer[30]).toBe(0); + expect(buffer[31]).toBe(0); + expect(buffer[32]).toBe(0); + expect(buffer[33]).toBe(0); + expect(buffer[34]).toBe(0.949999988079071); + expect(buffer[35]).toBe(0.699999988079071); + expect(buffer[36]).toBe(0.5); + expect(buffer[37]).toBe(0.5); + expect(buffer[38]).toBe(0); + expect(buffer[39]).toBe(0); + expect(buffer[40]).toBe(0); + expect(buffer[41]).toBe(1); + expect(buffer[42]).toBe(1); + expect(buffer[43]).toBe(0); + expect(buffer[44]).toBe(0); + expect(buffer[45]).toBe(0); + expect(buffer[46]).toBe(1); + expect(buffer[47]).toBe(0); + expect(buffer[48]).toBe(0); + expect(buffer[49]).toBe(0); + expect(buffer[50]).toBe(0); + expect(buffer[51]).toBe(-0.75); + expect(buffer[52]).toBe(0.8999999761581421); + expect(buffer[53]).toBe(0.5); + expect(buffer[54]).toBe(0.5); + expect(buffer[55]).toBe(0); + expect(buffer[56]).toBe(0); + expect(buffer[57]).toBe(0); + expect(buffer[58]).toBe(1); + expect(buffer[59]).toBe(1); + expect(buffer[60]).toBe(0); + expect(buffer[61]).toBe(0); + expect(buffer[62]).toBe(0); + expect(buffer[63]).toBe(1); + expect(buffer[64]).toBe(0); + expect(buffer[65]).toBe(0); + expect(buffer[66]).toBe(0); + expect(buffer[67]).toBe(0); + expect(buffer[68]).toBe(0.949999988079071); + expect(buffer[69]).toBe(0.699999988079071); + expect(buffer[70]).toBe(0.5); + expect(buffer[71]).toBe(0.5); + expect(buffer[72]).toBe(0); + expect(buffer[73]).toBe(0); + expect(buffer[74]).toBe(0); + expect(buffer[75]).toBe(1); + expect(buffer[76]).toBe(1); + expect(buffer[77]).toBe(0); + expect(buffer[78]).toBe(0); + expect(buffer[79]).toBe(0); + expect(buffer[80]).toBe(1); + expect(buffer[81]).toBe(0); + expect(buffer[82]).toBe(0); + expect(buffer[83]).toBe(0); + expect(buffer[84]).toBe(0); + expect(buffer[85]).toBe(1.149999976158142); + expect(buffer[86]).toBe(-0.30000001192092896); + expect(buffer[87]).toBe(0.5); + expect(buffer[88]).toBe(0.5); + expect(buffer[89]).toBe(0); + expect(buffer[90]).toBe(0); + expect(buffer[91]).toBe(0); + expect(buffer[92]).toBe(1); + expect(buffer[93]).toBe(1); + expect(buffer[94]).toBe(0); + expect(buffer[95]).toBe(0); + expect(buffer[96]).toBe(0); + expect(buffer[97]).toBe(1); + expect(buffer[98]).toBe(0); + expect(buffer[99]).toBe(0); + expect(buffer[100]).toBe(0); + expect(buffer[101]).toBe(0); + expect(buffer[102]).toBe(0.949999988079071); + expect(buffer[103]).toBe(0.699999988079071); + expect(buffer[104]).toBe(0); + expect(buffer[105]).toBe(0); + expect(buffer[106]).toBe(0); + expect(buffer[107]).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshFillGenerateService.ts b/packages/webgl/src/Mesh/service/MeshFillGenerateService.ts new file mode 100644 index 00000000..b757231d --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshFillGenerateService.ts @@ -0,0 +1,244 @@ +import type { IPath } from "../../interface/IPath"; + +/** + * @description 塗りのメッシュを生成する + * Generate a fill mesh + * + * @param {IPath} vertex + * @param {Float32Array} buffer + * @param {number} index + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {number} tx + * @param {number} ty + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + * @return {number} + * @method + * @protected + */ +export const execute = ( + vertex: IPath, + buffer: Float32Array, + index: number, + a: number, + b: number, + c: number, + d: number, + tx: number, + ty: number, + red: number, + green: number, + blue: number, + alpha: number +): number => { + + const length = vertex.length - 5; + for (let idx = 3; idx < length; idx += 3) { + + let position = index * 17; + if (vertex[idx + 2]) { + + // 座標A + buffer[position++] = vertex[idx - 3] as number; + buffer[position++] = vertex[idx - 2] as number; + buffer[position++] = 0; + buffer[position++] = 0; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標B + buffer[position++] = vertex[idx] as number; + buffer[position++] = vertex[idx + 1] as number; + buffer[position++] = 0.5; + buffer[position++] = 0; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標C + buffer[position++] = vertex[idx + 3] as number; + buffer[position++] = vertex[idx + 4] as number; + buffer[position++] = 1; + buffer[position++] = 1; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + } else if (vertex[idx + 5]) { + + // 座標A + buffer[position++] = vertex[0] as number; + buffer[position++] = vertex[1] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標B + buffer[position++] = vertex[idx] as number; + buffer[position++] = vertex[idx + 1] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標C + buffer[position++] = vertex[idx + 6] as number; + buffer[position++] = vertex[idx + 7] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + } else { + + // 座標A + buffer[position++] = vertex[0] as number; + buffer[position++] = vertex[1] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標B + buffer[position++] = vertex[idx] as number; + buffer[position++] = vertex[idx + 1] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + + // 座標C + buffer[position++] = vertex[idx + 3] as number; + buffer[position++] = vertex[idx + 4] as number; + buffer[position++] = 0.5; + buffer[position++] = 0.5; + + buffer[position++] = red; + buffer[position++] = green; + buffer[position++] = blue; + buffer[position++] = alpha; + + buffer[position++] = a; + buffer[position++] = b; + buffer[position++] = 0; + buffer[position++] = c; + buffer[position++] = d; + buffer[position++] = 0; + buffer[position++] = tx; + buffer[position++] = ty; + buffer[position++] = 0; + } + + index += 3; + } + + return index; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshFindOverlappingPathsService.ts b/packages/webgl/src/Mesh/service/MeshFindOverlappingPathsService.ts new file mode 100644 index 00000000..79f57c0e --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshFindOverlappingPathsService.ts @@ -0,0 +1,44 @@ +import type { IPath } from "../../interface/IPath"; + +/** + * @description メッシュのパスの中で指定座標が含まれる線を探す + * Find lines in the path of the mesh that contain the specified coordinates + * + * @param {number} x + * @param {number} y + * @param {IPath} paths + * @return {number[]} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + r: number, + paths: IPath +): number[] => { + + const points: number[] = []; + for (let idx = 0; idx < paths.length; idx += 3) { + + // カーブのコントロール座標なら終了 + if (paths[idx + 2] as boolean) { + continue; + } + + const dx = paths[idx ] as number; + const dy = paths[idx + 1] as number; + + const distance = Math.sqrt( + Math.pow(dx - x, 2) + Math.pow(dy - y, 2) + ); + + if (distance !== r) { + continue; + } + + points.push(dx, dy); + } + + return points; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshGenerateCalculateRoundCapService.ts b/packages/webgl/src/Mesh/service/MeshGenerateCalculateRoundCapService.ts new file mode 100644 index 00000000..774f0f54 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshGenerateCalculateRoundCapService.ts @@ -0,0 +1,63 @@ +import type { IPath } from "../../interface/IPath"; + +/** + * @description 始点、終点から終点の反対側の線の丸みを算出 + * Calculate the roundness of the line on the opposite side of the end point from the start point + * + * @param {IPath} vertices + * @param {number} r + * @param {IPath[]} rectangles + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertices: IPath, + r: number, + rectangles: IPath[] +): void => { + + for (let idx = 0; idx < 2; ++idx) { + + let sx = 0; + let sy = 0; + let ex = 0; + let ey = 0; + + if (idx === 0) { + sx = vertices[0] as number; + sy = vertices[1] as number; + ex = vertices[3] as number; + ey = vertices[4] as number; + } else { + sx = vertices[vertices.length - 3] as number; + sy = vertices[vertices.length - 2] as number; + ex = vertices[vertices.length - 6] as number; + ey = vertices[vertices.length - 5] as number; + } + + const dx = ex - sx; + const dy = ey - sy; + + const angleToEnd = Math.atan2(dy, dx); + const startAngle = angleToEnd + Math.PI / 2;// 反対側 + const endAngle = angleToEnd - Math.PI / 2; + + const segments = 16; + const step = (endAngle - startAngle) / segments; + + const points: IPath = []; + for (let idx = 0; idx <= segments; idx++) { + const t = endAngle + step * idx; + const x = sx + r * Math.cos(t); + const y = sy + r * Math.sin(t); + points.push(x, y, false); + } + + if (idx === 0) { + rectangles.unshift(points); + } else { + rectangles.push(points); + } + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshGenerateCalculateSquareCapService.ts b/packages/webgl/src/Mesh/service/MeshGenerateCalculateSquareCapService.ts new file mode 100644 index 00000000..64fcb4a0 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshGenerateCalculateSquareCapService.ts @@ -0,0 +1,78 @@ +import type { IPath } from "../../interface/IPath"; + +/** + * @description 始点、終点から終点の反対側の矩形のパスを算出 + * Calculate the path of the rectangle on the opposite side of the end point from the start point + * + * @param {IPath} vertices + * @param {number} r + * @param {IPath[]} rectangles + * @return {void} + * @method + * @protected + */ +export const execute = ( + vertices: IPath, + r: number, + rectangles: IPath[] +): void => { + + for (let idx = 0; idx < 2; ++idx) { + + let sx = 0; + let sy = 0; + let ex = 0; + let ey = 0; + + if (idx === 0) { + sx = vertices[0] as number; + sy = vertices[1] as number; + ex = vertices[3] as number; + ey = vertices[4] as number; + } else { + sx = vertices[vertices.length - 3] as number; + sy = vertices[vertices.length - 2] as number; + ex = vertices[vertices.length - 6] as number; + ey = vertices[vertices.length - 5] as number; + } + + // 「アンカー -> コントロール」のベクトル(dx, dy) + const dx = -(ex - sx); + const dy = -(ey - sy); + + // ベクトル(dx, dy) の長さ + const length = Math.sqrt(dx * dx + dy * dy); + if (length === 0) { + // アンカーとコントロールが同じ場合はベクトルが作れない + continue ; + } + + // 正規化 (長さ1にする) + const ux = dx / length; + const uy = dy / length; + + // 矩形キャップの中心点 + // → アンカー(ax, ay) から 逆方向ベクトルに r 分進めた場所 + const cxn = sx + r * ux; + const cyn = sy + r * uy; + + // 垂直ベクトル + const vx = -uy; + const vy = ux; + + const points: IPath = [ + cxn + r * ux + r * vx, cyn + r * uy + r * vy, false, // 1 + cxn + r * ux - r * vx, cyn + r * uy - r * vy, false, // 2 + cxn - r * ux - r * vx, cyn - r * uy - r * vy, false, // 3 + cxn + r * ux + r * vx, cyn + r * uy + r * vy, false, // 1 + cxn - r * ux - r * vx, cyn - r * uy - r * vy, false, // 3 + cxn - r * ux + r * vx, cyn - r * uy + r * vy, false // 4 + ]; + + if (idx === 0) { + rectangles.unshift(points); + } else { + rectangles.push(points); + } + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierPointService.ts b/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierPointService.ts new file mode 100644 index 00000000..ad859063 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierPointService.ts @@ -0,0 +1,25 @@ +import type { IPoint } from "../../interface/IPoint"; + +/** + * @description 二次ベジエ曲線上の座標を計算する + * Calculate the coordinates on the quadratic Bezier curve + * + * @param {number} t + * @param {IPoint} start_point + * @param {IPoint} control_point + * @param {IPoint} end_point + * @return {IPoint} + * @method + * @protected + */ +export const execute = ( + t: number, + start_point: IPoint, + control_point: IPoint, + end_point: IPoint +): IPoint => { + return { + "x": (1 - t) ** 2 * start_point.x + 2 * (1 - t) * t * control_point.x + t ** 2 * end_point.x, + "y": (1 - t) ** 2 * start_point.y + 2 * (1 - t) * t * control_point.y + t ** 2 * end_point.y + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierTangentService.ts b/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierTangentService.ts new file mode 100644 index 00000000..e45d9dc6 --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshGetQuadraticBezierTangentService.ts @@ -0,0 +1,25 @@ +import type { IPoint } from "../../interface/IPoint"; + +/** + * @description 二次ベジエ曲線上の接線ベクトルを計算する + * Calculate the tangent vector on the quadratic Bezier curve + * + * @param {number} t + * @param {IPoint} start_point + * @param {IPoint} control_point + * @param {IPoint} end_point + * @return {IPoint} + * @method + * @protected + */ +export const execute = ( + t: number, + start_point: IPoint, + control_point: IPoint, + end_point: IPoint +): IPoint => { + return { + "x": 2 * (1 - t) * (control_point.x - start_point.x) + 2 * t * (end_point.x - control_point.x), + "y": 2 * (1 - t) * (control_point.y - start_point.y) + 2 * t * (end_point.y - control_point.y) + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshIsPointInsideRectangleService.ts b/packages/webgl/src/Mesh/service/MeshIsPointInsideRectangleService.ts new file mode 100644 index 00000000..b238fdfd --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshIsPointInsideRectangleService.ts @@ -0,0 +1,59 @@ +import type { IPath } from "../../interface/IPath"; + +const canvas = new OffscreenCanvas(1, 1); +const $context = canvas.getContext("2d") as OffscreenCanvasRenderingContext2D; + +/** + * @description 矩形内に含まれてない座標を返却 + * Returns coordinates that are not included in the rectangle + * + * @param {number[]} points + * @param {IPath} rectangle + * @return {number[] | null} + * @method + * @protected + */ +export const execute = ( + points: number[], + rectangle: IPath +): number[] | null => { + + $context.beginPath(); + $context.moveTo( + rectangle[0] as number, + rectangle[1] as number + ); + + for (let idx = 3; idx < rectangle.length; idx += 3) { + if (rectangle[idx + 2] as boolean) { + $context.quadraticCurveTo( + rectangle[idx ] as number, + rectangle[idx + 1] as number, + rectangle[idx + 3] as number, + rectangle[idx + 4] as number + ); + idx += 3; + } else { + $context.lineTo( + rectangle[idx ] as number, + rectangle[idx + 1] as number + ); + } + } + + $context.closePath(); + + for (let idx = 0; idx < points.length; idx += 2) { + + const x = points[idx]; + const y = points[idx + 1]; + + if ($context.isPointInPath(x, y)) { + continue; + } + + return [x, y]; + } + + return null; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/service/MeshLerpService.ts b/packages/webgl/src/Mesh/service/MeshLerpService.ts new file mode 100644 index 00000000..eb5164aa --- /dev/null +++ b/packages/webgl/src/Mesh/service/MeshLerpService.ts @@ -0,0 +1,23 @@ +import type { IPoint } from "../../interface/IPoint"; + +/** + * @description 線形補間 + * Linear interpolation + * + * @param {IPoint} pointA + * @param {IPoint} pointB + * @param {number} t + * @return {IPoint} + * @method + * @protected + */ +export const execute = ( + pointA: IPoint, + pointB: IPoint, + t: number +): IPoint => { + return { + "x": pointA.x + (pointB.x - pointA.x) * t, + "y": pointA.y + (pointB.y - pointA.y) * t + }; +}; diff --git a/packages/webgl/src/Mesh/usecase/MeshApproximateOffsetQuadraticUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshApproximateOffsetQuadraticUseCase.ts new file mode 100644 index 00000000..b463a597 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshApproximateOffsetQuadraticUseCase.ts @@ -0,0 +1,48 @@ +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshGetQuadraticBezierPointService } from "../service/MeshGetQuadraticBezierPointService"; +import { execute as meshGetQuadraticBezierTangentService } from "../service/MeshGetQuadraticBezierTangentService"; +import { execute as meshCalculateNormalizeBezierService } from "../service/MeshCalculateNormalizeBezierService"; + +/** + * @description 2次ベジエのオフセットを計算する + * Calculate the offset of the quadratic Bezier + * + * @param {IPoint} s0 + * @param {IPoint} s1 + * @param {IPoint} s2 + * @param {number} offset + * @return {IPoint[]} + * @method + * @protected + */ +export const execute = ( + s0: IPoint, + s1: IPoint, + s2: IPoint, + offset: number +): IPoint[] => { + + const tValues = [0, 0.5, 1]; + const newPoints: IPoint[] = []; + + for (let idx = 0; idx < tValues.length; ++idx) { + + const t = tValues[idx]; + const pos = meshGetQuadraticBezierPointService(t, s0, s1, s2); + const tan = meshGetQuadraticBezierTangentService(t, s0, s1, s2); + + // 左方向の法線 = (-dy, dx) + const n = meshCalculateNormalizeBezierService({ + "x": -tan.y, + "y": tan.x + }); + + newPoints.push({ + "x": pos.x + n.x * offset, + "y": pos.y + n.y * offset + }); + } + + // [Q0, Q1, Q2] + return newPoints; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshCalculateCurveRectangleUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshCalculateCurveRectangleUseCase.ts new file mode 100644 index 00000000..094621f4 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshCalculateCurveRectangleUseCase.ts @@ -0,0 +1,80 @@ +import type { IPath } from "../../interface/IPath"; +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshSplitBezierMultipleTimesUseCase } from "./MeshSplitBezierMultipleTimesUseCase"; +import { execute as meshApproximateOffsetQuadraticUseCase } from "./MeshApproximateOffsetQuadraticUseCase"; + +/** + * @description カーブの矩形を計算する + * Calculate curve rectangle + * + * @param {IPoint} start_point + * @param {IPoint} control_point + * @param {IPoint} end_point + * @param {number} thickness + * @return {IPath} + * @memthod + * @protected + */ +export const execute = ( + start_point: IPoint, + control_point: IPoint, + end_point: IPoint, + thickness: number +): IPath => { + + const segments = meshSplitBezierMultipleTimesUseCase( + start_point, + control_point, + end_point, + 5 + ); + + // "左右" にオフセットした2次ベジエを作る + // { leftCurves: [...], rightCurves: [...] } の形で保持する + // leftCurves[i] = [Q0, Q1, Q2] (2次ベジエの3点) + // rightCurves[i] = [Q0, Q1, Q2] + const leftCurves: Array = []; + const rightCurves: Array = []; + for (let idx = 0; idx < segments.length; ++idx) { + + const [s0, s1, s2] = segments[idx]; + + // +offset と -offset を作成 + leftCurves.push(meshApproximateOffsetQuadraticUseCase(s0, s1, s2, +thickness)); + rightCurves.push(meshApproximateOffsetQuadraticUseCase(s0, s1, s2, -thickness)); + } + + // 左サイドの最初のサブカーブ始点 + const leftStart = leftCurves[0][0]; + const paths: IPath = [leftStart.x, leftStart.y, false]; + + // 左サイドの最終サブカーブ終了点 → 右サイドの最終サブカーブ終了点へ移動 + for (let idx = 0; idx < leftCurves.length; ++idx) { + const curves = leftCurves[idx]; + paths.push( + curves[1].x, curves[1].y, true, + curves[2].x, curves[2].y, false + ); + } + + const reversedRight = [...rightCurves].reverse(); + for (let idx = 0; idx < reversedRight.length; ++idx) { + const [q0, q1, q2] = reversedRight[idx]; + reversedRight[idx] = [q2, q1, q0]; // [Q2, Q1, Q0] + } + + // 右サイドの最初のサブカーブ始点 + const rightEnd = reversedRight[0][0]; + paths.push(rightEnd.x, rightEnd.y, false); + + // 右サイドの最終サブカーブ終了点 → 左サイドの最終サブカーブ終了点へ移動 + for (let idx = 0; idx < reversedRight.length; ++idx) { + const curves = reversedRight[idx]; + paths.push( + curves[1].x, curves[1].y, true, + curves[2].x, curves[2].y, false + ); + } + + return paths; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshCalculateLineRectangleUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshCalculateLineRectangleUseCase.ts new file mode 100644 index 00000000..435357be --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshCalculateLineRectangleUseCase.ts @@ -0,0 +1,58 @@ +import type { IPath } from "../../interface/IPath"; +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshCalculateNormalVectorService } from "../service/MeshCalculateNormalVectorService"; + +/** + * @description 直線の矩形を計算 + * Calculate the rectangle of a line + * + * @param {IPoint} start_point + * @param {IPoint} end_point + * @param {number} thickness + * @return {IPath} + * @method + * @protected + */ +export const execute = ( + start_point: IPoint, + end_point: IPoint, + thickness: number +): IPath => { + + const vector: IPoint = { + "x": end_point.x - start_point.x, + "y": end_point.y - start_point.y + }; + + const normal = meshCalculateNormalVectorService( + vector.x, vector.y, thickness + ); + + const shiftedUpStart: IPoint = { + "x": start_point.x + normal.x, + "y": start_point.y + normal.y + }; + + const shiftedUpEnd: IPoint = { + "x": end_point.x + normal.x, + "y": end_point.y + normal.y + }; + + const shiftedDownStart: IPoint = { + "x": start_point.x - normal.x, + "y": start_point.y - normal.y + }; + + const shiftedDownEnd: IPoint = { + "x": end_point.x - normal.x, + "y": end_point.y - normal.y + }; + + return [ + shiftedUpStart.x, shiftedUpStart.y, false, + shiftedUpEnd.x, shiftedUpEnd.y, false, + shiftedDownEnd.x, shiftedDownEnd.y, false, + shiftedDownStart.x, shiftedDownStart.y, false, + shiftedUpStart.x, shiftedUpStart.y, false + ]; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.test.ts b/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.test.ts new file mode 100644 index 00000000..6430fb56 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.test.ts @@ -0,0 +1,38 @@ +import { execute } from "./MeshFillGenerateUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("MeshFillGenerateUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + "$context": { + "$fillStyle": new Float32Array([0, 0, 0, 1]), + "$matrix": new Float32Array([1, 0, 0, 1, 0, 0, 0, 0, 1]), + } + } + }); + + const vertices = [ + -0.75, 0.8999999761581421, false, + 0.15000000596046448, 1.149999976158142, false, + 0.949999988079071, 0.699999988079071, false, + 1.25, 0.25, true, + 1.149999976158142, -0.30000001192092896, false, + 0.8999999761581421, -1.25, true, + -0.10000000149011612, -1.149999976158142, false, + -1.149999976158142, -1.100000023841858, true, + -1.2000000476837158, 0, false, + -1.25, 0.550000011920929, true, + -0.75, 0.8999999761581421, false + ]; + + const vertexArrayObject = execute([vertices]); + expect(vertexArrayObject.indexCount).toBe(27); + expect(vertexArrayObject.buffer.length).toBe(459); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.ts new file mode 100644 index 00000000..74c80ce5 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshFillGenerateUseCase.ts @@ -0,0 +1,64 @@ +import type { IPath } from "../../interface/IPath"; +import type { IFillMesh } from "../../interface/IFillMesh"; +import { execute as meshFillGenerateService } from "../service/MeshFillGenerateService"; +import { + $context, + $getViewportWidth, + $getViewportHeight +} from "../../WebGLUtil"; + +/** + * @description 塗りのメッシュを生成する + * Generate a fill mesh + * + * @param {IPath[]} vertices + * @return {IFillMesh} + * @method + * @protected + */ +export const execute = ( + vertices: IPath[], + style: "fill" | "stroke" = "fill" +): IFillMesh => { + + const colorStyle = style === "fill" + ? $context.$fillStyle + : $context.$strokeStyle; + + const red = colorStyle[0]; + const green = colorStyle[1]; + const blue = colorStyle[2]; + const alpha = colorStyle[3]; + + const matrix = $context.$matrix; + const width = $getViewportWidth(); + const height = $getViewportHeight(); + + const a = matrix[0] / width; + const c = matrix[3] / width; + const tx = matrix[6] / width; + const b = matrix[1] / height; + const d = matrix[4] / height; + const ty = matrix[7] / height; + + let length = 0; + for (let idx = 0; idx < vertices.length; ++idx) { + length += (vertices[idx].length / 3 - 2) * 51; + } + + const buffer = new Float32Array(length); + + let currentIndex = 0; + for (let idx = 0; idx < vertices.length; ++idx) { + currentIndex = meshFillGenerateService( + vertices[idx], buffer, currentIndex, + a, b, c, d, tx, ty, + red, green, blue, alpha + ); + } + + return { + "buffer": buffer, + "indexCount": currentIndex + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateBevelJoinUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateBevelJoinUseCase.ts new file mode 100644 index 00000000..8c0ca083 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateBevelJoinUseCase.ts @@ -0,0 +1,60 @@ +import type { IPath } from "../../interface/IPath"; +import { execute as meshFindOverlappingPathsUseCase } from "../service/MeshFindOverlappingPathsService"; +import { execute as meshIsPointInsideRectangleService } from "../service/MeshIsPointInsideRectangleService"; + +/** + * @description 線と線の繋ぎ目を繋ぐ + * Connect the connection between lines + * + * @param {number} x + * @param {number} y + * @param {number} r + * @param {IPath[]} rectangles + * @param {boolean} [is_last=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + r: number, + rectangles: IPath[], + is_last: boolean = false +): void => { + + const indexA = is_last ? 0 : rectangles.length - 1; + const indexB = is_last ? rectangles.length - 1 : rectangles.length - 2; + const pathsA = meshFindOverlappingPathsUseCase(x, y, r, rectangles[indexA]); + const pathsB = meshFindOverlappingPathsUseCase(x, y, r, rectangles[indexB]); + + // パスが並行であれば終了 + if (pathsA[0] === pathsB[0] && pathsA[1] === pathsB[1] + || pathsA[0] === pathsB[2] && pathsA[1] === pathsB[3] + ) { + return ; + } + + const pointA = meshIsPointInsideRectangleService( + pathsA, rectangles[indexB] + ); + + if (!pointA) { + return ; + } + + const pointB = meshIsPointInsideRectangleService( + pathsB, rectangles[indexA] + ); + + if (!pointB) { + return ; + } + + rectangles.splice(-1, 0, [ + x, y, false, + pointA[0], pointA[1], false, + pointB[0], pointB[1], false, + x, y, false + ]); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateMiterJoinUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateMiterJoinUseCase.ts new file mode 100644 index 00000000..812b707b --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateMiterJoinUseCase.ts @@ -0,0 +1,98 @@ +import type { IPath } from "../../interface/IPath"; +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshFindOverlappingPathsUseCase } from "../service/MeshFindOverlappingPathsService"; +import { execute as meshIsPointInsideRectangleService } from "../service/MeshIsPointInsideRectangleService"; + +/** + * @description 線と線の繋ぎ目をmiterで繋ぐ + * Connect the connection between lines with miter + * + * @param {IPoint} start_point + * @param {IPoint} end_point + * @param {IPoint} prev_point + * @param {number} r + * @param {IPath[]} rectangles + * @param {boolean} [is_last=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + start_point: IPoint, + end_point: IPoint, + prev_point: IPoint, + r: number, + rectangles: IPath[], + is_last: boolean = false +): void => { + + const indexA = is_last ? 0 : rectangles.length - 1; + const indexB = is_last ? rectangles.length - 1 : rectangles.length - 2; + const pathsA = meshFindOverlappingPathsUseCase(start_point.x, start_point.y, r, rectangles[indexA]); + const pathsB = meshFindOverlappingPathsUseCase(start_point.x, start_point.y, r, rectangles[indexB]); + + // パスが並行であれば終了 + if (pathsA[0] === pathsB[0] && pathsA[1] === pathsB[1] + || pathsA[0] === pathsB[2] && pathsA[1] === pathsB[3] + ) { + return ; + } + + const pointA = meshIsPointInsideRectangleService( + pathsA, rectangles[indexB] + ); + + if (!pointA) { + return ; + } + + const pointB = meshIsPointInsideRectangleService( + pathsB, rectangles[indexA] + ); + + if (!pointB) { + return ; + } + + const aVx = end_point.x - start_point.x; + const aVy = end_point.y - start_point.y; + const lengthA = Math.hypot(aVx, aVy); + const normalizeA = { + "x": aVx / lengthA, + "y": aVy / lengthA + }; + + const bVx = prev_point.x - start_point.x; + const bVy = prev_point.y - start_point.y; + const lengthB = Math.hypot(bVx, bVy); + const normalizeB = { + "x": bVx / lengthB, + "y": bVy / lengthB + }; + + const d1x = normalizeA.x, d1y = normalizeA.y; + const d2x = normalizeB.x, d2y = normalizeB.y; + + const denom = d1x * d2y - d1y * d2x; + if (denom === 0) { + rectangles.splice(-1, 0, [ + start_point.x, start_point.y, false, + pointA[0], pointA[1], false, + pointB[0], pointB[1], false + ]); + } + + const t = ((pointB[0] - pointA[0]) * d2y - (pointB[1] - pointA[1]) * d2x) / denom; + + const ix = pointA[0] + t * d1x; + const iy = pointA[1] + t * d1y; + + rectangles.splice(-1, 0, [ + start_point.x, start_point.y, false, + pointA[0], pointA[1], false, + ix, iy, false, + start_point.x, start_point.y, false, + pointB[0], pointB[1], false, + ix, iy, false + ]); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateRoundJoinUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateRoundJoinUseCase.ts new file mode 100644 index 00000000..8724f439 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshGenerateCalculateRoundJoinUseCase.ts @@ -0,0 +1,72 @@ +import type { IPath } from "../../interface/IPath"; +import { execute as meshFindOverlappingPathsService } from "../service/MeshFindOverlappingPathsService"; +import { execute as meshIsPointInsideRectangleService } from "../service/MeshIsPointInsideRectangleService"; + +/** + * @description 線と線の繋ぎ目を丸くする + * Make the connection between lines round + * + * @param {number} x + * @param {number} y + * @param {number} r + * @param {IPath[]} rectangles + * @param {boolean} [is_last=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + r: number, + rectangles: IPath[], + is_last: boolean = false +): void => { + + const indexA = is_last ? 0 : rectangles.length - 1; + const indexB = is_last ? rectangles.length - 1 : rectangles.length - 2; + const pathsA = meshFindOverlappingPathsService(x, y, r, rectangles[indexA]); + const pathsB = meshFindOverlappingPathsService(x, y, r, rectangles[indexB]); + + const pointA = meshIsPointInsideRectangleService( + pathsA, rectangles[indexB] + ); + + // 接続点が矩形の内部にある場合は終了 + if (!pointA) { + return ; + } + + const pointB = meshIsPointInsideRectangleService( + pathsB, rectangles[indexA] + ); + + // 接続点が矩形の内部にある場合は終了 + if (!pointB) { + return ; + } + + const angleA = Math.atan2(pointA[1] - y, pointA[0] - x); + const angleB = Math.atan2(pointB[1] - y, pointB[0] - x); + + // 角度差を正規化して180度以下にする + let angleDiff = angleB - angleA; + if (angleDiff > Math.PI) { + angleDiff -= 2 * Math.PI; + } else if (angleDiff < -Math.PI) { + angleDiff += 2 * Math.PI; + } + + const segment = 8; + const step = angleDiff / segment; + + const points: IPath = [x, y, false]; + for (let idx = 0; idx <= segment; idx++) { + const angle = angleA + idx * step; + const dx = x + r * Math.cos(angle); + const dy = y + r * Math.sin(angle); + points.push(dx, dy, false); + } + + rectangles.splice(-1, 0, points); +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshGenerateStrokeOutlineUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshGenerateStrokeOutlineUseCase.ts new file mode 100644 index 00000000..d0d38e07 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshGenerateStrokeOutlineUseCase.ts @@ -0,0 +1,159 @@ +import type { IPath } from "../../interface/IPath"; +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshCalculateLineRectangleUseCase } from "./MeshCalculateLineRectangleUseCase"; +import { execute as meshCalculateCurveRectangleUseCase } from "./MeshCalculateCurveRectangleUseCase"; +import { execute as meshGenerateCalculateRoundJoinUseCase } from "./MeshGenerateCalculateRoundJoinUseCase"; +import { execute as meshGenerateCalculateBevelJoinUseCase } from "./MeshGenerateCalculateBevelJoinUseCase"; +import { execute as meshGenerateCalculateRoundCapService } from "../service/MeshGenerateCalculateRoundCapService"; +import { execute as meshGenerateCalculateSquareCapService } from "../service/MeshGenerateCalculateSquareCapService"; +import { execute as meshGenerateCalculateMiterJoinUseCase } from "../usecase/MeshGenerateCalculateMiterJoinUseCase"; +import { $context } from "../../WebGLUtil"; + +/** + * @description 線の外周を算出して塗りのフォーマットで返却 + * Calculate the outer circumference of the line and return it in the format of the fill + * + * @param {IPath} vertices + * @param {number} thickness + * @return {IPath[]} + * @method + * @protected + */ +export const execute = (vertices: IPath, thickness: number): IPath[] => +{ + const startPoint: IPoint = { + "x": vertices[0] as number, + "y": vertices[1] as number + }; + + const controlPoint: IPoint = { + "x": 0, + "y": 0 + }; + + const endPoint: IPoint = { + "x": 0, + "y": 0 + }; + + const prevPoint: IPoint = { + "x": 0, + "y": 0 + }; + + const rectangles: IPath[] = []; + for (let idx = 3; idx < vertices.length; idx += 3) { + + const x = vertices[idx ] as number; + const y = vertices[idx + 1] as number; + + if (vertices[idx + 2] as boolean) { + controlPoint.x = x; + controlPoint.y = y; + continue; + } + + endPoint.x = x; + endPoint.y = y; + if (vertices[idx - 1] as boolean) { + rectangles.push( + meshCalculateCurveRectangleUseCase(startPoint, controlPoint, endPoint, thickness) + ); + } else { + rectangles.push( + meshCalculateLineRectangleUseCase(startPoint, endPoint, thickness) + ); + } + + if (rectangles.length > 1) { + switch ($context.joints) { + + case 0: // bevel + meshGenerateCalculateBevelJoinUseCase( + startPoint.x, startPoint.y, thickness, rectangles + ); + break; + + case 1: // miter + prevPoint.x = vertices[idx - 6] as number; + prevPoint.y = vertices[idx - 5] as number; + meshGenerateCalculateMiterJoinUseCase( + startPoint, endPoint, prevPoint, + thickness, rectangles + ); + break; + + case 2: // round + meshGenerateCalculateRoundJoinUseCase( + startPoint.x, startPoint.y, thickness, rectangles + ); + break; + + } + } + + startPoint.x = endPoint.x; + startPoint.y = endPoint.y; + } + + if (vertices[0] === vertices[vertices.length - 3] + && vertices[1] === vertices[vertices.length - 2] + ) { + + // 始点と終点が繋がっている時はjointsの設定を適用 + switch ($context.joints) { + + case 0: // bevel + meshGenerateCalculateBevelJoinUseCase( + startPoint.x, startPoint.y, thickness, rectangles, true + ); + break; + + case 1: // miter + startPoint.x = vertices[0] as number; + startPoint.y = vertices[1] as number; + endPoint.x = vertices[3] as number; + endPoint.y = vertices[4] as number; + prevPoint.x = vertices[vertices.length - 6] as number; + prevPoint.y = vertices[vertices.length - 5] as number; + meshGenerateCalculateMiterJoinUseCase( + startPoint, endPoint, prevPoint, + thickness, rectangles, true + ); + break; + + case 2: // round + meshGenerateCalculateRoundJoinUseCase( + startPoint.x, startPoint.y, thickness, rectangles, true + ); + break; + + default: + break; + + } + } else { + + // 始点と終点が繋がってない時はcapsの設定を適用 + switch ($context.caps) { + + case 1: // round + meshGenerateCalculateRoundCapService( + vertices, thickness, rectangles + ); + break; + + case 2: // square: + meshGenerateCalculateSquareCapService( + vertices, thickness, rectangles + ); + break; + + default: + break; + + } + } + + return rectangles; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshSplitBezierMultipleTimesUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshSplitBezierMultipleTimesUseCase.ts new file mode 100644 index 00000000..a0e11d23 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshSplitBezierMultipleTimesUseCase.ts @@ -0,0 +1,43 @@ +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshSplitQuadraticBezierUseCase } from "./MeshSplitQuadraticBezierUseCase"; + +/** + * @description ベジェ曲線を指定回数分割する + * Divides a Bezier curve a specified number of times + * + * @param {IPoint} start_point + * @param {IPoint} control_point + * @param {IPoint} end_point + * @param {number} [n = 4] + * @return {Array} + * @method + * @protected + */ +export const execute = ( + start_point: IPoint, + control_point: IPoint, + end_point: IPoint, + n: number = 4 +): Array => { + + // 初期リスト:1本だけ + let segments: Array = [[start_point, control_point, end_point]]; + + for (let idx = 0; idx < n; ++idx) { + const newSegments: Array = []; + for (let idx = 0; idx < segments.length; ++idx) { + const seg = segments[idx]; + if (!seg) { + continue ; + } + + // seg = [s0, s1, s2] + const splitted = meshSplitQuadraticBezierUseCase(seg[0], seg[1], seg[2], 0.5); + newSegments.push(splitted[0], splitted[1]); + } + segments = newSegments; + } + + // segmentsには 2^n 本の [x,y]が入る + return segments; // [[p0,p1,p2], [p0,p1,p2], ...] +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshSplitQuadraticBezierUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshSplitQuadraticBezierUseCase.ts new file mode 100644 index 00000000..f1919861 --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshSplitQuadraticBezierUseCase.ts @@ -0,0 +1,37 @@ +import type { IPoint } from "../../interface/IPoint"; +import { execute as meshLerpService } from "../service/MeshLerpService"; + +/** + * @description 二次ベジェ曲線を分割する + * Split a quadratic Bezier curve + * + * @param {IPoint} start_point + * @param {IPoint} control_point + * @param {IPoint} end_point + * @param {number} [t = 0.5] + * @return {Array} + * @method + * @protected + */ +export const execute = ( + start_point: IPoint, + control_point: IPoint, + end_point: IPoint, + t: number = 0.5 +): Array => { + + // 二次ベジエ曲線の分割 + // M0 = lerp(P0, P1, t) + // M1 = lerp(P1, P2, t) + // M01 = lerp(M0, M1, t) + const M0 = meshLerpService(start_point, control_point, t); + const M1 = meshLerpService(control_point, end_point, t); + const M01 = meshLerpService(M0, M1, t); + + // 左サブ (0...t): [P0, M0, M01] + // 右サブ (t...1): [M01, M1, P2] + return [ + [start_point, M0, M01], + [M01, M1, end_point] + ]; +}; \ No newline at end of file diff --git a/packages/webgl/src/Mesh/usecase/MeshStrokeGenerateUseCase.ts b/packages/webgl/src/Mesh/usecase/MeshStrokeGenerateUseCase.ts new file mode 100644 index 00000000..1ad6b7ee --- /dev/null +++ b/packages/webgl/src/Mesh/usecase/MeshStrokeGenerateUseCase.ts @@ -0,0 +1,27 @@ +import type { IFillMesh } from "../../interface/IFillMesh"; +import type { IPath } from "../../interface/IPath"; +import { execute as meshGenerateStrokeOutlineUseCase } from "./MeshGenerateStrokeOutlineUseCase"; +import { execute as meshFillGenerateUseCase } from "./MeshFillGenerateUseCase"; +import { $context } from "../../WebGLUtil"; + +/** + * @description ストロークのメッシュを生成 + * Generate a stroke mesh + * + * @param {IPath[]} vertices + * @return {IFillMesh} + * @method + * @protected + */ +export const execute = (vertices: IPath[]): IFillMesh => +{ + const thickness = $context.thickness / 2; + + const fillVertices: IPath[] = []; + for (let idx = 0; idx < vertices.length; ++idx) { + const vertex = meshGenerateStrokeOutlineUseCase(vertices[idx], thickness); + fillVertices.push(...vertex); + } + + return meshFillGenerateUseCase(fillVertices, "stroke"); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand.ts b/packages/webgl/src/PathCommand.ts new file mode 100644 index 00000000..3f625069 --- /dev/null +++ b/packages/webgl/src/PathCommand.ts @@ -0,0 +1,36 @@ +import type { IPath } from "./interface/IPath"; +import { execute as pathCommandPushCurrentPathToVerticesService } from "./PathCommand/service/PathCommandPushCurrentPathToVerticesService"; +import { $getArray } from "./WebGLUtil"; + +/** + * @description 現在操作中のパス配列 + * Current path array being operated + * + * @type {array} + * @protected + */ +export const $currentPath: IPath = $getArray(); + +/** + * @description 頂点配列 + * Vertex array + * + * @type {array} + * @protected + */ +export const $vertices: IPath[] = $getArray(); + +/** + * @description 頂点配列を取得 + * Get the vertex array + * + * @param {boolean} [stroke=false] + * @return {array} + * @method + * @public + */ +export const $getVertices = (stroke: boolean = false): IPath[] => +{ + pathCommandPushCurrentPathToVerticesService(stroke); + return $vertices; +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.test.ts b/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.test.ts new file mode 100644 index 00000000..50379a22 --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.test.ts @@ -0,0 +1,26 @@ + +import { execute } from "./PathCommandBeginPathService"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandBeginPathService.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + $currentPath.push(1, 2, true); + $vertices.push([1, 2, true], [3, 4, false]); + + expect($currentPath.length).toBe(3); + expect($vertices.length).toBe(2); + + execute(); + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.ts b/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.ts new file mode 100644 index 00000000..02b5eede --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandBeginPathService.ts @@ -0,0 +1,22 @@ +import { $poolArray } from "../../WebGLUtil"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +/** + * @description 現在操作中のパス配列を全てクリアします。 + * Clear all path arrays currently being operated. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + $currentPath.length = 0; + + while ($vertices.length) { + $poolArray($vertices.pop()); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.test.ts b/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.test.ts new file mode 100644 index 00000000..b02605ea --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.test.ts @@ -0,0 +1,25 @@ + +import { execute } from "./PathCommandCreateRectVerticesService"; +import { describe, expect, it } from "vitest"; + +describe("PathCommandCreateRectVerticesService.js method test", () => +{ + it("test case", () => + { + const vertices = execute(0, 0, 100, 100); + expect(vertices.length).toBe(1); + expect(vertices[0].length).toBe(12); + expect(vertices[0][0]).toBe(0); + expect(vertices[0][1]).toBe(0); + expect(vertices[0][2]).toBe(false); + expect(vertices[0][3]).toBe(100); + expect(vertices[0][4]).toBe(0); + expect(vertices[0][5]).toBe(false); + expect(vertices[0][6]).toBe(100); + expect(vertices[0][7]).toBe(100); + expect(vertices[0][8]).toBe(false); + expect(vertices[0][9]).toBe(0); + expect(vertices[0][10]).toBe(100); + expect(vertices[0][11]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.ts b/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.ts new file mode 100644 index 00000000..db0f2abf --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandCreateRectVerticesService.ts @@ -0,0 +1,24 @@ +import type { IPath } from "../../interface/IPath"; +import { $getArray } from "../../WebGLUtil"; + +/** + * @description 指定の座標とサイズで矩形を描画 + * Draw a rectangle with the specified coordinates and size + * + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @return {array} + * @method + * @public + */ +export const execute = (x: number, y: number, w: number, h: number): IPath[] => +{ + return $getArray($getArray( + x, y, false, + x + w, y, false, + x + w, y + h, false, + x, y + h, false + )); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.test.ts b/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.test.ts new file mode 100644 index 00000000..6933f4ca --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.test.ts @@ -0,0 +1,20 @@ +import { execute } from "./PathCommandEqualsToLastPointService"; +import { describe, expect, it } from "vitest"; +import { $currentPath } from "../../PathCommand"; + +describe("PathCommandEqualsToLastPointService.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $currentPath.push( + 1, 2, false, + 3, 4, false, + 5, 6, false + ); + + expect($currentPath.length).toBe(9); + expect(execute(1, 2)).toBe(false); + expect(execute(5, 6)).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.ts b/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.ts new file mode 100644 index 00000000..0192dc08 --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandEqualsToLastPointService.ts @@ -0,0 +1,19 @@ +import { $currentPath } from "../../PathCommand"; + +/** + * @description 最終の頂点情報と指定の(x,y)座標が一致するかどうかを判定します。 + * Determines whether the last vertex information matches the specified (x, y) coordinates. + * + * @param {number} x + * @param {number} y + * @return {boolean} + * @method + * @protected + */ +export const execute = (x: number, y: number): boolean => +{ + const length = $currentPath.length; + const lastX: number = +$currentPath[length - 3]; + const lastY: number = +$currentPath[length - 2]; + return x === lastX && y === lastY; +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.test.ts b/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.test.ts new file mode 100644 index 00000000..9b792772 --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.test.ts @@ -0,0 +1,43 @@ +import { execute } from "./PathCommandPushCurrentPathToVerticesService"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandPushCurrentPathToVerticesService.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $currentPath.push( + 1, 2, false, + 3, 4, false, + 5, 6, true, + 7, 8, false + ); + + $vertices.length = 0; + + expect($currentPath.length).toBe(12); + expect($vertices.length).toBe(0); + + execute(); + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(1); + expect($vertices[0][0]).toBe(1); + expect($vertices[0][1]).toBe(2); + expect($vertices[0][2]).toBe(false); + expect($vertices[0][3]).toBe(3); + expect($vertices[0][4]).toBe(4); + expect($vertices[0][5]).toBe(false); + expect($vertices[0][6]).toBe(5); + expect($vertices[0][7]).toBe(6); + expect($vertices[0][8]).toBe(true); + expect($vertices[0][9]).toBe(7); + expect($vertices[0][10]).toBe(8); + expect($vertices[0][11]).toBe(false); + + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.ts b/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.ts new file mode 100644 index 00000000..ef05ad63 --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandPushCurrentPathToVerticesService.ts @@ -0,0 +1,25 @@ +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +/** + * @description 現在操作中のパス配列を全てverticesに統合します + * Integrate all path arrays currently being operated into vertices + * + * @param {boolean} [stroke=false] + * @return {void} + * @method + * @protected + */ +export const execute = (stroke: boolean = false): void => +{ + const minVertices = stroke ? 4 : 10; + if ($currentPath.length < minVertices) { + $currentPath.length = 0; + return ; + } + + $vertices.push($currentPath.slice(0)); + $currentPath.length = 0; +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.test.ts b/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.test.ts new file mode 100644 index 00000000..ac48fd29 --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.test.ts @@ -0,0 +1,25 @@ +import { execute } from "./PathCommandPushPointToCurrentPathService"; +import { describe, expect, it } from "vitest"; +import { $currentPath } from "../../PathCommand"; + +describe("PathCommandPushPointToCurrentPathService.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + + expect($currentPath.length).toBe(0); + execute(1, 2, true); + + expect($currentPath.length).toBe(3); + expect($currentPath[0]).toBe(1); + expect($currentPath[1]).toBe(2); + expect($currentPath[2]).toBe(true); + + execute(3, 4, false); + expect($currentPath.length).toBe(6); + expect($currentPath[3]).toBe(3); + expect($currentPath[4]).toBe(4); + expect($currentPath[5]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.ts b/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.ts new file mode 100644 index 00000000..ae51cc8a --- /dev/null +++ b/packages/webgl/src/PathCommand/service/PathCommandPushPointToCurrentPathService.ts @@ -0,0 +1,17 @@ +import { $currentPath } from "../../PathCommand"; + +/** + * @description 現在のパスに頂点情報を追加します。 + * Add vertex information to the current path. + * + * @param {number} x + * @param {number} y + * @param {boolean} is_control_point + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number, is_control_point: boolean): void => +{ + $currentPath.push(x, y, is_control_point); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.test.ts new file mode 100644 index 00000000..56ce5eb1 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.test.ts @@ -0,0 +1,75 @@ +import { execute } from "./PathCommandArcUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandArcUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + + execute(10, 10, 20); + + expect($currentPath.length).toBe(195); + expect($vertices.length).toBe(0); + + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + expect($currentPath[3]).toBe(5.625); + expect($currentPath[4]).toBe(3.9460678100585938); + expect($currentPath[5]).toBe(true); + expect($currentPath[6]).toBe(9.496014595031738); + expect($currentPath[7]).toBe(7.331478595733643); + expect($currentPath[8]).toBe(false); + expect($currentPath[9]).toBe(13.367029190063477); + expect($currentPath[10]).toBe(10.716890335083008); + expect($currentPath[11]).toBe(true); + expect($currentPath[12]).toBe(15.772050857543945); + expect($currentPath[13]).toBe(13.566152572631836); + expect($currentPath[14]).toBe(false); + expect($currentPath[15]).toBe(18.17707061767578); + expect($currentPath[16]).toBe(16.415414810180664); + expect($currentPath[17]).toBe(true); + expect($currentPath[18]).toBe(19.260093688964844); + expect($currentPath[19]).toBe(18.74078369140625); + expect($currentPath[20]).toBe(false); + expect($currentPath[21]).toBe(20.343116760253906); + expect($currentPath[22]).toBe(21.06615447998047); + expect($currentPath[23]).toBe(true); + expect($currentPath[24]).toBe(20.392135620117188); + expect($currentPath[25]).toBe(22.892135620117188); + expect($currentPath[26]).toBe(false); + expect($currentPath[27]).toBe(20.44115447998047); + expect($currentPath[28]).toBe(24.718116760253906); + expect($currentPath[29]).toBe(true); + expect($currentPath[30]).toBe(19.60015869140625); + expect($currentPath[31]).toBe(26.056968688964844); + expect($currentPath[32]).toBe(false); + expect($currentPath[33]).toBe(18.759164810180664); + expect($currentPath[34]).toBe(27.39582061767578); + expect($currentPath[35]).toBe(true); + expect($currentPath[36]).toBe(17.316152572631836); + expect($currentPath[37]).toBe(28.272050857543945); + expect($currentPath[38]).toBe(false); + expect($currentPath[39]).toBe(15.873140335083008); + expect($currentPath[40]).toBe(29.148279190063477); + expect($currentPath[41]).toBe(true); + expect($currentPath[42]).toBe(13.972103118896484); + expect($currentPath[43]).toBe(29.574138641357422); + expect($currentPath[44]).toBe(false); + expect($currentPath[45]).toBe(12.071067810058594); + expect($currentPath[46]).toBe(30); + expect($currentPath[47]).toBe(true); + expect($currentPath[48]).toBe(10); + expect($currentPath[49]).toBe(30); + expect($currentPath[50]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.ts new file mode 100644 index 00000000..323bd7ba --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandArcUseCase.ts @@ -0,0 +1,31 @@ +import { execute as pathCommandBezierCurveToUseCase } from "./PathCommandBezierCurveToUseCase"; + +/** + * @description 円弧を描画します。 + * Draw an arc. + * + * @param {number} x + * @param {number} y + * @param {number} radius + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number, radius: number): void => +{ + const r = radius; + const k = radius * 0.5522847498307936; + + pathCommandBezierCurveToUseCase( + x + r, y + k, x + k, y + r, x, y + r + ); + pathCommandBezierCurveToUseCase( + x - k, y + r, x - r, y + k, x - r, y + ); + pathCommandBezierCurveToUseCase( + x - r, y - k, x - k, y - r, x, y - r + ); + pathCommandBezierCurveToUseCase( + x + k, y - r, x + r, y - k, x + r, y + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.test.ts new file mode 100644 index 00000000..5565f688 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.test.ts @@ -0,0 +1,73 @@ +import { execute } from "./PathCommandBezierCurveToUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandBezierCurveToUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + + execute(10, 10, 20, 20, 30, 30); + expect($currentPath.length).toBe(51); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + expect($currentPath[3]).toBe(1.875); + expect($currentPath[4]).toBe(1.875); + expect($currentPath[5]).toBe(true); + expect($currentPath[6]).toBe(3.75); + expect($currentPath[7]).toBe(3.75); + expect($currentPath[8]).toBe(false); + expect($currentPath[9]).toBe(5.625); + expect($currentPath[10]).toBe(5.625); + expect($currentPath[11]).toBe(true); + expect($currentPath[12]).toBe(7.5); + expect($currentPath[13]).toBe(7.5); + expect($currentPath[14]).toBe(false); + expect($currentPath[15]).toBe(9.375); + expect($currentPath[16]).toBe(9.375); + expect($currentPath[17]).toBe(true); + expect($currentPath[18]).toBe(11.25); + expect($currentPath[19]).toBe(11.25); + expect($currentPath[20]).toBe(false); + expect($currentPath[21]).toBe(13.125); + expect($currentPath[22]).toBe(13.125); + expect($currentPath[23]).toBe(true); + expect($currentPath[24]).toBe(15); + expect($currentPath[25]).toBe(15); + expect($currentPath[26]).toBe(false); + expect($currentPath[27]).toBe(16.875); + expect($currentPath[28]).toBe(16.875); + expect($currentPath[29]).toBe(true); + expect($currentPath[30]).toBe(18.75); + expect($currentPath[31]).toBe(18.75); + expect($currentPath[32]).toBe(false); + expect($currentPath[33]).toBe(20.625); + expect($currentPath[34]).toBe(20.625); + expect($currentPath[35]).toBe(true); + expect($currentPath[36]).toBe(22.5); + expect($currentPath[37]).toBe(22.5); + expect($currentPath[38]).toBe(false); + expect($currentPath[39]).toBe(24.375); + expect($currentPath[40]).toBe(24.375); + expect($currentPath[41]).toBe(true); + expect($currentPath[42]).toBe(26.25); + expect($currentPath[43]).toBe(26.25); + expect($currentPath[44]).toBe(false); + expect($currentPath[45]).toBe(28.125); + expect($currentPath[46]).toBe(28.125); + expect($currentPath[47]).toBe(true); + expect($currentPath[48]).toBe(30); + expect($currentPath[49]).toBe(30); + expect($currentPath[50]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.ts new file mode 100644 index 00000000..3f8b8a4e --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandBezierCurveToUseCase.ts @@ -0,0 +1,48 @@ +import { execute as pathCommandMoveToUseCase } from "./PathCommandMoveToUseCase"; +import { execute as pathCommandEqualsToLastPointService } from "../service/PathCommandEqualsToLastPointService"; +import { execute as pathCommandQuadraticCurveToUseCase } from "./PathCommandQuadraticCurveToUseCase"; +import { execute as bezierConverterCubicToQuadUseCase } from "../../BezierConverter/usecase/BezierConverterCubicToQuadUseCase"; +import { $currentPath } from "../../PathCommand"; + +/** + * @description 3次ベジェ曲線を描画します。 + * Draw a cubic Bezier curve. + * + * @param {number} cx1 + * @param {number} cy1 + * @param {number} cx2 + * @param {number} cy2 + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = ( + cx1: number, cy1: number, + cx2: number, cy2: number, + x: number, y: number +): void => { + + if (!$currentPath.length) { + pathCommandMoveToUseCase(0, 0); + } + + if (pathCommandEqualsToLastPointService(x, y)) { + return; + } + + const length = $currentPath.length; + const fromX: number = +$currentPath[length - 3]; + const fromY: number = +$currentPath[length - 2]; + + const buffer = bezierConverterCubicToQuadUseCase(fromX, fromY, cx1, cy1, cx2, cy2, x, y); + for (let idx = 0; 32 > idx; ) { + pathCommandQuadraticCurveToUseCase( + buffer[idx++], + buffer[idx++], + buffer[idx++], + buffer[idx++] + ); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.test.ts new file mode 100644 index 00000000..05529f0d --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.test.ts @@ -0,0 +1,40 @@ +import { execute } from "./PathCommandClosePathUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandClosePathUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + $currentPath.push( + 1, 2, false, + 3, 4, true, + 5, 6, false + ); + + expect($currentPath.length).toBe(9); + expect($vertices.length).toBe(0); + + execute(); + + expect($currentPath.length).toBe(12); + expect($currentPath[0]).toBe(1); + expect($currentPath[1]).toBe(2); + expect($currentPath[2]).toBe(false); + expect($currentPath[3]).toBe(3); + expect($currentPath[4]).toBe(4); + expect($currentPath[5]).toBe(true); + expect($currentPath[6]).toBe(5); + expect($currentPath[7]).toBe(6); + expect($currentPath[8]).toBe(false); + expect($currentPath[9]).toBe(1); + expect($currentPath[10]).toBe(2); + expect($currentPath[11]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.ts new file mode 100644 index 00000000..f6321828 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandClosePathUseCase.ts @@ -0,0 +1,27 @@ +import { execute as pathCommandEqualsToLastPointService } from "../service/PathCommandEqualsToLastPointService"; +import { execute as pathCommandPushPointToCurrentPathService } from "../service/PathCommandPushPointToCurrentPathService"; +import { $currentPath } from "../../PathCommand"; + +/** + * @description パスを閉じる + * Close the path + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + if ($currentPath.length < 7) { + return ; + } + + const x: number = +$currentPath[0]; + const y: number = +$currentPath[1]; + + if (pathCommandEqualsToLastPointService(x, y)) { + return; + } + + pathCommandPushPointToCurrentPathService(x, y, false); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.test.ts new file mode 100644 index 00000000..df8b6dfb --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.test.ts @@ -0,0 +1,42 @@ +import { execute } from "./PathCommandLineToUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandLineToUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + + execute(0, 0); + expect($currentPath.length).toBe(3); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + + execute(0, 0); + expect($currentPath.length).toBe(3); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + + execute(10, 10); + expect($currentPath.length).toBe(6); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + expect($currentPath[3]).toBe(10); + expect($currentPath[4]).toBe(10); + expect($currentPath[5]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.ts new file mode 100644 index 00000000..37ec44f0 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandLineToUseCase.ts @@ -0,0 +1,30 @@ +import { execute as pathCommandPushPointToCurrentPathService } from "../service/PathCommandPushPointToCurrentPathService"; +import { execute as pathCommandEqualsToLastPointService } from "../service/PathCommandEqualsToLastPointService"; +import { execute as pathCommandMoveToUseCase } from "./PathCommandMoveToUseCase"; +import { $currentPath } from "../../PathCommand"; + +/** + * @description 現在の座標から指定の(x,y)座標に直線を描画します。 + * Draws a straight line from the current coordinates to the specified (x, y) coordinates. + * + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number): void => +{ + // 現在のパスが存在しない場合はMoveToを実行 + if (!$currentPath.length) { + pathCommandMoveToUseCase(x, y); + } + + // 追加する座標が最終の座標と一致する場合は何もしない + if (pathCommandEqualsToLastPointService(x, y)) { + return ; + } + + // 新しいパスに座標を追加 + pathCommandPushPointToCurrentPathService(x, y, false); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.test.ts new file mode 100644 index 00000000..36cdd556 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.test.ts @@ -0,0 +1,42 @@ +import { execute } from "./PathCommandMoveToUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandMoveToUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + + execute(0, 0); + expect($currentPath.length).toBe(3); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + + execute(0, 0); + expect($currentPath.length).toBe(3); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + + $currentPath.push(30, 30 ,true); + expect($currentPath.length).toBe(6); + + execute(10, 10); + expect($currentPath.length).toBe(3); + expect($currentPath[0]).toBe(10); + expect($currentPath[1]).toBe(10); + expect($currentPath[2]).toBe(false); + expect($vertices.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.ts new file mode 100644 index 00000000..d694b8c0 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandMoveToUseCase.ts @@ -0,0 +1,37 @@ +import { execute as pathCommandPushPointToCurrentPathService } from "../service/PathCommandPushPointToCurrentPathService"; +import { execute as pathCommandEqualsToLastPointService } from "../service/PathCommandEqualsToLastPointService"; +import { execute as pathCommandPushCurrentPathToVerticesService } from "../service/PathCommandPushCurrentPathToVerticesService"; +import { $currentPath } from "../../PathCommand"; + +/** + * @description 指定の(x,y)座標に移動となる頂点情報を追加します。 + * Add vertex information that moves to the specified (x, y) coordinates. + * + * @param {array} current_path + * @param {array} vertices + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = (x: number, y: number): void => +{ + + // 現在のパスが存在しない場合は初期処理を行って終了 + if (!$currentPath.length) { + pathCommandPushPointToCurrentPathService(x, y, false); + return; + } + + // 追加する座標が最終の座標と一致する場合は何もしない + if (pathCommandEqualsToLastPointService(x, y)) { + return; + } + + // 現在のパスをverticesに追加して新しいパスを作成 + pathCommandPushCurrentPathToVerticesService(); + + // 新しいパスに座標を追加 + pathCommandPushPointToCurrentPathService(x, y, false); +}; \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.test.ts b/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.test.ts new file mode 100644 index 00000000..12f0c168 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.test.ts @@ -0,0 +1,31 @@ +import { execute } from "./PathCommandQuadraticCurveToUseCase"; +import { describe, expect, it } from "vitest"; +import { + $currentPath, + $vertices +} from "../../PathCommand"; + +describe("PathCommandQuadraticCurveToUseCase.js method test", () => +{ + it("test case", () => + { + $currentPath.length = 0; + $vertices.length = 0; + + expect($currentPath.length).toBe(0); + expect($vertices.length).toBe(0); + + execute(10, 20, 30, 40); + expect($currentPath.length).toBe(9); + expect($vertices.length).toBe(0); + expect($currentPath[0]).toBe(0); + expect($currentPath[1]).toBe(0); + expect($currentPath[2]).toBe(false); + expect($currentPath[3]).toBe(10); + expect($currentPath[4]).toBe(20); + expect($currentPath[5]).toBe(true); + expect($currentPath[6]).toBe(30); + expect($currentPath[7]).toBe(40); + expect($currentPath[8]).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.ts b/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.ts new file mode 100644 index 00000000..f44d91a1 --- /dev/null +++ b/packages/webgl/src/PathCommand/usecase/PathCommandQuadraticCurveToUseCase.ts @@ -0,0 +1,33 @@ +import { execute as pathCommandMoveToUseCase } from "./PathCommandMoveToUseCase"; +import { execute as pathCommandEqualsToLastPointService } from "../service/PathCommandEqualsToLastPointService"; +import { execute as pathCommandPushPointToCurrentPathService } from "../service/PathCommandPushPointToCurrentPathService"; +import { $currentPath } from "../../PathCommand"; + +/** + * @description 二次曲線を描画 + * Draw a quadratic curve + * + * @param {number} cx + * @param {number} cy + * @param {number} x + * @param {number} y + * @return {void} + * @method + * @protected + */ +export const execute = ( + cx: number, cy: number, + x: number ,y: number +): void => { + + if (!$currentPath.length) { + pathCommandMoveToUseCase(0, 0); + } + + if (pathCommandEqualsToLastPointService(x, y)) { + return; + } + + pathCommandPushPointToCurrentPathService(cx, cy, true); + pathCommandPushPointToCurrentPathService(x, y, false); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceBlurFilter.ts b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceBlurFilter.ts new file mode 100644 index 00000000..670a695c --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceBlurFilter.ts @@ -0,0 +1,37 @@ +/** + * @param {number} half_blur + * @return {string} + * @method + * @static + */ +export const BLUR_FILTER_TEMPLATE = (half_blur: number): string => +{ + const halfBlurFixed = half_blur.toFixed(1); + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +uniform vec4 u_mediump; + +in vec2 v_coord; +out vec4 o_color; + +void main() { + vec2 offset = u_mediump.xy; + float fraction = u_mediump.z; + float samples = u_mediump.w; + + vec4 color = texture(u_texture, v_coord); + + for (float i = 1.0; i < ${halfBlurFixed}; i += 1.0) { + color += texture(u_texture, v_coord + offset * i); + color += texture(u_texture, v_coord - offset * i); + } + color += texture(u_texture, v_coord + offset * ${halfBlurFixed}) * fraction; + color += texture(u_texture, v_coord - offset * ${halfBlurFixed}) * fraction; + color /= samples; + + o_color = color; +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceColorMatrixFilter.ts b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceColorMatrixFilter.ts new file mode 100644 index 00000000..c9549645 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceColorMatrixFilter.ts @@ -0,0 +1,29 @@ +/** + * @return {string} + * @method + * @static + */ +export const COLOR_MATRIX_FILTER_TEMPLATE = (): string => +{ + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +uniform vec4 u_mediump[5]; + +in vec2 v_coord; +out vec4 o_color; + +void main() { + mat4 mul = mat4(u_mediump[0], u_mediump[1], u_mediump[2], u_mediump[3]); + vec4 add = u_mediump[4]; + + vec4 color = texture(u_texture, v_coord); + + color.rgb /= max(0.0001, color.a); + color = clamp(color * mul + add, 0.0, 1.0); + color.rgb *= color.a; + + o_color = color; +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceConvolutionFilter.ts b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceConvolutionFilter.ts new file mode 100644 index 00000000..00091943 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceConvolutionFilter.ts @@ -0,0 +1,86 @@ +import { FUNCTION_IS_INSIDE } from "../FragmentShaderLibrary"; + +/** + * @param {number} mediump_length + * @param {number} x + * @param {number} y + * @param {boolean} preserve_alpha + * @param {boolean} clamp + * @return {string} + * @method + * @static + */ +export const CONVOLUTION_FILTER_TEMPLATE = ( + mediump_length: number, + x: number, + y: number, + preserve_alpha: boolean, + clamp: boolean +): string => { + + const halfX = Math.floor(x * 0.5); + const halfY = Math.floor(y * 0.5); + const size = x * y; + + let matrixStatement = ""; + const matrixIndex = clamp ? 1 : 2; + for (let idx = 0; idx < size; ++idx) { + + const index = matrixIndex + Math.floor(idx / 4); + + const component = idx % 4; + + matrixStatement += ` + result += getWeightedColor(${idx}, u_mediump[${index}][${component}]); +`; + } + + const preserve_alphaStatement = preserve_alpha + ? "result.a = texture(u_texture, v_coord).a;" + : ""; + const clampStatement = clamp + ? "" + : ` + vec4 substitute_color = u_mediump[1]; + color = mix(substitute_color, color, isInside(uv)); +`; + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +uniform vec4 u_mediump[${mediump_length}]; + +in vec2 v_coord; +out vec4 o_color; + +${FUNCTION_IS_INSIDE()} + +vec4 getWeightedColor (in int i, in float weight) { + vec2 rcp_size = u_mediump[0].xy; + + int i_div_x = i / ${x}; + int i_mod_x = i - ${x} * i_div_x; + vec2 offset = vec2(i_mod_x - ${halfX}, ${halfY} - i_div_x); + vec2 uv = v_coord + offset * rcp_size; + + vec4 color = texture(u_texture, uv); + color.rgb /= max(0.0001, color.a); + ${clampStatement} + + return color * weight; +} + +void main() { + float rcp_divisor = u_mediump[0].z; + float bias = u_mediump[0].w; + + vec4 result = vec4(0.0); + ${matrixStatement} + result = clamp(result * rcp_divisor + bias, 0.0, 1.0); + ${preserve_alphaStatement} + + result.rgb *= result.a; + o_color = result; +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceDisplacementMapFilter.ts b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceDisplacementMapFilter.ts new file mode 100644 index 00000000..b7c7e7e0 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceDisplacementMapFilter.ts @@ -0,0 +1,126 @@ +import { FUNCTION_IS_INSIDE } from "../FragmentShaderLibrary"; + +/** + * @param {number} mediump_length + * @param {number} component_x + * @param {number} component_y + * @param {number} mode + * @return {string} + * @method + * @static + */ +export const DISPLACEMENT_MAP_FILTER_TEMPLATE = ( + mediump_length: number, + component_x: number, + component_y: number, + mode: number +): string => { + + let cx: string; + let cy: string; + let modeStatement: string; + + switch (component_x) { + + case 1: // BitmapDataChannel.RED + cx = "map_color.r"; + break; + + case 2: // BitmapDataChannel.GREEN + cx = "map_color.g"; + break; + + case 4: // BitmapDataChannel.BLUE + cx = "map_color.b"; + break; + + case 8: // BitmapDataChannel.ALPHA + cx = "map_color.a"; + break; + + default: + cx = "0.5"; + break; + + } + + switch (component_y) { + + case 1: // BitmapDataChannel.RED + cy = "map_color.r"; + break; + + case 2: // BitmapDataChannel.GREEN + cy = "map_color.g"; + break; + + case 4: // BitmapDataChannel.BLUE + cy = "map_color.b"; + break; + + case 8: // BitmapDataChannel.ALPHA + cy = "map_color.a"; + break; + + default: + cy = "0.5"; + break; + + } + + switch (mode) { + + case 0: + modeStatement = ` + vec4 source_color = texture(u_textures[0], uv); +`; + break; + + case 3: + // 置き換え後の座標が範囲外なら、置き換え前の座標をとる(x軸とy軸を別々に判定する) + modeStatement = ` + vec4 source_color =texture(u_textures[0], mix(v_coord, uv, step(abs(uv - vec2(0.5)), vec2(0.5)))); +`; + break; + + case 1: + modeStatement = ` + vec4 substitute_color = u_mediump[2]; + vec4 source_color = mix(substitute_color, texture(u_textures[0], uv), isInside(uv)); +`; + break; + + case 2: + default: + modeStatement = ` + vec4 source_color = texture(u_textures[0], fract(uv)); +`; + break; + } + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_textures[2]; +uniform vec4 u_mediump[${mediump_length}]; + +in vec2 v_coord; +out vec4 o_color; + +${FUNCTION_IS_INSIDE()} + +void main() { + vec2 uv_to_st_scale = u_mediump[0].xy; + vec2 uv_to_st_offset = u_mediump[0].zw; + vec2 scale = u_mediump[1].xy; + + vec2 st = v_coord * uv_to_st_scale - uv_to_st_offset; + vec4 map_color = texture(u_textures[1], st); + + vec2 offset = vec2(${cx}, ${cy}) - 0.5; + vec2 uv = v_coord + offset * scale; + ${modeStatement} + + o_color = mix(texture(u_textures[0], v_coord), source_color, isInside(st)); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceFilter.ts b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceFilter.ts new file mode 100644 index 00000000..862d4617 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/Filter/FragmentShaderSourceFilter.ts @@ -0,0 +1,347 @@ +import { FUNCTION_IS_INSIDE } from "../FragmentShaderLibrary"; + +/** + * @param {number} index + * @return {string} + * @method + * @private + */ +const STATEMENT_BASE_TEXTURE_TRANSFORM = (index: number): string => +{ + return ` + vec2 base_scale = u_mediump[${index}].xy; + vec2 base_offset = u_mediump[${index}].zw; + + vec2 uv = v_coord * base_scale - base_offset; + vec4 base = mix(vec4(0.0), texture(u_textures[1], uv), isInside(uv)); +`; +}; + +/** + * @return {string} + * @method + * @private + */ +const STATEMENT_BLUR_TEXTURE = (): string => +{ + return ` + vec4 blur = texture(u_textures[0], v_coord); +`; +}; + +/** + * @param {number} index + * @return {string} + * @method + * @private + */ +const STATEMENT_BLUR_TEXTURE_TRANSFORM = (index: number): string => +{ + return ` + vec2 blur_scale = u_mediump[${index}].xy; + vec2 blur_offset = u_mediump[${index}].zw; + + vec2 st = v_coord * blur_scale - blur_offset; + vec4 blur = mix(vec4(0.0), texture(u_textures[0], st), isInside(st)); +`; +}; + +/** + * @param {number} offset + * @return {string} + * @method + * @private + */ +const STATEMENT_GLOW_STRENGTH = (offset: number): string => +{ + const index = Math.floor(offset / 4); + const component = offset % 4; + return ` + float strength = u_mediump[${index}][${component}]; + blur.a = clamp(blur.a * strength, 0.0, 1.0); +`; +}; + +/** + * @param {number} index + * @return {string} + * @method + * @static + */ +const STATEMENT_GLOW_SOLID_COLOR = (index: number): string => +{ + return ` + vec4 color = u_mediump[${index}]; + blur = color * blur.a; +`; +}; + +/** + * @param {boolean} transforms_base + * @return {string} + * @method + * @static + */ +const STATEMENT_GLOW_GRADIENT_COLOR = (transforms_base: boolean): string => +{ + return ` + blur = texture(u_textures[${transforms_base ? 2 : 1}], vec2(blur.a, 0.5)); +`; +}; + +/** + * @param {boolean} is_inner + * @param {boolean} transforms_base + * @param {boolean} applies_strength + * @param {boolean} is_gradient + * @param {number} color_index + * @param {number} strength_offset + * @return {string} + * @method + * @private + */ +const STATEMENT_GLOW = ( + is_inner: boolean, + transforms_base: boolean, + applies_strength: boolean, + is_gradient: boolean, + color_index: number, + strength_offset: number +): string => { + + const innerStatement = is_inner + ? "blur.a = 1.0 - blur.a;" + : ""; + + const strengthStatement = applies_strength + ? STATEMENT_GLOW_STRENGTH(strength_offset) + : ""; + + const colorStatement = is_gradient + ? STATEMENT_GLOW_GRADIENT_COLOR(transforms_base) + : STATEMENT_GLOW_SOLID_COLOR(color_index); + + return ` + ${innerStatement} + ${strengthStatement} + ${colorStatement} +`; +}; + +/** + * @return {string} + * @method + * @static + */ +const STATEMENT_BLUR_TEXTURE_2 = (): string => +{ + return ` + vec4 blur2 = texture(u_textures[0], 1.0 - v_coord); +`; +}; + +/** + * @return {string} + * @method + * @static + */ +const STATEMENT_BLUR_TEXTURE_TRANSFORM_2 = (): string => +{ + return ` + vec2 pq = (1.0 - v_coord) * blur_scale - blur_offset; + vec4 blur2 = mix(vec4(0.0), texture(u_textures[0], pq), isInside(pq)); +`; +}; + +/** + * @param {boolean} offset + * @return {string} + * @method + * @static + */ +const STATEMENT_BEVEL_STRENGTH = (offset: number): string => +{ + const index = Math.floor(offset / 4); + const component = offset % 4; + + return ` + float strength = u_mediump[${index}][${component}]; + highlight_alpha *= strength; + shadow_alpha *= strength; +`; +}; + +/** + * @param {number} index + * @return {string} + * @method + * @static + */ +const STATEMENT_BEVEL_SOLID_COLOR = (index: number): string => +{ + return ` + vec4 highlight_color = u_mediump[${index}]; + vec4 shadow_color = u_mediump[${index + 1}]; + blur = highlight_color * highlight_alpha + shadow_color * shadow_alpha; +`; +}; + +/** + * @param {boolean} transforms_base + * @return {string} + * @method + * @static + */ +const STATEMENT_BEVEL_GRADIENT_COLOR = (transforms_base: boolean): string => +{ + return ` + blur = texture(u_textures[${transforms_base ? 2 : 1}], vec2( + 0.5019607843137255 - 0.5019607843137255 * shadow_alpha + 0.4980392156862745 * highlight_alpha, + 0.5 + )); +`; +}; + +/** + * @param {number} textures_length + * @param {number} mediump_length + * @param {boolean} transforms_base + * @param {boolean} transforms_blur + * @param {boolean} is_glow + * @param {string} type + * @param {boolean} knockout + * @param {boolean} applies_strength + * @param {boolean} is_gradient + * @return {string} + * @method + * @static + */ +export const BITMAP_FILTER_TEMPLATE = ( + textures_length: number, + mediump_length: number, + transforms_base: boolean, + transforms_blur: boolean, + is_glow: boolean, + type: string, + knockout: boolean, + applies_strength: boolean, + is_gradient: boolean +): string => { + + let index = 0; + + const baseStatement = transforms_base + ? STATEMENT_BASE_TEXTURE_TRANSFORM(index++) + : ""; + + const blurStatement = transforms_blur + ? STATEMENT_BLUR_TEXTURE_TRANSFORM(index++) + : STATEMENT_BLUR_TEXTURE(); + + const isInner = type === "inner"; + + const colorIndex = index; + let strengthOffset = index * 4; + let colorStatement; + + if (is_gradient) { + + colorStatement = is_glow + ? STATEMENT_GLOW(false, transforms_base, applies_strength, is_gradient, colorIndex, strengthOffset) + : STATEMENT_BEVEL(transforms_base, transforms_blur, applies_strength, is_gradient, colorIndex, strengthOffset); + + } else if (is_glow) { + + strengthOffset += 4; + colorStatement = STATEMENT_GLOW(isInner, transforms_base, applies_strength, is_gradient, colorIndex, strengthOffset); + + } else { + + strengthOffset += 8; + colorStatement = STATEMENT_BEVEL(transforms_base, transforms_blur, applies_strength, is_gradient, colorIndex, strengthOffset); + + } + + let modeExpression; + switch (type) { + + case "outer": + modeExpression = knockout + ? "blur - blur * base.a" + : "base + blur - blur * base.a"; + break; + + case "full": + modeExpression = knockout + ? "blur" + : "base - base * blur.a + blur"; + break; + + case "inner": + default: + modeExpression = "blur"; + break; + + } + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_textures[${textures_length}]; +uniform vec4 u_mediump[${mediump_length}]; + +in vec2 v_coord; +out vec4 o_color; + +${FUNCTION_IS_INSIDE()} + +void main() { + ${baseStatement} + ${blurStatement} + ${colorStatement} + o_color = ${modeExpression}; +}`; +}; + +/** + * @param {string} transforms_base + * @param {boolean} transforms_blur + * @param {boolean} applies_strength + * @param {boolean} is_gradient + * @param {number} color_index + * @param {number} strength_offset + * @return {string} + * @method + * @static + */ +const STATEMENT_BEVEL = ( + transforms_base: boolean, + transforms_blur: boolean, + applies_strength: boolean, + is_gradient: boolean, + color_index: number, + strength_offset: number +): string => { + + const blur2Statement = transforms_blur + ? STATEMENT_BLUR_TEXTURE_TRANSFORM_2() + : STATEMENT_BLUR_TEXTURE_2(); + + const strengthStatement = applies_strength + ? STATEMENT_BEVEL_STRENGTH(strength_offset) + : ""; + + const colorStatement = is_gradient + ? STATEMENT_BEVEL_GRADIENT_COLOR(transforms_base) + : STATEMENT_BEVEL_SOLID_COLOR(color_index); + + return ` + ${blur2Statement} + float highlight_alpha = blur.a - blur2.a; + float shadow_alpha = blur2.a - blur.a; + ${strengthStatement} + highlight_alpha = clamp(highlight_alpha, 0.0, 1.0); + shadow_alpha = clamp(shadow_alpha, 0.0, 1.0); + ${colorStatement} +`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderLibrary.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderLibrary.ts new file mode 100644 index 00000000..24e3359c --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderLibrary.ts @@ -0,0 +1,39 @@ +/** + * @return {string} + * @method + * @static + */ +export const FUNCTION_IS_INSIDE = (): string => +{ + return ` +float isInside(in vec2 uv) { + return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0))); +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const STATEMENT_INSTANCED_COLOR_TRANSFORM_ON = (): string => +{ + return ` + src.rgb /= max(0.0001, src.a); + src = clamp(src * mul + add, 0.0, 1.0); + src.rgb *= src.a;`; +}; + +/** + * @param {number} mediump_index + * @return {string} + * @method + * @static + */ +export const STATEMENT_COLOR_TRANSFORM_ON = (mediump_index: number): string => +{ + return ` + vec4 mul = u_mediump[${mediump_index}]; + vec4 add = u_mediump[${mediump_index + 1}]; +${STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderSource.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderSource.ts new file mode 100644 index 00000000..fc2e9910 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderSource.ts @@ -0,0 +1,123 @@ +/** + * @description 頂点シェーダから受け取ったカラー情報をそのまま出力。 + * Outputs the color information received from the vertex shader as it is. + * + * @return {string} + * @method + * @static + */ +export const SOLID_FILL_COLOR = (): string => +{ + return `#version 300 es +precision mediump float; + +in vec4 v_color; +out vec4 o_color; + +void main() { + o_color = vec4(v_color.rgb * v_color.a, v_color.a); +}`; +}; + +/** + * @description ビットマップの繰り返しではない場合の塗りつぶし。 + * Filling when the bitmap is not repeated. + * + * @return {string} + * @method + * @static + */ +export const BITMAP_CLIPPED = (): string => +{ + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +uniform vec4 u_mediump[1]; + +in vec2 v_uv; +out vec4 o_color; + +void main() { + vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy; + + vec4 src = texture(u_texture, uv); + o_color = src; +}`; +}; + +/** + * @description ビットマップの繰り返しの場合の塗りつぶし。 + * Filling in the case of repeating the bitmap. + * + * @return {string} + * @method + * @static + */ +export const BITMAP_PATTERN = (): string => +{ + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +uniform vec4 u_mediump[1]; + +in vec2 v_uv; +out vec4 o_color; + +void main() { + vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy); + + vec4 src = texture(u_texture, uv); + o_color = src; +}`; +}; + +/** + * @description マスク専用のシェーダ。 + * Shader dedicated to masks. + * + * @return {string} + * @method + * @static + */ +export const MASK = (): string => +{ + return `#version 300 es +precision mediump float; + +in vec2 v_bezier; +out vec4 o_color; + +void main() { + vec2 px = dFdx(v_bezier); + vec2 py = dFdy(v_bezier); + + vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y); + float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f); + + if (alpha > 0.0) { + o_color = vec4(min(alpha, 1.0)); + } else { + discard; + } +}`; +}; + +/** + * @description 矩形の塗りつぶし、カラーは固定。 + * Fill the rectangle, the color is fixed. + * + * @return {string} + * @method + * @static + */ +export const FILL_RECT_COLOR = (): string => +{ + return `#version 300 es +precision mediump float; +out vec4 o_color; +void main() { + o_color = vec4(1.0); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderSourceBlend.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceBlend.ts new file mode 100644 index 00000000..0eefc424 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceBlend.ts @@ -0,0 +1,319 @@ +import { STATEMENT_COLOR_TRANSFORM_ON } from "./FragmentShaderLibrary"; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_NORMAL = (): string => +{ + return ` +vec4 blend (in vec4 src, in vec4 dst) { + return src + dst - dst * src.a; +}`; +}; + +// 各ブレンド式は、前景と背景の両方のアルファを考慮する必要がある +// https://odashi.hatenablog.com/entry/20110921/1316610121 +// https://hakuhin.jp/as3/blend.html +// +// [基本計算式] +// ・色(rgb)はストレートアルファ +// ・アルファ(a)が0の場合は例外処理をする +// 前景色 a: src.rgb * (src.a * (1.0 - dst.a)) +// 背景色 b: dst.rgb * (dst.a * (1.0 - src.a)) +// 合成色 c: mix.rgb * (src.a * dst.a) +// 最終結果: a + b + c + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_SUBTRACT = (): string => +{ + // [合成色計算式] + // dst - src + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 c = vec4(dst.rgb - src.rgb, src.a * dst.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_MULTIPLY = (): string => +{ + // [合成色計算式] + // src * dst + return ` +vec4 blend (in vec4 src, in vec4 dst) { + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + vec4 c = src * dst; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_LIGHTEN = (): string => +{ + // [合成色計算式] + // (src > dst) ? src : dst + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 c = vec4(mix(src.rgb, dst.rgb, step(src.rgb, dst.rgb)), src.a * dst.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_DARKEN = (): string => +{ + // [合成色計算式] + // (src < dst) ? src : dst + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 c = vec4(mix(src.rgb, dst.rgb, step(dst.rgb, src.rgb)), src.a * dst.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_OVERLAY = (): string => +{ + // [合成色計算式] + // if (dst < 0.5) { + // return 2.0 * src * dst + // } else { + // return 1.0 - 2.0 * (1.0 - src) * (1.0 - dst) + // } + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 mul = src * dst; + vec3 c1 = 2.0 * mul.rgb; + vec3 c2 = 2.0 * (src.rgb + dst.rgb - mul.rgb) - 1.0; + vec4 c = vec4(mix(c1, c2, step(vec3(0.5), dst.rgb)), mul.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_HARDLIGHT = (): string => +{ + // [合成色計算式] + // if (src < 0.5) { + // return 2.0 * src * dst + // } else { + // return 1.0 - 2.0 * (1.0 - src) * (1.0 - dst) + // } + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 mul = src * dst; + vec3 c1 = 2.0 * mul.rgb; + vec3 c2 = 2.0 * (src.rgb + dst.rgb - mul.rgb) - 1.0; + vec4 c = vec4(mix(c1, c2, step(vec3(0.5), src.rgb)), mul.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_DIFFERENCE = (): string => +{ + // [合成色計算式] + // abs(src - dst) + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 a = src - src * dst.a; + vec4 b = dst - dst * src.a; + + src.rgb /= src.a; + dst.rgb /= dst.a; + + vec4 c = vec4(abs(src.rgb - dst.rgb), src.a * dst.a); + c.rgb *= c.a; + + return a + b + c; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +const FUNCTION_INVERT = (): string => +{ + // [基本計算式] + // ((1.0 - dst) * src.a) + (dst * (1.0 - src.a)) + return ` +vec4 blend (in vec4 src, in vec4 dst) { + if (src.a == 0.0) { return dst; } + if (dst.a == 0.0) { return src; } + + vec4 b = dst - dst * src.a; + vec4 c = vec4(src.a - dst.rgb * src.a, src.a); + + return b + c; +}`; +}; + +/** + * @param {string} operation + * @param {boolean} with_color_transform + * @return {string} + * @method + * @static + */ +export const BLEND_TEMPLATE = (operation: string, with_color_transform: boolean): string => +{ + let blendFunction: string; + switch (operation) { + + case "subtract": + blendFunction = FUNCTION_SUBTRACT(); + break; + + case "multiply": + blendFunction = FUNCTION_MULTIPLY(); + break; + + case "lighten": + blendFunction = FUNCTION_LIGHTEN(); + break; + + case "darken": + blendFunction = FUNCTION_DARKEN(); + break; + + case "overlay": + blendFunction = FUNCTION_OVERLAY(); + break; + + case "hardlight": + blendFunction = FUNCTION_HARDLIGHT(); + break; + + case "difference": + blendFunction = FUNCTION_DIFFERENCE(); + break; + + case "invert": + blendFunction = FUNCTION_INVERT(); + break; + + default: + blendFunction = FUNCTION_NORMAL(); + break; + + } + + const colorTransformUniform = with_color_transform + ? "uniform vec4 u_mediump[2];" + : ""; + + const colorTransformStatement = with_color_transform + ? STATEMENT_COLOR_TRANSFORM_ON(0) + : ""; + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_textures[2]; +${colorTransformUniform} + +in vec2 v_coord; +out vec4 o_color; + +${blendFunction} + +void main() { + vec4 dst = texture(u_textures[0], v_coord); + vec4 src = texture(u_textures[1], v_coord); + ${colorTransformStatement} + o_color = blend(src, dst); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradient.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradient.ts new file mode 100644 index 00000000..1a66d2f1 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradient.ts @@ -0,0 +1,124 @@ +/** + * @param {number} index + * @return {string} + * @method + * @private + */ +const STATEMENT_FOCAL_POINT_ON = (index: number): string => +{ + return ` + vec2 focal = vec2(u_highp[${index}][1], 0.0); + + vec2 dir = normalize(coord - focal); + + float a = dot(dir, dir); + float b = 2.0 * dot(dir, focal); + float c = dot(focal, focal) - 1.0; + float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); + + float t = distance(focal, coord) / distance(focal, focal + dir * x);`; +}; + +/** + * @return {string} + * @method + * @private + */ +const STATEMENT_FOCAL_POINT_OFF = (): string => +{ + return "float t = length(coord);"; +}; + +/** + * @param {number} index + * @param {boolean} has_focal_point + * @return {string} + * @method + * @private + */ +const STATEMENT_GRADIENT_TYPE_RADIAL = (index: number, has_focal_point: boolean): string => +{ + const focalPointStatement = has_focal_point + ? STATEMENT_FOCAL_POINT_ON(index) + : STATEMENT_FOCAL_POINT_OFF(); + + return ` + float radius = u_highp[${index}][0]; + vec2 coord = p / radius; + ${focalPointStatement} +`; +}; + +/** + * @param {number} index + * @return {string} + * @method + * @private + */ +const STATEMENT_GRADIENT_TYPE_LINEAR = (index: number): string => +{ + return ` + vec2 a = u_highp[${index}].xy; + vec2 b = u_highp[${index}].zw; + + vec2 ab = b - a; + vec2 ap = p - a; + + float t = dot(ab, ap) / dot(ab, ab);`; +}; + +/** + * @param {number} highp_length + * @param {number} fragment_index + * @param {boolean} is_radial + * @param {boolean} has_focal_point + * @param {number} spread_method + * @return {string} + * @method + * @private + */ +export const GRADIENT_TEMPLATE = ( + highp_length: number, + fragment_index: number, + is_radial: boolean, + has_focal_point: boolean, + spread_method: number +): string => { + + const gradientTypeStatement = is_radial + ? STATEMENT_GRADIENT_TYPE_RADIAL(fragment_index, has_focal_point) + : STATEMENT_GRADIENT_TYPE_LINEAR(fragment_index); + + let spread_methodExpression: string; + switch (spread_method) { + + case 0: + spread_methodExpression = "1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)"; + break; + + case 1: + spread_methodExpression = "fract(t)"; + break; + + default: + spread_methodExpression = "clamp(t, 0.0, 1.0)"; + break; + + } + + return `#version 300 es +precision highp float; + +uniform sampler2D u_texture; +uniform vec4 u_highp[${highp_length}]; + +in vec2 v_uv; +out vec4 o_color; + +void main() { + vec2 p = v_uv; + ${gradientTypeStatement} + t = ${spread_methodExpression}; + o_color = texture(u_texture, vec2(t, 0.5)); +}`; +}; diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradientLUT.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradientLUT.ts new file mode 100644 index 00000000..9d6c6d35 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceGradientLUT.ts @@ -0,0 +1,62 @@ +/** + * @description グラデーションのLUTのフラグメントシェーダーソース + * Fragment shader source of gradient LUT + * + * @param {number} mediump_length + * @param {number} stops_length + * @param {number} is_linear_space + * @return {string} + * @method + * @protected + */ +export const GRADIENT_LUT_TEMPLATE = ( + mediump_length: number, + stops_length: number, + is_linear_space: boolean +): string => { + + let loopStatement: string = ""; + for (let idx = 1; idx < stops_length; ++idx) { + + const i0: number = idx - 1; + const i1: number = idx; + const t0: string = `u_mediump[${stops_length + Math.floor(i0 / 4)}][${i0 % 4}]`; + const t1: string = `u_mediump[${stops_length + Math.floor(i1 / 4)}][${i1 % 4}]`; + const c0: string = `u_mediump[${i0}]`; + const c1: string = `u_mediump[${i1}]`; + + loopStatement += ` + if (t <= ${t1}) { + return mix(${c0}, ${c1}, (t - ${t0}) / (${t1} - ${t0})); + } +`; + } + + const colorSpaceStatement: string = is_linear_space + ? "color = pow(color, vec4(0.45454545));" + : ""; + + return `#version 300 es +precision mediump float; + +uniform vec4 u_mediump[${mediump_length}]; + +in vec2 v_coord; +out vec4 o_color; + +vec4 getGradientColor(in float t) { + if (t <= u_mediump[${stops_length}][0]) { + return u_mediump[0]; + } + ${loopStatement} + return u_mediump[${stops_length - 1}]; +} + +void main() { + vec4 color = getGradientColor(v_coord.x); + ${colorSpaceStatement} + color.rgb *= color.a; + + o_color = color; +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Fragment/FragmentShaderSourceTexture.ts b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceTexture.ts new file mode 100644 index 00000000..70e435f5 --- /dev/null +++ b/packages/webgl/src/Shader/Fragment/FragmentShaderSourceTexture.ts @@ -0,0 +1,65 @@ +import { STATEMENT_COLOR_TRANSFORM_ON } from "./FragmentShaderLibrary"; + +/** + * @param {boolean} with_color_transform + * @return {string} + * @method + * @static + */ +export const TEXTURE = (with_color_transform: boolean): string => +{ + const colorTransformUniform = with_color_transform + ? "uniform vec4 u_mediump[2];" + : ""; + + const colorTransformStatement = with_color_transform + ? STATEMENT_COLOR_TRANSFORM_ON(0) + : ""; + + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; +${colorTransformUniform} + +in vec2 v_coord; +out vec4 o_color; + +void main() { + vec4 src = texture(u_texture, v_coord); + ${colorTransformStatement} + o_color = src; +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const INSTANCE_TEXTURE = (): string => +{ + return `#version 300 es +precision mediump float; + +uniform sampler2D u_texture; + +in vec4 v_mul; +in vec4 v_add; +in vec2 v_coord; +out vec4 o_color; + +void main() { + vec4 src = texture(u_texture, v_coord); + + if (v_mul.x != 1.0 || v_mul.y != 1.0 || v_mul.z != 1.0 || v_mul.w != 1.0 + || v_add.x != 0.0 || v_add.y != 0.0 || v_add.z != 0.0 || v_add.w != 0.0 + ) { + src.rgb /= max(0.0001, src.a); + src = clamp(src * v_mul + v_add, 0.0, 1.0); + src.rgb *= src.a; + } + + o_color = src; +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator.ts b/packages/webgl/src/Shader/GradientLUTGenerator.ts new file mode 100644 index 00000000..a5f20365 --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator.ts @@ -0,0 +1,72 @@ +import type { IAttachmentObject } from "../interface/IAttachmentObject"; +import { execute as frameBufferManagerGetAttachmentObjectUseCase } from "../FrameBufferManager/usecase/FrameBufferManagerGetAttachmentObjectUseCase"; + +/** + * @type {IAttachmentObject | null} + * @private + */ +let $gradientAttachmentObject: IAttachmentObject | null = null; + +/** + * @return {IAttachmentObject} + * @method + * @protected + */ +export const $getGradientAttachmentObject = (): IAttachmentObject => +{ + if (!$gradientAttachmentObject) { + $gradientAttachmentObject = frameBufferManagerGetAttachmentObjectUseCase(512, 1, false); + } + return $gradientAttachmentObject as NonNullable; +}; + +/** + * @type {number} + * @private + */ +let $maxLength: number = 0; + +/** + * @description 最大長を返却 + * Returns the maximum length + * + * @return {number} + * @method + * @protected + */ +export const $getGradientLUTGeneratorMaxLength = (): number => +{ + return $maxLength; +}; + +/** + * @description 最大長を設定 + * Set the maximum length + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setGradientLUTGeneratorMaxLength = (gl: WebGL2RenderingContext): void => +{ + $maxLength = Math.floor(gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS) * 0.75); +}; + +/** + * @type {Float32Array} + * @private + */ +export const $rgbToLinearTable: Float32Array = new Float32Array(256); + +/** + * @type {Float32Array} + * @private + */ +export const $rgbIdentityTable: Float32Array = new Float32Array(256); + +for (let idx = 0; idx < 256; ++idx) { + const t = idx / 255; + $rgbToLinearTable[idx] = Math.pow(t, 2.23333333); + $rgbIdentityTable[idx] = t; +} \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetFilterUniformService.ts b/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetFilterUniformService.ts new file mode 100644 index 00000000..2636b64c --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetFilterUniformService.ts @@ -0,0 +1,44 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description グラデーションフィルターのLUTのuniform変数を設定します。 + * Set the uniform variables of the gradient filter LUT. + * + * @param {ShaderManager} shader_manager + * @param {Float32Array} ratios + * @param {Float32Array} colors + * @param {Float32Array}alphas + * @param {number} begin + * @param {number} end + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + ratios: Float32Array, + colors: Float32Array, + alphas: Float32Array, + begin: number, + end: number +): void => { + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_gradient_color + let i = 0; + for (let j = begin; j < end; j++) { + + const color = colors[j]; + + mediump[i++] = (color >> 16) / 255; + mediump[i++] = (color >> 8 & 0xff) / 255; + mediump[i++] = (color & 0xff) / 255; + mediump[i++] = alphas[j]; + } + + // fragment: u_gradient_t + for (let j = begin; j < end; j++) { + mediump[i++] = ratios[j] / 255; + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetUniformService.ts b/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetUniformService.ts new file mode 100644 index 00000000..bc56bf96 --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator/service/GradientLUTSetUniformService.ts @@ -0,0 +1,43 @@ +import { ShaderManager } from "../../ShaderManager"; + +/** + * @description グラデーションLUTのuniform変数を設定します。 + * Set the uniform variables of the gradient LUT. + * + * @param {ShaderManager} shader_manager + * @param {array} stops + * @param {number} begin + * @param {number} end + * @param {Float32Array} table + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + stops: number[], + begin: number, + end: number, + table: Float32Array +): void => { + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + let index = 0; + + // fragment: u_gradient_color + for (let idx = begin; idx < end; ++idx) { + + const position = idx * 5; + + mediump[index++] = table[stops[position + 1]]; + mediump[index++] = table[stops[position + 2]]; + mediump[index++] = table[stops[position + 3]]; + mediump[index++] = table[stops[position + 4]]; + } + + // fragment: u_gradient_t + for (let idx = begin; idx < end; ++idx) { + mediump[index++] = stops[idx * 5]; + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateFilterTextureUseCase.ts b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateFilterTextureUseCase.ts new file mode 100644 index 00000000..9444587b --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateFilterTextureUseCase.ts @@ -0,0 +1,62 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { $context } from "../../../WebGLUtil"; +import { execute as blendOneZeroService } from "../../../Blend/service/BlendOneZeroService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as variantsGradientLUTShaderService } from "../../Variants/GradientLUT/service/VariantsGradientLUTShaderService"; +import { execute as gradientLUTSetFilterUniformService } from "../../GradientLUTGenerator/service/GradientLUTSetFilterUniformService"; +import { execute as gradientLUTGeneratorFillTextureUseCase } from "./GradientLUTGeneratorFillTextureUseCase"; +import { + $getGradientAttachmentObject, + $getGradientLUTGeneratorMaxLength +} from "../../GradientLUTGenerator"; + +/** + * @description グラデーションフィルター用のテクスチャを生成します。 + * Generates a texture for the gradient filter. + * + * @param {Float32Array} ratios + * @param {Float32Array} colors + * @param {Float32Array} alphas + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + ratios: Float32Array, + colors: Float32Array, + alphas: Float32Array +): ITextureObject => { + + const currentAttachment = $context.currentAttachmentObject; + + const gradientAttachmentObject = $getGradientAttachmentObject(); + $context.bind(gradientAttachmentObject); + + const stopsLength = ratios.length; + blendOneZeroService(); + + const maxLength = $getGradientLUTGeneratorMaxLength(); + for (let begin = 0; begin < stopsLength; begin += maxLength - 1) { + + const end = Math.min(begin + maxLength, stopsLength); + + const shaderManager = variantsGradientLUTShaderService( + end - begin, false + ); + + gradientLUTSetFilterUniformService( + shaderManager, ratios, colors, alphas, begin, end + ); + + gradientLUTGeneratorFillTextureUseCase( + shaderManager, ratios[begin] / 255, ratios[end - 1] / 255 + ); + } + blendResetService(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + + return gradientAttachmentObject.texture as ITextureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateShapeTextureUseCase.ts b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateShapeTextureUseCase.ts new file mode 100644 index 00000000..87c1d9e0 --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGenerateShapeTextureUseCase.ts @@ -0,0 +1,77 @@ +import type { ITextureObject } from "../../../interface/ITextureObject"; +import { execute as variantsGradientLUTShaderService } from "../../Variants/GradientLUT/service/VariantsGradientLUTShaderService"; +import { execute as blendOneZeroService } from "../../../Blend/service/BlendOneZeroService"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; +import { execute as gradientLUTSetUniformService } from "../service/GradientLUTSetUniformService"; +import { execute as gradientLUTGeneratorFillTextureUseCase } from "./GradientLUTGeneratorFillTextureUseCase"; +import { execute as contextBeginNodeRenderingService } from "../../../Context/service/ContextBeginNodeRenderingService"; +import { + $gl, + $context +} from "../../../WebGLUtil"; +import { + $getGradientAttachmentObject, + $getGradientLUTGeneratorMaxLength, + $rgbIdentityTable, + $rgbToLinearTable +} from "../../GradientLUTGenerator"; + +/** + * @description グラデーションのテクスチャを生成します。 + * Generates a texture of the gradient. + * + * @param {array} stops + * @param {number} interpolation + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (stops: number[], interpolation: number): ITextureObject => +{ + const currentAttachment = $context.currentAttachmentObject; + const scissorBox = $gl.getParameter($gl.SCISSOR_BOX); + $gl.disable($gl.SCISSOR_TEST); + + const gradientAttachmentObject = $getGradientAttachmentObject(); + $context.bind(gradientAttachmentObject); + + const isLinearSpace = interpolation === 0; + const stopsLength = stops.length / 5; + + const table: Float32Array = isLinearSpace + ? $rgbToLinearTable + : $rgbIdentityTable; + + blendOneZeroService(); + + const maxLength = $getGradientLUTGeneratorMaxLength(); + for (let begin = 0; begin < stopsLength; begin += maxLength - 1) { + + const end: number = Math.min(begin + maxLength, stopsLength); + + const shaderManager = variantsGradientLUTShaderService( + end - begin, isLinearSpace + ); + + gradientLUTSetUniformService( + shaderManager, stops, begin, end, table + ); + + gradientLUTGeneratorFillTextureUseCase( + shaderManager, + stops[0], + stops[stops.length - 5] + ); + } + blendResetService(); + + if (currentAttachment) { + $context.bind(currentAttachment); + } + + contextBeginNodeRenderingService( + scissorBox[0], scissorBox[1], scissorBox[2], scissorBox[3] + ); + + return gradientAttachmentObject.texture as NonNullable; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGeneratorFillTextureUseCase.ts b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGeneratorFillTextureUseCase.ts new file mode 100644 index 00000000..6e76e70c --- /dev/null +++ b/packages/webgl/src/Shader/GradientLUTGenerator/usecase/GradientLUTGeneratorFillTextureUseCase.ts @@ -0,0 +1,31 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { $gl } from "../../../WebGLUtil"; +import { execute as vertexArrayObjectGetGradientObjectUseCase } from "../../../VertexArrayObject/usecase/VertexArrayObjectGetGradientObjectUseCase"; +import { execute as vertexArrayObjectBindService } from "../../../VertexArrayObject/service/VertexArrayObjectBindService"; + +/** + * @description グラデーションのtextureの描画を実行 + * Execute drawing of gradient texture + * + * @param {ShaderManager} shader_manager + * @param {number} begin + * @param {number} end + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + begin: number, + end: number +): void => { + + shader_manager.useProgram(); + shader_manager.bindUniform(); + + vertexArrayObjectBindService( + vertexArrayObjectGetGradientObjectUseCase(begin, end) + ); + + $gl.drawArrays($gl.TRIANGLE_STRIP, 0, 4); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderInstancedManager.ts b/packages/webgl/src/Shader/ShaderInstancedManager.ts new file mode 100644 index 00000000..69faf345 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderInstancedManager.ts @@ -0,0 +1,44 @@ +import { ShaderManager } from "./ShaderManager"; +import { renderQueue } from "@next2d/render-queue"; + +/** + * @class + * @extends ShaderManager + */ +export class ShaderInstancedManager extends ShaderManager +{ + /** + * @description attribute変数の数 + * Number of attribute variables + * + * @type {number} + * @public + */ + public count: number; + + /** + * @param {string} vertex_source + * @param {string} fragment_source + * @param {boolean} [atlas=true] + * @constructor + * @public + */ + constructor (vertex_source: string, fragment_source: string, atlas: boolean = true) + { + super(vertex_source, fragment_source, atlas); + this.count = 0; + } + + /** + * @description attributeの配列を初期化します。 + * Initialize the array of attributes. + * + * @return {void} + * @method + * @public + */ + clear (): void + { + this.count = renderQueue.offset = 0; + } +} \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderInstancedManager/usecase/ShaderInstancedManagerDrawArraysInstancedUseCase.ts b/packages/webgl/src/Shader/ShaderInstancedManager/usecase/ShaderInstancedManagerDrawArraysInstancedUseCase.ts new file mode 100644 index 00000000..f30895f0 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderInstancedManager/usecase/ShaderInstancedManagerDrawArraysInstancedUseCase.ts @@ -0,0 +1,25 @@ +import type { ShaderInstancedManager } from "../../ShaderInstancedManager"; +import { execute as vertexArrayObjectBindAttributeUseCase } from "../../../VertexArrayObject/usecase/VertexArrayObjectBindAttributeUseCase"; +import { $gl } from "../../../WebGLUtil"; + +/** + * @description インスタンシング描画を実行します。 + * Execute instanced drawing. + * + * @param {ShaderInstancedManager} shader_instanced_manager + * @return {void} + * @method + * @protected + */ +export const execute = (shader_instanced_manager: ShaderInstancedManager): void => +{ + // setup + shader_instanced_manager.useProgram(); + shader_instanced_manager.bindUniform(); + + // bind data + vertexArrayObjectBindAttributeUseCase(); + + // draw + $gl.drawArraysInstanced($gl.TRIANGLES, 0, 6, shader_instanced_manager.count); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager.ts b/packages/webgl/src/Shader/ShaderManager.ts new file mode 100644 index 00000000..4cd8338d --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager.ts @@ -0,0 +1,121 @@ +import type { IProgramObject } from "../interface/IProgramObject"; +import type { IUniformData } from "../interface/IUniformData"; +import { execute as shaderManagerCreateProgramService } from "./ShaderManager/service/ShaderManagerCreateProgramService"; +import { execute as shaderManagerInitializeUniformService } from "./ShaderManager/service/ShaderManagerInitializeUniformService"; +import { execute as shaderManagerUseProgramService } from "./ShaderManager/service/ShaderManagerUseProgramService"; +import { execute as shaderManagerBindUniformService } from "./ShaderManager/service/ShaderManagerBindUniformService"; + +/** + * @description 利用用途に合わせたシェーダークラス + * Shader class tailored to the intended use + * + * @class + * @public + */ +export class ShaderManager +{ + /** + * @description WebGLプログラムオブジェクト + * WebGL program object + * + * @type {IProgramObject} + * @public + */ + private readonly _$programObject: IProgramObject; + + /** + * @description uniform変数のマップ + * Map of uniform variables + * + * @type {Map} + * @private + */ + private readonly _$uniformMap: Map; + + /** + * @param {string} vertex_source + * @param {string} fragment_source + * @param {boolean} [atlas=false] + * @constructor + * @public + */ + constructor (vertex_source: string, fragment_source: string, atlas: boolean = false) + { + this._$programObject = shaderManagerCreateProgramService(vertex_source, fragment_source); + this._$uniformMap = new Map(); + + shaderManagerInitializeUniformService( + this._$programObject.resource, + this._$uniformMap, + atlas + ); + } + + /** + * @description 生成したプログラムを利用します。 + * Use the generated program. + * + * @return {void} + * @method + * @public + */ + useProgram (): void + { + shaderManagerUseProgramService(this._$programObject); + } + + /** + * @description uniform変数をバインドします。 + * Bind uniform variables. + * + * @return {void} + * @method + * @public + */ + bindUniform (): void + { + shaderManagerBindUniformService(this._$uniformMap); + } + + /** + * @description highp uniform変数 + * highp uniform variable + * + * @type {Int32Array | Float32Array} + * @readonly + * @public + */ + get highp (): Int32Array | Float32Array + { + const data = this._$uniformMap.get("u_highp") as NonNullable; + return data.array as Int32Array | Float32Array; + } + + /** + * @description mediump uniform変数 + * mediump uniform variable + * + * @type {Int32Array | Float32Array} + * @readonly + * @public + */ + get mediump (): Int32Array | Float32Array + { + const data = this._$uniformMap.get("u_mediump") as NonNullable; + return data.array as Int32Array | Float32Array; + } + + /** + * @description texture uniform変数 + * texture uniform variable + * + * @type {Int32Array | Float32Array} + * @readonly + * @public + */ + get textures (): Int32Array | Float32Array + { + const data = this._$uniformMap.get("u_textures") as NonNullable; + return data.array as Int32Array | Float32Array; + } +} \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerBindUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerBindUniformService.ts new file mode 100644 index 00000000..3dd0611f --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerBindUniformService.ts @@ -0,0 +1,31 @@ +import type { IUniformData } from "../../../interface/IUniformData"; + +/** + * @description uniform変数をバインドします。 + * Bind uniform variables. + * + * @param {Map} uniform_map + * @return {void} + * @method + * @protected + */ +export const execute = (uniform_map: Map): void => +{ + for (const data of uniform_map.values()) { + + if (data.method === undefined || data.assign === undefined) { + continue; + } + + if (data.assign < 0) { + + data.method(data.array); + + } else if (data.assign > 0) { + + data.assign--; + data.method(data.array); + + } + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.test.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.test.ts new file mode 100644 index 00000000..10437586 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.test.ts @@ -0,0 +1,29 @@ +import { execute } from "./ShaderManagerCreateProgramService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ShaderManagerCreateProgramService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createProgram": vi.fn(() => { return "createProgram" }), + "createShader": vi.fn(() => { return "createShader" }), + "shaderSource": vi.fn(() => { return "shaderSource" }), + "compileShader": vi.fn(() => { return "compileShader" }), + "attachShader": vi.fn(() => { return "attachShader" }), + "linkProgram": vi.fn(() => { return "linkProgram" }), + "detachShader": vi.fn(() => { return "detachShader" }), + "deleteShader": vi.fn(() => { return "deleteShader" }), + } + } + }); + + const programObject = execute("", ""); + expect(programObject.resource).toBe("createProgram"); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.ts new file mode 100644 index 00000000..cfb2e251 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerCreateProgramService.ts @@ -0,0 +1,49 @@ +import type { IProgramObject } from "../../../interface/IProgramObject"; +import { $gl } from "../../../WebGLUtil"; + +/** + * @description シェーダープログラムID + * Shader program ID + * + * @type {number} + * @private + */ +let $programId: number = 0; + +/** + * @description 指定のシェーダープログラムを生成する + * Generates the specified shader program + * + * @param {string} vertex_source + * @param {string} fragment_source + * @return {IProgramObject} + * @method + * @protected + */ +export const execute = (vertex_source: string, fragment_source: string): IProgramObject => +{ + const program: WebGLProgram = $gl.createProgram() as NonNullable; + + const vertexShader: WebGLShader = $gl.createShader($gl.VERTEX_SHADER) as NonNullable; + $gl.shaderSource(vertexShader, vertex_source); + $gl.compileShader(vertexShader); + + const fragmentShader: WebGLShader = $gl.createShader($gl.FRAGMENT_SHADER) as NonNullable; + $gl.shaderSource(fragmentShader, fragment_source); + $gl.compileShader(fragmentShader); + + $gl.attachShader(program, vertexShader); + $gl.attachShader(program, fragmentShader); + $gl.linkProgram(program); + + $gl.detachShader(program, vertexShader); + $gl.detachShader(program, fragmentShader); + + $gl.deleteShader(vertexShader); + $gl.deleteShader(fragmentShader); + + return { + "id": $programId++, + "resource": program + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.test.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.test.ts new file mode 100644 index 00000000..f3501e1c --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.test.ts @@ -0,0 +1,57 @@ +import { execute } from "./ShaderManagerInitializeUniformService"; +import { describe, expect, it, vi } from "vitest"; + +describe("ShaderManagerInitializeUniformService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../../WebGLUtil.ts", async (importOriginal) => + { + let count = 0; + let types = 0; + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "FLOAT_VEC4": 0, + "INT_VEC4": 1, + "SAMPLER_2D": 2, + "uniform4fv": vi.fn(() => { return "uniform4fv" }), + "uniform4iv": vi.fn(() => { return "uniform4iv" }), + "uniform1iv": vi.fn(() => { return "uniform1iv" }), + "getUniformLocation": vi.fn(() => { return 0 }), + "getProgramParameter": vi.fn(() => { return 3 }), + "getActiveUniform": vi.fn(() => + { + return { + "name": `test${count++}`, + "type": types++, + "size": 10 + } + }), + } + } + }); + + const map = new Map(); + expect(map.size).toBe(0); + + execute({}, map); + expect(map.size).toBe(3); + + const test0 = map.get("test0"); + expect(test0.method()).toBe("uniform4fv"); + expect(test0.array.length).toBe(40); + expect(test0.assign).toBe(-1); + + const test1 = map.get("test1"); + expect(test1.method()).toBe("uniform4iv"); + expect(test1.array.length).toBe(40); + expect(test1.assign).toBe(-1); + + const test2 = map.get("test2"); + expect(test2.method()).toBe("uniform1iv"); + expect(test2.array.length).toBe(10); + expect(test2.assign).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.ts new file mode 100644 index 00000000..009b169b --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerInitializeUniformService.ts @@ -0,0 +1,79 @@ +import type { IUniformData } from "../../../interface/IUniformData"; +import { $gl } from "../../../WebGLUtil"; + +/** + * @description uniform変数の初期化 + * Initialize uniform variables + * + * @param {WebGLProgram} program + * @param {Map} map + * @param {boolean} [atlas=false] + * @method + * @protected + */ +export const execute = ( + program: WebGLProgram, + map: Map, + atlas: boolean = false +): void => { + const count = $gl.getProgramParameter(program, $gl.ACTIVE_UNIFORMS); + for (let idx = 0; idx < count; ++idx) { + + const info = $gl.getActiveUniform(program, idx) as NonNullable; + + const name: string = info.name.endsWith("[0]") + ? info.name.slice(0, -3) + : info.name; + + const location = $gl.getUniformLocation(program, name) as NonNullable; + + // WebGLの仕様でuniformのint型のデフォルト値は0に設定されるため、 + // sampler2D(size=1)の値の更新は不要 + if (info.type === $gl.SAMPLER_2D && info.size === 1 && !atlas) { + continue; + } + + const data: IUniformData = {}; + switch (info.type) { + + // uniformの値の設定は、gl.uniform4[fi]v()が最速のため、 + // 可能な限りFloat32Arrayに値をパックして転送するようにする + case $gl.FLOAT_VEC4: + data.method = $gl.uniform4fv.bind($gl, location); + data.array = new Float32Array(4 * info.size); + data.assign = -1; + break; + + case $gl.INT_VEC4: + data.method = $gl.uniform4iv.bind($gl, location); + data.array = new Int32Array(4 * info.size); + data.assign = -1; + break; + + // uniformの値の設定は、programに保持されるため、 + // sampler2Dは一度だけ設定するようにする + case $gl.SAMPLER_2D: + data.method = atlas + ? $gl.uniform1i.bind($gl,location, 3) + : $gl.uniform1iv.bind($gl, location); + data.array = new Int32Array(info.size); + data.assign = 1; + break; + + case $gl.FLOAT: + case $gl.FLOAT_VEC2: + case $gl.FLOAT_VEC3: + case $gl.FLOAT_MAT2: + case $gl.FLOAT_MAT3: + case $gl.FLOAT_MAT4: + case $gl.INT: + case $gl.INT_VEC2: + case $gl.INT_VEC3: + default: + throw new Error("Use gl.FLOAT_VEC4 or gl.INT_VEC4 instead"); + + } + + map.set(name, data); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFillUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFillUniformService.ts new file mode 100644 index 00000000..3580e725 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFillUniformService.ts @@ -0,0 +1,117 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $context, + $inverseMatrix, + $getViewportWidth, + $getViewportHeight +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerのBitmapの塗りのuniform変数を設定します。 + * Set the fill uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @param {Float32Array | null} grid_data + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + width: number, + height: number, + grid_data: Float32Array | null +): void => { + + const highp = shader_manager.highp; + + // vertex: u_matrix + const matrix = $context.$stack[$context.$stack.length - 1]; + highp[0] = matrix[0]; + highp[1] = matrix[1]; + highp[2] = matrix[2]; + + highp[4] = matrix[3]; + highp[5] = matrix[4]; + highp[6] = matrix[5]; + + highp[8] = matrix[6]; + highp[9] = matrix[7]; + highp[10] = matrix[8]; + + // vertex: u_inverse_matrix + const inverseMatrix = $inverseMatrix($context.$matrix); + highp[12] = inverseMatrix[0]; + highp[13] = inverseMatrix[1]; + highp[14] = inverseMatrix[2]; + + highp[16] = inverseMatrix[3]; + highp[17] = inverseMatrix[4]; + highp[18] = inverseMatrix[5]; + + highp[11] = inverseMatrix[6]; + highp[15] = inverseMatrix[7]; + highp[19] = inverseMatrix[8]; + + // vertex: u_viewport + highp[3] = $getViewportWidth(); + highp[7] = $getViewportHeight(); + + if (grid_data) { + // vertex: u_parent_matrix + highp[20] = grid_data[0]; + highp[21] = grid_data[1]; + highp[22] = 0; + + highp[24] = grid_data[2]; + highp[25] = grid_data[3]; + highp[26] = 0; + + highp[28] = grid_data[4]; + highp[29] = grid_data[5]; + highp[30] = 1; + + // vertex: u_ancestor_matrix + highp[32] = grid_data[6]; + highp[33] = grid_data[7]; + highp[34] = 0; + + highp[36] = grid_data[8]; + highp[37] = grid_data[9]; + highp[38] = 0; + + highp[40] = grid_data[10]; + highp[41] = grid_data[11]; + highp[42] = 1; + + // vertex: u_parent_viewport + highp[31] = grid_data[12]; + highp[35] = grid_data[13]; + highp[39] = grid_data[14]; + highp[43] = grid_data[15]; + + // vertex: u_grid_min + highp[44] = grid_data[16]; + highp[45] = grid_data[17]; + highp[46] = grid_data[18]; + highp[47] = grid_data[19]; + + // vertex: u_grid_max + highp[48] = grid_data[20]; + highp[49] = grid_data[21]; + highp[50] = grid_data[22]; + highp[51] = grid_data[23]; + + // vertex: u_offset + highp[52] = grid_data[24]; + highp[53] = grid_data[25]; + } + + const mediump = shader_manager.mediump; + + // fragment: u_uv + mediump[0] = width; + mediump[1] = height; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFilterUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFilterUniformService.ts new file mode 100644 index 00000000..79136743 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBitmapFilterUniformService.ts @@ -0,0 +1,131 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description BitmapFilterのUniformを設定します。 + * Set the Uniform of the BitmapFilter. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @param {number} base_width + * @param {number} base_height + * @param {number} base_offset_x + * @param {number} base_offset_y + * @param {number} blur_width + * @param {number} blur_height + * @param {number} blur_offset_x + * @param {number} blur_offset_y + * @param {boolean} is_glow + * @param {number} strength + * @param {number} color_r1 + * @param {number} color_g1 + * @param {number} color_b1 + * @param {number} color_a1 + * @param {number} color_r2 + * @param {number} color_g2 + * @param {number} color_b2 + * @param {number} color_a2 + * @param {boolean} transforms_base + * @param {boolean} transforms_blur + * @param {boolean} applies_strength + * @param {boolean} is_gradient + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + width: number, + height: number, + base_width: number, + base_height: number, + base_offset_x: number, + base_offset_y: number, + blur_width: number, + blur_height: number, + blur_offset_x: number, + blur_offset_y: number, + is_glow: boolean, + strength: number, + color_r1: number, + color_g1: number, + color_b1: number, + color_a1: number, + color_r2: number, + color_g2: number, + color_b2: number, + color_a2: number, + transforms_base: boolean, + transforms_blur: boolean, + applies_strength: boolean, + is_gradient: boolean +): void => { + + let textures: Int32Array | Float32Array; + + // fragment: u_textures + if (transforms_base) { + + textures = shader_manager.textures; + textures[0] = 0; + textures[1] = 1; + if (is_gradient) { + textures[2] = 2; + } + + } else if (is_gradient) { + + textures = shader_manager.textures; + textures[0] = 0; + textures[1] = 2; + + } + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + let i = 0; + + if (transforms_base) { + // fragment: u_uv_scale + mediump[i++] = width / base_width; + mediump[i++] = height / base_height; + // fragment: u_uv_offset + mediump[i++] = base_offset_x / base_width; + mediump[i++] = (height - base_height - base_offset_y) / base_height; + } + + if (transforms_blur) { + // fragment: u_st_scale + mediump[i++] = width / blur_width; + mediump[i++] = height / blur_height; + // fragment: u_st_offset + mediump[i++] = blur_offset_x / blur_width; + mediump[i++] = (height - blur_height - blur_offset_y) / blur_height; + } + + if (is_gradient) { + // do nothing + } else if (is_glow) { + // fragment: u_color + mediump[i++] = color_r1; + mediump[i++] = color_g1; + mediump[i++] = color_b1; + mediump[i++] = color_a1; + } else { + // fragment: u_highlight_color + mediump[i++] = color_r1; + mediump[i++] = color_g1; + mediump[i++] = color_b1; + mediump[i++] = color_a1; + // fragment: u_shadow_color + mediump[i++] = color_r2; + mediump[i++] = color_g2; + mediump[i++] = color_b2; + mediump[i++] = color_a2; + } + + if (applies_strength) { + // fragment: u_strength + mediump[i] = strength; + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendUniformService.ts new file mode 100644 index 00000000..ec2ff0b2 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendUniformService.ts @@ -0,0 +1,17 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description ShaderManagerのブレンドモードののuniform変数を設定します。 + * Set the uniform variable of the blend mode of ShaderManager. + * + * @param {ShaderManager} shader_manager + * @return {void} + * @method + * @protected + */ +export const execute = (shader_manager: ShaderManager): void => +{ + const textures: Int32Array | Float32Array = shader_manager.textures; + textures[0] = 0; + textures[1] = 1; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendWithColorTransformUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendWithColorTransformUniformService.ts new file mode 100644 index 00000000..5b3aca0d --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlendWithColorTransformUniformService.ts @@ -0,0 +1,43 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description ShaderManagerのブレンドモードののuniform変数を設定します。 + * Set the uniform variable of the blend mode of ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {number} ct0 + * @param {number} ct1 + * @param {number} ct2 + * @param {number} ct3 + * @param {number} ct4 + * @param {number} ct5 + * @param {number} ct6 + * @param {number} ct7 + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + ct0: number, ct1: number, ct2: number, ct3: number, + ct4: number, ct5: number, ct6: number, ct7: number +): void => { + + const textures: Int32Array | Float32Array = shader_manager.textures; + textures[0] = 0; + textures[1] = 1; + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_color_transform_mul + mediump[0] = ct0; + mediump[1] = ct1; + mediump[2] = ct2; + mediump[3] = ct3; + + // fragment: u_color_transform_add + mediump[4] = ct4; + mediump[5] = ct5; + mediump[6] = ct6; + mediump[7] = ct7; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlurFilterUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlurFilterUniformService.ts new file mode 100644 index 00000000..5cb7c409 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetBlurFilterUniformService.ts @@ -0,0 +1,42 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description ぼかしフィルターのUniformを設定します。 + * Set the Uniform of the blur filter. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @param {boolean} is_horizontal + * @param {number} fraction + * @param {number} samples + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + width: number, + height: number, + is_horizontal: boolean, + fraction: number, + samples: number +): void => { + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_offset + if (is_horizontal) { + mediump[0] = 1 / width; + mediump[1] = 0; + } else { + mediump[0] = 0; + mediump[1] = 1 / height; + } + + // fragment: u_fraction + mediump[2] = fraction; + + // fragment: u_samples + mediump[3] = samples; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetColorMatrixFilterUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetColorMatrixFilterUniformService.ts new file mode 100644 index 00000000..9fd51908 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetColorMatrixFilterUniformService.ts @@ -0,0 +1,46 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description カラーマトリックスフィルターのUniformを設定します。 + * Set the Uniform of the color matrix filter. + * + * @param {ShaderManager} shader_manager + * @param {Float32Array} matrix + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + matrix: Float32Array +): void => { + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_mul + mediump[0] = matrix[0]; + mediump[1] = matrix[1]; + mediump[2] = matrix[2]; + mediump[3] = matrix[3]; + + mediump[4] = matrix[5]; + mediump[5] = matrix[6]; + mediump[6] = matrix[7]; + mediump[7] = matrix[8]; + + mediump[8] = matrix[10]; + mediump[9] = matrix[11]; + mediump[10] = matrix[12]; + mediump[11] = matrix[13]; + + mediump[12] = matrix[15]; + mediump[13] = matrix[16]; + mediump[14] = matrix[17]; + mediump[15] = matrix[18]; + + // fragment: u_add + mediump[16] = matrix[4] / 255; + mediump[17] = matrix[9] / 255; + mediump[18] = matrix[14] / 255; + mediump[19] = matrix[19] / 255; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetConvolutionFilterUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetConvolutionFilterUniformService.ts new file mode 100644 index 00000000..a0917ce9 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetConvolutionFilterUniformService.ts @@ -0,0 +1,62 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description カラーマトリックスフィルターのUniformを設定します。 + * Set the Uniform of the color matrix filter. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @param {Float32Array} matrix + * @param {number} divisor + * @param {number} bias + * @param {boolean} clamp + * @param {number} color_r + * @param {number} color_g + * @param {number} color_b + * @param {number} color_a + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + width: number, + height: number, + matrix: Float32Array, + divisor: number, + bias: number, + clamp: boolean, + color_r: number, + color_g: number, + color_b: number, + color_a: number +): void => { + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_rcp_size + mediump[0] = 1 / width; + mediump[1] = 1 / height; + + // fragment: u_rcp_divisor + mediump[2] = 1 / divisor; + + // fragment: u_bias + mediump[3] = bias / 255; + + let i = 4; + if (!clamp) { + // fragment: u_substitute_color + mediump[i++] = color_r; + mediump[i++] = color_g; + mediump[i++] = color_b; + mediump[i++] = color_a; + } + + // fragment: u_matrix + const length = matrix.length; + for (let j = 0; j < length; j++) { + mediump[i++] = matrix[j]; + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetDisplacementMapFilterUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetDisplacementMapFilterUniformService.ts new file mode 100644 index 00000000..0bd064cc --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetDisplacementMapFilterUniformService.ts @@ -0,0 +1,66 @@ +import type { ShaderManager } from "../../ShaderManager"; + +/** + * @description ディスプレイスメントマップ・フィルタのユニフォームを設定する + * Set the uniform of the displacement map filter + * + * @param {ShaderManager} shader_manager + * @param {number} map_width + * @param {number} map_height + * @param {number} base_width + * @param {number} base_height + * @param {number} point_x + * @param {number} point_y + * @param {number} scale_x + * @param {number} scale_y + * @param {number} mode + * @param {number} color_r + * @param {number} color_g + * @param {number} color_b + * @param {number} color_a + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + map_width: number, + map_height: number, + base_width: number, + base_height: number, + point_x: number, + point_y: number, + scale_x: number, + scale_y: number, + mode: number, + color_r: number, + color_g: number, + color_b: number, + color_a: number +): void => { + + const textures: Int32Array | Float32Array = shader_manager.textures; + textures[0] = 0; + textures[1] = 1; + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + + // fragment: u_uv_to_st_scale + mediump[0] = base_width / map_width; + mediump[1] = base_height / map_height; + // fragment: u_uv_to_st_offset + mediump[2] = point_x / map_width; + mediump[3] = (base_height - map_height - point_y) / map_height; + + // fragment: u_scale + mediump[4] = scale_x / base_width; + mediump[5] = -scale_y / base_height; + + if (mode === 1) { + // fragment: u_substitute_color + mediump[8] = color_r; + mediump[9] = color_g; + mediump[10] = color_b; + mediump[11] = color_a; + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetFillUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetFillUniformService.ts new file mode 100644 index 00000000..1fb30f4f --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetFillUniformService.ts @@ -0,0 +1,75 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $getViewportWidth, + $getViewportHeight +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerの塗りのuniform変数を設定します。 + * Set the fill uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {Float32Array} grid_data + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + grid_data: Float32Array +): void => { + + const highp = shader_manager.highp; + + // vertex: u_parent_matrix + highp[0] = grid_data[0]; + highp[1] = grid_data[1]; + highp[2] = 0; + + highp[4] = grid_data[2]; + highp[5] = grid_data[3]; + highp[6] = 0; + + highp[8] = grid_data[4]; + highp[9] = grid_data[5]; + highp[10] = 1; + + // vertex: u_ancestor_matrix + highp[12] = grid_data[6]; + highp[13] = grid_data[7]; + highp[14] = 0; + + highp[16] = grid_data[8]; + highp[17] = grid_data[9]; + highp[18] = 0; + + highp[20] = grid_data[10]; + highp[21] = grid_data[11]; + highp[22] = 1; + + // vertex: u_viewport + highp[3] = $getViewportWidth(); + highp[7] = $getViewportHeight(); + + // vertex: u_parent_viewport + highp[11] = grid_data[12]; + highp[15] = grid_data[13]; + highp[19] = grid_data[14]; + highp[23] = grid_data[15]; + + // vertex: u_grid_min + highp[24] = grid_data[16]; + highp[25] = grid_data[17]; + highp[26] = grid_data[18]; + highp[27] = grid_data[19]; + + // vertex: u_grid_max + highp[28] = grid_data[20]; + highp[29] = grid_data[21]; + highp[30] = grid_data[22]; + highp[31] = grid_data[23]; + + // vertex: u_offset + highp[32] = grid_data[24]; + highp[33] = grid_data[25]; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetGradientFillUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetGradientFillUniformService.ts new file mode 100644 index 00000000..dcdc966f --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetGradientFillUniformService.ts @@ -0,0 +1,130 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $getViewportWidth, + $getViewportHeight, + $clamp +} from "../../../WebGLUtil"; + +/** + * @description 塗りのグラデーションのuniformを設定 + * Set fill gradient uniform + * + * @param {ShaderManager} shader_manager + * @param {number} type + * @param {Float32Array} matrix + * @param {Float32Array} inverse_matrix + * @param {number} [focal_point_ratio=0] + * @param {Float32Array} [points=null] + * @param {Float32Array | null} [grid_data=null] + * @return {void} + * @method + * @public + */ +export const execute = ( + shader_manager: ShaderManager, + type: number, + matrix: Float32Array, + inverse_matrix: Float32Array, + focal_point_ratio: number = 0, + points: Float32Array | null = null, + grid_data: Float32Array | null = null +): void => { + + const highp: Float32Array | Int32Array = shader_manager.highp; + + // vertex: u_matrix + highp[0] = matrix[0]; + highp[1] = matrix[1]; + highp[2] = matrix[2]; + + highp[4] = matrix[3]; + highp[5] = matrix[4]; + highp[6] = matrix[5]; + + highp[8] = matrix[6]; + highp[9] = matrix[7]; + highp[10] = matrix[8]; + + // vertex: u_inverse_matrix + highp[12] = inverse_matrix[0]; + highp[13] = inverse_matrix[1]; + highp[14] = inverse_matrix[2]; + + highp[16] = inverse_matrix[3]; + highp[17] = inverse_matrix[4]; + highp[18] = inverse_matrix[5]; + + highp[11] = inverse_matrix[6]; + highp[15] = inverse_matrix[7]; + highp[19] = inverse_matrix[8]; + + // vertex: u_viewport + highp[3] = $getViewportWidth(); + highp[7] = $getViewportHeight(); + + let index = 20; + if (grid_data) { + // vertex: u_parent_matrix + highp[20] = grid_data[0]; + highp[21] = grid_data[1]; + highp[22] = 0; + + highp[24] = grid_data[2]; + highp[25] = grid_data[3]; + highp[26] = 0; + + highp[28] = grid_data[4]; + highp[29] = grid_data[5]; + highp[30] = 1; + + // vertex: u_ancestor_matrix + highp[32] = grid_data[6]; + highp[33] = grid_data[7]; + highp[34] = 0; + + highp[36] = grid_data[8]; + highp[37] = grid_data[9]; + highp[38] = 0; + + highp[40] = grid_data[10]; + highp[41] = grid_data[11]; + highp[42] = 1; + + // vertex: u_parent_viewport + highp[31] = grid_data[12]; + highp[35] = grid_data[13]; + highp[39] = grid_data[14]; + highp[43] = grid_data[15]; + + // vertex: u_grid_min + highp[44] = grid_data[16]; + highp[45] = grid_data[17]; + highp[46] = grid_data[18]; + highp[47] = grid_data[19]; + + // vertex: u_grid_max + highp[48] = grid_data[20]; + highp[49] = grid_data[21]; + highp[50] = grid_data[22]; + highp[51] = grid_data[23]; + + // vertex: u_offset + highp[52] = grid_data[24]; + highp[53] = grid_data[25]; + + index = 56; + } + + if (type === 0) { + // fragment: u_linear_points + highp[index++] = (points as Float32Array)[0]; + highp[index++] = (points as Float32Array)[1]; + highp[index++] = (points as Float32Array)[2]; + highp[index] = (points as Float32Array)[3]; + } else { + // fragment: u_radial_point + highp[index++] = 819.2; + // fragment: u_focal_point_ratio + highp[index] = $clamp(focal_point_ratio, -0.975, 0.975, 0); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMaskUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMaskUniformService.ts new file mode 100644 index 00000000..2067b011 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMaskUniformService.ts @@ -0,0 +1,74 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $getViewportWidth, + $getViewportHeight +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerのマスクのuniform変数を設定します。 + * Set the mask uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + grid_data: Float32Array +): void => { + + const highp = shader_manager.highp; + + // vertex: u_parent_matrix + highp[0] = grid_data[0]; + highp[1] = grid_data[1]; + highp[2] = 0; + + highp[4] = grid_data[2]; + highp[5] = grid_data[3]; + highp[6] = 0; + + highp[8] = grid_data[4]; + highp[9] = grid_data[5]; + highp[10] = 1; + + // vertex: u_ancestor_matrix + highp[12] = grid_data[6]; + highp[13] = grid_data[7]; + highp[14] = 0; + + highp[16] = grid_data[8]; + highp[17] = grid_data[9]; + highp[18] = 0; + + highp[20] = grid_data[10]; + highp[21] = grid_data[11]; + highp[22] = 1; + + // vertex: u_viewport + highp[3] = $getViewportWidth(); + highp[7] = $getViewportHeight(); + + // vertex: u_parent_viewport + highp[11] = grid_data[12]; + highp[15] = grid_data[13]; + highp[19] = grid_data[14]; + highp[23] = grid_data[15]; + + // vertex: u_grid_min + highp[24] = grid_data[16]; + highp[25] = grid_data[17]; + highp[26] = grid_data[18]; + highp[27] = grid_data[19]; + + // vertex: u_grid_max + highp[28] = grid_data[20]; + highp[29] = grid_data[21]; + highp[30] = grid_data[22]; + highp[31] = grid_data[23]; + + // vertex: u_offset + highp[32] = grid_data[24]; + highp[33] = grid_data[25]; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService.ts new file mode 100644 index 00000000..71542237 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureUniformService.ts @@ -0,0 +1,42 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $context, + $getViewportHeight, + $getViewportWidth +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerのTextureのuniform変数を設定します。 + * Set the Texture uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + width: number, height: number +): void => { + + const highp = shader_manager.highp; + const matrix = $context.$matrix; + + // vertex: u_offset + highp[0] = matrix[0]; // a + highp[1] = matrix[1]; // b + highp[2] = matrix[3]; // c + highp[3] = matrix[4]; // d + highp[4] = matrix[6]; // tx + highp[5] = matrix[7]; // ty + + // vertex: u_size + highp[6] = width; + highp[7] = height; + + // vertex: u_viewport + highp[8] = $getViewportWidth(); + highp[9] = $getViewportHeight(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureWithColorTransformUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureWithColorTransformUniformService.ts new file mode 100644 index 00000000..5f927dac --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetMatrixTextureWithColorTransformUniformService.ts @@ -0,0 +1,53 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $context, + $getViewportHeight, + $getViewportWidth +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerのTextureのuniform変数を設定します。 + * Set the Texture uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + color_transform: Float32Array, + width: number, height: number +): void => { + + const highp = shader_manager.highp; + const matrix = $context.$matrix; + + // vertex: u_offset + highp[0] = matrix[0]; // a + highp[1] = matrix[1]; // b + highp[2] = matrix[3]; // c + highp[3] = matrix[4]; // d + highp[4] = matrix[6]; // tx + highp[5] = matrix[7]; // ty + + // vertex: u_size + highp[6] = width; + highp[7] = height; + + // vertex: u_viewport + highp[8] = $getViewportWidth(); + highp[9] = $getViewportHeight(); + + const mediump: Int32Array | Float32Array = shader_manager.mediump; + mediump[0] = color_transform[0]; + mediump[1] = color_transform[1]; + mediump[2] = color_transform[2]; + mediump[3] = color_transform[3]; + mediump[4] = color_transform[4]; + mediump[5] = color_transform[5]; + mediump[6] = color_transform[6]; + mediump[7] = color_transform[7]; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetTextureUniformService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetTextureUniformService.ts new file mode 100644 index 00000000..db47b24e --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerSetTextureUniformService.ts @@ -0,0 +1,35 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { + $context, + $getViewportHeight, + $getViewportWidth +} from "../../../WebGLUtil"; + +/** + * @description ShaderManagerのTextureのuniform変数を設定します。 + * Set the Texture uniform variables of the ShaderManager. + * + * @param {ShaderManager} shader_manager + * @param {number} width + * @param {number} height + * @return {void} + * @method + * @protected + */ +export const execute = (shader_manager: ShaderManager, width: number, height: number): void => +{ + const highp = shader_manager.highp; + const matrix = $context.$matrix; + + // vertex: u_offset + highp[0] = matrix[6]; // x + highp[1] = matrix[7]; // y + + // vertex: u_size + highp[2] = width; + highp[3] = height; + + // vertex: u_viewport + highp[4] = $getViewportWidth(); + highp[5] = $getViewportHeight(); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerUseProgramService.ts b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerUseProgramService.ts new file mode 100644 index 00000000..944f4acb --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/service/ShaderManagerUseProgramService.ts @@ -0,0 +1,30 @@ +import { IProgramObject } from "../../../interface/IProgramObject"; +import { $gl } from "../../../WebGLUtil"; + +/** + * @description 現在利用中のプログラムID + * Currently used program ID + * + * @type {number} + * @private + */ +let $currentProgramId: number = -1; + +/** + * @description ProgramObjectのプログラムの利用を開始します。 + * Start using the program of the ProgramObject. + * + * @param {IProgramObject} program_object + * @return {void} + * @method + * @protected + */ +export const execute = (program_object: IProgramObject): void => +{ + if ($currentProgramId === program_object.id) { + return ; + } + + $currentProgramId = program_object.id; + $gl.useProgram(program_object.resource); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase.ts b/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase.ts new file mode 100644 index 00000000..866c885c --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerDrawTextureUseCase.ts @@ -0,0 +1,26 @@ +import type { ShaderManager } from "../../ShaderManager"; +import { execute as vertexArrayObjectBindService } from "../../../VertexArrayObject/service/VertexArrayObjectBindService"; +import { $gl } from "../../../WebGLUtil"; +import { $getRectVertexArrayObject } from "../../../VertexArrayObject"; + +/** + * @description Textureの描画を行います。 + * Draw Texture. + * + * @param {ShaderManager} shader_manager + * @return {void} + * @method + * @protected + */ +export const execute = (shader_manager: ShaderManager): void => +{ + // setup + shader_manager.useProgram(); + shader_manager.bindUniform(); + + // bind vertex array + vertexArrayObjectBindService($getRectVertexArrayObject()); + + // draw fill + $gl.drawArrays($gl.TRIANGLES, 0, 6); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerFillUseCase.ts b/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerFillUseCase.ts new file mode 100644 index 00000000..289405e3 --- /dev/null +++ b/packages/webgl/src/Shader/ShaderManager/usecase/ShaderManagerFillUseCase.ts @@ -0,0 +1,36 @@ +import type { ShaderManager } from "../../ShaderManager"; +import type { IVertexArrayObject } from "../../../interface/IVertexArrayObject"; +import { execute as vertexArrayObjectBindService } from "../../../VertexArrayObject/service/VertexArrayObjectBindService"; +import { $gl } from "../../../WebGLUtil"; +import { execute as blendResetService } from "../../../Blend/service/BlendResetService"; + +/** + * @description シェーダーマネージャの塗り実行します。 + * Execute ShaderManager fill. + * + * @param {ShaderManager} shader_manager + * @param {IVertexArrayObject} vertex_array_object + * @return {void} + * @method + * @protected + */ +export const execute = ( + shader_manager: ShaderManager, + vertex_array_object: IVertexArrayObject, + offset: number, + count: number +): void => { + + // setup + shader_manager.useProgram(); + shader_manager.bindUniform(); + + // set alpha + blendResetService(); + + // bind vertex array + vertexArrayObjectBindService(vertex_array_object); + + // draw fill + $gl.drawArrays($gl.TRIANGLES, offset, count); +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Bitmap/service/VariantsBitmapShaderService.ts b/packages/webgl/src/Shader/Variants/Bitmap/service/VariantsBitmapShaderService.ts new file mode 100644 index 00000000..478f93e3 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Bitmap/service/VariantsBitmapShaderService.ts @@ -0,0 +1,42 @@ +import { $collection } from "../../BitmapVariants"; +import { ShaderManager } from "../../../ShaderManager"; +import { FILL_TEMPLATE } from "../../../Vertex/VertexShaderSourceFill"; +import { + BITMAP_PATTERN, + BITMAP_CLIPPED +} from "../../../Fragment/FragmentShaderSource"; + +/** + * @description BitmapShaderを取得 + * Get BitmapShader + * + * @param {boolean} repeat + * @param {boolean} use_grid + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = ( + repeat: boolean, + use_grid: boolean +): ShaderManager => { + + const key = `b${repeat ? "y" : "n"}${use_grid ? "y" : "n"}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const fragmentShaderSource = repeat + ? BITMAP_PATTERN() + : BITMAP_CLIPPED(); + + const shaderManager = new ShaderManager( + FILL_TEMPLATE(use_grid ? 14 : 5, true, false, use_grid), + fragmentShaderSource + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/BitmapVariants.ts b/packages/webgl/src/Shader/Variants/BitmapVariants.ts new file mode 100644 index 00000000..5620d06f --- /dev/null +++ b/packages/webgl/src/Shader/Variants/BitmapVariants.ts @@ -0,0 +1,10 @@ +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description Bitmapのシェーダー管理クラスのコレクション + * Collection of Bitmap shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendDrawShaderService.ts b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendDrawShaderService.ts new file mode 100644 index 00000000..a45a5ea6 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendDrawShaderService.ts @@ -0,0 +1,33 @@ +import type { IBlendMode } from "../../../../interface/IBlendMode"; +import { BLEND_TEMPLATE } from "../../../Fragment/FragmentShaderSourceBlend"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../BlendVariants"; + +/** + * @description Blendのシェーダーを生成して返却 + * Generate and return the shader of Blend + * + * @param {IBlendMode} operation + * @param {boolean} with_color_transform + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (operation: IBlendMode, with_color_transform: boolean): ShaderManager => +{ + const key = `i${operation}${with_color_transform ? "y" : "n"}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + BLEND_TEMPLATE(operation, with_color_transform) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendInstanceShaderService.ts b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendInstanceShaderService.ts new file mode 100644 index 00000000..4dd05d5e --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendInstanceShaderService.ts @@ -0,0 +1,30 @@ +import { INSTANCE_TEXTURE } from "../../../Fragment/FragmentShaderSourceTexture"; +import { ShaderInstancedManager } from "../../../ShaderInstancedManager"; +import { INSTANCE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../BlendVariants"; + +/** + * @description InstancedArrayのシェーダーを生成して返却 + * Generate and return the shader of InstancedArray + * + * @return {ShaderInstancedManager} + * @method + * @protected + */ +export const execute = (): ShaderInstancedManager => +{ + const key = "i"; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderInstancedManager( + INSTANCE_TEMPLATE(), + INSTANCE_TEXTURE() + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService.ts b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService.ts new file mode 100644 index 00000000..1aa44e8f --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendMatrixTextureShaderService.ts @@ -0,0 +1,31 @@ +import { TEXTURE } from "../../../Fragment/FragmentShaderSourceTexture"; +import { ShaderManager } from "../../../ShaderManager"; +import { BLEND_MATRIX_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../BlendVariants"; + +/** + * @description Textureのシェーダーを生成して返却 + * Generate and return the shader of Texture + * + * @param {boolean} [with_color_transform=false] + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (with_color_transform: boolean = false): ShaderManager => +{ + const key = `m${with_color_transform ? "y" : "n"}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + BLEND_MATRIX_TEMPLATE(), + TEXTURE(with_color_transform) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendTextureShaderService.ts b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendTextureShaderService.ts new file mode 100644 index 00000000..817a8a42 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Blend/service/VariantsBlendTextureShaderService.ts @@ -0,0 +1,30 @@ +import { TEXTURE } from "../../../Fragment/FragmentShaderSourceTexture"; +import { ShaderManager } from "../../../ShaderManager"; +import { BLEND_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../BlendVariants"; + +/** + * @description Textureのシェーダーを生成して返却 + * Generate and return the shader of Texture + * + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (): ShaderManager => +{ + const key = "p"; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + BLEND_TEMPLATE(), + TEXTURE(false) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/BlendVariants.ts b/packages/webgl/src/Shader/Variants/BlendVariants.ts new file mode 100644 index 00000000..d19da8ec --- /dev/null +++ b/packages/webgl/src/Shader/Variants/BlendVariants.ts @@ -0,0 +1,11 @@ +import type { ShaderInstancedManager } from "../ShaderInstancedManager"; +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description ブレンドのシェーダー管理クラスのコレクション + * Collection of blend shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Filter/service/VariantsBitmapFilterShaderService.ts b/packages/webgl/src/Shader/Variants/Filter/service/VariantsBitmapFilterShaderService.ts new file mode 100644 index 00000000..88038fea --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Filter/service/VariantsBitmapFilterShaderService.ts @@ -0,0 +1,70 @@ +import { BITMAP_FILTER_TEMPLATE } from "../../../Fragment/Filter/FragmentShaderSourceFilter"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../FilterVariants"; + +/** + * @description BitmapFilterShaderを生成する + * Create BitmapFilterShader + * + * @param {boolean} transforms_base + * @param {boolean} transforms_blur + * @param {boolean} is_glow + * @param {boolean} type + * @param {string} transforms_base + * @param {boolean} knockout + * @param {boolean} applies_strength + * @param {boolean} is_gradient + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = ( + transforms_base: boolean, + transforms_blur: boolean, + is_glow: boolean, + type: string, + knockout: boolean, + applies_strength: boolean, + is_gradient: boolean +): ShaderManager => { + + const key1 = transforms_base ? "y" : "n"; + const key2 = transforms_blur ? "y" : "n"; + const key3 = is_glow ? "y" : "n"; + const key4 = knockout ? "y" : "n"; + const key5 = applies_strength ? "y" : "n"; + const key = `f${key1}${key2}${key3}${type}${key4}${key5}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + let texturesLength = 1; + if (transforms_base) { texturesLength++ } + if (is_gradient) { texturesLength++ } + + let mediumpLength = (transforms_base ? 4 : 0) + + (transforms_blur ? 4 : 0) + + (applies_strength ? 1 : 0); + + if (!is_gradient) { + mediumpLength += is_glow ? 4 : 8; + } + + mediumpLength = Math.ceil(mediumpLength / 4); + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + BITMAP_FILTER_TEMPLATE( + texturesLength, mediumpLength, + transforms_base, transforms_blur, + is_glow, type, knockout, + applies_strength, is_gradient + ) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Filter/service/VariantsBlurFilterShaderService.ts b/packages/webgl/src/Shader/Variants/Filter/service/VariantsBlurFilterShaderService.ts new file mode 100644 index 00000000..0fad14a5 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Filter/service/VariantsBlurFilterShaderService.ts @@ -0,0 +1,31 @@ +import { BLUR_FILTER_TEMPLATE } from "../../../Fragment/Filter/FragmentShaderSourceBlurFilter"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../FilterVariants"; + +/** + * @description BlurFilterShaderを生成する + * Create BlurFilterShader + * + * @param {number} half_blur + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (half_blur: number): ShaderManager => +{ + const key = `b${half_blur}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + BLUR_FILTER_TEMPLATE(half_blur) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Filter/service/VariantsColorMatrixFilterShaderService.ts b/packages/webgl/src/Shader/Variants/Filter/service/VariantsColorMatrixFilterShaderService.ts new file mode 100644 index 00000000..55a079a3 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Filter/service/VariantsColorMatrixFilterShaderService.ts @@ -0,0 +1,30 @@ +import { COLOR_MATRIX_FILTER_TEMPLATE } from "../../../Fragment/Filter/FragmentShaderSourceColorMatrixFilter"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../FilterVariants"; + +/** + * @description ColorMatrixFilterShaderを生成する + * Create ColorMatrixFilterShader + * + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (): ShaderManager => +{ + const key = "m"; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + COLOR_MATRIX_FILTER_TEMPLATE() + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Filter/service/VariantsConvolutionFilterShaderService.ts b/packages/webgl/src/Shader/Variants/Filter/service/VariantsConvolutionFilterShaderService.ts new file mode 100644 index 00000000..71159cbf --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Filter/service/VariantsConvolutionFilterShaderService.ts @@ -0,0 +1,45 @@ +import { CONVOLUTION_FILTER_TEMPLATE } from "../../../Fragment/Filter/FragmentShaderSourceConvolutionFilter"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { $collection } from "../../FilterVariants"; + +/** + * @description ConvolutionFilterShaderを生成する + * Create ConvolutionFilterShader + * + * @param {number} x + * @param {number} y + * @param {boolean} preserve_alpha + * @param {boolean} clamp + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + preserve_alpha: boolean, + clamp: boolean +): ShaderManager => { + + const key1 = ("0" + x).slice(-2); + const key2 = ("0" + y).slice(-2); + const key3 = preserve_alpha ? "y" : "n"; + const key4 = clamp ? "y" : "n"; + const key = `c${key1}${key2}${key3}${key4}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const mediumpLength: number = (clamp ? 1 : 2) + Math.ceil(x * y / 4); + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + CONVOLUTION_FILTER_TEMPLATE(mediumpLength, x, y, preserve_alpha, clamp) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Filter/service/VariantsDisplacementMapFilterShaderService.ts b/packages/webgl/src/Shader/Variants/Filter/service/VariantsDisplacementMapFilterShaderService.ts new file mode 100644 index 00000000..60744f24 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Filter/service/VariantsDisplacementMapFilterShaderService.ts @@ -0,0 +1,36 @@ +import { ShaderManager } from "../../../ShaderManager"; +import { $collection } from "../../FilterVariants"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { DISPLACEMENT_MAP_FILTER_TEMPLATE } from "../../../Fragment/Filter/FragmentShaderSourceDisplacementMapFilter"; + +/** + * @description ディスプレイスメントマップ・フィルタのシェーダーマネージャを取得する + * Get the shader manager of the displacement map filter + * + * @param {number} component_x + * @param {number} component_y + * @param {number} mode + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (component_x: number, component_y: number, mode: number): ShaderManager => +{ + const key = `d${component_x}${component_y}${mode}`; + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const mediumpLength = mode === 1 ? 3 : 2; + + const shaderManager = new ShaderManager( + TEXTURE_TEMPLATE(), + DISPLACEMENT_MAP_FILTER_TEMPLATE( + mediumpLength, component_x, component_y, mode + ) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/FilterVariants.ts b/packages/webgl/src/Shader/Variants/FilterVariants.ts new file mode 100644 index 00000000..91be3b49 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/FilterVariants.ts @@ -0,0 +1,10 @@ +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description フィルターのシェーダー管理クラスのコレクション + * Collection of filter shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.test.ts b/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.test.ts new file mode 100644 index 00000000..63e23d86 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.test.ts @@ -0,0 +1,15 @@ +import { execute } from "./VariantsGradientCreateCollectionKeyService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VariantsGradientCreateCollectionKeyService.js method test", () => +{ + it("test case", () => + { + expect(execute(true, true, true, 0)).toBe("yyy0"); + expect(execute(true, true, true, 1)).toBe("yyy1"); + expect(execute(true, true, false, 0)).toBe("yyn0"); + expect(execute(true, false, false, 0)).toBe("ynn0"); + expect(execute(false, false, false, 0)).toBe("nnn0"); + expect(execute(false, false, false, 0)).toBe("nnn0"); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.ts b/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.ts new file mode 100644 index 00000000..dc6fad34 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Gradient/service/VariantsGradientCreateCollectionKeyService.ts @@ -0,0 +1,26 @@ +/** + * @description グラデーションのマップキーを生成 + * Generates a map key for the gradient + * + * @param {boolean} has_grid + * @param {boolean} is_radial + * @param {boolean} has_focal_point + * @param {number} spread_method + * @return {string} + * @method + * @protected + */ +export const execute = ( + has_grid: boolean, + is_radial: boolean, + has_focal_point: boolean, + spread_method: number +): string => { + + const key1 = has_grid ? "y" : "n"; + const key2 = is_radial ? "y" : "n"; + const key3 = is_radial && has_focal_point ? "y" : "n"; + const key4 = `${spread_method}`; + + return `${key1}${key2}${key3}${key4}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Gradient/usecase/VariantsGradientShapeShaderUseCase.ts b/packages/webgl/src/Shader/Variants/Gradient/usecase/VariantsGradientShapeShaderUseCase.ts new file mode 100644 index 00000000..3293bd63 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Gradient/usecase/VariantsGradientShapeShaderUseCase.ts @@ -0,0 +1,48 @@ +import { ShaderManager } from "../../../ShaderManager"; +import { $collection } from "../../GradientVariants"; +import { execute as variantsGradientCreateCollectionKeyService } from "../service/VariantsGradientCreateCollectionKeyService"; +import { FILL_TEMPLATE } from "../../../Vertex/VertexShaderSourceFill"; +import { GRADIENT_TEMPLATE } from "../../../Fragment/FragmentShaderSourceGradient"; + +/** + * @description グラデーションのシェーダを生成して返却 + * Generate and return the shader of gradient + * + * @param {boolean} is_radial + * @param {boolean} has_focal_point + * @param {number} spread_method + * @param {boolean} use_grid + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = ( + is_radial: boolean, + has_focal_point: boolean, + spread_method: number, + use_grid: boolean +): ShaderManager => { + + const key = variantsGradientCreateCollectionKeyService( + use_grid, is_radial, has_focal_point, spread_method + ); + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const highpLength = (use_grid ? 14 : 5) + 1; + const fragmentIndex = highpLength - 1; + + const shaderManager = new ShaderManager( + FILL_TEMPLATE(highpLength, true, false, use_grid), + GRADIENT_TEMPLATE( + highpLength, fragmentIndex, + is_radial, has_focal_point, spread_method + ) + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/GradientLUT/service/VariantsGradientLUTShaderService.ts b/packages/webgl/src/Shader/Variants/GradientLUT/service/VariantsGradientLUTShaderService.ts new file mode 100644 index 00000000..d6429127 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/GradientLUT/service/VariantsGradientLUTShaderService.ts @@ -0,0 +1,39 @@ +import { $collection } from "../../GradientLUTVariants"; +import { ShaderManager } from "../../../ShaderManager"; +import { TEXTURE_TEMPLATE } from "../../../Vertex/VertexShaderSource"; +import { GRADIENT_LUT_TEMPLATE } from "../../../Fragment/FragmentShaderSourceGradientLUT"; + +/** + * @description グラデーションLUTのシェーダーを返却 + * Returns the shader of the gradient LUT + * + * @param {number} stops_length + * @param {boolean} is_linear_space + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = ( + stops_length: number, + is_linear_space: boolean +): ShaderManager => { + + const key1 = ("00" + stops_length).slice(-3); + const key2 = is_linear_space ? "y" : "n"; + const key = `l${key1}${key2}`; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const mediumpLength: number = Math.ceil(stops_length * 5 / 4); + + const shader = new ShaderManager( + TEXTURE_TEMPLATE(), + GRADIENT_LUT_TEMPLATE(mediumpLength, stops_length, is_linear_space) + ); + + $collection.set(key, shader); + + return shader; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/GradientLUTVariants.ts b/packages/webgl/src/Shader/Variants/GradientLUTVariants.ts new file mode 100644 index 00000000..0dc2f5c9 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/GradientLUTVariants.ts @@ -0,0 +1,10 @@ +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description グラデーションLUTのシェーダー管理クラスのコレクション + * Collection of gradient LUT shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/GradientVariants.ts b/packages/webgl/src/Shader/Variants/GradientVariants.ts new file mode 100644 index 00000000..0dc2f5c9 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/GradientVariants.ts @@ -0,0 +1,10 @@ +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description グラデーションLUTのシェーダー管理クラスのコレクション + * Collection of gradient LUT shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeMaskShaderService.ts b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeMaskShaderService.ts new file mode 100644 index 00000000..b8e2874f --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeMaskShaderService.ts @@ -0,0 +1,30 @@ +import { ShaderManager } from "../../../ShaderManager"; +import { $collection } from "../../ShapeVariants"; +import { FILL_TEMPLATE } from "../../../Vertex/VertexShaderSourceFill"; +import { MASK } from "../../../Fragment/FragmentShaderSource"; + +/** + * @description Shapeのマスクのシェーダを返却 + * Returns the mask shader of Shape + * + * @param {boolean} use_grid + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (use_grid: boolean): ShaderManager => +{ + const key = `m${use_grid ? "y" : "n"}`; + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + FILL_TEMPLATE(use_grid ? 9 : 0, false, true, use_grid), + MASK() + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeRectShaderService.ts b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeRectShaderService.ts new file mode 100644 index 00000000..94a4492f --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeRectShaderService.ts @@ -0,0 +1,30 @@ +import { ShaderManager } from "../../../ShaderManager"; +import { $collection } from "../../ShapeVariants"; +import { FILL_RECT_TEMPLATE } from "../../../Vertex/VertexShaderSourceFill"; +import { FILL_RECT_COLOR } from "../../../Fragment/FragmentShaderSource"; + +/** + * @description 画面全体の矩形専用のシェーダを返却 + * Returns a shader dedicated to the entire screen rectangle + * + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (): ShaderManager => +{ + const key = "rmnn"; + + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + FILL_RECT_TEMPLATE(), + FILL_RECT_COLOR() + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeSolidColorShaderService.ts b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeSolidColorShaderService.ts new file mode 100644 index 00000000..b67cec97 --- /dev/null +++ b/packages/webgl/src/Shader/Variants/Shape/service/VariantsShapeSolidColorShaderService.ts @@ -0,0 +1,30 @@ +import { ShaderManager } from "../../../ShaderManager"; +import { $collection } from "../../ShapeVariants"; +import { FILL_TEMPLATE } from "../../../Vertex/VertexShaderSourceFill"; +import { SOLID_FILL_COLOR } from "../../../Fragment/FragmentShaderSource"; + +/** + * @description Shapeのノーマルな塗りのシェーダを返却 + * Returns the normal fill shader of Shape + * + * @param {boolean} use_grid + * @return {ShaderManager} + * @method + * @protected + */ +export const execute = (use_grid: boolean): ShaderManager => +{ + const key = `s${use_grid ? "y" : "n"}`; + if ($collection.has(key)) { + return $collection.get(key) as NonNullable; + } + + const shaderManager = new ShaderManager( + FILL_TEMPLATE(use_grid ? 9 : 0, false, false, use_grid), + SOLID_FILL_COLOR() + ); + + $collection.set(key, shaderManager); + + return shaderManager; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Variants/ShapeVariants.ts b/packages/webgl/src/Shader/Variants/ShapeVariants.ts new file mode 100644 index 00000000..7b1cf02f --- /dev/null +++ b/packages/webgl/src/Shader/Variants/ShapeVariants.ts @@ -0,0 +1,10 @@ +import type { ShaderManager } from "../ShaderManager"; + +/** + * @description シェイプのシェーダー管理クラスのコレクション + * Collection of shape shader management classes + * + * @type {Map} + * @public + */ +export const $collection: Map = new Map(); \ No newline at end of file diff --git a/packages/webgl/src/Shader/Vertex/VertexShaderLibrary.ts b/packages/webgl/src/Shader/Vertex/VertexShaderLibrary.ts new file mode 100644 index 00000000..d3a44160 --- /dev/null +++ b/packages/webgl/src/Shader/Vertex/VertexShaderLibrary.ts @@ -0,0 +1,68 @@ +/** + * @description グリッドがオフの場合の頂点シェーダー + * Vertex shader when grid is off + * + * @return {string} + * @method + * @static + */ +export const FUNCTION_GRID_OFF = (): string => +{ + return ` +vec2 applyMatrix(in vec2 vertex) { + mat3 matrix = mat3(a_matrix0, a_matrix1, a_matrix2); + return (matrix * vec3(vertex, 1.0)).xy; +}`; +}; + +/** + * @description グリッドがオンの場合の頂点シェーダー + * Vertex shader when grid is on + * + * @param {number} index + * @return {STRing} + * @method + * @static + */ +export const FUNCTION_GRID_ON = (index: number): string => +{ + return ` +vec2 applyMatrix(in vec2 vertex) { + mat3 parent_matrix = mat3( + u_highp[${index }].xyz, + u_highp[${index + 1}].xyz, + u_highp[${index + 2}].xyz + ); + mat3 ancestor_matrix = mat3( + u_highp[${index + 3}].xyz, + u_highp[${index + 4}].xyz, + u_highp[${index + 5}].xyz + ); + + vec2 parent_offset = vec2(u_highp[${index + 2}].w, u_highp[${index + 3}].w); + vec2 parent_size = vec2(u_highp[${index + 4}].w, u_highp[${index + 5}].w); + vec4 grid_min = u_highp[${index + 6}]; + vec4 grid_max = u_highp[${index + 7}]; + + vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy; + position = (position - parent_offset) / parent_size; + + vec4 ga = grid_min; + vec4 gb = grid_max - grid_min; + vec4 gc = vec4(1.0) - grid_max; + + vec2 pa = position; + vec2 pb = position - grid_min.st; + vec2 pc = position - grid_max.st; + + position = (ga.pq / ga.st) * min(pa, ga.st) + + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st) + + (gc.pq / gc.st) * max(vec2(0.0), pc); + + position = position * parent_size + parent_offset; + position = (ancestor_matrix * vec3(position, 1.0)).xy; + + position = position + vec2(u_highp[${index + 8}].x, u_highp[${index + 8}].y); + return position / vec2(u_highp[0].w, u_highp[1].w); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Vertex/VertexShaderSource.ts b/packages/webgl/src/Shader/Vertex/VertexShaderSource.ts new file mode 100644 index 00000000..5e420f1f --- /dev/null +++ b/packages/webgl/src/Shader/Vertex/VertexShaderSource.ts @@ -0,0 +1,150 @@ +/** + * @return {string} + * @method + * @static + */ +export const TEXTURE_TEMPLATE = (): string => +{ + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; + +out vec2 v_coord; + +void main() { + v_coord = a_vertex; + + vec2 position = a_vertex * 2.0 - 1.0; + gl_Position = vec4(position, 0.0, 1.0); +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const VECTOR_TEMPLATE = (): string => +{ + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; + +out vec2 v_coord; + +void main() { + v_coord = a_vertex; + + vec2 position = a_vertex * 2.0 - 1.0; + gl_Position = vec4(position.x, -position.y, 0.0, 1.0); +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const BLEND_MATRIX_TEMPLATE = (): string => +{ + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; +uniform vec4 u_highp[3]; + +out vec2 v_coord; + +void main() { + v_coord = a_vertex; + + mat3 matrix = mat3( + u_highp[0].x, u_highp[0].y, 0.0, + u_highp[0].z, u_highp[0].w, 0.0, + u_highp[1].x, u_highp[1].y, 1.0 + ); + + vec2 size = u_highp[1].zw; + vec2 viewport = vec2(u_highp[2].x, u_highp[2].y); + + vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); + position = position * size; + position = (matrix * vec3(position, 1.0)).xy; + position /= viewport; + + position = position * 2.0 - 1.0; + gl_Position = vec4(position.x, -position.y, 0.0, 1.0); +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const BLEND_TEMPLATE = (): string => +{ + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; +uniform vec4 u_highp[2]; + +out vec2 v_coord; + +void main() { + v_coord = a_vertex; + + vec2 offset = u_highp[0].xy; + vec2 size = u_highp[0].zw; + vec2 viewport = vec2(u_highp[1].x, u_highp[1].y); + + vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); + position = position * size + offset; + position /= viewport; + + position = position * 2.0 - 1.0; + gl_Position = vec4(position.x, -position.y, 0.0, 1.0); +}`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const INSTANCE_TEMPLATE = (): string => +{ + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; +layout (location = 1) in vec4 a_rect; +layout (location = 2) in vec4 a_size; +layout (location = 3) in vec2 a_offset; +layout (location = 4) in vec4 a_matrix; +layout (location = 5) in vec4 a_mul; +layout (location = 6) in vec4 a_add; + +out vec2 v_coord; +out vec4 v_mul; +out vec4 v_add; + +void main() { + v_coord = a_vertex * a_rect.zw + a_rect.xy; + v_mul = a_mul; + v_add = a_add; + + vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); + position = position * a_size.xy; + + mat3 matrix = mat3( + a_matrix.x, a_matrix.y, 0.0, + a_matrix.z, a_matrix.w, 0.0, + a_offset.x, a_offset.y, 1.0 + ); + + position = (matrix * vec3(position, 1.0)).xy; + position /= a_size.zw; + + position = position * 2.0 - 1.0; + gl_Position = vec4(position.x, -position.y, 0.0, 1.0); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/Shader/Vertex/VertexShaderSourceFill.ts b/packages/webgl/src/Shader/Vertex/VertexShaderSourceFill.ts new file mode 100644 index 00000000..f217b857 --- /dev/null +++ b/packages/webgl/src/Shader/Vertex/VertexShaderSourceFill.ts @@ -0,0 +1,205 @@ +import { + FUNCTION_GRID_OFF, + FUNCTION_GRID_ON +} from "./VertexShaderLibrary"; + +/** + * @return {string} + * @method + * @static + */ +export const ATTRIBUTE_BEZIER_ON = (): string => +{ + return "layout (location = 1) in vec2 a_bezier;"; +}; + +/** + * @returns {string} + * @method + * @static + */ +export const ATTRIBUTE_MATRIX_ON = (): string => +{ + return `layout (location = 3) in vec3 a_matrix0; +layout (location = 4) in vec3 a_matrix1; +layout (location = 5) in vec3 a_matrix2;`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const VARYING_UV_ON = (): string => +{ + return "out vec2 v_uv;"; +}; + +/** + * @return {string} + * @method + * @static + */ +export const VARYING_BEZIER_ON = (): string => +{ + return "out vec2 v_bezier;"; +}; + +/** + * @return {string} + * @method + * @static + */ +export const STATEMENT_UV_ON = (): string => +{ + return ` + mat3 uv_matrix = mat3( + u_highp[0].xyz, + u_highp[1].xyz, + u_highp[2].xyz + ); + mat3 inverse_matrix = mat3( + u_highp[3].xyz, + u_highp[4].xyz, + vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w) + ); + v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy;`; +}; + +/** + * @return {string} + * @method + * @static + */ +export const STATEMENT_BEZIER_ON = (): string => +{ + return "v_bezier = a_bezier;"; +}; + +/** + * @return {string} + * @method + * @static + */ +export const ATTRIBUTE_COLOR_ON = (): string => +{ + return "layout (location = 2) in vec4 a_color;"; +}; + +/** + * @return {string} + * @method + * @static + */ +export const VARYING_COLOR_ON = (): string => +{ + return "out vec4 v_color;"; +}; + +/** + * @return {string} + * @method + * @static + */ +export const STATEMENT_COLOR_ON = (): string => +{ + return "v_color = a_color;"; +}; + +/** + * @description 各種、塗りのシェーダのテンプレートを返却 + * Returns a template for various fill shaders + * + * @param {number} highp_length + * @param {boolean} with_uv + * @param {boolean} for_mask + * @param {boolean} is_grid_enabled + * @return {string} + * @method + * @static + */ +export const FILL_TEMPLATE = ( + highp_length: number, + with_uv: boolean, + for_mask: boolean, + is_grid_enabled: boolean +): string => { + + const bezierAttribute = for_mask + ? ATTRIBUTE_BEZIER_ON() + : ""; + + const uvVarying = for_mask + ? VARYING_BEZIER_ON() + : with_uv + ? VARYING_UV_ON() + : ""; + + const uvStatement = for_mask + ? STATEMENT_BEZIER_ON() + : with_uv + ? STATEMENT_UV_ON() + : ""; + + const gridFunction = is_grid_enabled + ? FUNCTION_GRID_ON(with_uv ? 5 : 0) + : FUNCTION_GRID_OFF(); + + const colorAttribute = for_mask + ? "" + : ATTRIBUTE_COLOR_ON(); + + const colorVarying = for_mask + ? "" + : VARYING_COLOR_ON(); + + const colorStatement = for_mask + ? "" + : STATEMENT_COLOR_ON(); + + const matrixAttribute = is_grid_enabled + ? "" + : ATTRIBUTE_MATRIX_ON(); + + const uniform = highp_length > 1 + ? `uniform vec4 u_highp[${highp_length}];` + : ""; + + return `#version 300 es + +layout (location = 0) in vec2 a_vertex; +${bezierAttribute} +${colorAttribute} +${matrixAttribute} + +${uniform} +${uvVarying} +${colorVarying} +${gridFunction} + +void main() { + ${colorStatement} + ${uvStatement} + vec2 pos = applyMatrix(a_vertex); + pos = pos * 2.0 - 1.0; + gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0); +}`; +}; + +/** + * @description 矩形の塗りのシェーダのテンプレートを返却 + * Returns a template for filling rectangles + * + * @return {string} + * @method + * @static + */ +export const FILL_RECT_TEMPLATE = (): string => +{ + return `#version 300 es +layout (location = 0) in vec2 a_vertex; +void main() { + vec2 pos = a_vertex * 2.0 - 1.0; + gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0); +}`; +}; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject.ts b/packages/webgl/src/StencilBufferObject.ts new file mode 100644 index 00000000..80ea5e87 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject.ts @@ -0,0 +1,10 @@ +import { IStencilBufferObject } from "./interface/IStencilBufferObject"; + +/** + * @description StencilBufferObjectの再利用のための配列のオブジェクトプール + * Object pool of array for reusing StencilBufferObject + * + * @type {IStencilBufferObject[]} + * @private + */ +export const $objectPool: IStencilBufferObject[] = []; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.test.ts b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.test.ts new file mode 100644 index 00000000..cd3b59a4 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.test.ts @@ -0,0 +1,26 @@ +import { execute } from "./StencilBufferObjectCreateService"; +import { describe, expect, it, vi } from "vitest"; + +describe("StencilBufferObjectCreateService.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }) + } + } + }); + + const stencilBufferObject = execute(); + expect(stencilBufferObject.resource).toBe("createRenderbuffer"); + expect(stencilBufferObject.width).toBe(0); + expect(stencilBufferObject.height).toBe(0); + expect(stencilBufferObject.area).toBe(0); + expect(stencilBufferObject.dirty).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.ts b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.ts new file mode 100644 index 00000000..5e46f670 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectCreateService.ts @@ -0,0 +1,28 @@ +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +let $id: number = 0; + +/** + * @description 新規のStencilBufferObjectを生成する + * Create a new StencilBufferObject + * + * @return {object} + * @method + * @protected + */ +export const execute = (): IStencilBufferObject => +{ + return { + "id": $id++, + "resource": $gl.createRenderbuffer() as NonNullable, + "width": 0, + "height": 0, + "area": 0, + "dirty": false + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.test.ts b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.test.ts new file mode 100644 index 00000000..c96f2bf9 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.test.ts @@ -0,0 +1,58 @@ +import { execute } from "./StencilBufferObjectReleaseColorBufferObjectService"; +import { describe, expect, it } from "vitest"; +import { $objectPool } from "../../StencilBufferObject"; + +describe("StencilBufferObjectReleaseColorBufferObjectService.js method test", () => +{ + it("test case", async () => + { + $objectPool.length = 0; + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + expect($objectPool.length).toBe(4); + + const stencilBufferObject = { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 620, + "height": 480, + "area": 620 * 480, + "dirty": false, + }; + + execute(stencilBufferObject); + expect($objectPool.length).toBe(5); + expect($objectPool[$objectPool.length - 1]).toBe(stencilBufferObject); + + // 重複チェック + execute(stencilBufferObject); + expect($objectPool.length).toBe(5); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.ts b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.ts new file mode 100644 index 00000000..79b87f46 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/service/StencilBufferObjectReleaseColorBufferObjectService.ts @@ -0,0 +1,28 @@ +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { $objectPool } from "../../StencilBufferObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description StencilBufferObjectをオブジェクトプールに保管、サイズオーバー時は削除します。 + * Stores the StencilBufferObject in the object pool and deletes it if it exceeds the size. + * + * @param {IStencilBufferObject} stencil_bffer_object + * @return {void} + * @method + * @protected + */ +export const execute = (stencil_bffer_object: IStencilBufferObject): void => +{ + if ($objectPool.indexOf(stencil_bffer_object) > -1) { + return ; + } + + // プールに10個以上ある場合は削除 + if ($objectPool.length > 10) { + $gl.deleteRenderbuffer(stencil_bffer_object.resource); + return; + } + + stencil_bffer_object.dirty = true; + $objectPool.push(stencil_bffer_object); +}; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.test.ts b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.test.ts new file mode 100644 index 00000000..d68613d7 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.test.ts @@ -0,0 +1,73 @@ +import { execute } from "./StencilBufferObjectAcquireObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../StencilBufferObject"; + +describe("StencilBufferObjectAcquireObjectUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createRenderbuffer": vi.fn(() => { return "createRenderbuffer" }) + } + } + }); + + // new + $objectPool.length = 0; + const newStencilBufferObject = execute(320, 240); + expect(newStencilBufferObject.width).toBe(0); + expect(newStencilBufferObject.height).toBe(0); + expect(newStencilBufferObject.area).toBe(0); + + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + + // hit + expect($objectPool.length).toBe(4); + const poolColorBufferObject = execute(512, 512) + expect(poolColorBufferObject.width).toBe(512); + expect(poolColorBufferObject.area).toBe(512 * 512); + expect(poolColorBufferObject.height).toBe(512); + expect($objectPool.length).toBe(3); + + // not hit + const oldColorBufferObject = execute(100, 100) + expect(oldColorBufferObject.width).toBe(256); + expect(oldColorBufferObject.area).toBe(256 * 256); + expect(oldColorBufferObject.height).toBe(256); + expect($objectPool.length).toBe(2); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.ts b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.ts new file mode 100644 index 00000000..2184ddee --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectAcquireObjectUseCase.ts @@ -0,0 +1,36 @@ +import { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { $objectPool } from "../../StencilBufferObject"; +import { execute as stencilBufferObjectCreateService } from "../service/StencilBufferObjectCreateService"; + +/** + * @description オブジェクトプールにStencilBufferObjectがあれば再利用、なければ新規作成して返却します。 + * If there is a StencilBufferObject in the object pool, it will be reused, + * otherwise it will be created and returned. + * + * @param {number} width + * @param {number} height + * @return {IStencilBufferObject} + * @method + * @protected + */ +export const execute = (width: number, height: number): IStencilBufferObject => +{ + if (!$objectPool.length) { + return stencilBufferObjectCreateService(); + } + + for (let idx: number = 0; idx < $objectPool.length; ++idx) { + const stencilBufferObject = $objectPool[idx]; + + if (stencilBufferObject.width !== width + || stencilBufferObject.height !== height + ) { + continue; + } + + $objectPool.splice(idx, 1); + return stencilBufferObject; + } + + return $objectPool.shift() as NonNullable; +}; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.test.ts b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.test.ts new file mode 100644 index 00000000..2033b60c --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.test.ts @@ -0,0 +1,69 @@ +import { execute } from "./StencilBufferObjectGetStencilBufferObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $objectPool } from "../../StencilBufferObject"; + +describe("StencilBufferObjectGetStencilBufferObjectUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "bindRenderbuffer": vi.fn(() => { return "bindRenderbuffer" }), + "renderbufferStorage": vi.fn(() => { return "renderbufferStorage" }), + } + } + }); + + // new + $objectPool.length = 0; + $objectPool.push( + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 256, + "height": 256, + "area": 256 * 256, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 512, + "height": 512, + "area": 512 * 512, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 1024, + "height": 1024, + "area": 1024 * 1024, + "dirty": false, + }, + { + "resource": {} as unknown as WebGLRenderbuffer, + "width": 2048, + "height": 2048, + "area": 2048 * 2048, + "dirty": false, + }, + ); + expect($objectPool.length).toBe(4); + + // hit + const cacheStencilBufferObject = execute(256, 256); + expect($objectPool.length).toBe(3); + expect(cacheStencilBufferObject.width).toBe(256); + expect(cacheStencilBufferObject.height).toBe(256); + expect(cacheStencilBufferObject.area).toBe(256 * 256); + + // no hit + const newStencilBufferObject = execute(320, 240); + expect($objectPool.length).toBe(2); + expect(newStencilBufferObject.width).toBe(320); + expect(newStencilBufferObject.height).toBe(240); + expect(newStencilBufferObject.area).toBe(320 * 240); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.ts b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.ts new file mode 100644 index 00000000..74949ac1 --- /dev/null +++ b/packages/webgl/src/StencilBufferObject/usecase/StencilBufferObjectGetStencilBufferObjectUseCase.ts @@ -0,0 +1,38 @@ +import type { IStencilBufferObject } from "../../interface/IStencilBufferObject"; +import { execute as stencilBufferObjectAcquireObjectUseCase } from "./StencilBufferObjectAcquireObjectUseCase"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description StencilBufferObjectを利用できる状態にして、返却します。 + * Make the StencilBufferObject available and return it. + * + * @param {number} width + * @param {number} height + * @return {IStencilBufferObject} + * @method + * @protected + */ +export const execute = (width: number, height: number): IStencilBufferObject => +{ + const stencilBufferObject = stencilBufferObjectAcquireObjectUseCase(width, height); + + if (stencilBufferObject.width !== width + || stencilBufferObject.height !== height + ) { + // update + stencilBufferObject.width = width; + stencilBufferObject.height = height; + stencilBufferObject.area = width * height; + stencilBufferObject.dirty = false; + + // bind + $gl.bindRenderbuffer($gl.RENDERBUFFER, stencilBufferObject.resource); + $gl.renderbufferStorage( + $gl.RENDERBUFFER, + $gl.STENCIL_INDEX8, + width, height + ); + } + + return stencilBufferObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/StencilBufferPool.ts b/packages/webgl/src/StencilBufferPool.ts deleted file mode 100644 index 582b40d7..00000000 --- a/packages/webgl/src/StencilBufferPool.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { $getArray } from "@next2d/share"; - -/** - * @class - */ -export class StencilBufferPool -{ - private readonly _$gl: WebGL2RenderingContext; - private readonly _$objectPool: WebGLRenderbuffer[]; - private _$objectPoolArea: number; - public _$maxWidth: number; - public _$maxHeight: number; - - /** - * @param {WebGL2RenderingContext} gl - * @constructor - */ - constructor (gl: WebGL2RenderingContext) - { - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$objectPool = $getArray(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$objectPoolArea = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxHeight = 0; - } - - /** - * @member {number} - * @param {number} max_width - * @public - */ - set maxWidth (max_width: number) - { - this._$maxWidth = max_width; - } - - /** - * @member {number} - * @param {number} max_height - * @public - */ - set maxHeight (max_height: number) - { - this._$maxHeight = max_height; - } - - /** - * @return {WebGLRenderbuffer} - * @method - * @private - */ - _$createStencilBuffer (): WebGLRenderbuffer - { - const stencilBuffer: WebGLRenderbuffer|null = this._$gl.createRenderbuffer(); - if (!stencilBuffer) { - throw new Error("the stencil buffer is null."); - } - - stencilBuffer.width = 0; - stencilBuffer.height = 0; - stencilBuffer.area = 0; - stencilBuffer.dirty = true; - - return stencilBuffer; - } - - /** - * @param {number} width - * @param {number} height - * @return {WebGLRenderbuffer} - * @method - * @private - */ - _$getStencilBuffer (width: number, height: number): WebGLRenderbuffer - { - const length: number = this._$objectPool.length; - for (let idx: number = 0; idx < length; ++idx) { - - const stencilBuffer: WebGLRenderbuffer = this._$objectPool[idx]; - if (stencilBuffer.width === width - && stencilBuffer.height === height - ) { - this._$objectPool.splice(idx, 1); - this._$objectPoolArea -= stencilBuffer.area; - return stencilBuffer; - } - } - - if (length > 100) { - const stencilBuffer: WebGLRenderbuffer | void = this._$objectPool.shift(); - if (stencilBuffer) { - this._$objectPoolArea -= stencilBuffer.area; - return stencilBuffer; - } - } - - return this._$createStencilBuffer(); - } - - /** - * @param {number} width - * @param {number} height - * @return {WebGLRenderbuffer} - * @method - * @public - */ - create (width: number, height: number): WebGLRenderbuffer - { - const stencilBuffer: WebGLRenderbuffer = this._$getStencilBuffer(width, height); - - if (stencilBuffer.width !== width || stencilBuffer.height !== height) { - stencilBuffer.width = width; - stencilBuffer.height = height; - stencilBuffer.area = width * height; - stencilBuffer.dirty = false; - - this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER, stencilBuffer); - this._$gl.renderbufferStorage( - this._$gl.RENDERBUFFER, - this._$gl.STENCIL_INDEX8, - width, height - ); - } - - return stencilBuffer; - } - - /** - * @param {WebGLRenderbuffer} stencilBuffer - * @return {void} - * @method - * @public - */ - release (stencilBuffer: WebGLRenderbuffer): void - { - // ステンシルバッファのサイズが非常に大きい場合はプールしない - if (stencilBuffer.area > this._$maxWidth * this._$maxHeight * 2) { - this._$gl.deleteRenderbuffer(stencilBuffer); - return; - } - - stencilBuffer.dirty = true; - this._$objectPool.push(stencilBuffer); - this._$objectPoolArea += stencilBuffer.area; - - // プール容量が一定を超えたら、古いステンシルバッファから削除していく - if (this._$objectPoolArea > this._$maxWidth * this._$maxHeight * 10) { - const oldStencilBuffer: WebGLRenderbuffer | void = this._$objectPool.shift(); - if (oldStencilBuffer) { - this._$objectPoolArea -= oldStencilBuffer.area; - this._$gl.deleteRenderbuffer(oldStencilBuffer); - } - } - } -} \ No newline at end of file diff --git a/packages/webgl/src/TextureManager.ts b/packages/webgl/src/TextureManager.ts index f64f8281..b17cce40 100644 --- a/packages/webgl/src/TextureManager.ts +++ b/packages/webgl/src/TextureManager.ts @@ -1,789 +1,31 @@ -import { $RENDER_SIZE } from "./Const"; -import type { CachePositionImpl } from "./interface/CachePositionImpl"; -import type { GridImpl } from "./interface/GridImpl"; +import type { ITextureObject } from "./interface/ITextureObject"; /** - * @class + * @description 現在のアクティブなテクスチャーの番号 + * Number of the currently binded active texture + * + * @type {number} + * @protected */ -export class TextureManager -{ - public _$maxWidth: number; - public _$maxHeight: number; - private _$objectPoolArea: number; - private _$activeTexture: number; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$objectPool: WebGLTexture[]; - private readonly _$boundTextures: Array; - private readonly _$atlasNodes: Map; - private readonly _$atlasTextures: WebGLTexture[]; - private readonly _$positionObjectArray: CachePositionImpl[]; - private readonly _$nodeObjectArray: GridImpl[]; - private readonly _$atlasCacheMap: Map; - - /** - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (gl: WebGL2RenderingContext) - { - - // init setting - gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$objectPool = []; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$objectPoolArea = 0; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$activeTexture = -1; - - /** - * @type {array} - * @private - */ - this._$boundTextures = [null, null, null]; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxHeight = 0; - - /** - * @type {array} - * @private - */ - this._$atlasTextures = []; - - /** - * @type {array} - * @private - */ - this._$atlasCacheMap = new Map(); - - /** - * @type {array} - * @private - */ - this._$positionObjectArray = []; - - /** - * @type {array} - * @private - */ - this._$nodeObjectArray = []; - - /** - * @type {array} - * @private - */ - this._$atlasNodes = new Map(); - } - - /** - * @return {void} - * @method - * @public - */ - createTextureAtlas (): void - { - const texture: WebGLTexture = this._$gl.createTexture() as NonNullable; - texture.width = $RENDER_SIZE; - texture.height = $RENDER_SIZE; - - this._$gl.activeTexture(this._$gl.TEXTURE3); - this._$gl.bindTexture(this._$gl.TEXTURE_2D, texture); - - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_WRAP_S, this._$gl.CLAMP_TO_EDGE); - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_WRAP_T, this._$gl.CLAMP_TO_EDGE); - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_MIN_FILTER, this._$gl.NEAREST); - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_MAG_FILTER, this._$gl.NEAREST); - - this._$gl.texStorage2D(this._$gl.TEXTURE_2D, 1, this._$gl.RGBA8, $RENDER_SIZE, $RENDER_SIZE); - this._$gl.bindTexture(this._$gl.TEXTURE_2D, null); - - if (this._$activeTexture > -1) { - this._$gl.activeTexture(this._$activeTexture); - } - - // init array - const index: number = this._$atlasTextures.length; - this._$atlasNodes.set(index, []); - this._$atlasCacheMap.set(index, []); - - this._$atlasTextures.push(texture); - } - - /** - * @param {number} index - * @return {WebGLTexture} - * @method - * @public - */ - getAtlasTexture (index: number): WebGLTexture - { - return this._$atlasTextures[index]; - } - - /** - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @return {object} - * @method - * @public - */ - getNode (x: number, y: number, w: number, h: number): GridImpl - { - const node = this._$nodeObjectArray.length - ? this._$nodeObjectArray.pop() as NonNullable - : { - "x": 0, - "y": 0, - "w": 0, - "h": 0 - }; - - node.x = x; - node.y = y; - node.w = w; - node.h = h; - - return node; - } - - /** - * @param {number} width - * @param {number} height - * @return {object} - * @method - * @public - */ - createCachePosition (width: number, height: number): CachePositionImpl - { - const object: CachePositionImpl = this._$positionObjectArray.length - ? this._$positionObjectArray.pop() as NonNullable - : { - "index": 0, - "x": 0, - "y": 0, - "w": 0, - "h": 0 - }; - - // init - object.x = object.y = 0; - object.w = width; - object.h = height; - - // search - for (const [index, nodes] of this._$atlasNodes) { - - // root node - if (!nodes.length) { - - if (width > height) { - - if ($RENDER_SIZE - width - 1 > 0) { - nodes.push(this.getNode( - width + 1, - 0, - $RENDER_SIZE - width - 1, - height - )); - } - - if ($RENDER_SIZE - height - 1 > 0) { - nodes.push(this.getNode( - 0, - height + 1, - $RENDER_SIZE, - $RENDER_SIZE - height - 1 - )); - } - - } else { - if ($RENDER_SIZE - height - 1 > 0) { - nodes.push(this.getNode( - 0, - height + 1, - width, - $RENDER_SIZE - height - 1 - )); - } - - if ($RENDER_SIZE - width - 1 > 0) { - nodes.push(this.getNode( - width + 1, - 0, - $RENDER_SIZE - width - 1, - $RENDER_SIZE - )); - } - } - - object.index = index; - - const caches = this._$atlasCacheMap.get(object.index) as NonNullable; - caches.push(object); - - return object; - } - - const length: number = nodes.length; - for (let idx = 0; idx < length; ++idx) { - - const node = nodes[idx]; - - // no hit - if (width > node.w || height > node.h) { - continue; - } - - object.index = index; - object.x = node.x; - object.y = node.y; - - const caches = this._$atlasCacheMap.get(object.index) as NonNullable; - caches.push(object); - - // division - if (node.w !== width || node.h !== height) { - - if (width > height) { - - if (node.h - height - 1 > 0) { - nodes.push(this.getNode( - node.x, - node.y + height + 1, - node.w, - node.h - height - 1 - )); - } - - if (node.w - width - 1 > 0) { - node.x = node.x + width + 1; - node.w = node.w - width - 1; - node.h = height; - } else { - nodes.splice(idx, 1); - this._$nodeObjectArray.push(node); - } - - } else { - - if (node.w - width - 1 > 0) { - nodes.push(this.getNode( - node.x + width + 1, - node.y, - node.w - width - 1, - node.h - )); - } - - if (node.h - height - 1 > 0) { - node.y = node.y + height + 1; - node.w = width; - node.h = node.h - height - 1; - } else { - nodes.splice(idx, 1); - this._$nodeObjectArray.push(node); - } - } - - } else { - - nodes.splice(idx, 1); - this._$nodeObjectArray.push(node); - - } - - return object; - } - } - - // ヒットしない場合は新しいtextureを生成 - const index: number = this._$atlasTextures.length; - this.createTextureAtlas(); - - const nodes: GridImpl[] = this._$atlasNodes.get(index) as NonNullable; - if (width > height) { - - if ($RENDER_SIZE - width - 1 > 0) { - nodes.push(this.getNode( - width + 1, - 0, - $RENDER_SIZE - width - 1, - height - )); - } - - if ($RENDER_SIZE - height - 1 > 0) { - nodes.push(this.getNode( - 0, - height + 1, - $RENDER_SIZE, - $RENDER_SIZE - height - 1 - )); - } - - } else { - if ($RENDER_SIZE - height - 1 > 0) { - nodes.push(this.getNode( - 0, - height + 1, - width, - $RENDER_SIZE - height - 1 - )); - } - - if ($RENDER_SIZE - width - 1 > 0) { - nodes.push(this.getNode( - width + 1, - 0, - $RENDER_SIZE - width - 1, - $RENDER_SIZE - )); - } - } - - object.index = index; - const caches: CachePositionImpl[] = this._$atlasCacheMap.get(object.index) as NonNullable; - caches.push(object); - - return object; - } - - /** - * @param {object} - * @method - * @public - */ - releasePosition (position: CachePositionImpl): void - { - if (!this._$atlasNodes.has(position.index)) { - return ; - } - - // 先頭にrootとして再登録 - this - ._$atlasNodes - .get(position.index) - ?.unshift(this.getNode( - position.x, - position.y, - position.w, - position.h - )); - - // pool - this._$positionObjectArray.push(position); - } +export let $activeTextureUnit: number = -1; - /** - * @return {void} - * @method - * @private - */ - clearCache (): void - { - for (const caches of this._$atlasCacheMap.values()) { - caches.length = 0; - } - for (const caches of this._$atlasNodes.values()) { - caches.length = 0; - } - } - - /** - * @param {number} width - * @param {number} height - * @return {WebGLTexture} - * @method - * @private - */ - _$createTexture (width: number, height: number): WebGLTexture - { - const texture: WebGLTexture = this._$gl.createTexture() as NonNullable; - - texture.width = 0; - texture.height = 0; - texture.area = 0; - texture.dirty = true; - texture.smoothing = true; - - this.bind0(texture, false); - - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_WRAP_S, this._$gl.CLAMP_TO_EDGE); - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_WRAP_T, this._$gl.CLAMP_TO_EDGE); - - texture.width = width; - texture.height = height; - texture.area = width * height; - texture.dirty = false; - - this._$gl.texStorage2D(this._$gl.TEXTURE_2D, 1, this._$gl.RGBA8, width, height); - - return texture; - } - - /** - * @param {number} width - * @param {number} height - * @return {WebGLTexture} - * @method - * @private - */ - _$getTexture (width: number, height: number): WebGLTexture - { - // プールに同じサイズのテクスチャがあれば、それを使い回す - for (let i: number = 0; i < this._$objectPool.length; i++) { - - const texture: WebGLTexture = this._$objectPool[i]; - if (texture.width === width && texture.height === height) { - - this._$objectPool.splice(i, 1); - - this._$objectPoolArea -= texture.area; - - this.bind0(texture, false); - - return texture; - } - } - - return this._$createTexture(width, height); - } - - /** - * @param {number} width - * @param {number} height - * @param {Uint8Array} [pixels=null] - * @param {boolean} [premultiplied_alpha=false] - * @param {boolean} [flip_y=true] - * @method - * @return {WebGLTexture} - */ - create ( - width: number, height: number, - pixels: Uint8Array | null = null, - premultiplied_alpha: boolean = false, - flip_y: boolean = true - ): WebGLTexture { - - const texture: WebGLTexture = this._$getTexture(width, height); - - if (premultiplied_alpha) { - this._$gl.pixelStorei(this._$gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); - } - - if (!flip_y) { - this._$gl.pixelStorei(this._$gl.UNPACK_FLIP_Y_WEBGL, false); - } - - if (texture.width !== width || texture.height !== height) { - - texture.width = width; - texture.height = height; - texture.area = width * height; - texture.dirty = false; - - this._$gl.texImage2D( - this._$gl.TEXTURE_2D, 0, this._$gl.RGBA, width, height, - 0, this._$gl.RGBA, this._$gl.UNSIGNED_BYTE, pixels - ); - - } else if (pixels) { - - texture.dirty = false; - - this._$gl.texSubImage2D( - this._$gl.TEXTURE_2D, 0, 0, 0, width, height, - this._$gl.RGBA, this._$gl.UNSIGNED_BYTE, pixels - ); - } - - if (premultiplied_alpha) { - this._$gl.pixelStorei(this._$gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); - } - - if (!flip_y) { - this._$gl.pixelStorei(this._$gl.UNPACK_FLIP_Y_WEBGL, true); - } - - return texture; - } - - /** - * @param {HTMLImageElement} image - * @param {boolean} [smoothing=false] - * @return {WebGLTexture} - * @method - * @public - */ - createFromImage (image: HTMLImageElement, smoothing: boolean = false): WebGLTexture - { - return this._$createFromElement( - image.width, - image.height, - image, - smoothing - ); - } - - /** - * @param {HTMLCanvasElement} canvas - * @return {WebGLTexture} - * @method - * @public - */ - createFromCanvas (canvas: HTMLCanvasElement | OffscreenCanvas): WebGLTexture - { - return this._$createFromElement( - canvas.width, - canvas.height, - canvas, - false - ); - } - - /** - * @param {HTMLVideoElement} video - * @param {boolean} [smoothing=false] - * @return {WebGLTexture} - * @method - * @public - */ - createFromVideo ( - video: HTMLVideoElement, - smoothing: boolean = false - ): WebGLTexture { - return this._$createFromElement( - video.videoWidth, - video.videoHeight, - video, - smoothing - ); - } - - /** - * @param {number} width - * @param {number} height - * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} element - * @param {boolean} [smoothing=false] - * @return {WebGLTexture} - * @method - * @private - */ - _$createFromElement ( - width: number, height: number, - element: HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | OffscreenCanvas, - smoothing: boolean = false - ): WebGLTexture { - - const texture: WebGLTexture = this._$getTexture(width, height); - - texture.dirty = false; - - this.bind0(texture, smoothing); - - this._$gl.pixelStorei(this._$gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); - - if (texture.width !== width || texture.height !== height) { - - texture.width = width; - texture.height = height; - texture.area = width * height; - - this._$gl.texImage2D( - this._$gl.TEXTURE_2D, 0, this._$gl.RGBA, - this._$gl.RGBA, this._$gl.UNSIGNED_BYTE, element - ); - - } else { - - this._$gl.texSubImage2D( - this._$gl.TEXTURE_2D, 0, 0, 0, - this._$gl.RGBA, this._$gl.UNSIGNED_BYTE, element - ); - } - - this._$gl.pixelStorei(this._$gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); - - return texture; - } - - /** - * @param {WebGLTexture} texture - * @return {void} - * @method - * @public - */ - release (texture: WebGLTexture): void - { - // テクスチャのサイズが非常に大きい場合はプールしない - if (texture.area > this._$maxWidth * this._$maxHeight * 2) { - this._$gl.deleteTexture(texture); - return ; - } - - texture.dirty = true; - this._$objectPool.push(texture); - - this._$objectPoolArea += texture.area; - - // プール容量が一定を超えたら、古いテクスチャから削除していく - if (this._$objectPool.length - && this._$objectPoolArea > this._$maxWidth * this._$maxHeight * 10 - ) { - - const oldTexture: WebGLTexture = this._$objectPool.shift() as NonNullable; - - this._$objectPoolArea -= oldTexture.area; - - this._$gl.deleteTexture(oldTexture); - } - } - - /** - * @param {WebGLTexture} texture0 - * @param {boolean|null} [smoothing0=null] - * @return {void} - * @method - * @public - */ - bind0 (texture0: WebGLTexture, smoothing0: boolean|null = null): void - { - this._$bindTexture(2, this._$gl.TEXTURE2, null, null); - this._$bindTexture(1, this._$gl.TEXTURE1, null, null); - this._$bindTexture(0, this._$gl.TEXTURE0, texture0, smoothing0); - } - - /** - * @param {WebGLTexture} texture0 - * @param {WebGLTexture} texture1 - * @param {boolean} [smoothing01=null] - * @return {void} - * @method - * @public - */ - bind01 ( - texture0: WebGLTexture, - texture1: WebGLTexture, - smoothing01: boolean|null = null - ): void { - this._$bindTexture(2, this._$gl.TEXTURE2, null, null); - this._$bindTexture(1, this._$gl.TEXTURE1, texture1, smoothing01); - this._$bindTexture(0, this._$gl.TEXTURE0, texture0, smoothing01); - } - - /** - * @param {WebGLTexture} texture0 - * @param {WebGLTexture} texture1 - * @param {WebGLTexture} texture2 - * @param {boolean} [smoothing2=null] - * @return {void} - * @method - * @public - */ - bind012 ( - texture0: WebGLTexture, - texture1: WebGLTexture, - texture2: WebGLTexture, - smoothing2: boolean|null = null - ): void { - this._$bindTexture(2, this._$gl.TEXTURE2, texture2, smoothing2); - this._$bindTexture(1, this._$gl.TEXTURE1, texture1, null); - this._$bindTexture(0, this._$gl.TEXTURE0, texture0, null); - } - - /** - * @param {WebGLTexture} texture0 - * @param {WebGLTexture} texture2 - * @param {boolean} [smoothing2=null] - * @return {void} - * @method - * @public - */ - bind02 ( - texture0: WebGLTexture, - texture2: WebGLTexture, - smoothing2: boolean|null = null - ): void { - this._$bindTexture(2, this._$gl.TEXTURE2, texture2, smoothing2); - this._$bindTexture(1, this._$gl.TEXTURE1, null, null); - this._$bindTexture(0, this._$gl.TEXTURE0, texture0, null); - } - - /** - * @param {number} index - * @param {number} target - * @param {WebGLTexture} texture - * @param {boolean} smoothing - * @return {void} - * @method - * @private - */ - _$bindTexture ( - index: number, target: number, - texture: WebGLTexture|null = null, - smoothing: boolean|null = null - ): void { - - const shouldBind:boolean = texture !== this._$boundTextures[index]; - const shouldSmooth:boolean = smoothing !== null && texture !== null && smoothing !== texture.smoothing; - const shouldActive:boolean = (shouldBind || shouldSmooth || target === this._$gl.TEXTURE0) - && target !== this._$activeTexture; - - if (shouldActive) { - this._$activeTexture = target; - this._$gl.activeTexture(target); - } - - if (shouldBind) { - this._$boundTextures[index] = texture; - this._$gl.bindTexture(this._$gl.TEXTURE_2D, texture); - } - - if (shouldSmooth) { - - if (texture) { - texture.smoothing = !!smoothing; - } +/** + * @description 現在 + * @param {number} unit + * @return {void} + * @method + * @protected + */ +export const $setActiveTextureUnit = (unit: number): void => +{ + $activeTextureUnit = unit; +}; - const filter: number = smoothing ? this._$gl.LINEAR : this._$gl.NEAREST; - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_MIN_FILTER, filter); - this._$gl.texParameteri(this._$gl.TEXTURE_2D, this._$gl.TEXTURE_MAG_FILTER, filter); - } - } -} +/** + * @description 現在bindされてるテクスチャの配列 + * Array of currently binded textures + * + * @type {Array} + * @protected + */ +export const $boundTextures: Array = [null, null, null]; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerBindService.test.ts b/packages/webgl/src/TextureManager/service/TextureManagerBindService.test.ts new file mode 100644 index 00000000..15ce34df --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerBindService.test.ts @@ -0,0 +1,45 @@ +import { execute } from "./TextureManagerBindService"; +import { describe, expect, it, vi } from "vitest"; +import { + $activeTextureUnit, + $boundTextures, + $setActiveTextureUnit +} from "../../TextureManager"; + +describe("TextureManagerBindService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + } + } + }); + + const textureObject = { + "resource": {} as WebGLTexture, + "width": 200, + "height": 300, + "area": 200 * 300 + }; + + $boundTextures[0] = null; + $setActiveTextureUnit(-1); + expect($activeTextureUnit).toBe(-1); + expect($boundTextures[0]).toBe(null); + + execute(0, 0x84C0, textureObject); + expect($activeTextureUnit).toBe(0x84C0); + expect($boundTextures[0]).toBe(textureObject); + + execute(0, 0x84C0, null); + expect($boundTextures[0]).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerBindService.ts b/packages/webgl/src/TextureManager/service/TextureManagerBindService.ts new file mode 100644 index 00000000..2c449121 --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerBindService.ts @@ -0,0 +1,49 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { $gl } from "../../WebGLUtil"; +import { + $activeTextureUnit, + $boundTextures, + $setActiveTextureUnit +} from "../../TextureManager"; + +/** + * @description 指定のunitにテクスチャをバインドします。nullの場合はバインドを解除します。 + * Binds a texture to the specified unit. If null, the binding is released. + * + * @param {number} index + * @param {number} unit + * @param {ITextureObject} [texture_object=null] + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + index: number, + unit: number, + texture_object: ITextureObject | null = null, + smooth: boolean = false +): void => { + + if ($activeTextureUnit === -1 || unit !== $activeTextureUnit) { + $setActiveTextureUnit(unit); + $gl.activeTexture(unit); + } + + const boundTextures = $boundTextures[index]; + if (boundTextures !== null && texture_object !== null + && boundTextures.id === texture_object.id + || texture_object === boundTextures + ) { + return; + } + + $boundTextures[index] = texture_object; + $gl.bindTexture($gl.TEXTURE_2D, texture_object ? texture_object.resource : null); + + if (texture_object && texture_object.smooth !== smooth) { + texture_object.smooth = smooth; + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MIN_FILTER, smooth ? $gl.LINEAR : $gl.NEAREST); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MAG_FILTER, smooth ? $gl.LINEAR : $gl.NEAREST); + } +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.test.ts b/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.test.ts new file mode 100644 index 00000000..53158feb --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.test.ts @@ -0,0 +1,25 @@ +import { execute } from "./TextureManagerCreateTextureObjectService"; +import { describe, expect, it, vi } from "vitest"; + +describe("TextureManagerCreateTextureObjectService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createTexture": vi.fn(() => { return "createTexture" }) + } + } + }); + + const textureObject = execute(200, 300); + expect(textureObject.resource).toBe("createTexture"); + expect(textureObject.width).toBe(200); + expect(textureObject.height).toBe(300); + expect(textureObject.area).toBe(200 * 300); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.ts b/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.ts new file mode 100644 index 00000000..f4743dca --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerCreateTextureObjectService.ts @@ -0,0 +1,30 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @type {number} + * @private + */ +let $id: number = 0; + +/** + * @description 新規のテクスチャオブジェクトを作成します。 + * Create a new texture object. + * + * @param {number} width + * @param {number} height + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (width: number, height: number): ITextureObject => +{ + return { + "id": $id++, + "resource": $gl.createTexture() as NonNullable, + "width": width, + "height": height, + "area": width * height, + "smooth": false + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.test.ts b/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.test.ts new file mode 100644 index 00000000..1934f384 --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.test.ts @@ -0,0 +1,33 @@ +import { execute } from "./TextureManagerInitializeBindService"; +import { describe, expect, it, vi } from "vitest"; + +describe("TextureManagerInitializeBindService.js method test", () => +{ + it("test case", () => + { + const result = ""; + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }) + } + } + }); + + const textureObject = { + "resource": {} as WebGLTexture, + "width": 200, + "height": 300, + "area": 200 * 300 + }; + + execute(textureObject); + expect(result).toBe(""); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.ts b/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.ts new file mode 100644 index 00000000..006dccd7 --- /dev/null +++ b/packages/webgl/src/TextureManager/service/TextureManagerInitializeBindService.ts @@ -0,0 +1,35 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { $gl } from "../../WebGLUtil"; +import { + $activeTextureUnit, + $boundTextures, + $setActiveTextureUnit +} from "../../TextureManager"; + +/** + * @description テクスチャの初期設定を行います。 + * Initialize the texture. + * + * @param {ITextureObject} textrue_object + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = (textrue_object: ITextureObject, smooth: boolean = false): void => +{ + if ($activeTextureUnit !== $gl.TEXTURE0) { + $setActiveTextureUnit($gl.TEXTURE0); + $gl.activeTexture($gl.TEXTURE0); + } + + $boundTextures[0] = textrue_object; + $gl.bindTexture($gl.TEXTURE_2D, textrue_object.resource); + + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_WRAP_S, $gl.CLAMP_TO_EDGE); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_WRAP_T, $gl.CLAMP_TO_EDGE); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MIN_FILTER, smooth ? $gl.LINEAR : $gl.NEAREST); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MAG_FILTER, smooth ? $gl.LINEAR : $gl.NEAREST); + + $gl.texStorage2D($gl.TEXTURE_2D, 1, $gl.RGBA8, textrue_object.width, textrue_object.height); +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerBind012UseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerBind012UseCase.ts new file mode 100644 index 00000000..b9014750 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerBind012UseCase.ts @@ -0,0 +1,26 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerBindService } from "../service/TextureManagerBindService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description TEXTURE0、TEXTURE1、TEXTURE2にテクスチャをバインドします。 + * Binds textures to TEXTURE0, TEXTURE1, and TEXTURE2. + * + * @param {ITextureObject} texture0 + * @param {ITextureObject} texture1 + * @param {ITextureObject} texture2 + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + texture0: ITextureObject, + texture1: ITextureObject, + texture2: ITextureObject, + smooth: boolean = false +): void => { + textureManagerBindService(2, $gl.TEXTURE2, texture2, smooth); + textureManagerBindService(1, $gl.TEXTURE1, texture1, smooth); + textureManagerBindService(0, $gl.TEXTURE0, texture0, smooth); +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerBind01UseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerBind01UseCase.ts new file mode 100644 index 00000000..db358015 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerBind01UseCase.ts @@ -0,0 +1,24 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerBindService } from "../service/TextureManagerBindService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description TEXTURE0とTEXTURE1にテクスチャをバインドします。 + * Binds textures to TEXTURE0 and TEXTURE1. + * + * @param {ITextureObject} texture0 + * @param {ITextureObject} texture1 + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + texture0: ITextureObject, + texture1: ITextureObject, + smooth: boolean = false +): void => { + textureManagerBindService(2, $gl.TEXTURE2, null, smooth); + textureManagerBindService(1, $gl.TEXTURE1, texture1, smooth); + textureManagerBindService(0, $gl.TEXTURE0, texture0, smooth); +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerBind02UseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerBind02UseCase.ts new file mode 100644 index 00000000..4db1cddf --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerBind02UseCase.ts @@ -0,0 +1,24 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerBindService } from "../service/TextureManagerBindService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description TEXTURE0とTEXTURE2にテクスチャをバインドします。 + * Binds textures to TEXTURE0 and TEXTURE2. + * + * @param {ITextureObject} texture0 + * @param {ITextureObject} texture2 + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = ( + texture0: ITextureObject, + texture2: ITextureObject, + smooth: boolean = false +): void => { + textureManagerBindService(2, $gl.TEXTURE2, texture2, smooth); + textureManagerBindService(1, $gl.TEXTURE1, null); + textureManagerBindService(0, $gl.TEXTURE0, texture0, smooth); +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.test.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.test.ts new file mode 100644 index 00000000..dd58d4b2 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.test.ts @@ -0,0 +1,59 @@ +import { execute } from "./TextureManagerBind0UseCase"; +import { describe, expect, it, vi } from "vitest"; +import { + $activeTextureUnit, + $setActiveTextureUnit, + $boundTextures +} from "../../TextureManager"; + +describe("TextureManagerBind0UseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "bindTexture" }), + "TEXTURE0": 0, + "TEXTURE1": 1, + "TEXTURE2": 2 + } + } + }); + + const textureObject = { + "resource": {} as WebGLTexture, + "width": 200, + "height": 300, + "area": 200 * 300 + }; + + // not hit + $setActiveTextureUnit(-1); + expect($activeTextureUnit).toBe(-1); + + const mock = { + "resource": {} as WebGLTexture, + "width": 100, + "height": 100, + "area": 100 * 100 + }; + $boundTextures[0] = null; + $boundTextures[1] = mock; + $boundTextures[2] = mock; + expect($boundTextures[0]).toBe(null); + expect($boundTextures[1]).toBe(mock); + expect($boundTextures[2]).toBe(mock); + + execute(textureObject) + expect($activeTextureUnit).toBe(0); + expect($boundTextures[0]).toBe(textureObject); + expect($boundTextures[1]).toBe(null); + expect($boundTextures[2]).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.ts new file mode 100644 index 00000000..9e2d0d17 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerBind0UseCase.ts @@ -0,0 +1,20 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerBindService } from "../service/TextureManagerBindService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description テクスチャをTEXTURE0にバインドし、TEXTURE1、TEXTURE2をアンバインドします。 + * Bind the texture to TEXTURE0 and unbind TEXTURE1 and TEXTURE2. + * + * @param {ITextureObject} texture0 + * @param {boolean} [smooth=false] + * @return {void} + * @method + * @protected + */ +export const execute = (texture0: ITextureObject, smooth: boolean = false): void => +{ + textureManagerBindService(2, $gl.TEXTURE2, null); + textureManagerBindService(1, $gl.TEXTURE1, null); + textureManagerBindService(0, $gl.TEXTURE0, texture0, smooth); +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.test.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.test.ts new file mode 100644 index 00000000..cab35ea7 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.test.ts @@ -0,0 +1,40 @@ +import { execute } from "./TextureManagerCreateAtlasTextureUseCase"; +import { describe, expect, it, vi } from "vitest"; +import { $setActiveTextureUnit } from "../../TextureManager"; +import { $RENDER_MAX_SIZE } from "../../WebGLUtil.ts"; + +describe("TextureManagerCreateAtlasTextureUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "activeTexture": vi.fn((v) => + { + expect(v).toBe(0); + }), + "createTexture": vi.fn(() => { return "createTexture" }), + "bindTexture": vi.fn((a, v) => + { + expect(v).toBe("createTexture"); + }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }), + "TEXTURE0": 0, + "TEXTURE3": 0, + } + } + }); + + // not hit + $setActiveTextureUnit(-1); + + const textureObject = execute(); + expect(textureObject.width).toBe($RENDER_MAX_SIZE); + expect(textureObject.height).toBe($RENDER_MAX_SIZE); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.ts new file mode 100644 index 00000000..74de8f27 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateAtlasTextureUseCase.ts @@ -0,0 +1,37 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerCreateTextureObjectService } from "../service/TextureManagerCreateTextureObjectService"; +import { $activeTextureUnit } from "../../TextureManager"; +import { + $RENDER_MAX_SIZE, + $gl +} from "../../WebGLUtil"; + +/** + * @description アトラス専用のテクスチャを作成します。 + * Create a texture for the atlas. + * + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (): ITextureObject => +{ + const textureObject = textureManagerCreateTextureObjectService($RENDER_MAX_SIZE, $RENDER_MAX_SIZE); + + $gl.activeTexture($gl.TEXTURE3); + $gl.bindTexture($gl.TEXTURE_2D, textureObject.resource); + + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_WRAP_S, $gl.CLAMP_TO_EDGE); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_WRAP_T, $gl.CLAMP_TO_EDGE); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MIN_FILTER, $gl.LINEAR); + $gl.texParameteri($gl.TEXTURE_2D, $gl.TEXTURE_MAG_FILTER, $gl.NEAREST); + + $gl.texStorage2D($gl.TEXTURE_2D, 1, $gl.RGBA8, textureObject.width, textureObject.height); + + $gl.activeTexture($activeTextureUnit !== -1 + ? $activeTextureUnit + : $gl.TEXTURE0 + ); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromCanvasUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromCanvasUseCase.ts new file mode 100644 index 00000000..27973bb3 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromCanvasUseCase.ts @@ -0,0 +1,32 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerGetTextureUseCase } from "./TextureManagerGetTextureUseCase"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description OffscreenCanvasからテクスチャを作成します。 + * Create a texture from OffscreenCanvas data. + * + * @param {number} width + * @param {number} height + * @param {OffscreenCanvas | ImageBitmap} element + * @param {boolean} [smooth=false] + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + width: number, + height: number, + element: OffscreenCanvas | ImageBitmap, + smooth: boolean = false +): ITextureObject => { + + const textureObject = textureManagerGetTextureUseCase(width, height, smooth); + + $gl.texSubImage2D( + $gl.TEXTURE_2D, 0, 0, 0, + $gl.RGBA, $gl.UNSIGNED_BYTE, element + ); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.test.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.test.ts new file mode 100644 index 00000000..19d56ba9 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.test.ts @@ -0,0 +1,44 @@ +import { execute } from "./TextureManagerCreateFromPixelsUseCase"; +import { $setActiveTextureUnit } from "../../TextureManager"; +import { describe, expect, it, vi } from "vitest"; + +describe("TextureManagerCreateFromPixelsUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "activeTexture": vi.fn((v) => + { + expect(v).toBe(0); + }), + "createTexture": vi.fn(() => { return "createTexture" }), + "bindTexture": vi.fn((a, v) => + { + expect(v).toBe("createTexture"); + }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }), + "TEXTURE0": 0, + "TEXTURE3": 0, + "texSubImage2D": vi.fn((a, b, c, d, width, height, e, f, pixels) => { + expect(width).toBe(100); + expect(height).toBe(200); + expect(pixels.length).toBe(100 * 200 * 4); + }), + } + } + }); + + // not hit + $setActiveTextureUnit(-1); + const textureObject = execute(100, 200, new Uint8Array(100 * 200 * 4), true); + + expect(textureObject.width).toBe(100); + expect(textureObject.height).toBe(200); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.ts new file mode 100644 index 00000000..03de5b05 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerCreateFromPixelsUseCase.ts @@ -0,0 +1,32 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerGetTextureUseCase } from "./TextureManagerGetTextureUseCase"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description ピクセルデータからテクスチャを作成します。 + * Create a texture from pixel data. + * + * @param {number} width + * @param {number} height + * @param {Uint8Array} pixels + * @param {boolean} [smooth=false] + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + width: number, + height: number, + pixels: Uint8Array, + smooth: boolean = false +): ITextureObject => { + + const textureObject = textureManagerGetTextureUseCase(width, height, smooth); + + $gl.texSubImage2D( + $gl.TEXTURE_2D, 0, 0, 0, width, height, + $gl.RGBA, $gl.UNSIGNED_BYTE, pixels + ); + + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerGetMainTextureFromBoundsUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerGetMainTextureFromBoundsUseCase.ts new file mode 100644 index 00000000..d90c6079 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerGetMainTextureFromBoundsUseCase.ts @@ -0,0 +1,92 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import type { IAttachmentObject } from "../../interface/IAttachmentObject"; +import { execute as textureManagerGetTextureUseCase } from "./TextureManagerGetTextureUseCase"; +import { execute as textureManagerBind0UseCase } from "./TextureManagerBind0UseCase"; +import { + $context, + $gl +} from "../../WebGLUtil"; +import { + $getDrawBitmapFrameBuffer, + $readFrameBuffer +} from "../../FrameBufferManager"; + +/** + * @type {ITextureObject} + * @private + */ +let $mainTextureObject: ITextureObject | null = null; + +/** + * @description メインのフレームバッファから指定の範囲のテクスチャを描画して返却します。 + * Draw and return a texture from the specified range of the main framebuffer. + * + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = ( + x: number, + y: number, + width: number, + height: number +): ITextureObject => { + + const currentAttachmentObject = $context.currentAttachmentObject as IAttachmentObject; + + // メインのアタッチメントオブジェクトをセット + const mainAttachmentObject = $context.$mainAttachmentObject as IAttachmentObject; + $context.bind(mainAttachmentObject); + + const drawBitmapFrameBuffer = $getDrawBitmapFrameBuffer(); + $gl.bindFramebuffer($gl.FRAMEBUFFER, drawBitmapFrameBuffer); + + // 描画先のテクスチャを更新 + if (!$mainTextureObject + || $mainTextureObject.width !== mainAttachmentObject.width + || $mainTextureObject.height !== mainAttachmentObject.height + ) { + $mainTextureObject = textureManagerGetTextureUseCase( + mainAttachmentObject.width, mainAttachmentObject.height + ); + } + + textureManagerBind0UseCase($mainTextureObject); + $gl.framebufferTexture2D( + $gl.FRAMEBUFFER, $gl.COLOR_ATTACHMENT0, + $gl.TEXTURE_2D, $mainTextureObject.resource, 0 + ); + + $gl.bindFramebuffer($gl.FRAMEBUFFER, null); + $gl.bindFramebuffer($gl.READ_FRAMEBUFFER, $readFrameBuffer); + $gl.bindFramebuffer($gl.DRAW_FRAMEBUFFER, drawBitmapFrameBuffer); + + $gl.enable($gl.SCISSOR_TEST); + $gl.scissor( + x, + mainAttachmentObject.height - y - height, + width + 1, + height + 1 + ); + + // execute + $gl.blitFramebuffer( + 0, 0, mainAttachmentObject.width, mainAttachmentObject.height, + 0, 0, mainAttachmentObject.width, mainAttachmentObject.height, + $gl.COLOR_BUFFER_BIT, + $gl.NEAREST + ); + $gl.disable($gl.SCISSOR_TEST); + + $gl.bindFramebuffer($gl.FRAMEBUFFER, $readFrameBuffer); + + if (currentAttachmentObject) { + $context.bind(currentAttachmentObject); + } + + return $mainTextureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.test.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.test.ts new file mode 100644 index 00000000..76586def --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./TextureManagerGetTextureUseCase.ts"; +import { describe, expect, it, vi } from "vitest"; + +describe("TextureManagerGetTextureUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createTexture": vi.fn(() => { return "createTexture" }), + "activeTexture": vi.fn(() => { return "activeTexture" }), + "bindTexture": vi.fn(() => { return "bindTexture" }), + "texParameteri": vi.fn(() => { return "texParameteri" }), + "texStorage2D": vi.fn(() => { return "texStorage2D" }), + } + } + }); + + // new + // $objectPool.length = 0; + const newTextureObject = execute(320, 240); + expect(newTextureObject.width).toBe(320); + expect(newTextureObject.height).toBe(240); + expect(newTextureObject.area).toBe(320 * 240); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.ts new file mode 100644 index 00000000..bd96d0ad --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerGetTextureUseCase.ts @@ -0,0 +1,22 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { execute as textureManagerCreateTextureObjectService } from "../service/TextureManagerCreateTextureObjectService"; +import { execute as textureManagerInitializeBindService } from "../service/TextureManagerInitializeBindService"; + +/** + * @description オブジェクトプールにTextureObjectがあれば再利用、なければ新規作成して返却します。 + * If there is a TextureObject in the object pool, it will be reused, + * otherwise it will be created and returned. + * + * @param {number} width + * @param {number} height + * @param {boolean} [smooth=false] + * @return {ITextureObject} + * @method + * @protected + */ +export const execute = (width: number, height: number, smooth: boolean = false): ITextureObject => +{ + const textureObject = textureManagerCreateTextureObjectService(width, height); + textureManagerInitializeBindService(textureObject, smooth); + return textureObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.test.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.test.ts new file mode 100644 index 00000000..8e1bdf21 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.test.ts @@ -0,0 +1,35 @@ +import { execute } from "./TextureManagerReleaseTextureObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("TextureManagerReleaseTextureObjectUseCase.js method test", () => +{ + it("test case", async () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "deleteTexture": vi.fn((object) => { return object.delete = true; }) + } + } + }); + + const textureObject = { + "resource": { + "delete": false + } as unknown as WebGLTexture, + "width": 620, + "height": 480, + "area": 620 * 480, + "dirty": false, + }; + + // @ts-ignore + expect(textureObject.resource.delete).toBe(false); + execute(textureObject); + // @ts-ignore + expect(textureObject.resource.delete).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.ts b/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.ts new file mode 100644 index 00000000..17c88f35 --- /dev/null +++ b/packages/webgl/src/TextureManager/usecase/TextureManagerReleaseTextureObjectUseCase.ts @@ -0,0 +1,16 @@ +import type { ITextureObject } from "../../interface/ITextureObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description TextureObjectをオブジェクトプールに保管、サイズオーバー時は削除します。 + * Stores the TextureObject in the object pool and deletes it if it exceeds the size. + * + * @param {ITextureObject} texture_object + * @return {void} + * @method + * @protected + */ +export const execute = (texture_object: ITextureObject): void => +{ + $gl.deleteTexture(texture_object.resource); +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject.ts b/packages/webgl/src/VertexArrayObject.ts new file mode 100644 index 00000000..cefd9c3a --- /dev/null +++ b/packages/webgl/src/VertexArrayObject.ts @@ -0,0 +1,120 @@ +import type { IVertexArrayObject } from "./interface/IVertexArrayObject"; +import type { IStrokeVertexArrayObject } from "./interface/IStrokeVertexArrayObject"; +import { execute as vertexArrayObjectCreateRectVertexArrayObjectUseCase } from "./VertexArrayObject/usecase/VertexArrayObjectCreateRectVertexArrayObjectUseCase"; + +/** + * @type {number} + * @private + */ +let $id: number = 0; + +/** + * @description VertexArrayObject管理用のユニークIDを返却 + * Returns a unique ID for managing VertexArrayObject + * + * @return {number} + * @method + * @protected + */ +export const $getId = (): number => +{ + return $id++; +}; + +/** + * @description VertexArrayObjectの再利用のための配列のオブジェクトプール + * Object pool of array for reusing VertexArrayObject + * + * @type {IVertexArrayObject[]} + * @protected + */ +export const $objectPool: IVertexArrayObject[] = []; + +/** + * @description Stroke用のVertexArrayObjectの再利用のための配列のオブジェクトプール + * Object pool of array for reusing VertexArrayObject for Stroke + * + * @type {IStrokeVertexArrayObject[]} + * @protected + */ +export const $strokeObjectPool: IStrokeVertexArrayObject[] = []; + +/** + * @description 頂点バッファのデータ、 + * Vertex buffer data + * + * @type {Float32Array} + * @protected + */ +export const $vertexBufferData: Float32Array = new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]); + +/** + * @description インスタンス用のWebGLBuffer + * WebGLBuffer for instance + * + * @type {WebGLBuffer} + * @protected + */ +export let $attributeWebGLBuffer: WebGLBuffer; + +/** + * @description インスタンス用のWebGLBufferをセット + * Set the WebGLBuffer for the instance + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setAttributeWebGLBuffer = (gl: WebGL2RenderingContext): void => +{ + $attributeWebGLBuffer = gl.createBuffer() as NonNullable; +}; + +/** + * @description インスタンス用のVertexArrayObject + * VertexArrayObject for instance + * + * @type {IVertexArrayObject} + * @protected + */ +export let $instancedVertexArrayObject: IVertexArrayObject; + +/** + * @description インスタンス用のVertexArrayObjectをセット + * Set the VertexArrayObject for the instance + * + * @param {IVertexArrayObject} vertex_array_object + * @return {void} + * @method + * @protected + */ +export const $setInstancedVertexArrayObject = (vertex_array_object: IVertexArrayObject): void => +{ + $instancedVertexArrayObject = vertex_array_object; +}; + +/** + * @description 矩形描画用のVertexArrayObject + * VertexArrayObject for rectangle drawing + * + * @type {IVertexArrayObject} + * @protected + */ +let $rectVertexArrayObject: IVertexArrayObject; + +/** + * @description 矩形描画用のVertexArrayObjectを返却 + * Returns the VertexArrayObject for rectangle drawing + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const $getRectVertexArrayObject = (): IVertexArrayObject => +{ + if (!$rectVertexArrayObject) { + $rectVertexArrayObject = vertexArrayObjectCreateRectVertexArrayObjectUseCase(); + } + return $rectVertexArrayObject as NonNullable; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.test.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.test.ts new file mode 100644 index 00000000..dc2683f7 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.test.ts @@ -0,0 +1,30 @@ +import { execute } from "./VertexArrayObjectBindService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VertexArrayObjectBindService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "bindVertexArray": vi.fn((resource) => { + expect(resource).toBe("createVertexArray"); + }), + }, + } + }); + + const vertexArrayObject = { + "resource": "createVertexArray", + "indexRanges": [], + "vertexBuffer": "createBuffer", + "vertexLength": 0, + }; + + execute(vertexArrayObject); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.ts new file mode 100644 index 00000000..59ecc067 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectBindService.ts @@ -0,0 +1,32 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description 現在バインドされているVertexArrayObject + * VertexArrayObject currently bound + * + * @type {IVertexArrayObject} + * @private + */ +let $boundsVertexArrayObject: IVertexArrayObject; + +/** + * @description 指定のVertexArrayObjectのresourceをバインドする + * Bind the resource of the specified VertexArrayObject + * + * @param {IVertexArrayObject} vertex_array_object + * @return {void} + * @method + * @protected + */ +export const execute = (vertex_array_object: IVertexArrayObject): void => +{ + if ($boundsVertexArrayObject + && $boundsVertexArrayObject.id === vertex_array_object.id + ) { + return ; + } + + $boundsVertexArrayObject = vertex_array_object; + $gl.bindVertexArray(vertex_array_object.resource); +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.test.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.test.ts new file mode 100644 index 00000000..b8c3d282 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.test.ts @@ -0,0 +1,25 @@ +import { execute } from "./VertexArrayObjectCreateFillObjectService"; +import { describe, expect, it, vi } from "vitest"; + +describe("VertexArrayObjectCreateFillObjectService.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createVertexArray": vi.fn(() => { return "createVertexArray" }), + "createBuffer": vi.fn(() => { return "createBuffer" }), + } + } + }); + + const vertexArrayObject = execute(); + expect(vertexArrayObject.resource).toBe("createVertexArray"); + expect(vertexArrayObject.vertexBuffer).toBe("createBuffer"); + expect(vertexArrayObject.vertexLength).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.ts new file mode 100644 index 00000000..1f6d7248 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectCreateFillObjectService.ts @@ -0,0 +1,21 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { $gl } from "../../WebGLUtil"; +import { $getId } from "../../VertexArrayObject"; + +/** + * @description 新規のVertexArrayObjectを生成する + * Create a new VertexArrayObject + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (): IVertexArrayObject => +{ + return { + "id": $getId(), + "resource": $gl.createVertexArray() as NonNullable, + "vertexBuffer": $gl.createBuffer() as NonNullable, + "vertexLength": 0 + }; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.test.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.test.ts new file mode 100644 index 00000000..4a534c57 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.test.ts @@ -0,0 +1,22 @@ +import { execute } from "./VertexArrayObjectReleaseVertexArrayObjectService"; +import { describe, expect, it } from "vitest"; +import { $objectPool } from "../../VertexArrayObject"; + +describe("VertexArrayObjectReleaseVertexArrayObjectService.js method test", () => +{ + it("test case", () => + { + $objectPool.length = 0; + const vertexArrayObject = { + "id": 1, + "resource": "createVertexArray", + "indexCount": 6, + "vertexBuffer": "createBuffer", + "vertexLength": 0, + }; + + expect($objectPool.length).toBe(0); + execute(vertexArrayObject); + expect($objectPool.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.ts b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.ts new file mode 100644 index 00000000..f5b6d2a8 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/service/VertexArrayObjectReleaseVertexArrayObjectService.ts @@ -0,0 +1,19 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { $objectPool } from "../../VertexArrayObject"; + +/** + * @description VertexArrayObjectをオブジェクトプールに保管、サイズオーバー時は削除します。 + * Stores the VertexArrayObject in the object pool and deletes it if it exceeds the size. + * + * @param {IVertexArrayObject} vertex_array_object + * @return {void} + * @method + * @protected + */ +export const execute = (vertex_array_object: IVertexArrayObject): void => +{ + if ($objectPool.indexOf(vertex_array_object) > -1) { + return ; + } + $objectPool.push(vertex_array_object); +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindAttributeUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindAttributeUseCase.ts new file mode 100644 index 00000000..e2aacf29 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindAttributeUseCase.ts @@ -0,0 +1,44 @@ +import { execute as vertexArrayObjectBindService } from "../service/VertexArrayObjectBindService"; +import { $gl } from "../../WebGLUtil"; +import { + $instancedVertexArrayObject, + $attributeWebGLBuffer +} from "../../VertexArrayObject"; +import { renderQueue } from "@next2d/render-queue"; + +/** + * @type {number} + * @private + */ +let $attributeBufferLength: number = 0; + +/** + * @description インスタンス用のデータをバインドします。 + * Binds data for instances. + * + * @return {void} + * @method + * @protected + */ +export const execute = (): void => +{ + vertexArrayObjectBindService($instancedVertexArrayObject); + + $gl.bindBuffer($gl.ARRAY_BUFFER, $attributeWebGLBuffer); + + if (renderQueue.buffer.length > $attributeBufferLength) { + + $attributeBufferLength = renderQueue.buffer.length; + + $gl.bufferData( + $gl.ARRAY_BUFFER, + $attributeBufferLength * 4, // renderQueue.buffer.byteLength + $gl.DYNAMIC_DRAW + ); + } + + $gl.bufferSubData( + $gl.ARRAY_BUFFER, 0, + renderQueue.buffer.subarray(0, renderQueue.offset) + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.test.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.test.ts new file mode 100644 index 00000000..b16d9c42 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.test.ts @@ -0,0 +1,36 @@ +import { execute } from "./VertexArrayObjectBindFillMeshUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("VertexArrayObjectBindFillMeshUseCase.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createVertexArray": vi.fn(() => { return "createVertexArray" }), + "createBuffer": vi.fn(() => { return "createBuffer" }), + "bindVertexArray": vi.fn(() => { return "bindVertexArray" }), + "bindBuffer": vi.fn(() => { return "bindBuffer" }), + "enableVertexAttribArray": vi.fn(() => { return "enableVertexAttribArray" }), + "vertexAttribPointer": vi.fn(() => { return "vertexAttribPointer" }), + "bufferData": vi.fn(() => { return "bufferData" }), + "bufferSubData": vi.fn(() => { return "bufferSubData" }), + }, + $context: { + "$fillStyle": new Float32Array([0, 0, 0, 1]), + "$matrix": new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]), + } + } + }); + + const vertexArrayObject = execute(); + + expect(vertexArrayObject.resource).toBe("createVertexArray"); + expect(vertexArrayObject.vertexBuffer).toBe("createBuffer"); + expect(vertexArrayObject.vertexLength).toBe(128); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.ts new file mode 100644 index 00000000..48d1acd3 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBindFillMeshUseCase.ts @@ -0,0 +1,38 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as vertexArrayObjectGetFillObjectUseCase } from "../../VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase"; +import { execute as vertexArrayObjectBindService } from "../../VertexArrayObject/service/VertexArrayObjectBindService"; +import { + $getFillBuffer, + $getFillBufferOffset +} from "../../Mesh"; +import { + $gl, + $upperPowerOfTwo +} from "../../WebGLUtil"; + +/** + * @description 塗りのコマンドからメッシュを生成して、VertexArrayにバインドする + * Generate a mesh from the fill command and bind it to the VertexArray + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (): IVertexArrayObject => +{ + const vertexArrayObject = vertexArrayObjectGetFillObjectUseCase(); + vertexArrayObjectBindService(vertexArrayObject); + + const buffer = $getFillBuffer(); + const offset = $getFillBufferOffset(); + + $gl.bindBuffer($gl.ARRAY_BUFFER, vertexArrayObject.vertexBuffer); + if (vertexArrayObject.vertexLength < buffer.length) { + vertexArrayObject.vertexLength = $upperPowerOfTwo(buffer.length); + $gl.bufferData($gl.ARRAY_BUFFER, vertexArrayObject.vertexLength * 4, $gl.DYNAMIC_DRAW); + } + + $gl.bufferSubData($gl.ARRAY_BUFFER, 0, buffer.subarray(0, offset)); + + return vertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBootUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBootUseCase.ts new file mode 100644 index 00000000..9e9c99a6 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectBootUseCase.ts @@ -0,0 +1,26 @@ +import { execute as vertexArrayObjectCreateInstancedVertexArrayObjectUseCase } from "./VertexArrayObjectCreateInstancedVertexArrayObjectUseCase"; +import { + $setInstancedVertexArrayObject, + $setAttributeWebGLBuffer +} from "../../VertexArrayObject"; + +/** + * @description VertexArrayObjectの起動ユースケース + * VertexArrayObject boot use case + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const execute = (gl: WebGL2RenderingContext): void => +{ + // インスタンス用のバッファをセット + // fixed logic + $setAttributeWebGLBuffer(gl); + + // Array Instance用のVertexArrayObjectをセット + $setInstancedVertexArrayObject( + vertexArrayObjectCreateInstancedVertexArrayObjectUseCase() + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateGradientVertexArrayObjectUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateGradientVertexArrayObjectUseCase.ts new file mode 100644 index 00000000..2048c05e --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateGradientVertexArrayObjectUseCase.ts @@ -0,0 +1,34 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as vertexArrayObjectCreateFillObjectService } from "../service/VertexArrayObjectCreateFillObjectService"; +import { execute as vertexArrayObjectBindService } from "../service/VertexArrayObjectBindService"; +import { $gl } from "../../WebGLUtil"; +import { $vertexBufferData } from "../../VertexArrayObject"; + +/** + * @description インスタンス用の頂点配列オブジェクトを生成します。 + * Generates a vertex array object for instances. + * + * @param {number} begin + * @param {number} end + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (begin: number, end: number): IVertexArrayObject => +{ + const vertexArrayObject = vertexArrayObjectCreateFillObjectService(); + vertexArrayObjectBindService(vertexArrayObject); + + $gl.bindBuffer($gl.ARRAY_BUFFER, vertexArrayObject.vertexBuffer); + + $vertexBufferData[0] = begin; + $vertexBufferData[2] = begin; + $vertexBufferData[4] = end; + $vertexBufferData[6] = end; + $gl.bufferData($gl.ARRAY_BUFFER, $vertexBufferData, $gl.STATIC_DRAW); + + $gl.enableVertexAttribArray(0); + $gl.vertexAttribPointer(0, 2, $gl.FLOAT, false, 0, 0); + + return vertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateInstancedVertexArrayObjectUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateInstancedVertexArrayObjectUseCase.ts new file mode 100644 index 00000000..c16dddd7 --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateInstancedVertexArrayObjectUseCase.ts @@ -0,0 +1,64 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as vertexArrayObjectCreateFillObjectService } from "../service/VertexArrayObjectCreateFillObjectService"; +import { execute as vertexArrayObjectBindService } from "../service/VertexArrayObjectBindService"; +import { $gl } from "../../WebGLUtil"; +import { renderQueue } from "@next2d/render-queue"; +import { $attributeWebGLBuffer } from "../../VertexArrayObject"; + +/** + * @description インスタンス用の頂点配列オブジェクトを生成します。 + * Generates a vertex array object for instances. + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (): IVertexArrayObject => +{ + const vertexArrayObject = vertexArrayObjectCreateFillObjectService(); + vertexArrayObjectBindService(vertexArrayObject); + + $gl.bindBuffer($gl.ARRAY_BUFFER, vertexArrayObject.vertexBuffer); + $gl.bufferData($gl.ARRAY_BUFFER, new Float32Array([ + 0, 0, 1, 0, 0, 1, + 1, 1, 0, 1, 1, 0 + ]), $gl.STATIC_DRAW); + + $gl.enableVertexAttribArray(0); + $gl.vertexAttribPointer(0, 2, $gl.FLOAT, false, 0, 0); + + $gl.bindBuffer($gl.ARRAY_BUFFER, $attributeWebGLBuffer); + $gl.bufferData($gl.ARRAY_BUFFER, renderQueue.buffer.length, $gl.DYNAMIC_DRAW); + + // texture rectangle + $gl.enableVertexAttribArray(1); + $gl.vertexAttribPointer(1, 4, $gl.FLOAT, false, 88, 0); + $gl.vertexAttribDivisor(1, 1); + + // texture width, height and viewport width, height + $gl.enableVertexAttribArray(2); + $gl.vertexAttribPointer(2, 4, $gl.FLOAT, false, 88, 16); + $gl.vertexAttribDivisor(2, 1); + + // matrix tx, ty + $gl.enableVertexAttribArray(3); + $gl.vertexAttribPointer(3, 2, $gl.FLOAT, false, 88, 32); + $gl.vertexAttribDivisor(3, 1); + + // matrix scale0, rotate0, scale1, rotate1 + $gl.enableVertexAttribArray(4); + $gl.vertexAttribPointer(4, 4, $gl.FLOAT, false, 88, 40); + $gl.vertexAttribDivisor(4, 1); + + // mulColor + $gl.enableVertexAttribArray(5); + $gl.vertexAttribPointer(5, 4, $gl.FLOAT, false, 88, 56); + $gl.vertexAttribDivisor(5, 1); + + // addColor + $gl.enableVertexAttribArray(6); + $gl.vertexAttribPointer(6, 4, $gl.FLOAT, false, 88, 72); + $gl.vertexAttribDivisor(6, 1); + + return vertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateRectVertexArrayObjectUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateRectVertexArrayObjectUseCase.ts new file mode 100644 index 00000000..8d4dd0ad --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectCreateRectVertexArrayObjectUseCase.ts @@ -0,0 +1,31 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { execute as vertexArrayObjectCreateFillObjectService } from "../service/VertexArrayObjectCreateFillObjectService"; +import { execute as vertexArrayObjectBindService } from "../service/VertexArrayObjectBindService"; +import { $gl } from "../../WebGLUtil"; + +/** + * @description 矩形描画用のVertexArrayObjectを生成します。 + * Generates a VertexArrayObject for rectangle drawing. + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (): IVertexArrayObject => +{ + const vertexArrayObject = vertexArrayObjectCreateFillObjectService(); + vertexArrayObjectBindService(vertexArrayObject); + + $gl.bindBuffer($gl.ARRAY_BUFFER, vertexArrayObject.vertexBuffer); + + const vertexBufferData = new Float32Array([ + 0, 0, 1, 0, 0, 1, + 1, 1, 0, 1, 1, 0 + ]); + $gl.bufferData($gl.ARRAY_BUFFER, vertexBufferData, $gl.STATIC_DRAW); + + $gl.enableVertexAttribArray(0); + $gl.vertexAttribPointer(0, 2, $gl.FLOAT, false, 0, 0); + + return vertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.test.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.test.ts new file mode 100644 index 00000000..93b12e6d --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.test.ts @@ -0,0 +1,37 @@ +import { execute } from "./VertexArrayObjectGetFillObjectUseCase"; +import { describe, expect, it, vi } from "vitest"; + +describe("VertexArrayObjectGetFillObjectUseCase.js method test", () => +{ + it("test case", () => + { + vi.mock("../../WebGLUtil.ts", async (importOriginal) => + { + let enableVertexAttribArray = 0; + let vertexAttribPointer = 0; + const mod = await importOriginal(); + return { + ...mod, + $gl: { + "createVertexArray": vi.fn(() => { return "createVertexArray" }), + "createBuffer": vi.fn(() => { return "createBuffer" }), + "bindVertexArray": vi.fn(() => { return "bindVertexArray" }), + "bindBuffer": vi.fn((target, buffer) => { + expect(buffer).toBe("createBuffer"); + }), + "enableVertexAttribArray": vi.fn((index) => { + expect(index).toBe(enableVertexAttribArray++); + }), + "vertexAttribPointer": vi.fn((index) => { + expect(index).toBe(vertexAttribPointer++); + }) + } + } + }); + + const vertexArrayObject = execute(); + expect(vertexArrayObject.resource).toBe("createVertexArray"); + expect(vertexArrayObject.vertexBuffer).toBe("createBuffer"); + expect(vertexArrayObject.vertexLength).toBe(0); + }); +}); \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.ts new file mode 100644 index 00000000..fa0801ea --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetFillObjectUseCase.ts @@ -0,0 +1,40 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { $objectPool } from "../../VertexArrayObject"; +import { $gl } from "../../WebGLUtil"; +import { execute as vertexArrayObjectCreateFillObjectService } from "../service/VertexArrayObjectCreateFillObjectService"; +import { execute as vertexArrayObjectBindService } from "../service/VertexArrayObjectBindService"; + +/** + * @description 塗り用のVertexArrayObjectを返却 + * Returns a VertexArrayObject for filling + * + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (): IVertexArrayObject => +{ + if ($objectPool.length) { + return $objectPool.pop() as NonNullable; + } + + const vertexArrayObject = vertexArrayObjectCreateFillObjectService(); + vertexArrayObjectBindService(vertexArrayObject); + + $gl.bindBuffer($gl.ARRAY_BUFFER, vertexArrayObject.vertexBuffer); + + $gl.enableVertexAttribArray(0); + $gl.enableVertexAttribArray(1); + $gl.enableVertexAttribArray(2); + $gl.enableVertexAttribArray(3); + $gl.enableVertexAttribArray(4); + $gl.enableVertexAttribArray(5); + $gl.vertexAttribPointer(0, 2, $gl.FLOAT, false, 68, 0); // vertex + $gl.vertexAttribPointer(1, 2, $gl.FLOAT, false, 68, 8); // bezier or uv + $gl.vertexAttribPointer(2, 4, $gl.FLOAT, false, 68, 16); // color + $gl.vertexAttribPointer(3, 3, $gl.FLOAT, false, 68, 32); // matrix_a + $gl.vertexAttribPointer(4, 3, $gl.FLOAT, false, 68, 44); // matrix_b + $gl.vertexAttribPointer(5, 3, $gl.FLOAT, false, 68, 56); // matrix_c + + return vertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetGradientObjectUseCase.ts b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetGradientObjectUseCase.ts new file mode 100644 index 00000000..807b1dcc --- /dev/null +++ b/packages/webgl/src/VertexArrayObject/usecase/VertexArrayObjectGetGradientObjectUseCase.ts @@ -0,0 +1,39 @@ +import type { IVertexArrayObject } from "../../interface/IVertexArrayObject"; +import { $vertexBufferData } from "../../VertexArrayObject"; +import { $gl } from "../../WebGLUtil"; +import { execute as vertexArrayObjectCreateGradientVertexArrayObjectUseCase } from "./VertexArrayObjectCreateGradientVertexArrayObjectUseCase"; + +/** + * @type {IVertexArrayObject} + * @private + */ +let $gradientVertexArrayObject: IVertexArrayObject; + +/** + * @description グラデーション用の頂点配列オブジェクトを生成 + * Generates a vertex array object for gradient + * + * @param {number} begin + * @param {number} end + * @return {IVertexArrayObject} + * @method + * @protected + */ +export const execute = (begin: number, end: number): IVertexArrayObject => +{ + if (!$gradientVertexArrayObject) { + $gradientVertexArrayObject = vertexArrayObjectCreateGradientVertexArrayObjectUseCase(begin, end); + } else { + if ($vertexBufferData[0] !== begin + || $vertexBufferData[4] !== end + ) { + $vertexBufferData[0] = begin; + $vertexBufferData[2] = begin; + $vertexBufferData[4] = end; + $vertexBufferData[6] = end; + $gl.bufferSubData($gl.ARRAY_BUFFER, 0, $vertexBufferData); + } + } + + return $gradientVertexArrayObject; +}; \ No newline at end of file diff --git a/packages/webgl/src/VertexArrayObjectManager.ts b/packages/webgl/src/VertexArrayObjectManager.ts deleted file mode 100644 index 8caf8a78..00000000 --- a/packages/webgl/src/VertexArrayObjectManager.ts +++ /dev/null @@ -1,439 +0,0 @@ -import { WebGLFillMeshGenerator } from "./WebGLFillMeshGenerator"; -import { WebGLStrokeMeshGenerator } from "./WebGLStrokeMeshGenerator"; -import type { FillMeshImpl } from "./interface/FillMeshImpl"; -import type { StrokeMethImpl } from "./interface/StrokeMethImpl"; -import type { CapsStyleImpl } from "./interface/CapsStyleImpl"; -import type { JointStyleImpl } from "./interface/JointStyleImpl"; -import type { WebGLShaderInstance } from "./shader/WebGLShaderInstance"; -import { - $upperPowerOfTwo -} from "@next2d/share"; - -/** - * @class - */ -export class VertexArrayObjectManager -{ - private _$gl: WebGL2RenderingContext; - private readonly _$fillVertexArrayPool: WebGLVertexArrayObject[]; - private readonly _$strokeVertexArrayPool: WebGLVertexArrayObject[]; - private _$boundVertexArray: WebGLVertexArrayObject | null; - private readonly _$fillAttrib_vertex: number; - private readonly _$fillAttrib_bezier: number; - private readonly _$strokeAttrib_vertex: number; - private readonly _$strokeAttrib_option1: number; - private readonly _$strokeAttrib_option2: number; - private readonly _$strokeAttrib_type: number; - private readonly _$vertexBufferData: Float32Array; - private readonly _$commonVertexArray: WebGLVertexArrayObject; - private readonly _$instanceVertexArray: WebGLVertexArrayObject; - private readonly _$attributeVertexBuffer: WebGLBuffer; - private _$attributeBuffer: Float32Array; - - /** - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (gl: WebGL2RenderingContext) - { - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$fillVertexArrayPool = []; - - /** - * @type {array} - * @private - */ - this._$strokeVertexArrayPool = []; - - /** - * @type {WebGLVertexArrayObject} - * @default null - * @private - */ - this._$boundVertexArray = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillAttrib_vertex = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$fillAttrib_bezier = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$strokeAttrib_vertex = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$strokeAttrib_option1 = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$strokeAttrib_option2 = 2; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$strokeAttrib_type = 3; - - /** - * @type {Float32Array} - * @default 0 - * @private - */ - this._$vertexBufferData = new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]); - - /** - * @type {WebGLBuffer} - * @private - */ - this._$attributeVertexBuffer = gl.createBuffer() as NonNullable; - - /** - * @type {Float32Array} - * @private - */ - this._$attributeBuffer = new Float32Array(22); - - /** - * @type {WebGLVertexArrayObject} - * @private - */ - this._$instanceVertexArray = this._$getCommonVertexArray(); - - /** - * @type {WebGLVertexArrayObject} - * @private - */ - this._$commonVertexArray = this._$getVertexArray(0, 1); - } - - /** - * @param {number} begin - * @param {number} end - * @return {WebGLVertexArrayObject} - * @method - * @private - */ - _$getCommonVertexArray (): WebGLVertexArrayObject - { - const vertexArray: WebGLVertexArrayObject = this._$gl.createVertexArray() as NonNullable; - this.bind(vertexArray); - - const vertexBuffer: WebGLBuffer = this._$gl.createBuffer() as NonNullable; - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexBuffer); - this._$gl.bufferData(this._$gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), this._$gl.STATIC_DRAW); - this._$gl.enableVertexAttribArray(0); - this._$gl.vertexAttribPointer(0, 2, this._$gl.FLOAT, false, 0, 0); - - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, this._$attributeVertexBuffer); - this._$gl.bufferData(this._$gl.ARRAY_BUFFER, this._$attributeBuffer.byteLength, this._$gl.DYNAMIC_DRAW); - this._$gl.enableVertexAttribArray(1); - this._$gl.vertexAttribPointer(1, 4, this._$gl.FLOAT, false, 88, 0); - this._$gl.vertexAttribDivisor(1, 1); - - this._$gl.enableVertexAttribArray(2); - this._$gl.vertexAttribPointer(2, 4, this._$gl.FLOAT, false, 88, 16); - this._$gl.vertexAttribDivisor(2, 1); - - this._$gl.enableVertexAttribArray(3); - this._$gl.vertexAttribPointer(3, 2, this._$gl.FLOAT, false, 88, 32); - this._$gl.vertexAttribDivisor(3, 1); - - this._$gl.enableVertexAttribArray(4); - this._$gl.vertexAttribPointer(4, 4, this._$gl.FLOAT, false, 88, 40); - this._$gl.vertexAttribDivisor(4, 1); - - this._$gl.enableVertexAttribArray(5); - this._$gl.vertexAttribPointer(5, 4, this._$gl.FLOAT, false, 88, 56); - this._$gl.vertexAttribDivisor(5, 1); - - this._$gl.enableVertexAttribArray(6); - this._$gl.vertexAttribPointer(6, 4, this._$gl.FLOAT, false, 88, 72); - this._$gl.vertexAttribDivisor(6, 1); - - return vertexArray; - } - - /** - * @param {number} begin - * @param {number} end - * @return {WebGLVertexArrayObject} - * @method - * @private - */ - _$getVertexArray (begin: number, end: number): WebGLVertexArrayObject - { - const vertexArray: WebGLVertexArrayObject = this._$gl.createVertexArray() as NonNullable; - this.bind(vertexArray); - - const vertexBuffer: WebGLBuffer = this._$gl.createBuffer() as NonNullable; - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexBuffer); - - this._$vertexBufferData[0] = begin; - this._$vertexBufferData[2] = begin; - this._$vertexBufferData[4] = end; - this._$vertexBufferData[6] = end; - this._$gl.bufferData(this._$gl.ARRAY_BUFFER, this._$vertexBufferData, this._$gl.STATIC_DRAW); - - this._$gl.enableVertexAttribArray(0); - this._$gl.vertexAttribPointer(0, 2, this._$gl.FLOAT, false, 0, 0); - - return vertexArray; - } - - /** - * @return {WebGLVertexArrayObject} - * @method - * @private - */ - _$getFillVertexArray (): WebGLVertexArrayObject - { - if (this._$fillVertexArrayPool.length) { - const vertexArray: WebGLVertexArrayObject | void = this._$fillVertexArrayPool.pop(); - if (vertexArray) { - return vertexArray; - } - } - - const vertexArray: WebGLVertexArrayObject = this._$gl.createVertexArray() as NonNullable; - this.bind(vertexArray); - - const vertexBuffer: WebGLBuffer = this._$gl.createBuffer() as NonNullable; - vertexArray.vertexBuffer = vertexBuffer; - vertexArray.vertexLength = 0; - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexBuffer); - - this._$gl.enableVertexAttribArray(0); - this._$gl.enableVertexAttribArray(1); - this._$gl.vertexAttribPointer(this._$fillAttrib_vertex, 2, this._$gl.FLOAT, false, 16, 0); - this._$gl.vertexAttribPointer(this._$fillAttrib_bezier, 2, this._$gl.FLOAT, false, 16, 8); - - return vertexArray; - } - - /** - * @return {WebGLVertexArrayObject} - * @method - * @private - */ - _$getStrokeVertexArray (): WebGLVertexArrayObject - { - if (this._$strokeVertexArrayPool.length) { - const vertexArray: WebGLVertexArrayObject | void = this._$strokeVertexArrayPool.pop(); - if (vertexArray) { - return vertexArray; - } - } - - const vertexArray: WebGLVertexArrayObject = this._$gl.createVertexArray() as NonNullable; - this.bind(vertexArray); - - const vertexBuffer: WebGLBuffer = this._$gl.createBuffer() as NonNullable; - vertexArray.vertexBuffer = vertexBuffer; - vertexArray.vertexLength = 0; - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexBuffer); - - const indexBuffer: WebGLBuffer = this._$gl.createBuffer() as NonNullable; - - vertexArray.indexBuffer = indexBuffer; - vertexArray.indexLength = 0; - this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER, indexBuffer); - - this._$gl.enableVertexAttribArray(0); - this._$gl.enableVertexAttribArray(1); - this._$gl.enableVertexAttribArray(2); - this._$gl.enableVertexAttribArray(3); - this._$gl.vertexAttribPointer(this._$strokeAttrib_vertex, 2, this._$gl.FLOAT, false, 28, 0); - this._$gl.vertexAttribPointer(this._$strokeAttrib_option1, 2, this._$gl.FLOAT, false, 28, 8); - this._$gl.vertexAttribPointer(this._$strokeAttrib_option2, 2, this._$gl.FLOAT, false, 28, 16); - this._$gl.vertexAttribPointer(this._$strokeAttrib_type, 1, this._$gl.FLOAT, false, 28, 24); - - return vertexArray; - } - - /** - * @param {array} vertices - * @return {WebGLVertexArrayObject} - * @method - * @public - */ - createFill (vertices: any[]): WebGLVertexArrayObject - { - const mesh: FillMeshImpl = WebGLFillMeshGenerator.generate(vertices); - const vertexBufferData: Float32Array = mesh.vertexBufferData; - - const vertexArray: WebGLVertexArrayObject = this._$getFillVertexArray(); - vertexArray.indexRanges = mesh.indexRanges; - this.bind(vertexArray); - - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexArray.vertexBuffer); - - if (vertexArray.vertexLength < vertexBufferData.length) { - vertexArray.vertexLength = $upperPowerOfTwo(vertexBufferData.length); - this._$gl.bufferData(this._$gl.ARRAY_BUFFER, vertexArray.vertexLength * 4, this._$gl.DYNAMIC_DRAW); - } - - this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER, 0, vertexBufferData); - - return vertexArray; - } - - /** - * @param {array} vertices - * @param {string} lineCap - * @param {string} lineJoin - * @return {WebGLVertexArrayObject} - * @method - * @public - */ - createStroke ( - vertices: any[], - lineCap: CapsStyleImpl, - lineJoin: JointStyleImpl - ): WebGLVertexArrayObject { - - const mesh: StrokeMethImpl = WebGLStrokeMeshGenerator.generate(vertices, lineCap, lineJoin); - const vertexBufferData: Float32Array = mesh.vertexBufferData; - const indexBufferData: Int16Array = mesh.indexBufferData; - - const vertexArray: WebGLVertexArrayObject = this._$getStrokeVertexArray(); - vertexArray.indexCount = indexBufferData.length; - this.bind(vertexArray); - - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, vertexArray.vertexBuffer); - this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER, vertexArray.indexBuffer); - - if (vertexArray.vertexLength < vertexBufferData.length) { - vertexArray.vertexLength = $upperPowerOfTwo(vertexBufferData.length); - this._$gl.bufferData(this._$gl.ARRAY_BUFFER, vertexArray.vertexLength * 4, this._$gl.DYNAMIC_DRAW); - } - - if (vertexArray.indexLength < indexBufferData.length) { - vertexArray.indexLength = $upperPowerOfTwo(indexBufferData.length); - this._$gl.bufferData(this._$gl.ELEMENT_ARRAY_BUFFER, vertexArray.indexLength * 2, this._$gl.DYNAMIC_DRAW); - } - - this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER, 0, vertexBufferData); - this._$gl.bufferSubData(this._$gl.ELEMENT_ARRAY_BUFFER, 0, indexBufferData); - - return vertexArray; - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @return {void} - * @method - * @public - */ - releaseFill (vertex_array: WebGLVertexArrayObject): void - { - this._$fillVertexArrayPool.push(vertex_array); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @return {void} - * @method - * @public - */ - releaseStroke (vertex_array: WebGLVertexArrayObject): void - { - this._$strokeVertexArrayPool.push(vertex_array); - } - - /** - * @param {WebGLVertexArrayObject} [vertex_array = null] - * @return {void} - * @method - * @public - */ - bind (vertex_array: WebGLVertexArrayObject | null = null): void - { - if (vertex_array === this._$boundVertexArray) { - return ; - } - - this._$boundVertexArray = vertex_array; - - this._$gl.bindVertexArray(vertex_array); - } - - /** - * @param {WebGLShaderInstance} instance - * @return {void} - * @method - * @public - */ - bindInstnceArray (instance: WebGLShaderInstance): void - { - // bind vao - this.bind(this._$instanceVertexArray); - - this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER, this._$attributeVertexBuffer); - if (instance.attributes.length > this._$attributeBuffer.length) { - this._$attributeBuffer = new Float32Array(instance.attributes.length); - this._$gl.bufferData( - this._$gl.ARRAY_BUFFER, - this._$attributeBuffer.byteLength, - this._$gl.DYNAMIC_DRAW - ); - } - this._$attributeBuffer.set(instance.attributes); - this._$gl.bufferSubData( - this._$gl.ARRAY_BUFFER, 0, - this._$attributeBuffer.subarray(0, instance.attributes.length) - ); - } - - /** - * @return {void} - * @method - * @public - */ - bindCommonVertexArray (): void - { - this.bind(this._$commonVertexArray); - } - - /** - * @param {number} begin - * @param {number} end - * @return {void} - * @method - * @public - */ - bindGradientVertexArray (begin: number, end: number): void - { - const vertexArray: WebGLVertexArrayObject = this._$getVertexArray(begin, end); - this.bind(vertexArray); - } -} \ No newline at end of file diff --git a/packages/webgl/src/WebGLFillMeshGenerator.ts b/packages/webgl/src/WebGLFillMeshGenerator.ts deleted file mode 100644 index fa00f54c..00000000 --- a/packages/webgl/src/WebGLFillMeshGenerator.ts +++ /dev/null @@ -1,135 +0,0 @@ -import type { FillMeshImpl } from "./interface/FillMeshImpl"; -import type { IndexRangeImpl } from "./interface/IndexRangeImpl"; -import { - $Float32Array, - $getArray -} from "@next2d/share"; - -/** - * @class - */ -export class WebGLFillMeshGenerator -{ - private static _$vertexBufferData: Float32Array; - private static _$indexRanges: IndexRangeImpl[]; - private static _$indexRangePool: IndexRangeImpl[] = $getArray(); - private static _$currentIndex: number; - - /** - * @member {array} - * @static - */ - static get indexRangePool () - { - return this._$indexRangePool; - } - - /** - * @param {array} vertices - * @return {FillMeshImpl} - * @method - * @static - */ - static generate (vertices: any[]): FillMeshImpl - { - let vertexBufferLen: number = 0; - for (let idx = 0; idx < vertices.length; ++idx) { - vertexBufferLen += (vertices[idx].length / 3 - 2) * 12; - } - - this._$vertexBufferData = new $Float32Array(vertexBufferLen); - this._$indexRanges = $getArray(); - this._$currentIndex = 0; - - for (let idx: number = 0; idx < vertices.length; ++idx) { - - const first: number = this._$currentIndex; - this._$generateMesh(vertices[idx]); - const count: number = this._$currentIndex - first; - - const indexRange: IndexRangeImpl = this._$indexRangePool.pop() || { "first": 0, "count": 0 }; - indexRange.first = first; - indexRange.count = count; - this._$indexRanges.push(indexRange); - } - - return { - "vertexBufferData": this._$vertexBufferData, - "indexRanges" : this._$indexRanges - }; - } - - /** - * @param {array} vertex - * @return {void} - * @method - * @static - * @private - */ - static _$generateMesh (vertex: any[]): void - { - const vbd: Float32Array = this._$vertexBufferData; - let currentIndex: number = this._$currentIndex; - - const length: number = vertex.length - 5; - for (let v: number = 3; v < length; v += 3) { - - let i: number = currentIndex * 4; - if (vertex[v + 2]) { - - vbd[i++] = vertex[v - 3]; - vbd[i++] = vertex[v - 2]; - vbd[i++] = 0; - vbd[i++] = 0; - - vbd[i++] = vertex[v]; - vbd[i++] = vertex[v + 1]; - vbd[i++] = 0.5; - vbd[i++] = 0; - - vbd[i++] = vertex[v + 3]; - vbd[i++] = vertex[v + 4]; - vbd[i++] = 1; - vbd[i++] = 1; - - } else if (vertex[v + 5]) { - - vbd[i++] = vertex[0]; - vbd[i++] = vertex[1]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - vbd[i++] = vertex[v]; - vbd[i++] = vertex[v + 1]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - vbd[i++] = vertex[v + 6]; - vbd[i++] = vertex[v + 7]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - } else { - - vbd[i++] = vertex[0]; - vbd[i++] = vertex[1]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - vbd[i++] = vertex[v]; - vbd[i++] = vertex[v + 1]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - vbd[i++] = vertex[v + 3]; - vbd[i++] = vertex[v + 4]; - vbd[i++] = 0.5; - vbd[i++] = 0.5; - - } - currentIndex += 3; - } - - this._$currentIndex = currentIndex; - } -} \ No newline at end of file diff --git a/packages/webgl/src/WebGLStrokeMeshGenerator.ts b/packages/webgl/src/WebGLStrokeMeshGenerator.ts deleted file mode 100644 index 48c9286e..00000000 --- a/packages/webgl/src/WebGLStrokeMeshGenerator.ts +++ /dev/null @@ -1,691 +0,0 @@ -import type { StrokeMethImpl } from "./interface/StrokeMethImpl"; -import type { JointStyleImpl } from "./interface/JointStyleImpl"; -import type { CapsStyleImpl } from "./interface/CapsStyleImpl"; -import { - $Float32Array, - $Int16Array, - $Math -} from "@next2d/share"; - -/** - * @class - */ -export class WebGLStrokeMeshGenerator -{ - private static _$vertexBufferData: Float32Array; - private static _$vertexBufferPos: number; - private static _$indexBufferData: Int16Array; - private static _$indexBufferPos: number; - private static _$lineCap: string; - private static _$lineJoin: string; - - /** - * @param {array} vertices - * @param {string} line_cap - * @param {string} line_join - * @return {object} - * @method - * @static - */ - static generate ( - vertices: any[], - line_cap: CapsStyleImpl, - line_join: JointStyleImpl - ): StrokeMethImpl { - - this._$vertexBufferData = this._$vertexBufferData || new $Float32Array(1024); - this._$vertexBufferPos = 0; - - this._$indexBufferData = this._$indexBufferData || new Int16Array(256); - this._$indexBufferPos = 0; - - this._$lineCap = line_cap; - this._$lineJoin = line_join; - - for (let i: number = 0; i < vertices.length; i++) { - - const vertex_begin_offset: number = this._$vertexBufferPos; - this._$generateLineSegment(vertices[i]); - const vertex_end_offset: number = this._$vertexBufferPos; - - this._$generateLineJoin(vertex_begin_offset, vertex_end_offset); - this._$generateLineCap(vertex_begin_offset, vertex_end_offset); - } - - return { - "vertexBufferData": this._$vertexBufferData.slice(0, this._$vertexBufferPos), - "indexBufferData" : this._$indexBufferData.slice(0, this._$indexBufferPos) - }; - } - - /** - * @param {number} delta_length - * @return {void} - * @method - * @static - * @private - */ - static _$expandVertexBufferIfNeeded (delta_length: number): void - { - if (this._$vertexBufferPos + delta_length > this._$vertexBufferData.length) { - const biggerBuffer: Float32Array = new $Float32Array(this._$vertexBufferData.length * 2); - biggerBuffer.set(this._$vertexBufferData); - this._$vertexBufferData = biggerBuffer; - } - } - - /** - * @param {number} delta_length - * @return {void} - * @method - * @static - * @private - */ - static _$expandIndexBufferIfNeeded(delta_length: number): void - { - if (this._$indexBufferPos + delta_length > this._$indexBufferData.length) { - const biggerBuffer: Int16Array = new $Int16Array(this._$indexBufferData.length * 2); - biggerBuffer.set(this._$indexBufferData); - this._$indexBufferData = biggerBuffer; - } - } - - /** - * @param {Float32Array} vertex - * @return {void} - * @method - * @static - * @private - */ - static _$generateLineSegment (vertex: any[]): void - { - const length: number = vertex.length - 5; - for (let v: number = 0; v < length; v += 3) { - - if (vertex[v + 2]) { - continue; - } - - if (vertex[v + 5]) { - this._$addQuadSegmentMesh( - vertex[v], vertex[v + 1], - vertex[v + 3], vertex[v + 4], - vertex[v + 6], vertex[v + 7] - ); - } else { - this._$addLineSegmentMesh( - vertex[v], vertex[v + 1], - vertex[v + 3], vertex[v + 4] - ); - } - } - } - - /** - * @param {number} x1 線分の始点のx座標 - * @param {number} y1 線分の始点のy座標 - * @param {number} cx 線分の制御点のx座標 - * @param {number} cy 線分の制御点のy座標 - * @param {number} x2 線分の終点のx座標 - * @param {number} y2 線分の終点のy座標 - * @return {void} - * @method - * @static - * @private - */ - static _$addQuadSegmentMesh ( - x1: number, y1: number, - cx: number, cy: number, - x2: number, y2: number - ): void { - - const div: number = 11; - - let stx: number = x1; - let sty: number = y1; - for (let i: number = 1; i < div; i++) { - - const t: number = i / div; - const rt: number = 1 - t; - - const edx: number = (x1 * rt + cx * t) * rt + (cx * rt + x2 * t) * t; - const edy: number = (y1 * rt + cy * t) * rt + (cy * rt + y2 * t) * t; - - this._$addLineSegmentMesh(stx, sty, edx, edy, 2); - - stx = edx; - sty = edy; - } - - this._$addLineSegmentMesh(stx, sty, x2, y2); - } - - /** - * @param {number} x1 線分の始点のx座標 - * @param {number} y1 線分の始点のy座標 - * @param {number} x2 線分の終点のx座標 - * @param {number} y2 線分の終点のy座標 - * @param {number} [type = 1] - * @return {void} - * @method - * @static - * @private - */ - static _$addLineSegmentMesh ( - x1: number, y1: number, - x2: number, y2: number, - type: number = 1 - ): void { - - const index0: number = this._$vertexBufferPos / 7; - const index1: number = index0 + 1; - const index2: number = index0 + 2; - const index3: number = index0 + 3; - - this._$expandIndexBufferIfNeeded(6); - const ibd: Int16Array = this._$indexBufferData; - let ibp: number = this._$indexBufferPos; - - ibd[ibp++] = index0; - ibd[ibp++] = index1; - ibd[ibp++] = index3; - - ibd[ibp++] = index3; - ibd[ibp++] = index2; - ibd[ibp++] = index0; - - this._$indexBufferPos = ibp; - - this._$expandVertexBufferIfNeeded(28); - const vbd: Float32Array = this._$vertexBufferData; - let vbp: number = this._$vertexBufferPos; - - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = 1; - vbd[vbp++] = 1; - vbd[vbp++] = 1; - - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = -1; - vbd[vbp++] = -1; - vbd[vbp++] = 1; - - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = -1; - vbd[vbp++] = -1; - vbd[vbp++] = type; - - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = 1; - vbd[vbp++] = 1; - vbd[vbp++] = type; - - this._$vertexBufferPos = vbp; - } - - /** - * @param {number} vertex_begin_offset 結合対象の頂点の範囲(開始) - * @param {number} vertex_end_offset 結合対象の頂点の範囲(終了) - * @return {void} - * @method - * @static - * @private - */ - static _$generateLineJoin ( - vertex_begin_offset: number, - vertex_end_offset: number - ): void { - - const vbd: Float32Array = this._$vertexBufferData; - const length: number = vertex_end_offset - 55; - for (let v: number = vertex_begin_offset; v < length; v += 28) { - const indexOffset: number = v / 7; - this._$addLineJoinMesh( - vbd[v], vbd[v + 1], - vbd[v + 21], vbd[v + 22], vbd[v + 27], - vbd[v + 49], vbd[v + 50], - indexOffset + 2, indexOffset + 3, indexOffset + 4, indexOffset + 5 - ); - } - } - - /** - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @return {number} - * @method - * @static - */ - static _$cross = (x1: number, y1: number, x2: number, y2: number): number => - { - return x1 * y2 - x2 * y1; - }; - - /** - * @param {number} x1 線分Aの始点のx座標 - * @param {number} y1 線分Aの始点のy座標 - * @param {number} x2 結合点のx座標 - * @param {number} y2 結合点のy座標 - * @param {number} type 線分タイプ - * @param {number} x3 線分Bの終点のx座標 - * @param {number} y3 線分Bの終点のy座標 - * @param {number} index_offset2 線分Aの凸側の頂点インデックス - * @param {number} index_offset3 線分Aの凹側の頂点インデックス - * @param {number} index_offset4 線分Bの凸側の頂点インデックス - * @param {number} index_offset5 線分Bの凹側の頂点インデックス - * @return {void} - * @method - * @static - * @private - */ - static _$addLineJoinMesh ( - x1: number, y1: number, - x2: number, y2: number, - type: number, x3: number, y3: number, - index_offset2: number, index_offset3: number, - index_offset4: number = 0, index_offset5: number = 0 - ): void { - - // AとBがほぼ平行なら、結合せずに終了 - const ax: number = x2 - x1; - const ay: number = y2 - y1; - const bx: number = x3 - x2; - const by: number = y3 - y2; - const det: number = this._$cross(ax, ay, bx, by); - if ($Math.abs(det) < 0.0001) { - return ; - } - - // 分割したベジェ曲線はベベルで結合する - if (type === 2) { - this._$addBevelJoinMesh( - x2, y2, - index_offset4, index_offset2, index_offset3, index_offset5 - ); - return; - } - - // 結合タイプに合わせたメッシュを追加する - switch (this._$lineJoin) { - - case "round": - this._$addRoundJoinMesh(x2, y2); - break; - - case "miter": - this._$addMiterJoinMesh( - x2, y2, x1, y1, x3, y3, - index_offset4, index_offset2, index_offset3, index_offset5 - ); - break; - - default: - this._$addBevelJoinMesh( - x2, y2, - index_offset4, index_offset2, index_offset3, index_offset5 - ); - break; - - } - } - - /** - * @param {number} x 結合点のx座標 - * @param {number} y 結合点のy座標 - * @return {void} - * @method - * @static - * @private - */ - static _$addRoundJoinMesh (x: number, y: number): void - { - const index0: number = this._$vertexBufferPos / 7; - - this._$expandIndexBufferIfNeeded(57); - const ibd: Int16Array = this._$indexBufferData; - let ibp: number = this._$indexBufferPos; - - for (let i: number = 1; i < 18; i++) { - const indexN: number = index0 + i; - ibd[ibp++] = index0; - ibd[ibp++] = indexN; - ibd[ibp++] = indexN + 1; - } - - ibd[ibp++] = index0; - ibd[ibp++] = index0 + 18; - ibd[ibp++] = index0 + 1; - - this._$indexBufferPos = ibp; - - this._$expandVertexBufferIfNeeded(133); - const vbd: Float32Array = this._$vertexBufferData; - let vbp: number = this._$vertexBufferPos; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - - for (let i: number = 0; i < 18; i++) { - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 30 + i; - } - - this._$vertexBufferPos = vbp; - } - - /** - * @param {number} x 結合点のx座標 - * @param {number} y 結合点のy座標 - * @param {number} ax 線分Aの始点のx座標 - * @param {number} ay 線分Aの始点のy座標 - * @param {number} bx 線分Bの終点のx座標 - * @param {number} by 線分Bの終点のy座標 - * @param {number} index1 - * @param {number} index4 - * @param {number} index5 - * @param {number} index8 - * @return {void} - * @method - * @static - * @private - */ - static _$addMiterJoinMesh ( - x: number, y: number, - ax: number, ay: number, - bx: number, by: number, - index1: number, index4: number, - index5: number, index8: number - ): void { - - const index0: number = this._$vertexBufferPos / 7; - const index2: number = index0 + 1; - const index3: number = index0 + 2; - const index6: number = index0 + 3; - const index7: number = index0 + 4; - - this._$expandIndexBufferIfNeeded(18); - const ibd: Int16Array = this._$indexBufferData; - let ibp: number = this._$indexBufferPos; - - ibd[ibp++] = index0; - ibd[ibp++] = index1; - ibd[ibp++] = index2; - - ibd[ibp++] = index0; - ibd[ibp++] = index2; - ibd[ibp++] = index3; - - ibd[ibp++] = index0; - ibd[ibp++] = index3; - ibd[ibp++] = index4; - - ibd[ibp++] = index0; - ibd[ibp++] = index5; - ibd[ibp++] = index6; - - ibd[ibp++] = index0; - ibd[ibp++] = index6; - ibd[ibp++] = index7; - - ibd[ibp++] = index0; - ibd[ibp++] = index7; - ibd[ibp++] = index8; - - this._$indexBufferPos = ibp; - - this._$expandVertexBufferIfNeeded(35); - const vbd: Float32Array = this._$vertexBufferData; - let vbp: number = this._$vertexBufferPos; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = ax; - vbd[vbp++] = ay; - vbd[vbp++] = bx; - vbd[vbp++] = by; - vbd[vbp++] = 0; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = ax; - vbd[vbp++] = ay; - vbd[vbp++] = bx; - vbd[vbp++] = by; - vbd[vbp++] = 21; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = ax; - vbd[vbp++] = ay; - vbd[vbp++] = bx; - vbd[vbp++] = by; - vbd[vbp++] = 22; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = ax; - vbd[vbp++] = ay; - vbd[vbp++] = bx; - vbd[vbp++] = by; - vbd[vbp++] = 23; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = ax; - vbd[vbp++] = ay; - vbd[vbp++] = bx; - vbd[vbp++] = by; - vbd[vbp++] = 24; - - this._$vertexBufferPos = vbp; - } - - /** - * @param {number} x 結合点のx座標 - * @param {number} y 結合点のy座標 - * @param {number} index1 - * @param {number} index2 - * @param {number} index3 - * @param {number} index4 - * @return {void} - * @method - * @static - * @private - */ - static _$addBevelJoinMesh ( - x: number, y: number, - index1: number, index2: number, - index3: number, index4: number - ): void { - - const index0: number = this._$vertexBufferPos / 7; - - this._$expandIndexBufferIfNeeded(6); - const ibd: Int16Array = this._$indexBufferData; - let ibp: number = this._$indexBufferPos; - - ibd[ibp++] = index0; - ibd[ibp++] = index1; - ibd[ibp++] = index2; - - ibd[ibp++] = index0; - ibd[ibp++] = index3; - ibd[ibp++] = index4; - - this._$indexBufferPos = ibp; - - this._$expandVertexBufferIfNeeded(7); - const vbd: Float32Array = this._$vertexBufferData; - let vbp: number = this._$vertexBufferPos; - - vbd[vbp++] = x; - vbd[vbp++] = y; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - vbd[vbp++] = 0; - - this._$vertexBufferPos = vbp; - } - - /** - * @param {number} vertex_begin_offset 結合対象の頂点の範囲(開始) - * @param {number} vertex_end_offset 結合対象の頂点の範囲(終了) - * @return {void} - * @method - * @static - * @private - */ - static _$generateLineCap ( - vertex_begin_offset: number, - vertex_end_offset: number - ): void { - - const vbd: Float32Array = this._$vertexBufferData; - const stx1: number = vbd[vertex_begin_offset]; - const sty1: number = vbd[vertex_begin_offset + 1]; - const stx2: number = vbd[vertex_begin_offset + 2]; - const sty2: number = vbd[vertex_begin_offset + 3]; - const edx1: number = vbd[vertex_end_offset - 7]; - const edy1: number = vbd[vertex_end_offset - 6]; - const edx2: number = vbd[vertex_end_offset - 5]; - const edy2: number = vbd[vertex_end_offset - 4]; - - const indexBeginOffset: number = vertex_begin_offset / 7; - const indexEndOffset: number = vertex_end_offset / 7; - - // 始点st1と終点ed1が同じなら、線端は追加せずに結合する - if (stx1 === edx1 && sty1 === edy1) { - this._$addLineJoinMesh( - edx2, edy2, stx1, sty1, stx2, sty2, - indexEndOffset - 2, indexEndOffset - 1, - indexBeginOffset, indexBeginOffset + 1 - ); - return; - } - - // 始点の線端を追加する - this._$addLineCapMesh(stx1, sty1, stx2, sty2, indexBeginOffset, indexBeginOffset + 1); - - // 終点の線端を追加する - this._$addLineCapMesh(edx1, edy1, edx2, edy2, indexEndOffset - 1, indexEndOffset - 2); - } - - /** - * @param {number} x1 線端のx座標 - * @param {number} y1 線端のy座標 - * @param {number} x2 もう一方の端点のx座標 - * @param {number} y2 もう一方の端点のy座標 - * @param {number} index1 端点から反時計回り側の頂点インデックス - * @param {number} index2 端点から時計回り側の頂点インデックス - * @return {void} - * @method - * @static - * @private - */ - static _$addLineCapMesh ( - x1: number, y1: number, - x2: number, y2: number, - index1: number, index2: number - ): void { - - // 線端タイプに合わせたメッシュを追加する - switch (this._$lineCap) { - - case "round": - this._$addRoundJoinMesh(x1, y1); - break; - - case "square": - this._$addSquareCapMesh(x1, y1, x2, y2, index1, index2); - break; - - default: - break; - - } - } - - /** - * @param {number} x1 線端のx座標 - * @param {number} y1 線端のy座標 - * @param {number} x2 もう一方の端点のx座標 - * @param {number} y2 もう一方の端点のy座標 - * @param {number} index1 端点から反時計回り側の頂点インデックス - * @param {number} index2 端点から時計回り側の頂点インデックス - * @return {void} - * @method - * @static - * @private - */ - static _$addSquareCapMesh ( - x1: number, y1: number, - x2: number, y2: number, - index1: number, index2: number - ): void { - - const index3: number = this._$vertexBufferPos / 7; - const index4: number = index3 + 1; - - this._$expandIndexBufferIfNeeded(6); - const ibd: Int16Array = this._$indexBufferData; - let ibp: number = this._$indexBufferPos; - - ibd[ibp++] = index1; - ibd[ibp++] = index3; - ibd[ibp++] = index4; - - ibd[ibp++] = index4; - ibd[ibp++] = index2; - ibd[ibp++] = index1; - - this._$indexBufferPos = ibp; - - this._$expandVertexBufferIfNeeded(14); - const vbd: Float32Array = this._$vertexBufferData; - let vbp: number = this._$vertexBufferPos; - - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = -1; - vbd[vbp++] = -1; - vbd[vbp++] = 10; - - vbd[vbp++] = x1; - vbd[vbp++] = y1; - vbd[vbp++] = x2; - vbd[vbp++] = y2; - vbd[vbp++] = 1; - vbd[vbp++] = 1; - vbd[vbp++] = 10; - - this._$vertexBufferPos = vbp; - } -} \ No newline at end of file diff --git a/packages/webgl/src/WebGLUtil.ts b/packages/webgl/src/WebGLUtil.ts new file mode 100644 index 00000000..cd17d27a --- /dev/null +++ b/packages/webgl/src/WebGLUtil.ts @@ -0,0 +1,552 @@ +import type { Context } from "./Context"; + +/** + * @description 描画の最大サイズ + * Maximum size of drawing + * + * @type {number} + * @protected + */ +export let $RENDER_MAX_SIZE: number = 2048; + +/** + * @description 描画の最大サイズを変更 + * Change the maximum size of drawing + * + * @param {number} size + * @return {void} + * @method + * @protected + */ +export const $setRenderMaxSize = (size: number): void => +{ + $RENDER_MAX_SIZE = Math.min(4096, size / 2); +}; + +/** + * @description 描画のサンプリング数 + * Number of samples for drawing + * + * @type {number} + * @default 4 + * @protected + */ +export let $samples: number = 4; + +/** + * @description 描画のサンプリング数を変更 + * Change the number of samples for drawing + * + * @param {number} samples + * @return {void} + * @method + * @protected + */ +export const $setSamples = (samples: number): void => +{ + $samples = samples; +}; + +/** + * @type {WebGL2RenderingContext} + * @protected + */ +export let $gl: WebGL2RenderingContext; + +/** + * @description WebGL2のコンテキストをセット + * Set WebGL2 context + * + * @param {WebGL2RenderingContext} gl + * @return {void} + * @method + * @protected + */ +export const $setWebGL2RenderingContext = (gl: WebGL2RenderingContext): void => +{ + $gl = gl; +}; + +/** + * @type {Context} + * @public + */ +export let $context: Context; + +/** + * @description 起動したコンテキストをセット + * Set the context that started + * + * @param {Context} context + * @return {void} + * @method + * @protected + */ +export const $setContext = (context: Context): void => +{ + $context = context; +}; + +/** + * @description 指定された値を範囲内にクランプします。 + * Clamps the specified value within the range. + * + * @param {number} value + * @param {number} min + * @param {number} max + * @param {number} [default_value=null] + * @return {number} + * @method + * @protected + */ +export const $clamp = ( + value: number, + min: number, max: number, + default_value: number | null = null +): number => { + + const number: number = +value; + + return isNaN(number) && default_value !== null + ? default_value + : Math.min(Math.max(min, isNaN(number) ? 0 : number), max); +}; + +/** + * @type {array} + * @private + */ +const $arrays: any[] = []; + +/** + * @description プールした配列があれば再利用、なければ新規作成 + * Reuse the pooled array if available, otherwise create a new one. + * + * @param {array} args + * @return {array} + * @method + * @protected + */ +export const $getArray = (...args: any[]): any[] => +{ + const array: any[] = $arrays.pop() || []; + if (args.length) { + array.push(...args); + } + return array; +}; + +/** + * @description 使用済みの配列をプールに保管 + * Store the used array in the pool. + * + * @param {array} array + * @return {void} + * @method + * @protected + */ +export const $poolArray = (array: any[] | null = null): void => +{ + if (!array) { + return ; + } + + if (array.length) { + array.length = 0; + } + + $arrays.push(array); +}; + +/** + * @description 指定された値を2の累乗に切り上げます。 + * Rounds the specified value up to a power of two. + * + * @param {number} v + * @return {number} + * @method + * @protected + */ +export const $upperPowerOfTwo = (v: number): number => +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $float32Array4: Float32Array[] = []; + +/** + * @description プールしたFloat32Arrayがあれば再利用、なければ新規作成 + * Reuse the pooled Float32Array if available, otherwise create a new one. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getFloat32Array4 = ( + f0: number = 0, f1: number = 0, + f2: number = 0, f3: number = 0 +): Float32Array => { + + const array: Float32Array = $float32Array4.pop() || new Float32Array(4); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + + return array; +}; + +/** + * @description 使用済みのFloat32Arrayをプールに保管 + * Store the used Float32Array in the pool. + * + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolFloat32Array4 = (array: Float32Array): void => +{ + $float32Array4.push(array); +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $float32Array9: Float32Array[] = []; + +/** + * @description プールしたFloat32Arrayがあれば再利用、なければ新規作成 + * Reuse the pooled Float32Array if available, otherwise create a new one. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @param {number} [f6=0] + * @param {number} [f7=0] + * @param {number} [f8=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getFloat32Array9 = ( + f0: number = 0, f1: number = 0, f2: number = 0, + f3: number = 0, f4: number = 0, f5: number = 0, + f6: number = 0, f7: number = 0, f8: number = 0 +): Float32Array => { + + const array: Float32Array = $float32Array9.pop() || new Float32Array(9); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + array[6] = f6; + array[7] = f7; + array[8] = f8; + + return array; +}; + +/** + * @description 使用済みのFloat32Arrayをプールに保管 + * Store the used Float32Array in the pool. + * + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolFloat32Array9 = (array: Float32Array): void => +{ + $float32Array9.push(array); +}; + +/** + * @type {Float32Array[]} + * @private + */ +const $float32Array6: Float32Array[] = []; + +/** + * @description プールしたFloat32Arrayがあれば再利用、なければ新規作成 + * Reuse the pooled Float32Array if available, otherwise create a new one. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @param {number} [f4=0] + * @param {number} [f5=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getFloat32Array6 = ( + f0: number = 0, f1: number = 0, + f2: number = 0, f3: number = 0, + f4: number = 0, f5: number = 0 +): Float32Array => { + + const array: Float32Array = $float32Array6.pop() || new Float32Array(6); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + array[4] = f4; + array[5] = f5; + + return array; +}; + +/** + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolFloat32Array6 = (array: Float32Array): void => +{ + $float32Array6.push(array); +}; + +/** + * @type {Int32Array[]} + * @private + */ +const $int32Array4: Int32Array[] = []; + +/** + * @description プールしたInt32Arrayがあれば再利用、なければ新規作成 + * Reuse the pooled Int32Array if available, otherwise create a new one. + * + * @param {number} [f0=0] + * @param {number} [f1=0] + * @param {number} [f2=0] + * @param {number} [f3=0] + * @return {Float32Array} + * @method + * @protected + */ +export const $getInt32Array4 = ( + f0: number = 0, f1: number = 0, + f2: number = 0, f3: number = 0 +): Int32Array => { + + const array: Int32Array = $int32Array4.pop() || new Int32Array(4); + + array[0] = f0; + array[1] = f1; + array[2] = f2; + array[3] = f3; + + return array; +}; + +/** + * @description 使用済みのInt32Arrayをプールに保管 + * Store the used Int32Array in the pool. + * + * @param {Float32Array} array + * @return {void} + * @method + * @protected + */ +export const $poolInt32Array4 = (array: Int32Array): void => +{ + $int32Array4.push(array); +}; + +/** + * @description 逆行列を取得 + * Get the inverse matrix + * + * @param {Float32Array} m + * @returns {Float32Array} + * @method + * @protected + */ +export const $inverseMatrix = (m: Float32Array): Float32Array => +{ + const rdet: number = 1 / (m[0] * m[4] - m[3] * m[1]); + const tx: number = m[3] * m[7] - m[4] * m[6]; + const ty: number = m[1] * m[6] - m[0] * m[7]; + + return $getFloat32Array9( + m[4] * rdet, 0 - m[1] * rdet, 0, + 0 - m[3] * rdet, m[0] * rdet, 0, + tx * rdet, ty * rdet, 1 + ); +}; + +/** + * @type {number} + * @default 0 + * @private + */ +let $viewportWidth: number = 0; + +/** + * @type {number} + * @default 0 + * @private + */ +let $viewportHeight: number = 0; + +/** + * @description ビューポートの幅を取得 + * Get the width of the viewport + * + * @returns {number} + * @method + * @protected + */ +export const $getViewportWidth = (): number => +{ + return $viewportWidth; +}; + +/** + * @description ビューポートの高さを取得 + * Get the height of the viewport + * + * @returns {number} + * @method + * @protected + */ +export const $getViewportHeight = (): number => +{ + return $viewportHeight; +}; + +/** + * @description ビューポートのサイズをセット + * Set the size of the viewport + * + * @param {number} viewport_width + * @param {number} viewport_height + * @return {void} + * @method + * @protected + */ +export const $setViewportSize = (viewport_width: number, viewport_height: number): void => +{ + $viewportWidth = viewport_width; + $viewportHeight = viewport_height; +}; + +/** + * @description ビューポートのサイズを取得 + * Get the size of the viewport + * + * @param {Float32Array} matrix + * @return {Float32Array} + * @method + * @protected + */ +export const $linearGradientXY = (matrix: Float32Array): Float32Array => +{ + const x0: number = -819.2 * matrix[0] - 819.2 * matrix[2] + matrix[4]; + const x1: number = 819.2 * matrix[0] - 819.2 * matrix[2] + matrix[4]; + const x2: number = -819.2 * matrix[0] + 819.2 * matrix[2] + matrix[4]; + const y0: number = -819.2 * matrix[1] - 819.2 * matrix[3] + matrix[5]; + const y1: number = 819.2 * matrix[1] - 819.2 * matrix[3] + matrix[5]; + const y2: number = -819.2 * matrix[1] + 819.2 * matrix[3] + matrix[5]; + + let vx2: number = x2 - x0; + let vy2: number = y2 - y0; + + const r1: number = Math.sqrt(vx2 * vx2 + vy2 * vy2); + if (r1) { + vx2 = vx2 / r1; + vy2 = vy2 / r1; + } else { + vx2 = 0; + vy2 = 0; + } + + const r2: number = (x1 - x0) * vx2 + (y1 - y0) * vy2; + + return $getFloat32Array4(x0 + r2 * vx2, y0 + r2 * vy2, x1, y1); +}; + +/** + * @type {number} + * @public + */ +let $devicePixelRatio: number = 1; + +/** + * @description デバイスのピクセル比率を設定 + * Set the device's pixel ratio + * + * @param {number} device_pixel_ratio + * @return {void} + * @method + * @public + */ +export const $setDevicePixelRatio = (device_pixel_ratio: number): void => +{ + $devicePixelRatio = device_pixel_ratio; +}; + +/** + * @description デバイスのピクセル比率を取得 + * Get the device's pixel ratio + * + * @return {number} + * @method + * @public + */ +export const $getDevicePixelRatio = (): number => +{ + return $devicePixelRatio; +}; + +/** + * @description 2つの行列を乗算 + * Multiply two matrices + * + * @param {Float32Array} a + * @param {Float32Array} b + * @return {Float32Array} + * @method + * @protected + */ +export const $multiplyMatrices = (a: Float32Array, b: Float32Array): Float32Array => +{ + const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5]; + const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5]; + + return $getFloat32Array6( + a0 * b0 + a2 * b1, + a1 * b0 + a3 * b1, + a0 * b2 + a2 * b3, + a1 * b2 + a3 * b3, + a0 * b4 + a2 * b5 + a4, + a1 * b4 + a3 * b5 + a5 + ); +}; \ No newline at end of file diff --git a/packages/webgl/src/index.ts b/packages/webgl/src/index.ts index f79807ad..6ba9f62a 100644 --- a/packages/webgl/src/index.ts +++ b/packages/webgl/src/index.ts @@ -1,6 +1 @@ -export * from "./CanvasGradientToWebGL"; -export * from "./CanvasPatternToWebGL"; -export * from "./CanvasToWebGLContext"; -export * from "./CanvasToWebGLContextPath"; -export * from "./CanvasToWebGLContextStyle"; -export * from "./FrameBufferManager"; \ No newline at end of file +export * from "./Context"; \ No newline at end of file diff --git a/packages/webgl/src/interface/AttachmentImpl.ts b/packages/webgl/src/interface/AttachmentImpl.ts deleted file mode 100644 index 34e91f64..00000000 --- a/packages/webgl/src/interface/AttachmentImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AttachmentImpl { - width: number; - height: number; - color: WebGLTexture | WebGLRenderbuffer | null; - texture: WebGLTexture | null; - msaa: boolean; - stencil: WebGLRenderbuffer | null; - mask: boolean; - clipLevel: number; - isActive: boolean; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/BlendModeImpl.ts b/packages/webgl/src/interface/BlendModeImpl.ts deleted file mode 100644 index 6ed290eb..00000000 --- a/packages/webgl/src/interface/BlendModeImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type BlendModeImpl = "copy" - | "add" - | "alpha" - | "darken" - | "difference" - | "erase" - | "hardlight" - | "invert" - | "layer" - | "lighten" - | "multiply" - | "normal" - | "overlay" - | "screen" - | "subtract"; \ No newline at end of file diff --git a/packages/webgl/src/interface/BoundsImpl.ts b/packages/webgl/src/interface/BoundsImpl.ts deleted file mode 100644 index 7af56a28..00000000 --- a/packages/webgl/src/interface/BoundsImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BoundsImpl { - xMin: number; - xMax: number; - yMin: number; - yMax: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/CachePositionImpl.ts b/packages/webgl/src/interface/CachePositionImpl.ts deleted file mode 100644 index 98f72340..00000000 --- a/packages/webgl/src/interface/CachePositionImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CachePositionImpl { - index: number; - x: number; - y: number; - w: number; - h: number; - filterState?: boolean; - matrix?: string; - offsetX?: number; - offsetY?: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/CapsStyleImpl.ts b/packages/webgl/src/interface/CapsStyleImpl.ts deleted file mode 100644 index db351e90..00000000 --- a/packages/webgl/src/interface/CapsStyleImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type CapsStyleImpl = "none" | "round" | "square"; \ No newline at end of file diff --git a/packages/webgl/src/interface/ClipObjectImpl.ts b/packages/webgl/src/interface/ClipObjectImpl.ts deleted file mode 100644 index 412babf7..00000000 --- a/packages/webgl/src/interface/ClipObjectImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ClipObjectImpl { - vertexArrayObject: WebGLVertexArrayObject; - matrixA: number; - matrixB: number; - matrixC: number; - matrixD: number; - matrixE: number; - matrixF: number; - matrixG: number; - matrixH: number; - matrixI: number; - viewportWidth: number; - viewportHeight: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/FillMeshImpl.ts b/packages/webgl/src/interface/FillMeshImpl.ts deleted file mode 100644 index 1f100adb..00000000 --- a/packages/webgl/src/interface/FillMeshImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IndexRangeImpl } from "./IndexRangeImpl"; - -export interface FillMeshImpl { - vertexBufferData: Float32Array; - indexRanges: IndexRangeImpl[]; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/GradientTypeImpl.ts b/packages/webgl/src/interface/GradientTypeImpl.ts deleted file mode 100644 index 37f6dabc..00000000 --- a/packages/webgl/src/interface/GradientTypeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type GradientTypeImpl = "linear" | "radial"; \ No newline at end of file diff --git a/packages/webgl/src/interface/GridImpl.ts b/packages/webgl/src/interface/GridImpl.ts deleted file mode 100644 index eecb9aba..00000000 --- a/packages/webgl/src/interface/GridImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GridImpl { - x: number; - y: number; - w: number; - h: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/IAttachmentObject.ts b/packages/webgl/src/interface/IAttachmentObject.ts new file mode 100644 index 00000000..f23ef758 --- /dev/null +++ b/packages/webgl/src/interface/IAttachmentObject.ts @@ -0,0 +1,15 @@ +import type { IColorBufferObject } from "./IColorBufferObject"; +import type { ITextureObject } from "./ITextureObject"; +import type { IStencilBufferObject } from "./IStencilBufferObject"; + +export interface IAttachmentObject { + id: number; + width: number; + height: number; + clipLevel: number; + msaa: boolean; + mask: boolean; + color: IColorBufferObject | null; + texture: ITextureObject | null; + stencil: IStencilBufferObject | null; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IBlendMode.ts b/packages/webgl/src/interface/IBlendMode.ts new file mode 100644 index 00000000..9480efe7 --- /dev/null +++ b/packages/webgl/src/interface/IBlendMode.ts @@ -0,0 +1,15 @@ +export type IBlendMode = "copy" + | "add" + | "alpha" + | "darken" + | "difference" + | "erase" + | "hardlight" + | "invert" + | "layer" + | "lighten" + | "multiply" + | "normal" + | "overlay" + | "screen" + | "subtract"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IBounds.ts b/packages/webgl/src/interface/IBounds.ts new file mode 100644 index 00000000..b20c1ee3 --- /dev/null +++ b/packages/webgl/src/interface/IBounds.ts @@ -0,0 +1,6 @@ +export interface IBounds { + xMin: number; + xMax: number; + yMin: number; + yMax: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IClipObject.ts b/packages/webgl/src/interface/IClipObject.ts new file mode 100644 index 00000000..6df96a5f --- /dev/null +++ b/packages/webgl/src/interface/IClipObject.ts @@ -0,0 +1,14 @@ +export interface IClipObject { + vertexArrayObject: WebGLVertexArrayObject; + matrixA: number; + matrixB: number; + matrixC: number; + matrixD: number; + matrixE: number; + matrixF: number; + matrixG: number; + matrixH: number; + matrixI: number; + viewportWidth: number; + viewportHeight: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IColorBufferObject.ts b/packages/webgl/src/interface/IColorBufferObject.ts new file mode 100644 index 00000000..c17bd0bd --- /dev/null +++ b/packages/webgl/src/interface/IColorBufferObject.ts @@ -0,0 +1,10 @@ +import type { IStencilBufferObject } from "./IStencilBufferObject"; + +export interface IColorBufferObject { + resource: WebGLRenderbuffer; + stencil: IStencilBufferObject; + width: number; + height: number; + area: number; + dirty: boolean; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IFillMesh.ts b/packages/webgl/src/interface/IFillMesh.ts new file mode 100644 index 00000000..51dda0fa --- /dev/null +++ b/packages/webgl/src/interface/IFillMesh.ts @@ -0,0 +1,4 @@ +export interface IFillMesh { + buffer: Float32Array; + indexCount: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IFillType.ts b/packages/webgl/src/interface/IFillType.ts new file mode 100644 index 00000000..0b8c40fc --- /dev/null +++ b/packages/webgl/src/interface/IFillType.ts @@ -0,0 +1 @@ +export type IFillType = "fill" | "linear" | "radial" | "bitmap" | "stroke"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IGradientType.ts b/packages/webgl/src/interface/IGradientType.ts new file mode 100644 index 00000000..1c97a3e9 --- /dev/null +++ b/packages/webgl/src/interface/IGradientType.ts @@ -0,0 +1 @@ +export type IGradientType = "linear" | "radial"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IGrid.ts b/packages/webgl/src/interface/IGrid.ts new file mode 100644 index 00000000..f95d98fd --- /dev/null +++ b/packages/webgl/src/interface/IGrid.ts @@ -0,0 +1,6 @@ +export interface IGrid { + x: number; + y: number; + w: number; + h: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IInterpolationMethod.ts b/packages/webgl/src/interface/IInterpolationMethod.ts new file mode 100644 index 00000000..a15b1bef --- /dev/null +++ b/packages/webgl/src/interface/IInterpolationMethod.ts @@ -0,0 +1 @@ +export type IInterpolationMethod = "rgb" | "linearRGB"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IJointStyle.ts b/packages/webgl/src/interface/IJointStyle.ts new file mode 100644 index 00000000..7fa2abd9 --- /dev/null +++ b/packages/webgl/src/interface/IJointStyle.ts @@ -0,0 +1 @@ +export type IJointStyle = "bevel" | "miter" | "round"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IPath.ts b/packages/webgl/src/interface/IPath.ts new file mode 100644 index 00000000..7e7fa917 --- /dev/null +++ b/packages/webgl/src/interface/IPath.ts @@ -0,0 +1 @@ +export type IPath = Array; \ No newline at end of file diff --git a/packages/webgl/src/interface/IPoint.ts b/packages/webgl/src/interface/IPoint.ts new file mode 100644 index 00000000..72306c33 --- /dev/null +++ b/packages/webgl/src/interface/IPoint.ts @@ -0,0 +1,4 @@ +export interface IPoint { + x: number; + y: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IProgramObject.ts b/packages/webgl/src/interface/IProgramObject.ts new file mode 100644 index 00000000..fb89e927 --- /dev/null +++ b/packages/webgl/src/interface/IProgramObject.ts @@ -0,0 +1,5 @@ +export interface IProgramObject +{ + id: number; + resource: WebGLProgram; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/ISpreadMethod.ts b/packages/webgl/src/interface/ISpreadMethod.ts new file mode 100644 index 00000000..dcae8d20 --- /dev/null +++ b/packages/webgl/src/interface/ISpreadMethod.ts @@ -0,0 +1 @@ +export type ISpreadMethod = "pad" | "reflect" | "repeat"; \ No newline at end of file diff --git a/packages/webgl/src/interface/IStencilBufferObject.ts b/packages/webgl/src/interface/IStencilBufferObject.ts new file mode 100644 index 00000000..c4b1c164 --- /dev/null +++ b/packages/webgl/src/interface/IStencilBufferObject.ts @@ -0,0 +1,8 @@ +export interface IStencilBufferObject { + id: number; + resource: WebGLRenderbuffer; + width: number; + height: number; + area: number; + dirty: boolean; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IStrokeVertexArrayObject.ts b/packages/webgl/src/interface/IStrokeVertexArrayObject.ts new file mode 100644 index 00000000..5e934878 --- /dev/null +++ b/packages/webgl/src/interface/IStrokeVertexArrayObject.ts @@ -0,0 +1,7 @@ +import type { IVertexArrayObject } from "./IVertexArrayObject"; + +export interface IStrokeVertexArrayObject extends IVertexArrayObject { + indexBuffer: WebGLBuffer; + indexLength: number; + indexCount: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/ITextureObject.ts b/packages/webgl/src/interface/ITextureObject.ts new file mode 100644 index 00000000..a783472a --- /dev/null +++ b/packages/webgl/src/interface/ITextureObject.ts @@ -0,0 +1,8 @@ +export interface ITextureObject { + id: number; + resource: WebGLTexture; + width: number; + height: number; + area: number; + smooth: boolean; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IUniformData.ts b/packages/webgl/src/interface/IUniformData.ts new file mode 100644 index 00000000..629d145a --- /dev/null +++ b/packages/webgl/src/interface/IUniformData.ts @@ -0,0 +1,5 @@ +export interface IUniformData { + method?: Function; + array? : Int32Array | Float32Array; + assign? : number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IVertexArrayObject.ts b/packages/webgl/src/interface/IVertexArrayObject.ts new file mode 100644 index 00000000..61b39f38 --- /dev/null +++ b/packages/webgl/src/interface/IVertexArrayObject.ts @@ -0,0 +1,6 @@ +export interface IVertexArrayObject { + id: number; + resource: WebGLVertexArrayObject; + vertexBuffer: WebGLBuffer; + vertexLength: number; +} \ No newline at end of file diff --git a/packages/webgl/src/interface/IndexRangeImpl.ts b/packages/webgl/src/interface/IndexRangeImpl.ts deleted file mode 100644 index ac74cc1b..00000000 --- a/packages/webgl/src/interface/IndexRangeImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IndexRangeImpl { - first: number; - count: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/InterpolationMethodImpl.ts b/packages/webgl/src/interface/InterpolationMethodImpl.ts deleted file mode 100644 index a623a088..00000000 --- a/packages/webgl/src/interface/InterpolationMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type InterpolationMethodImpl = "rgb" | "linearRGB"; \ No newline at end of file diff --git a/packages/webgl/src/interface/JointStyleImpl.ts b/packages/webgl/src/interface/JointStyleImpl.ts deleted file mode 100644 index d9b443a2..00000000 --- a/packages/webgl/src/interface/JointStyleImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type JointStyleImpl = "bevel" | "miter" | "round"; \ No newline at end of file diff --git a/packages/webgl/src/interface/PointImpl.ts b/packages/webgl/src/interface/PointImpl.ts deleted file mode 100644 index b912bda3..00000000 --- a/packages/webgl/src/interface/PointImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface PointImpl { - x: number; - y: number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/SpreadMethodImpl.ts b/packages/webgl/src/interface/SpreadMethodImpl.ts deleted file mode 100644 index b04e5e3f..00000000 --- a/packages/webgl/src/interface/SpreadMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type SpreadMethodImpl = "pad" | "reflect" | "repeat"; \ No newline at end of file diff --git a/packages/webgl/src/interface/StrokeMethImpl.ts b/packages/webgl/src/interface/StrokeMethImpl.ts deleted file mode 100644 index fd4c58a3..00000000 --- a/packages/webgl/src/interface/StrokeMethImpl.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface StrokeMethImpl { - vertexBufferData: Float32Array; - indexBufferData: Int16Array; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/UniformDataImpl.ts b/packages/webgl/src/interface/UniformDataImpl.ts deleted file mode 100644 index 1931fd77..00000000 --- a/packages/webgl/src/interface/UniformDataImpl.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface UniformDataImpl { - method?: Function; - array? : Int32Array | Float32Array; - assign? : number; -} \ No newline at end of file diff --git a/packages/webgl/src/interface/VerticesImpl.ts b/packages/webgl/src/interface/VerticesImpl.ts deleted file mode 100644 index 454c4108..00000000 --- a/packages/webgl/src/interface/VerticesImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type VerticesImpl = Array>; \ No newline at end of file diff --git a/packages/webgl/src/shader/CanvasToWebGLShader.ts b/packages/webgl/src/shader/CanvasToWebGLShader.ts deleted file mode 100644 index 3ab553a1..00000000 --- a/packages/webgl/src/shader/CanvasToWebGLShader.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { WebGLShaderUniform } from "./WebGLShaderUniform"; -import { WebGLShaderInstance } from "./WebGLShaderInstance"; -import type { CanvasToWebGLContext } from "../CanvasToWebGLContext"; -import type { CanvasToWebGLShaderList } from "./CanvasToWebGLShaderList"; -import type { IndexRangeImpl } from "../interface/IndexRangeImpl"; -import { $getProgramId } from "@next2d/share"; - -/** - * @class - */ -export class CanvasToWebGLShader -{ - private readonly _$gl: WebGL2RenderingContext; - private readonly _$context: CanvasToWebGLContext; - private readonly _$program: WebGLProgram; - private readonly _$uniform: WebGLShaderUniform; - private _$instance: WebGLShaderInstance | null; - - /** - * @param {WebGL2RenderingContext} gl - * @param {CanvasToWebGLContext} context - * @param {string} vertex_source - * @param {string} fragment_source - * @constructor - * @public - */ - constructor ( - gl: WebGL2RenderingContext, - context: CanvasToWebGLContext, - vertex_source: string, - fragment_source: string - ) { - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGLProgram} - * @private - */ - this._$program = this._$createProgram(vertex_source, fragment_source); - - /** - * @type {WebGLShaderUniform} - * @private - */ - this._$uniform = new WebGLShaderUniform(gl, this._$program); - - /** - * @type {WebGLShaderInstance} - * @default null - * @private - */ - this._$instance = null; - } - - /** - * @return {WebGLShaderInstance} - * @readonly - * @public - */ - get instance (): WebGLShaderInstance - { - if (!this._$instance) { - this._$instance = new WebGLShaderInstance(); - } - return this._$instance; - } - - /** - * @return {WebGLShaderUniform} - * @readonly - * @public - */ - get uniform (): WebGLShaderUniform - { - return this._$uniform; - } - - /** - * @param {string} vertex_source - * @param {string} fragment_source - * @return {WebGLProgram} - * @method - * @private - */ - _$createProgram (vertex_source: string, fragment_source: string): WebGLProgram - { - const program: WebGLProgram = this._$gl.createProgram() as NonNullable; - - // control number - program.id = $getProgramId(); - - const vertexShader: WebGLShader = this._$gl.createShader(this._$gl.VERTEX_SHADER) as NonNullable; - this._$gl.shaderSource(vertexShader, vertex_source); - this._$gl.compileShader(vertexShader); - - const fragmentShader: WebGLShader = this._$gl.createShader(this._$gl.FRAGMENT_SHADER) as NonNullable; - this._$gl.shaderSource(fragmentShader, fragment_source); - this._$gl.compileShader(fragmentShader); - - this._$gl.attachShader(program, vertexShader); - this._$gl.attachShader(program, fragmentShader); - this._$gl.linkProgram(program); - - this._$gl.detachShader(program, vertexShader); - this._$gl.detachShader(program, fragmentShader); - - this._$gl.deleteShader(vertexShader); - this._$gl.deleteShader(fragmentShader); - - return program; - } - - /** - * @return {void} - * @private - */ - _$attachProgram (): void - { - const shaderList: CanvasToWebGLShaderList = this._$context.shaderList; - - if (shaderList.currentProgramId !== this._$program.id) { - shaderList.currentProgramId = this._$program.id; - this._$gl.useProgram(this._$program); - } - } - - /** - * @param {WebGLShaderInstance} instance - * @method - * @public - */ - drawArraysInstanced (instance: WebGLShaderInstance): void - { - // setup - this._$attachProgram(); - - // bind data - this._$context.vao.bindInstnceArray(instance); - - // draw - this._$gl.drawArraysInstanced(this._$gl.TRIANGLE_STRIP, 0, 4, instance.count); - } - - /** - * @return {void} - * @method - * @public - */ - _$drawImage (): void - { - this._$attachProgram(); - this._$uniform.bindUniforms(); - this._$context.vao.bindCommonVertexArray(); - this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP, 0, 4); - } - - /** - * @param {number} begin - * @param {number} end - * @return {void} - * @method - * @public - */ - _$drawGradient (begin: number, end: number): void - { - this._$attachProgram(); - this._$uniform.bindUniforms(); - this._$context.vao.bindGradientVertexArray(begin, end); - this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP, 0, 4); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array_object - * @return {void} - * @method - * @public - */ - _$stroke (vertex_array_object: WebGLVertexArrayObject): void - { - // setup - this._$attachProgram(); - - // set alpha - this._$context.blend.reset(); - - // update data - this._$uniform.bindUniforms(); - - // bind vertex array - this._$context.vao.bind(vertex_array_object); - - // draw - this._$gl.drawElements( - this._$gl.TRIANGLES, - vertex_array_object.indexCount, - this._$gl.UNSIGNED_SHORT, 0 - ); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array_object - * @return {void} - * @method - * @public - */ - _$fill (vertex_array_object: WebGLVertexArrayObject): void - { - // setup - this._$attachProgram(); - - // set alpha - this._$context.blend.reset(); - - // update data - this._$uniform.bindUniforms(); - - // bind vertex array - this._$context.vao.bind(vertex_array_object); - - // draw fill - const indexRanges: IndexRangeImpl[] = vertex_array_object.indexRanges; - const range: IndexRangeImpl = indexRanges[indexRanges.length - 1]; - this._$gl.drawArrays(this._$gl.TRIANGLES, 0, range.first + range.count); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @param {number} first - * @param {number} count - * @return {void} - * @method - * @public - */ - _$containerClip ( - vertex_array: WebGLVertexArrayObject, first: number, count: number - ): void { - - // setup - this._$attachProgram(); - - // set alpha - this._$context.blend.reset(); - - // update data - this._$uniform.bindUniforms(); - - // bind vertex array - this._$context.vao.bind(vertex_array); - - // draw fill - this._$gl.drawArrays(this._$gl.TRIANGLES, first, count); - } - - /** - * @param {WebGLVertexArrayObject} vertex_array - * @param {number} first - * @param {number} count - * @return {void} - * @method - * @public - */ - _$drawPoints ( - vertex_array: WebGLVertexArrayObject, - first: number, count: number - ): void { - - // setup - this._$attachProgram(); - - // update data - this._$uniform.bindUniforms(); - - // bind vertex array - this._$context.vao.bind(vertex_array); - - // draw fill - this._$gl.drawArrays(this._$gl.POINTS, first, count); - } -} diff --git a/packages/webgl/src/shader/CanvasToWebGLShaderList.ts b/packages/webgl/src/shader/CanvasToWebGLShaderList.ts deleted file mode 100644 index 1bfa1cbc..00000000 --- a/packages/webgl/src/shader/CanvasToWebGLShaderList.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { ShapeShaderVariantCollection } from "./variants/ShapeShaderVariantCollection"; -import { GradientShapeShaderVariantCollection } from "./variants/GradientShapeShaderVariantCollection"; -import { GradientLUTShaderVariantCollection } from "./variants/GradientLUTShaderVariantCollection"; -import { FilterShaderVariantCollection } from "./variants/FilterShaderVariantCollection"; -import { BlendShaderVariantCollection } from "./variants/BlendShaderVariantCollection"; -import type { CanvasToWebGLContext } from "../CanvasToWebGLContext"; - -/** - * @class - */ -export class CanvasToWebGLShaderList -{ - private _$currentProgramId: number; - private readonly _$shapeShaderVariants: ShapeShaderVariantCollection; - private readonly _$gradientShapeShaderVariants: GradientShapeShaderVariantCollection; - private readonly _$gradientLUTShaderVariants: GradientLUTShaderVariantCollection; - private readonly _$filterShaderVariants: FilterShaderVariantCollection; - private readonly _$blendShaderVariants: BlendShaderVariantCollection; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {number} - * @default -1 - * @public - */ - this._$currentProgramId = -1; - - /** - * @type {ShapeShaderVariantCollection} - * @private - */ - this._$shapeShaderVariants = new ShapeShaderVariantCollection(context, gl); - - /** - * @type {GradientShapeShaderVariantCollection} - * @private - */ - this._$gradientShapeShaderVariants = new GradientShapeShaderVariantCollection(context, gl); - - /** - * @type {GradientLUTShaderVariantCollection} - * @private - */ - this._$gradientLUTShaderVariants = new GradientLUTShaderVariantCollection(context, gl); - - /** - * @type {FilterShaderVariantCollection} - * @private - */ - this._$filterShaderVariants = new FilterShaderVariantCollection(context, gl); - - /** - * @type {BlendShaderVariantCollection} - * @private - */ - this._$blendShaderVariants = new BlendShaderVariantCollection(context, gl); - } - - /** - * @member {number} - * @public - */ - get currentProgramId (): number - { - return this._$currentProgramId; - } - set currentProgramId (current_program_id: number) - { - this._$currentProgramId = current_program_id; - } - - /** - * @memberof CanvasToWebGLShaderList# - * @property {ShapeShaderVariantCollection} shapeShaderVariants - * @return {ShapeShaderVariantCollection} - * @readonly - * @public - */ - get shapeShaderVariants (): ShapeShaderVariantCollection - { - return this._$shapeShaderVariants; - } - - /** - * @memberof CanvasToWebGLShaderList# - * @property {GradientShapeShaderVariantCollection} gradientShapeShaderVariants - * @return {GradientShapeShaderVariantCollection} - * @readonly - * @public - */ - get gradientShapeShaderVariants (): GradientShapeShaderVariantCollection - { - return this._$gradientShapeShaderVariants; - } - - /** - * @memberof CanvasToWebGLShaderList# - * @property {GradientLUTShaderVariantCollection} gradientLUTShaderVariants - * @return {GradientLUTShaderVariantCollection} - * @readonly - * @public - */ - get gradientLUTShaderVariants (): GradientLUTShaderVariantCollection - { - return this._$gradientLUTShaderVariants; - } - - /** - * @memberof CanvasToWebGLShaderList# - * @property {FilterShaderVariantCollection} filterShaderVariants - * @return {FilterShaderVariantCollection} - * @readonly - * @public - */ - get filterShaderVariants (): FilterShaderVariantCollection - { - return this._$filterShaderVariants; - } - - /** - * @memberof CanvasToWebGLShaderList# - * @property {BlendShaderVariantCollection} blendShaderVariants - * @return {BlendShaderVariantCollection} - * @readonly - * @public - */ - get blendShaderVariants (): BlendShaderVariantCollection - { - return this._$blendShaderVariants; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/GradientLUTGenerator.ts b/packages/webgl/src/shader/GradientLUTGenerator.ts deleted file mode 100644 index 7c0ba445..00000000 --- a/packages/webgl/src/shader/GradientLUTGenerator.ts +++ /dev/null @@ -1,195 +0,0 @@ -import type { CanvasToWebGLContext } from "../CanvasToWebGLContext"; -import type { GradientLUTShaderVariantCollection } from "./variants/GradientLUTShaderVariantCollection"; -import type { CanvasToWebGLShader } from "./CanvasToWebGLShader"; -import type { WebGLShaderUniform } from "./WebGLShaderUniform"; -import type { AttachmentImpl } from "../interface/AttachmentImpl"; -import { $Math } from "@next2d/share"; - -/** - * @class - */ -export class GradientLUTGenerator -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$attachment: AttachmentImpl; - private readonly _$maxLength: number; - private readonly _$rgbToLinearTable: Float32Array; - private readonly _$rgbIdentityTable: Float32Array; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {AttachmentImpl} - * @private - */ - this._$attachment = context - .frameBuffer - .createTextureAttachment(512, 1); - - /** - * @type {number} - * @private - */ - this._$maxLength = $Math.floor(gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS) * 0.75); - - /** - * @type {Float32Array} - * @private - */ - this._$rgbToLinearTable = new Float32Array(256); - - /** - * @type {Float32Array} - * @private - */ - this._$rgbIdentityTable = new Float32Array(256); - - for (let i: number = 0; i < 256; i++) { - const t: number = i / 255; - this._$rgbToLinearTable[i] = $Math.pow(t, 2.23333333); - this._$rgbIdentityTable[i] = t; - } - } - - /** - * @param {array} stops - * @param {boolean} is_linear_space - * @return {WebGLTexture|null} - * @method - * @public - */ - generateForShape ( - stops: any[], is_linear_space: boolean - ): WebGLTexture { - - const currentAttachment: AttachmentImpl | null = this - ._$context - .frameBuffer - .currentAttachment; - - const cachePosition = this._$context.cachePosition; - if (cachePosition) { - this._$gl.disable(this._$gl.SCISSOR_TEST); - } - - this._$context._$bind(this._$attachment); - - const stopsLength: number = stops.length; - - const variants: GradientLUTShaderVariantCollection = this - ._$context - .shaderList - .gradientLUTShaderVariants; - - const table: Float32Array = is_linear_space - ? this._$rgbToLinearTable - : this._$rgbIdentityTable; - - this._$context.blend.toOneZero(); - - for (let begin: number = 0; begin < stopsLength; begin += this._$maxLength - 1) { - - const end: number = $Math.min(begin + this._$maxLength, stopsLength); - - const shader: CanvasToWebGLShader = variants - .getGradientLUTShader(end - begin, is_linear_space); - - const uniform: WebGLShaderUniform = shader.uniform; - variants - .setGradientLUTUniformForShape(uniform, stops, begin, end, table); - - shader._$drawGradient( - begin === 0 ? 0 : stops[begin][0], - end === stopsLength ? 1 : stops[end - 1][0] - ); - } - - this._$context._$bind(currentAttachment); - - if (!this._$attachment.texture) { - throw new Error("the texture is null."); - } - - if (cachePosition) { - this._$context.bindRenderBuffer(cachePosition); - } - - return this._$attachment.texture; - } - - /** - * @param {array} ratios - * @param {array} colors - * @param {array} alphas - * @return {WebGLTexture} - * @method - * @public - */ - generateForFilter ( - ratios: number[], - colors: number[], - alphas: number[] - ): WebGLTexture { - - const currentAttachment: AttachmentImpl|null = this - ._$context - .frameBuffer - .currentAttachment; - - this._$context._$bind(this._$attachment); - - const stopsLength: number = ratios.length; - - const variants: GradientLUTShaderVariantCollection = this - ._$context - .shaderList - .gradientLUTShaderVariants; - - this._$context.blend.toOneZero(); - - for (let begin: number = 0; begin < stopsLength; begin += this._$maxLength - 1) { - - const end: number = $Math.min(begin + this._$maxLength, stopsLength); - - const shader: CanvasToWebGLShader = variants - .getGradientLUTShader(end - begin, false); - - const uniform: WebGLShaderUniform = shader.uniform; - - variants - .setGradientLUTUniformForFilter(uniform, ratios, colors, alphas, begin, end); - - shader._$drawGradient( - begin === 0 ? 0 : ratios[begin], - end === stopsLength ? 1 : ratios[end - 1] - ); - } - - this._$context._$bind(currentAttachment); - - if (!this._$attachment.texture) { - throw new Error("the texture is null."); - } - - return this._$attachment.texture; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/WebGLShaderInstance.ts b/packages/webgl/src/shader/WebGLShaderInstance.ts deleted file mode 100644 index a310306c..00000000 --- a/packages/webgl/src/shader/WebGLShaderInstance.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @class - */ -export class WebGLShaderInstance -{ - private readonly _$attributes: number[]; - private _$count: number; - - /** - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor () - { - /** - * @type {array} - * @private - */ - this._$attributes = []; - - /** - * @type {number} - * @private - */ - this._$count = 0; - } - - /** - * @member {array} - * @readonly - * @public - */ - get attributes (): number[] - { - return this._$attributes; - } - - /** - * @member {number} - * @public - */ - get count (): number - { - return this._$count; - } - set count (count: number) - { - this._$count = count; - } - - /** - * @method - * @public - */ - clear (): void - { - this._$attributes.length = 0; - this._$count = 0; - } - -} \ No newline at end of file diff --git a/packages/webgl/src/shader/WebGLShaderUniform.ts b/packages/webgl/src/shader/WebGLShaderUniform.ts deleted file mode 100644 index e3728ffa..00000000 --- a/packages/webgl/src/shader/WebGLShaderUniform.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { UniformDataImpl } from "../interface/UniformDataImpl"; -import { - $getMap, - $Float32Array, - $Int32Array -} from "@next2d/share"; - -/** - * @class - */ -export class WebGLShaderUniform -{ - private readonly _$gl: WebGL2RenderingContext; - private readonly _$array: UniformDataImpl[]; - private readonly _$map: Map; - - /** - * @param {WebGL2RenderingContext} gl - * @param {WebGLProgram} program - * @constructor - * @public - */ - constructor (gl: WebGL2RenderingContext, program: WebGLProgram) - { - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {array} - * @private - */ - this._$array = []; - - /** - * @type {Map} - * @private - */ - this._$map = $getMap(); - - const activeUniforms: number = this._$gl.getProgramParameter(program, this._$gl.ACTIVE_UNIFORMS); - for (let i: number = 0; i < activeUniforms; i++) { - - const info: WebGLActiveInfo = this._$gl.getActiveUniform(program, i) as NonNullable; - - const name: string = info.name.endsWith("[0]") - ? info.name.slice(0, -3) - : info.name; - - const location: WebGLUniformLocation = this._$gl.getUniformLocation(program, name) as NonNullable; - - // WebGLの仕様でuniformのint型のデフォルト値は0に設定されるため、 - // sampler2D(size=1)の値の更新は不要 - if (info.type === this._$gl.SAMPLER_2D && info.size === 1) { - continue; - } - - const data: UniformDataImpl = {}; - switch (info.type) { - - // uniformの値の設定は、gl.uniform4[fi]v()が最速のため、 - // 可能な限りFloat32Arrayに値をパックして転送するようにする - case this._$gl.FLOAT_VEC4: - data.method = this._$gl.uniform4fv.bind(this._$gl, location); - data.array = new $Float32Array(4 * info.size); - data.assign = -1; - break; - - case this._$gl.INT_VEC4: - data.method = this._$gl.uniform4iv.bind(this._$gl, location); - data.array = new $Int32Array(4 * info.size); - data.assign = -1; - break; - - // uniformの値の設定は、programに保持されるため、 - // sampler2Dは一度だけ設定するようにする - case this._$gl.SAMPLER_2D: - data.method = this._$gl.uniform1iv.bind(this._$gl, location); - data.array = new $Int32Array(info.size); - data.assign = 1; - break; - - case this._$gl.FLOAT: - case this._$gl.FLOAT_VEC2: - case this._$gl.FLOAT_VEC3: - case this._$gl.FLOAT_MAT2: - case this._$gl.FLOAT_MAT3: - case this._$gl.FLOAT_MAT4: - case this._$gl.INT: - case this._$gl.INT_VEC2: - case this._$gl.INT_VEC3: - default: - throw new Error("Use gl.FLOAT_VEC4 or gl.INT_VEC4 instead"); - } - - this._$array.push(data); - this._$map.set(name, data); - } - } - - /** - * @param {string} name - * @return {Int32Array|Float32Array} - * @method - * @public - */ - getArray (name: string): Int32Array | Float32Array - { - const data: UniformDataImpl | void = this._$map.get(name); - if (!data || !data.array) { - throw new Error("the UniformData is null."); - } - return data.array; - } - - /** - * @member {Int32Array|Float32Array} - * @readonly - * @public - */ - get textures (): Int32Array | Float32Array - { - const data: UniformDataImpl | void = this._$map.get("u_textures"); - if (!data || !data.array) { - throw new Error("the UniformData is null."); - } - return data.array; - } - - /** - * @member {Int32Array|Float32Array} - * @readonly - * @public - */ - get highp (): Int32Array | Float32Array - { - const data: UniformDataImpl | void = this._$map.get("u_highp"); - if (!data || !data.array) { - throw new Error("the UniformData is null."); - } - return data.array; - } - - /** - * @member {Int32Array|Float32Array} - * @readonly - * @public - */ - get mediump (): Int32Array | Float32Array - { - const data: UniformDataImpl | void = this._$map.get("u_mediump"); - if (!data || !data.array) { - throw new Error("the UniformData is null."); - } - return data.array; - } - - /** - * @member {Int32Array|Float32Array} - * @readonly - * @public - */ - get integer (): Int32Array|Float32Array - { - const data: UniformDataImpl|void = this._$map.get("u_integer"); - if (!data || !data.array) { - throw new Error("the UniformData is null."); - } - return data.array; - } - - /** - * @return {void} - * @method - * @public - */ - bindUniforms (): void - { - const length: number = this._$array.length; - for (let i: number = 0; i < length; i++) { - - const data: UniformDataImpl = this._$array[i]; - if (data.method === undefined || data.assign === undefined) { - continue; - } - - if (data.assign < 0) { - - data.method(data.array); - - } else if (data.assign > 0) { - - data.assign--; - data.method(data.array); - - } - } - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/FragmentShaderLibrary.ts b/packages/webgl/src/shader/fragment/FragmentShaderLibrary.ts deleted file mode 100644 index 3b9250dd..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderLibrary.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @class - */ -export class FragmentShaderLibrary -{ - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_IS_INSIDE (): string - { - return ` - -float isInside(in vec2 uv) { - return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0))); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_INSTANCED_COLOR_TRANSFORM_ON (): string - { - return ` - src.rgb /= max(0.0001, src.a); - src = clamp(src * mul + add, 0.0, 1.0); - src.rgb *= src.a; -`; - } - - /** - * @param {number} mediump_index - * @return {string} - * @method - * @static - */ - static STATEMENT_COLOR_TRANSFORM_ON (mediump_index: number): string - { - return ` - vec4 mul = u_mediump[${mediump_index}]; - vec4 add = u_mediump[${mediump_index + 1}]; -${FragmentShaderLibrary.STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()} -`; - } -} diff --git a/packages/webgl/src/shader/fragment/FragmentShaderSource.ts b/packages/webgl/src/shader/fragment/FragmentShaderSource.ts deleted file mode 100644 index 72cdfb9e..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderSource.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { FragmentShaderLibrary } from "./FragmentShaderLibrary"; - -/** - * @class - */ -export class FragmentShaderSource -{ - /** - * @return {string} - * @method - * @static - */ - static SOLID_COLOR (): string - { - return `#version 300 es -precision mediump float; - -uniform vec4 u_mediump; - -out vec4 o_color; - -void main() { - o_color = vec4(u_mediump.rgb * u_mediump.a, u_mediump.a); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static BITMAP_CLIPPED (): string - { - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -uniform vec4 u_mediump[3]; - -in vec2 v_uv; -out vec4 o_color; - -void main() { - vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy; - - vec4 src = texture(u_texture, uv); - ${FragmentShaderLibrary.STATEMENT_COLOR_TRANSFORM_ON(1)} - o_color = src; -}`; - } - - /** - * @return {string} - * @method - * @static - */ - static BITMAP_PATTERN (): string - { - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -uniform vec4 u_mediump[3]; - -in vec2 v_uv; -out vec4 o_color; - -void main() { - vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy); - - vec4 src = texture(u_texture, uv); - ${FragmentShaderLibrary.STATEMENT_COLOR_TRANSFORM_ON(1)} - o_color = src; -}`; - } - - /** - * @return {string} - * @method - * @static - */ - static MASK (): string - { - return `#version 300 es -precision mediump float; - -in vec2 v_bezier; -out vec4 o_color; - -void main() { - vec2 px = dFdx(v_bezier); - vec2 py = dFdy(v_bezier); - - vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y); - float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f); - - if (alpha > 0.0) { - o_color = vec4(min(alpha, 1.0)); - } else { - discard; - } -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/FragmentShaderSourceBlend.ts b/packages/webgl/src/shader/fragment/FragmentShaderSourceBlend.ts deleted file mode 100644 index 6b0a59ae..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderSourceBlend.ts +++ /dev/null @@ -1,434 +0,0 @@ -import { FragmentShaderLibrary } from "./FragmentShaderLibrary"; - -/** - * @class - */ -export class FragmentShaderSourceBlend -{ - /** - * @param {string} operation - * @param {boolean} with_color_transform - * @return {string} - * @method - * @static - */ - static TEMPLATE (operation: string, with_color_transform: boolean): string - { - let blendFunction: string; - switch (operation) { - - case "subtract": - blendFunction = this.FUNCTION_SUBTRACT(); - break; - - case "multiply": - blendFunction = this.FUNCTION_MULTIPLY(); - break; - - case "lighten": - blendFunction = this.FUNCTION_LIGHTEN(); - break; - - case "darken": - blendFunction = this.FUNCTION_DARKEN(); - break; - - case "overlay": - blendFunction = this.FUNCTION_OVERLAY(); - break; - - case "hardlight": - blendFunction = this.FUNCTION_HARDLIGHT(); - break; - - case "difference": - blendFunction = this.FUNCTION_DIFFERENCE(); - break; - - case "invert": - blendFunction = this.FUNCTION_INVERT(); - break; - - default: - blendFunction = this.FUNCTION_NORMAL(); - break; - - } - - const colorTransformUniform: string = with_color_transform - ? "uniform vec4 u_mediump[2];" - : ""; - - const colorTransformStatement: string = with_color_transform - ? FragmentShaderLibrary.STATEMENT_COLOR_TRANSFORM_ON(0) - : ""; - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_textures[2]; -${colorTransformUniform} - -in vec2 v_coord; -out vec4 o_color; - -${blendFunction} - -void main() { - vec4 dst = texture(u_textures[0], v_coord); - vec4 src = texture(u_textures[1], v_coord); - ${colorTransformStatement} - o_color = blend(src, dst); -} - -`; - } - - /** - * @param {string} operation - * @param {boolean} with_color_transform - * @return {string} - * @method - * @static - */ - static INSTANCE_TEMPLATE (operation: string, with_color_transform: boolean): string - { - let blendFunction: string; - switch (operation) { - - case "subtract": - blendFunction = this.FUNCTION_SUBTRACT(); - break; - - case "multiply": - blendFunction = this.FUNCTION_MULTIPLY(); - break; - - case "lighten": - blendFunction = this.FUNCTION_LIGHTEN(); - break; - - case "darken": - blendFunction = this.FUNCTION_DARKEN(); - break; - - case "overlay": - blendFunction = this.FUNCTION_OVERLAY(); - break; - - case "hardlight": - blendFunction = this.FUNCTION_HARDLIGHT(); - break; - - case "difference": - blendFunction = this.FUNCTION_DIFFERENCE(); - break; - - case "invert": - blendFunction = this.FUNCTION_INVERT(); - break; - - default: - blendFunction = this.FUNCTION_NORMAL(); - break; - - } - - const colorTransformUniform: string = with_color_transform - ? "uniform vec4 u_mediump[2];" - : ""; - - const colorTransformStatement: string = with_color_transform - ? FragmentShaderLibrary.STATEMENT_COLOR_TRANSFORM_ON(0) - : ""; - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_textures[2]; -${colorTransformUniform} - -in vec2 v_src_coord; -in vec2 v_dst_coord; -out vec4 o_color; - -${blendFunction} - -void main() { - vec4 dst = texture(u_textures[0], v_dst_coord); - vec4 src = texture(u_textures[1], v_src_coord); - ${colorTransformStatement} - o_color = blend(src, dst); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_NORMAL (): string - { - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - return src + dst - dst * src.a; -} - -`; - } - - // 各ブレンド式は、前景と背景の両方のアルファを考慮する必要がある - // https://odashi.hatenablog.com/entry/20110921/1316610121 - // https://hakuhin.jp/as3/blend.html - // - // [基本計算式] - // ・色(rgb)はストレートアルファ - // ・アルファ(a)が0の場合は例外処理をする - // 前景色 a: src.rgb * (src.a * (1.0 - dst.a)) - // 背景色 b: dst.rgb * (dst.a * (1.0 - src.a)) - // 合成色 c: mix.rgb * (src.a * dst.a) - // 最終結果: a + b + c - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_SUBTRACT (): string - { - // [合成色計算式] - // dst - src - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 c = vec4(dst.rgb - src.rgb, src.a * dst.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_MULTIPLY (): string - { - // [合成色計算式] - // src * dst - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - vec4 c = src * dst; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_LIGHTEN (): string - { - // [合成色計算式] - // (src > dst) ? src : dst - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 c = vec4(mix(src.rgb, dst.rgb, step(src.rgb, dst.rgb)), src.a * dst.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_DARKEN (): string - { - // [合成色計算式] - // (src < dst) ? src : dst - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 c = vec4(mix(src.rgb, dst.rgb, step(dst.rgb, src.rgb)), src.a * dst.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_OVERLAY (): string - { - // [合成色計算式] - // if (dst < 0.5) { - // return 2.0 * src * dst - // } else { - // return 1.0 - 2.0 * (1.0 - src) * (1.0 - dst) - // } - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 mul = src * dst; - vec3 c1 = 2.0 * mul.rgb; - vec3 c2 = 2.0 * (src.rgb + dst.rgb - mul.rgb) - 1.0; - vec4 c = vec4(mix(c1, c2, step(vec3(0.5), dst.rgb)), mul.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_HARDLIGHT (): string - { - // [合成色計算式] - // if (src < 0.5) { - // return 2.0 * src * dst - // } else { - // return 1.0 - 2.0 * (1.0 - src) * (1.0 - dst) - // } - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 mul = src * dst; - vec3 c1 = 2.0 * mul.rgb; - vec3 c2 = 2.0 * (src.rgb + dst.rgb - mul.rgb) - 1.0; - vec4 c = vec4(mix(c1, c2, step(vec3(0.5), src.rgb)), mul.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_DIFFERENCE (): string - { - // [合成色計算式] - // abs(src - dst) - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 a = src - src * dst.a; - vec4 b = dst - dst * src.a; - - src.rgb /= src.a; - dst.rgb /= dst.a; - - vec4 c = vec4(abs(src.rgb - dst.rgb), src.a * dst.a); - c.rgb *= c.a; - - return a + b + c; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_INVERT (): string - { - // [基本計算式] - // ((1.0 - dst) * src.a) + (dst * (1.0 - src.a)) - return ` - -vec4 blend (in vec4 src, in vec4 dst) { - if (src.a == 0.0) { return dst; } - if (dst.a == 0.0) { return src; } - - vec4 b = dst - dst * src.a; - vec4 c = vec4(src.a - dst.rgb * src.a, src.a); - - return b + c; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/FragmentShaderSourceGradient.ts b/packages/webgl/src/shader/fragment/FragmentShaderSourceGradient.ts deleted file mode 100644 index 65348faf..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderSourceGradient.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @class - */ -export class FragmentShaderSourceGradient -{ - /** - * @param {number} highp_length - * @param {number} fragment_index - * @param {boolean} is_radial - * @param {boolean} has_focal_point - * @param {string} spread_method - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - highp_length: number, fragment_index: number, - is_radial: boolean, has_focal_point: boolean, - spread_method: string - ): string { - - const gradientTypeStatement: string = is_radial - ? this.STATEMENT_GRADIENT_TYPE_RADIAL(fragment_index, has_focal_point) - : this.STATEMENT_GRADIENT_TYPE_LINEAR(fragment_index); - - let spread_methodExpression: string; - switch (spread_method) { - - case "reflect": - spread_methodExpression = "1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)"; - break; - - case "repeat": - spread_methodExpression = "fract(t)"; - break; - - default: - spread_methodExpression = "clamp(t, 0.0, 1.0)"; - break; - - } - - return `#version 300 es -precision highp float; - -uniform sampler2D u_texture; -uniform vec4 u_highp[${highp_length}]; - -in vec2 v_uv; -out vec4 o_color; - -void main() { - vec2 p = v_uv; - ${gradientTypeStatement} - t = ${spread_methodExpression}; - o_color = texture(u_texture, vec2(t, 0.5)); -} - -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_GRADIENT_TYPE_LINEAR (index: number): string - { - return ` - vec2 a = u_highp[${index}].xy; - vec2 b = u_highp[${index}].zw; - - vec2 ab = b - a; - vec2 ap = p - a; - - float t = dot(ab, ap) / dot(ab, ab); -`; - } - - /** - * @param {number} index - * @param {boolean} has_focal_point - * @return {string} - * @method - * @static - */ - static STATEMENT_GRADIENT_TYPE_RADIAL ( - index: number, has_focal_point: boolean - ): string { - - const focalPointStatement: string = has_focal_point - ? this.STATEMENT_FOCAL_POINT_ON(index) - : this.STATEMENT_FOCAL_POINT_OFF(); - - return ` - float radius = u_highp[${index}][0]; - - vec2 coord = p / radius; - ${focalPointStatement} -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_FOCAL_POINT_OFF (): string - { - return ` - float t = length(coord); -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_FOCAL_POINT_ON (index: number): string - { - return ` - vec2 focal = vec2(u_highp[${index}][1], 0.0); - - vec2 dir = normalize(coord - focal); - - float a = dot(dir, dir); - float b = 2.0 * dot(dir, focal); - float c = dot(focal, focal) - 1.0; - float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); - - float t = distance(focal, coord) / distance(focal, focal + dir * x); -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/FragmentShaderSourceGradientLUT.ts b/packages/webgl/src/shader/fragment/FragmentShaderSourceGradientLUT.ts deleted file mode 100644 index 98d93561..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderSourceGradientLUT.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { $Math } from "@next2d/share"; - -/** - * @class - */ -export class FragmentShaderSourceGradientLUT -{ - /** - * @param {number} mediump_length - * @param {number} stops_length - * @param {boolean} is_linear_space - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - mediump_length: number, - stops_length: number, - is_linear_space: boolean - ): string { - - let loopStatement: string = ""; - for (let i: number = 1; i < stops_length; i++) { - - const i0: number = i - 1; - const i1: number = i; - const t0: string = `u_mediump[${stops_length + $Math.floor(i0 / 4)}][${i0 % 4}]`; - const t1: string = `u_mediump[${stops_length + $Math.floor(i1 / 4)}][${i1 % 4}]`; - const c0: string = `u_mediump[${i0}]`; - const c1: string = `u_mediump[${i1}]`; - - loopStatement += ` - if (t <= ${t1}) { - return mix(${c0}, ${c1}, (t - ${t0}) / (${t1} - ${t0})); - } -`; - } - - const colorSpaceStatement: string = is_linear_space - ? "color = pow(color, vec4(0.45454545));" - : ""; - - return `#version 300 es -precision mediump float; - -uniform vec4 u_mediump[${mediump_length}]; - -in vec2 v_coord; -out vec4 o_color; - -vec4 getGradientColor(in float t) { - if (t <= u_mediump[${stops_length}][0]) { - return u_mediump[0]; - } - ${loopStatement} - return u_mediump[${stops_length - 1}]; -} - -void main() { - vec4 color = getGradientColor(v_coord.x); - ${colorSpaceStatement} - color.rgb *= color.a; - - o_color = color; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/FragmentShaderSourceTexture.ts b/packages/webgl/src/shader/fragment/FragmentShaderSourceTexture.ts deleted file mode 100644 index 496753b5..00000000 --- a/packages/webgl/src/shader/fragment/FragmentShaderSourceTexture.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { FragmentShaderLibrary } from "./FragmentShaderLibrary"; - -/** - * @class - */ -export class FragmentShaderSourceTexture -{ - /** - * @param {boolean} with_color_transform - * @return {string} - * @method - * @static - */ - static TEMPLATE (with_color_transform: boolean): string - { - const colorTransformUniform: string = with_color_transform - ? "uniform vec4 u_mediump[2];" - : ""; - - const colorTransformStatement: string = with_color_transform - ? FragmentShaderLibrary.STATEMENT_COLOR_TRANSFORM_ON(0) - : ""; - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -${colorTransformUniform} - -in vec2 v_coord; -out vec4 o_color; - -void main() { - vec4 src = texture(u_texture, v_coord); - ${colorTransformStatement} - o_color = src; -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static INSTANCE_TEMPLATE (): string - { - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; - -in vec4 mul; -in vec4 add; -in vec2 v_coord; -out vec4 o_color; - -void main() { - vec4 src = texture(u_texture, v_coord); - - if (mul.x != 1.0 || mul.y != 1.0 || mul.z != 1.0 || mul.w != 1.0 - || add.x != 0.0 || add.y != 0.0 || add.z != 0.0 || add.w != 0.0 - ) { - src.rgb /= max(0.0001, src.a); - src = clamp(src * mul + add, 0.0, 1.0); - src.rgb *= src.a; - } - - o_color = src; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceBlurFilter.ts b/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceBlurFilter.ts deleted file mode 100644 index e4705af6..00000000 --- a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceBlurFilter.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @class - */ -export class FragmentShaderSourceBlurFilter -{ - /** - * @param {number} half_blur - * @return {string} - * @method - * @static - */ - static TEMPLATE (half_blur: number): string - { - const halfBlurFixed: string = half_blur.toFixed(1); - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -uniform vec4 u_mediump; - -in vec2 v_coord; -out vec4 o_color; - -void main() { - vec2 offset = u_mediump.xy; - float fraction = u_mediump.z; - float samples = u_mediump.w; - - vec4 color = texture(u_texture, v_coord); - - for (float i = 1.0; i < ${halfBlurFixed}; i += 1.0) { - color += texture(u_texture, v_coord + offset * i); - color += texture(u_texture, v_coord - offset * i); - } - color += texture(u_texture, v_coord + offset * ${halfBlurFixed}) * fraction; - color += texture(u_texture, v_coord - offset * ${halfBlurFixed}) * fraction; - color /= samples; - - o_color = color; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceColorMatrixFilter.ts b/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceColorMatrixFilter.ts deleted file mode 100644 index 2d1fed3a..00000000 --- a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceColorMatrixFilter.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @class - */ -export class FragmentShaderSourceColorMatrixFilter -{ - /** - * @return {string} - * @method - * @static - */ - static TEMPLATE (): string - { - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -uniform vec4 u_mediump[5]; - -in vec2 v_coord; -out vec4 o_color; - -void main() { - mat4 mul = mat4(u_mediump[0], u_mediump[1], u_mediump[2], u_mediump[3]); - vec4 add = u_mediump[4]; - - vec4 color = texture(u_texture, v_coord); - - color.rgb /= max(0.0001, color.a); - color = clamp(color * mul + add, 0.0, 1.0); - color.rgb *= color.a; - - o_color = color; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceConvolutionFilter.ts b/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceConvolutionFilter.ts deleted file mode 100644 index aa1760cc..00000000 --- a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceConvolutionFilter.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { FragmentShaderLibrary } from "../FragmentShaderLibrary"; -import { $Math } from "@next2d/share"; - -/** - * @class - */ -export class FragmentShaderSourceConvolutionFilter -{ - /** - * @param {number} mediump_length - * @param {number} x - * @param {number} y - * @param {boolean} preserve_alpha - * @param {boolean} clamp - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - mediump_length: number, - x: number, y: number, - preserve_alpha: boolean, clamp: boolean - ): string { - - const halfX: number = $Math.floor(x * 0.5); - const halfY: number = $Math.floor(y * 0.5); - const size: number = x * y; - - let matrixStatement: string = ""; - const matrixIndex: number = clamp ? 1 : 2; - for (let idx: number = 0; idx < size; ++idx) { - - const index: number = matrixIndex + $Math.floor(idx / 4); - - const component: number = idx % 4; - - matrixStatement += ` - result += getWeightedColor(${idx}, u_mediump[${index}][${component}]); -`; - } - - const preserve_alphaStatement: string = preserve_alpha - ? "result.a = texture(u_texture, v_coord).a;" - : ""; - const clampStatement: string = clamp - ? "" - : ` - vec4 substitute_color = u_mediump[1]; - color = mix(substitute_color, color, isInside(uv)); -`; - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_texture; -uniform vec4 u_mediump[${mediump_length}]; - -in vec2 v_coord; -out vec4 o_color; - -${FragmentShaderLibrary.FUNCTION_IS_INSIDE()} - -vec4 getWeightedColor (in int i, in float weight) { - vec2 rcp_size = u_mediump[0].xy; - - int i_div_x = i / ${x}; - int i_mod_x = i - ${x} * i_div_x; - vec2 offset = vec2(i_mod_x - ${halfX}, ${halfY} - i_div_x); - vec2 uv = v_coord + offset * rcp_size; - - vec4 color = texture(u_texture, uv); - color.rgb /= max(0.0001, color.a); - ${clampStatement} - - return color * weight; -} - -void main() { - float rcp_divisor = u_mediump[0].z; - float bias = u_mediump[0].w; - - vec4 result = vec4(0.0); - ${matrixStatement} - result = clamp(result * rcp_divisor + bias, 0.0, 1.0); - ${preserve_alphaStatement} - - result.rgb *= result.a; - o_color = result; -} - -`; - } -} diff --git a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceDisplacementMapFilter.ts b/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceDisplacementMapFilter.ts deleted file mode 100644 index 68d34d76..00000000 --- a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceDisplacementMapFilter.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { FragmentShaderLibrary } from "../FragmentShaderLibrary"; - -/** - * @class - */ -export class FragmentShaderSourceDisplacementMapFilter -{ - /** - * @param {number} mediump_length - * @param {number} component_x - * @param {number} component_y - * @param {string} mode - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - mediump_length: number, - component_x: number, component_y: number, - mode: string - ): string { - - let cx: string; - let cy: string; - let modeStatement: string; - - switch (component_x) { - - case 1: // BitmapDataChannel.RED - cx = "map_color.r"; - break; - - case 2: // BitmapDataChannel.GREEN - cx = "map_color.g"; - break; - - case 4: // BitmapDataChannel.BLUE - cx = "map_color.b"; - break; - - case 8: // BitmapDataChannel.ALPHA - cx = "map_color.a"; - break; - - default: - cx = "0.5"; - break; - - } - - switch (component_y) { - - case 1: // BitmapDataChannel.RED - cy = "map_color.r"; - break; - - case 2: // BitmapDataChannel.GREEN - cy = "map_color.g"; - break; - - case 4: // BitmapDataChannel.BLUE - cy = "map_color.b"; - break; - - case 8: // BitmapDataChannel.ALPHA - cy = "map_color.a"; - break; - - default: - cy = "0.5"; - break; - - } - - switch (mode) { - - case "clamp": - modeStatement = ` - vec4 source_color = texture(u_textures[0], uv); -`; - break; - - case "ignore": - // 置き換え後の座標が範囲外なら、置き換え前の座標をとる(x軸とy軸を別々に判定する) - modeStatement = ` - vec4 source_color =texture(u_textures[0], mix(v_coord, uv, step(abs(uv - vec2(0.5)), vec2(0.5)))); -`; - break; - - case "color": - modeStatement = ` - vec4 substitute_color = u_mediump[2]; - vec4 source_color = mix(substitute_color, texture(u_textures[0], uv), isInside(uv)); -`; - break; - - case "wrap": - default: - modeStatement = ` - vec4 source_color = texture(u_textures[0], fract(uv)); -`; - break; - } - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_textures[2]; -uniform vec4 u_mediump[${mediump_length}]; - -in vec2 v_coord; -out vec4 o_color; - -${FragmentShaderLibrary.FUNCTION_IS_INSIDE()} - -void main() { - vec2 uv_to_st_scale = u_mediump[0].xy; - vec2 uv_to_st_offset = u_mediump[0].zw; - vec2 scale = u_mediump[1].xy; - - vec2 st = v_coord * uv_to_st_scale - uv_to_st_offset; - vec4 map_color = texture(u_textures[1], st); - - vec2 offset = vec2(${cx}, ${cy}) - 0.5; - vec2 uv = v_coord + offset * scale; - ${modeStatement} - - o_color = mix(texture(u_textures[0], v_coord), source_color, isInside(st)); -} - -`; - } -} diff --git a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceFilter.ts b/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceFilter.ts deleted file mode 100644 index bd041fa2..00000000 --- a/packages/webgl/src/shader/fragment/filter/FragmentShaderSourceFilter.ts +++ /dev/null @@ -1,337 +0,0 @@ -import { $Math } from "@next2d/share"; -import { FragmentShaderLibrary } from "../FragmentShaderLibrary"; - -/** - * @class - */ -export class FragmentShaderSourceFilter -{ - /** - * @param {number} textures_length - * @param {number} mediump_length - * @param {boolean} transforms_base - * @param {boolean} transforms_blur - * @param {boolean} isGlow - * @param {string} type - * @param {boolean} knockout - * @param {boolean} applies_strength - * @param {boolean} is_gradient - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - textures_length: number, mediump_length: number, - transforms_base: boolean, transforms_blur: boolean, - isGlow: boolean, type: string, knockout: boolean, - applies_strength: boolean, is_gradient: boolean - ): string { - - let index: number = 0; - - const baseStatement: string = transforms_base - ? this.STATEMENT_BASE_TEXTURE_TRANSFORM(index++) - : ""; - - const blurStatement: string = transforms_blur - ? this.STATEMENT_BLUR_TEXTURE_TRANSFORM(index++) - : this.STATEMENT_BLUR_TEXTURE(); - - const isInner: boolean = type === "inner"; - - const colorIndex: number = index; - let strengthOffset: number = index * 4; - let colorStatement: string; - - if (is_gradient) { - - colorStatement = isGlow - ? this.STATEMENT_GLOW(false, transforms_base, applies_strength, is_gradient, colorIndex, strengthOffset) - : this.STATEMENT_BEVEL(transforms_base, transforms_blur, applies_strength, is_gradient, colorIndex, strengthOffset); - - } else if (isGlow) { - - strengthOffset += 4; - colorStatement = this.STATEMENT_GLOW(isInner, transforms_base, applies_strength, is_gradient, colorIndex, strengthOffset); - - } else { - - strengthOffset += 8; - colorStatement = this.STATEMENT_BEVEL(transforms_base, transforms_blur, applies_strength, is_gradient, colorIndex, strengthOffset); - - } - - let modeExpression: string; - switch (type) { - - case "outer": - modeExpression = knockout - ? "blur - blur * base.a" - : "base + blur - blur * base.a"; - break; - - case "full": - modeExpression = knockout - ? "blur" - : "base - base * blur.a + blur"; - break; - - case "inner": - default: - modeExpression = "blur"; - break; - - } - - return `#version 300 es -precision mediump float; - -uniform sampler2D u_textures[${textures_length}]; -uniform vec4 u_mediump[${mediump_length}]; - -in vec2 v_coord; -out vec4 o_color; - -${FragmentShaderLibrary.FUNCTION_IS_INSIDE()} - -void main() { - ${baseStatement} - ${blurStatement} - ${colorStatement} - o_color = ${modeExpression}; -} - -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_BASE_TEXTURE_TRANSFORM (index :number): string - { - return ` - vec2 base_scale = u_mediump[${index}].xy; - vec2 base_offset = u_mediump[${index}].zw; - - vec2 uv = v_coord * base_scale - base_offset; - vec4 base = mix(vec4(0.0), texture(u_textures[1], uv), isInside(uv)); -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_BLUR_TEXTURE (): string - { - return ` - vec4 blur = texture(u_textures[0], v_coord); -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_BLUR_TEXTURE_TRANSFORM (index: number): string - { - return ` - vec2 blur_scale = u_mediump[${index}].xy; - vec2 blur_offset = u_mediump[${index}].zw; - - vec2 st = v_coord * blur_scale - blur_offset; - vec4 blur = mix(vec4(0.0), texture(u_textures[0], st), isInside(st)); -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_GLOW ( - is_inner: boolean, transforms_base: boolean, applies_strength: boolean, - is_gradient: boolean, color_index: number, strength_offset: number - ): string { - - const innerStatement: string = is_inner - ? "blur.a = 1.0 - blur.a;" - : ""; - - const strengthStatement: string = applies_strength - ? this.STATEMENT_GLOW_STRENGTH(strength_offset) - : ""; - - const colorStatement: string = is_gradient - ? this.STATEMENT_GLOW_GRADIENT_COLOR(transforms_base) - : this.STATEMENT_GLOW_SOLID_COLOR(color_index); - - return ` - ${innerStatement} - ${strengthStatement} - ${colorStatement} -`; - } - - /** - * @param {number} offset - * @return {string} - * @method - * @static - */ - static STATEMENT_GLOW_STRENGTH (offset: number): string - { - const index: number = $Math.floor(offset / 4); - const component: number = offset % 4; - return ` - float strength = u_mediump[${index}][${component}]; - blur.a = clamp(blur.a * strength, 0.0, 1.0); -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_GLOW_SOLID_COLOR (index: number): string - { - return ` - vec4 color = u_mediump[${index}]; - blur = color * blur.a; -`; - } - - /** - * @param {boolean} transforms_base - * @return {string} - * @method - * @static - */ - static STATEMENT_GLOW_GRADIENT_COLOR (transforms_base: boolean): string - { - return ` - blur = texture(u_textures[${transforms_base ? 2 : 1}], vec2(blur.a, 0.5)); -`; - } - - /** - * @param {string} transforms_base - * @param {boolean} transforms_blur - * @param {boolean} applies_strength - * @param {boolean} is_gradient - * @param {number} color_index - * @param {number} strength_offset - * @return {string} - * @method - * @static - */ - static STATEMENT_BEVEL ( - transforms_base: boolean, transforms_blur: boolean, applies_strength: boolean, - is_gradient: boolean, color_index: number, strength_offset: number - ): string { - - const blur2Statement: string = transforms_blur - ? this.STATEMENT_BLUR_TEXTURE_TRANSFORM_2() - : this.STATEMENT_BLUR_TEXTURE_2(); - - const strengthStatement: string = applies_strength - ? this.STATEMENT_BEVEL_STRENGTH(strength_offset) - : ""; - - const colorStatement: string = is_gradient - ? this.STATEMENT_BEVEL_GRADIENT_COLOR(transforms_base) - : this.STATEMENT_BEVEL_SOLID_COLOR(color_index); - - return ` - ${blur2Statement} - float highlight_alpha = blur.a - blur2.a; - float shadow_alpha = blur2.a - blur.a; - ${strengthStatement} - highlight_alpha = clamp(highlight_alpha, 0.0, 1.0); - shadow_alpha = clamp(shadow_alpha, 0.0, 1.0); - ${colorStatement} -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_BLUR_TEXTURE_2 (): string - { - return ` - vec4 blur2 = texture(u_textures[0], 1.0 - v_coord); -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_BLUR_TEXTURE_TRANSFORM_2 (): string - { - return ` - vec2 pq = (1.0 - v_coord) * blur_scale - blur_offset; - vec4 blur2 = mix(vec4(0.0), texture(u_textures[0], pq), isInside(pq)); -`; - } - - /** - * @param {boolean} offset - * @return {string} - * @method - * @static - */ - static STATEMENT_BEVEL_STRENGTH (offset: number): string - { - const index: number = $Math.floor(offset / 4); - const component: number = offset % 4; - - return ` - float strength = u_mediump[${index}][${component}]; - highlight_alpha *= strength; - shadow_alpha *= strength; -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static STATEMENT_BEVEL_SOLID_COLOR (index: number): string - { - return ` - vec4 highlight_color = u_mediump[${index}]; - vec4 shadow_color = u_mediump[${index + 1}]; - blur = highlight_color * highlight_alpha + shadow_color * shadow_alpha; -`; - } - - /** - * @param {boolean} transforms_base - * @return {string} - * @method - * @static - */ - static STATEMENT_BEVEL_GRADIENT_COLOR (transforms_base: boolean): string - { - return ` - blur = texture(u_textures[${transforms_base ? 2 : 1}], vec2( - 0.5019607843137255 - 0.5019607843137255 * shadow_alpha + 0.4980392156862745 * highlight_alpha, - 0.5 - )); -`; - } -} diff --git a/packages/webgl/src/shader/variants/BlendShaderVariantCollection.ts b/packages/webgl/src/shader/variants/BlendShaderVariantCollection.ts deleted file mode 100644 index d62ab538..00000000 --- a/packages/webgl/src/shader/variants/BlendShaderVariantCollection.ts +++ /dev/null @@ -1,521 +0,0 @@ -import { CanvasToWebGLShader } from "../CanvasToWebGLShader"; -import { VertexShaderSource } from "../vertex/VertexShaderSource"; -import { FragmentShaderSourceTexture } from "../fragment/FragmentShaderSourceTexture"; -import { FragmentShaderSourceBlend } from "../fragment/FragmentShaderSourceBlend"; -import type { CanvasToWebGLContext } from "../../CanvasToWebGLContext"; -import type { WebGLShaderUniform } from "../WebGLShaderUniform"; -import type { WebGLShaderInstance } from "../WebGLShaderInstance"; -import { $getMap } from "@next2d/share"; -import { $RENDER_SIZE } from "../../Const"; - -/** - * @class - */ -export class BlendShaderVariantCollection -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$collection: Map; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {Map} - * @private - */ - this._$collection = $getMap(); - } - - /** - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getInstanceShader (): CanvasToWebGLShader - { - const key: string = "i"; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader | void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.INSTANCE(), - FragmentShaderSourceTexture.INSTANCE_TEMPLATE() - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {boolean} with_color_transform - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getNormalBlendShader (with_color_transform: boolean): CanvasToWebGLShader - { - const key: string = `n${with_color_transform ? "y" : "n"}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.BLEND(), - FragmentShaderSourceTexture.TEMPLATE(with_color_transform) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getClipShader (): CanvasToWebGLShader - { - const key: string = "c"; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.BLEND_CLIP(), - FragmentShaderSourceTexture.TEMPLATE(false) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {string} operation - * @param {boolean} with_color_transform - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getBlendShader ( - operation: string, - with_color_transform: boolean - ): CanvasToWebGLShader { - - const key: string = `${operation}${with_color_transform ? "y" : "n"}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.BLEND(), - FragmentShaderSourceBlend.TEMPLATE(operation, with_color_transform) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {string} operation - * @param {boolean} with_color_transform - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getInstanceBlendShader ( - operation: string, - with_color_transform: boolean - ): CanvasToWebGLShader { - - const key: string = `i${operation}${with_color_transform ? "y" : "n"}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader | void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.INSTANCE_BLEND(), - FragmentShaderSourceBlend.INSTANCE_TEMPLATE(operation, with_color_transform) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} matrix - * @param {number} render_width - * @param {number} render_height - * @param {boolean} with_color_transform - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @return {void} - * @method - * @public - */ - setNormalBlendUniform ( - uniform: WebGLShaderUniform, - x: number, y: number, w: number, h: number, - matrix: Float32Array, render_width: number, render_height: number, - with_color_transform: boolean, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - - // vertex: u_offset - highp[0] = x; - highp[1] = y; - // vertex: u_size - highp[2] = w; - highp[3] = h; - - // vertex: u_matrix - highp[4] = matrix[0]; - highp[5] = matrix[1]; - highp[6] = matrix[2]; - - highp[8] = matrix[3]; - highp[9] = matrix[4]; - highp[10] = matrix[5]; - - highp[12] = matrix[6]; - highp[13] = matrix[7]; - highp[14] = matrix[8]; - - // vertex: u_viewport - highp[7] = render_width; - highp[11] = render_height; - - if (with_color_transform) { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_color_transform_mul - mediump[0] = ct0; - mediump[1] = ct1; - mediump[2] = ct2; - mediump[3] = ct3; - // fragment: u_color_transform_add - mediump[4] = ct4; - mediump[5] = ct5; - mediump[6] = ct6; - mediump[7] = ct7; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} matrix - * @param {number} render_width - * @param {number} render_height - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @return {void} - * @method - * @public - */ - pushNormalBlend ( - instance: WebGLShaderInstance, - x: number, y: number, w: number, h: number, - matrix: Float32Array, render_width: number, render_height: number, - ct0: number = 1, ct1: number = 1, ct2: number = 1, ct3: number = 1, - ct4: number = 0, ct5: number = 0, ct6: number = 0, ct7: number = 0 - ): void { - - // texture rectangle - instance.attributes.push( - // texture rectangle - x / $RENDER_SIZE, y / $RENDER_SIZE, - w / $RENDER_SIZE, h / $RENDER_SIZE, - // texture width, height and viewport width, height - w, h, render_width, render_height, - // matrix tx, ty and with_color_transform - matrix[6], matrix[7], - // matrix scale0, rotate0, scale1, rotate1 - matrix[0], matrix[1], matrix[3], matrix[4], - // mulColor - ct0, ct1, ct2, ct3, - // addColor - ct4, ct5, ct6, ct7 - ); - - instance.count++; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} inverse_matrix - * @param {number} render_width - * @param {number} render_height - * @return {void} - * @method - * @public - */ - setClipUniform ( - uniform: WebGLShaderUniform, - x: number, y: number, w: number, h: number, - inverse_matrix: Float32Array, render_width: number, render_height: number - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - - // vertex: u_offset - highp[0] = x; - highp[1] = y; - // vertex: u_size - highp[2] = w; - highp[3] = h; - - // vertex: u_inverse_matrix - highp[4] = inverse_matrix[0]; - highp[5] = inverse_matrix[1]; - highp[6] = inverse_matrix[2]; - - highp[8] = inverse_matrix[3]; - highp[9] = inverse_matrix[4]; - highp[10] = inverse_matrix[5]; - - highp[12] = inverse_matrix[6]; - highp[13] = inverse_matrix[7]; - highp[14] = inverse_matrix[8]; - - // vertex: u_viewport - highp[7] = render_width; - highp[11] = render_height; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} matrix - * @param {number} render_width - * @param {number} render_height - * @param {boolean} with_color_transform - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @return {void} - * @method - * @public - */ - setInstanceBlendUniform ( - uniform: WebGLShaderUniform, - x: number, y: number, w: number, h: number, - texture_width: number, texture_height: number, - matrix: Float32Array, render_width: number, render_height: number, - with_color_transform: boolean, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number - ): void { - - const textures: Int32Array|Float32Array = uniform.textures; - textures[0] = 0; - textures[1] = 1; - - const highp: Int32Array|Float32Array = uniform.highp; - - // vertex: u_rect - highp[0] = x; - highp[1] = y; - highp[2] = w; - highp[3] = h; - - // vertex: u_matrix - highp[4] = matrix[0]; - highp[5] = matrix[1]; - highp[6] = matrix[2]; - - highp[8] = matrix[3]; - highp[9] = matrix[4]; - highp[10] = matrix[5]; - - highp[12] = matrix[6]; - highp[13] = matrix[7]; - highp[14] = matrix[8]; - - // vertex: u_viewport - highp[7] = render_width; - highp[11] = render_height; - - // vertex: u_size - highp[16] = texture_width; - highp[17] = texture_height; - - if (with_color_transform) { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_color_transform_mul - mediump[0] = ct0; - mediump[1] = ct1; - mediump[2] = ct2; - mediump[3] = ct3; - // fragment: u_color_transform_add - mediump[4] = ct4; - mediump[5] = ct5; - mediump[6] = ct6; - mediump[7] = ct7; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Float32Array} matrix - * @param {number} render_width - * @param {number} render_height - * @param {Float32Array} with_color_transform - * @param {number} ct0 - * @param {number} ct1 - * @param {number} ct2 - * @param {number} ct3 - * @param {number} ct4 - * @param {number} ct5 - * @param {number} ct6 - * @param {number} ct7 - * @return {void} - * @method - * @public - */ - setBlendUniform ( - uniform: WebGLShaderUniform, - x: number, y: number, w: number, h: number, - matrix: Float32Array, render_width: number, render_height: number, - with_color_transform: boolean, - ct0: number, ct1: number, ct2: number, ct3: number, - ct4: number, ct5: number, ct6: number, ct7: number - ): void { - - const textures: Int32Array|Float32Array = uniform.textures; - textures[0] = 0; - textures[1] = 1; - - const highp: Int32Array|Float32Array = uniform.highp; - - // vertex: u_offset - highp[0] = x; - highp[1] = y; - // vertex: u_size - highp[2] = w; - highp[3] = h; - - // vertex: u_matrix - highp[4] = matrix[0]; - highp[5] = matrix[1]; - highp[6] = matrix[2]; - - highp[8] = matrix[3]; - highp[9] = matrix[4]; - highp[10] = matrix[5]; - - highp[12] = matrix[6]; - highp[13] = matrix[7]; - highp[14] = matrix[8]; - - // vertex: u_viewport - highp[7] = render_width; - highp[11] = render_height; - - if (with_color_transform) { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_color_transform_mul - mediump[0] = ct0; - mediump[1] = ct1; - mediump[2] = ct2; - mediump[3] = ct3; - // fragment: u_color_transform_add - mediump[4] = ct4; - mediump[5] = ct5; - mediump[6] = ct6; - mediump[7] = ct7; - } - } -} diff --git a/packages/webgl/src/shader/variants/FilterShaderVariantCollection.ts b/packages/webgl/src/shader/variants/FilterShaderVariantCollection.ts deleted file mode 100644 index 8fa7f292..00000000 --- a/packages/webgl/src/shader/variants/FilterShaderVariantCollection.ts +++ /dev/null @@ -1,552 +0,0 @@ -import { CanvasToWebGLShader } from "../CanvasToWebGLShader"; -import { VertexShaderSource } from "../vertex/VertexShaderSource"; -import { FragmentShaderSourceFilter } from "../fragment/filter/FragmentShaderSourceFilter"; -import { FragmentShaderSourceBlurFilter } from "../fragment/filter/FragmentShaderSourceBlurFilter"; -import { FragmentShaderSourceColorMatrixFilter } from "../fragment/filter/FragmentShaderSourceColorMatrixFilter"; -import { FragmentShaderSourceConvolutionFilter } from "../fragment/filter/FragmentShaderSourceConvolutionFilter"; -import { FragmentShaderSourceDisplacementMapFilter } from "../fragment/filter/FragmentShaderSourceDisplacementMapFilter"; -import type { WebGLShaderUniform } from "../WebGLShaderUniform"; -import type { CanvasToWebGLContext } from "../../CanvasToWebGLContext"; -import { - $Math, - $getMap -} from "@next2d/share"; - -/** - * @class - */ -export class FilterShaderVariantCollection -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$collection: Map; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {Map} - * @private - */ - this._$collection = $getMap(); - } - - /** - * @param {number} half_blur - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getBlurFilterShader (half_blur: number): CanvasToWebGLShader - { - const key: string = `b${half_blur}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceBlurFilter.TEMPLATE(half_blur) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {boolean} transforms_base - * @param {boolean} transforms_blur - * @param {boolean} is_glow - * @param {string} type - * @param {boolean} knockout - * @param {boolean} applies_strength - * @param {boolean} is_gradient - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getBitmapFilterShader ( - transforms_base: boolean, transforms_blur: boolean, - is_glow: boolean, type: string, - knockout: boolean, applies_strength: boolean, - is_gradient: boolean - ): CanvasToWebGLShader { - - const key1: string = transforms_base ? "y" : "n"; - const key2: string = transforms_blur ? "y" : "n"; - const key3: string = is_glow ? "y" : "n"; - const key4: string = knockout ? "y" : "n"; - const key5: string = applies_strength ? "y" : "n"; - const key: string = `f${key1}${key2}${key3}${type}${key4}${key5}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - let texturesLength = 1; - if (transforms_base) { texturesLength++ } - if (is_gradient) { texturesLength++ } - - let mediumpLength: number = (transforms_base ? 4 : 0) - + (transforms_blur ? 4 : 0) - + (applies_strength ? 1 : 0); - - if (!is_gradient) { - mediumpLength += is_glow ? 4 : 8; - } - - mediumpLength = $Math.ceil(mediumpLength / 4); - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceFilter.TEMPLATE( - texturesLength, mediumpLength, - transforms_base, transforms_blur, - is_glow, type, knockout, - applies_strength, is_gradient - ) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getColorMatrixFilterShader (): CanvasToWebGLShader - { - const key: string = "m"; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceColorMatrixFilter.TEMPLATE() - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {number} x - * @param {number} y - * @param {boolean} preserve_alpha - * @param {boolean} clamp - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getConvolutionFilterShader ( - x: number, y: number, - preserve_alpha: boolean, clamp: boolean - ): CanvasToWebGLShader { - - const key1: string = ("0" + x).slice(-2); - const key2: string = ("0" + y).slice(-2); - const key3: string = preserve_alpha ? "y" : "n"; - const key4: string = clamp ? "y" : "n"; - const key: string = `c${key1}${key2}${key3}${key4}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const mediumpLength: number = (clamp ? 1 : 2) + $Math.ceil(x * y / 4); - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceConvolutionFilter.TEMPLATE( - mediumpLength, x, y, preserve_alpha, clamp - ) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {number} component_x - * @param {number} component_y - * @param {string} mode - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getDisplacementMapFilterShader ( - component_x: number, component_y: number, mode: string - ): CanvasToWebGLShader { - - const key: string = `d${component_x}${component_y}${mode}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const mediumpLength: number = mode === "color" ? 3 : 2; - - const shader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceDisplacementMapFilter.TEMPLATE( - mediumpLength, component_x, component_y, mode - ) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} width - * @param {number} height - * @param {boolean} is_horizontal - * @param {number} fraction - * @param {number} samples - * @return {void} - * @method - * @public - */ - setBlurFilterUniform ( - uniform: WebGLShaderUniform, - width: number, height: number, - is_horizontal: boolean, fraction: number, samples: number - ): void { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_offset - if (is_horizontal) { - mediump[0] = 1 / width; - mediump[1] = 0; - } else { - mediump[0] = 0; - mediump[1] = 1 / height; - } - - // fragment: u_fraction - mediump[2] = fraction; - - // fragment: u_samples - mediump[3] = samples; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} width - * @param {number} height - * @param {number} base_width - * @param {number} base_height - * @param {number} base_offset_x - * @param {number} base_offset_y - * @param {number} blur_width - * @param {number} blur_height - * @param {number} blur_offset_x - * @param {number} blur_offset_y - * @param {boolean} is_glow - * @param {number} strength - * @param {number} color_r1 - * @param {number} color_g1 - * @param {number} color_b1 - * @param {number} color_a1 - * @param {number} color_r2 - * @param {number} color_g2 - * @param {number} color_b2 - * @param {number} color_a2 - * @param {boolean} transforms_base - * @param {boolean} transforms_blur - * @param {boolean} applies_strength - * @param {boolean} is_gradient - * @method - * @public - */ - setBitmapFilterUniform ( - uniform: WebGLShaderUniform, - width: number, height: number, - base_width: number, base_height: number, - base_offset_x: number, base_offset_y: number, - blur_width: number, blur_height: number, - blur_offset_x: number, blur_offset_y: number, - is_glow: boolean, strength: number, - color_r1: number, color_g1: number, - color_b1: number, color_a1: number, - color_r2: number, color_g2: number, - color_b2: number, color_a2: number, - transforms_base: boolean, transforms_blur: boolean, - applies_strength: boolean, is_gradient: boolean - ): void { - - let textures: Int32Array|Float32Array; - - // fragment: u_textures - if (transforms_base) { - - textures = uniform.textures; - textures[0] = 0; - textures[1] = 1; - if (is_gradient) { - textures[2] = 2; - } - - } else if (is_gradient) { - - textures = uniform.textures; - textures[0] = 0; - textures[1] = 2; - - } - - const mediump: Int32Array|Float32Array = uniform.mediump; - - let i: number = 0; - - if (transforms_base) { - // fragment: u_uv_scale - mediump[i] = width / base_width; - mediump[i + 1] = height / base_height; - // fragment: u_uv_offset - mediump[i + 2] = base_offset_x / base_width; - mediump[i + 3] = (height - base_height - base_offset_y) / base_height; - i += 4; - } - - if (transforms_blur) { - // fragment: u_st_scale - mediump[i] = width / blur_width; - mediump[i + 1] = height / blur_height; - // fragment: u_st_offset - mediump[i + 2] = blur_offset_x / blur_width; - mediump[i + 3] = (height - blur_height - blur_offset_y) / blur_height; - i += 4; - } - - if (is_gradient) { - // do nothing - } else if (is_glow) { - // fragment: u_color - mediump[i] = color_r1; - mediump[i + 1] = color_g1; - mediump[i + 2] = color_b1; - mediump[i + 3] = color_a1; - i += 4; - } else { - // fragment: u_highlight_color - mediump[i] = color_r1; - mediump[i + 1] = color_g1; - mediump[i + 2] = color_b1; - mediump[i + 3] = color_a1; - // fragment: u_shadow_color - mediump[i + 4] = color_r2; - mediump[i + 5] = color_g2; - mediump[i + 6] = color_b2; - mediump[i + 7] = color_a2; - i += 8; - } - - if (applies_strength) { - // fragment: u_strength - mediump[i] = strength; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {array} matrix - * @return {void} - * @method - * @public - */ - setColorMatrixFilterUniform ( - uniform: WebGLShaderUniform, - matrix: number[] - ): void { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_mul - mediump[0] = matrix[0]; - mediump[1] = matrix[1]; - mediump[2] = matrix[2]; - mediump[3] = matrix[3]; - - mediump[4] = matrix[5]; - mediump[5] = matrix[6]; - mediump[6] = matrix[7]; - mediump[7] = matrix[8]; - - mediump[8] = matrix[10]; - mediump[9] = matrix[11]; - mediump[10] = matrix[12]; - mediump[11] = matrix[13]; - - mediump[12] = matrix[15]; - mediump[13] = matrix[16]; - mediump[14] = matrix[17]; - mediump[15] = matrix[18]; - - // fragment: u_add - mediump[16] = matrix[4] / 255; - mediump[17] = matrix[9] / 255; - mediump[18] = matrix[14] / 255; - mediump[19] = matrix[19] / 255; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} width - * @param {number} height - * @param {Float32Array} matrix - * @param {number} divisor - * @param {number} bias - * @param {boolean} clamp - * @param {number} color_r - * @param {number} color_g - * @param {number} color_b - * @param {number} color_a - * @return {void} - * @method - * @public - */ - setConvolutionFilterUniform ( - uniform: WebGLShaderUniform, - width: number, height: number, - matrix: number[], divisor: number, - bias: number, clamp: boolean, - color_r: number, color_g: number, - color_b: number, color_a: number - ): void { - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_rcp_size - mediump[0] = 1 / width; - mediump[1] = 1 / height; - - // fragment: u_rcp_divisor - mediump[2] = 1 / divisor; - - // fragment: u_bias - mediump[3] = bias / 255; - - let i: number = 4; - - if (!clamp) { - // fragment: u_substitute_color - mediump[i] = color_r; - mediump[i + 1] = color_g; - mediump[i + 2] = color_b; - mediump[i + 3] = color_a; - i += 4; - } - - // fragment: u_matrix - const length = matrix.length; - for (let j: number = 0; j < length; j++) { - mediump[i++] = matrix[j]; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} map_width - * @param {number} map_height - * @param {number} base_width - * @param {number} base_height - * @param {number} point_x - * @param {number} point_y - * @param {number} scale_x - * @param {number} scale_y - * @param {string} mode - * @param {number} color_r - * @param {number} color_g - * @param {number} color_b - * @param {number} color_a - * @return {void} - * @method - * @public - */ - setDisplacementMapFilterUniform ( - uniform: WebGLShaderUniform, - map_width: number, map_height: number, - base_width: number, base_height: number, - point_x: number, point_y: number, - scale_x: number, scale_y: number, - mode: string, - color_r: number, color_g: number, - color_b: number, color_a: number - ): void { - - const textures: Int32Array|Float32Array = uniform.textures; - textures[0] = 0; - textures[1] = 1; - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_uv_to_st_scale - mediump[0] = base_width / map_width; - mediump[1] = base_height / map_height; - // fragment: u_uv_to_st_offset - mediump[2] = point_x / map_width; - mediump[3] = (base_height - map_height - point_y) / map_height; - - // fragment: u_scale - mediump[4] = scale_x / base_width; - mediump[5] = -scale_y / base_height; - - if (mode === "color") { - // fragment: u_substitute_color - mediump[8] = color_r; - mediump[9] = color_g; - mediump[10] = color_b; - mediump[11] = color_a; - } - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/variants/GradientLUTShaderVariantCollection.ts b/packages/webgl/src/shader/variants/GradientLUTShaderVariantCollection.ts deleted file mode 100644 index b5d07923..00000000 --- a/packages/webgl/src/shader/variants/GradientLUTShaderVariantCollection.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { VertexShaderSource } from "../vertex/VertexShaderSource"; -import { FragmentShaderSourceGradientLUT } from "../fragment/FragmentShaderSourceGradientLUT"; -import { CanvasToWebGLShader } from "../CanvasToWebGLShader"; -import type { CanvasToWebGLContext } from "../../CanvasToWebGLContext"; -import type { WebGLShaderUniform } from "../WebGLShaderUniform"; -import { - $Math, - $getMap -} from "@next2d/share"; - -/** - * @class - */ -export class GradientLUTShaderVariantCollection -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$collection: Map; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {Map} - * @private - */ - this._$collection = $getMap(); - } - - /** - * @param {number} stops_length - * @param {boolean} is_linear_space - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getGradientLUTShader ( - stops_length: number, - is_linear_space: boolean - ): CanvasToWebGLShader { - - const key1: string = ("00" + stops_length).slice(-3); - const key2: string = is_linear_space ? "y" : "n"; - const key: string = `l${key1}${key2}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const mediumpLength: number = $Math.ceil(stops_length * 5 / 4); - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - VertexShaderSource.TEXTURE(), - FragmentShaderSourceGradientLUT.TEMPLATE(mediumpLength, stops_length, is_linear_space) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {array} stops - * @param {number} begin - * @param {number} end - * @param {Float32Array} table - * @return {void} - * @method - * @public - */ - setGradientLUTUniformForShape ( - uniform: WebGLShaderUniform, - stops: any[], - begin: number, end: number, - table: Float32Array - ): void { - - let i: number = 0; - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_gradient_color - for (let j: number = begin; j < end; j++) { - - const color: number[] = stops[j][1]; - mediump[i++] = table[color[0]]; - mediump[i++] = table[color[1]]; - mediump[i++] = table[color[2]]; - mediump[i++] = table[color[3]]; - } - - // fragment: u_gradient_t - for (let j: number = begin; j < end; j++) { - mediump[i++] = stops[j][0]; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {array} ratios - * @param {array} colors - * @param {array} alphas - * @param {number} begin - * @param {number} end - * @return {void} - * @method - * @public - */ - setGradientLUTUniformForFilter ( - uniform: WebGLShaderUniform, - ratios: number[], colors: number[], alphas: number[], - begin: number, end: number - ): void { - - let i: number = 0; - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_gradient_color - for (let j: number = begin; j < end; j++) { - - const color: number = colors[j]; - - mediump[i++] = (color >> 16) / 255; - mediump[i++] = (color >> 8 & 0xFF) / 255; - mediump[i++] = (color & 0xFF) / 255; - mediump[i++] = alphas[j]; - } - - // fragment: u_gradient_t - for (let j: number = begin; j < end; j++) { - mediump[i++] = ratios[j]; - } - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/variants/GradientShapeShaderVariantCollection.ts b/packages/webgl/src/shader/variants/GradientShapeShaderVariantCollection.ts deleted file mode 100644 index 0e9da6d6..00000000 --- a/packages/webgl/src/shader/variants/GradientShapeShaderVariantCollection.ts +++ /dev/null @@ -1,272 +0,0 @@ -import { CanvasToWebGLShader } from "../CanvasToWebGLShader"; -import { VertexShaderSourceStroke } from "../vertex/VertexShaderSourceStroke"; -import { VertexShaderSourceFill } from "../vertex/VertexShaderSourceFill"; -import { FragmentShaderSourceGradient } from "../fragment/FragmentShaderSourceGradient"; -import type { CanvasToWebGLContext } from "../../CanvasToWebGLContext"; -import type { WebGLShaderUniform } from "../WebGLShaderUniform"; -import type { CanvasToWebGLContextGrid } from "../../CanvasToWebGLContextGrid"; -import { $getMap } from "@next2d/share"; - -/** - * @class - */ -export class GradientShapeShaderVariantCollection -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$collection: Map; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {Map} - * @private - */ - this._$collection = $getMap(); - } - - /** - * @param {boolean} is_stroke - * @param {boolean} has_grid - * @param {boolean} is_radial - * @param {boolean} has_focal_point - * @param {string} spread_method - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getGradientShapeShader ( - is_stroke: boolean, has_grid: boolean, - is_radial: boolean, has_focal_point: boolean, - spread_method: string - ): CanvasToWebGLShader { - - const key: string = this.createCollectionKey( - is_stroke, has_grid, is_radial, has_focal_point, spread_method - ); - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const highpLength: number = (has_grid ? 13 : 5) + (is_stroke ? 1 : 0) + 1; - const fragmentIndex: number = highpLength - 1; - - let vertexShaderSource: string; - if (is_stroke) { - vertexShaderSource = VertexShaderSourceStroke.TEMPLATE( - highpLength, fragmentIndex, - true, has_grid - ); - } else { - vertexShaderSource = VertexShaderSourceFill.TEMPLATE( - highpLength, true, false, has_grid - ); - } - - const shader = new CanvasToWebGLShader( - this._$gl, this._$context, - vertexShaderSource, - FragmentShaderSourceGradient.TEMPLATE( - highpLength, fragmentIndex, - is_radial, has_focal_point, spread_method - ) - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {boolean} is_stroke - * @param {boolean} has_grid - * @param {boolean} is_radial - * @param {boolean} has_focal_point - * @param {string} spread_method - * @return {string} - * @method - * @private - */ - createCollectionKey ( - is_stroke: boolean, has_grid: boolean, - is_radial: boolean, has_focal_point: boolean, - spread_method: string - ): string { - - const key1: string = is_stroke ? "y" : "n"; - const key2: string = has_grid ? "y" : "n"; - const key3: string = is_radial ? "y" : "n"; - const key4: string = is_radial && has_focal_point ? "y" : "n"; - - let key5: number = 0; - switch (spread_method) { - - case "reflect": - key5 = 1; - break; - - case "repeat": - key5 = 2; - break; - - } - - return `${key1}${key2}${key3}${key4}${key5}`; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {boolean} is_stroke - * @param {number} half_width - * @param {number} face - * @param {number} miter_limit - * @param {boolean} has_grid - * @param {Float32Array} matrix - * @param {Float32Array} inverse_matrix - * @param {number} viewport_width - * @param {number} viewport_height - * @param {CanvasToWebGLContextGrid} grid - * @param {boolean} is_radial - * @param {Float32Array} points - * @param {number} focal_point_ratio - * @return {void} - * @method - * @public - */ - setGradientShapeUniform ( - uniform: WebGLShaderUniform, - is_stroke: boolean, half_width: number, face: number, miter_limit: number, - has_grid: boolean, matrix: Float32Array, inverse_matrix: Float32Array, - viewport_width: number, viewport_height: number, - grid: CanvasToWebGLContextGrid, - is_radial: boolean, points: Float32Array, - focal_point_ratio: number - ): void { - - const highp: Float32Array | Int32Array = uniform.highp; - - // vertex: u_matrix - highp[0] = matrix[0]; - highp[1] = matrix[1]; - highp[2] = matrix[2]; - - highp[4] = matrix[3]; - highp[5] = matrix[4]; - highp[6] = matrix[5]; - - highp[8] = matrix[6]; - highp[9] = matrix[7]; - highp[10] = matrix[8]; - - // vertex: u_inverse_matrix - highp[12] = inverse_matrix[0]; - highp[13] = inverse_matrix[1]; - highp[14] = inverse_matrix[2]; - - highp[16] = inverse_matrix[3]; - highp[17] = inverse_matrix[4]; - highp[18] = inverse_matrix[5]; - - highp[11] = inverse_matrix[6]; - highp[15] = inverse_matrix[7]; - highp[19] = inverse_matrix[8]; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - - let i: number = 20; - if (has_grid) { - // vertex: u_parent_matrix - highp[i] = grid.parentMatrixA; - highp[i + 1] = grid.parentMatrixB; - highp[i + 2] = grid.parentMatrixC; - - highp[i + 4] = grid.parentMatrixD; - highp[i + 5] = grid.parentMatrixE; - highp[i + 6] = grid.parentMatrixF; - - highp[i + 8] = grid.parentMatrixG; - highp[i + 9] = grid.parentMatrixH; - highp[i + 10] = grid.parentMatrixI; - - // vertex: u_ancestor_matrix - highp[i + 12] = grid.ancestorMatrixA; - highp[i + 13] = grid.ancestorMatrixB; - highp[i + 14] = grid.ancestorMatrixC; - - highp[i + 16] = grid.ancestorMatrixD; - highp[i + 17] = grid.ancestorMatrixE; - highp[i + 18] = grid.ancestorMatrixF; - - highp[i + 20] = grid.ancestorMatrixG; - highp[i + 21] = grid.ancestorMatrixH; - highp[i + 22] = grid.ancestorMatrixI; - - // vertex: u_parent_viewport - highp[i + 11] = grid.parentViewportX; - highp[i + 15] = grid.parentViewportY; - highp[i + 19] = grid.parentViewportW; - highp[i + 23] = grid.parentViewportH; - - // vertex: u_grid_min - highp[i + 24] = grid.minXST; - highp[i + 25] = grid.minYST; - highp[i + 26] = grid.minXPQ; - highp[i + 27] = grid.minYPQ; - // vertex: u_grid_max - highp[i + 28] = grid.maxXST; - highp[i + 29] = grid.maxYST; - highp[i + 30] = grid.maxXPQ; - highp[i + 31] = grid.maxYPQ; - - i = 52; - } - - if (is_stroke) { - // vertex: u_half_width - highp[i] = half_width; - // vertex: u_face - highp[i + 1] = face; - // vertex: u_miter_limit - highp[i + 2] = miter_limit; - - i += 4; - } - - if (is_radial) { - // fragment: u_radial_point - highp[i] = points[5]; - // fragment: u_focal_point_ratio - highp[i + 1] = focal_point_ratio; - } else { - // fragment: u_linear_points - highp[i] = points[0]; - highp[i + 1] = points[1]; - highp[i + 2] = points[2]; - highp[i + 3] = points[3]; - } - } -} diff --git a/packages/webgl/src/shader/variants/ShapeShaderVariantCollection.ts b/packages/webgl/src/shader/variants/ShapeShaderVariantCollection.ts deleted file mode 100644 index ea8a5722..00000000 --- a/packages/webgl/src/shader/variants/ShapeShaderVariantCollection.ts +++ /dev/null @@ -1,586 +0,0 @@ -import { CanvasToWebGLShader } from "../CanvasToWebGLShader"; -import { VertexShaderSourceStroke } from "../vertex/VertexShaderSourceStroke"; -import { VertexShaderSourceFill } from "../vertex/VertexShaderSourceFill"; -import { FragmentShaderSource } from "../fragment/FragmentShaderSource"; -import type { WebGLShaderUniform } from "../WebGLShaderUniform"; -import type { CanvasToWebGLContext } from "../../CanvasToWebGLContext"; -import type { CanvasToWebGLContextGrid } from "../../CanvasToWebGLContextGrid"; -import { $getMap } from "@next2d/share"; - -/** - * @class - */ -export class ShapeShaderVariantCollection -{ - private readonly _$context: CanvasToWebGLContext; - private readonly _$gl: WebGL2RenderingContext; - private readonly _$collection: Map; - - /** - * @param {CanvasToWebGLContext} context - * @param {WebGL2RenderingContext} gl - * @constructor - * @public - */ - constructor (context: CanvasToWebGLContext, gl: WebGL2RenderingContext) - { - /** - * @type {CanvasToWebGLContext} - * @private - */ - this._$context = context; - - /** - * @type {WebGL2RenderingContext} - * @private - */ - this._$gl = gl; - - /** - * @type {Map} - * @private - */ - this._$collection = $getMap(); - } - - /** - * @param {boolean} is_stroke - * @param {boolean} has_grid - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getSolidColorShapeShader ( - is_stroke: boolean, - has_grid: boolean - ): CanvasToWebGLShader { - - const key: string = `s${is_stroke ? "y" : "n"}${has_grid ? "y" : "n"}`; - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const highpLength: number = (has_grid ? 8 : 3) + (is_stroke ? 1 : 0); - const fragmentIndex: number = highpLength; - - let vertexShaderSource: string; - if (is_stroke) { - vertexShaderSource = VertexShaderSourceStroke.TEMPLATE( - highpLength, fragmentIndex, - false, has_grid - ); - } else { - vertexShaderSource = VertexShaderSourceFill.TEMPLATE( - highpLength, false, false, has_grid - ); - } - - const shader = new CanvasToWebGLShader( - this._$gl, this._$context, - vertexShaderSource, - FragmentShaderSource.SOLID_COLOR() - ); - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {boolean} is_stroke - * @param {boolean} repeat - * @param {boolean} has_grid - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getBitmapShapeShader ( - is_stroke: boolean, - repeat: boolean, - has_grid: boolean - ): CanvasToWebGLShader { - - const key: string = `b${is_stroke ? "y" : "n"}${repeat ? "y" : "n"}${has_grid ? "y" : "n"}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const highpLength: number = (has_grid ? 13 : 5) + (is_stroke ? 1 : 0); - const fragmentIndex: number = highpLength; - - let vertexShaderSource: string; - if (is_stroke) { - vertexShaderSource = VertexShaderSourceStroke.TEMPLATE( - highpLength, fragmentIndex, - true, has_grid - ); - } else { - vertexShaderSource = VertexShaderSourceFill.TEMPLATE( - highpLength, true, false, has_grid - ); - } - - const fragmentShaderSource = repeat - ? FragmentShaderSource.BITMAP_PATTERN() - : FragmentShaderSource.BITMAP_CLIPPED(); - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - vertexShaderSource, - fragmentShaderSource - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {boolean} is_stroke - * @param {boolean} has_grid - * @return {CanvasToWebGLShader} - * @method - * @public - */ - getMaskShapeShader ( - is_stroke: boolean, - has_grid: boolean - ): CanvasToWebGLShader { - - const key: string = `m${is_stroke ? "y" : "n"}${has_grid ? "y" : "n"}`; - - if (this._$collection.has(key)) { - const shader: CanvasToWebGLShader|void = this._$collection.get(key); - if (shader) { - return shader; - } - } - - const highpLength: number = (has_grid ? 8 : 3) + (is_stroke ? 1 : 0); - const fragmentIndex: number = highpLength; - - let vertexShaderSource: string; - if (is_stroke) { - vertexShaderSource = VertexShaderSourceStroke.TEMPLATE( - highpLength, fragmentIndex, - false, has_grid - ); - } else { - vertexShaderSource = VertexShaderSourceFill.TEMPLATE( - highpLength, false, true, has_grid - ); - } - - const shader: CanvasToWebGLShader = new CanvasToWebGLShader( - this._$gl, this._$context, - vertexShaderSource, - FragmentShaderSource.MASK() - ); - - this._$collection.set(key, shader); - - return shader; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {boolean} is_stroke - * @param {number} half_width - * @param {number} face - * @param {number} miter_limit - * @param {boolean} has_grid - * @param {array} matrix - * @param {number} viewport_width - * @param {number} viewport_height - * @param {CanvasToWebGLContextGrid} [grid = null] - * @param {array} color - * @param {number} alpha - * @method - * @public - */ - setSolidColorShapeUniform ( - uniform: WebGLShaderUniform, - is_stroke: boolean, half_width: number, - face: number, miter_limit: number, - has_grid: boolean, matrix: Float32Array, - viewport_width: number, viewport_height: number, - grid: CanvasToWebGLContextGrid, - color: Float32Array, alpha: number - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - let i: number; - - if (has_grid) { - // vertex: u_parent_matrix - highp[0] = grid.parentMatrixA; - highp[1] = grid.parentMatrixB; - highp[2] = grid.parentMatrixC; - - highp[4] = grid.parentMatrixD; - highp[5] = grid.parentMatrixE; - highp[6] = grid.parentMatrixF; - - highp[8] = grid.parentMatrixG; - highp[9] = grid.parentMatrixH; - highp[10] = grid.parentMatrixI; - - // vertex: u_ancestor_matrix - highp[12] = grid.ancestorMatrixA; - highp[13] = grid.ancestorMatrixB; - highp[14] = grid.ancestorMatrixC; - - highp[16] = grid.ancestorMatrixD; - highp[17] = grid.ancestorMatrixE; - highp[18] = grid.ancestorMatrixF; - - highp[20] = grid.ancestorMatrixG; - highp[21] = grid.ancestorMatrixH; - highp[22] = grid.ancestorMatrixI; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - - // vertex: u_parent_viewport - highp[11] = grid.parentViewportX; - highp[15] = grid.parentViewportY; - highp[19] = grid.parentViewportW; - highp[23] = grid.parentViewportH; - - // vertex: u_grid_min - highp[24] = grid.minXST; - highp[25] = grid.minYST; - highp[26] = grid.minXPQ; - highp[27] = grid.minYPQ; - // vertex: u_grid_max - highp[28] = grid.maxXST; - highp[29] = grid.maxYST; - highp[30] = grid.maxXPQ; - highp[31] = grid.maxYPQ; - - i = 32; - } else { - // vertex: u_matrix - highp[0] = matrix[0]; - highp[1] = matrix[1]; - highp[2] = matrix[2]; - - highp[4] = matrix[3]; - highp[5] = matrix[4]; - highp[6] = matrix[5]; - - highp[8] = matrix[6]; - highp[9] = matrix[7]; - highp[10] = matrix[8]; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - - i = 12; - } - - if (is_stroke) { - // vertex: u_half_width - highp[i] = half_width; - // vertex: u_face - highp[i + 1] = face; - // vertex: u_miter_limit - highp[i + 2] = miter_limit; - } - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_color - mediump[0] = color[0]; - mediump[1] = color[1]; - mediump[2] = color[2]; - mediump[3] = color[3] * alpha; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {boolean} is_stroke - * @param {number} half_width - * @param {number} face - * @param {number} miter_limit - * @param {boolean} has_grid - * @param {Float32Array} matrix - * @param {array} inverse_matrix - * @param {number} viewport_width - * @param {number} viewport_height - * @param {CanvasToWebGLContextGrid} grid - * @param {number} texture_width - * @param {number} texture_height - * @param {number} mul1 - * @param {number} mul2 - * @param {number} mul3 - * @param {number} mul4 - * @param {number} add1 - * @param {number} add2 - * @param {number} add3 - * @param {number} add4 - * @return {void} - * @method - * @public - */ - setBitmapShapeUniform ( - uniform: WebGLShaderUniform, - is_stroke: boolean, half_width: number, - face: number, miter_limit: number, - has_grid: boolean, - matrix: Float32Array, inverse_matrix: Float32Array, - viewport_width: number, viewport_height: number, - grid: CanvasToWebGLContextGrid, - texture_width: number, texture_height: number, - mul1: number, mul2: number, mul3: number, mul4: number, - add1: number, add2: number, add3: number, add4: number - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - let i: number; - - // vertex: u_matrix - highp[0] = matrix[0]; - highp[1] = matrix[1]; - highp[2] = matrix[2]; - - highp[4] = matrix[3]; - highp[5] = matrix[4]; - highp[6] = matrix[5]; - - highp[8] = matrix[6]; - highp[9] = matrix[7]; - highp[10] = matrix[8]; - - // vertex: u_inverse_matrix - highp[12] = inverse_matrix[0]; - highp[13] = inverse_matrix[1]; - highp[14] = inverse_matrix[2]; - - highp[16] = inverse_matrix[3]; - highp[17] = inverse_matrix[4]; - highp[18] = inverse_matrix[5]; - - highp[11] = inverse_matrix[6]; - highp[15] = inverse_matrix[7]; - highp[19] = inverse_matrix[8]; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - - i = 20; - - if (has_grid) { - // vertex: u_parent_matrix - highp[i] = grid.parentMatrixA; - highp[i + 1] = grid.parentMatrixB; - highp[i + 2] = grid.parentMatrixC; - - highp[i + 4] = grid.parentMatrixD; - highp[i + 5] = grid.parentMatrixE; - highp[i + 6] = grid.parentMatrixF; - - highp[i + 8] = grid.parentMatrixG; - highp[i + 9] = grid.parentMatrixH; - highp[i + 10] = grid.parentMatrixI; - - // vertex: u_ancestor_matrix - highp[i + 12] = grid.ancestorMatrixA; - highp[i + 13] = grid.ancestorMatrixB; - highp[i + 14] = grid.ancestorMatrixC; - - highp[i + 16] = grid.ancestorMatrixD; - highp[i + 17] = grid.ancestorMatrixE; - highp[i + 18] = grid.ancestorMatrixF; - - highp[i + 20] = grid.ancestorMatrixG; - highp[i + 21] = grid.ancestorMatrixH; - highp[i + 22] = grid.ancestorMatrixI; - - // vertex: u_parent_viewport - highp[i + 11] = grid.parentViewportX; - highp[i + 15] = grid.parentViewportY; - highp[i + 19] = grid.parentViewportW; - highp[i + 23] = grid.parentViewportH; - - // vertex: u_grid_min - highp[i + 24] = grid.minXST; - highp[i + 25] = grid.minYST; - highp[i + 26] = grid.minXPQ; - highp[i + 27] = grid.minYPQ; - // vertex: u_grid_max - highp[i + 28] = grid.maxXST; - highp[i + 29] = grid.maxYST; - highp[i + 30] = grid.maxXPQ; - highp[i + 31] = grid.maxYPQ; - - i = 52; - } - - if (is_stroke) { - // vertex: u_half_width - highp[i] = half_width; - // vertex: u_face - highp[i + 1] = face; - // vertex: u_miter_limit - highp[i + 2] = miter_limit; - } - - const mediump: Int32Array|Float32Array = uniform.mediump; - - // fragment: u_uv - mediump[0] = texture_width; - mediump[1] = texture_height; - - // fragment: u_color_transform_mul - mediump[4] = mul1; - mediump[5] = mul2; - mediump[6] = mul3; - mediump[7] = mul4; - // fragment: u_color_transform_add - mediump[8] = add1; - mediump[9] = add2; - mediump[10] = add3; - mediump[11] = add4; - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {boolean} has_grid - * @param {number} matrix_a - * @param {number} matrix_b - * @param {number} matrix_c - * @param {number} matrix_d - * @param {number} matrix_e - * @param {number} matrix_f - * @param {number} matrix_g - * @param {number} matrix_h - * @param {number} matrix_i - * @param {number} viewport_width - * @param {number} viewport_height - * @param {CanvasToWebGLContextGrid} grid - * @return {void} - * @method - * @public - */ - setMaskShapeUniform ( - uniform: WebGLShaderUniform, - has_grid: boolean, - matrix_a: number, matrix_b: number, matrix_c: number, - matrix_d: number, matrix_e: number, matrix_f: number, - matrix_g: number, matrix_h: number, matrix_i: number, - viewport_width: number, viewport_height: number, - grid: CanvasToWebGLContextGrid | null = null - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - - if (has_grid && grid) { - // vertex: u_parent_matrix - highp[0] = grid.parentMatrixA; - highp[1] = grid.parentMatrixB; - highp[2] = grid.parentMatrixC; - - highp[4] = grid.parentMatrixD; - highp[5] = grid.parentMatrixE; - highp[6] = grid.parentMatrixF; - - highp[8] = grid.parentMatrixG; - highp[9] = grid.parentMatrixH; - highp[10] = grid.parentMatrixI; - - // vertex: u_ancestor_matrix - highp[12] = grid.ancestorMatrixA; - highp[13] = grid.ancestorMatrixB; - highp[14] = grid.ancestorMatrixC; - - highp[16] = grid.ancestorMatrixD; - highp[17] = grid.ancestorMatrixE; - highp[18] = grid.ancestorMatrixF; - - highp[20] = grid.ancestorMatrixG; - highp[21] = grid.ancestorMatrixH; - highp[22] = grid.ancestorMatrixI; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - - // vertex: u_parent_viewport - highp[11] = grid.parentViewportX; - highp[15] = grid.parentViewportY; - highp[19] = grid.parentViewportW; - highp[23] = grid.parentViewportH; - - // vertex: u_grid_min - highp[24] = grid.minXST; - highp[25] = grid.minYST; - highp[26] = grid.minXPQ; - highp[27] = grid.minYPQ; - // vertex: u_grid_max - highp[28] = grid.maxXST; - highp[29] = grid.maxYST; - highp[30] = grid.maxXPQ; - highp[31] = grid.maxYPQ; - } else { - // vertex: u_matrix - highp[0] = matrix_a; - highp[1] = matrix_b; - highp[2] = matrix_c; - - highp[4] = matrix_d; - highp[5] = matrix_e; - highp[6] = matrix_f; - - highp[8] = matrix_g; - highp[9] = matrix_h; - highp[10] = matrix_i; - - // vertex: u_viewport - highp[3] = viewport_width; - highp[7] = viewport_height; - } - } - - /** - * @param {WebGLShaderUniform} uniform - * @param {number} width - * @param {number} height - * @return {void} - * @method - * @public - */ - setMaskShapeUniformIdentity ( - uniform: WebGLShaderUniform, - width: number, height: number - ): void { - - const highp: Int32Array|Float32Array = uniform.highp; - - // vertex: u_matrix - highp[0] = 1; - highp[1] = 0; - highp[2] = 0; - - highp[4] = 0; - highp[5] = 1; - highp[6] = 0; - - highp[8] = 0; - highp[9] = 0; - highp[10] = 1; - - // vertex: u_viewport - highp[3] = width; - highp[7] = height; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/vertex/VertexShaderLibrary.ts b/packages/webgl/src/shader/vertex/VertexShaderLibrary.ts deleted file mode 100644 index f1ae7f81..00000000 --- a/packages/webgl/src/shader/vertex/VertexShaderLibrary.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @class - */ -export class VertexShaderLibrary -{ - /** - * @return {string} - * @method - * @static - */ - static FUNCTION_GRID_OFF (): string - { - return ` - -vec2 applyMatrix(in vec2 vertex) { - mat3 matrix = mat3( - u_highp[0].xyz, - u_highp[1].xyz, - u_highp[2].xyz - ); - - vec2 position = (matrix * vec3(vertex, 1.0)).xy; - - return position; -} - -`; - } - - /** - * @param {number} index - * @return {string} - * @method - * @static - */ - static FUNCTION_GRID_ON (index: number): string - { - return ` - -vec2 applyMatrix(in vec2 vertex) { - mat3 parent_matrix = mat3( - u_highp[${index }].xyz, - u_highp[${index + 1}].xyz, - u_highp[${index + 2}].xyz - ); - mat3 ancestor_matrix = mat3( - u_highp[${index + 3}].xyz, - u_highp[${index + 4}].xyz, - u_highp[${index + 5}].xyz - ); - vec2 parent_offset = vec2(u_highp[${index + 2}].w, u_highp[${index + 3}].w); - vec2 parent_size = vec2(u_highp[${index + 4}].w, u_highp[${index + 5}].w); - vec4 grid_min = u_highp[${index + 6}]; - vec4 grid_max = u_highp[${index + 7}]; - - vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy; - position = (position - parent_offset) / parent_size; - - vec4 ga = grid_min; - vec4 gb = grid_max - grid_min; - vec4 gc = vec4(1.0) - grid_max; - - vec2 pa = position; - vec2 pb = position - grid_min.st; - vec2 pc = position - grid_max.st; - - position = (ga.pq / ga.st) * min(pa, ga.st) - + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st) - + (gc.pq / gc.st) * max(vec2(0.0), pc); - - position = position * parent_size + parent_offset; - position = (ancestor_matrix * vec3(position, 1.0)).xy; - - return position; -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/vertex/VertexShaderSource.ts b/packages/webgl/src/shader/vertex/VertexShaderSource.ts deleted file mode 100644 index 21ff65de..00000000 --- a/packages/webgl/src/shader/vertex/VertexShaderSource.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * @class - */ -export class VertexShaderSource -{ - /** - * @return {string} - * @method - * @static - */ - static TEXTURE (): string - { - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; - -out vec2 v_coord; - -void main() { - v_coord = a_vertex; - - vec2 position = a_vertex * 2.0 - 1.0; - gl_Position = vec4(position, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static BLEND (): string - { - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; - -uniform vec4 u_highp[4]; - -out vec2 v_coord; - -void main() { - v_coord = a_vertex; - - vec2 offset = u_highp[0].xy; - vec2 size = u_highp[0].zw; - mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz); - vec2 viewport = vec2(u_highp[1].w, u_highp[2].w); - - vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); - position = position * size + offset; - position = (matrix * vec3(position, 1.0)).xy; - position /= viewport; - - position = position * 2.0 - 1.0; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static INSTANCE_BLEND (): string - { - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; - -uniform vec4 u_highp[5]; - -out vec2 v_src_coord; -out vec2 v_dst_coord; - -void main() { - vec4 rect = vec4(u_highp[0].x, u_highp[0].y, u_highp[0].z, u_highp[0].w); - vec2 size = vec2(u_highp[4].x, u_highp[4].y); - mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz); - vec2 viewport = vec2(u_highp[1].w, u_highp[2].w); - - v_src_coord = a_vertex * rect.zw + rect.xy; - v_dst_coord = a_vertex; - - vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); - position = position * size; - position = (matrix * vec3(position, 1.0)).xy; - position /= viewport; - - position = position * 2.0 - 1.0; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static INSTANCE (): string - { - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; -layout (location = 1) in vec4 a_rect; -layout (location = 2) in vec4 a_size; -layout (location = 3) in vec2 a_offset; -layout (location = 4) in vec4 a_matrix; -layout (location = 5) in vec4 a_mul; -layout (location = 6) in vec4 a_add; - -out vec2 v_coord; -out vec4 mul; -out vec4 add; - -void main() { - v_coord = a_vertex * a_rect.zw + a_rect.xy; - mul = a_mul; - add = a_add; - - vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); - position = position * a_size.xy; - mat3 matrix = mat3(a_matrix.x, a_matrix.y, 0.0, a_matrix.z, a_matrix.w, 0.0, a_offset.x, a_offset.y, 1.0); - position = (matrix * vec3(position, 1.0)).xy; - position /= a_size.zw; - - position = position * 2.0 - 1.0; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static BLEND_CLIP (): string - { - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; - -uniform vec4 u_highp[4]; - -out vec2 v_coord; - -void main() { - v_coord = a_vertex; - - vec2 offset = u_highp[0].xy; - vec2 size = u_highp[0].zw; - mat3 inv_matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz); - vec2 viewport = vec2(u_highp[1].w, u_highp[2].w); - - vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y); - position *= viewport; - position = (inv_matrix * vec3(position, 1.0)).xy; - position = (position - offset) / size; - - position = position * 2.0 - 1.0; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/vertex/VertexShaderSourceFill.ts b/packages/webgl/src/shader/vertex/VertexShaderSourceFill.ts deleted file mode 100644 index c0d8fea4..00000000 --- a/packages/webgl/src/shader/vertex/VertexShaderSourceFill.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { VertexShaderLibrary } from "./VertexShaderLibrary"; - -/** - * @class - */ -export class VertexShaderSourceFill -{ - /** - * @param {number} highp_length - * @param {boolean} with_uv - * @param {boolean} for_mask - * @param {boolean} has_grid - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - highp_length: number, with_uv: boolean, - for_mask: boolean, has_grid: boolean - ): string { - - const bezierAttribute: string = for_mask - ? this.ATTRIBUTE_BEZIER_ON() - : ""; - - const uvVarying: string = for_mask - ? this.VARYING_BEZIER_ON() - : with_uv - ? this.VARYING_UV_ON() - : ""; - - const uvStatement: string = for_mask - ? this.STATEMENT_BEZIER_ON() - : with_uv - ? this.STATEMENT_UV_ON() - : ""; - - const gridFunction: string = has_grid - ? VertexShaderLibrary.FUNCTION_GRID_ON(with_uv ? 5 : 0) - : VertexShaderLibrary.FUNCTION_GRID_OFF(); - - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; -${bezierAttribute} - -uniform vec4 u_highp[${highp_length}]; - -${uvVarying} - -${gridFunction} - -void main() { - vec2 viewport = vec2(u_highp[0].w, u_highp[1].w); - - ${uvStatement} - - vec2 pos = applyMatrix(a_vertex) / viewport; - pos = pos * 2.0 - 1.0; - gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static ATTRIBUTE_BEZIER_ON (): string - { - return ` -layout (location = 1) in vec2 a_bezier; -`; - } - - /** - * @return {string} - * @method - * @static - */ - static VARYING_UV_ON (): string - { - return ` -out vec2 v_uv; -`; - } - - /** - * @return {string} - * @method - * @static - */ - static VARYING_BEZIER_ON (): string - { - return ` -out vec2 v_bezier; -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_UV_ON (): string - { - return ` - mat3 uv_matrix = mat3( - u_highp[0].xyz, - u_highp[1].xyz, - u_highp[2].xyz - ); - mat3 inverse_matrix = mat3( - u_highp[3].xyz, - u_highp[4].xyz, - vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w) - ); - - v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy; -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_BEZIER_ON (): string - { - return ` - v_bezier = a_bezier; -`; - } -} \ No newline at end of file diff --git a/packages/webgl/src/shader/vertex/VertexShaderSourceStroke.ts b/packages/webgl/src/shader/vertex/VertexShaderSourceStroke.ts deleted file mode 100644 index 2f6ad18e..00000000 --- a/packages/webgl/src/shader/vertex/VertexShaderSourceStroke.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { VertexShaderLibrary } from "./VertexShaderLibrary"; - -/** - * @class - */ -export class VertexShaderSourceStroke -{ - /** - * @param {number} highp_length - * @param {number} fragment_index - * @param {boolean} with_uv - * @param {boolean} has_grid - * @return {string} - * @method - * @static - */ - static TEMPLATE ( - highp_length: number, fragment_index: number, - with_uv: boolean, has_grid: boolean - ): string { - - const strokeIndex: number = fragment_index - 1; - - const uvVarying: string = with_uv - ? this.VARYING_UV_ON() - : ""; - - const uvStatement: string = with_uv - ? this.STATEMENT_UV_ON() - : ""; - - const gridFunction: string = has_grid - ? VertexShaderLibrary.FUNCTION_GRID_ON(with_uv ? 5 : 0) - : VertexShaderLibrary.FUNCTION_GRID_OFF(); - - return `#version 300 es - -layout (location = 0) in vec2 a_vertex; -layout (location = 1) in vec2 a_option1; -layout (location = 2) in vec2 a_option2; -layout (location = 3) in float a_type; - -uniform vec4 u_highp[${highp_length}]; - -${uvVarying} - -${gridFunction} - -float crossVec2(in vec2 v1, in vec2 v2) { - return v1.x * v2.y - v2.x * v1.y; -} - -vec2 perpendicularVec2(in vec2 v1) { - float face = u_highp[${strokeIndex}][1]; - - return face * vec2(v1.y, -v1.x); -} - -vec2 calculateNormal(in vec2 direction) { - vec2 normalized = normalize(direction); - return perpendicularVec2(normalized); -} - -vec2 calculateIntersection(in vec2 v1, in vec2 v2, in vec2 o1, in vec2 o2) { - float t = crossVec2(o2 - o1, v2) / crossVec2(v1, v2); - return (o1 + t * v1); -} - -vec2 calculateAnchor(in vec2 position, in float convex, out vec2 v1, out vec2 v2, out vec2 o1, out vec2 o2) { - float miter_limit = u_highp[${strokeIndex}][2]; - - vec2 a = applyMatrix(a_option1); - vec2 b = applyMatrix(a_option2); - - v1 = convex * (position - a); - v2 = convex * (b - position); - o1 = calculateNormal(v1) + a; - o2 = calculateNormal(v2) + position; - - vec2 anchor = calculateIntersection(v1, v2, o1, o2) - position; - return normalize(anchor) * min(length(anchor), miter_limit); -} - -void main() { - vec2 viewport = vec2(u_highp[0].w, u_highp[1].w); - float half_width = u_highp[${strokeIndex}][0]; - - vec2 position = applyMatrix(a_vertex); - vec2 offset = vec2(0.0); - vec2 v1, v2, o1, o2; - - if (a_type == 1.0 || a_type == 2.0) { // 線分 - offset = calculateNormal(a_option2 * (applyMatrix(a_option1) - position)); - } else if (a_type == 10.0) { // スクエア線端 - offset = normalize(position - applyMatrix(a_option1)); - offset += a_option2 * perpendicularVec2(offset); - } else if (a_type == 21.0) { // マイター結合(線分Bの凸側) - offset = calculateAnchor(position, 1.0, v1, v2, o1, o2); - offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position; - } else if (a_type == 22.0) { // マイター結合(線分Aの凸側) - offset = calculateAnchor(position, 1.0, v1, v2, o1, o2); - offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position; - } else if (a_type == 23.0) { // マイター結合(線分Aの凹側) - offset = calculateAnchor(position, -1.0, v1, v2, o1, o2); - offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position; - } else if (a_type == 24.0) { // マイター結合(線分Bの凹側) - offset = calculateAnchor(position, -1.0, v1, v2, o1, o2); - offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position; - } else if (a_type >= 30.0) { // ラウンド結合 - float face = u_highp[${strokeIndex}][1]; - float rad = face * (a_type - 30.0) * 0.3488888889; /* 0.3488888889 = PI / 9.0 */ - offset = mat2(cos(rad), sin(rad), -sin(rad), cos(rad)) * vec2(1.0, 0.0); - } - - offset *= half_width; - position += offset; - ${uvStatement} - - position /= viewport; - position = position * 2.0 - 1.0; - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - -`; - } - - /** - * @return {string} - * @method - * @static - */ - static VARYING_UV_ON (): string - { - return ` -out vec2 v_uv; -`; - } - - /** - * @return {string} - * @method - * @static - */ - static STATEMENT_UV_ON (): string - { - return ` - mat3 uv_matrix = mat3( - u_highp[0].xyz, - u_highp[1].xyz, - u_highp[2].xyz - ); - mat3 inverse_matrix = mat3( - u_highp[3].xyz, - u_highp[4].xyz, - vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w) - ); - - v_uv = (uv_matrix * vec3(a_vertex, 1.0)).xy; - v_uv += offset; - v_uv = (inverse_matrix * vec3(v_uv, 1.0)).xy; -`; - } -} \ No newline at end of file diff --git a/packages/webpack-worker-loader-plugin/index.js b/packages/webpack-worker-loader-plugin/index.js deleted file mode 100644 index 0474155a..00000000 --- a/packages/webpack-worker-loader-plugin/index.js +++ /dev/null @@ -1,152 +0,0 @@ -"use strict"; - -const fs = require("fs"); - -module.exports = class Next2DWebpackWorkerLoaderPlugin -{ - /** - * @param {Compiler} compiler - * @returns {void} - * @method - * @public - */ - apply (compiler) - { - compiler.hooks.beforeCompile.tapAsync("Next2DWebpackWorkerLoaderPlugin", (compilation, callback) => - { - if (compiler.options.mode === "production") { - this._$margeVersion(compilation.normalModuleFactory.context); - this._$margeUnzipWorker(compilation.normalModuleFactory.context); - this._$margeRenderWorker(compilation.normalModuleFactory.context); - } - callback(); - }); - } - - /** - * @param {string} dir - * @return {void} - * @method - * @private - */ - _$margeVersion (dir) - { - const indexPath = `${dir}/src/index.ts`; - if (fs.existsSync(indexPath)) { - - const src = fs.readFileSync(indexPath, "utf8"); - const packageJson = require(`${dir}/package.json`); - - const texts = src.split("\n"); - for (let idx = 0; idx < texts.length; ++idx) { - - const text = texts[idx]; - if (text.indexOf("Next2D Player") === -1) { - continue; - } - - const top = texts.slice(0, idx).join("\n"); - const lower = texts.slice(idx + 1).join("\n"); - - fs.writeFileSync( - indexPath, - `${top} - console.log("%c Next2D Player %c ${packageJson.version} %c https://next2d.app", -${lower}` - ); - - break; - } - } - } - - /** - * @param {string} dir - * @return {void} - * @method - * @private - */ - _$margeRenderWorker (dir) - { - const RenderWorkerPath = `${dir}/worker/renderer/RendererWorker.min.js`; - - if (fs.existsSync(RenderWorkerPath)) { - - const worker = fs.readFileSync(RenderWorkerPath, "utf8") - .replace(/\\/g, "\\\\") - .replace(/"/g, "\\\"") - .replace(/\n/g, ""); - - const utilPath = `${dir}/packages/util/src/Util.ts`; - - const util = fs.readFileSync(utilPath, "utf8"); - const index = util.indexOf("const $renderURL"); - const top = util.slice(0, index - 1); - - let lower = ""; - const texts = util.split("\n"); - for (let idx = 0; idx < texts.length; ++idx) { - - const text = texts[idx]; - if (text.indexOf("const $renderURL") === -1) { - continue; - } - - lower = texts.slice(idx + 1).join("\n"); - break; - } - - fs.writeFileSync( - utilPath, - `${top} -const $renderURL: string = "${worker}"; -${lower}` - ); - } - } - - /** - * - * @param {string} dir - * @return {void} - * @method - * @private - */ - _$margeUnzipWorker (dir) - { - const UnzipWorkerPath = `${dir}/worker/unzip/UnzipWorker.min.js`; - if (fs.existsSync(UnzipWorkerPath)) { - - const worker = fs.readFileSync(UnzipWorkerPath, "utf8") - .replace(/\\/g, "\\\\") - .replace(/"/g, "\\\"") - .replace(/\n/g, ""); - - const utilPath = `${dir}/packages/util/src/Util.ts`; - - const util = fs.readFileSync(utilPath, "utf8"); - const index = util.indexOf("const $unzipURL"); - const top = util.slice(0, index - 1); - - let lower = ""; - const texts = util.split("\n"); - for (let idx = 0; idx < texts.length; ++idx) { - - const text = texts[idx]; - if (text.indexOf("const $unzipURL") === -1) { - continue; - } - - lower = texts.slice(idx + 1).join("\n"); - break; - } - - fs.writeFileSync( - utilPath, - `${top} -const $unzipURL: string = URL.createObjectURL(new Blob(["${worker}"], { "type": "text/javascript" })); -${lower}` - ); - } - } -}; diff --git a/packages/webpack-worker-loader-plugin/package.json b/packages/webpack-worker-loader-plugin/package.json deleted file mode 100644 index 2887ba8e..00000000 --- a/packages/webpack-worker-loader-plugin/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "@next2d/webpack-worker-loader-plugin", - "description": "Next2D Framework webpack Worker Loader plugin.", - "author": "Toshiyuki Ienaga (https://github.com/ienaga/)", - "license": "MIT", - "main": "index.js", - "homepage": "https://next2d.app", - "bugs": "https://github.com/Next2D/Player/issues", - "exports": { - ".": { - "import": { - "default": "./index.js" - }, - "require": { - "default": "./index.js" - } - } - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Next2D/Player.git" - } -} diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index b04e9b3b..00000000 --- a/scripts/build.js +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env node - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); - -const execute = () => -{ - const dirPath = `${process.cwd()}/packages/`; - - const files = fs.readdirSync(dirPath); - - const dirList = files.filter((file) => - { - return fs.statSync(path.join(dirPath, file)).isDirectory(); - }); - - for (let idx = 0; idx < dirList.length; ++idx) { - - const dirName = dirList[idx]; - - const targetPath = path.join(process.cwd(), `dist/packages/${dirName}/src`); - const packagePath = path.join(dirPath, dirName); - - const outDir = path.join(packagePath, "dist"); - if (fs.existsSync(outDir)) { - cp.spawnSync(`rm -rf ${outDir}`, { "shell": true }); - } - - cp.spawnSync( - `mv ${targetPath} ${outDir}`, - { "shell": true } - ); - } - - const distPath = `${process.cwd()}/dist`; - if (fs.existsSync(distPath)) { - cp.spawnSync(`rm -rf ${distPath}`, { "shell": true }); - } -}; - -execute(); \ No newline at end of file diff --git a/scripts/clean.js b/scripts/clean.js index 9a93a03b..2df5cf78 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -2,36 +2,19 @@ "use strict"; -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); +import { existsSync } from "fs"; +import { spawnSync } from "child_process"; const execute = () => { const distPath = `${process.cwd()}/dist`; - if (fs.existsSync(distPath)) { - cp.spawnSync(`rm -rf ${distPath}`, { "shell": true }); + if (existsSync(distPath)) { + spawnSync(`rm -rf ${distPath}`, { "shell": true }); } - const dirPath = `${process.cwd()}/packages/`; - - const files = fs.readdirSync(dirPath); - - const dirList = files.filter((file) => - { - return fs.statSync(path.join(dirPath, file)).isDirectory(); - }); - - for (let idx = 0; idx < dirList.length; ++idx) { - - const dirName = dirList[idx]; - - const packagePath = path.join(dirPath, dirName); - - const outDir = path.join(packagePath, "dist"); - if (fs.existsSync(outDir)) { - cp.spawnSync(`rm -rf ${outDir}`, { "shell": true }); - } + const buildPath = `${process.cwd()}/build`; + if (existsSync(buildPath)) { + spawnSync(`rm -rf ${buildPath}`, { "shell": true }); } }; diff --git a/scripts/create.js b/scripts/create.js deleted file mode 100644 index db02fafb..00000000 --- a/scripts/create.js +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env node - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); - -const execute = () => -{ - - const configPath = `${process.cwd()}/config/tsconfig.json`; - const dirPath = `${process.cwd()}/src/`; - - const files = fs.readdirSync(dirPath); - - const dirList = files.filter((file) => - { - return fs.statSync(path.join(dirPath, file)).isDirectory(); - }); - - for (let idx = 0; idx < dirList.length; ++idx) { - - const dirName = dirList[idx]; - - const targetDirPath = path.join(dirPath, dirName); - - const nodeModulesPath = path.join(targetDirPath, "node_modules"); - if (fs.existsSync(nodeModulesPath)) { - fs.rmdirSync(nodeModulesPath, { "recursive": true }); - } - - const stream = cp.spawn("npm", [ - "--prefix", - targetDirPath, - "install", - targetDirPath - ]); - - // eslint-disable-next-line no-loop-func - stream.on("exit", () => - { - const targetConfigPath = path.join(targetDirPath, "tsconfig.json"); - - console.log("copy tsconfig.json: ", targetConfigPath); - fs.cpSync(configPath, targetConfigPath); - - console.log("start tsc"); - const stream = cp.spawn("npx", [ - "tsc", - "--project", - targetConfigPath - ]); - - // eslint-disable-next-line no-loop-func - stream.on("exit", () => - { - console.log("end tsc"); - fs.unlinkSync(targetConfigPath); - }); - - fs.unlinkSync(targetConfigPath); - }); - } -}; - -execute(); \ No newline at end of file diff --git a/scripts/publish.js b/scripts/publish.js index 9b982d76..db407573 100644 --- a/scripts/publish.js +++ b/scripts/publish.js @@ -2,23 +2,23 @@ "use strict"; -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); +import { readdirSync, statSync, readFileSync, writeFileSync } from "fs"; +import { join } from "path"; +import { spawnSync } from "child_process"; const execute = () => { const dirPath = `${process.cwd()}/dist/packages/`; - const files = fs.readdirSync(dirPath); + const files = readdirSync(dirPath); const dirList = files.filter((file) => { - return fs.statSync(path.join(dirPath, file)).isDirectory(); + return statSync(join(dirPath, file)).isDirectory(); }); const basePackageJson = JSON.parse( - fs.readFileSync(`${process.cwd()}/package.json`, { "encoding": "utf8" }) + readFileSync(`${process.cwd()}/package.json`, { "encoding": "utf8" }) ); for (let idx = 0; idx < dirList.length; ++idx) { @@ -27,48 +27,41 @@ const execute = () => basePackageJson.dependencies[`@next2d/${dirName}`] = basePackageJson.version; - const targetPath = path.join(process.cwd(), `dist/packages/${dirName}/src`); - const packagePath = path.join(process.cwd(), `packages/${dirName}`); - - const outDir = path.join(process.cwd(), `dist/packages/${dirName}`); - if (fs.existsSync(`${outDir}/dist`)) { - cp.spawnSync(`rm -rf ${outDir}`, { "shell": true }); - } - - cp.spawnSync( - `mv ${targetPath} ${outDir}/dist`, - { "shell": true } - ); + const outDir = join(process.cwd(), `dist/packages/${dirName}`); + const packagePath = join(process.cwd(), `packages/${dirName}`); // LICENSE - cp.spawnSync( + spawnSync( `cp -r ${packagePath}/LICENSE ${outDir}/LICENSE`, { "shell": true } ); // README - cp.spawnSync( + spawnSync( `cp -r ${packagePath}/README.md ${outDir}/README.md`, { "shell": true } ); // package.json const packageJson = JSON.parse( - fs.readFileSync(`${packagePath}/package.json`, { "encoding": "utf8" }) + readFileSync(`${packagePath}/package.json`, { "encoding": "utf8" }) ); // write version packageJson.version = basePackageJson.version; if (packageJson.peerDependencies) { + packageJson.dependencies = {}; const keys = Object.keys(packageJson.peerDependencies); for (let idx = 0; idx < keys.length; ++idx) { - packageJson.peerDependencies[keys[idx]] = basePackageJson.version; + packageJson.dependencies[keys[idx]] = basePackageJson.version; } + + delete packageJson.peerDependencies; } // write package.json - fs.writeFileSync( + writeFileSync( `${outDir}/package.json`, JSON.stringify(packageJson, null, 2) ); @@ -79,28 +72,42 @@ const execute = () => } // write package.json - fs.writeFileSync( - path.join(process.cwd(), "dist/src/package.json"), + writeFileSync( + join(process.cwd(), "dist/src/package.json"), JSON.stringify(basePackageJson, null, 2) ); // minify - cp.spawnSync( - `cp -r ${process.cwd()}/next2d.js ${process.cwd()}/dist/src/dist/next2d.min.js`, + spawnSync( + `cp -r ${process.cwd()}/build/next2d.js ${process.cwd()}/dist/src/next2d.js`, { "shell": true } ); // LICENSE - cp.spawnSync( + spawnSync( `cp -r ${process.cwd()}/LICENSE ${process.cwd()}/dist/src/LICENSE`, { "shell": true } ); // README - cp.spawnSync( + spawnSync( `cp -r ${process.cwd()}/README.md ${process.cwd()}/dist/src/README.md`, { "shell": true } ); + + // move + spawnSync( + `mkdir ${process.cwd()}/dist/src/src`, + { "shell": true } + ); + spawnSync( + `mv ${process.cwd()}/dist/src/index.js ${process.cwd()}/dist/src/src/index.js`, + { "shell": true } + ); + spawnSync( + `mv ${process.cwd()}/dist/src/index.d.ts ${process.cwd()}/dist/src/src/index.d.ts`, + { "shell": true } + ); }; execute(); \ No newline at end of file diff --git a/scripts/version.js b/scripts/version.js new file mode 100644 index 00000000..2ede5e82 --- /dev/null +++ b/scripts/version.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +"use strict"; + +import * as fs from "fs"; + +/** + * @return {void} + * @method + * @private + */ +const execute = () => +{ + const indexPath = `${process.cwd()}/src/index.ts`; + if (fs.existsSync(indexPath)) { + + const src = fs.readFileSync(indexPath, "utf8"); + const packageJson = JSON.parse( + fs.readFileSync(`${process.cwd()}/package.json`, { "encoding": "utf8" }) + ); + + const texts = src.split("\n"); + for (let idx = 0; idx < texts.length; ++idx) { + + const text = texts[idx]; + if (text.indexOf("Next2D Player") === -1) { + continue; + } + + const top = texts.slice(0, idx).join("\n"); + const lower = texts.slice(idx + 1).join("\n"); + + fs.writeFileSync( + indexPath, + `${top} + console.log("%c Next2D Player %c ${packageJson.version} %c https://next2d.app", +${lower}` + ); + + break; + } + } +}; + +execute(); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 77df967c..e019452a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,49 +1,12 @@ "use strict"; -import { - $currentPlayer, - $initialize -} from "@next2d/util"; import { Next2D } from "@next2d/core"; if (!("next2d" in window)) { - - console.log("%c Next2D Player %c 1.18.12 %c https://next2d.app", + console.log("%c Next2D Player %c 2.0.0 %c https://next2d.app", "color: #fff; background: #5f5f5f", "color: #fff; background: #4bc729", ""); - window.next2d = new Next2D([new Promise((resolve) => - { - if (document.readyState === "loading") { - - const initialize = (): void => - { - window.removeEventListener("DOMContentLoaded", initialize); - - $initialize() - .then((): void => - { - $currentPlayer() - ._$initializeCanvas(); - - resolve(); - }); - }; - - window.addEventListener("DOMContentLoaded", initialize); - - } else { - - $initialize() - .then((): void => - { - $currentPlayer() - ._$initializeCanvas(); - - resolve(); - }); - - } - })]); + (window as any).next2d = new Next2D(); } \ No newline at end of file diff --git a/test.setup.ts b/test.setup.ts new file mode 100644 index 00000000..fddf8bac --- /dev/null +++ b/test.setup.ts @@ -0,0 +1,23 @@ +// test/global-setup.ts +class MockOffscreenCanvas { + width: number; + height: number; + + constructor (width: number, height: number) + { + this.width = width; + this.height = height; + } + + getContext () { + // CanvasRenderingContext2D などをモック + return { + // 必要に応じてメソッドを追加 + "fillRect": (x: number, y: number, w: number, h: number) => {} + }; + } +} + +if (typeof globalThis.OffscreenCanvas === "undefined") { + (globalThis as any).OffscreenCanvas = MockOffscreenCanvas; +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index 142f4c2f..00000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "src/**/*.ts", - "packages/**/*.ts", - "worker/**/*.ts", - ".eslintrc.js" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9bc4c58c..9a9d388d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,36 +1,49 @@ { "compilerOptions": { - "strict": true, + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", "resolveJsonModule": true, "strictFunctionTypes": false, "esModuleInterop": true, - "skipLibCheck": true, "declaration": true, - "target": "es6", - "module": "es6", - "lib": [ - "es6", - "dom" - ], - "moduleResolution": "node", + "isolatedModules": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", "outDir": "./dist", "paths": { "@next2d/*": [ "packages/*/src" ] - } + }, + + "types": [ + "vitest/globals" + ] }, "include": [ "src/index.ts", + "packages/**/*.ts", "@types/**/*.ts" ], "exclude": [ "node_modules", "**/dist/**", - "worker", + "**/build/**", "scripts", - "__tests__", - "dist" + "dist", + "**/*.test.ts", + "**/.github", ] } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..0e076287 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,33 @@ +/// +/// +/// + +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + "server": { + "open": "index.html" + }, + "build": { + "outDir": "build", + "target": "esnext", + "modulePreload": { + "polyfill": false + }, + "rollupOptions": { //ファイル出力設定 + "output": { + "entryFileNames": "next2d.js" + } + } + }, + "test": { + "globals": true, + "environment": "jsdom", + "setupFiles": [ + "test.setup.ts", + "@vitest/web-worker", + "vitest-webgl-canvas-mock" + ], + "include": ["packages/**/*.test.ts"] + } +}); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 1b96f37f..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,131 +0,0 @@ -const path = require("path"); -const ESLintPlugin = require("eslint-webpack-plugin"); -const WebpackWorkerLoaderPlugin = require("@next2d/webpack-worker-loader-plugin"); - -const unzip_worker = { - "mode": "production", - "entry": path.resolve(__dirname, "worker/unzip/src/index.ts"), - "output": { - "filename": "UnzipWorker.min.js", - "path": path.resolve(__dirname, "worker/unzip") - }, - "cache": { - "type": "filesystem", - "buildDependencies": { - "config": [__filename] - } - }, - "plugins": [ - new ESLintPlugin({ - "extensions": [".ts"], - "exclude": "node_modules" - }) - ], - "module": { - "rules": [ - { - "test": /\.ts$/, - "loader": "ts-loader", - "options": { - "configFile": path.resolve(__dirname, "worker/unzip/tsconfig.json") - } - } - ] - }, - "performance": { - "hints": false - } -}; - -const render_worker = { - "mode": "production", - "entry": path.resolve(__dirname, "worker/renderer/src/index.ts"), - "output": { - "filename": "RendererWorker.min.js", - "path": path.resolve(__dirname, "worker/renderer") - }, - "cache": { - "type": "filesystem", - "buildDependencies": { - "config": [__filename] - } - }, - "plugins": [ - new ESLintPlugin({ - "extensions": [".ts"], - "exclude": "node_modules" - }) - ], - "resolve": { - "alias": { - "@": path.resolve(__dirname, "worker/renderer") - }, - "extensions": [".ts", ".js"] - }, - "module": { - "rules": [ - { - "test": /\.ts$/, - "loader": "ts-loader", - "options": { - "configFile": path.resolve(__dirname, "worker/renderer/tsconfig.json") - } - } - ] - }, - "performance": { - "hints": false - } -}; - -const player = { - "mode": "development", - "entry": path.resolve(__dirname, "src/index.ts"), - "output": { - "filename": "next2d.js", - "path": __dirname - }, - "cache": { - "type": "filesystem", - "buildDependencies": { - "config": [__filename] - } - }, - "plugins": [ - new ESLintPlugin({ - "extensions": [".ts"], - "exclude": "node_modules" - }), - new WebpackWorkerLoaderPlugin() - ], - "resolve": { - "alias": { - "@": path.resolve(__dirname, "src") - }, - "extensions": [".ts", ".js"] - }, - "module": { - "rules": [ - { - "test": /\.ts$/, - "loader": "ts-loader", - "options": { - "configFile": path.resolve(__dirname, "src/tsconfig.json") - } - } - ] - }, - "devServer": { - "static": [ - { "directory": __dirname } - ], - "historyApiFallback": true, - "compress": false, - "open": true - }, - "performance": { - "hints": false - } -}; - -module.exports = [unzip_worker, render_worker, player]; \ No newline at end of file diff --git a/worker/renderer/RendererWorker.min.js b/worker/renderer/RendererWorker.min.js deleted file mode 100644 index 4c818e63..00000000 --- a/worker/renderer/RendererWorker.min.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";let t=1,e=0,i=!1;const s=1/0,r=Math,n=Array,a=Map,h=Number,o=Float32Array,_=Int32Array,l=Int16Array,c=OffscreenCanvas,$=isNaN,u=requestAnimationFrame,d=setTimeout,g=clearTimeout,f=new o([1,0,0,1,0,0]),m=new o([1,1,1,1,0,0,0,0]),p=r.PI/180,x=(r.PI,[]),b=[],v=[],T=[],A=[],M=[],y=[],E=[],C=[],S=new c(1,1).getContext("2d"),F=(t=0,e=0,i=0,s=0)=>{const r=C.pop()||{xMin:0,xMax:0,yMin:0,yMax:0};return r.xMin=t,r.xMax=e,r.yMin=i,r.yMax=s,r},B=t=>{C.push(t)},w=(t=0,e=0,i=0,s=0)=>{const r=v.pop()||new o(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},R=t=>{v.push(t)},I=(t=0,e=0,i=0,s=0)=>{const r=b.pop()||new _(4);return r[0]=t,r[1]=e,r[2]=i,r[3]=s,r},P=(t=0,e=0,i=0,s=0,r=0,n=0)=>{const a=T.pop()||new o(6);return a[0]=t,a[1]=e,a[2]=i,a[3]=s,a[4]=r,a[5]=n,a},N=t=>{T.push(t)},k=(t=1,e=1,i=1,s=1,r=0,n=0,a=0,h=0)=>{const _=A.pop()||new o(8);return _[0]=t,_[1]=e,_[2]=i,_[3]=s,_[4]=r,_[5]=n,_[6]=a,_[7]=h,_},L=t=>{A.push(t)},O=(t=0,e=0,i=0,s=0,r=0,n=0,a=0,h=0,_=0)=>{const l=M.pop()||new o(9);return l[0]=t,l[1]=e,l[2]=i,l[3]=s,l[4]=r,l[5]=n,l[6]=a,l[7]=h,l[8]=_,l},U=(...t)=>{const e=y.pop()||[];return t.length&&e.push(...t),e},D=(t=null)=>{t&&(t.length&&(t.length=0),y.push(t))},X=t=>{t.size&&t.clear(),E.push(t)},V=()=>E.pop()||new a,Y=t=>(t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t),z=t=>{const e=1/(t[0]*t[4]-t[3]*t[1]),i=t[3]*t[7]-t[4]*t[6],s=t[1]*t[6]-t[0]*t[7];return O(t[4]*e,0-t[1]*e,0,0-t[3]*e,t[0]*e,0,i*e,s*e,1)},G=(t,e,i,s=null)=>{const n=+t;return $(n)&&null!==s?s:r.min(r.max(e,$(n)?0:n),i)},H=(t,e)=>P(t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],t[0]*e[4]+t[2]*e[5]+t[4],t[1]*e[4]+t[3]*e[5]+t[5]),W=(t,e)=>k(t[0]*e[0],t[1]*e[1],t[2]*e[2],t[3]*e[3],t[0]*e[4]+t[4],t[1]*e[5]+t[5],t[2]*e[6]+t[6],t[3]*e[7]+t[7]),q=(t,e)=>{const i=t.xMax*e[0]+t.yMax*e[2]+e[4],s=t.xMax*e[0]+t.yMin*e[2]+e[4],n=t.xMin*e[0]+t.yMax*e[2]+e[4],a=t.xMin*e[0]+t.yMin*e[2]+e[4],o=t.xMax*e[1]+t.yMax*e[3]+e[5],_=t.xMax*e[1]+t.yMin*e[3]+e[5],l=t.xMin*e[1]+t.yMax*e[3]+e[5],c=t.xMin*e[1]+t.yMin*e[3]+e[5],$=r.min(h.MAX_VALUE,i,s,n,a),u=r.max(0-h.MAX_VALUE,i,s,n,a),d=r.min(h.MAX_VALUE,o,_,l,c),g=r.max(0-h.MAX_VALUE,o,_,l,c);return F($,u,d,g)},j=t=>$(+t)?(t=>{if(!S)return 0;S.fillStyle=t;const e=+`0x${S.fillStyle.slice(1)}`;return S.fillStyle="rgba(0, 0, 0, 1)",e})(`${t}`):+t,K=(t,e,i)=>(t>>16)*(i?e:1)/255,Q=(t,e,i)=>(t>>8&255)*(i?e:1)/255,J=(t,e,i)=>(255&t)*(i?e:1)/255,Z=(t,e=1)=>({R:(16711680&t)>>16,G:(65280&t)>>8,B:255&t,A:255*e}),tt=(t,e,i=!1,s=!1)=>{let r="";return i&&(r="italic "),s&&(r+="bold "),`${r}${e}px '${t}','sans-serif'`},et=t=>{t.color&&L(t.color),t.isLayer=!1,t.isUpdated=null,t.canApply=null,t.matrix=null,t.color=null,t.filters=null,t.blendMode="normal",t.sw=0,t.sh=0,x.push(t)},it=new Map([[1,"normal"],[2,"layer"],[3,"multiply"],[4,"screen"],[5,"lighten"],[6,"darken"],[7,"difference"],[8,"add"],[9,"subtract"],[10,"invert"],[11,"alpha"],[12,"erase"],[13,"overlay"],[14,"hardlight"]]),st=t=>it.has(t)&&it.get(t)||"normal",rt=new class{constructor(){this._$pool=[],this._$store=new Map,this._$timerMap=new Map,this._$context=null}set context(t){this._$context=t}reset(){for(const t of this._$store.values()){for(const e of t.values())this.destroy(e);X(t)}this._$store.clear(),this._$context&&this._$context.frameBuffer.clearCache()}destroy(t=null){if(t&&"object"==typeof t)if(t instanceof WebGLTexture)u((()=>{this._$context&&this._$context.frameBuffer.releaseTexture(t)}));else{if("canvas"in t&&t instanceof CanvasRenderingContext2D){const e=t.canvas,i=e.width,s=e.height;t.clearRect(0,0,i+1,s+1),e.width=e.height=1,this._$pool.push(e)}this._$context&&"index"in t&&this._$context.frameBuffer.textureManager.releasePosition(t)}}getCanvas(){return this._$pool.pop()||document.createElement("canvas")}remove(t,e){if(!this._$store.has(t))return;const i=this._$store.get(t);i.has(e)&&(i.delete(e),i.size||(X(i),this._$store.delete(t)))}stopTimer(t){t=`${t}`,this._$timerMap.has(t)&&(g(this._$timerMap.get(t)),this._$timerMap.delete(t))}removeCache(t){if(t=`${t}`,this._$store.has(t)){const e=this._$store.get(t);for(const t of e.values())this.destroy(t);e.clear(),X(e),this._$store.delete(t)}this._$timerMap.delete(t)}setRemoveTimer(t){if(t=`${t}`,this.stopTimer(t),this._$store.has(t)){const e=d((()=>{this.removeCache(t)}),5e3);this._$timerMap.set(t,e)}}get(t){const e=`${t[0]}`,i=`${t[1]}`;if(this._$store.has(e)){this.stopTimer(e);const t=this._$store.get(e);if(t.has(i))return t.get(i)}return null}set(t,e=null){const i=`${t[0]}`,s=`${t[1]}`;this._$store.has(i)||this._$store.set(i,V());const r=this._$store.get(i);if(null===e){if(!r.has(s))return;return this.destroy(r.get(s)),r.delete(s),void(r.size||(X(r),this._$store.delete(i)))}r.set(s,e)}has(t){const e=`${t[0]}`;return!!this._$store.has(e)&&this._$store.get(e).has(`${t[1]}`)}generateKeys(t,e=null,i=null){let s="";e&&e.length&&(s+=`${e[0]}_${e[1]}`),i&&i.length&&(s+=0===i[7]?"":`_${i[7]}`);const r=U();if(s){let t=0;const e=s.length;for(let i=0;i{i=t})()}}class at extends nt{constructor(t=4,e=4,i=1){super(),this._$blurX=4,this._$blurY=4,this._$quality=1,this.blurX=t,this.blurY=e,this.quality=i}static toString(){return"[class BlurFilter]"}static get namespace(){return"next2d.filters.BlurFilter"}toString(){return"[object BlurFilter]"}get namespace(){return"next2d.filters.BlurFilter"}static get STEP(){return[.5,1.05,1.4,1.55,1.75,1.9,2,2.15,2.2,2.3,2.5,3,3,3.5,3.5]}get blurX(){return this._$blurX}set blurX(t){(t=G(+t,0,255,0))!==this._$blurX&&(this._$blurX=t,this._$doChanged())}get blurY(){return this._$blurY}set blurY(t){(t=G(+t,0,255,0))!==this._$blurY&&(this._$blurY=t,this._$doChanged())}get quality(){return this._$quality}set quality(t){(t=G(0|t,0,15,1))!==this._$quality&&(this._$quality=t,this._$doChanged())}clone(){return new at(this._$blurX,this._$blurY,this._$quality)}_$toArray(){return U(1,this._$blurX,this._$blurY,this._$quality)}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$quality)return s;const n=at.STEP[this._$quality-1];let a=0>=this._$blurX?1:this._$blurX*n,h=0>=this._$blurY?1:this._$blurY*n;return e?a*=e:a=r.round(a),i?h*=i:h=r.round(h),s.xMin-=a,s.xMax+=2*a,s.yMin-=h,s.yMax+=2*h,s}_$canApply(){return 0!==this._$blurX&&0!==this._$blurY}_$applyFilter(e,i,s=!0){this._$updated=!1;const n=e.frameBuffer,a=n.currentAttachment,h=n.getTextureFromCurrentAttachment();if(!this._$canApply())return s?h:n.createTextureFromCurrentAttachment();let o=r.sqrt(i[0]*i[0]+i[1]*i[1]),_=r.sqrt(i[2]*i[2]+i[3]*i[3]);o/=t,_/=t,o*=2,_*=2;const l=F(0,h.width,0,h.height),c=this._$generateFilterRect(l,o,_);B(l);const $=0|r.ceil(c.xMax),u=0|r.ceil(c.yMax),d=r.ceil(r.abs(c.xMin)+.5*r.abs($-c.xMax)),g=r.ceil(r.abs(c.yMin)+.5*r.abs(u-c.yMax));e._$offsetX=d+e._$offsetX,e._$offsetY=g+e._$offsetY;const f=this._$blurX*o,m=this._$blurY*_;let p=1,x=1;f>128?p=.0625:f>64?p=.125:f>32?p=.25:f>16&&(p=.5),m>128?x=.0625:m>64?x=.125:m>32?x=.25:m>16&&(x=.5);const b=f*p,v=m*x,T=r.ceil($*p),A=r.ceil(u*x),M=n.createTextureAttachment(T,A),y=[M,n.createTextureAttachment(T,A)];let E=0;e._$bind(M),e.reset(),e.setTransform(p,0,0,x,0,0),e.drawImage(h,d,g,h.width,h.height),e.blend.toOneZero();let C=n.getTextureFromCurrentAttachment();for(let t=0;t0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!0,b),C=n.getTextureFromCurrentAttachment()}if(this._$blurY>0){E=(E+1)%2;const t=y[E];e._$bind(t),e._$applyBlurFilter(C,!1,v),C=n.getTextureFromCurrentAttachment()}}if(e.blend.reset(),1!==p||1!==x){const t=n.createTextureAttachment($,u);e._$bind(t),e.reset(),e.imageSmoothingEnabled=!0,e.setTransform(1/p,0,0,1/x,0,0),e.drawImage(C,0,0,T,A),C=n.getTextureFromCurrentAttachment(),e.reset(),e.setTransform(1,0,0,1,0,0),n.releaseAttachment(y[0],!0),n.releaseAttachment(y[1],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(t,!1)}else n.releaseAttachment(y[(E+1)%2],!0),s?n.releaseAttachment(a,!0):n.releaseAttachment(y[E],!1);return C}}class ht extends nt{constructor(t=4,e=45,i=16777215,s=1,r=0,n=1,a=4,h=4,o=1,_=1,l="inner",c=!1){super(),this._$blurFilter=new at(a,h,_),this._$distance=4,this._$angle=45,this._$highlightColor=16777215,this._$highlightAlpha=1,this._$shadowColor=0,this._$shadowAlpha=1,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.highlightColor=i,this.highlightAlpha=s,this.shadowColor=r,this.shadowAlpha=n,this.strength=o,this.type=l,this.knockout=c}static toString(){return"[class BevelFilter]"}static get namespace(){return"next2d.filters.BevelFilter"}toString(){return"[object BevelFilter]"}get namespace(){return"next2d.filters.BevelFilter"}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get highlightAlpha(){return this._$highlightAlpha}set highlightAlpha(t){(t=G(+t,0,1,0))!==this._$highlightAlpha&&(this._$highlightAlpha=t,this._$doChanged())}get highlightColor(){return this._$highlightColor}set highlightColor(t){(t=G(j(t),0,16777215,16777215))!==this._$highlightColor&&(this._$highlightColor=t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get shadowAlpha(){return this._$shadowAlpha}set shadowAlpha(t){(t=G(+t,0,1,0))!==this._$shadowAlpha&&(this._$shadowAlpha=t,this._$doChanged())}get shadowColor(){return this._$shadowColor}set shadowColor(t){(t=G(j(t),0,16777215,0))!==this._$shadowColor&&(this._$shadowColor=t,this._$doChanged())}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}get type(){return this._$type}set type(t){(t=`${t}`)!==this._$type&&(this._$type=t,this._$doChanged())}clone(){return new ht(this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$toArray(){return U(0,this._$distance,this._$angle,this._$highlightColor,this._$highlightAlpha,this._$shadowColor,this._$shadowAlpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$type,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.abs(r.cos(n)*this._$distance),h=r.abs(r.sin(n)*this._$distance);return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&0!==this._$distance&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply())return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=this._$angle*p,d=r.cos(u)*this._$distance*c,g=r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation="erase",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A="inner"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),s.releaseAttachment(f,!0),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,null,null,null,K(this._$highlightColor,this._$highlightAlpha,!0),Q(this._$highlightColor,this._$highlightAlpha,!0),J(this._$highlightColor,this._$highlightAlpha,!0),this._$highlightAlpha,K(this._$shadowColor,this._$shadowAlpha,!0),Q(this._$shadowColor,this._$shadowAlpha,!0),J(this._$shadowColor,this._$shadowAlpha,!0),this._$shadowAlpha),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseTexture(m),s.getTextureFromCurrentAttachment()}}class ot extends nt{constructor(t=null){super(),this._$matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],this.matrix=t}static toString(){return"[class ColorMatrixFilter]"}static get namespace(){return"next2d.filters.ColorMatrixFilter"}toString(){return"[object ColorMatrixFilter]"}get namespace(){return"next2d.filters.ColorMatrixFilter"}get matrix(){return this._$matrix}set matrix(t){if(t&&n.isArray(t)&&20===t.length){for(let e=0;e<20;++e)if(t[e]!==this._$matrix[e]){this._$doChanged();break}this._$matrix=t}}clone(){return new ot(this._$matrix)}_$toArray(){return U(2,this._$matrix)}_$generateFilterRect(t){return t}_$canApply(){return!0}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment(),r=s.width,n=s.height,a=e.createTextureAttachment(r,n);return t._$bind(a),t.reset(),t._$applyColorMatrixFilter(s,this._$matrix),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()}}class _t extends nt{constructor(t=0,e=0,i=null,s=1,r=0,n=!0,a=!0,h=0,o=0){super(),this._$matrixX=0,this._$matrixY=0,this._$matrix=null,this._$divisor=1,this._$bias=0,this._$preserveAlpha=!0,this._$clamp=!0,this._$color=0,this._$alpha=0,this.matrixX=t,this.matrixY=e,this.matrix=i,this.divisor=s,this.bias=r,this.preserveAlpha=n,this.clamp=a,this.color=h,this.alpha=o}static toString(){return"[class ConvolutionFilter]"}static get namespace(){return"next2d.filters.ConvolutionFilter"}toString(){return"[object ConvolutionFilter]"}get namespace(){return"next2d.filters.ConvolutionFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get bias(){return this._$bias}set bias(t){t!==this._$bias&&(this._$bias=0|t,this._$doChanged())}get clamp(){return this._$clamp}set clamp(t){t!==this._$clamp&&(this._$clamp=!!t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get divisor(){return this._$divisor}set divisor(t){t!==this._$divisor&&(this._$divisor=0|t,this._$doChanged())}get matrix(){return this._$matrix}set matrix(t){n.isArray(this._$matrix)&&D(this._$matrix),this._$matrix=n.isArray(t)?t:null,this._$doChanged()}get matrixX(){return this._$matrixX}set matrixX(t){(t=0|G(0|t,0,15,0))!==this._$matrixX&&(this._$matrixX=t,this._$doChanged())}get matrixY(){return this._$matrixY}set matrixY(t){(t=0|G(0|t,0,15,0))!==this._$matrixY&&(this._$matrixY=t,this._$doChanged())}get preserveAlpha(){return this._$preserveAlpha}set preserveAlpha(t){t!==this._$preserveAlpha&&(this._$preserveAlpha=!!t,this._$doChanged())}clone(){return new _t(this._$matrixX,this._$matrixY,this._$matrix?this._$matrix.slice():null,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$toArray(){return U(3,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$matrix&&this._$matrixX*this._$matrixY===this._$matrix.length}_$applyFilter(t){this._$updated=!1;const e=t.frameBuffer,i=e.currentAttachment;t.setTransform(1,0,0,1,0,0);const s=e.getTextureFromCurrentAttachment();return this._$canApply()&&this._$matrix?(t._$applyConvolutionFilter(s,this._$matrixX,this._$matrixY,this._$matrix,this._$divisor,this._$bias,this._$preserveAlpha,this._$clamp,K(this._$color,this._$alpha,!1),Q(this._$color,this._$alpha,!1),J(this._$color,this._$alpha,!1),this._$alpha),e.releaseAttachment(i,!0),e.getTextureFromCurrentAttachment()):s}}class lt extends nt{constructor(t=null,e=null,i=0,s=0,r=0,n=0,a="wrap",h=0,o=0){super(),this._$mapBitmap=null,this._$mapPoint=null,this._$componentX=0,this._$componentY=0,this._$scaleX=0,this._$scaleY=0,this._$mode="wrap",this._$color=0,this._$alpha=0,this.mapBitmap=t,this.mapPoint=e,this.componentX=i,this.componentY=s,this.scaleX=r,this.scaleY=n,this.mode=a,this.color=h,this.alpha=o}static toString(){return"[class DisplacementMapFilter]"}static get namespace(){return"next2d.filters.DisplacementMapFilter"}toString(){return"[object DisplacementMapFilter]"}get namespace(){return"next2d.filters.DisplacementMapFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get componentX(){return this._$componentX}set componentX(t){t!==this._$componentX&&(this._$componentX=t,this._$doChanged())}get componentY(){return this._$componentY}set componentY(t){t!==this._$componentY&&(this._$componentY=t,this._$doChanged())}get mapBitmap(){return this._$mapBitmap}set mapBitmap(t){t!==this._$mapBitmap&&(this._$mapBitmap=t,this._$doChanged())}get mapPoint(){return this._$mapPoint}set mapPoint(t){t!==this._$mapPoint&&(this._$mapPoint=t,this._$doChanged())}get mode(){return this._$mode}set mode(t){t!==this._$mode&&(this._$mode=t,this._$doChanged())}get scaleX(){return this._$scaleX}set scaleX(t){(t=G(+t,-65535,65535,0))!==this._$scaleX&&(this._$scaleX=t,this._$doChanged())}get scaleY(){return this._$scaleY}set scaleY(t){(t=G(+t,-65535,65535,0))!==this._$scaleY&&(this._$scaleY=t,this._$doChanged())}clone(){return new lt(this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$toArray(){return U(4,this._$mapBitmap,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,this._$color,this._$alpha)}_$generateFilterRect(t){return t}_$canApply(){return null!==this._$mapBitmap&&this._$componentX>0&&this._$componentY>0&&0!==this._$scaleX&&0!==this._$scaleY}_$applyFilter(t,e){this._$updated=!1;const i=t.frameBuffer,s=i.currentAttachment;t.setTransform(1,0,0,1,0,0);const n=i.getTextureFromCurrentAttachment();if(!this._$canApply()||!s||!this._$mapBitmap)return n;const a=r.sqrt(e[0]*e[0]+e[1]*e[1]),h=r.sqrt(e[2]*e[2]+e[3]*e[3]);return t._$applyDisplacementMapFilter(n,this._$mapBitmap,n.width/a,n.height/h,this._$mapPoint,this._$componentX,this._$componentY,this._$scaleX,this._$scaleY,this._$mode,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha),i.releaseAttachment(s,!0),i.getTextureFromCurrentAttachment()}}class ct extends nt{constructor(t=4,e=45,i=0,s=1,r=4,n=4,a=1,h=1,o=!1,_=!1,l=!1){super(),this._$blurFilter=new at(r,n,h),this._$distance=4,this._$angle=45,this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this._$hideObject=!1,this.distance=t,this.angle=e,this.color=i,this.alpha=s,this.strength=a,this.inner=o,this.knockout=_,this.hideObject=l}static toString(){return"[class DropShadowFilter]"}static get namespace(){return"next2d.filters.DropShadowFilter"}toString(){return"[object DropShadowFilter]"}get namespace(){return"next2d.filters.DropShadowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get angle(){return this._$angle}set angle(t){(t%=360)!==this._$angle&&(this._$angle=G(t,-360,360,45),this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,0))!==this._$color&&(this._$color=t,this._$doChanged())}get distance(){return this._$distance}set distance(t){(t=G(+t,-255,255,4))!==this._$distance&&(this._$distance=t,this._$doChanged())}get hideObject(){return this._$hideObject}set hideObject(t){t!==this._$hideObject&&(this._$hideObject=!!t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new ct(this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$toArray(){return U(5,this._$distance,this._$angle,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout,this._$hideObject)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){let s=F(t.xMin,t.xMax,t.yMin,t.yMax);if(!this._$canApply())return s;s=this._$blurFilter._$generateFilterRect(s,e,i);const n=this._$angle*p;let a=r.cos(n)*this._$distance,h=r.sin(n)*this._$distance;return e&&(a*=e),i&&(h*=i),s.xMin=r.min(s.xMin,a),a>0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(e,i){const s=e.frameBuffer,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");if(e.setTransform(1,0,0,1,0,0),!this._$canApply())return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=this._$angle*p,v=r.cos(b)*this._$distance*m,T=r.sin(b)*this._$distance*x,A=this._$inner?a:c+r.max(0,r.abs(v)-g),M=this._$inner?h:$+r.max(0,r.abs(T)-f),y=r.ceil(A),E=r.ceil(M),C=(y-A)/2,S=(E-M)/2,F=this._$inner?0:r.max(0,g-v)+C,B=this._$inner?0:r.max(0,f-T)+S,w=this._$inner?v-u:(v>0?r.max(0,v-g):0)+C,R=this._$inner?T-d:(T>0?r.max(0,T-f):0)+S;let I,P;return this._$inner?(I="inner",P=this._$knockout||this._$hideObject):!this._$knockout&&this._$hideObject?(I="full",P=!0):(I="outer",P=this._$knockout),e._$bind(n),e._$applyBitmapFilter(l,y,E,a,h,F,B,c,$,w,R,!0,I,P,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),e._$offsetX=o+F,e._$offsetY=_+B,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class $t extends nt{constructor(t=0,e=1,i=4,s=4,r=1,n=1,a=!1,h=!1){super(),this._$blurFilter=new at(i,s,n),this._$color=0,this._$alpha=1,this._$strength=1,this._$inner=!1,this._$knockout=!1,this.color=t,this.alpha=e,this.strength=r,this.inner=a,this.knockout=h}static toString(){return"[class GlowFilter]"}static get namespace(){return"next2d.filters.GlowFilter"}toString(){return"[object GlowFilter]"}get namespace(){return"next2d.filters.GlowFilter"}get alpha(){return this._$alpha}set alpha(t){(t=G(+t,0,1,0))!==this._$alpha&&(this._$alpha=t,this._$doChanged())}get blurX(){return this._$blurFilter.blurX}set blurX(t){this._$blurFilter.blurX=t}get blurY(){return this._$blurFilter.blurY}set blurY(t){this._$blurFilter.blurY=t}get color(){return this._$color}set color(t){(t=G(j(t),0,16777215,4))!==this._$color&&(this._$color=t,this._$doChanged())}get inner(){return this._$inner}set inner(t){t!==this._$inner&&(this._$inner=!!t,this._$doChanged())}get knockout(){return this._$knockout}set knockout(t){t!==this._$knockout&&(this._$knockout=!!t,this._$doChanged())}get quality(){return this._$blurFilter.quality}set quality(t){this._$blurFilter.quality=t}get strength(){return this._$strength}set strength(t){(t=G(0|t,0,255,0))!==this._$strength&&(this._$strength=t,this._$doChanged())}clone(){return new $t(this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$toArray(){return U(6,this._$color,this._$alpha,this._$blurFilter.blurX,this._$blurFilter.blurY,this._$strength,this._$blurFilter.quality,this._$inner,this._$knockout)}_$isUpdated(){return this._$updated||this._$blurFilter._$isUpdated()}_$generateFilterRect(t,e=0,i=0){const s=F(t.xMin,t.xMax,t.yMin,t.yMax);return this._$canApply()?this._$blurFilter._$generateFilterRect(s,e,i):s}_$canApply(){return this._$alpha>0&&this._$strength>0&&this._$blurFilter._$canApply()}_$applyFilter(t,e){const i=t.frameBuffer,s=i.currentAttachment;if(!s)throw new Error("the current attachment is null.");if(this._$updated=!1,t.setTransform(1,0,0,1,0,0),!this._$canApply())return i.getTextureFromCurrentAttachment();const r=s.width,n=s.height,a=t._$offsetX,h=t._$offsetY,o=this._$blurFilter._$applyFilter(t,e,!1),_=o.width,l=o.height,c=t._$offsetX,$=t._$offsetY,u=this._$inner?r:_,d=this._$inner?n:l,g=this._$inner?0:c-a,f=this._$inner?0:$-h,m=this._$inner?-c:0,p=this._$inner?-$:0,x=this._$inner?"inner":"outer";return t._$bind(s),t._$applyBitmapFilter(o,u,d,r,n,g,f,_,l,m,p,!0,x,this._$knockout,this._$strength,null,null,null,K(this._$color,this._$alpha,!0),Q(this._$color,this._$alpha,!0),J(this._$color,this._$alpha,!0),this._$alpha,0,0,0,0),t._$offsetX=a+g,t._$offsetY=h+f,i.releaseTexture(o),i.getTextureFromCurrentAttachment()}}class ut extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_="inner",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return"[class GradientBevelFilter]"}static get namespace(){return"next2d.filters.GradientBevelFilter"}toString(){return"[object GradientBevelFilter]"}get namespace(){return"next2d.filters.GradientBevelFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;e.setTransform(1,0,0,1,0,0);const a=s.getTextureFromCurrentAttachment();if(!this._$canApply()||!n)return a;const h=n.width,o=n.height,_=e._$offsetX,l=e._$offsetY;let c=r.sqrt(i[0]*i[0]+i[1]*i[1]),$=r.sqrt(i[2]*i[2]+i[3]*i[3]);c/=t,$/=t,c*=2,$*=2;const u=+this._$angle*p,d=+r.cos(u)*this._$distance*c,g=+r.sin(u)*this._$distance*$,f=s.createTextureAttachment(h,o);e._$bind(f),e.reset(),e.drawImage(a,0,0,h,o),e.globalCompositeOperation="erase",e.drawImage(a,2*d,2*g,h,o);const m=this._$blurFilter._$applyFilter(e,i,!1),x=m.width,b=m.height,v=r.ceil(x+2*r.abs(d)),T=r.ceil(b+2*r.abs(g)),A="inner"===this._$type,M=A?h:v,y=A?o:T,E=r.abs(d),C=r.abs(g),S=(x-h)/2,F=(b-o)/2,B=A?0:E+S,w=A?0:C+F,R=A?-S-d:E-d,I=A?-F-g:C-g;return e._$bind(n),e._$applyBitmapFilter(m,M,y,h,o,B,w,x,b,R,I,!1,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=_+B,e._$offsetY=l+w,s.releaseAttachment(f,!0),s.getTextureFromCurrentAttachment()}}class dt extends nt{constructor(t=4,e=45,i=null,s=null,r=null,n=4,a=4,h=1,o=1,_="inner",l=!1){super(),this._$blurFilter=new at(n,a,o),this._$distance=4,this._$angle=45,this._$colors=null,this._$alphas=null,this._$ratios=null,this._$strength=1,this._$type="inner",this._$knockout=!1,this.distance=t,this.angle=e,this.colors=i,this.alphas=s,this.ratios=r,this.strength=h,this.type=_,this.knockout=l}static toString(){return"[class GradientGlowFilter]"}static get namespace(){return"next2d.filters.GradientGlowFilter"}toString(){return"[object GradientGlowFilter]"}get namespace(){return"next2d.filters.GradientGlowFilter"}get alphas(){return this._$alphas}set alphas(t){if(t!==this._$alphas){if(this._$alphas=t,n.isArray(t)){for(let e=0;e0&&(s.xMax+=a),s.yMin=r.min(s.yMin,h),h>0&&(s.yMax+=h),s}_$canApply(){return this._$strength>0&&this._$distance>0&&null!==this._$alphas&&null!==this._$ratios&&null!==this._$colors&&this._$blurFilter._$canApply()}_$applyFilter(e,i){this._$updated=!1;const s=e.frameBuffer,n=s.currentAttachment;if(e.setTransform(1,0,0,1,0,0),!this._$canApply()||!n)return s.getTextureFromCurrentAttachment();const a=n.width,h=n.height,o=e._$offsetX,_=e._$offsetY,l=this._$blurFilter._$applyFilter(e,i,!1),c=l.width,$=l.height,u=e._$offsetX,d=e._$offsetY,g=u-o,f=d-_;let m=r.sqrt(i[0]*i[0]+i[1]*i[1]),x=r.sqrt(i[2]*i[2]+i[3]*i[3]);m/=t,x/=t,m*=2,x*=2;const b=+this._$angle*p,v=+r.cos(b)*this._$distance*m,T=+r.sin(b)*this._$distance*x,A="inner"===this.type,M=A?a:c+r.max(0,r.abs(v)-g),y=A?h:$+r.max(0,r.abs(T)-f),E=r.ceil(M),C=r.ceil(y),S=(E-M)/2,F=(C-y)/2,B=A?0:r.max(0,g-v)+S,w=A?0:r.max(0,f-T)+F,R=A?v-u:(v>0?r.max(0,v-g):0)+S,I=A?T-d:(T>0?r.max(0,T-f):0)+F;return e._$bind(n),e._$applyBitmapFilter(l,E,C,a,h,B,w,c,$,R,I,!0,this._$type,this._$knockout,this._$strength,this._$ratios,this._$colors,this._$alphas,0,0,0,0,0,0,0,0),e._$offsetX=o+B,e._$offsetY=_+w,s.releaseTexture(l),s.getTextureFromCurrentAttachment()}}class gt{constructor(){this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$clipDepth=0,this._$depth=0,this._$isMask=!1,this._$updated=!0,this._$matrix=P(1,0,0,1,0,0),this._$colorTransform=k(1,1,1,1,0,0,0,0),this._$blendMode="normal",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$maskMatrix=null,this._$isMask=!1,this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$scale9Grid=null,this._$matrixBase=null}_$shouldClip(t){const e=this._$getBounds(t),i=r.abs(e.xMax-e.xMin),s=r.abs(e.yMax-e.yMin);return B(e),!(!i||!s)}_$getLayerBounds(e){const i=this._$getBounds(),s=q(i,e);B(i);const n=this._$filters;if(!n||!n.length)return s;let a=F(0,r.abs(s.xMax-s.xMin),0,r.abs(s.yMax-s.yMin));B(s);let h=+r.sqrt(e[0]*e[0]+e[1]*e[1]),o=+r.sqrt(e[2]*e[2]+e[3]*e[3]);h/=t,o/=t,h*=2,o*=2;for(let t=0;t-1){const t=le.instances;if(!t.has(this._$parentId))return;const e=t.get(this._$parentId);e._$updated||e._$doChanged()}}_$update(t){if(this._$doChanged(),this._$visible=t.visible,"depth"in t&&(this._$depth=t.depth),"isMask"in t&&(this._$isMask=t.isMask),"clipDepth"in t&&(this._$clipDepth=t.clipDepth),"maskId"in t&&(this._$maskId=t.maskId,this._$maskId>-1&&t.maskMatrix&&(this._$maskMatrix=t.maskMatrix)),this._$matrix[0]="a"in t?t.a:1,this._$matrix[1]="b"in t?t.b:0,this._$matrix[2]="c"in t?t.c:0,this._$matrix[3]="d"in t?t.d:1,this._$matrix[4]="tx"in t?t.tx:0,this._$matrix[5]="ty"in t?t.ty:0,this._$colorTransform[0]="f0"in t?t.f0:1,this._$colorTransform[1]="f1"in t?t.f1:1,this._$colorTransform[2]="f2"in t?t.f2:1,this._$colorTransform[3]="f3"in t?t.f3:1,this._$colorTransform[4]="f4"in t?t.f4:0,this._$colorTransform[5]="f5"in t?t.f5:0,this._$colorTransform[6]="f6"in t?t.f6:0,this._$colorTransform[7]="f7"in t?t.f7:0,this._$blendMode=t.blendMode||"normal",this._$filters=null,t.filters&&t.filters.length){this._$filters=U();for(let e=0;e-1&&this._$characterId&&rt.setRemoveTimer(`${this._$loaderInfoId}@${this._$characterId}`),t.instances.delete(this._$instanceId),this._$instanceId=-1,this._$parentId=-1,this._$loaderInfoId=-1,this._$characterId=-1,this._$blendMode="normal",this._$filters=null,this._$visible=!0,this._$maskId=-1,this._$isMask=!1,this._$depth=0,this._$clipDepth=0,this._$scale9Grid=null}_$isUpdated(){return this._$updated}_$isFilterUpdated(t,e=null,i=!1){if(this._$isUpdated())return!0;if(i&&e)for(let t=0;tc||s._$clipDepth>0)&&(t.restore(),l&&t._$leaveClip(),c=0,l=!0),!l)continue;if(s._$clipDepth>0){c=s._$clipDepth,l=s._$shouldClip(o),l&&(t.save(),l=s._$startClip(t,o));continue}const a=s._$maskId>-1&&$.has(s._$maskId)?$.get(s._$maskId):null;if(a){let e;if(a._$updated=!1,this._$instanceId===a._$parentId)e=o;else{e=f;let i=$.get(a._$parentId);for(;i||i._$instanceId!==i._$parentId;)e=H(i._$matrix,e),i=$.get(i._$parentId);const s=le.scaleX,r=P(s,0,0,s,0,0);if(e=H(r,e),N(r),t.isLayer){const i=t.getCurrentPosition();e[4]-=i.xMin,e[5]-=i.yMin}}if(!a._$shouldClip(e))continue;const i=a._$startClip(t,e);if(t.save(),!i){t.restore();continue}}s._$draw(t,o,_),s._$updated=!1,a&&(t.restore(),t._$leaveClip())}if(c&&(t.restore(),l&&t._$leaveClip()),h.isLayer)return this._$postDraw(t,e,s,h);h.matrix!==e&&N(h.matrix),s!==i&&L(s),et(h)}_$getLayerBounds(e){const i=this._$children;if(!i.length)return F(0,0,0,0);const s=h.MAX_VALUE;let n=s,a=-s,o=s,_=-s;const l=le.instances;for(let t=0;t0){const s=this._$getBounds(null),o=q(s,i);B(s);const _=+o.xMax,l=+o.xMin,c=+o.yMax,$=+o.yMin;B(o);const u=r.ceil(r.abs(_-l)),d=r.ceil(r.abs(c-$));if(0>=u||0>=d)return et(n),i!==e&&N(i),null;let g=+r.sqrt(i[0]*i[0]+i[1]*i[1]);if(!h.isInteger(g)){const t=g.toString(),e=t.indexOf("e");-1!==e&&(g=+t.slice(0,e)),g=+g.toFixed(4)}let f=+r.sqrt(i[2]*i[2]+i[3]*i[3]);if(!h.isInteger(f)){const t=f.toString(),e=t.indexOf("e");-1!==e&&(f=+t.slice(0,e)),f=+f.toFixed(4)}n.canApply=this._$canApply(this._$filters);let m=F(0,u,0,d);if(n.canApply&&this._$filters)for(let t=0;tp.width||$-m.yMin>p.height)return B(m),et(n),i!==e&&N(i),null;if(0>l+m.xMax||0>$+m.yMax)return B(m),et(n),i!==e&&N(i),null;let x=i[4]-l,b=i[5]-$;t._$startLayer(F(l,_,$,c));const v=this._$isFilterUpdated(i,this._$filters,n.canApply),T=this._$getLayerBounds(i),A=r.ceil(r.abs(T.xMax-T.xMin)),M=r.ceil(r.abs(T.yMax-T.yMin));B(T);const y=A-m.xMax+m.xMin,E=M-m.yMax+m.yMin;x+=y,b+=E,n.sw=y,n.sh=E,v&&t._$saveAttachment(r.ceil(u+y),r.ceil(d+E),!0),n.isLayer=!0,n.isUpdated=v,n.filters=this._$filters,n.blendMode=a,n.color=k(),n.matrix=P(i[0],i[1],i[2],i[3],x,b),i!==e&&N(i),B(m)}return n}_$postDraw(t,e,i,s){t.drawInstacedArray();const r=U(this._$instanceId,"f"),n=t.frameBuffer,a=s.matrix;let h=0,o=0,_=rt.get(r);if(!_||s.isUpdated){_&&rt.set(r,null),_=n.getTextureFromCurrentAttachment();const i=s.filters;let l=!1;if(i&&i.length){for(let s=0;s{switch(!0){case t[0]>e[0]:return 1;case e[0]>t[0]:return-1;default:return 0}})),this._$stops}linear(t,e,i,s,r="rgb",n="pad"){return this._$type="linear",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$rgb=r,this._$mode=n,this._$stops.length&&(this._$stops.length=0),this}radial(t,e,i,s,r,n,a="rgb",h="pad",o=0){return this._$type="radial",this._$points[0]=t,this._$points[1]=e,this._$points[2]=i,this._$points[3]=s,this._$points[4]=r,this._$points[5]=n,this._$rgb=a,this._$mode=h,this._$focalPointRatio=G(o,-.975,.975,0),this._$stops.length&&(this._$stops.length=0),this}addColorStop(t,e){this._$stops.push(U(t,e))}}class pt{constructor(t,e,i){this._$texture=t,this._$repeat=e,this._$colorTransform=i}get texture(){return this._$texture}get repeat(){return this._$repeat}get colorTransform(){return this._$colorTransform}}class xt{constructor(){this._$fillStyle=w(1,1,1,1),this._$strokeStyle=w(1,1,1,1),this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5}get miterLimit(){return this._$miterLimit}set miterLimit(t){this._$miterLimit=t}get lineWidth(){return this._$lineWidth}set lineWidth(t){this._$lineWidth=t}get lineCap(){return this._$lineCap}set lineCap(t){this._$lineCap=t}get lineJoin(){return this._$lineJoin}set lineJoin(t){this._$lineJoin=t}get fillStyle(){return this._$fillStyle}set fillStyle(t){this._$fillStyle instanceof o&&R(this._$fillStyle),this._$fillStyle=t}get strokeStyle(){return this._$strokeStyle}set strokeStyle(t){this._$strokeStyle instanceof o&&R(this._$strokeStyle),this._$strokeStyle=t}clear(){this._$lineWidth=1,this._$lineCap="round",this._$lineJoin="round",this._$miterLimit=5,this._$clearFill(),this._$clearStroke()}_$clearFill(){if(this._$fillStyle instanceof mt)return this._$fillStyle.dispose(),void(this._$fillStyle=w(1,1,1,1));this._$fillStyle instanceof pt?this._$fillStyle=w(1,1,1,1):this._$fillStyle.fill(1)}_$clearStroke(){if(this._$strokeStyle instanceof mt)return this._$strokeStyle.dispose(),void(this._$strokeStyle=w(1,1,1,1));this._$strokeStyle instanceof pt?this._$strokeStyle=w(1,1,1,1):this._$strokeStyle.fill(1)}}let bt=2048;class vt{constructor(t){t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this._$gl=t,this._$objectPool=[],this._$objectPoolArea=0,this._$activeTexture=-1,this._$boundTextures=[null,null,null],this._$maxWidth=0,this._$maxHeight=0,this._$atlasTextures=[],this._$atlasCacheMap=new Map,this._$positionObjectArray=[],this._$nodeObjectArray=[],this._$atlasNodes=new Map}createTextureAtlas(){const t=this._$gl.createTexture();t.width=bt,t.height=bt,this._$gl.activeTexture(this._$gl.TEXTURE3),this._$gl.bindTexture(this._$gl.TEXTURE_2D,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,this._$gl.NEAREST),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,this._$gl.NEAREST),this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,bt,bt),this._$gl.bindTexture(this._$gl.TEXTURE_2D,null),this._$activeTexture>-1&&this._$gl.activeTexture(this._$activeTexture);const e=this._$atlasTextures.length;this._$atlasNodes.set(e,[]),this._$atlasCacheMap.set(e,[]),this._$atlasTextures.push(t)}getAtlasTexture(t){return this._$atlasTextures[t]}getNode(t,e,i,s){const r=this._$nodeObjectArray.length?this._$nodeObjectArray.pop():{x:0,y:0,w:0,h:0};return r.x=t,r.y=e,r.w=i,r.h=s,r}createCachePosition(t,e){const i=this._$positionObjectArray.length?this._$positionObjectArray.pop():{index:0,x:0,y:0,w:0,h:0};i.x=i.y=0,i.w=t,i.h=e;for(const[s,r]of this._$atlasNodes){if(!r.length)return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i;const n=r.length;for(let a=0;an.w||e>n.h))return i.index=s,i.x=n.x,i.y=n.y,this._$atlasCacheMap.get(i.index).push(i),n.w!==t||n.h!==e?t>e?(n.h-e-1>0&&r.push(this.getNode(n.x,n.y+e+1,n.w,n.h-e-1)),n.w-t-1>0?(n.x=n.x+t+1,n.w=n.w-t-1,n.h=e):(r.splice(a,1),this._$nodeObjectArray.push(n))):(n.w-t-1>0&&r.push(this.getNode(n.x+t+1,n.y,n.w-t-1,n.h)),n.h-e-1>0?(n.y=n.y+e+1,n.w=t,n.h=n.h-e-1):(r.splice(a,1),this._$nodeObjectArray.push(n))):(r.splice(a,1),this._$nodeObjectArray.push(n)),i}}const s=this._$atlasTextures.length;this.createTextureAtlas();const r=this._$atlasNodes.get(s);return t>e?(bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,e)),bt-e-1>0&&r.push(this.getNode(0,e+1,bt,bt-e-1))):(bt-e-1>0&&r.push(this.getNode(0,e+1,t,bt-e-1)),bt-t-1>0&&r.push(this.getNode(t+1,0,bt-t-1,bt))),i.index=s,this._$atlasCacheMap.get(i.index).push(i),i}releasePosition(t){var e;this._$atlasNodes.has(t.index)&&(null===(e=this._$atlasNodes.get(t.index))||void 0===e||e.unshift(this.getNode(t.x,t.y,t.w,t.h)),this._$positionObjectArray.push(t))}clearCache(){for(const t of this._$atlasCacheMap.values())t.length=0;for(const t of this._$atlasNodes.values())t.length=0}_$createTexture(t,e){const i=this._$gl.createTexture();return i.width=0,i.height=0,i.area=0,i.dirty=!0,i.smoothing=!0,this.bind0(i,!1),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_S,this._$gl.CLAMP_TO_EDGE),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_WRAP_T,this._$gl.CLAMP_TO_EDGE),i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.texStorage2D(this._$gl.TEXTURE_2D,1,this._$gl.RGBA8,t,e),i}_$getTexture(t,e){for(let i=0;ithis._$maxWidth*this._$maxHeight*2)this._$gl.deleteTexture(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPool.length&&this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();this._$objectPoolArea-=t.area,this._$gl.deleteTexture(t)}}bind0(t,e=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,e)}bind01(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,null,null),this._$bindTexture(1,this._$gl.TEXTURE1,e,i),this._$bindTexture(0,this._$gl.TEXTURE0,t,i)}bind012(t,e,i,s=null){this._$bindTexture(2,this._$gl.TEXTURE2,i,s),this._$bindTexture(1,this._$gl.TEXTURE1,e,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}bind02(t,e,i=null){this._$bindTexture(2,this._$gl.TEXTURE2,e,i),this._$bindTexture(1,this._$gl.TEXTURE1,null,null),this._$bindTexture(0,this._$gl.TEXTURE0,t,null)}_$bindTexture(t,e,i=null,s=null){const r=i!==this._$boundTextures[t],n=null!==s&&null!==i&&s!==i.smoothing;if((r||n||e===this._$gl.TEXTURE0)&&e!==this._$activeTexture&&(this._$activeTexture=e,this._$gl.activeTexture(e)),r&&(this._$boundTextures[t]=i,this._$gl.bindTexture(this._$gl.TEXTURE_2D,i)),n){i&&(i.smoothing=!!s);const t=s?this._$gl.LINEAR:this._$gl.NEAREST;this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MIN_FILTER,t),this._$gl.texParameteri(this._$gl.TEXTURE_2D,this._$gl.TEXTURE_MAG_FILTER,t)}}}class Tt{constructor(t){this._$gl=t,this._$objectPool=U(),this._$objectPoolArea=0,this._$maxWidth=0,this._$maxHeight=0}set maxWidth(t){this._$maxWidth=t}set maxHeight(t){this._$maxHeight=t}_$createStencilBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the stencil buffer is null.");return t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getStencilBuffer(t,e){const i=this._$objectPool.length;for(let s=0;s100){const t=this._$objectPool.shift();if(t)return this._$objectPoolArea-=t.area,t}return this._$createStencilBuffer()}create(t,e){const i=this._$getStencilBuffer(t,e);return i.width===t&&i.height===e||(i.width=t,i.height=e,i.area=t*e,i.dirty=!1,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,i),this._$gl.renderbufferStorage(this._$gl.RENDERBUFFER,this._$gl.STENCIL_INDEX8,t,e)),i}release(t){if(t.area>this._$maxWidth*this._$maxHeight*2)this._$gl.deleteRenderbuffer(t);else if(t.dirty=!0,this._$objectPool.push(t),this._$objectPoolArea+=t.area,this._$objectPoolArea>this._$maxWidth*this._$maxHeight*10){const t=this._$objectPool.shift();t&&(this._$objectPoolArea-=t.area,this._$gl.deleteRenderbuffer(t))}}}class At{constructor(t,e){this._$gl=t,this._$samples=e,this._$objectPool=U()}set samples(t){this._$samples=t}_$createColorBuffer(){const t=this._$gl.createRenderbuffer();if(!t)throw new Error("the color buffer is null.");const e=this._$gl.createRenderbuffer();if(!e)throw new Error("the stencil buffer is null.");return t.stencil=e,t.samples=0,t.width=0,t.height=0,t.area=0,t.dirty=!0,t}_$getColorBuffer(t){if(!this._$objectPool.length)return this._$createColorBuffer();const e=this._$bsearch(t);if(e1;){const s=r.floor((i+e)/2);t<=this._$objectPool[s].area?i=s:e=s}return i}}class Mt{constructor(t,e){this._$gl=t,this._$objectPool=[],this._$frameBuffer=t.createFramebuffer(),t.bindFramebuffer(t.READ_FRAMEBUFFER,this._$frameBuffer),this._$frameBufferTexture=t.createFramebuffer(),this._$currentAttachment=null,this._$isBinding=!1,this._$textureManager=new vt(t),this._$stencilBufferPool=new Tt(t),this._$colorBufferPool=new At(t,e),this._$isRenderBinding=!1,this._$colorBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.RGBA8,bt,bt),this._$stencilBuffer=this._$gl.createRenderbuffer(),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.renderbufferStorageMultisample(this._$gl.RENDERBUFFER,e,this._$gl.STENCIL_INDEX8,bt,bt)}bindRenderBuffer(){this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),this._$isRenderBinding||(this._$isRenderBinding=!0,this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,this._$colorBuffer),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,this._$stencilBuffer),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,this._$stencilBuffer))}get currentAttachment(){return this._$currentAttachment}get textureManager(){return this._$textureManager}createCacheAttachment(t,e,i=!1,s=0){const r=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},n=this._$textureManager.create(t,e);return r.width=t,r.height=e,i?(r.color=this._$colorBufferPool.create(t,e,s),r.texture=n,r.msaa=!0,r.stencil=r.color.stencil):(r.color=n,r.texture=n,r.msaa=!1,r.stencil=this._$stencilBufferPool.create(t,e)),r.mask=!1,r.clipLevel=0,r.isActive=!0,r}clearCache(){this._$textureManager.clearCache()}setMaxSize(t,e){this._$stencilBufferPool._$maxWidth=t,this._$stencilBufferPool._$maxHeight=e,this._$textureManager._$maxWidth=t,this._$textureManager._$maxHeight=e}createTextureAttachment(t,e){const i=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!1},s=this._$textureManager.create(t,e);return i.width=t,i.height=e,i.color=s,i.texture=s,i.msaa=!1,i.stencil=null,i.mask=!1,i.clipLevel=0,i.isActive=!0,i}createTextureAttachmentFrom(t){const e=this._$objectPool.pop()||{width:0,height:0,color:null,texture:null,msaa:!1,stencil:null,mask:!1,clipLevel:0,isActive:!0};return e.width=t.width,e.height=t.height,e.color=t,e.texture=t,e.msaa=!1,e.stencil=null,e.mask=!1,e.clipLevel=0,e.isActive=!0,e}releaseAttachment(t=null,e=!1){t&&t.isActive&&(t.msaa?t.color instanceof WebGLRenderbuffer&&this._$colorBufferPool.release(t.color):t.stencil&&this._$stencilBufferPool.release(t.stencil),e&&t.texture&&this._$textureManager.release(t.texture),t.color=null,t.texture=null,t.stencil=null,t.isActive=!1,this._$objectPool.push(t))}bind(t){this._$currentAttachment=t,this._$isBinding||(this._$isBinding=!0,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)),t.msaa?t.color instanceof WebGLRenderbuffer&&(this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.color),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.RENDERBUFFER,t.color)):t.color instanceof WebGLTexture&&(t.color&&this._$textureManager.bind0(t.color),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,t.color,0)),this._$gl.bindRenderbuffer(this._$gl.RENDERBUFFER,t.stencil),this._$gl.framebufferRenderbuffer(this._$gl.FRAMEBUFFER,this._$gl.STENCIL_ATTACHMENT,this._$gl.RENDERBUFFER,t.stencil),this._$isRenderBinding=!1}unbind(){this._$currentAttachment=null,this._$isBinding&&(this._$isBinding=!1,this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,null))}transferToMainTexture(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,null),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBuffer)}createCachePosition(t,e){return this._$textureManager.createCachePosition(t,e)}transferTexture(t){this._$gl.disable(this._$gl.SCISSOR_TEST),this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture);const e=this._$textureManager.getAtlasTexture(t.index);this._$textureManager.bind0(e),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,e,0);const i=r.max(0,t.x-1),s=r.max(0,t.y-1),n=r.min(bt,t.x+t.w+1),a=r.min(bt,t.y+t.h+1);this._$gl.blitFramebuffer(i,s,n,a,i,s,n,a,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer)}getTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");if(!this._$currentAttachment.msaa&&this._$currentAttachment.texture)return this._$currentAttachment.texture;const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$currentAttachment.texture;if(!i)throw new Error("the texture is null.");return i.dirty=!1,this._$gl.bindFramebuffer(this._$gl.DRAW_FRAMEBUFFER,this._$frameBufferTexture),this._$textureManager.bind0(i),this._$gl.framebufferTexture2D(this._$gl.FRAMEBUFFER,this._$gl.COLOR_ATTACHMENT0,this._$gl.TEXTURE_2D,i,0),this._$gl.blitFramebuffer(0,0,t,e,0,0,t,e,this._$gl.COLOR_BUFFER_BIT,this._$gl.NEAREST),this._$gl.bindFramebuffer(this._$gl.FRAMEBUFFER,this._$frameBuffer),i}createTextureFromPixels(t,e,i=null,s=!1,r=!0){return this._$textureManager.create(t,e,i,s,r)}createTextureFromCanvas(t){return this._$textureManager.createFromCanvas(t)}createTextureFromImage(t,e=!1){return this._$textureManager.createFromImage(t,e)}createTextureFromVideo(t,e=!1){return this._$textureManager.createFromVideo(t,e)}createTextureFromCurrentAttachment(){if(!this._$currentAttachment)throw new Error("the current attachment is null.");const t=this._$currentAttachment.width,e=this._$currentAttachment.height,i=this._$textureManager.create(t,e);return this._$textureManager.bind0(i),this._$gl.copyTexSubImage2D(this._$gl.TEXTURE_2D,0,0,0,0,0,t,e),i}releaseTexture(t){this._$textureManager.release(t)}}class yt{constructor(){this._$bezierConverterBuffer=new o(32)}cubicToQuad(t,e,i,s,r,n,a,h){this._$split2Cubic(t,e,i,s,r,n,a,h,0,16),this._$split2Cubic(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0,8),this._$split2Cubic(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16,24),this._$split2Quad(this._$bezierConverterBuffer[0],this._$bezierConverterBuffer[1],this._$bezierConverterBuffer[2],this._$bezierConverterBuffer[3],this._$bezierConverterBuffer[4],this._$bezierConverterBuffer[5],this._$bezierConverterBuffer[6],this._$bezierConverterBuffer[7],0),this._$split2Quad(this._$bezierConverterBuffer[8],this._$bezierConverterBuffer[9],this._$bezierConverterBuffer[10],this._$bezierConverterBuffer[11],this._$bezierConverterBuffer[12],this._$bezierConverterBuffer[13],this._$bezierConverterBuffer[14],this._$bezierConverterBuffer[15],8),this._$split2Quad(this._$bezierConverterBuffer[16],this._$bezierConverterBuffer[17],this._$bezierConverterBuffer[18],this._$bezierConverterBuffer[19],this._$bezierConverterBuffer[20],this._$bezierConverterBuffer[21],this._$bezierConverterBuffer[22],this._$bezierConverterBuffer[23],16),this._$split2Quad(this._$bezierConverterBuffer[24],this._$bezierConverterBuffer[25],this._$bezierConverterBuffer[26],this._$bezierConverterBuffer[27],this._$bezierConverterBuffer[28],this._$bezierConverterBuffer[29],this._$bezierConverterBuffer[30],this._$bezierConverterBuffer[31],24)}_$split2Cubic(t,e,i,s,r,n,a,h,o,_){const l=.125*(t+3*(i+r)+a),c=.125*(e+3*(s+n)+h),$=.125*(a+r-i-t),u=.125*(h+n-s-e);this._$bezierConverterBuffer[o]=t,this._$bezierConverterBuffer[o+1]=e,this._$bezierConverterBuffer[o+2]=.5*(t+i),this._$bezierConverterBuffer[o+3]=.5*(e+s),this._$bezierConverterBuffer[o+4]=l-$,this._$bezierConverterBuffer[o+5]=c-u,this._$bezierConverterBuffer[o+6]=l,this._$bezierConverterBuffer[o+7]=c,this._$bezierConverterBuffer[_]=l,this._$bezierConverterBuffer[_+1]=c,this._$bezierConverterBuffer[_+2]=l+$,this._$bezierConverterBuffer[_+3]=c+u,this._$bezierConverterBuffer[_+4]=.5*(r+a),this._$bezierConverterBuffer[_+5]=.5*(n+h),this._$bezierConverterBuffer[_+6]=a,this._$bezierConverterBuffer[_+7]=h}_$split2Quad(t,e,i,s,r,n,a,h,o){const _=.125*(t+3*(i+r)+a),l=.125*(e+3*(s+n)+h);this._$bezierConverterBuffer[o]=.25*t+.75*i,this._$bezierConverterBuffer[o+1]=.25*e+.75*s,this._$bezierConverterBuffer[o+2]=_,this._$bezierConverterBuffer[o+3]=l,this._$bezierConverterBuffer[o+4]=.75*r+.25*a,this._$bezierConverterBuffer[o+5]=.75*n+.25*h,this._$bezierConverterBuffer[o+6]=a,this._$bezierConverterBuffer[o+7]=h}}class Et{constructor(){this._$currentPath=U(),this._$vertices=U(),this._$bezierConverter=new yt}get vertices(){return this._$pushCurrentPathToVertices(),this._$vertices}begin(){for(this._$currentPath.length=0;this._$vertices.length;)D(this._$vertices.pop())}moveTo(t,e){this._$currentPath.length?this._$equalsToLastPoint(t,e)||(this._$pushCurrentPathToVertices(),this._$pushPointToCurrentPath(t,e,!1)):this._$pushPointToCurrentPath(t,e,!1)}lineTo(t,e){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}quadTo(t,e,i,s){this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(i,s)||(this._$pushPointToCurrentPath(t,e,!0),this._$pushPointToCurrentPath(i,s,!1))}cubicTo(t,e,i,s,r,n){if(this._$currentPath.length||this.moveTo(0,0),this._$equalsToLastPoint(r,n))return;const a=+this._$currentPath[this._$currentPath.length-3],h=+this._$currentPath[this._$currentPath.length-2];this._$bezierConverter.cubicToQuad(a,h,t,e,i,s,r,n);const o=this._$bezierConverter._$bezierConverterBuffer;for(let t=0;t<32;)this.quadTo(o[t++],o[t++],o[t++],o[t++])}drawCircle(t,e,i){const s=i,r=.5522847498307936*i;this.cubicTo(t+s,e+r,t+r,e+s,t,e+s),this.cubicTo(t-r,e+s,t-s,e+r,t-s,e),this.cubicTo(t-s,e-r,t-r,e-s,t,e-s),this.cubicTo(t+r,e-s,t+s,e-r,t+s,e)}close(){if(this._$currentPath.length<=6)return;const t=+this._$currentPath[0],e=+this._$currentPath[1];this._$equalsToLastPoint(t,e)||this._$pushPointToCurrentPath(t,e,!1)}_$equalsToLastPoint(t,e){const i=+this._$currentPath[this._$currentPath.length-3],s=+this._$currentPath[this._$currentPath.length-2];return t===i&&e===s}_$pushPointToCurrentPath(t,e,i){this._$currentPath.push(t,e,i)}_$pushCurrentPathToVertices(){this._$currentPath.length<4?this._$currentPath.length=0:(this._$vertices.push(this._$currentPath),this._$currentPath=U())}createRectVertices(t,e,i,s){return U(U(t,e,!1,t+i,e,!1,t+i,e+s,!1,t,e+s,!1))}}class Ct{constructor(){this.enabled=!1,this.parentMatrixA=1,this.parentMatrixB=0,this.parentMatrixC=0,this.parentMatrixD=0,this.parentMatrixE=1,this.parentMatrixF=0,this.parentMatrixG=0,this.parentMatrixH=0,this.parentMatrixI=1,this.ancestorMatrixA=1,this.ancestorMatrixB=0,this.ancestorMatrixC=0,this.ancestorMatrixD=0,this.ancestorMatrixE=1,this.ancestorMatrixF=0,this.ancestorMatrixG=0,this.ancestorMatrixH=0,this.ancestorMatrixI=1,this.parentViewportX=0,this.parentViewportY=0,this.parentViewportW=0,this.parentViewportH=0,this.minXST=1e-5,this.minYST=1e-5,this.minXPQ=1e-5,this.minYPQ=1e-5,this.maxXST=.99999,this.maxYST=.99999,this.maxXPQ=.99999,this.maxYPQ=.99999}enable(t,e,i,s,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x){const b=n.xMax-n.xMin,v=n.yMax-n.yMin,T=a.w,A=a.h,M=r.abs(r.ceil(b*h)),y=r.abs(r.ceil(v*h)),E=T>0?(a.x-n.xMin)/b:1e-5,C=A>0?(a.y-n.yMin)/v:1e-5,S=T>0?(a.x+a.w-n.xMin)/b:.99999,F=A>0?(a.y+a.h-n.yMin)/v:.99999;let B=M*E/i,w=y*C/s,R=(i-M*(1-S))/i,I=(s-y*(1-F))/s;if(B>=R){const t=E/(E+(1-S));B=r.max(t-1e-5,0),R=r.min(t+1e-5,1)}if(w>=I){const t=C/(C+(1-F));w=r.max(t-1e-5,0),I=r.min(t+1e-5,1)}this.enabled=!0,this.parentMatrixA=o,this.parentMatrixB=_,this.parentMatrixD=l,this.parentMatrixE=c,this.parentMatrixG=$,this.parentMatrixH=u,this.ancestorMatrixA=d,this.ancestorMatrixB=g,this.ancestorMatrixD=f,this.ancestorMatrixE=m,this.ancestorMatrixG=p,this.ancestorMatrixH=x,this.parentViewportX=t,this.parentViewportY=e,this.parentViewportW=i,this.parentViewportH=s,this.minXST=E,this.minYST=C,this.minXPQ=B,this.minYPQ=w,this.maxXST=S,this.maxYST=F,this.maxXPQ=R,this.maxYPQ=I}disable(){this.enabled=!1}}class St{constructor(t,e){this._$gl=t,this._$array=[],this._$map=V();const i=this._$gl.getProgramParameter(e,this._$gl.ACTIVE_UNIFORMS);for(let t=0;t0&&(t.assign--,t.method(t.array)))}}}class Ft{constructor(){this._$attributes=[],this._$count=0}get attributes(){return this._$attributes}get count(){return this._$count}set count(t){this._$count=t}clear(){this._$attributes.length=0,this._$count=0}}class Bt{constructor(t,e,i,s){this._$gl=t,this._$context=e,this._$program=this._$createProgram(i,s),this._$uniform=new St(t,this._$program),this._$instance=null}get instance(){return this._$instance||(this._$instance=new Ft),this._$instance}get uniform(){return this._$uniform}_$createProgram(t,i){const s=this._$gl.createProgram();s.id=e++;const r=this._$gl.createShader(this._$gl.VERTEX_SHADER);this._$gl.shaderSource(r,t),this._$gl.compileShader(r);const n=this._$gl.createShader(this._$gl.FRAGMENT_SHADER);return this._$gl.shaderSource(n,i),this._$gl.compileShader(n),this._$gl.attachShader(s,r),this._$gl.attachShader(s,n),this._$gl.linkProgram(s),this._$gl.detachShader(s,r),this._$gl.detachShader(s,n),this._$gl.deleteShader(r),this._$gl.deleteShader(n),s}_$attachProgram(){const t=this._$context.shaderList;t.currentProgramId!==this._$program.id&&(t.currentProgramId=this._$program.id,this._$gl.useProgram(this._$program))}drawArraysInstanced(t){this._$attachProgram(),this._$context.vao.bindInstnceArray(t),this._$gl.drawArraysInstanced(this._$gl.TRIANGLE_STRIP,0,4,t.count)}_$drawImage(){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindCommonVertexArray(),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$drawGradient(t,e){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bindGradientVertexArray(t,e),this._$gl.drawArrays(this._$gl.TRIANGLE_STRIP,0,4)}_$stroke(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawElements(this._$gl.TRIANGLES,t.indexCount,this._$gl.UNSIGNED_SHORT,0)}_$fill(t){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t);const e=t.indexRanges,i=e[e.length-1];this._$gl.drawArrays(this._$gl.TRIANGLES,0,i.first+i.count)}_$containerClip(t,e,i){this._$attachProgram(),this._$context.blend.reset(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.TRIANGLES,e,i)}_$drawPoints(t,e,i){this._$attachProgram(),this._$uniform.bindUniforms(),this._$context.vao.bind(t),this._$gl.drawArrays(this._$gl.POINTS,e,i)}}class wt{static FUNCTION_GRID_OFF(){return"\n\nvec2 applyMatrix(in vec2 vertex) {\n mat3 matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n\n vec2 position = (matrix * vec3(vertex, 1.0)).xy;\n\n return position;\n}\n\n"}static FUNCTION_GRID_ON(t){return`\n\nvec2 applyMatrix(in vec2 vertex) {\n mat3 parent_matrix = mat3(\n u_highp[${t}].xyz,\n u_highp[${t+1}].xyz,\n u_highp[${t+2}].xyz\n );\n mat3 ancestor_matrix = mat3(\n u_highp[${t+3}].xyz,\n u_highp[${t+4}].xyz,\n u_highp[${t+5}].xyz\n );\n vec2 parent_offset = vec2(u_highp[${t+2}].w, u_highp[${t+3}].w);\n vec2 parent_size = vec2(u_highp[${t+4}].w, u_highp[${t+5}].w);\n vec4 grid_min = u_highp[${t+6}];\n vec4 grid_max = u_highp[${t+7}];\n\n vec2 position = (parent_matrix * vec3(vertex, 1.0)).xy;\n position = (position - parent_offset) / parent_size;\n\n vec4 ga = grid_min;\n vec4 gb = grid_max - grid_min;\n vec4 gc = vec4(1.0) - grid_max;\n\n vec2 pa = position;\n vec2 pb = position - grid_min.st;\n vec2 pc = position - grid_max.st;\n\n position = (ga.pq / ga.st) * min(pa, ga.st)\n + (gb.pq / gb.st) * clamp(pb, vec2(0.0), gb.st)\n + (gc.pq / gc.st) * max(vec2(0.0), pc);\n\n position = position * parent_size + parent_offset;\n position = (ancestor_matrix * vec3(position, 1.0)).xy;\n\n return position;\n}\n\n`}}class Rt{static TEMPLATE(t,e,i,s){const r=e-1,n=i?this.VARYING_UV_ON():"",a=i?this.STATEMENT_UV_ON():"";return`#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\nlayout (location = 1) in vec2 a_option1;\nlayout (location = 2) in vec2 a_option2;\nlayout (location = 3) in float a_type;\n\nuniform vec4 u_highp[${t}];\n\n${n}\n\n${s?wt.FUNCTION_GRID_ON(i?5:0):wt.FUNCTION_GRID_OFF()}\n\nfloat crossVec2(in vec2 v1, in vec2 v2) {\n return v1.x * v2.y - v2.x * v1.y;\n}\n\nvec2 perpendicularVec2(in vec2 v1) {\n float face = u_highp[${r}][1];\n\n return face * vec2(v1.y, -v1.x);\n}\n\nvec2 calculateNormal(in vec2 direction) {\n vec2 normalized = normalize(direction);\n return perpendicularVec2(normalized);\n}\n\nvec2 calculateIntersection(in vec2 v1, in vec2 v2, in vec2 o1, in vec2 o2) {\n float t = crossVec2(o2 - o1, v2) / crossVec2(v1, v2);\n return (o1 + t * v1);\n}\n\nvec2 calculateAnchor(in vec2 position, in float convex, out vec2 v1, out vec2 v2, out vec2 o1, out vec2 o2) {\n float miter_limit = u_highp[${r}][2];\n\n vec2 a = applyMatrix(a_option1);\n vec2 b = applyMatrix(a_option2);\n\n v1 = convex * (position - a);\n v2 = convex * (b - position);\n o1 = calculateNormal(v1) + a;\n o2 = calculateNormal(v2) + position;\n\n vec2 anchor = calculateIntersection(v1, v2, o1, o2) - position;\n return normalize(anchor) * min(length(anchor), miter_limit);\n}\n\nvoid main() {\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\n float half_width = u_highp[${r}][0];\n\n vec2 position = applyMatrix(a_vertex);\n vec2 offset = vec2(0.0);\n vec2 v1, v2, o1, o2;\n\n if (a_type == 1.0 || a_type == 2.0) { // 線分\n offset = calculateNormal(a_option2 * (applyMatrix(a_option1) - position));\n } else if (a_type == 10.0) { // スクエア線端\n offset = normalize(position - applyMatrix(a_option1));\n offset += a_option2 * perpendicularVec2(offset);\n } else if (a_type == 21.0) { // マイター結合(線分Bの凸側)\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\n } else if (a_type == 22.0) { // マイター結合(線分Aの凸側)\n offset = calculateAnchor(position, 1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\n } else if (a_type == 23.0) { // マイター結合(線分Aの凹側)\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v1, perpendicularVec2(offset), o1, position + offset) - position;\n } else if (a_type == 24.0) { // マイター結合(線分Bの凹側)\n offset = calculateAnchor(position, -1.0, v1, v2, o1, o2);\n offset = calculateIntersection(v2, perpendicularVec2(offset), o2, position + offset) - position;\n } else if (a_type >= 30.0) { // ラウンド結合\n float face = u_highp[${r}][1];\n float rad = face * (a_type - 30.0) * 0.3488888889; /* 0.3488888889 = PI / 9.0 */\n offset = mat2(cos(rad), sin(rad), -sin(rad), cos(rad)) * vec2(1.0, 0.0);\n }\n \n offset *= half_width;\n position += offset;\n ${a}\n\n position /= viewport;\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n`}static VARYING_UV_ON(){return"\nout vec2 v_uv;\n"}static STATEMENT_UV_ON(){return"\n mat3 uv_matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n mat3 inverse_matrix = mat3(\n u_highp[3].xyz,\n u_highp[4].xyz,\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\n );\n\n v_uv = (uv_matrix * vec3(a_vertex, 1.0)).xy;\n v_uv += offset;\n v_uv = (inverse_matrix * vec3(v_uv, 1.0)).xy;\n"}}class It{static TEMPLATE(t,e,i,s){const r=i?this.ATTRIBUTE_BEZIER_ON():"",n=i?this.VARYING_BEZIER_ON():e?this.VARYING_UV_ON():"",a=i?this.STATEMENT_BEZIER_ON():e?this.STATEMENT_UV_ON():"";return`#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n${r}\n\nuniform vec4 u_highp[${t}];\n\n${n}\n\n${s?wt.FUNCTION_GRID_ON(e?5:0):wt.FUNCTION_GRID_OFF()}\n\nvoid main() {\n vec2 viewport = vec2(u_highp[0].w, u_highp[1].w);\n\n ${a}\n\n vec2 pos = applyMatrix(a_vertex) / viewport;\n pos = pos * 2.0 - 1.0;\n gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n}\n\n`}static ATTRIBUTE_BEZIER_ON(){return"\nlayout (location = 1) in vec2 a_bezier;\n"}static VARYING_UV_ON(){return"\nout vec2 v_uv;\n"}static VARYING_BEZIER_ON(){return"\nout vec2 v_bezier;\n"}static STATEMENT_UV_ON(){return"\n mat3 uv_matrix = mat3(\n u_highp[0].xyz,\n u_highp[1].xyz,\n u_highp[2].xyz\n );\n mat3 inverse_matrix = mat3(\n u_highp[3].xyz,\n u_highp[4].xyz,\n vec3(u_highp[2].w, u_highp[3].w, u_highp[4].w)\n );\n\n v_uv = (inverse_matrix * uv_matrix * vec3(a_vertex, 1.0)).xy;\n"}static STATEMENT_BEZIER_ON(){return"\n v_bezier = a_bezier;\n"}}class Pt{static FUNCTION_IS_INSIDE(){return"\n\nfloat isInside(in vec2 uv) {\n return step(4.0, dot(step(vec4(0.0, uv.x, 0.0, uv.y), vec4(uv.x, 1.0, uv.y, 1.0)), vec4(1.0)));\n}\n\n"}static STATEMENT_INSTANCED_COLOR_TRANSFORM_ON(){return"\n src.rgb /= max(0.0001, src.a);\n src = clamp(src * mul + add, 0.0, 1.0);\n src.rgb *= src.a;\n"}static STATEMENT_COLOR_TRANSFORM_ON(t){return`\n vec4 mul = u_mediump[${t}];\n vec4 add = u_mediump[${t+1}];\n${Pt.STATEMENT_INSTANCED_COLOR_TRANSFORM_ON()}\n`}}class Nt{static SOLID_COLOR(){return"#version 300 es\nprecision mediump float;\n\nuniform vec4 u_mediump;\n\nout vec4 o_color;\n\nvoid main() {\n o_color = vec4(u_mediump.rgb * u_mediump.a, u_mediump.a);\n}\n\n"}static BITMAP_CLIPPED(){return`#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_mediump[3];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 uv = vec2(v_uv.x, u_mediump[0].y - v_uv.y) / u_mediump[0].xy;\n\n vec4 src = texture(u_texture, uv);\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\n o_color = src;\n}`}static BITMAP_PATTERN(){return`#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_mediump[3];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 uv = fract(vec2(v_uv.x, -v_uv.y) / u_mediump[0].xy);\n \n vec4 src = texture(u_texture, uv);\n ${Pt.STATEMENT_COLOR_TRANSFORM_ON(1)}\n o_color = src;\n}`}static MASK(){return"#version 300 es\nprecision mediump float;\n\nin vec2 v_bezier;\nout vec4 o_color;\n\nvoid main() {\n vec2 px = dFdx(v_bezier);\n vec2 py = dFdy(v_bezier);\n\n vec2 f = (2.0 * v_bezier.x) * vec2(px.x, py.x) - vec2(px.y, py.y);\n float alpha = 0.5 - (v_bezier.x * v_bezier.x - v_bezier.y) / length(f);\n\n if (alpha > 0.0) {\n o_color = vec4(min(alpha, 1.0));\n } else {\n discard;\n } \n}\n\n"}}class kt{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getSolidColorShapeShader(t,e){const i=`s${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!1,e);const a=new Bt(this._$gl,this._$context,n,Nt.SOLID_COLOR());return this._$collection.set(i,a),a}getBitmapShapeShader(t,e,i){const s=`b${t?"y":"n"}${e?"y":"n"}${i?"y":"n"}`;if(this._$collection.has(s)){const t=this._$collection.get(s);if(t)return t}const r=(i?13:5)+(t?1:0),n=r;let a;a=t?Rt.TEMPLATE(r,n,!0,i):It.TEMPLATE(r,!0,!1,i);const h=e?Nt.BITMAP_PATTERN():Nt.BITMAP_CLIPPED(),o=new Bt(this._$gl,this._$context,a,h);return this._$collection.set(s,o),o}getMaskShapeShader(t,e){const i=`m${t?"y":"n"}${e?"y":"n"}`;if(this._$collection.has(i)){const t=this._$collection.get(i);if(t)return t}const s=(e?8:3)+(t?1:0),r=s;let n;n=t?Rt.TEMPLATE(s,r,!1,e):It.TEMPLATE(s,!1,!0,e);const a=new Bt(this._$gl,this._$context,n,Nt.MASK());return this._$collection.set(i,a),a}setSolidColorShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.highp;let u;n?($[0]=_.parentMatrixA,$[1]=_.parentMatrixB,$[2]=_.parentMatrixC,$[4]=_.parentMatrixD,$[5]=_.parentMatrixE,$[6]=_.parentMatrixF,$[8]=_.parentMatrixG,$[9]=_.parentMatrixH,$[10]=_.parentMatrixI,$[12]=_.ancestorMatrixA,$[13]=_.ancestorMatrixB,$[14]=_.ancestorMatrixC,$[16]=_.ancestorMatrixD,$[17]=_.ancestorMatrixE,$[18]=_.ancestorMatrixF,$[20]=_.ancestorMatrixG,$[21]=_.ancestorMatrixH,$[22]=_.ancestorMatrixI,$[3]=h,$[7]=o,$[11]=_.parentViewportX,$[15]=_.parentViewportY,$[19]=_.parentViewportW,$[23]=_.parentViewportH,$[24]=_.minXST,$[25]=_.minYST,$[26]=_.minXPQ,$[27]=_.minYPQ,$[28]=_.maxXST,$[29]=_.maxYST,$[30]=_.maxXPQ,$[31]=_.maxYPQ,u=32):($[0]=a[0],$[1]=a[1],$[2]=a[2],$[4]=a[3],$[5]=a[4],$[6]=a[5],$[8]=a[6],$[9]=a[7],$[10]=a[8],$[3]=h,$[7]=o,u=12),e&&($[u]=i,$[u+1]=s,$[u+2]=r);const d=t.mediump;d[0]=l[0],d[1]=l[1],d[2]=l[2],d[3]=l[3]*c}setBitmapShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g,f,m,p,x,b){const v=t.highp;let T;v[0]=a[0],v[1]=a[1],v[2]=a[2],v[4]=a[3],v[5]=a[4],v[6]=a[5],v[8]=a[6],v[9]=a[7],v[10]=a[8],v[12]=h[0],v[13]=h[1],v[14]=h[2],v[16]=h[3],v[17]=h[4],v[18]=h[5],v[11]=h[6],v[15]=h[7],v[19]=h[8],v[3]=o,v[7]=_,T=20,n&&(v[T]=l.parentMatrixA,v[T+1]=l.parentMatrixB,v[T+2]=l.parentMatrixC,v[T+4]=l.parentMatrixD,v[T+5]=l.parentMatrixE,v[T+6]=l.parentMatrixF,v[T+8]=l.parentMatrixG,v[T+9]=l.parentMatrixH,v[T+10]=l.parentMatrixI,v[T+12]=l.ancestorMatrixA,v[T+13]=l.ancestorMatrixB,v[T+14]=l.ancestorMatrixC,v[T+16]=l.ancestorMatrixD,v[T+17]=l.ancestorMatrixE,v[T+18]=l.ancestorMatrixF,v[T+20]=l.ancestorMatrixG,v[T+21]=l.ancestorMatrixH,v[T+22]=l.ancestorMatrixI,v[T+11]=l.parentViewportX,v[T+15]=l.parentViewportY,v[T+19]=l.parentViewportW,v[T+23]=l.parentViewportH,v[T+24]=l.minXST,v[T+25]=l.minYST,v[T+26]=l.minXPQ,v[T+27]=l.minYPQ,v[T+28]=l.maxXST,v[T+29]=l.maxYST,v[T+30]=l.maxXPQ,v[T+31]=l.maxYPQ,T=52),e&&(v[T]=i,v[T+1]=s,v[T+2]=r);const A=t.mediump;A[0]=c,A[1]=$,A[4]=u,A[5]=d,A[6]=g,A[7]=f,A[8]=m,A[9]=p,A[10]=x,A[11]=b}setMaskShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u=null){const d=t.highp;e&&u?(d[0]=u.parentMatrixA,d[1]=u.parentMatrixB,d[2]=u.parentMatrixC,d[4]=u.parentMatrixD,d[5]=u.parentMatrixE,d[6]=u.parentMatrixF,d[8]=u.parentMatrixG,d[9]=u.parentMatrixH,d[10]=u.parentMatrixI,d[12]=u.ancestorMatrixA,d[13]=u.ancestorMatrixB,d[14]=u.ancestorMatrixC,d[16]=u.ancestorMatrixD,d[17]=u.ancestorMatrixE,d[18]=u.ancestorMatrixF,d[20]=u.ancestorMatrixG,d[21]=u.ancestorMatrixH,d[22]=u.ancestorMatrixI,d[3]=c,d[7]=$,d[11]=u.parentViewportX,d[15]=u.parentViewportY,d[19]=u.parentViewportW,d[23]=u.parentViewportH,d[24]=u.minXST,d[25]=u.minYST,d[26]=u.minXPQ,d[27]=u.minYPQ,d[28]=u.maxXST,d[29]=u.maxYST,d[30]=u.maxXPQ,d[31]=u.maxYPQ):(d[0]=i,d[1]=s,d[2]=r,d[4]=n,d[5]=a,d[6]=h,d[8]=o,d[9]=_,d[10]=l,d[3]=c,d[7]=$)}setMaskShapeUniformIdentity(t,e,i){const s=t.highp;s[0]=1,s[1]=0,s[2]=0,s[4]=0,s[5]=1,s[6]=0,s[8]=0,s[9]=0,s[10]=1,s[3]=e,s[7]=i}}class Lt{static TEMPLATE(t,e,i,s,r){const n=i?this.STATEMENT_GRADIENT_TYPE_RADIAL(e,s):this.STATEMENT_GRADIENT_TYPE_LINEAR(e);let a;switch(r){case"reflect":a="1.0 - abs(fract(t * 0.5) * 2.0 - 1.0)";break;case"repeat":a="fract(t)";break;default:a="clamp(t, 0.0, 1.0)"}return`#version 300 es\nprecision highp float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_highp[${t}];\n\nin vec2 v_uv;\nout vec4 o_color;\n\nvoid main() {\n vec2 p = v_uv;\n ${n}\n t = ${a};\n o_color = texture(u_texture, vec2(t, 0.5));\n}\n\n`}static STATEMENT_GRADIENT_TYPE_LINEAR(t){return`\n vec2 a = u_highp[${t}].xy;\n vec2 b = u_highp[${t}].zw;\n\n vec2 ab = b - a;\n vec2 ap = p - a;\n\n float t = dot(ab, ap) / dot(ab, ab);\n`}static STATEMENT_GRADIENT_TYPE_RADIAL(t,e){return`\n float radius = u_highp[${t}][0];\n\n vec2 coord = p / radius;\n ${e?this.STATEMENT_FOCAL_POINT_ON(t):this.STATEMENT_FOCAL_POINT_OFF()}\n`}static STATEMENT_FOCAL_POINT_OFF(){return"\n float t = length(coord);\n"}static STATEMENT_FOCAL_POINT_ON(t){return`\n vec2 focal = vec2(u_highp[${t}][1], 0.0);\n\n vec2 dir = normalize(coord - focal);\n\n float a = dot(dir, dir);\n float b = 2.0 * dot(dir, focal);\n float c = dot(focal, focal) - 1.0;\n float x = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);\n\n float t = distance(focal, coord) / distance(focal, focal + dir * x);\n`}}class Ot{constructor(t,e){this._$context=t,this._$gl=e,this._$collection=V()}getGradientShapeShader(t,e,i,s,r){const n=this.createCollectionKey(t,e,i,s,r);if(this._$collection.has(n)){const t=this._$collection.get(n);if(t)return t}const a=(e?13:5)+(t?1:0)+1,h=a-1;let o;o=t?Rt.TEMPLATE(a,h,!0,e):It.TEMPLATE(a,!0,!1,e);const _=new Bt(this._$gl,this._$context,o,Lt.TEMPLATE(a,h,i,s,r));return this._$collection.set(n,_),_}createCollectionKey(t,e,i,s,r){const n=t?"y":"n",a=e?"y":"n",h=i?"y":"n",o=i&&s?"y":"n";let _=0;switch(r){case"reflect":_=1;break;case"repeat":_=2}return`${n}${a}${h}${o}${_}`}setGradientShapeUniform(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.highp;d[0]=a[0],d[1]=a[1],d[2]=a[2],d[4]=a[3],d[5]=a[4],d[6]=a[5],d[8]=a[6],d[9]=a[7],d[10]=a[8],d[12]=h[0],d[13]=h[1],d[14]=h[2],d[16]=h[3],d[17]=h[4],d[18]=h[5],d[11]=h[6],d[15]=h[7],d[19]=h[8],d[3]=o,d[7]=_;let g=20;n&&(d[g]=l.parentMatrixA,d[g+1]=l.parentMatrixB,d[g+2]=l.parentMatrixC,d[g+4]=l.parentMatrixD,d[g+5]=l.parentMatrixE,d[g+6]=l.parentMatrixF,d[g+8]=l.parentMatrixG,d[g+9]=l.parentMatrixH,d[g+10]=l.parentMatrixI,d[g+12]=l.ancestorMatrixA,d[g+13]=l.ancestorMatrixB,d[g+14]=l.ancestorMatrixC,d[g+16]=l.ancestorMatrixD,d[g+17]=l.ancestorMatrixE,d[g+18]=l.ancestorMatrixF,d[g+20]=l.ancestorMatrixG,d[g+21]=l.ancestorMatrixH,d[g+22]=l.ancestorMatrixI,d[g+11]=l.parentViewportX,d[g+15]=l.parentViewportY,d[g+19]=l.parentViewportW,d[g+23]=l.parentViewportH,d[g+24]=l.minXST,d[g+25]=l.minYST,d[g+26]=l.minXPQ,d[g+27]=l.minYPQ,d[g+28]=l.maxXST,d[g+29]=l.maxYST,d[g+30]=l.maxXPQ,d[g+31]=l.maxYPQ,g=52),e&&(d[g]=i,d[g+1]=s,d[g+2]=r,g+=4),c?(d[g]=$[5],d[g+1]=u):(d[g]=$[0],d[g+1]=$[1],d[g+2]=$[2],d[g+3]=$[3])}}class Ut{static TEXTURE(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 position = a_vertex * 2.0 - 1.0;\n gl_Position = vec4(position, 0.0, 1.0);\n}\n\n"}static BLEND(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[4];\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 offset = u_highp[0].xy;\n vec2 size = u_highp[0].zw;\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * size + offset;\n position = (matrix * vec3(position, 1.0)).xy;\n position /= viewport;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static INSTANCE_BLEND(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[5];\n\nout vec2 v_src_coord;\nout vec2 v_dst_coord;\n\nvoid main() {\n vec4 rect = vec4(u_highp[0].x, u_highp[0].y, u_highp[0].z, u_highp[0].w);\n vec2 size = vec2(u_highp[4].x, u_highp[4].y);\n mat3 matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n v_src_coord = a_vertex * rect.zw + rect.xy;\n v_dst_coord = a_vertex;\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * size;\n position = (matrix * vec3(position, 1.0)).xy;\n position /= viewport;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static INSTANCE(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\nlayout (location = 1) in vec4 a_rect;\nlayout (location = 2) in vec4 a_size;\nlayout (location = 3) in vec2 a_offset;\nlayout (location = 4) in vec4 a_matrix;\nlayout (location = 5) in vec4 a_mul;\nlayout (location = 6) in vec4 a_add;\n\nout vec2 v_coord;\nout vec4 mul;\nout vec4 add;\n\nvoid main() {\n v_coord = a_vertex * a_rect.zw + a_rect.xy;\n mul = a_mul;\n add = a_add;\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position = position * a_size.xy;\n mat3 matrix = mat3(a_matrix.x, a_matrix.y, 0.0, a_matrix.z, a_matrix.w, 0.0, a_offset.x, a_offset.y, 1.0);\n position = (matrix * vec3(position, 1.0)).xy;\n position /= a_size.zw;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}static BLEND_CLIP(){return"#version 300 es\n\nlayout (location = 0) in vec2 a_vertex;\n\nuniform vec4 u_highp[4];\n\nout vec2 v_coord;\n\nvoid main() {\n v_coord = a_vertex;\n\n vec2 offset = u_highp[0].xy;\n vec2 size = u_highp[0].zw;\n mat3 inv_matrix = mat3(u_highp[1].xyz, u_highp[2].xyz, u_highp[3].xyz);\n vec2 viewport = vec2(u_highp[1].w, u_highp[2].w);\n\n vec2 position = vec2(a_vertex.x, 1.0 - a_vertex.y);\n position *= viewport;\n position = (inv_matrix * vec3(position, 1.0)).xy;\n position = (position - offset) / size;\n\n position = position * 2.0 - 1.0;\n gl_Position = vec4(position.x, -position.y, 0.0, 1.0);\n}\n\n"}}class Dt{static TEMPLATE(t,e,i){let s="";for(let t=1;t>16)/255,h[a++]=(e>>8&255)/255,h[a++]=(255&e)/255,h[a++]=s[t]}for(let t=r;tthis._$vertexBufferData.length){const t=new o(2*this._$vertexBufferData.length);t.set(this._$vertexBufferData),this._$vertexBufferData=t}}static _$expandIndexBufferIfNeeded(t){if(this._$indexBufferPos+t>this._$indexBufferData.length){const t=new l(2*this._$indexBufferData.length);t.set(this._$indexBufferData),this._$indexBufferData=t}}static _$generateLineSegment(t){const e=t.length-5;for(let i=0;it*s-i*e;class ee{constructor(t){this._$gl=t,this._$fillVertexArrayPool=[],this._$strokeVertexArrayPool=[],this._$boundVertexArray=null,this._$fillAttrib_vertex=0,this._$fillAttrib_bezier=1,this._$strokeAttrib_vertex=0,this._$strokeAttrib_option1=1,this._$strokeAttrib_option2=2,this._$strokeAttrib_type=3,this._$vertexBufferData=new Float32Array([0,0,0,1,1,0,1,1]),this._$attributeVertexBuffer=t.createBuffer(),this._$attributeBuffer=new Float32Array(22),this._$instanceVertexArray=this._$getCommonVertexArray(),this._$commonVertexArray=this._$getVertexArray(0,1)}_$getCommonVertexArray(){const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,this._$attributeVertexBuffer),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(1,4,this._$gl.FLOAT,!1,88,0),this._$gl.vertexAttribDivisor(1,1),this._$gl.enableVertexAttribArray(2),this._$gl.vertexAttribPointer(2,4,this._$gl.FLOAT,!1,88,16),this._$gl.vertexAttribDivisor(2,1),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(3,2,this._$gl.FLOAT,!1,88,32),this._$gl.vertexAttribDivisor(3,1),this._$gl.enableVertexAttribArray(4),this._$gl.vertexAttribPointer(4,4,this._$gl.FLOAT,!1,88,40),this._$gl.vertexAttribDivisor(4,1),this._$gl.enableVertexAttribArray(5),this._$gl.vertexAttribPointer(5,4,this._$gl.FLOAT,!1,88,56),this._$gl.vertexAttribDivisor(5,1),this._$gl.enableVertexAttribArray(6),this._$gl.vertexAttribPointer(6,4,this._$gl.FLOAT,!1,88,72),this._$gl.vertexAttribDivisor(6,1),t}_$getVertexArray(t,e){const i=this._$gl.createVertexArray();this.bind(i);const s=this._$gl.createBuffer();return this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s),this._$vertexBufferData[0]=t,this._$vertexBufferData[2]=t,this._$vertexBufferData[4]=e,this._$vertexBufferData[6]=e,this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$vertexBufferData,this._$gl.STATIC_DRAW),this._$gl.enableVertexAttribArray(0),this._$gl.vertexAttribPointer(0,2,this._$gl.FLOAT,!1,0,0),i}_$getFillVertexArray(){if(this._$fillVertexArrayPool.length){const t=this._$fillVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();return t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.vertexAttribPointer(this._$fillAttrib_vertex,2,this._$gl.FLOAT,!1,16,0),this._$gl.vertexAttribPointer(this._$fillAttrib_bezier,2,this._$gl.FLOAT,!1,16,8),t}_$getStrokeVertexArray(){if(this._$strokeVertexArrayPool.length){const t=this._$strokeVertexArrayPool.pop();if(t)return t}const t=this._$gl.createVertexArray();this.bind(t);const e=this._$gl.createBuffer();t.vertexBuffer=e,t.vertexLength=0,this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,e);const i=this._$gl.createBuffer();return t.indexBuffer=i,t.indexLength=0,this._$gl.bindBuffer(this._$gl.ELEMENT_ARRAY_BUFFER,i),this._$gl.enableVertexAttribArray(0),this._$gl.enableVertexAttribArray(1),this._$gl.enableVertexAttribArray(2),this._$gl.enableVertexAttribArray(3),this._$gl.vertexAttribPointer(this._$strokeAttrib_vertex,2,this._$gl.FLOAT,!1,28,0),this._$gl.vertexAttribPointer(this._$strokeAttrib_option1,2,this._$gl.FLOAT,!1,28,8),this._$gl.vertexAttribPointer(this._$strokeAttrib_option2,2,this._$gl.FLOAT,!1,28,16),this._$gl.vertexAttribPointer(this._$strokeAttrib_type,1,this._$gl.FLOAT,!1,28,24),t}createFill(t){const e=Zt.generate(t),i=e.vertexBufferData,s=this._$getFillVertexArray();return s.indexRanges=e.indexRanges,this.bind(s),this._$gl.bindBuffer(this._$gl.ARRAY_BUFFER,s.vertexBuffer),s.vertexLengththis._$attributeBuffer.length&&(this._$attributeBuffer=new Float32Array(t.attributes.length),this._$gl.bufferData(this._$gl.ARRAY_BUFFER,this._$attributeBuffer.byteLength,this._$gl.DYNAMIC_DRAW)),this._$attributeBuffer.set(t.attributes),this._$gl.bufferSubData(this._$gl.ARRAY_BUFFER,0,this._$attributeBuffer.subarray(0,t.attributes.length))}bindCommonVertexArray(){this.bind(this._$commonVertexArray)}bindGradientVertexArray(t,e){const i=this._$getVertexArray(t,e);this.bind(i)}}class ie{constructor(t,e){this._$context=t,this._$gl=e,this._$clips=[],this._$poolClip=[],this._$clipStatus=!1,this._$containerClip=!1,this._$currentClip=!1}get containerClip(){return this._$containerClip}set containerClip(t){this._$containerClip=t}_$onClear(t){t&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0)}_$onBind(t){!t&&this._$currentClip?(this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1):t&&!this._$currentClip&&(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0,this._$endClipDef())}_$onClearRect(){this._$gl.disable(this._$gl.STENCIL_TEST),this._$currentClip=!1}_$enterClip(){this._$currentClip||(this._$gl.enable(this._$gl.STENCIL_TEST),this._$currentClip=!0);const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");t.mask=!0,++t.clipLevel}_$beginClipDef(){const t=this._$context.frameBuffer.currentAttachment;if(!t)throw new Error("mask currentAttachment is null.");this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.stencilMask(1<7&&(this._$unionStencilMask(e,a,h),n=e)}n>e+1&&this._$unionStencilMask(e,a,h)}_$unionStencilMask(t,e,i){const s=this._$context.path.createRectVertices(0,0,e,i),r=this._$context.vao.createFill(s);D(s.pop()),D(s);const n=this._$context.shaderList.shapeShaderVariants,a=n.getMaskShapeShader(!1,!1),h=a.uniform;n.setMaskShapeUniformIdentity(h,e,i);const o=r.indexRanges[0];this._$gl.stencilFunc(this._$gl.LEQUAL,1<this._$maxTextureSize?this._$maxTextureSize/i:1}drawInstacedArray(){this._$blend.drawInstacedArray()}clearInstacedArray(){this._$blend.clearInstacedArray()}bindRenderBuffer(t){this._$frameBufferManager.bindRenderBuffer(),this._$gl.clearColor(0,0,0,0),this._$gl.clear(this._$gl.COLOR_BUFFER_BIT|this._$gl.STENCIL_BUFFER_BIT),this._$viewportWidth=t.w,this._$viewportHeight=t.h,this._$gl.viewport(t.x,t.y,t.w,t.h),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(t.x,t.y,t.w,t.h)}getTextureFromRect(t){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t.index),s=e.currentAttachment,r=e.createTextureAttachment(t.w,t.h);this._$bind(r),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(i,-t.x,-i.height+t.h+t.y,i.width,i.height),this.restore();const n=r.texture;return e.releaseAttachment(r),this._$bind(s),n}drawBitmap(t){const e=this._$shaderList.blendShaderVariants,i=e.getNormalBlendShader(!1);e.setNormalBlendUniform(i.uniform,0,0,t.width,t.height,this._$matrix,this._$viewportWidth,this._$viewportHeight,!1,1,1,1,1,0,0,0,0),this._$frameBufferManager.textureManager.bind0(t,this._$imageSmoothingEnabled),this._$blend.toOperation("normal"),i._$drawImage()}drawTextureFromRect(t,e){const i=this._$frameBufferManager,s=i.currentAttachment;this.bindRenderBuffer(e),i.transferTexture(e);const r=i.textureManager.getAtlasTexture(e.index),n=i.createTextureAttachmentFrom(r);this._$bind(n),this._$gl.enable(this._$gl.SCISSOR_TEST),this._$gl.scissor(e.x,e.y,e.w,e.h),this._$gl.clearColor(0,0,0,0),this._$gl.disable(this._$gl.SCISSOR_TEST),this.save(),this.setTransform(1,0,0,1,0,0),this.reset(),this.drawImage(t,e.x,r.height-e.h-e.y,t.width,t.height),this.restore(),i.releaseAttachment(n),this._$bind(s),i.textureManager.release(t)}stopStencil(){this._$mask._$onClearRect()}_$bind(t=null){if(!t)return;this._$frameBufferManager.bind(t);const e=t.color,i=t.stencil,s=t.width,r=t.height;this._$viewportWidth===s&&this._$viewportHeight===r||(this._$viewportWidth=s,this._$viewportHeight=r,this._$gl.viewport(0,0,s,r)),(e&&e.dirty||i&&i.dirty)&&(e&&(e.dirty=!1),i&&(i.dirty=!1),this._$gl.clearColor(0,0,0,0),this.clearRect(0,0,this._$viewportWidth,this._$viewportHeight),this._$gl.clearColor(this._$clearColorR,this._$clearColorG,this._$clearColorB,this._$clearColorA),this._$mask._$onClear(t.mask)),this._$mask._$onBind(t.mask)}setTransform(t,e,i,s,r,n){this._$matrix[0]=t,this._$matrix[1]=e,this._$matrix[3]=i,this._$matrix[4]=s,this._$matrix[6]=r,this._$matrix[7]=n}setMaxSize(t,e){this._$frameBufferManager.setMaxSize(t,e)}transform(t,e,i,s,r,n){const a=this._$matrix[0],h=this._$matrix[1],o=this._$matrix[3],_=this._$matrix[4],l=this._$matrix[6],c=this._$matrix[7];this._$matrix[0]=t*a+e*o,this._$matrix[1]=t*h+e*_,this._$matrix[3]=i*a+s*o,this._$matrix[4]=i*h+s*_,this._$matrix[6]=r*a+n*o+l,this._$matrix[7]=r*h+n*_+c}debug(t=0){const e=this._$frameBufferManager,i=e.textureManager.getAtlasTexture(t),s=e.currentAttachment,r=e.createTextureAttachmentFrom(i);this._$bind(r);const n=new Uint8Array(i.width*i.height*4);this._$gl.readPixels(0,0,i.width,i.height,this._$gl.RGBA,this._$gl.UNSIGNED_BYTE,n);const a=document.createElement("canvas");a.width=i.width,a.height=i.height;const h=a.getContext("2d"),o=new ImageData(i.width,i.height);for(let t=0;ts.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this.fillStyle;let r,n,a,h=this._$matrix;const o=this._$grid.enabled;if(s instanceof mt){const t=s.stops,e="linearRGB"===s.rgb;if(r=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(r,!0),this._$frameBufferManager.bindRenderBuffer(),n=this._$shaderList.gradientShapeShaderVariants,"linear"===s.type)a=n.getGradientShapeShader(!1,o,!1,!1,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,s.points,0);else{h=this._$stack[this._$stack.length-1];const t=0!==s.focalPointRatio;a=n.getGradientShapeShader(!1,o,!0,t,s.mode),n.setGradientShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,s.points,s.focalPointRatio)}}else if(s instanceof pt){h=this._$stack[this._$stack.length-1];const t=s.colorTransform;r=s.texture,this._$frameBufferManager.textureManager.bind0(r,this._$imageSmoothingEnabled),n=this._$shaderList.shapeShaderVariants,a=n.getBitmapShapeShader(!1,s.repeat,o),t?n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):n.setBitmapShapeUniform(a.uniform,!1,0,0,0,o,h,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,r.width,r.height,1,1,1,this._$globalAlpha,0,0,0,0)}else n=this._$shaderList.shapeShaderVariants,a=n.getSolidColorShapeShader(!1,this._$grid.enabled),n.setSolidColorShapeUniform(a.uniform,!1,0,0,0,o,h,this._$viewportWidth,this._$viewportHeight,this._$grid,s,this._$globalAlpha);const _=this._$shaderList.shapeShaderVariants,l=_.getMaskShapeShader(!1,o);_.setMaskShapeUniform(l.uniform,o,h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],this._$viewportWidth,this._$viewportHeight,this._$grid),this._$gl.enable(this._$gl.STENCIL_TEST),this._$gl.stencilMask(255),this._$gl.enable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.ALWAYS,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.INVERT,this._$gl.INVERT),this._$gl.colorMask(!1,!1,!1,!1),l._$fill(i),this._$gl.disable(this._$gl.SAMPLE_ALPHA_TO_COVERAGE),this._$gl.stencilFunc(this._$gl.NOTEQUAL,0,255),this._$gl.stencilOp(this._$gl.KEEP,this._$gl.ZERO,this._$gl.ZERO),this._$gl.colorMask(!0,!0,!0,!0),a._$fill(i),this._$gl.disable(this._$gl.STENCIL_TEST),this.releaseFillVertexArray(i)}releaseFillVertexArray(t){this._$vao.releaseFill(t);const e=t.indexRanges;for(let t=0;ta.width||i>a.height||0>e&&0>=s+e||0>i&&0>=n+i||(this._$maskBounds.xMin=r.max(0,r.min(this._$maskBounds.xMin,e)),this._$maskBounds.yMin=r.max(0,r.min(this._$maskBounds.yMin,i)),this._$maskBounds.xMax=r.min(a.width,r.min(this._$maskBounds.xMax,s)),this._$maskBounds.yMax=r.min(a.height,r.min(this._$maskBounds.yMax,n)),0))}_$endClipDef(){this._$mask._$endClipDef()}_$leaveClip(){this.drawInstacedArray(),this._$mask._$leaveClip()}_$drawContainerClip(){this._$mask._$drawContainerClip()}closePath(){this._$path.close()}stroke(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createStroke(t,this.lineCap,this.lineJoin);let s=this._$matrix;const n=this.strokeStyle;let a=r.sign(s[0]*s[4]);a>0&&0!==s[1]&&0!==s[3]&&(a=-r.sign(s[1]*s[3]));let h,o,_=.5*this.lineWidth;this._$grid.enabled?(h=r.abs(this._$grid.ancestorMatrixA+this._$grid.ancestorMatrixD),o=r.abs(this._$grid.ancestorMatrixB+this._$grid.ancestorMatrixE)):(h=r.abs(s[0]+s[3]),o=r.abs(s[1]+s[4]));const l=r.min(h,o),c=r.max(h,o);_*=c*(1-.3*r.cos(.5*r.PI*(l/c))),_=r.max(1,_);const $=this._$grid.enabled;let u,d,g;if(n instanceof mt){"radial"===n.type&&(s=this._$stack[this._$stack.length-1]);const t=n.stops,e="linearRGB"===n.rgb;if(u=this._$gradientLUT.generateForShape(t,e),this._$frameBufferManager.textureManager.bind0(u,!0),d=this._$shaderList.gradientShapeShaderVariants,"linear"===n.type)g=d.getGradientShapeShader(!0,$,!1,!1,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!1,n.points,0);else{s=this._$stack[this._$stack.length-1];const t=0!==n.focalPointRatio;g=d.getGradientShapeShader(!0,$,!0,t,n.mode),d.setGradientShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,!0,n.points,n.focalPointRatio)}}else if(n instanceof pt){s=this._$stack[this._$stack.length-1];const t=n.colorTransform;u=n.texture,this._$frameBufferManager.textureManager.bind0(u),d=this._$shaderList.shapeShaderVariants,g=d.getBitmapShapeShader(!0,n.repeat,this._$grid.enabled),t?d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,t[0],t[1],t[2],this._$globalAlpha,t[4]/255,t[5]/255,t[6]/255,0):d.setBitmapShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,z(this._$matrix),this._$viewportWidth,this._$viewportHeight,this._$grid,u.width,u.height,1,1,1,this._$globalAlpha,0,0,0,0)}else d=this._$shaderList.shapeShaderVariants,g=d.getSolidColorShapeShader(!0,this._$grid.enabled),d.setSolidColorShapeUniform(g.uniform,!0,_,a,this.miterLimit,$,s,this._$viewportWidth,this._$viewportHeight,this._$grid,n,this._$globalAlpha);g._$stroke(i),this._$vao.releaseStroke(i)}arc(t,e,i){this._$path.drawCircle(t,e,i)}clip(){const t=this._$path.vertices;if(!t.length)return;const e=U();for(let i=0;is.length||e.push(s)}if(!e.length)return void D(e);const i=this._$vao.createFill(e),s=this._$shaderList.shapeShaderVariants,r=s.getMaskShapeShader(!1,!1),n=r.uniform;s.setMaskShapeUniform(n,!1,this._$matrix[0],this._$matrix[1],this._$matrix[2],this._$matrix[3],this._$matrix[4],this._$matrix[5],this._$matrix[6],this._$matrix[7],this._$matrix[8],this._$viewportWidth,this._$viewportHeight,null),this._$mask._$onClip(i,this._$matrix,this._$viewportWidth,this._$viewportHeight)||(r._$fill(i),this.beginPath())}save(){const t=this._$matrix;this._$stack.push(O(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])),this._$mask._$onSave()}restore(){var t;this._$stack.length&&(t=this._$matrix,M.push(t),this._$matrix=this._$stack.pop()||O()),this._$mask._$onRestore()}createPattern(t,e,i){return new pt(t,e,i)}createLinearGradient(t,e,i,s,r="rgb",n="pad"){return(new mt).linear(t,e,i,s,r,n)}createRadialGradient(t,e,i,s,r,n,a="rgb",h="pad",o=0){return(new mt).radial(t,e,i,s,r,n,a,h,o)}_$applyBlurFilter(t,e,i){const s=this._$frameBufferManager,n=s.currentAttachment;if(!n)throw new Error("the current attachment is null.");const a=n.width,h=n.height;s.textureManager.bind0(t,!0);const o=r.ceil(.5*i),_=1-(o-.5*i),l=1+i,c=this._$shaderList.filterShaderVariants,$=c.getBlurFilterShader(o);c.setBlurFilterUniform($.uniform,a,h,e,_,l),$._$drawImage()}_$applyBitmapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u,d,g=null,f=null,m=null,p=0,x=0,b=0,v=0,T=0,A=0,M=0,y=0){const E=this._$frameBufferManager,C="inner"===$,S=E.currentAttachment,F=E.getTextureFromCurrentAttachment();let B=null;const w=null!==g&&null!==f&&null!==m;let R;null!==g&&null!==f&&null!==m&&(B=this._$gradientLUT.generateForFilter(g,f,m)),C?w&&B?E.textureManager.bind02(t,B,!0):E.textureManager.bind0(t):(R=this._$frameBufferManager.createTextureAttachment(e,i),this._$bind(R),w&&B?E.textureManager.bind012(t,F,B,!0):E.textureManager.bind01(t,F));const I=!(C||"full"===$&&u),P=!(e===h&&i===o&&0===_&&0===l),N=!(1===d),k=this._$shaderList.filterShaderVariants,L=k.getBitmapFilterShader(I,P,c,$,u,N,w);k.setBitmapFilterUniform(L.uniform,e,i,s,r,n,a,h,o,_,l,c,d,p,x,b,v,T,A,M,y,I,P,N,w),C?u?this._$blend.toSourceIn():this._$blend.toSourceAtop():this._$blend.toOneZero(),L._$drawImage(),C||E.releaseAttachment(S,!0)}_$applyColorMatrixFilter(t,e){this._$frameBufferManager.textureManager.bind0(t,!0);const i=this._$shaderList.filterShaderVariants,s=i.getColorMatrixFilterShader();i.setColorMatrixFilterUniform(s.uniform,e),this._$blend.reset(),s._$drawImage()}_$applyConvolutionFilter(t,e,i,s,r,n,a,h,o,_,l,c){const $=t.width,u=t.height,d=this._$frameBufferManager.createTextureAttachment($,u);this._$bind(d),this._$frameBufferManager.textureManager.bind0(t,!0);const g=this._$shaderList.filterShaderVariants,f=g.getConvolutionFilterShader(e,i,a,h);g.setConvolutionFilterUniform(f.uniform,$,u,s,r,n,h,o,_,l,c),this._$blend.reset(),f._$drawImage()}_$applyDisplacementMapFilter(t,e,i,s,r,n,a,h,o,_,l,c,$,u){const d=t.width,g=t.height,f=this._$frameBufferManager.createTextureAttachment(d,g);this._$bind(f),r||(r={x:0,y:0});const m=this._$frameBufferManager.createTextureFromImage(e);this._$frameBufferManager.textureManager.bind01(t,m);const p=this._$shaderList.filterShaderVariants,x=p.getDisplacementMapFilterShader(n,a,_);p.setDisplacementMapFilterUniform(x.uniform,e.width,e.height,i,s,r.x,r.y,h,o,_,l,c,$,u),this._$blend.reset(),x._$drawImage(),this._$frameBufferManager.releaseTexture(m)}_$startLayer(t){this._$positions.push(t),this._$blends.push(this._$isLayer),this._$isLayer=!0}_$endLayer(){const t=this._$positions.pop();t&&B(t),this._$isLayer=!!this._$blends.pop()}_$saveAttachment(t,e,i=!1){this.drawInstacedArray();const s=this._$frameBufferManager;this._$attachmentArray.push(s.currentAttachment),this._$bind(s.createCacheAttachment(t,e,i))}_$restoreAttachment(t=!1){const e=this._$frameBufferManager;e.releaseAttachment(e.currentAttachment,t),this._$bind(this._$attachmentArray.pop())}getCurrentPosition(){return this._$positions[this._$positions.length-1]}textureScale(t,e){const i=r.max(t,e);return i>this._$maxTextureSize?this._$maxTextureSize/i:1}}class ne extends gt{constructor(){super(),this._$recodes=null,this._$maxAlpha=0,this._$canDraw=!1,this._$uniqueKey="",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0),this._$bitmapId=0,this._$mode="shape"}_$clip(t,e){if(!this._$recodes)return;const i=this._$getBounds(),n=q(i,e);B(i);const a=r.ceil(r.abs(n.xMax-n.xMin)),h=r.ceil(r.abs(n.yMax-n.yMin));switch(B(n),!0){case 0===a:case 0===h:case a===-1/0:case h===-1/0:case a===s:case h===s:return}t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),this._$runCommand(t,this._$recodes,null,!0),t.clip()}_$createCacheKey(){if(!this._$recodes)return"";let t=0;for(let e=0;e0&&this._$canApply(a);let T=F(0,m,0,p);if(v&&a)for(let t=0;tM.width||f-T.yMin>M.height)return void B(T);if(0>d+T.xMax||0>f+T.yMax)return void B(T);if(B(T),""===this._$uniqueKey&&(!l&&this._$loaderInfoId>-1&&this._$characterId>-1?this._$uniqueKey=`${this._$loaderInfoId}@${this._$characterId}`:this._$uniqueKey=this._$createCacheKey()),"bitmap"===this._$mode)this._$cacheKeys.length||(this._$cacheKeys=rt.generateKeys(this._$uniqueKey));else if(!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$uniqueKey,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=A.currentAttachment;s&&s.mask&&t.stopStencil();let n=0,a=0;if("shape"===this._$mode){n=r.ceil(r.abs(c.xMax-c.xMin)*x),a=r.ceil(r.abs(c.yMax-c.yMin)*b);const e=t._$getTextureScale(n,a);e<1&&(n*=e,a*=e)}else n=r.ceil(r.abs(c.xMax-c.xMin)),a=r.ceil(r.abs(c.yMax-c.yMin));if(t.cachePosition=A.createCachePosition(n,a),t.bindRenderBuffer(t.cachePosition),t.reset(),"shape"===this._$mode?t.setTransform(x,0,0,b,-c.xMin*x,-c.yMin*b):t.setTransform(1,0,0,1,-c.xMin,-c.yMin),l){const i=le.scaleX,s=P(i,0,0,i,0,0),n=H(s,_);N(s);const a=this._$matrixBase,h=P(a[0],a[1],a[2],a[3],a[4]*i-d,a[5]*i-f),o=H(h,n),l=o[4]-(e[4]-d),$=o[5]-(e[5]-f);N(o);const u=q(c,n),g=+u.xMax,m=+u.xMin,p=+u.yMax,x=+u.yMin,b=r.ceil(r.abs(g-m)),v=r.ceil(r.abs(p-x));B(u),t.grid.enable(m,x,b,v,c,this._$scale9Grid,i,n[0],n[1],n[2],n[3],n[4],n[5],h[0],h[1],h[2],h[3],h[4]-l,h[5]-$),N(n),N(h)}this._$runCommand(t,this._$recodes,i,!1),l&&t.grid.disable(),A.transferTexture(t.cachePosition),rt.set(this._$cacheKeys,t.cachePosition),t._$bind(s)}let y=0,E=0;if(v&&a){const i=this._$createBitmapTexture(t,t.cachePosition,x,b,m,p),s=this._$drawFilter(t,e,a,m,p,i);s.offsetX&&(y=s.offsetX),s.offsetY&&(E=s.offsetY),t.cachePosition=s}if(v||"bitmap"!==this._$mode){const i=r.atan2(e[1],e[0]),s=r.atan2(-e[2],e[3]);if(v||!i&&!s)t.setTransform(1,0,0,1,d-y,f-E);else{const n=c.xMin*x,a=c.yMin*b,h=r.cos(i),o=r.sin(i),_=r.cos(s),l=r.sin(s);t.setTransform(h,o,-l,_,n*h-a*l+e[4],n*o+a*_+e[5])}}else t.setTransform(e[0],e[1],e[2],e[3],c.xMin*e[0]+c.yMin*e[2]+e[4],c.xMin*e[1]+c.yMin*e[3]+e[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled="shape"===this._$mode,t.globalCompositeOperation=n,t.drawInstance(d-y,f-E,u,g,i),t.cachePosition=null),B(c)}setupStroke(t,e,i,s,r){switch(t.lineWidth=e,i){case 0:t.lineCap="none";break;case 1:t.lineCap="round";break;case 2:t.lineCap="square"}switch(s){case 0:t.lineJoin="bevel";break;case 1:t.lineJoin="miter";break;case 2:t.lineJoin="round"}t.miterLimit=r}createGradientStyle(t,e,i,s,n,a,h,o=null){let _,l="pad";switch(n){case 0:l="reflect";break;case 1:l="repeat"}if(0===e){const e=(t=>{const e=-819.2*t[0]-819.2*t[2]+t[4],i=819.2*t[0]-819.2*t[2]+t[4],s=-819.2*t[0]+819.2*t[2]+t[4],n=-819.2*t[1]-819.2*t[3]+t[5],a=819.2*t[1]-819.2*t[3]+t[5];let h=s-e,o=-819.2*t[1]+819.2*t[3]+t[5]-n;const _=r.sqrt(h*h+o*o);_?(h/=_,o/=_):(h=0,o=0);const l=(i-e)*h+(a-n)*o;return w(e+l*h,n+l*o,i,a)})(s);_=t.createLinearGradient(e[0],e[1],e[2],e[3],a?"rgb":"linearRGB",l)}else t.save(),t.transform(s[0],s[1],s[2],s[3],s[4],s[5]),_=t.createRadialGradient(0,0,0,0,0,819.2,a?"rgb":"linearRGB",l,h);for(let t=0;t-1&&this._$characterId>-1&&rt.removeCache(`${this._$loaderInfoId}@${this._$characterId}`))}}class ae extends ne{_$clip(t,e){let i=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(i=H(e,n));const a=this._$getBounds(),h=q(a,i);B(a);const o=r.ceil(r.abs(h.xMax-h.xMin)),_=r.ceil(r.abs(h.yMax-h.yMin));switch(B(h),!0){case 0===o:case 0===_:case o===-1/0:case _===-1/0:case o===s:case _===s:return}super._$clip(t,i),i!==e&&N(i)}_$draw(t,e,i){if(!this._$visible||!this._$maxAlpha||!this._$canDraw)return;let s=i;const r=this._$colorTransform;if(1===r[0]&&1===r[1]&&1===r[2]&&1===r[3]&&0===r[4]&&0===r[5]&&0===r[6]&&0===r[7]||(s=W(i,r)),!G(s[3]+s[7]/255,0,1,0))return void(s!==i&&L(s));let n=e;const a=this._$matrix;1===a[0]&&0===a[1]&&0===a[2]&&1===a[3]&&0===a[4]&&0===a[5]||(n=H(e,a)),super._$draw(t,n,s,this._$blendMode,this._$filters),n!==e&&N(n),s!==i&&L(s)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$recodes=null,super._$remove(),ce.push(this)}}class he extends gt{constructor(){super(),this._$background=!1,this._$backgroundColor=16777215,this._$border=!1,this._$borderColor=0,this._$wordWrap=!1,this._$textData=U(),this._$textAreaActive=!1,this._$thickness=0,this._$thicknessColor=0,this._$limitWidth=0,this._$limitHeight=0,this._$autoSize="none",this._$widthTable=U(),this._$heightTable=U(),this._$objectTable=U(),this._$textHeightTable=U(),this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$maxScrollV=null,this._$scrollV=1,this._$textHeight=0,this._$verticalAlign="top",this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}get width(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.xMax-t.xMin);switch(B(t),!0){case 0===e:case e===s:case e===-1/0:return 0;default:return e}}get height(){const t=q(this._$getBounds(null),this._$matrix),e=r.abs(t.yMax-t.yMin);switch(B(t),e){case 0:case s:case-1/0:return 0;default:return e}}get maxScrollV(){if(null===this._$maxScrollV){this._$maxScrollV=1;const t=this._$textHeightTable.length,e=this.height;if(e>this._$textHeight)return this._$maxScrollV;let i=0,s=0;for(;t>s&&(i+=this._$textHeightTable[s++],!(i>e));)this._$maxScrollV++}return this._$maxScrollV}_$clip(t,e){const i=this._$getBounds(),s=i.xMax,n=i.xMin,a=i.yMax,h=i.yMin;B(i);const o=r.ceil(r.abs(s-n)),_=r.ceil(r.abs(a-h));if(!o||!_)return;let l=e;const c=this._$matrix;1===c[0]&&0===c[1]&&0===c[2]&&1===c[3]&&0===c[4]&&0===c[5]||(l=H(e,c)),t.reset(),t.setTransform(e[0],e[1],e[2],e[3],e[4],e[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(o,0),t.lineTo(o,_),t.lineTo(0,_),t.lineTo(0,0),t.clip(),l!==e&&N(l)}_$draw(t,e,i){if(!this._$visible||this._$textAreaActive)return;if(!this._$background&&!this._$border&&2>this._$textData.length)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1);if(!o)return;let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds(null);c.xMin-=this._$thickness,c.xMax+=this._$thickness,c.yMin-=this._$thickness,c.yMax+=this._$thickness;const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf("e");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf("e");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),this._$isUpdated()&&(rt.removeCache(this._$instanceId),t.cachePosition=null,this._$cacheKeys.length=0),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U(x,b);this._$cacheKeys=rt.generateKeys(this._$instanceId,t),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const s=r.min(1,r.max(x,b)),a=r.ceil(r.abs(c.xMax-c.xMin)*x),h=r.ceil(r.abs(c.yMax-c.yMin)*b);n[3]=1;const o=new OffscreenCanvas(a+2*s,h+2*s).getContext("2d");if(!o)return;if(this._$background||this._$border){if(o.beginPath(),o.moveTo(0,0),o.lineTo(a,0),o.lineTo(a,h),o.lineTo(0,h),o.lineTo(0,0),this._$background){const t=Z(this._$backgroundColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.fillStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.fill()}if(this._$border){const t=Z(this._$borderColor),e=r.max(0,r.min(255*t.A*i[3]+i[7],255))/255;o.lineWidth=s,o.strokeStyle=`rgba(${t.R},${t.G},${t.B},${e})`,o.stroke()}}o.save(),o.beginPath(),o.moveTo(2,2),o.lineTo(a-2,2),o.lineTo(a-2,h-2),o.lineTo(2,h-2),o.lineTo(2,2),o.clip(),o.beginPath(),o.setTransform(x,0,0,b,0,0),this._$doDraw(o,e,i,a/x),o.restore();const _=M.createCachePosition(m,p),l=M.createTextureFromCanvas(o.canvas);t.drawTextureFromRect(l,_),t.cachePosition=_,rt.set(this._$cacheKeys,_)}let E=!1,C=0,S=0;if(v&&v.length&&this._$canApply(v)){E=!0;const e=this._$drawFilter(t,_,v,m,p);e.offsetX&&(C=e.offsetX),e.offsetY&&(S=e.offsetY),t.cachePosition=e}const w=r.atan2(_[1],_[0]),R=r.atan2(-_[2],_[3]);if(E||!w&&!R)t.setTransform(1,0,0,1,d-C,f-S);else{const e=c.xMin*x,i=c.yMin*b,s=r.cos(w),n=r.sin(w),a=r.cos(R),h=r.sin(R);t.setTransform(s,n,-h,a,e*s-i*h+_[4],e*n+i*a+_[5])}t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),B(c),_!==e&&N(_),n!==i&&L(n)}_$doDraw(t,e,i,s){const n=this.width,a=this.height;let h=0,o=0,_=0,l=0;if("top"!==this._$verticalAlign&&this.height>this._$textHeight)switch(this._$verticalAlign){case"middle":l=(this.height-this._$textHeight+2)/2;break;case"bottom":l=this.height-this._$textHeight+2}const c=this._$textData.length;for(let $=0;$a||u>n))continue;const d=c.textFormat,g=Z(c.textFormat._$color),f=r.max(0,r.min(255*g.A*i[3]+i[7],255))/255;if(t.fillStyle=`rgba(${g.R},${g.G},${g.B},${f})`,this._$thickness){const e=Z(this._$thicknessColor),s=r.max(0,r.min(255*e.A*i[3]+i[7],255))/255;t.lineWidth=this._$thickness,t.strokeStyle=`rgba(${e.R},${e.G},${e.B},${s})`}const m=c.yIndex;switch(c.mode){case"break":case"wrap":if(_++,this._$scrollV>_)continue;if(o+=this._$textHeightTable[m],h=this._$getAlignOffset(this._$objectTable[m],s),d._$underline){const s=c.textFormat._$size/12,n=Z(d._$color),a=r.max(0,r.min(255*n.A*i[3]+i[7],255))/255;t.lineWidth=r.max(1,1/r.min(e[0],e[3])),t.strokeStyle=`rgba(${n.R},${n.G},${n.B},${a})`,t.beginPath(),t.moveTo(h,l+o-s),t.lineTo(h+this._$widthTable[m],l+o-s),t.stroke()}break;case"text":{if(this._$scrollV>_)continue;let e=o-this._$heightTable[0];_e||(e+=c.textFormat._$size/12*2),t.beginPath(),t.textBaseline="top",t.font=tt(d._$font,d._$size,d._$italic,d._$bold),this._$thickness&&t.strokeText(c.text,u,l+e),t.fillText(c.text,u,l+e)}break;case"image":if(!c.loaded)continue;t.beginPath(),t.drawImage(c.image,c.hspace,l+c.y,c.width,c.height)}}}_$getAlignOffset(t,e){const i=this._$widthTable[t.yIndex],s=t.textFormat,n=s._$blockIndent+s._$leftMargin>0?s._$blockIndent+s._$leftMargin:0;switch(!0){case!this._$wordWrap&&i>e:return r.max(0,n);case"center"===s._$align:case"center"===this._$autoSize:return r.max(0,e/2-n-s._$rightMargin-i/2);case"right"===s._$align:case"right"===this._$autoSize:return r.max(0,e-n-i-s._$rightMargin-2);default:return r.max(0,n+2)}}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textAreaActive=!1,super._$remove(),$e.push(this)}_$updateProperty(t){this._$textAreaActive=!!t.textAreaActive,this._$textData.length=0,this._$widthTable.length=0,this._$heightTable.length=0,this._$objectTable.length=0,this._$textHeightTable.length=0,this._$textData.push(...t.textData),this._$widthTable.push(...t.widthTable),this._$heightTable.push(...t.heightTable),this._$objectTable.push(...t.objectTable),this._$textHeightTable.push(...t.textHeightTable),this._$wordWrap=t.wordWrap,this._$limitWidth=t.limitWidth,this._$limitHeight=t.limitHeight,this._$autoSize=t.autoSize,this._$scrollV=t.scrollV,this._$textHeight=t.textHeight,this._$verticalAlign=t.verticalAlign,this._$border=t.border,this._$border&&(this._$borderColor=t.borderColor),this._$background=t.background,this._$background&&(this._$backgroundColor=t.backgroundColor),"thickness"in t&&(this._$thickness=t.thickness,this._$thicknessColor=t.thicknessColor)}_$update(t){super._$update(t),this._$textAreaActive=!!t.textAreaActive,this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,t.textData&&this._$updateProperty(t)}}class oe extends gt{constructor(){super(),this._$imageBitmap=null,this._$context=null,this._$smoothing=!0,this._$cacheKeys=U(),this._$cacheParams=U(0,0,0)}_$clip(t,e){const i=this._$xMax,s=this._$yMax;if(!i||!s)return;let r=e;const n=this._$matrix;1===n[0]&&0===n[1]&&0===n[2]&&1===n[3]&&0===n[4]&&0===n[5]||(r=H(e,n)),t.reset(),t.setTransform(r[0],r[1],r[2],r[3],r[4],r[5]),t.beginPath(),t.moveTo(0,0),t.lineTo(i,0),t.lineTo(i,s),t.lineTo(0,s),t.lineTo(0,0),t.clip(),r!==e&&N(r)}_$draw(t,e,i){if(!this._$visible||!this._$imageBitmap||!this._$context)return;let n=i;const a=this._$colorTransform;1===a[0]&&1===a[1]&&1===a[2]&&1===a[3]&&0===a[4]&&0===a[5]&&0===a[6]&&0===a[7]||(n=W(i,a));const o=G(n[3]+n[7]/255,0,1,0);if(!o)return void(n!==i&&L(n));let _=e;const l=this._$matrix;1===l[0]&&0===l[1]&&0===l[2]&&1===l[3]&&0===l[4]&&0===l[5]||(_=H(e,l));const c=this._$getBounds();B(c);const $=q(c,_),u=+$.xMax,d=+$.xMin,g=+$.yMax,f=+$.yMin;B($);const m=r.ceil(r.abs(u-d)),p=r.ceil(r.abs(g-f));switch(!0){case 0===m:case 0===p:case m===-1/0:case p===-1/0:case m===s:case p===s:return}let x=+r.sqrt(_[0]*_[0]+_[1]*_[1]);if(!h.isInteger(x)){const t=x.toString(),e=t.indexOf("e");-1!==e&&(x=+t.slice(0,e)),x=+x.toFixed(4)}let b=+r.sqrt(_[2]*_[2]+_[3]*_[3]);if(!h.isInteger(b)){const t=b.toString(),e=t.indexOf("e");-1!==e&&(b=+t.slice(0,e)),b=+b.toFixed(4)}const v=this._$filters,T=null!==v&&v.length>0&&this._$canApply(v);let A=F(0,m,0,p);if(T&&v)for(let t=0;ty.width||f-A.yMin>y.height)return void B(A);if(0>d+A.xMax||0>f+A.yMax)return void B(A);if(B(A),!this._$cacheKeys.length||this._$cacheParams[0]!==x||this._$cacheParams[1]!==b||this._$cacheParams[2]!==i[7]){const t=U();t[0]=x,t[1]=b,this._$cacheKeys=rt.generateKeys(this._$instanceId,t,i),D(t),this._$cacheParams[0]=x,this._$cacheParams[1]=b,this._$cacheParams[2]=i[7]}if(t.cachePosition=rt.get(this._$cacheKeys),!t.cachePosition){const e=r.ceil(r.abs(this._$xMax-this._$xMin)),i=r.ceil(r.abs(this._$yMax-this._$yMin)),s=M.createCachePosition(e,i);t.cachePosition=s,rt.set(this._$cacheKeys,s)}this._$context.drawImage(this._$imageBitmap,0,0);const E=M.textureManager._$createFromElement(this._$imageBitmap.width,this._$imageBitmap.height,this._$context.canvas,this._$smoothing);let C=0,S=0;if(T&&v){const e=M.currentAttachment,i=M.createCacheAttachment(m,p);t._$bind(i),t.reset();const s=P(x,0,0,b,m/2,p/2),r=P(1,0,0,1,-E.width/2,-E.height/2),n=H(s,r);N(s),N(r),t.setTransform(n[0],n[1],n[2],n[3],n[4],n[5]),t.drawImage(E,0,0,E.width,E.height);const a=M.getTextureFromCurrentAttachment();t._$bind(e),M.releaseAttachment(i),t.drawTextureFromRect(E,t.cachePosition);const h=this._$drawFilter(t,_,v,m,p,a);h.offsetX&&(C=h.offsetX),h.offsetY&&(S=h.offsetY),t.cachePosition=h,t.setTransform(1,0,0,1,d-C,f-S)}else t.drawTextureFromRect(E,t.cachePosition),t.setTransform(_[0],_[1],_[2],_[3],_[4],_[5]);t.cachePosition&&(t.globalAlpha=o,t.imageSmoothingEnabled=!0,t.globalCompositeOperation=this._$blendMode,t.drawInstance(d-C,f-S,u,g,i),t.cachePosition=null),_!==e&&N(_),n!==i&&L(n)}_$remove(){this._$xMin=0,this._$yMin=0,this._$xMax=0,this._$yMax=0,this._$context=null,this._$imageBitmap=null,this._$smoothing=!0,super._$remove(),de.push(this)}_$updateProperty(t){if(this._$xMin=t.xMin,this._$yMin=t.yMin,this._$xMax=t.xMax,this._$yMax=t.yMax,this._$imageBitmap=t.imageBitmap,this._$smoothing=t.smoothing,!this._$context&&this._$imageBitmap){const t=new c(this._$imageBitmap.width,this._$imageBitmap.height);this._$context=t.getContext("2d")}}_$update(t){super._$update(t),this._$updateProperty(t)}}let _e=!1;const le=new class{constructor(){this._$instances=new Map,this._$matrix=P(1,0,0,1,0,0),this._$width=0,this._$height=0,this._$stage=new ft,this._$canvas=null,this._$context=null,this._$attachment=null}get instances(){return this._$instances}get context(){return this._$context}get scaleX(){return this._$matrix[0]}stop(){rt.reset()}_$initialize(e,i){let s=0;var r,n;this._$setStage(e[s++]),n=1===e[s++],_e=n,r=e[s++],t=r,this._$canvas=i;const a=i.getContext("webgl2",{stencil:!0,premultipliedAlpha:!0,antialias:!1,depth:!1,preserveDrawingBuffer:!0});if(a){const t=new re(a,e[s++]);this._$context=t,rt.context=t}}_$setBackgroundColor(t){if(!this._$context)return;const e=t[0];if(-1===e)this._$context._$setColor(0,0,0,0);else{const t={A:(i=e)>>>24,R:(16711680&i)>>16,G:(65280&i)>>8,B:255&i};this._$context._$setColor(t.R/255,t.G/255,t.B/255,1)}var i}_$bitmapDraw(t,e,i,s){const r=this._$context;if(!r)return;r._$bind(this._$attachment),r.reset(),r.setTransform(1,0,0,1,0,0),r.clearRect(0,0,this._$width,this._$height),r.beginPath(),t._$draw(r,e,i),r.frameBuffer.transferToMainTexture();const n=s.getContext("2d");n&&this._$canvas&&n.drawImage(this._$canvas,0,0)}_$draw(){if(!this._$width||!this._$height)return;const t=this._$context;t&&(t.reset(),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,this._$width,this._$height),t.beginPath(),this._$stage._$draw(t,this._$matrix,m),this._$stage._$updated=!1,t.drawInstacedArray(),t.frameBuffer.transferToMainTexture())}_$resize(t){let e=0;const i=t[e++],s=t[e++];if(this._$width=i,this._$height=s,!this._$canvas)return;if(this._$canvas.width===i&&this._$canvas.height===s)return;const r=this._$context;if(!r)return;const n=t[e++];this._$matrix[0]=n,this._$matrix[3]=n,this._$matrix[4]=t[e++],this._$matrix[5]=t[e++],this._$stage._$updated=!0,rt.reset(),r.clearInstacedArray(),this._$canvas.width=i,this._$canvas.height=s,r._$gl.viewport(0,0,i,s);const a=r.frameBuffer;this._$attachment&&(a.unbind(),a.releaseAttachment(this._$attachment,!0)),this._$attachment=a.createCacheAttachment(i,s,!0),r.setMaxSize(i,s),r._$bind(this._$attachment)}_$setStage(t){this._$stage._$instanceId=t,this._$instances.set(t,this._$stage)}_$updateStage(){this._$stage._$updated=!0}_$createDisplayObjectContainer(t){const e=ge();let i=0;e._$instanceId=t[i++],e._$parentId=t[i++],this._$setProperty(e,t,2),this._$instances.set(e._$instanceId,e)}_$setProperty(t,e,i){t._$visible=1===e[i++],t._$depth=e[i++],t._$clipDepth=e[i++],t._$isMask=1===e[i++],1===e[i++]?(t._$maskId=e[i++],t._$maskMatrix||(t._$maskMatrix=P()),t._$maskMatrix[0]=e[i++],t._$maskMatrix[1]=e[i++],t._$maskMatrix[2]=e[i++],t._$maskMatrix[3]=e[i++],t._$maskMatrix[4]=e[i++],t._$maskMatrix[5]=e[i++]):(t._$maskId=-1,t._$maskMatrix&&(N(t._$maskMatrix),t._$maskMatrix=null),i+=7),t._$visible?(t._$matrix[0]=e[i++],t._$matrix[1]=e[i++],t._$matrix[2]=e[i++],t._$matrix[3]=e[i++],t._$matrix[4]=e[i++],t._$matrix[5]=e[i++],t._$colorTransform[0]=e[i++],t._$colorTransform[1]=e[i++],t._$colorTransform[2]=e[i++],t._$colorTransform[3]=e[i++],t._$colorTransform[4]=e[i++],t._$colorTransform[5]=e[i++],t._$colorTransform[6]=e[i++],t._$colorTransform[7]=e[i++]):(i+=6,i+=8),t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null,t._$blendMode=st(e[i++]),e[i++]?t._$scale9Grid={x:e[i++],y:e[i++],w:e[i++],h:e[i++]}:t._$scale9Grid=null}_$registerShapeRecodes(t,e){this._$instances.has(t)||this._$instances.set(t,pe()),this._$instances.get(t)._$recodes=e}_$createShape(t){let e=0;const i=t[e++];this._$instances.has(i)||this._$instances.set(i,pe());const s=this._$instances.get(i);s._$instanceId=i,s._$parentId=t[e++],s._$maxAlpha=t[e++],s._$canDraw=1===t[e++],s._$xMin=t[e++],s._$yMin=t[e++],s._$xMax=t[e++],s._$yMax=t[e++],s._$characterId=t[e++],s._$loaderInfoId=t[e++],this._$setProperty(s,t,10)}_$createVideo(t){const e=me();t.characterId&&(e._$characterId=t.characterId),"loaderInfoId"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}_$createTextField(t){const e=fe();e._$xMin=t.xMin||0,e._$yMin=t.yMin||0,e._$xMax=t.xMax||0,e._$yMax=t.yMax||0,t.characterId&&(e._$characterId=t.characterId),"loaderInfoId"in t&&(e._$loaderInfoId=t.loaderInfoId||0),e._$updateProperty(t),this._$instances.set(e._$instanceId,e)}},ce=[],$e=[],ue=[],de=[],ge=()=>ue.pop()||new ft,fe=()=>$e.pop()||new he,me=()=>de.pop()||new oe,pe=()=>ce.pop()||new ae;const xe=new class{constructor(){this.state="deactivate",this.queue=[],this._$options=[]}execute(){this.state="active";let t=!0;for(;this.queue.length;){const e=this.queue.shift();if(console.log(e),e){switch(t=!0,e.command){case"draw":le._$draw();break;case"setProperty":if(!le.instances.has(e.instanceId))continue;break;case"setChildren":{t=!1;const i=e.buffer,s=le.instances;if(!s.has(i[0]))continue;const r=s.get(i[0]);r._$doChanged(),r._$children=i.subarray(1)}break;case"remove":{const t=le.instances;if(!t.has(e.instanceId))continue;t.get(e.instanceId)._$remove(),t.delete(e.instanceId)}break;case"createShape":le._$createShape(e.buffer);break;case"createDisplayObjectContainer":le._$createDisplayObjectContainer(e.buffer);break;case"createTextField":le._$createTextField(e);break;case"createVideo":le._$createVideo(e);break;case"resize":le._$resize(e.buffer);break;case"initialize":le._$initialize(e.buffer,e.canvas);break;case"setBackgroundColor":le._$setBackgroundColor(e.buffer);break;case"stop":le.stop();break;case"removeCache":rt.removeCache(e.id);break;case"bitmapDraw":{const t=le.instances;if(!t.has(e.sourceId))continue;const i=t.get(e.sourceId),s=new c(e.width,e.height);le._$bitmapDraw(i,e.matrix||f,e.colorTransform||m,s);const r=s.transferToImageBitmap();globalThis.postMessage({command:"bitmapDraw",sourceId:e.sourceId,imageBitmap:r},[r])}break;default:if(e.command.indexOf("shapeRecodes")>-1){t=!1;const i=+e.command.split("@")[1];le._$registerShapeRecodes(i,e.buffer)}}e.buffer&&t&&(this._$options.length=0)}}this.state="deactivate"}};self.addEventListener("message",(t=>{return e=void 0,i=void 0,r=function*(){xe.queue.push(t.data),"deactivate"===xe.state&&xe.execute()},new((s=void 0)||(s=Promise))((function(t,n){function a(t){try{o(r.next(t))}catch(t){n(t)}}function h(t){try{o(r.throw(t))}catch(t){n(t)}}function o(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(a,h)}o((r=r.apply(e,i||[])).next())}));var e,i,s,r}))})(); \ No newline at end of file diff --git a/worker/renderer/src/CommandController.ts b/worker/renderer/src/CommandController.ts deleted file mode 100644 index 978c3b70..00000000 --- a/worker/renderer/src/CommandController.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { $renderPlayer } from "./RenderGlobal"; -import { - $MATRIX_ARRAY_IDENTITY, - $COLOR_ARRAY_IDENTITY, - $OffscreenCanvas, - $cacheStore -} from "@next2d/share"; -import type { PropertyMessageMapImpl } from "./interface/PropertyMessageMapImpl"; -import type { RenderDisplayObjectImpl } from "./interface/RenderDisplayObjectImpl"; - -/** - * @class - */ -export class CommandController -{ - public state: string; - public queue: PropertyMessageMapImpl[]; - private readonly _$options: ArrayBuffer[]; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {string} - * @default "deactivate" - * @public - */ - this.state = "deactivate"; - - /** - * @type {array} - * @public - */ - this.queue = []; - - /** - * @type {array} - * @private - */ - this._$options = []; - } - - /** - * @description 処理を実行 - * Execute process - * - * @return {void} - * @method - * @public - */ - execute (): void - { - this.state = "active"; - - let returnBuffer = true; - while (this.queue.length) { - - const object: PropertyMessageMapImpl | void = this.queue.shift(); - console.log(object); - if (!object) { - continue; - } - - returnBuffer = true; - switch (object.command) { - - case "draw": - $renderPlayer._$draw(); - break; - - case "setProperty": - { - const instances: Map> = $renderPlayer.instances; - if (!instances.has(object.instanceId)) { - continue; - } - - // instances.get(object.instanceId)._$update(object); - } - break; - - case "setChildren": - { - returnBuffer = false; - - const buffer: Float32Array = object.buffer; - - const instances: Map> = $renderPlayer.instances; - if (!instances.has(buffer[0])) { - continue; - } - - const instance: RenderDisplayObjectImpl = instances.get(buffer[0]); - instance._$doChanged(); - - instance._$children = buffer.subarray(1); - } - break; - - case "remove": - { - const instances: Map> = $renderPlayer.instances; - if (!instances.has(object.instanceId)) { - continue; - } - - instances.get(object.instanceId)._$remove(); - instances.delete(object.instanceId); - } - break; - - case "createShape": - $renderPlayer._$createShape(object.buffer); - break; - - case "createDisplayObjectContainer": - - $renderPlayer - ._$createDisplayObjectContainer(object.buffer); - - break; - - case "createTextField": - $renderPlayer._$createTextField(object); - break; - - case "createVideo": - $renderPlayer._$createVideo(object); - break; - - case "resize": - $renderPlayer._$resize(object.buffer); - break; - - case "initialize": - $renderPlayer._$initialize( - object.buffer, object.canvas - ); - break; - - case "setBackgroundColor": - $renderPlayer._$setBackgroundColor(object.buffer); - break; - - case "stop": - $renderPlayer.stop(); - break; - - case "removeCache": - $cacheStore.removeCache(object.id); - break; - - case "bitmapDraw": - { - const instances: Map> = $renderPlayer.instances; - if (!instances.has(object.sourceId)) { - continue; - } - - const instance: RenderDisplayObjectImpl = instances.get(object.sourceId); - - const canvas: OffscreenCanvas = new $OffscreenCanvas( - object.width, - object.height - ); - - $renderPlayer._$bitmapDraw( - instance, - object.matrix || $MATRIX_ARRAY_IDENTITY, - object.colorTransform || $COLOR_ARRAY_IDENTITY, - canvas - ); - - const imageBitmap: ImageBitmap = canvas.transferToImageBitmap(); - globalThis.postMessage({ - "command": "bitmapDraw", - "sourceId": object.sourceId, - "imageBitmap": imageBitmap - // @ts-ignore - }, [imageBitmap]); - - } - break; - - default: - if (object.command.indexOf("shapeRecodes") > -1) { - returnBuffer = false; - const instanceId: number = +object.command.split("@")[1]; - $renderPlayer._$registerShapeRecodes(instanceId, object.buffer); - } - break; - - } - - if (object.buffer && returnBuffer) { - // this._$options.push(object.buffer.buffer); - - // globalThis.postMessage({ - // "command": "renderBuffer", - // "buffer": object.buffer - // // @ts-ignore - // }, this._$options); - - // reset - this._$options.length = 0; - } - } - - this.state = "deactivate"; - } -} \ No newline at end of file diff --git a/worker/renderer/src/RenderGlobal.ts b/worker/renderer/src/RenderGlobal.ts deleted file mode 100644 index 7fd3a94c..00000000 --- a/worker/renderer/src/RenderGlobal.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { RenderPlayer } from "./RenderPlayer"; -import { RenderShape } from "./display/RenderShape"; -import { RenderTextField } from "./display/RenderTextField"; -import { RenderDisplayObjectContainer } from "./display/RenderDisplayObjectContainer"; -import { RenderVideo } from "./media/RenderVideo"; - -/** - * @type {boolean} - * @public - */ -export let $isSafari: boolean = false; - -/** - * @param {boolean} is_safari - * @method - * @public - */ -export const $setSafari = (is_safari: boolean): void => -{ - $isSafari = is_safari; -}; - -/** - * @type {RenderPlayer} - * @const - */ -export const $renderPlayer: RenderPlayer = new RenderPlayer(); - -/** - * @type {array} - * @static - */ -export const $shapes: RenderShape[] = []; - -/** - * @type {array} - * @static - */ -export const $textFields: RenderTextField[] = []; - -/** - * @type {array} - * @static - */ -export const $containers: RenderDisplayObjectContainer[] = []; - -/** - * @type {array} - * @static - */ -export const $videos: RenderVideo[] = []; - -/** - * @return {RenderDisplayObjectContainer} - * @method - * @static - */ -export const $getDisplayObjectContainer = (): RenderDisplayObjectContainer => -{ - return $containers.pop() || new RenderDisplayObjectContainer(); -}; - -/** - * @return {RenderTextField} - * @method - * @static - */ -export const $getTextField = (): RenderTextField => -{ - return $textFields.pop() || new RenderTextField(); -}; - -/** - * @return {RenderVideo} - * @method - * @static - */ -export const $getVideo = (): RenderVideo => -{ - return $videos.pop() || new RenderVideo(); -}; - -/** - * @return {RenderShape} - * @method - * @static - */ -export const $getShape = (): RenderShape => -{ - return $shapes.pop() || new RenderShape(); -}; \ No newline at end of file diff --git a/worker/renderer/src/RenderPlayer.ts b/worker/renderer/src/RenderPlayer.ts deleted file mode 100644 index 86606042..00000000 --- a/worker/renderer/src/RenderPlayer.ts +++ /dev/null @@ -1,639 +0,0 @@ -import { RenderDisplayObjectContainer } from "./display/RenderDisplayObjectContainer"; -import { CanvasToWebGLContext } from "@next2d/webgl"; -import type { RenderShape } from "./display/RenderShape"; -import type { RenderVideo } from "./media/RenderVideo"; -import type { RenderTextField } from "./display/RenderTextField"; -import type { RenderDisplayObjectImpl } from "./interface/RenderDisplayObjectImpl"; -import type { AttachmentImpl } from "./interface/AttachmentImpl"; -import type { PropertyTextMessageImpl } from "./interface/PropertyTextMessageImpl"; -import type { PropertyVideoMessageImpl } from "./interface/PropertyVideoMessageImpl"; -import type { RGBAImpl } from "./interface/RGBAImpl"; -import type { FrameBufferManager } from "@next2d/webgl"; -import { - $cacheStore, - $COLOR_ARRAY_IDENTITY, - $getFloat32Array6, - $uintToRGBA, - $setDevicePixelRatio, - $blendToString, - $poolFloat32Array6 -} from "@next2d/share"; -import { - $getDisplayObjectContainer, - $getShape, - $getTextField, - $getVideo, - $setSafari -} from "./RenderGlobal"; - -/** - * @class - */ -export class RenderPlayer -{ - private readonly _$instances: Map>; - private readonly _$matrix: Float32Array; - private _$context: CanvasToWebGLContext | null; - private _$canvas: OffscreenCanvas | null; - private _$width: number; - private _$height: number; - private readonly _$stage: RenderDisplayObjectContainer; - private _$attachment: AttachmentImpl | null; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {Map} - * @private - */ - this._$instances = new Map(); - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$matrix = $getFloat32Array6(1, 0, 0, 1, 0, 0); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$width = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$height = 0; - - /** - * @type {RenderDisplayObjectContainer} - * @default null - * @private - */ - this._$stage = new RenderDisplayObjectContainer(); - - /** - * @type {OffscreenCanvas} - * @default null - * @private - */ - this._$canvas = null; - - /** - * @type {CanvasToWebGLContext} - * @default null - * @private - */ - this._$context = null; - - /** - * @type {object} - * @default null - * @private - */ - this._$attachment = null; - } - - /** - * @return {Map} - * @readonly - * @public - */ - get instances (): Map> - { - return this._$instances; - } - - /** - * @return {CanvasToWebGLContext} - * @readonly - * @public - */ - get context (): CanvasToWebGLContext | null - { - return this._$context; - } - - /** - * @return {number} - * @readonly - * @public - */ - get scaleX (): number - { - return this._$matrix[0]; - } - - /** - * @description 描画の停止 - * - * @return {void} - * @method - * @public - */ - stop (): void - { - $cacheStore.reset(); - } - - /** - * @description WebGLを起動 - * - * @param {Float32Array} buffer - * @param {OffscreenCanvas} canvas - * @return {void} - * @method - * @public - */ - _$initialize ( - buffer: Float32Array, - canvas: OffscreenCanvas - ): void { - - let index = 0; - - // set stage - this._$setStage(buffer[index++]); - - // update - $setSafari(buffer[index++] === 1); - $setDevicePixelRatio(buffer[index++]); - - this._$canvas = canvas; - - const gl: WebGL2RenderingContext | null = canvas.getContext("webgl2", { - "stencil": true, - "premultipliedAlpha": true, - "antialias": false, - "depth": false, - "preserveDrawingBuffer": true - }); - - if (gl) { - const context: CanvasToWebGLContext = new CanvasToWebGLContext(gl, buffer[index++]); - this._$context = context; - $cacheStore.context = context; - } - } - - /** - * @description 背景色をセット - * - * @param {Float32Array} buffer - * @return {void} - * @method - * @public - */ - _$setBackgroundColor (buffer: Float32Array): void - { - if (!this._$context) { - return ; - } - - const backgroundColor: number = buffer[0]; - if (backgroundColor === -1) { - - this._$context._$setColor(0, 0, 0, 0); - - } else { - - const color: RGBAImpl = $uintToRGBA(backgroundColor); - - this._$context._$setColor( - color.R / 255, - color.G / 255, - color.B / 255, - 1 - ); - - } - } - - /** - * @description 指定canvasに転写 - * - * @param {RenderDisplayObject} source - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {OffscreenCanvas} canvas - * @return {void} - * @method - * @private - */ - _$bitmapDraw ( - source: RenderDisplayObjectImpl, - matrix: Float32Array, - color_transform: Float32Array, - canvas: OffscreenCanvas - ): void { - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { - return ; - } - - context._$bind(this._$attachment); - - // reset - context.reset(); - context.setTransform(1, 0, 0, 1, 0, 0); - context.clearRect(0, 0, this._$width, this._$height); - context.beginPath(); - - source._$draw(context, matrix, color_transform); - - context - .frameBuffer - .transferToMainTexture(); - - const ctx: OffscreenCanvasRenderingContext2D | null = canvas.getContext("2d"); - if (ctx && this._$canvas) { - ctx.drawImage(this._$canvas, 0, 0); - } - - } - - /** - * @description 描画処理を実行 - * - * @return {void} - * @method - * @private - */ - _$draw (): void - { - if (!this._$width || !this._$height) { - return ; - } - - // if (!this._$stage._$updated) { - // return ; - // } - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { - return ; - } - - // reset - context.reset(); - context.setTransform(1, 0, 0, 1, 0, 0); - context.clearRect(0, 0, this._$width, this._$height); - context.beginPath(); - - this._$stage._$draw( - context, - this._$matrix, - $COLOR_ARRAY_IDENTITY - ); - - // stage end - this._$stage._$updated = false; - - context.drawInstacedArray(); - context - .frameBuffer - .transferToMainTexture(); - } - - /** - * @description 描画範囲のサイズを変更 - * - * @param {number} width - * @param {number} height - * @param {number} scale - * @param {number} [tx = 0] - * @param {number} [ty = 0] - * @return {void} - * @method - * @private - */ - _$resize (buffer: Float32Array): void - { - - let index: number = 0; - const width: number = buffer[index++]; - const height: number = buffer[index++]; - - this._$width = width; - this._$height = height; - - if (!this._$canvas) { - return ; - } - - if (this._$canvas.width === width && this._$canvas.height === height) { - return ; - } - - const context: CanvasToWebGLContext | null = this._$context; - if (!context) { - return ; - } - - const scale: number = buffer[index++]; - this._$matrix[0] = scale; - this._$matrix[3] = scale; - this._$matrix[4] = buffer[index++]; - this._$matrix[5] = buffer[index++]; - - this._$stage._$updated = true; - $cacheStore.reset(); - - context.clearInstacedArray(); - - this._$canvas.width = width; - this._$canvas.height = height; - - context._$gl.viewport(0, 0, width, height); - - const manager: FrameBufferManager = context.frameBuffer; - if (this._$attachment) { - manager.unbind(); - manager.releaseAttachment(this._$attachment, true); - } - - this._$attachment = manager - .createCacheAttachment(width, height, true); - - // update cache max size - context.setMaxSize(width, height); - - context._$bind(this._$attachment); - } - - /** - * @param {number} instance_id - * @return {void} - * @method - * @private - */ - _$setStage (instance_id: number): void - { - this._$stage._$instanceId = instance_id; - this._$instances.set(instance_id, this._$stage); - } - - /** - * @description Stageの更新情報をセット - * - * @return {void} - * @method - * @private - */ - _$updateStage (): void - { - this._$stage._$updated = true; - } - - /** - * @description DisplayObjectContainerクラスを生成 - * - * @param {Float32Array} buffer - * @return {void} - * @method - * @private - */ - _$createDisplayObjectContainer (buffer: Float32Array): void - { - const sprite: RenderDisplayObjectContainer = $getDisplayObjectContainer(); - - let index = 0; - sprite._$instanceId = buffer[index++]; - sprite._$parentId = buffer[index++]; - - this._$setProperty(sprite, buffer, index); - - this._$instances.set(sprite._$instanceId, sprite); - } - - /** - * @param {object} instance - * @param {Float32Array} buffer - * @param {number} index - * @return {void} - * @method - * @private - */ - _$setProperty ( - instance: RenderDisplayObjectImpl, - buffer: Float32Array, - index: number - ): void { - - // visible - instance._$visible = buffer[index++] === 1; - - // depth - instance._$depth = buffer[index++]; - - // clip depth - instance._$clipDepth = buffer[index++]; - - // isMask - instance._$isMask = buffer[index++] === 1; - - const mask: boolean = buffer[index++] === 1; - if (mask) { - - instance._$maskId = buffer[index++]; - - if (!instance._$maskMatrix) { - instance._$maskMatrix = $getFloat32Array6(); - } - - instance._$maskMatrix[0] = buffer[index++]; - instance._$maskMatrix[1] = buffer[index++]; - instance._$maskMatrix[2] = buffer[index++]; - instance._$maskMatrix[3] = buffer[index++]; - instance._$maskMatrix[4] = buffer[index++]; - instance._$maskMatrix[5] = buffer[index++]; - - } else { - - instance._$maskId = -1; - if (instance._$maskMatrix) { - $poolFloat32Array6(instance._$maskMatrix); - instance._$maskMatrix = null; - } - index += 7; - - } - - if (instance._$visible) { - - // matrix - instance._$matrix[0] = buffer[index++]; - instance._$matrix[1] = buffer[index++]; - instance._$matrix[2] = buffer[index++]; - instance._$matrix[3] = buffer[index++]; - instance._$matrix[4] = buffer[index++]; - instance._$matrix[5] = buffer[index++]; - - // colorTransform - instance._$colorTransform[0] = buffer[index++]; - instance._$colorTransform[1] = buffer[index++]; - instance._$colorTransform[2] = buffer[index++]; - instance._$colorTransform[3] = buffer[index++]; - instance._$colorTransform[4] = buffer[index++]; - instance._$colorTransform[5] = buffer[index++]; - instance._$colorTransform[6] = buffer[index++]; - instance._$colorTransform[7] = buffer[index++]; - - } else { - - index += 6; // matrix - index += 8; // colorTransform - - } - - // blend mode - instance._$blendMode = $blendToString(buffer[index++]); - - // scale9Grid - if (buffer[index++]) { - instance._$scale9Grid = { - "x": buffer[index++], - "y": buffer[index++], - "w": buffer[index++], - "h": buffer[index++] - }; - } else { - instance._$scale9Grid = null; - } - - // blend mode - instance._$blendMode = $blendToString(buffer[index++]); - - // scale9Grid - if (buffer[index++]) { - instance._$scale9Grid = { - "x": buffer[index++], - "y": buffer[index++], - "w": buffer[index++], - "h": buffer[index++] - }; - } else { - instance._$scale9Grid = null; - } - } - - /** - * @description Shapeの描画レコードを登録 - * - * @param {number} instance_id - * @param {Float32Array} recodes - * @return {void} - * @method - * @private - */ - _$registerShapeRecodes (instance_id: number, recodes: Float32Array): void - { - if (!this._$instances.has(instance_id)) { - this._$instances.set(instance_id, $getShape()); - } - - const shape: RenderShape = this._$instances.get(instance_id); - shape._$recodes = recodes; - } - - /** - * @description Shapeクラスを生成 - * - * @param {Float32Array} buffer - * @return {void} - * @method - * @private - */ - _$createShape (buffer: Float32Array): void - { - let index = 0; - const instanceId = buffer[index++]; - - if (!this._$instances.has(instanceId)) { - this._$instances.set(instanceId, $getShape()); - } - const shape: RenderShape = this._$instances.get(instanceId); - - shape._$instanceId = instanceId; - shape._$parentId = buffer[index++]; - shape._$maxAlpha = buffer[index++]; - shape._$canDraw = buffer[index++] === 1; - - shape._$xMin = buffer[index++]; - shape._$yMin = buffer[index++]; - shape._$xMax = buffer[index++]; - shape._$yMax = buffer[index++]; - - shape._$characterId = buffer[index++]; - shape._$loaderInfoId = buffer[index++]; - - this._$setProperty(shape, buffer, index); - } - - /** - * @description Videoクラスを生成 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$createVideo (object: PropertyVideoMessageImpl): void - { - const video: RenderVideo = $getVideo(); - - // video._$instanceId = object.instanceId; - - if (object.characterId) { - video._$characterId = object.characterId; - } - if ("loaderInfoId" in object) { - video._$loaderInfoId = object.loaderInfoId || 0; - } - - video._$updateProperty(object); - - this._$instances.set(video._$instanceId, video); - } - - /** - * @description TextFieldクラスを生成 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$createTextField (object: PropertyTextMessageImpl): void - { - const textField: RenderTextField = $getTextField(); - - // textField._$instanceId = object.instanceId; - - // bounds - textField._$xMin = object.xMin || 0; - textField._$yMin = object.yMin || 0; - textField._$xMax = object.xMax || 0; - textField._$yMax = object.yMax || 0; - - if (object.characterId) { - textField._$characterId = object.characterId; - } - if ("loaderInfoId" in object) { - textField._$loaderInfoId = object.loaderInfoId || 0; - } - - textField._$updateProperty(object); - - this._$instances.set(textField._$instanceId, textField); - } -} \ No newline at end of file diff --git a/worker/renderer/src/display/RenderDisplayObject.ts b/worker/renderer/src/display/RenderDisplayObject.ts deleted file mode 100644 index 9f4c384a..00000000 --- a/worker/renderer/src/display/RenderDisplayObject.ts +++ /dev/null @@ -1,833 +0,0 @@ -import type { RenderPlayer } from "../RenderPlayer"; -import type { RenderDisplayObjectImpl } from "../interface/RenderDisplayObjectImpl"; -import type { BlendModeImpl } from "../interface/BlendModeImpl"; -import type { FilterArrayImpl } from "../interface/FilterArrayImpl"; -import type { BoundsImpl } from "../interface/BoundsImpl"; -import type { PropertyMessageMapImpl } from "../interface/PropertyMessageMapImpl"; -import type { AttachmentImpl } from "../interface/AttachmentImpl"; -import type { GridImpl } from "../interface/GridImpl"; -import type { CachePositionImpl } from "../interface/CachePositionImpl"; -import { $renderPlayer } from "../RenderGlobal"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - BevelFilter, - BlurFilter, - ColorMatrixFilter, - ConvolutionFilter, - DisplacementMapFilter, - DropShadowFilter, - GlowFilter, - GradientBevelFilter, - GradientGlowFilter -} from "@next2d/filters"; -import { - $cacheStore, - $getBoundsObject, - $poolBoundsObject, - $getFloat32Array6, - $getFloat32Array8, - $Math, - $multiplicationMatrix, - $boundsMatrix, - $poolFloat32Array6, - $devicePixelRatio, - $getArray, - $poolArray -} from "@next2d/share"; - -/** - * @class - */ -export class RenderDisplayObject -{ - public _$instanceId: number; - public _$parentId: number; - public _$loaderInfoId: number; - public _$characterId: number; - public _$clipDepth: number; - public _$depth: number; - public _$isMask: boolean; - public _$updated: boolean; - public readonly _$matrix: Float32Array; - public _$blendMode: BlendModeImpl; - public readonly _$colorTransform: Float32Array; - public _$filters: FilterArrayImpl | null; - public _$visible: boolean; - public _$maskId: number; - public _$maskMatrix: Float32Array | null; - public _$xMin: number; - public _$yMin: number; - public _$xMax: number; - public _$yMax: number; - public _$scale9Grid: GridImpl | null; - public _$matrixBase: Float32Array | null; - - /** - * @constructor - * @public - */ - constructor () - { - /** - * @type {number} - * @default -1 - * @private - */ - this._$instanceId = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$parentId = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$loaderInfoId = -1; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$characterId = -1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$clipDepth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$depth = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isMask = false; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$updated = true; - - /** - * @type {Float32Array} - * @private - */ - this._$matrix = $getFloat32Array6(1, 0, 0, 1, 0, 0); - - /** - * @type {Float32Array} - * @private - */ - this._$colorTransform = $getFloat32Array8(1, 1, 1, 1, 0, 0, 0, 0); - - /** - * @type {string} - * @default BlendMode.NORMAL - * @private - */ - this._$blendMode = "normal"; - - /** - * @type {array} - * @default null - * @private - */ - this._$filters = null; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$visible = true; - - /** - * @type {number} - * @default -1 - * @private - */ - this._$maskId = -1; - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$maskMatrix = null; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$isMask = false; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$xMin = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$yMin = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$xMax = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$yMax = 0; - - /** - * @type {object|null} - * @default null - * @private - */ - this._$scale9Grid = null; - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$matrixBase = null; - } - - /** - * @param {Float32Array} matrix - * @return {boolean} - * @method - * @private - */ - _$shouldClip (matrix: Float32Array): boolean - { - const bounds: BoundsImpl = this._$getBounds(matrix); - const width: number = $Math.abs(bounds.xMax - bounds.xMin); - const height: number = $Math.abs(bounds.yMax - bounds.yMin); - $poolBoundsObject(bounds); - - return !(!width || !height); - } - - /** - * @param {Float32Array} multi_matrix - * @returns {object} - * @private - */ - _$getLayerBounds (multi_matrix: Float32Array): BoundsImpl - { - const baseBounds: BoundsImpl = this._$getBounds(); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multi_matrix); - $poolBoundsObject(baseBounds); - - const filters: FilterArrayImpl | null = this._$filters; - if (!filters || !filters.length) { - return bounds; - } - - let filterBounds: BoundsImpl = $getBoundsObject( - 0, - $Math.abs(bounds.xMax - bounds.xMin), - 0, - $Math.abs(bounds.yMax - bounds.yMin) - ); - $poolBoundsObject(bounds); - - let xScale: number = +$Math.sqrt( - multi_matrix[0] * multi_matrix[0] - + multi_matrix[1] * multi_matrix[1] - ); - let yScale: number = +$Math.sqrt( - multi_matrix[2] * multi_matrix[2] - + multi_matrix[3] * multi_matrix[3] - ); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - - for (let idx: number = 0; idx < filters.length; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - - return filterBounds; - } - - /** - * @param {Float32Array} [matrix=null] - * @returns {object} - * @method - * @private - */ - _$getBounds (matrix: Float32Array | null = null): BoundsImpl - { - const baseBounds: BoundsImpl = $getBoundsObject( - this._$xMin, this._$xMax, - this._$yMin, this._$yMax - ); - - if (!matrix) { - return baseBounds; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - $poolBoundsObject(baseBounds); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return bounds; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {boolean} - * @method - * @private - */ - _$startClip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): boolean { - - context.drawInstacedArray(); - - const bounds: BoundsImpl = this._$getBounds(matrix); - const result = context._$startClip(bounds); - $poolBoundsObject(bounds); - - if (!result) { - return false; - } - - // start clip - context._$enterClip(); - - // mask start - context._$beginClipDef(); - - let containerClip = false; - if ("_$children" in this) { - containerClip = true; - context._$updateContainerClipFlag(true); - } - - // @ts-ignore - this._$clip(context, matrix); - this._$updated = false; - - // container clip - if (containerClip) { - - // update flag - context._$updateContainerClipFlag(false); - - // execute clip - context._$drawContainerClip(); - } - - // mask end - context._$endClipDef(); - - return true; - } - - /** - * @description 自身と親の状態をアクティブにする - * - * @return {void} - * @method - * @private - */ - _$doChanged (): void - { - this._$updated = true; - - if (this._$parentId > -1) { - - const instances: Map> = $renderPlayer.instances; - if (!instances.has(this._$parentId)) { - return ; - } - - const instance = instances.get(this._$parentId); - if (!instance._$updated) { - instance._$doChanged(); - } - } - } - - /** - * @description 描画情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$update (object: PropertyMessageMapImpl): void - { - this._$doChanged(); - - this._$visible = object.visible; - - if ("depth" in object) { - this._$depth = object.depth; - } - - if ("isMask" in object) { - this._$isMask = object.isMask; - } - - if ("clipDepth" in object) { - this._$clipDepth = object.clipDepth; - } - - if ("maskId" in object) { - this._$maskId = object.maskId; - if (this._$maskId > -1 && object.maskMatrix) { - this._$maskMatrix = object.maskMatrix; - } - } - - this._$matrix[0] = "a" in object ? object.a : 1; - this._$matrix[1] = "b" in object ? object.b : 0; - this._$matrix[2] = "c" in object ? object.c : 0; - this._$matrix[3] = "d" in object ? object.d : 1; - this._$matrix[4] = "tx" in object ? object.tx : 0; - this._$matrix[5] = "ty" in object ? object.ty : 0; - - this._$colorTransform[0] = "f0" in object ? object.f0 : 1; - this._$colorTransform[1] = "f1" in object ? object.f1 : 1; - this._$colorTransform[2] = "f2" in object ? object.f2 : 1; - this._$colorTransform[3] = "f3" in object ? object.f3 : 1; - this._$colorTransform[4] = "f4" in object ? object.f4 : 0; - this._$colorTransform[5] = "f5" in object ? object.f5 : 0; - this._$colorTransform[6] = "f6" in object ? object.f6 : 0; - this._$colorTransform[7] = "f7" in object ? object.f7 : 0; - - this._$blendMode = object.blendMode || "normal"; - - this._$filters = null; - if (object.filters && object.filters.length) { - this._$filters = $getArray(); - for (let idx: number = 0; idx < object.filters.length; ++idx) { - - const parameters = object.filters[idx]; - const type: number = parameters.shift(); - - switch (type) { - - case 0: - this._$filters.push( - new BevelFilter(...parameters) - ); - break; - - case 1: - this._$filters.push( - new BlurFilter(...parameters) - ); - break; - - case 2: - this._$filters.push( - new ColorMatrixFilter(...parameters) - ); - break; - - case 3: - this._$filters.push( - new ConvolutionFilter(...parameters) - ); - break; - - case 4: - this._$filters.push( - new DisplacementMapFilter(...parameters) - ); - break; - - case 5: - this._$filters.push( - new DropShadowFilter(...parameters) - ); - break; - - case 6: - this._$filters.push( - new GlowFilter(...parameters) - ); - break; - - case 7: - this._$filters.push( - new GradientBevelFilter(...parameters) - ); - break; - - case 8: - this._$filters.push( - new GradientGlowFilter(...parameters) - ); - break; - - } - } - } - - if (object.grid) { - this._$scale9Grid = object.grid; - - if (object.matrixBase) { - this._$matrixBase = object.matrixBase; - } - } - } - - /** - * @param {array} [filters] - * @return {boolean} - * @private - */ - _$canApply (filters: FilterArrayImpl | null = null): boolean - { - if (filters) { - for (let idx: number = 0; idx < filters.length; ++idx) { - if (filters[idx]._$canApply()) { - return true; - } - } - } - return false; - } - - /** - * @description Playerから登録を削除 - * - * @return {void} - * @method - * @private - */ - _$remove (): void - { - this._$doChanged(); - - const player: RenderPlayer = $renderPlayer; - - // キャッシュ削除のタイマーをセット - $cacheStore.setRemoveTimer(this._$instanceId); - - if (this._$loaderInfoId > -1 && this._$characterId) { - $cacheStore.setRemoveTimer( - `${this._$loaderInfoId}@${this._$characterId}` - ); - } - - player.instances.delete(this._$instanceId); - - // reset - this._$instanceId = -1; - this._$parentId = -1; - this._$loaderInfoId = -1; - this._$characterId = -1; - this._$blendMode = "normal"; - this._$filters = null; - this._$visible = true; - this._$maskId = -1; - this._$isMask = false; - this._$depth = 0; - this._$clipDepth = 0; - this._$scale9Grid = null; - } - - /** - * @return {boolean} - * @method - * @private - */ - _$isUpdated (): boolean - { - return this._$updated; - } - - /** - * @param {number} width - * @param {number} height - * @param {Float32Array} matrix - * @param {array} [filters=null] - * @param {boolean} [can_apply=false] - * @param {number} [position_x=0] - * @param {number} [position_y=0] - * @return {boolean} - * @private - */ - _$isFilterUpdated ( - matrix: Float32Array, - filters: FilterArrayImpl | null = null, - can_apply: boolean = false - ): boolean { - - // cache flag - if (this._$isUpdated()) { - return true; - } - - // check filter data - if (can_apply && filters) { - - for (let idx: number = 0; idx < filters.length; ++idx) { - - if (!filters[idx]._$isUpdated()) { - continue; - } - - return true; - } - - } - - // check status - const cache: CachePositionImpl = $cacheStore.get([this._$instanceId, "f"]); - - if (!cache) { - return true; - } - - if (cache.filterState !== can_apply) { - return true; - } - - if (cache.matrix !== `${matrix[0]}_${matrix[1]}_${matrix[2]}_${matrix[3]}`) { - return true; - } - - return false; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {array} filters - * @param {WebGLTexture} target_texture - * @param {Float32Array} matrix - * @param {number} width - * @param {number} height - * @return {WebGLTexture} - * @private - */ - _$applyFilter ( - context: CanvasToWebGLContext, - filters: FilterArrayImpl, - target_texture: WebGLTexture, - matrix: Float32Array, - width: number, height: number - ): WebGLTexture { - - const xScale: number = +$Math.sqrt( - matrix[0] * matrix[0] - + matrix[1] * matrix[1] - ); - const yScale: number = +$Math.sqrt( - matrix[2] * matrix[2] - + matrix[3] * matrix[3] - ); - - const radianX: number = $Math.atan2(matrix[1], matrix[0]); - const radianY: number = $Math.atan2(0 - matrix[2], matrix[3]); - - const parentMatrix: Float32Array = $getFloat32Array6( - $Math.cos(radianX), $Math.sin(radianX), - 0 - $Math.sin(radianY), $Math.cos(radianY), - width / 2, height / 2 - ); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - 0 - target_texture.width / 2, - 0 - target_texture.height / 2 - ); - - const multiMatrix: Float32Array = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - - context._$bind(attachment); - - context.reset(); - context.setTransform( - multiMatrix[0], multiMatrix[1], - multiMatrix[2], multiMatrix[3], - multiMatrix[4], multiMatrix[5] - ); - $poolFloat32Array6(multiMatrix); - - context.drawImage(target_texture, - 0, 0, target_texture.width, target_texture.height - ); - - // init - context._$offsetX = 0; - context._$offsetY = 0; - - const filterMatrix: Float32Array = $getFloat32Array6( - xScale, 0, 0, yScale, 0, 0 - ); - - let texture: WebGLTexture | null = null; - for (let idx: number = 0; idx < filters.length; ++idx) { - texture = filters[idx]._$applyFilter(context, filterMatrix); - } - - $poolFloat32Array6(filterMatrix); - - if (!texture) { - return target_texture; - } - - const offsetX: number = context._$offsetX; - const offsetY: number = context._$offsetY; - - // reset - context._$offsetX = 0; - context._$offsetY = 0; - - // set offset - texture.offsetX = offsetX; - texture.offsetY = offsetY; - - // cache texture - texture.matrix = - matrix[0] + "_" + matrix[1] + "_" - + matrix[2] + "_" + matrix[3]; - - texture.filterState = true; - - context._$bind(currentAttachment); - manager.releaseAttachment(attachment, false); - - return texture; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {array} filters - * @param {number} width - * @param {number} height - * @param {WebGLTexture} [target_texture = null] - * @return {object} - * @method - * @private - */ - _$drawFilter ( - context: CanvasToWebGLContext, - matrix: Float32Array, - filters: FilterArrayImpl, - width: number, - height: number, - target_texture: WebGLTexture | null = null - ): CachePositionImpl { - - const cacheKeys: any[] = $getArray(this._$instanceId, "f"); - let position: CachePositionImpl | void = $cacheStore.get(cacheKeys); - - const updated: boolean = this._$isFilterUpdated(matrix, filters, true); - - if (position && !updated) { - context.cachePosition = position; - return position; - } - - // cache clear - if (position) { - $cacheStore.set(cacheKeys, null); - } - - const manager: FrameBufferManager = context.frameBuffer; - const targetTexture: WebGLTexture = target_texture - ? target_texture - : context.getTextureFromRect( - context.cachePosition as NonNullable - ); - - const texture: WebGLTexture = this._$applyFilter( - context, filters, targetTexture, - matrix, width, height - ); - manager.textureManager.release(targetTexture); - - const bounds: BoundsImpl = this._$getLayerBounds(matrix); - position = manager.createCachePosition( - $Math.ceil($Math.abs(bounds.xMax - bounds.xMin)), - $Math.ceil($Math.abs(bounds.yMax - bounds.yMin)) - ); - - $poolBoundsObject(bounds); - position.filterState = true; - position.matrix = `${matrix[0]}_${matrix[1]}_${matrix[2]}_${matrix[3]}_0_0`; - position.offsetX = texture.offsetX; - position.offsetY = texture.offsetY; - - // 関数先でtextureがreleaseされる - context.drawTextureFromRect(texture, position); - - $cacheStore.set(cacheKeys, position); - $poolArray(cacheKeys); - - return position; - } -} \ No newline at end of file diff --git a/worker/renderer/src/display/RenderDisplayObjectContainer.ts b/worker/renderer/src/display/RenderDisplayObjectContainer.ts deleted file mode 100644 index 600fa175..00000000 --- a/worker/renderer/src/display/RenderDisplayObjectContainer.ts +++ /dev/null @@ -1,803 +0,0 @@ -import { RenderDisplayObject } from "./RenderDisplayObject"; -import type { RenderDisplayObjectImpl } from "../interface/RenderDisplayObjectImpl"; -import type { FilterArrayImpl } from "../interface/FilterArrayImpl"; -import type { BlendModeImpl } from "../interface/BlendModeImpl"; -import type { BoundsImpl } from "../interface/BoundsImpl"; -import type { AttachmentImpl } from "../interface/AttachmentImpl"; -import type { ParentImpl } from "../interface/ParentImpl"; -import type { PreObjectImpl } from "../interface/PreObjectImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $containers, - $renderPlayer -} from "../RenderGlobal"; -import { - $cacheStore, - $boundsMatrix, - $clamp, - $getArray, - $getBoundsObject, - $getFloat32Array6, - $getFloat32Array8, - $getPreObject, - $Math, - $MATRIX_ARRAY_IDENTITY, - $multiplicationColor, - $multiplicationMatrix, - $Number, - $poolBoundsObject, - $poolFloat32Array6, - $poolFloat32Array8, - $poolPreObject, - $devicePixelRatio -} from "@next2d/share"; - -/** - * @class - */ -export class RenderDisplayObjectContainer extends RenderDisplayObject -{ - private _$children: Int32Array; - - /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {Int32Array} - * @private - */ - this._$children = new Int32Array(); - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const instances: Map> = $renderPlayer.instances; - const children: Int32Array = this._$children; - for (let idx: number = 0; idx < this._$children.length; ++idx) { - - const id: number = children[idx]; - if (!instances.has(id)) { - continue; - } - - const instance: RenderDisplayObjectImpl | void = instances.get(id); - - // mask instance - if (!instance || instance._$isMask) { - continue; - } - - instance._$clip(context, multiMatrix); - instance._$updated = false; - - } - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - // not draw - if (!this._$visible) { - return ; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$colorTransform; - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - // not draw - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - return ; - } - - // not draw - const children: Int32Array = this._$children; - const length: number = children.length; - if (!length) { - return ; - } - - // pre data - const preObject: PreObjectImpl | null = this._$preDraw(context, matrix); - if (!preObject) { - return ; - } - - // use cache - if (preObject.isLayer && !preObject.isUpdated) { - this._$postDraw(context, matrix, multiColor, preObject); - return ; - } - - const preMatrix: Float32Array = preObject.matrix as NonNullable; - const preColorTransform: Float32Array = preObject.isLayer && preObject.color - ? preObject.color - : multiColor; - - // init clip params - let shouldClip: boolean = true; - let clipDepth: number = 0; - - // draw children - const instances: Map> = $renderPlayer.instances; - const isLayer: boolean = context.isLayer; - for (let idx: number = 0; idx < length; ++idx) { - - const id: number = children[idx]; - if (!instances.has(id)) { - continue; - } - - const instance: RenderDisplayObjectImpl = instances.get(id); - - // mask instance - if (instance._$isMask) { - continue; - } - - // not layer mode - const blendMode: BlendModeImpl = instance._$blendMode; - if ((blendMode === "alpha" || blendMode === "erase") - && !isLayer - ) { - continue; - } - - // mask end - if (clipDepth - && (instance._$depth > clipDepth || instance._$clipDepth > 0) - ) { - - context.restore(); - - if (shouldClip) { - context._$leaveClip(); - } - - // clear - clipDepth = 0; - shouldClip = true; - } - - // mask size 0 - if (!shouldClip) { - continue; - } - - // mask start - if (instance._$clipDepth > 0) { - - clipDepth = instance._$clipDepth; - shouldClip = instance._$shouldClip(preMatrix); - - if (shouldClip) { - context.save(); - shouldClip = instance._$startClip(context, preMatrix); - } - - continue; - } - - // mask start - const maskInstance: RenderDisplayObjectImpl | null = instance._$maskId > -1 && instances.has(instance._$maskId) - ? instances.get(instance._$maskId) - : null; - - if (maskInstance) { - - maskInstance._$updated = false; - - let maskMatrix: Float32Array; - - if (this._$instanceId === maskInstance._$parentId) { - - maskMatrix = preMatrix; - - } else { - - maskMatrix = $MATRIX_ARRAY_IDENTITY; - - let parent: ParentImpl | void = instances.get(maskInstance._$parentId); - while (parent || parent._$instanceId !== parent._$parentId) { - - maskMatrix = $multiplicationMatrix( - parent._$matrix, - maskMatrix - ); - - parent = instances.get(parent._$parentId); - } - - const mScale: number = $renderPlayer.scaleX; - const playerMatrix: Float32Array = $getFloat32Array6( - mScale, 0, 0, mScale, 0, 0 - ); - - maskMatrix = $multiplicationMatrix( - playerMatrix, maskMatrix - ); - $poolFloat32Array6(playerMatrix); - - if (context.isLayer) { - const currentPosition: BoundsImpl = context.getCurrentPosition(); - maskMatrix[4] -= currentPosition.xMin; - maskMatrix[5] -= currentPosition.yMin; - } - } - - if (!maskInstance._$shouldClip(maskMatrix)) { - continue; - } - - const result: boolean = maskInstance._$startClip(context, maskMatrix); - - context.save(); - - if (!result) { // fixed - context.restore(); - continue; - } - } - - instance._$draw(context, preMatrix, preColorTransform); - instance._$updated = false; - - // mask end - if (maskInstance) { - context.restore(); - context._$leaveClip(); - } - } - - // end mask - if (clipDepth) { - context.restore(); - - if (shouldClip) { - context._$leaveClip(); - } - } - - // filter and blend - if (preObject.isLayer) { - return this._$postDraw(context, matrix, multiColor, preObject); - } - - if (preObject.matrix !== matrix) { - $poolFloat32Array6(preObject.matrix as NonNullable); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - - $poolPreObject(preObject); - } - - /** - * @param {Float32Array} multi_matrix - * @return {object} - * @private - */ - _$getLayerBounds (multi_matrix: Float32Array): BoundsImpl - { - const children: Int32Array = this._$children; - const length: number = children.length; - - // size zero - if (!length) { - return $getBoundsObject(0, 0, 0, 0); - } - - // data init - const no: number = $Number.MAX_VALUE; - let xMin: number = no; - let xMax: number = -no; - let yMin: number = no; - let yMax: number = -no; - - const instances: Map> = $renderPlayer.instances; - for (let idx: number = 0; idx < children.length; ++idx) { - - const id: number = children[idx]; - if (!instances.has(id)) { - continue; - } - - const instance = instances.get(id); - - let multiMatrix = multi_matrix; - const rawMatrix: Float32Array = instance._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(multi_matrix, rawMatrix); - } - - const bounds: BoundsImpl = instance._$getLayerBounds(multiMatrix); - - xMin = $Math.min(xMin, bounds.xMin); - xMax = $Math.max(xMax, bounds.xMax); - yMin = $Math.min(yMin, bounds.yMin); - yMax = $Math.max(yMax, bounds.yMax); - - $poolBoundsObject(bounds); - - if (multiMatrix !== multi_matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - if (!this._$filters || !this._$filters.length) { - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - let filterBounds: BoundsImpl = $getBoundsObject( - 0, xMax - xMin, - 0, yMax - yMin - ); - - let xScale: number = +$Math.sqrt( - multi_matrix[0] * multi_matrix[0] - + multi_matrix[1] * multi_matrix[1] - ); - let yScale: number = +$Math.sqrt( - multi_matrix[2] * multi_matrix[2] - + multi_matrix[3] * multi_matrix[3] - ); - - xScale /= $devicePixelRatio; - yScale /= $devicePixelRatio; - - xScale *= 2; - yScale *= 2; - for (let idx: number = 0; idx < this._$filters.length; ++idx) { - filterBounds = this._$filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - - xMax += filterBounds.xMax - (xMax - xMin); - yMax += filterBounds.yMax - (yMax - yMin); - xMin += filterBounds.xMin; - yMin += filterBounds.yMin; - - $poolBoundsObject(filterBounds); - - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - /** - * @param {Float32Array} [matrix=null] - * @returns {object} - * @method - * @private - */ - _$getBounds (matrix: Float32Array | null = null): BoundsImpl - { - let multiMatrix: Float32Array = $MATRIX_ARRAY_IDENTITY; - if (matrix) { - - multiMatrix = matrix; - - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - } - - const children: Int32Array = this._$children; - const length: number = children.length; - - // size zero - if (!length) { - - const bounds: BoundsImpl = $getBoundsObject( - multiMatrix[4], -multiMatrix[4], - multiMatrix[5], -multiMatrix[5] - ); - - if (matrix && multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - return bounds; - } - - // data init - const no = $Number.MAX_VALUE; - let xMin = no; - let xMax = -no; - let yMin = no; - let yMax = -no; - - const instances: Map> = $renderPlayer.instances; - for (let idx: number = 0; idx < children.length; ++idx) { - - const id: number = children[idx]; - if (!instances.has(id)) { - continue; - } - - const bounds: BoundsImpl = instances - .get(id) - ._$getBounds(multiMatrix); - - xMin = $Math.min(xMin, bounds.xMin); - xMax = $Math.max(xMax, bounds.xMax); - yMin = $Math.min(yMin, bounds.yMin); - yMax = $Math.max(yMax, bounds.yMax); - - $poolBoundsObject(bounds); - - } - - if (matrix && multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - // end - return $getBoundsObject(xMin, xMax, yMin, yMax); - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {object} - * @private - */ - _$preDraw ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): PreObjectImpl | null { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - // size zero - if (!multiMatrix[0] && !multiMatrix[1] - || !multiMatrix[2] && !multiMatrix[3] - ) { - return null; - } - - // return object - const object: PreObjectImpl = $getPreObject(); - - // setup - object.matrix = multiMatrix; - - // check - const blendMode: BlendModeImpl = this._$blendMode; - if (blendMode !== "normal" - || this._$filters && this._$filters.length > 0 - ) { - - // check size - const baseBounds: BoundsImpl = this._$getBounds(null); - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - $poolBoundsObject(baseBounds); - - const xMax: number = +bounds.xMax; - const xMin: number = +bounds.xMin; - const yMax: number = +bounds.yMax; - const yMin: number = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - if (0 >= width || 0 >= height) { - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - object.canApply = this._$canApply(this._$filters); - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (object.canApply && this._$filters) { - for (let idx: number = 0; idx < this._$filters.length ; ++idx) { - filterBounds = this._$filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - const currentAttachment: AttachmentImpl | null = context - .frameBuffer - .currentAttachment; - - if (!currentAttachment - || !currentAttachment.texture - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - $poolPreObject(object); - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - return null; - } - - // move size - let tx: number = multiMatrix[4] - xMin; - let ty: number = multiMatrix[5] - yMin; - - // start layer - context._$startLayer( - $getBoundsObject(xMin, xMax, yMin, yMax) - ); - - // check cache - const updated: boolean = this._$isFilterUpdated( - multiMatrix, this._$filters, object.canApply - ); - - const layerBounds: BoundsImpl = this._$getLayerBounds(multiMatrix); - - const layerWidth: number = $Math.ceil($Math.abs(layerBounds.xMax - layerBounds.xMin)); - const layerHeight: number = $Math.ceil($Math.abs(layerBounds.yMax - layerBounds.yMin)); - $poolBoundsObject(layerBounds); - - const sw = layerWidth - filterBounds.xMax + filterBounds.xMin; - const sh = layerHeight - filterBounds.yMax + filterBounds.yMin; - - tx += sw; - ty += sh; - - object.sw = sw; - object.sh = sh; - if (updated) { - context._$saveAttachment( - $Math.ceil(width + sw), - $Math.ceil(height + sh), - true - ); - } - - // setup - object.isLayer = true; - object.isUpdated = updated; - object.filters = this._$filters; - object.blendMode = blendMode; - object.color = $getFloat32Array8(); - object.matrix = $getFloat32Array6( - multiMatrix[0], multiMatrix[1], - multiMatrix[2], multiMatrix[3], - tx, ty - ); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - $poolBoundsObject(filterBounds); - } - - return object; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {object} object - * @return {void} - * @method - * @private - */ - _$postDraw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array, - object: PreObjectImpl - ): void { - - context.drawInstacedArray(); - - // cache - const cacheKeys: any[] = $getArray(this._$instanceId, "f"); - - const manager: FrameBufferManager = context.frameBuffer; - const multiMatrix: Float32Array = object.matrix as NonNullable; - - let offsetX: number = 0; - let offsetY: number = 0; - let texture: WebGLTexture | null = $cacheStore.get(cacheKeys); - - if (!texture || object.isUpdated) { - - // remove - if (texture) { - $cacheStore.set(cacheKeys, null); - } - - texture = manager - .getTextureFromCurrentAttachment(); - - const filters: FilterArrayImpl | null = object.filters; - let filterState = false; - if (filters && filters.length) { - - for (let idx: number = 0; idx < filters.length; ++idx) { - texture = filters[idx] - ._$applyFilter(context, matrix); - } - - // update - filterState = true; - offsetX = context._$offsetX; - offsetY = context._$offsetY; - - // reset - context._$offsetX = 0; - context._$offsetY = 0; - } - - texture.filterState = filterState; - texture.matrix = `${multiMatrix[0]}_` - + `${multiMatrix[1]}_` - + `${multiMatrix[2]}_` - + `${multiMatrix[3]}`; - - texture.offsetX = offsetX; - texture.offsetY = offsetY; - - $cacheStore.set(cacheKeys, texture); - - context._$restoreAttachment(); - } - - if (texture.offsetX) { - offsetX = texture.offsetX; - } - - if (texture.offsetY) { - offsetY = texture.offsetY; - } - - // set - context.reset(); - context.globalAlpha = $clamp( - color_transform[3] + color_transform[7] / 255, 0, 1 - ); - context.globalCompositeOperation = object.blendMode; - - const bounds: BoundsImpl = context.getCurrentPosition(); - - context.setTransform( - 1, 0, 0, 1, - bounds.xMin - offsetX - object.sw, - bounds.yMin - offsetY - object.sh - ); - - context.drawImage(texture, - 0, 0, texture.width, texture.height, - color_transform - ); - - // end blend - context._$endLayer(); - - // end blend - context._$endLayer(); - - // object pool - $poolFloat32Array6(object.matrix as NonNullable); - $poolPreObject(object); - - // reset - context.cachePosition = null; - } - - /** - * @description Playerから登録を削除 - * - * @return {void} - * @method - * @private - */ - _$remove (): void - { - // reset - this._$children = new Int32Array(); - - super._$remove(); - - $containers.push(this); - } -} \ No newline at end of file diff --git a/worker/renderer/src/display/RenderGraphics.ts b/worker/renderer/src/display/RenderGraphics.ts deleted file mode 100644 index f27ebad6..00000000 --- a/worker/renderer/src/display/RenderGraphics.ts +++ /dev/null @@ -1,1176 +0,0 @@ -import { RenderDisplayObject } from "./RenderDisplayObject"; -import type { GridImpl } from "../interface/GridImpl"; -import type { BlendModeImpl } from "../interface/BlendModeImpl"; -import type { FilterArrayImpl } from "../interface/FilterArrayImpl"; -import type { BoundsImpl } from "../interface/BoundsImpl"; -import type { AttachmentImpl } from "../interface/AttachmentImpl"; -import type { SpreadMethodImpl } from "../interface/SpreadMethodImpl"; -import type { PropertyMessageMapImpl } from "../interface/PropertyMessageMapImpl"; -import type { ColorStopImpl } from "../interface/ColorStopImpl"; -import type { CachePositionImpl } from "../interface/CachePositionImpl"; -import type { ShapeModeImpl } from "../interface/ShapeModeImpl"; -import type { - CanvasToWebGLContext, - CanvasGradientToWebGL, - FrameBufferManager -} from "@next2d/webgl"; -import { $renderPlayer } from "../RenderGlobal"; -import { - $cacheStore, - $clamp, - $getBoundsObject, - $boundsMatrix, - $Math, - $poolBoundsObject, - $Infinity, - $Number, - $getArray, - $poolArray, - $getFloat32Array6, - $getFloat32Array4, - $multiplicationMatrix, - $poolFloat32Array6, - $getInt32Array4, - $linearGradientXY, - $getFloat32Array8 -} from "@next2d/share"; - -/** - * @class - */ -export class RenderGraphics extends RenderDisplayObject -{ - public _$recodes: Float32Array | null; - public _$maxAlpha: number; - public _$canDraw: boolean; - private _$uniqueKey: string; - private _$cacheKeys: string[]; - private _$cacheParams: number[]; - public _$bitmapId: number; - public _$mode: ShapeModeImpl; - - /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {Float32Array} - * @default null - * @private - */ - this._$recodes = null; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$maxAlpha = 0; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$canDraw = false; - - /** - * @type {string} - * @default "" - * @private - */ - this._$uniqueKey = ""; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$bitmapId = 0; - - /** - * @type {string} - * @default "shape" - * @private - */ - this._$mode = "shape"; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - if (!this._$recodes) { - return ; - } - - // size - const baseBounds: BoundsImpl = this._$getBounds(); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil($Math.abs(bounds.xMax - bounds.xMin)); - const height: number = $Math.ceil($Math.abs(bounds.yMax - bounds.yMin)); - $poolBoundsObject(bounds); - - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - context.reset(); - context.setTransform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - this._$runCommand(context, this._$recodes, null, true); - - context.clip(); - } - - /** - * @return {string} - * @method - * @private - */ - _$createCacheKey (): string - { - if (!this._$recodes) { - return ""; - } - - let hash = 0; - for (let idx: number = 0; idx < this._$recodes.length; idx++) { - - const chr: number = this._$recodes[idx]; - - hash = (hash << 5) - hash + chr; - hash |= 0; - } - - return `${hash}`; - } - - /** - * @return {WebGLTexture | null} - * @method - * @private - */ - _$createBitmapTexture ( - context: CanvasToWebGLContext, - position: CachePositionImpl, - x_scale: number, - y_scale: number, - width: number, - height: number - ): WebGLTexture | null { - - if (this._$mode !== "bitmap") { - return null; - } - - context.drawInstacedArray(); - - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - - context._$bind(attachment); - - context.reset(); - - const parentMatrix: Float32Array = $getFloat32Array6( - x_scale, 0, 0, y_scale, - width / 2, height / 2 - ); - - const texture: WebGLTexture = context.getTextureFromRect(position); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - -texture.width / 2, - -texture.height / 2 - ); - - const scaleMatrix = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - context.setTransform( - scaleMatrix[0], scaleMatrix[1], - scaleMatrix[2], scaleMatrix[3], - scaleMatrix[4], scaleMatrix[5] - ); - - context.drawImage(texture, 0, 0, texture.width, texture.height); - - const bitmapTexture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - context._$bind(currentAttachment); - - manager.releaseAttachment(attachment); - manager.textureManager.release(texture); - - return bitmapTexture; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {string} [blend_mode=BlendMode.NORMAL] - * @param {array} [filters=null] - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array, - blend_mode: BlendModeImpl = "normal", - filters: FilterArrayImpl | null = null - ): void { - - if (!this._$visible - || !this._$recodes - || !this._$maxAlpha - || !this._$canDraw - ) { - return ; - } - - const alpha: number = $clamp(color_transform[3] + color_transform[7] / 255, 0, 1, 0); - if (!alpha) { - return ; - } - - const rawMatrix: Float32Array = this._$matrix; - - // set grid data - let hasGrid: boolean = this._$scale9Grid !== null; - - // 9スライスを有効にしたオブジェクトが回転・傾斜成分を含む場合は - // 9スライスは無効になる - if (hasGrid) { - hasGrid = hasGrid - && $Math.abs(rawMatrix[1]) < 0.001 - && $Math.abs(rawMatrix[2]) < 0.0001; - } - - // size - const baseBounds: BoundsImpl = $getBoundsObject( - this._$xMin, this._$xMax, - this._$yMin, this._$yMax - ); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, matrix); - const xMax: number = bounds.xMax; - const xMin: number = bounds.xMin; - const yMax: number = bounds.yMax; - const yMin: number = bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === 0 - $Infinity: - case height === 0 - $Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - matrix[0] * matrix[0] - + matrix[1] * matrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - matrix[2] * matrix[2] - + matrix[3] * matrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const canApply: boolean = filters !== null - && filters.length > 0 - && this._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - - $poolBoundsObject(filterBounds); - - // get cache - if (this._$uniqueKey === "") { - if (!hasGrid - && this._$loaderInfoId > -1 - && this._$characterId > -1 - ) { - this._$uniqueKey = `${this._$loaderInfoId}@${this._$characterId}`; - } else { - this._$uniqueKey = this._$createCacheKey(); - } - } - - if (this._$mode === "bitmap") { - - if (!this._$cacheKeys.length) { - this._$cacheKeys = $cacheStore.generateKeys(this._$uniqueKey); - } - - } else { - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - - const keys: number[] = $getArray(); - keys[0] = xScale; - keys[1] = yScale; - - this._$cacheKeys = $cacheStore.generateKeys( - this._$uniqueKey, keys, color_transform - ); - - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - } - - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - if (currentAttachment && currentAttachment.mask) { - context.stopStencil(); - } - - let width: number = 0; - let height: number = 0; - if (this._$mode === "shape") { - - width = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin) * xScale); - height = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin) * yScale); - - // resize - const textureScale: number = context._$getTextureScale(width, height); - if (textureScale < 1) { - width *= textureScale; - height *= textureScale; - } - - } else { - width = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin)); - height = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin)); - } - - // create cache position - context.cachePosition = manager.createCachePosition(width, height); - context.bindRenderBuffer(context.cachePosition); - - // reset - context.reset(); - - if (this._$mode === "shape") { - context.setTransform( - xScale, 0, 0, yScale, - -baseBounds.xMin * xScale, - -baseBounds.yMin * yScale - ); - } else { - context.setTransform( - 1, 0, 0, 1, - -baseBounds.xMin, - -baseBounds.yMin - ); - } - - if (hasGrid) { - - const mScale: number = $renderPlayer.scaleX; - - const baseMatrix: Float32Array = $getFloat32Array6( - mScale, 0, 0, mScale, 0, 0 - ); - - const pMatrix: Float32Array = $multiplicationMatrix( - baseMatrix, rawMatrix - ); - - $poolFloat32Array6(baseMatrix); - - const aMatrixBase: Float32Array = this._$matrixBase as NonNullable; - const aMatrix = $getFloat32Array6( - aMatrixBase[0], aMatrixBase[1], - aMatrixBase[2], aMatrixBase[3], - aMatrixBase[4] * mScale - xMin, - aMatrixBase[5] * mScale - yMin - ); - - const apMatrix: Float32Array = $multiplicationMatrix( - aMatrix, pMatrix - ); - - const aOffsetX: number = apMatrix[4] - (matrix[4] - xMin); - const aOffsetY: number = apMatrix[5] - (matrix[5] - yMin); - $poolFloat32Array6(apMatrix); - - const parentBounds: BoundsImpl = $boundsMatrix(baseBounds, pMatrix); - const parentXMax: number = +parentBounds.xMax; - const parentXMin: number = +parentBounds.xMin; - const parentYMax: number = +parentBounds.yMax; - const parentYMin: number = +parentBounds.yMin; - const parentWidth: number = $Math.ceil($Math.abs(parentXMax - parentXMin)); - const parentHeight: number = $Math.ceil($Math.abs(parentYMax - parentYMin)); - - $poolBoundsObject(parentBounds); - - context.grid.enable( - parentXMin, parentYMin, parentWidth, parentHeight, - baseBounds, this._$scale9Grid as NonNullable, mScale, - pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5], - aMatrix[0], aMatrix[1], aMatrix[2], aMatrix[3], aMatrix[4] - aOffsetX, aMatrix[5] - aOffsetY - ); - - $poolFloat32Array6(pMatrix); - $poolFloat32Array6(aMatrix); - } - - this._$runCommand(context, this._$recodes, color_transform, false); - - if (hasGrid) { - context.grid.disable(); - } - - manager.transferTexture(context.cachePosition); - - // set cache - $cacheStore.set(this._$cacheKeys, context.cachePosition); - - // end draw and reset current buffer - context._$bind(currentAttachment); - } - - let offsetX: number = 0; - let offsetY: number = 0; - if (canApply && filters) { - - const bitmapTexture: WebGLTexture | null = this._$createBitmapTexture( - context, context.cachePosition, - xScale, yScale, width, height - ); - - const position: CachePositionImpl = this._$drawFilter( - context, matrix, filters, - width, height, bitmapTexture - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - } - - if (!canApply && this._$mode === "bitmap") { - - context.setTransform( - matrix[0], matrix[1], - matrix[2], matrix[3], - baseBounds.xMin * matrix[0] + baseBounds.yMin * matrix[2] + matrix[4], - baseBounds.xMin * matrix[1] + baseBounds.yMin * matrix[3] + matrix[5] - ); - - } else { - - const radianX: number = $Math.atan2(matrix[1], matrix[0]); - const radianY: number = $Math.atan2(-matrix[2], matrix[3]); - if (!canApply && (radianX || radianY)) { - - const tx: number = baseBounds.xMin * xScale; - const ty: number = baseBounds.yMin * yScale; - - const cosX: number = $Math.cos(radianX); - const sinX: number = $Math.sin(radianX); - const cosY: number = $Math.cos(radianY); - const sinY: number = $Math.sin(radianY); - - context.setTransform( - cosX, sinX, -sinY, cosY, - tx * cosX - ty * sinY + matrix[4], - tx * sinX + ty * cosY + matrix[5] - ); - - } else { - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = this._$mode === "shape"; - context.globalCompositeOperation = blend_mode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; - } - - // pool - $poolBoundsObject(baseBounds); - } - - /** - * @description strokeのセットアップ - * - * @param {CanvasToWebGLContext} context - * @param {number} line_width - * @param {number} line_cap - * @param {number} line_join - * @param {number} miter_limit - * @return {void} - * @method - * @public - */ - setupStroke ( - context: CanvasToWebGLContext, - line_width: number, line_cap: number, - line_join: number, miter_limit: number - ): void { - - context.lineWidth = line_width; - - switch (line_cap) { - - case 0: - context.lineCap = "none"; - break; - - case 1: - context.lineCap = "round"; - break; - - case 2: - context.lineCap = "square"; - break; - - } - - switch (line_join) { - - case 0: - context.lineJoin = "bevel"; - break; - - case 1: - context.lineJoin = "miter"; - break; - - case 2: - context.lineJoin = "round"; - break; - - } - - context.miterLimit = miter_limit; - } - - /** - * @description CanvasGradientToWebGLオブジェクトを生成 - * - * @param {CanvasToWebGLContext} context - * @param {number} type - * @param {array} stops - * @param {Float32Array} matrix - * @param {number} spread - * @param {number} interpolation - * @param {number} focal - * @param {Float32Array} [color_transform=null] - * @return {CanvasGradientToWebGL} - * @method - * @public - */ - createGradientStyle ( - context: CanvasToWebGLContext, - type: number, stops: ColorStopImpl[], - matrix: Float32Array, - spread: number, interpolation: number, focal: number, - color_transform: Float32Array | null = null - ): CanvasGradientToWebGL { - - let spreadMethod: SpreadMethodImpl = "pad"; - switch (spread) { - - case 0:// REFLECT - spreadMethod = "reflect"; - break; - - case 1: // REPEAT - spreadMethod = "repeat"; - break; - - } - - let css: CanvasGradientToWebGL; - if (type === 0) { - - // LINEAR - const xy: Float32Array = $linearGradientXY(matrix); - css = context.createLinearGradient( - xy[0], xy[1], xy[2], xy[3], - interpolation ? "rgb" : "linearRGB", - spreadMethod - ); - - } else { - - // RADIAL - context.save(); - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - - css = context.createRadialGradient( - 0, 0, 0, 0, 0, 819.2, - interpolation ? "rgb" : "linearRGB", - spreadMethod, focal - ); - - } - - for (let idx: number = 0; idx < stops.length; ++idx) { - - const color: ColorStopImpl = stops[idx]; - - let alpha: number = color.A; - if (color_transform) { - if (color_transform[3] !== 1 || color_transform[7] !== 0) { - alpha = $Math.max(0, $Math.min(color.A * color_transform[3] + color_transform[7], 255)) | 0; - } - } - - css.addColorStop(color.ratio, $getInt32Array4( - color.R, color.G, color.B, alpha - )); - } - - return css; - } - - /** - * @description Graphicsクラスの描画を実行 - * Execute drawing in the Graphics class - * - * @param {CanvasToWebGLContext} context - * @param {Float32Array} recodes - * @param {Float32Array} [color_transform=null] - * @param {boolean} [is_clip=false] - * @return {void} - * @method - * @public - */ - _$runCommand ( - context: CanvasToWebGLContext, - recodes: Float32Array, - color_transform: Float32Array | null = null, - is_clip: boolean = false - ): void { - - // reset - context.reset(); - context.beginPath(); - - const length: number = recodes.length; - for (let idx: number = 0; idx < length; ) { - - switch (recodes[idx++]) { - - case 9: // BEGIN_PATH - context.beginPath(); - break; - - case 0: // MOVE_TO - context.moveTo(recodes[idx++], recodes[idx++]); - break; - - case 2: // LINE_TO - context.lineTo(recodes[idx++], recodes[idx++]); - break; - - case 1: // CURVE_TO - context.quadraticCurveTo( - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++] - ); - break; - - case 5: // FILL_STYLE - { - if (is_clip) { - idx += 4; - continue; - } - - const color: Float32Array = $getFloat32Array4(); - color[0] = recodes[idx++] / 255; - color[1] = recodes[idx++] / 255; - color[2] = recodes[idx++] / 255; - color[3] = recodes[idx++] / 255; - - if (color_transform !== null) { - if (color_transform[3] !== 1 || color_transform[7] !== 0) { - color[3] = $Math.max(0, $Math.min( - color[3] * color_transform[3] + color_transform[7], 255) - ) / 255; - } - } - - context.fillStyle = color; - } - break; - - case 7: // END_FILL - - if (!is_clip) { - context.fill(); - } - - break; - - case 6: // STROKE_STYLE - { - if (is_clip) { - idx += 8; - continue; - } - - this.setupStroke( - context, - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++] - ); - - const color = $getFloat32Array4(); - - color[0] = recodes[idx++] / 255; - color[1] = recodes[idx++] / 255; - color[2] = recodes[idx++] / 255; - color[3] = recodes[idx++] / 255; - - if (color_transform !== null) { - if (color_transform[3] !== 1 || color_transform[7] !== 0) { - color[3] = $Math.max(0, $Math.min( - color[3] * color_transform[3] + color_transform[7], 255) - ) / 255; - } - } - - context.strokeStyle = color; - } - break; - - case 8: // END_STROKE - if (!is_clip) { - context.stroke(); - } - break; - - case 12: // CLOSE_PATH - context.closePath(); - break; - - case 3: // CUBIC - context.bezierCurveTo( - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++] - ); - break; - - case 4: // ARC - context.arc(recodes[idx++], recodes[idx++], recodes[idx++]); - break; - - case 10: // GRADIENT_FILL - { - if (is_clip) { - idx += 1; - const length = recodes[idx++]; - idx += length * 5; - idx += 9; - continue; - } - - const type: number = recodes[idx++]; - - let stopLength: number = recodes[idx++]; - const stops: ColorStopImpl[] = $getArray(); - while (stopLength) { - stops.push({ - "ratio": recodes[idx++], - "R": recodes[idx++], - "G": recodes[idx++], - "B": recodes[idx++], - "A": recodes[idx++] - }); - stopLength--; - } - - const matrix: Float32Array = $getFloat32Array6( - recodes[idx++], recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++], recodes[idx++] - ); - - context.fillStyle = this.createGradientStyle( - context, type, stops, matrix, - recodes[idx++], recodes[idx++], recodes[idx++], - color_transform - ); - - context.fill(); - - // if RADIAL - if (type === 1) { - context.restore(); - } - - $poolFloat32Array6(matrix); - $poolArray(stops); - } - break; - - case 11: // GRADIENT_STROKE - { - if (is_clip) { - idx += 5; - const length = recodes[idx++]; - idx += length * 5; - idx += 9; - continue; - } - - this.setupStroke( - context, - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++] - ); - - const type: number = recodes[idx++]; - - let stopLength: number = recodes[idx++]; - - const stops: ColorStopImpl[] = $getArray(); - while (stopLength) { - stops.push({ - "ratio": recodes[idx++], - "R": recodes[idx++], - "G": recodes[idx++], - "B": recodes[idx++], - "A": recodes[idx++] - }); - stopLength--; - } - - const matrix: Float32Array = $getFloat32Array6( - recodes[idx++], recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++], recodes[idx++] - ); - - context.strokeStyle = this.createGradientStyle( - context, type, stops, matrix, - recodes[idx++], recodes[idx++], recodes[idx++], - color_transform - ); - - context.stroke(); - - // if RADIAL - if (type === 1) { - context.restore(); - } - - $poolFloat32Array6(matrix); - $poolArray(stops); - } - break; - - case 13: // BITMAP_FILL - { - const width: number = recodes[idx++]; - const height: number = recodes[idx++]; - const graphicsWidth: number = recodes[idx++]; - const graphicsHeight: number = recodes[idx++]; - const bitmapLength: number = recodes[idx++]; - if (is_clip) { - idx += bitmapLength; - idx += 8; - continue; - } - - const buffer: Uint8Array = new Uint8Array( - recodes.subarray(idx, bitmapLength + idx) - ); - - idx += bitmapLength; - const matrix: Float32Array = $getFloat32Array6( - recodes[idx++], recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++], recodes[idx++] - ); - - const repeat: boolean = !!recodes[idx++]; - const smooth: boolean = !!recodes[idx++]; - - context.save(); - - if (matrix[0] !== 1 || matrix[1] !== 0 - || matrix[2] !== 0 || matrix[3] !== 1 - || matrix[4] !== 0 || matrix[5] !== 0 - ) { - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } - $poolFloat32Array6(matrix); - - const manager: FrameBufferManager = context.frameBuffer; - const texture: WebGLTexture = manager.createTextureFromPixels( - width, height, buffer, true - ); - - if (!repeat - && width === graphicsWidth - && height === graphicsHeight - ) { - - context.drawImage(texture, 0, 0, width, height); - manager.releaseTexture(texture); - - } else { - - context.fillStyle = context.createPattern( - texture, repeat, color_transform || $getFloat32Array8() - ); - - context.imageSmoothingEnabled = smooth; - context.fill(); - - } - - // restore - context.restore(); - context.imageSmoothingEnabled = false; - - } - break; - - case 14: // BITMAP_STROKE - { - if (is_clip) { - idx += 4; - const bitmapLength = recodes[idx++]; - idx += bitmapLength; - idx += 8; - continue; - } - - context.save(); - - this.setupStroke( - context, - recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++] - ); - - const width: number = recodes[idx++]; - const height: number = recodes[idx++]; - const bitmapLength: number = recodes[idx++]; - - const buffer: Uint8Array = new Uint8Array( - recodes.subarray(idx, bitmapLength + idx) - ); - - idx += bitmapLength; - const matrix: Float32Array = $getFloat32Array6( - recodes[idx++], recodes[idx++], recodes[idx++], - recodes[idx++], recodes[idx++], recodes[idx++] - ); - - if (matrix[0] !== 1 || matrix[1] !== 0 - || matrix[2] !== 0 || matrix[3] !== 1 - || matrix[4] !== 0 || matrix[5] !== 0 - ) { - context.transform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - } - $poolFloat32Array6(matrix); - - const repeat: boolean = !!recodes[idx++]; - const smooth: boolean = !!recodes[idx++]; - - const manager: FrameBufferManager = context.frameBuffer; - const texture: WebGLTexture = manager.createTextureFromPixels( - width, height, buffer, true - ); - - context.strokeStyle = context.createPattern( - texture, repeat, color_transform || $getFloat32Array8() - ); - - context.imageSmoothingEnabled = smooth; - context.stroke(); - - // restore - context.restore(); - context.imageSmoothingEnabled = false; - } - break; - - default: - break; - - } - } - } - - /** - * @description 描画情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$update (object: PropertyMessageMapImpl): void - { - super._$update(object); - - if (object.recodes) { - - this._$recodes = object.recodes; - this._$xMin = object.xMin; - this._$yMin = object.yMin; - this._$xMax = object.xMax; - this._$yMax = object.yMax; - this._$maxAlpha = object.maxAlpha; - this._$canDraw = object.canDraw; - - $cacheStore.removeCache(this._$instanceId); - - if (this._$loaderInfoId > -1 - && this._$characterId > -1 - ) { - $cacheStore.removeCache( - `${this._$loaderInfoId}@${this._$characterId}` - ); - } - } - } -} \ No newline at end of file diff --git a/worker/renderer/src/display/RenderShape.ts b/worker/renderer/src/display/RenderShape.ts deleted file mode 100644 index 2c826a9f..00000000 --- a/worker/renderer/src/display/RenderShape.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { RenderGraphics } from "./RenderGraphics"; -import { $shapes } from "../RenderGlobal"; -import type { BoundsImpl } from "../interface/BoundsImpl"; -import type { CanvasToWebGLContext } from "@next2d/webgl"; -import { - $boundsMatrix, - $clamp, - $Infinity, - $Math, - $multiplicationColor, - $multiplicationMatrix, - $poolBoundsObject, - $poolFloat32Array6, - $poolFloat32Array8 -} from "@next2d/share"; - -/** - * @class - */ -export class RenderShape extends RenderGraphics -{ - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - // size - const baseBounds: BoundsImpl = this._$getBounds(); - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - $poolBoundsObject(baseBounds); - - const width: number = $Math.ceil($Math.abs(bounds.xMax - bounds.xMin)); - const height: number = $Math.ceil($Math.abs(bounds.yMax - bounds.yMin)); - $poolBoundsObject(bounds); - - switch (true) { - - case width === 0: - case height === 0: - case width === 0 - $Infinity: - case height === 0 - $Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - super._$clip(context, multiMatrix); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible || !this._$maxAlpha || !this._$canDraw) { - return ; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$colorTransform; - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - return ; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - super._$draw( - context, multiMatrix, multiColor, - this._$blendMode, this._$filters - ); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - } - - /** - * @description Playerから登録を削除 - * - * @return {void} - * @method - * @private - */ - _$remove (): void - { - this._$xMin = 0; - this._$yMin = 0; - this._$xMax = 0; - this._$yMax = 0; - this._$recodes = null; - - super._$remove(); - - $shapes.push(this); - } -} \ No newline at end of file diff --git a/worker/renderer/src/display/RenderTextField.ts b/worker/renderer/src/display/RenderTextField.ts deleted file mode 100644 index 1a16d4cc..00000000 --- a/worker/renderer/src/display/RenderTextField.ts +++ /dev/null @@ -1,1031 +0,0 @@ -import { RenderDisplayObject } from "./RenderDisplayObject"; -import type{ TextDataImpl } from "../interface/TextDataImpl"; -import type{ PropertyTextMessageImpl } from "../interface/PropertyTextMessageImpl"; -import type{ TextFieldAutoSizeImpl } from "../interface/TextFieldAutoSizeImpl"; -import type{ TextFormatVerticalAlignImpl } from "../interface/TextFormatVerticalAlignImpl"; -import type{ BoundsImpl } from "../interface/BoundsImpl"; -import type{ AttachmentImpl } from "../interface/AttachmentImpl"; -import type{ RGBAImpl } from "../interface/RGBAImpl"; -import type{ TextFormatImpl } from "../interface/TextFormatImpl"; -import type { FilterArrayImpl } from "../interface/FilterArrayImpl"; -import type { CachePositionImpl } from "../interface/CachePositionImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $isSafari, - $textFields -} from "../RenderGlobal"; -import { - $cacheStore, - $boundsMatrix, - $clamp, - $generateFontStyle, - $getArray, - $Infinity, - $intToRGBA, - $Math, - $multiplicationColor, - $multiplicationMatrix, - $Number, - $poolArray, - $poolBoundsObject, - $poolFloat32Array6, - $poolFloat32Array8, - $getBoundsObject -} from "@next2d/share"; - -/** - * @class - */ -export class RenderTextField extends RenderDisplayObject -{ - private _$background: boolean; - private _$backgroundColor: number; - private _$border: boolean; - private _$borderColor: number; - private readonly _$textData: TextDataImpl[]; - private _$textAreaActive: boolean; - private _$thickness: number; - private _$thicknessColor: number; - private _$limitWidth: number; - private _$limitHeight: number; - private _$autoSize: TextFieldAutoSizeImpl; - private readonly _$widthTable: number[]; - private readonly _$heightTable: number[]; - private readonly _$objectTable: TextDataImpl[]; - private readonly _$textHeightTable: number[]; - private _$scrollV: number; - private _$maxScrollV: number | null; - private _$textHeight: number; - private _$verticalAlign: TextFormatVerticalAlignImpl; - private _$wordWrap: boolean; - private _$cacheKeys: string[]; - private _$cacheParams: number[]; - - /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$background = false; - - /** - * @type {number} - * @default 0xffffff - * @private - */ - this._$backgroundColor = 0xffffff; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$border = false; - - /** - * @type {number} - * @default 0x000000 - * @private - */ - this._$borderColor = 0x000000; - - /** - * @type {boolean} - * @default false - * @private - */ - this._$wordWrap = false; - - /** - * @type {array} - * @private - */ - this._$textData = $getArray(); - - /** - * @type {boolean} - * @default false - * @private - */ - this._$textAreaActive = false; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$thickness = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$thicknessColor = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$limitWidth = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$limitHeight = 0; - - /** - * @type {string} - * @default TextFieldAutoSize.NONE - * @private - */ - this._$autoSize = "none"; - - /** - * @type {array} - * @default null - * @private - */ - this._$widthTable = $getArray(); - - /** - * @type {array} - * @default null - * @private - */ - this._$heightTable = $getArray(); - - /** - * @type {array} - * @default null - * @private - */ - this._$objectTable = $getArray(); - - /** - * @type {array} - * @default null - * @private - */ - this._$textHeightTable = $getArray(); - - /** - * @type {number} - * @default 0 - * @private - */ - this._$xMin = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$yMin = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$xMax = 0; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$yMax = 0; - - /** - * @type {number} - * @default null - * @private - */ - this._$maxScrollV = null; - - /** - * @type {number} - * @default 1 - * @private - */ - this._$scrollV = 1; - - /** - * @type {number} - * @default 0 - * @private - */ - this._$textHeight = 0; - - /** - * @type {string} - * @default TextFormatVerticalAlign.TOP - * @private - */ - this._$verticalAlign = "top"; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - } - - /** - * @description 表示オブジェクトの幅を示します(ピクセル単位)。 - * Indicates the width of the display object, in pixels. - * - * @member {number} - * @public - */ - get width (): number - { - const bounds: BoundsImpl = $boundsMatrix( - this._$getBounds(null), - this._$matrix - ); - - const width: number = $Math.abs(bounds.xMax - bounds.xMin); - $poolBoundsObject(bounds); - - switch (true) { - - case width === 0: - case width === $Infinity: - case width === 0 - $Infinity: - return 0; - - default: - return width; - - } - } - - /** - * @description 表示オブジェクトの高さを示します(ピクセル単位)。 - * Indicates the height of the display object, in pixels. - * - * @member {number} - * @public - */ - get height (): number - { - const bounds: BoundsImpl = $boundsMatrix( - this._$getBounds(null), - this._$matrix - ); - - const height: number = $Math.abs(bounds.yMax - bounds.yMin); - - // object pool - $poolBoundsObject(bounds); - - switch (height) { - - case 0: - case $Infinity: - case 0 - $Infinity: - return 0; - - default: - return height; - - } - } - - /** - * @description scrollV の最大値です。 - * The maximum value of scrollV. - * - * @member {number} - * @readonly - * @public - */ - get maxScrollV (): number - { - if (this._$maxScrollV === null) { - - this._$maxScrollV = 1; - - const length: number = this._$textHeightTable.length; - const maxHeight: number = this.height; - - if (maxHeight > this._$textHeight) { - return this._$maxScrollV; - } - - let textHeight: number = 0; - - let idx: number = 0; - while (length > idx) { - - textHeight += this._$textHeightTable[idx++]; - if (textHeight > maxHeight) { - break; - } - - this._$maxScrollV++; - } - } - return this._$maxScrollV; - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - // size - const bounds: BoundsImpl = this._$getBounds(); - const xMax: number = bounds.xMax; - const xMin: number = bounds.xMin; - const yMax: number = bounds.yMax; - const yMin: number = bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - if (!width || !height) { - return; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - context.reset(); - context.setTransform( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - context.clip(); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible || this._$textAreaActive) { - return ; - } - - if (!this._$background && !this._$border - && 2 > this._$textData.length - ) { - return; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$colorTransform; - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0 ,1); - if (!alpha) { - return ; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - const baseBounds: BoundsImpl = this._$getBounds(null); - baseBounds.xMin -= this._$thickness; - baseBounds.xMax += this._$thickness; - baseBounds.yMin -= this._$thickness; - baseBounds.yMax += this._$thickness; - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - const xMax = +bounds.xMax; - const xMin = +bounds.xMin; - const yMax = +bounds.yMax; - const yMin = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === -$Infinity: - case height === -$Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const filters: FilterArrayImpl | null = this._$filters; - const canApply: boolean = filters !== null - && filters.length > 0 - && this._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - - $poolBoundsObject(filterBounds); - - // texture is small or renew - if (this._$isUpdated()) { - $cacheStore.removeCache(this._$instanceId); - context.cachePosition = null; - this._$cacheKeys.length = 0; - } - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - const keys: number[] = $getArray(xScale, yScale); - this._$cacheKeys = $cacheStore.generateKeys( - this._$instanceId, keys - ); - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - // resize - const lineWidth: number = $Math.min(1, $Math.max(xScale, yScale)); - const baseWidth: number = $Math.ceil($Math.abs(baseBounds.xMax - baseBounds.xMin) * xScale); - const baseHeight: number = $Math.ceil($Math.abs(baseBounds.yMax - baseBounds.yMin) * yScale); - - // alpha reset - multiColor[3] = 1; - - // new canvas - const canvas: OffscreenCanvas = new OffscreenCanvas( - baseWidth + lineWidth * 2, - baseHeight + lineWidth * 2 - ); - const ctx: OffscreenCanvasRenderingContext2D | null = canvas.getContext("2d"); - if (!ctx) { - return ; - } - - // border and background - if (this._$background || this._$border) { - - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(baseWidth, 0); - ctx.lineTo(baseWidth, baseHeight); - ctx.lineTo(0, baseHeight); - ctx.lineTo(0, 0); - - if (this._$background) { - - const color: RGBAImpl = $intToRGBA(this._$backgroundColor); - const alpha: number = $Math.max(0, $Math.min( - color.A * 255 * color_transform[3] + color_transform[7], 255) - ) / 255; - - ctx.fillStyle = `rgba(${color.R},${color.G},${color.B},${alpha})`; - ctx.fill(); - } - - if (this._$border) { - - const color: RGBAImpl = $intToRGBA(this._$borderColor); - const alpha: number = $Math.max(0, $Math.min( - color.A * 255 * color_transform[3] + color_transform[7], 255) - ) / 255; - - ctx.lineWidth = lineWidth; - ctx.strokeStyle = `rgba(${color.R},${color.G},${color.B},${alpha})`; - ctx.stroke(); - } - - } - - // mask start - ctx.save(); - ctx.beginPath(); - ctx.moveTo(2, 2); - ctx.lineTo(baseWidth - 2, 2); - ctx.lineTo(baseWidth - 2, baseHeight - 2); - ctx.lineTo(2, baseHeight - 2); - ctx.lineTo(2, 2); - ctx.clip(); - - ctx.beginPath(); - ctx.setTransform(xScale, 0, 0, yScale, 0, 0); - this._$doDraw(ctx, matrix, color_transform, baseWidth / xScale); - ctx.restore(); - - const position: CachePositionImpl = manager - .createCachePosition(width, height); - - const texture: WebGLTexture = manager - .createTextureFromCanvas(ctx.canvas); - - context.drawTextureFromRect(texture, position); - - // set cache - context.cachePosition = position; - $cacheStore.set(this._$cacheKeys, position); - } - - let drawFilter: boolean = false; - let offsetX: number = 0; - let offsetY: number = 0; - if (filters && filters.length - && this._$canApply(filters) - ) { - - drawFilter = true; - - const position: CachePositionImpl = this._$drawFilter( - context, multiMatrix, filters, - width, height - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - } - - const radianX: number = $Math.atan2(multiMatrix[1], multiMatrix[0]); - const radianY: number = $Math.atan2(-multiMatrix[2], multiMatrix[3]); - if (!drawFilter && (radianX || radianY)) { - - const tx: number = baseBounds.xMin * xScale; - const ty: number = baseBounds.yMin * yScale; - - const cosX: number = $Math.cos(radianX); - const sinX: number = $Math.sin(radianX); - const cosY: number = $Math.cos(radianY); - const sinY: number = $Math.sin(radianY); - - context.setTransform( - cosX, sinX, -sinY, cosY, - tx * cosX - ty * sinY + multiMatrix[4], - tx * sinX + ty * cosY + multiMatrix[5] - ); - - } else { - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = true; - context.globalCompositeOperation = this._$blendMode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; - } - - // get cache - $poolBoundsObject(baseBounds); - - // pool - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - } - - /** - * @param {CanvasRenderingContext2D} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @param {number} width - * @return {void} - * @method - * @private - */ - _$doDraw ( - context: OffscreenCanvasRenderingContext2D, - matrix: Float32Array, - color_transform: Float32Array, - width: number - ): void { - - const limitWidth: number = this.width; - const limitHeight: number = this.height; - - // setup - let xOffset: number = 0; - let offsetHeight: number = 0; - let currentV: number = 0; - - let yOffset: number = 0; - if (this._$verticalAlign !== "top" - && this.height > this._$textHeight - ) { - - switch (this._$verticalAlign) { - - case "middle": - yOffset = (this.height - this._$textHeight + 2) / 2; - break; - - case "bottom": - yOffset = this.height - this._$textHeight + 2; - break; - - } - - } - - const length: number = this._$textData.length; - for (let idx: number = 0; idx < length; ++idx) { - - const obj: TextDataImpl = this._$textData[idx]; - if (obj.width === 0) { - continue; - } - - // check - const offsetWidth: number = xOffset + obj.x; - if (this._$autoSize === "none" - && (offsetHeight > limitHeight || offsetWidth > limitWidth) - ) { - continue; - } - - const tf: TextFormatImpl = obj.textFormat; - - // color - const color: RGBAImpl = $intToRGBA(obj.textFormat._$color); - const alpha: number = $Math.max(0, $Math.min( - color.A * 255 * color_transform[3] + color_transform[7], 255) - ) / 255; - - context.fillStyle = `rgba(${color.R},${color.G},${color.B},${alpha})`; - - if (this._$thickness) { - const color: RGBAImpl = $intToRGBA(this._$thicknessColor); - const alpha: number = $Math.max(0, $Math.min( - color.A * 255 * color_transform[3] + color_transform[7], 255) - ) / 255; - context.lineWidth = this._$thickness; - context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${alpha})`; - } - - const yIndex: number = obj.yIndex; - switch (obj.mode) { - - case "break": - case "wrap": - - currentV++; - - if (this._$scrollV > currentV) { - continue; - } - - offsetHeight += this._$textHeightTable[yIndex]; - - xOffset = this._$getAlignOffset(this._$objectTable[yIndex], width); - if (tf._$underline) { - - const offset: number = obj.textFormat._$size / 12; - - const color: RGBAImpl = $intToRGBA(tf._$color); - const alpha: number = $Math.max(0, $Math.min( - color.A * 255 * color_transform[3] + color_transform[7], 255) - ) / 255; - - context.lineWidth = $Math.max(1, 1 / $Math.min(matrix[0], matrix[3])); - context.strokeStyle = `rgba(${color.R},${color.G},${color.B},${alpha})`; - - context.beginPath(); - context.moveTo(xOffset, yOffset + offsetHeight - offset); - context.lineTo(xOffset + this._$widthTable[yIndex], yOffset + offsetHeight - offset); - context.stroke(); - - } - - break; - - case "text": - { - if (this._$scrollV > currentV) { - continue; - } - - let offsetY = offsetHeight - this._$heightTable[0]; - if (!$isSafari) { - offsetY += 2 * (obj.textFormat._$size / 12); - } - - context.beginPath(); - context.textBaseline = "top"; - context.font = $generateFontStyle( - tf._$font, tf._$size, tf._$italic, tf._$bold - ); - - if (this._$thickness) { - context.strokeText(obj.text, offsetWidth, yOffset + offsetY); - } - - context.fillText(obj.text, offsetWidth, yOffset + offsetY); - - } - break; - - case "image": - - if (!obj.loaded) { - continue; - } - - context.beginPath(); - context.drawImage(obj.image, - obj.hspace, yOffset + obj.y, - obj.width, obj.height - ); - - break; - - } - } - } - - /** - * @param {object} obj - * @param {number} width - * @return {number} - * @private - */ - _$getAlignOffset ( - obj: TextDataImpl, - width: number - ): number { - - // default - const totalWidth: number = this._$widthTable[obj.yIndex]; - const textFormat: TextFormatImpl = obj.textFormat; - const indent: number = textFormat._$blockIndent + textFormat._$leftMargin > 0 - ? textFormat._$blockIndent + textFormat._$leftMargin - : 0; - - switch (true) { - - // wordWrap case - case !this._$wordWrap && totalWidth > width: - return $Math.max(0, indent); - - case textFormat._$align === "center": // format CENTER - case this._$autoSize === "center": // autoSize CENTER - return $Math.max(0, width / 2 - indent - textFormat._$rightMargin - totalWidth / 2); - - case textFormat._$align === "right": // format RIGHT - case this._$autoSize === "right": // autoSize RIGHT - return $Math.max(0, width - indent - totalWidth - textFormat._$rightMargin - 2); - - // autoSize LEFT - // format LEFT - default: - return $Math.max(0, indent + 2); - - } - } - - /** - * @description Playerから登録を削除 - * - * @return {void} - * @method - * @private - */ - _$remove (): void - { - this._$xMin = 0; - this._$yMin = 0; - this._$xMax = 0; - this._$yMax = 0; - - // array reset - this._$textData.length = 0; - this._$widthTable.length = 0; - this._$heightTable.length = 0; - this._$objectTable.length = 0; - this._$textHeightTable.length = 0; - - this._$textAreaActive = false; - - super._$remove(); - - $textFields.push(this); - } - - /** - * @description 情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$updateProperty (object: PropertyTextMessageImpl): void - { - // update property - this._$textAreaActive = !!object.textAreaActive; - - // set array - this._$textData.length = 0; - this._$widthTable.length = 0; - this._$heightTable.length = 0; - this._$objectTable.length = 0; - this._$textHeightTable.length = 0; - - this._$textData.push(...object.textData); - this._$widthTable.push(...object.widthTable); - this._$heightTable.push(...object.heightTable); - this._$objectTable.push(...object.objectTable); - this._$textHeightTable.push(...object.textHeightTable); - - // format - this._$wordWrap = object.wordWrap; - this._$limitWidth = object.limitWidth; - this._$limitHeight = object.limitHeight; - this._$autoSize = object.autoSize; - this._$scrollV = object.scrollV; - this._$textHeight = object.textHeight; - this._$verticalAlign = object.verticalAlign; - - // color - this._$border = object.border; - if (this._$border) { - this._$borderColor = object.borderColor as NonNullable; - } - - this._$background = object.background; - if (this._$background) { - this._$backgroundColor = object.backgroundColor as NonNullable; - } - - if ("thickness" in object) { - this._$thickness = object.thickness; - this._$thicknessColor = object.thicknessColor as NonNullable; - } - } - - /** - * @description 描画情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$update (object: PropertyTextMessageImpl): void - { - super._$update(object); - - this._$textAreaActive = !!object.textAreaActive; - - this._$xMin = object.xMin as NonNullable; - this._$yMin = object.yMin as NonNullable; - this._$xMax = object.xMax as NonNullable; - this._$yMax = object.yMax as NonNullable; - - if (object.textData) { - this._$updateProperty(object); - } - } -} \ No newline at end of file diff --git a/worker/renderer/src/index.ts b/worker/renderer/src/index.ts deleted file mode 100644 index b14809fd..00000000 --- a/worker/renderer/src/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -import { CommandController } from "./CommandController"; - -const command: CommandController = new CommandController(); - -/** - * @public - */ -self.addEventListener("message", async (event: MessageEvent) => -{ - command.queue.push(event.data); - if (command.state === "deactivate") { - command.execute(); - } -}); \ No newline at end of file diff --git a/worker/renderer/src/interface/AttachmentImpl.ts b/worker/renderer/src/interface/AttachmentImpl.ts deleted file mode 100644 index 34e91f64..00000000 --- a/worker/renderer/src/interface/AttachmentImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface AttachmentImpl { - width: number; - height: number; - color: WebGLTexture | WebGLRenderbuffer | null; - texture: WebGLTexture | null; - msaa: boolean; - stencil: WebGLRenderbuffer | null; - mask: boolean; - clipLevel: number; - isActive: boolean; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/BlendModeImpl.ts b/worker/renderer/src/interface/BlendModeImpl.ts deleted file mode 100644 index 6ed290eb..00000000 --- a/worker/renderer/src/interface/BlendModeImpl.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type BlendModeImpl = "copy" - | "add" - | "alpha" - | "darken" - | "difference" - | "erase" - | "hardlight" - | "invert" - | "layer" - | "lighten" - | "multiply" - | "normal" - | "overlay" - | "screen" - | "subtract"; \ No newline at end of file diff --git a/worker/renderer/src/interface/BoundsImpl.ts b/worker/renderer/src/interface/BoundsImpl.ts deleted file mode 100644 index 7af56a28..00000000 --- a/worker/renderer/src/interface/BoundsImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BoundsImpl { - xMin: number; - xMax: number; - yMin: number; - yMax: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/CachePositionImpl.ts b/worker/renderer/src/interface/CachePositionImpl.ts deleted file mode 100644 index 98f72340..00000000 --- a/worker/renderer/src/interface/CachePositionImpl.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CachePositionImpl { - index: number; - x: number; - y: number; - w: number; - h: number; - filterState?: boolean; - matrix?: string; - offsetX?: number; - offsetY?: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/ColorStopImpl.ts b/worker/renderer/src/interface/ColorStopImpl.ts deleted file mode 100644 index 65b98fb4..00000000 --- a/worker/renderer/src/interface/ColorStopImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ColorStopImpl { - ratio: number; - R: number; - G: number; - B: number; - A: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/FilterArrayImpl.ts b/worker/renderer/src/interface/FilterArrayImpl.ts deleted file mode 100644 index ea57db72..00000000 --- a/worker/renderer/src/interface/FilterArrayImpl.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - BlurFilter, - BevelFilter, - ColorMatrixFilter, - ConvolutionFilter, - DisplacementMapFilter, - DropShadowFilter, - GlowFilter, - GradientBevelFilter, - GradientGlowFilter -} from "@next2d/filters"; - -export type FilterArrayImpl = Array< - BlurFilter - | BevelFilter - | ColorMatrixFilter - | ConvolutionFilter - | DisplacementMapFilter - | DropShadowFilter - | GlowFilter - | GradientBevelFilter - | GradientGlowFilter ->; \ No newline at end of file diff --git a/worker/renderer/src/interface/GridImpl.ts b/worker/renderer/src/interface/GridImpl.ts deleted file mode 100644 index eecb9aba..00000000 --- a/worker/renderer/src/interface/GridImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GridImpl { - x: number; - y: number; - w: number; - h: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/ParentImpl.ts b/worker/renderer/src/interface/ParentImpl.ts deleted file mode 100644 index 1a2b2769..00000000 --- a/worker/renderer/src/interface/ParentImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { RenderDisplayObjectContainer } from "../display/RenderDisplayObjectContainer"; - -export type ParentImpl = T; \ No newline at end of file diff --git a/worker/renderer/src/interface/PreObjectImpl.ts b/worker/renderer/src/interface/PreObjectImpl.ts deleted file mode 100644 index f3118066..00000000 --- a/worker/renderer/src/interface/PreObjectImpl.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BlendModeImpl } from "./BlendModeImpl"; - -export interface PreObjectImpl -{ - isLayer: boolean; - isUpdated: boolean | null; - canApply: boolean | null; - matrix: Float32Array | null; - color: Float32Array | null; - blendMode: BlendModeImpl; - filters: any[] | null; - sw: number; - sh: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyContainerMessageImpl.ts b/worker/renderer/src/interface/PropertyContainerMessageImpl.ts deleted file mode 100644 index 0f265fd0..00000000 --- a/worker/renderer/src/interface/PropertyContainerMessageImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyContainerMessageImpl extends PropertyMessageImpl { - maxAlpha?: number; - canDraw?: boolean; - recodes?: Float32Array; - children?: number[]; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyMessageImpl.ts b/worker/renderer/src/interface/PropertyMessageImpl.ts deleted file mode 100644 index f7d9611e..00000000 --- a/worker/renderer/src/interface/PropertyMessageImpl.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BlendModeImpl } from "./BlendModeImpl"; -import { GridImpl } from "./GridImpl"; - -export interface PropertyMessageImpl { - command: string; - buffer: Float32Array; - instanceId?: number; - parentId?: number; - visible?: boolean; - isMask?: boolean; - clipDepth?: number; - depth?: number; - maskId?: number; - loaderInfoId?: number; - characterId?: number; - maskMatrix?: Float32Array; - xMin?: number; - yMin?: number; - xMax?: number; - yMax?: number; - a?: number; - b?: number; - c?: number; - d?: number; - tx?: number; - ty?: number; - f0?: number; - f1?: number; - f2?: number; - f3?: number; - f4?: number; - f5?: number; - f6?: number; - f7?: number; - filters?: any[]; - blendMode?: BlendModeImpl; - matrixBase?: Float32Array; - grid?: GridImpl; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyMessageMapImpl.ts b/worker/renderer/src/interface/PropertyMessageMapImpl.ts deleted file mode 100644 index 7cff7045..00000000 --- a/worker/renderer/src/interface/PropertyMessageMapImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export type PropertyMessageMapImpl = T; \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyShapeMessageImpl.ts b/worker/renderer/src/interface/PropertyShapeMessageImpl.ts deleted file mode 100644 index 8a14a2e8..00000000 --- a/worker/renderer/src/interface/PropertyShapeMessageImpl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyShapeMessageImpl extends PropertyMessageImpl { - maxAlpha?: number; - canDraw?: boolean; - recodes?: Float32Array; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyTextMessageImpl.ts b/worker/renderer/src/interface/PropertyTextMessageImpl.ts deleted file mode 100644 index 85c92502..00000000 --- a/worker/renderer/src/interface/PropertyTextMessageImpl.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; -import { TextDataImpl } from "./TextDataImpl"; -import { TextFormatVerticalAlignImpl } from "./TextFormatVerticalAlignImpl"; -import { TextFieldAutoSizeImpl } from "./TextFieldAutoSizeImpl"; - -export interface PropertyTextMessageImpl extends PropertyMessageImpl { - textAreaActive?: boolean; - textData: TextDataImpl[]; - scrollV: number; - widthTable: number[]; - heightTable: number[]; - textHeightTable: number[]; - objectTable: TextDataImpl[]; - limitWidth: number; - limitHeight: number; - textHeight: number; - verticalAlign: TextFormatVerticalAlignImpl; - autoSize: TextFieldAutoSizeImpl; - wordWrap: boolean; - border: boolean; - borderColor?: number; - background: boolean; - backgroundColor?: number; - thickness: number; - thicknessColor?: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/PropertyVideoMessageImpl.ts b/worker/renderer/src/interface/PropertyVideoMessageImpl.ts deleted file mode 100644 index f2089b28..00000000 --- a/worker/renderer/src/interface/PropertyVideoMessageImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PropertyMessageImpl } from "./PropertyMessageImpl"; - -export interface PropertyVideoMessageImpl extends PropertyMessageImpl { - smoothing?: boolean; - imageBitmap?: ImageBitmap; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/RGBAImpl.ts b/worker/renderer/src/interface/RGBAImpl.ts deleted file mode 100644 index 240c24bc..00000000 --- a/worker/renderer/src/interface/RGBAImpl.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface RGBAImpl { - A: number; - R: number; - G: number; - B: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/RenderDisplayObjectImpl.ts b/worker/renderer/src/interface/RenderDisplayObjectImpl.ts deleted file mode 100644 index 496db2bd..00000000 --- a/worker/renderer/src/interface/RenderDisplayObjectImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { RenderDisplayObject } from "../display/RenderDisplayObject"; - -export type RenderDisplayObjectImpl = T; \ No newline at end of file diff --git a/worker/renderer/src/interface/ShapeModeImpl.ts b/worker/renderer/src/interface/ShapeModeImpl.ts deleted file mode 100644 index 9c9dbe2d..00000000 --- a/worker/renderer/src/interface/ShapeModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type ShapeModeImpl = "shape" | "bitmap"; \ No newline at end of file diff --git a/worker/renderer/src/interface/SpreadMethodImpl.ts b/worker/renderer/src/interface/SpreadMethodImpl.ts deleted file mode 100644 index b04e5e3f..00000000 --- a/worker/renderer/src/interface/SpreadMethodImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type SpreadMethodImpl = "pad" | "reflect" | "repeat"; \ No newline at end of file diff --git a/worker/renderer/src/interface/TextDataImpl.ts b/worker/renderer/src/interface/TextDataImpl.ts deleted file mode 100644 index cb165d41..00000000 --- a/worker/renderer/src/interface/TextDataImpl.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { TextObjectImpl } from "./TextObjectImpl"; - -export type TextDataImpl = T; \ No newline at end of file diff --git a/worker/renderer/src/interface/TextFieldAutoSizeImpl.ts b/worker/renderer/src/interface/TextFieldAutoSizeImpl.ts deleted file mode 100644 index 470b9cae..00000000 --- a/worker/renderer/src/interface/TextFieldAutoSizeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFieldAutoSizeImpl = "center" | "left" | "none" | "right"; \ No newline at end of file diff --git a/worker/renderer/src/interface/TextFormatAlignImpl.ts b/worker/renderer/src/interface/TextFormatAlignImpl.ts deleted file mode 100644 index 07bad1e3..00000000 --- a/worker/renderer/src/interface/TextFormatAlignImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFormatAlignImpl = "center" | "left" | "right"; \ No newline at end of file diff --git a/worker/renderer/src/interface/TextFormatImpl.ts b/worker/renderer/src/interface/TextFormatImpl.ts deleted file mode 100644 index 3260095a..00000000 --- a/worker/renderer/src/interface/TextFormatImpl.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TextFormatAlignImpl } from "@next2d/interface"; - -export interface TextFormatImpl { - _$font: string; - _$size: number; - _$color: number; - _$bold: boolean; - _$italic: boolean; - _$underline: boolean; - _$align: TextFormatAlignImpl; - _$leftMargin: number; - _$rightMargin: number; - _$indent: number; - _$leading: number; - _$blockIndent: number; - _$letterSpacing: number; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/TextFormatVerticalAlignImpl.ts b/worker/renderer/src/interface/TextFormatVerticalAlignImpl.ts deleted file mode 100644 index ef84ee58..00000000 --- a/worker/renderer/src/interface/TextFormatVerticalAlignImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextFormatVerticalAlignImpl = "top" | "middle" | "bottom"; \ No newline at end of file diff --git a/worker/renderer/src/interface/TextObjectImpl.ts b/worker/renderer/src/interface/TextObjectImpl.ts deleted file mode 100644 index 54d5cf40..00000000 --- a/worker/renderer/src/interface/TextObjectImpl.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TextFormatImpl } from "./TextFormatImpl"; -import { TextObjectModeImpl } from "./TextObjectModeImpl"; - -export interface TextObjectImpl { - mode: TextObjectModeImpl; - x: number; - textFormat: TextFormatImpl; -} \ No newline at end of file diff --git a/worker/renderer/src/interface/TextObjectModeImpl.ts b/worker/renderer/src/interface/TextObjectModeImpl.ts deleted file mode 100644 index 3f558522..00000000 --- a/worker/renderer/src/interface/TextObjectModeImpl.ts +++ /dev/null @@ -1 +0,0 @@ -export type TextObjectModeImpl = "break" | "wrap" | "image" | "text"; \ No newline at end of file diff --git a/worker/renderer/src/media/RenderVideo.ts b/worker/renderer/src/media/RenderVideo.ts deleted file mode 100644 index 546e30ae..00000000 --- a/worker/renderer/src/media/RenderVideo.ts +++ /dev/null @@ -1,470 +0,0 @@ -import { RenderDisplayObject } from "../display/RenderDisplayObject"; -import { $videos } from "../RenderGlobal"; -import type { BoundsImpl } from "../interface/BoundsImpl"; -import type { AttachmentImpl } from "../interface/AttachmentImpl"; -import type { PropertyVideoMessageImpl } from "../interface/PropertyVideoMessageImpl"; -import type { FilterArrayImpl } from "../interface/FilterArrayImpl"; -import type { CachePositionImpl } from "../interface/CachePositionImpl"; -import type { - CanvasToWebGLContext, - FrameBufferManager -} from "@next2d/webgl"; -import { - $boundsMatrix, - $clamp, - $Infinity, - $Math, - $multiplicationColor, - $multiplicationMatrix, - $poolBoundsObject, - $poolFloat32Array6, - $poolFloat32Array8, - $OffscreenCanvas, - $getBoundsObject, - $getArray, - $cacheStore, - $Number, - $poolArray, - $getFloat32Array6 -} from "@next2d/share"; - -/** - * @class - */ -export class RenderVideo extends RenderDisplayObject -{ - private _$imageBitmap: ImageBitmap | null; - private _$context: OffscreenCanvasRenderingContext2D | null; - private _$smoothing: boolean; - private _$cacheKeys: string[]; - private _$cacheParams: number[]; - - /** - * @constructor - * @public - */ - constructor () - { - super(); - - /** - * @type {ImageBitmap} - * @default null - * @private - */ - this._$imageBitmap = null; - - /** - * @type {OffscreenCanvasRenderingContext2D} - * @default null - * @private - */ - this._$context = null; - - /** - * @type {boolean} - * @default true - * @private - */ - this._$smoothing = true; - - /** - * @type {array} - * @private - */ - this._$cacheKeys = $getArray(); - - /** - * @type {array} - * @private - */ - this._$cacheParams = $getArray(0, 0, 0); - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @return {void} - * @method - * @private - */ - _$clip ( - context: CanvasToWebGLContext, - matrix: Float32Array - ): void { - - const width: number = this._$xMax; - const height: number = this._$yMax; - if (!width || !height) { - return; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - context.reset(); - context.setTransform( - multiMatrix[0], multiMatrix[1], multiMatrix[2], - multiMatrix[3], multiMatrix[4], multiMatrix[5] - ); - - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width, height); - context.lineTo(0, height); - context.lineTo(0, 0); - context.clip(); - - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - } - - /** - * @param {CanvasToWebGLContext} context - * @param {Float32Array} matrix - * @param {Float32Array} color_transform - * @return {void} - * @method - * @private - */ - _$draw ( - context: CanvasToWebGLContext, - matrix: Float32Array, - color_transform: Float32Array - ): void { - - if (!this._$visible - || !this._$imageBitmap - || !this._$context - ) { - return ; - } - - let multiColor: Float32Array = color_transform; - const rawColor: Float32Array = this._$colorTransform; - if (rawColor[0] !== 1 || rawColor[1] !== 1 - || rawColor[2] !== 1 || rawColor[3] !== 1 - || rawColor[4] !== 0 || rawColor[5] !== 0 - || rawColor[6] !== 0 || rawColor[7] !== 0 - ) { - multiColor = $multiplicationColor(color_transform, rawColor); - } - - const alpha: number = $clamp(multiColor[3] + multiColor[7] / 255, 0, 1, 0); - if (!alpha) { - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - return ; - } - - let multiMatrix: Float32Array = matrix; - const rawMatrix: Float32Array = this._$matrix; - if (rawMatrix[0] !== 1 || rawMatrix[1] !== 0 - || rawMatrix[2] !== 0 || rawMatrix[3] !== 1 - || rawMatrix[4] !== 0 || rawMatrix[5] !== 0 - ) { - multiMatrix = $multiplicationMatrix(matrix, rawMatrix); - } - - // default bounds - const baseBounds: BoundsImpl = this._$getBounds(); - $poolBoundsObject(baseBounds); - - const bounds: BoundsImpl = $boundsMatrix(baseBounds, multiMatrix); - const xMax: number = +bounds.xMax; - const xMin: number = +bounds.xMin; - const yMax: number = +bounds.yMax; - const yMin: number = +bounds.yMin; - $poolBoundsObject(bounds); - - const width: number = $Math.ceil($Math.abs(xMax - xMin)); - const height: number = $Math.ceil($Math.abs(yMax - yMin)); - switch (true) { - - case width === 0: - case height === 0: - case width === 0 - $Infinity: - case height === 0 - $Infinity: - case width === $Infinity: - case height === $Infinity: - return; - - default: - break; - - } - - let xScale: number = +$Math.sqrt( - multiMatrix[0] * multiMatrix[0] - + multiMatrix[1] * multiMatrix[1] - ); - if (!$Number.isInteger(xScale)) { - const value: string = xScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - xScale = +value.slice(0, index); - } - xScale = +xScale.toFixed(4); - } - - let yScale: number = +$Math.sqrt( - multiMatrix[2] * multiMatrix[2] - + multiMatrix[3] * multiMatrix[3] - ); - if (!$Number.isInteger(yScale)) { - const value: string = yScale.toString(); - const index: number = value.indexOf("e"); - if (index !== -1) { - yScale = +value.slice(0, index); - } - yScale = +yScale.toFixed(4); - } - - const filters: FilterArrayImpl | null = this._$filters; - const canApply: boolean = filters !== null - && filters.length > 0 - && this._$canApply(filters); - - let filterBounds: BoundsImpl = $getBoundsObject(0, width, 0, height); - if (canApply && filters) { - for (let idx: number = 0; idx < filters.length ; ++idx) { - filterBounds = filters[idx] - ._$generateFilterRect(filterBounds, xScale, yScale); - } - } - - // cache current buffer - const manager: FrameBufferManager = context.frameBuffer; - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - if (!currentAttachment - || xMin - filterBounds.xMin > currentAttachment.width - || yMin - filterBounds.yMin > currentAttachment.height - ) { - $poolBoundsObject(filterBounds); - return; - } - - if (0 > xMin + filterBounds.xMax || 0 > yMin + filterBounds.yMax) { - $poolBoundsObject(filterBounds); - return; - } - $poolBoundsObject(filterBounds); - - if (!this._$cacheKeys.length - || this._$cacheParams[0] !== xScale - || this._$cacheParams[1] !== yScale - || this._$cacheParams[2] !== color_transform[7] - ) { - const keys: number[] = $getArray(); - keys[0] = xScale; - keys[1] = yScale; - - this._$cacheKeys = $cacheStore.generateKeys( - this._$instanceId, keys, color_transform - ); - - $poolArray(keys); - - this._$cacheParams[0] = xScale; - this._$cacheParams[1] = yScale; - this._$cacheParams[2] = color_transform[7]; - } - - context.cachePosition = $cacheStore.get(this._$cacheKeys); - if (!context.cachePosition) { - - const width: number = $Math.ceil($Math.abs(this._$xMax - this._$xMin)); - const height: number = $Math.ceil($Math.abs(this._$yMax - this._$yMin)); - - const position: CachePositionImpl = manager - .createCachePosition(width, height); - - context.cachePosition = position; - $cacheStore.set(this._$cacheKeys, position); - } - - this._$context.drawImage(this._$imageBitmap, 0, 0); - - const texture: WebGLTexture = manager - .textureManager - ._$createFromElement( - this._$imageBitmap.width, - this._$imageBitmap.height, - this._$context.canvas, - this._$smoothing - ); - - let offsetX: number = 0; - let offsetY: number = 0; - if (canApply && filters) { - - const currentAttachment: AttachmentImpl | null = manager.currentAttachment; - - const attachment: AttachmentImpl = manager - .createCacheAttachment(width, height); - - context._$bind(attachment); - - context.reset(); - - const parentMatrix: Float32Array = $getFloat32Array6( - xScale, 0, 0, yScale, - width / 2, height / 2 - ); - - const baseMatrix: Float32Array = $getFloat32Array6( - 1, 0, 0, 1, - -texture.width / 2, - -texture.height / 2 - ); - - const scaleMatrix = $multiplicationMatrix( - parentMatrix, baseMatrix - ); - $poolFloat32Array6(parentMatrix); - $poolFloat32Array6(baseMatrix); - - context.setTransform( - scaleMatrix[0], scaleMatrix[1], - scaleMatrix[2], scaleMatrix[3], - scaleMatrix[4], scaleMatrix[5] - ); - - context.drawImage(texture, 0, 0, texture.width, texture.height); - - const videoTexture: WebGLTexture = manager.getTextureFromCurrentAttachment(); - context._$bind(currentAttachment); - - manager.releaseAttachment(attachment); - - // release - context.drawTextureFromRect(texture, context.cachePosition); - - const position: CachePositionImpl = this._$drawFilter( - context, multiMatrix, filters, - width, height, videoTexture - ); - - if (position.offsetX) { - offsetX = position.offsetX; - } - - if (position.offsetY) { - offsetY = position.offsetY; - } - - // update - context.cachePosition = position; - - context.setTransform(1, 0, 0, 1, - xMin - offsetX, yMin - offsetY - ); - - } else { - - context.drawTextureFromRect(texture, context.cachePosition); - - context.setTransform( - multiMatrix[0], multiMatrix[1], multiMatrix[2], - multiMatrix[3], multiMatrix[4], multiMatrix[5] - ); - - } - - // draw - if (context.cachePosition) { - - context.globalAlpha = alpha; - context.imageSmoothingEnabled = true; - context.globalCompositeOperation = this._$blendMode; - - context.drawInstance( - xMin - offsetX, yMin - offsetY, xMax, yMax, - color_transform - ); - - // cache position clear - context.cachePosition = null; - } - - // pool - if (multiMatrix !== matrix) { - $poolFloat32Array6(multiMatrix); - } - - if (multiColor !== color_transform) { - $poolFloat32Array8(multiColor); - } - } - - /** - * @description Playerから登録を削除 - * - * @return {void} - * @method - * @private - */ - _$remove () - { - this._$xMin = 0; - this._$yMin = 0; - this._$xMax = 0; - this._$yMax = 0; - this._$context = null; - this._$imageBitmap = null; - this._$smoothing = true; - - super._$remove(); - - $videos.push(this); - } - - /** - * @description 情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$updateProperty (object: PropertyVideoMessageImpl) - { - this._$xMin = object.xMin as NonNullable; - this._$yMin = object.yMin as NonNullable; - this._$xMax = object.xMax as NonNullable; - this._$yMax = object.yMax as NonNullable; - this._$imageBitmap = object.imageBitmap as NonNullable; - this._$smoothing = object.smoothing as NonNullable; - - if (!this._$context && this._$imageBitmap) { - const canvas: OffscreenCanvas = new $OffscreenCanvas( - this._$imageBitmap.width, - this._$imageBitmap.height - ); - this._$context = canvas.getContext("2d"); - } - } - - /** - * @description 描画情報を更新 - * - * @param {object} object - * @return {void} - * @method - * @private - */ - _$update (object: PropertyVideoMessageImpl): void - { - super._$update(object); - this._$updateProperty(object); - } -} \ No newline at end of file diff --git a/worker/renderer/tsconfig.json b/worker/renderer/tsconfig.json deleted file mode 100644 index 57352be0..00000000 --- a/worker/renderer/tsconfig.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "resolveJsonModule": true, - "strictFunctionTypes": false, - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": false, - "target": "es6", - "lib": [ - "es6", - "dom" - ], - "module": "es6", - "moduleResolution": "node", - "baseUrl": "./", - "outDir": "./dist", - "paths": { - "@next2d/share": [ - "../../packages/share/src" - ], - "@next2d/webgl": [ - "../../packages/webgl/src" - ], - "@next2d/geom": [ - "../../packages/geom/src" - ], - "@next2d/filters": [ - "../../packages/filters/src" - ] - } - }, - "include": [ - "src/**/*.ts", - "../../@types/**/*.ts" - ], - "exclude": [ - "../../src", - "node_modules", - "dist" - ] -} \ No newline at end of file diff --git a/worker/unzip/UnzipWorker.min.js b/worker/unzip/UnzipWorker.min.js deleted file mode 100644 index c10543fd..00000000 --- a/worker/unzip/UnzipWorker.min.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var r=Uint8Array,n=Uint16Array,e=Int32Array,a=new r([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),t=new r([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),i=new r([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),o=function(r,a){for(var t=new n(31),i=0;i<31;++i)t[i]=a+=1<>1|(21845&s)<<1;w=(61680&(w=(52428&w)>>2|(13107&w)<<2))>>4|(3855&w)<<4,d[s]=((65280&w)>>8|(255&w)<<8)>>1}var h=function(r,e,a){for(var t=r.length,i=0,o=new n(e);i>v]=c}else for(f=new n(t),i=0;i>15-r[i]);return f},y=new r(288);for(s=0;s<144;++s)y[s]=8;for(s=144;s<256;++s)y[s]=9;for(s=256;s<280;++s)y[s]=7;for(s=280;s<288;++s)y[s]=8;var b=new r(32);for(s=0;s<32;++s)b[s]=5;var g=h(y,9,1),p=h(b,5,1),m=function(r){for(var n=r[0],e=1;en&&(n=r[e]);return n},k=function(r,n,e){var a=n/8|0;return(r[a]|r[a+1]<<8)>>(7&n)&e},x=function(r,n){var e=n/8|0;return(r[e]|r[e+1]<<8|r[e+2]<<16)>>(7&n)},T=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],z=function(r,n,e){var a=new Error(n||T[r]);if(a.code=r,Error.captureStackTrace&&Error.captureStackTrace(a,z),!e)throw a;return a},E=function(n,e,o,f){var v=n.length,c=f?f.length:0;if(!v||e.f&&!e.l)return o||new r(0);var d=!o,s=d||2!=e.i,w=e.i;d&&(o=new r(3*v));var y,b=function(n){var e=o.length;if(n>e){var a=new r(Math.max(2*e,n));a.set(o),o=a}},T=e.f||0,E=e.p||0,M=e.b||0,S=e.l,U=e.d,A=e.m,C=e.n,q=8*v;do{if(!S){T=k(n,E,1);var D=k(n,E+1,3);if(E+=3,!D){var F=n[(y=E,(H=4+((y+7)/8|0))-4)]|n[H-3]<<8,I=H+F;if(I>v){w&&z(0);break}s&&b(M+F),o.set(n.subarray(H,I),M),e.b=M+=F,e.p=E=8*I,e.f=T;continue}if(1==D)S=g,U=p,A=9,C=5;else if(2==D){var O=k(n,E,31)+257,J=k(n,E+10,15)+4,L=O+k(n,E+5,31)+1;E+=14;for(var N=new r(L),P=new r(19),R=0;R>4)<16)N[R++]=H;else{var Q=0,V=0;for(16==H?(V=3+k(n,E,3),E+=2,Q=N[R-1]):17==H?(V=3+k(n,E,7),E+=3):18==H&&(V=11+k(n,E,127),E+=7);V--;)N[R++]=Q}}var W=N.subarray(0,O),X=N.subarray(O);A=m(W),C=m(X),S=h(W,A,1),U=h(X,C,1)}else z(1);if(E>q){w&&z(0);break}}s&&b(M+131072);for(var Y=(1<>4;if((E+=15&Q)>q){w&&z(0);break}if(Q||z(2),_<256)o[M++]=_;else{if(256==_){$=E,S=null;break}var rr=_-254;if(_>264){var nr=a[R=_-257];rr=k(n,E,(1<>4;if(er||z(3),E+=15&er,X=l[ar],ar>3&&(nr=t[ar],X+=x(n,E)&(1<q){w&&z(0);break}s&&b(M+131072);var tr=M+rr;if(Mn.length)&&(a=n.length),new r(n.subarray(e,a))}(o,0,M):o.subarray(0,M)},M=new r(0);function S(n,e){var a,t,i=function(r){31==r[0]&&139==r[1]&&8==r[2]||z(6,"invalid gzip data");var n=r[3],e=10;4&n&&(e+=2+(r[10]|r[11]<<8));for(var a=(n>>3&1)+(n>>4&1);a>0;a-=!r[e++]);return e+(2&n)}(n);return i+8>n.length&&z(6,"invalid gzip data"),E(n.subarray(i,-8),{i:2},e&&e.out||new r((t=(a=n).length,(a[t-4]|a[t-3]<<8|a[t-2]<<16|a[t-1]<<24)>>>0)),e&&e.dictionary)}function U(r,n){return E(r.subarray((e=r,a=n&&n.dictionary,(8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31)&&z(6,"invalid zlib data"),(e[1]>>5&1)==+!a&&z(6,"invalid zlib data: "+(32&e[1]?"need":"unexpected")+" dictionary"),2+(e[1]>>3&4)),-4),{i:2},n&&n.out,n&&n.dictionary);var e,a}var A="undefined"!=typeof TextDecoder&&new TextDecoder;try{A.decode(M,{stream:!0})}catch(r){}"function"==typeof queueMicrotask?queueMicrotask:"function"==typeof setTimeout&&setTimeout;self.addEventListener("message",(r=>{return n=void 0,e=void 0,t=function*(){const n=31==(e=r.data)[0]&&139==e[1]&&8==e[2]?S(e,a):8!=(15&e[0])||e[0]>>4>7||(e[0]<<8|e[1])%31?function(r,n){return E(r,{i:2},n&&n.out,n&&n.dictionary)}(e,a):U(e,a);var e,a;let t="";for(let r=0;r -{ - const buffer: Uint8Array = decompressSync(event.data); - - let json: string = ""; - for (let idx: number = 0; idx < buffer.length; idx += 4096) { - json += String.fromCharCode(...buffer.slice(idx, idx + 4096)); - } - - self.postMessage(JSON.parse(decodeURIComponent(json))); -}); \ No newline at end of file diff --git a/worker/unzip/tsconfig.json b/worker/unzip/tsconfig.json deleted file mode 100644 index 64b1d8e2..00000000 --- a/worker/unzip/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "resolveJsonModule": true, - "strictFunctionTypes": false, - "esModuleInterop": true, - "skipLibCheck": true, - "declaration": false, - "target": "es6", - "lib": [ - "es6", - "dom" - ], - "module": "es6", - "moduleResolution": "node", - "baseUrl": "./", - "outDir": "./" - }, - "include": [ - "src/index.ts" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file