diff --git a/.eslintrc.json b/.eslintrc.json
index 41062f958e4544..bdb1813613c210 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -40,7 +40,10 @@
"XRMediaBinding": "readonly",
"CodeMirror": "readonly",
"esprima": "readonly",
- "jsonlint": "readonly"
+ "jsonlint": "readonly",
+ "VideoFrame": "readonly",
+ "VideoDecoder": "readonly",
+ "Float16Array": "readonly"
},
"rules": {
"no-throw-literal": [
@@ -56,6 +59,30 @@
"destructuring": "any",
"ignoreReadBeforeAssign": false
}
+ ],
+ "no-irregular-whitespace": [
+ "error"
+ ],
+ "no-duplicate-imports": [
+ "error"
+ ],
+ "prefer-spread": "error",
+ "valid-jsdoc": [
+ "error",
+ {
+ "requireReturn": false,
+ "requireReturnType": true,
+ "requireParamDescription": false,
+ "requireReturnDescription": false,
+ "requireParamType": true,
+ "preferType": {
+ "Any": "any",
+ "Boolean": "boolean",
+ "Number": "number",
+ "object": "Object",
+ "String": "string"
+ }
+ }
]
}
}
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 176ed7e4a3058d..f95d9835377634 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -34,8 +34,10 @@ body:
attributes:
label: Live example
value: |
- * [jsfiddle-latest-release](https://jsfiddle.net/g3atw6k5/)
- * [jsfiddle-dev](https://jsfiddle.net/hjqw94c5/)
+ * [jsfiddle-latest-release WebGLRenderer](https://jsfiddle.net/3mrkqyea/)
+ * [jsfiddle-dev WebGLRenderer](https://jsfiddle.net/gcqx26jv/)
+ * [jsfiddle-latest-release WebGPURenderer](https://jsfiddle.net/mnqr9oj0/)
+ * [jsfiddle-dev WebGPURenderer](https://jsfiddle.net/xno7bmw0/)
validations:
required: true
- type: textarea
@@ -72,6 +74,7 @@ body:
- Firefox
- Safari
- Edge
+ - Quest Browser
- type: dropdown
id: os
attributes:
@@ -81,5 +84,6 @@ body:
- Windows
- MacOS
- Linux
+ - ChromeOS
- Android
- iOS
diff --git a/.github/codeql-config.yml b/.github/codeql-config.yml
index d397d52e1bf691..a31e881530d9eb 100644
--- a/.github/codeql-config.yml
+++ b/.github/codeql-config.yml
@@ -5,3 +5,5 @@ paths-ignore:
- "examples/jsm/loaders/ifc/**/*.*"
- "build/*.*"
- "manual/3rdparty/**/*.*"
+ - "utils/docs/template/static/scripts/fuse/**/*.*"
+ - "utils/docs/template/static/scripts/prettify/**/*.*"
diff --git a/.github/renovate.json b/.github/renovate.json
index 59306918fe3901..d37fc66c0e1c56 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -1,7 +1,8 @@
{
"extends": [
"config:base",
- ":disableDependencyDashboard"
+ ":disableDependencyDashboard",
+ "helpers:pinGitHubActionDigests"
],
"timezone": "Asia/Tokyo",
"schedule": ["after 1am and before 7am every monday"],
@@ -11,6 +12,12 @@
"matchUpdateTypes": ["patch", "minor", "pin", "digest"],
"groupName": "devDependencies (non-major)",
"automerge": true
+ },
+ {
+ "description": "ESLint v9 requires flat configs, not yet supported by our plugins. See https://github.com/mrdoob/three.js/pull/28354#issuecomment-2106528332",
+ "matchPackageNames": ["eslint"],
+ "matchUpdateTypes": ["major"],
+ "enabled": false
}
]
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9146c9936daa21..73b89634fecfa4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,11 +6,6 @@ on:
- 'build/**'
- 'docs/**'
- 'files/**'
- push:
- paths-ignore:
- - 'build/**'
- - 'docs/**'
- - 'files/**'
permissions:
contents: read
@@ -21,9 +16,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 18
cache: 'npm'
@@ -38,9 +33,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 18
cache: 'npm'
@@ -50,6 +45,23 @@ jobs:
- name: === Unit testing ===
run: npm run test-unit
+ circular:
+ name: Circular dependencies testing
+ runs-on: ubuntu-latest
+ steps:
+ - name: Git checkout
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - name: Install Node
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
+ with:
+ node-version: 18
+ cache: 'npm'
+ - name: Install dependencies
+ run: npm ci
+
+ - name: === Circular dependencies testing ===
+ run: npm run test-circular-deps
+
e2e:
name: E2E testing
runs-on: ${{ matrix.os }}
@@ -57,30 +69,30 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ windows-latest, ubuntu-latest, macos-latest ]
+ os: [ windows-latest ]
CI: [ 0, 1, 2, 3 ]
env:
CI: ${{ matrix.CI }}
steps:
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
- run: npm run build
+ run: npm run build-module
- name: === E2E testing ===
run: npm run test-e2e
- name: Upload output screenshots
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
- name: Output screenshots
+ name: Output screenshots-${{ matrix.os }}-${{ matrix.CI }}
path: test/e2e/output-screenshots
if-no-files-found: ignore
@@ -89,9 +101,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 18
cache: 'npm'
diff --git a/.github/workflows/codeql-code-scanning.yml b/.github/workflows/codeql-code-scanning.yml
index dfd9bf36c79bc4..af6c54274c77a3 100644
--- a/.github/workflows/codeql-code-scanning.yml
+++ b/.github/workflows/codeql-code-scanning.yml
@@ -26,20 +26,20 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@39edc492dbe16b1465b0cafca41432d857bdb31a # v3
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql-config.yml
queries: security-and-quality
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@39edc492dbe16b1465b0cafca41432d857bdb31a # v3
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@39edc492dbe16b1465b0cafca41432d857bdb31a # v3
with:
category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/read-size.yml b/.github/workflows/read-size.yml
index 16173259cd0efe..109fb963ab47b5 100644
--- a/.github/workflows/read-size.yml
+++ b/.github/workflows/read-size.yml
@@ -20,11 +20,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
- node-version: 18
+ node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
@@ -35,18 +35,33 @@ jobs:
- name: Read bundle sizes
id: read-size
run: |
- FILESIZE=$(stat --format=%s build/three.module.min.js)
+ WEBGL_FILESIZE=$(stat --format=%s build/three.module.min.js)
gzip -k build/three.module.min.js
- FILESIZE_GZIP=$(stat --format=%s build/three.module.min.js.gz)
- TREESHAKEN=$(stat --format=%s test/treeshake/index.bundle.min.js)
+ WEBGL_FILESIZE_GZIP=$(stat --format=%s build/three.module.min.js.gz)
+ WEBGL_TREESHAKEN=$(stat --format=%s test/treeshake/index.bundle.min.js)
gzip -k test/treeshake/index.bundle.min.js
- TREESHAKEN_GZIP=$(stat --format=%s test/treeshake/index.bundle.min.js.gz)
+ WEBGL_TREESHAKEN_GZIP=$(stat --format=%s test/treeshake/index.bundle.min.js.gz)
+
+ WEBGPU_FILESIZE=$(stat --format=%s build/three.webgpu.min.js)
+ gzip -k build/three.webgpu.min.js
+ WEBGPU_FILESIZE_GZIP=$(stat --format=%s build/three.webgpu.min.js.gz)
+ WEBGPU_TREESHAKEN=$(stat --format=%s test/treeshake/index.webgpu.bundle.min.js)
+ gzip -k test/treeshake/index.webgpu.bundle.min.js
+ WEBGPU_TREESHAKEN_GZIP=$(stat --format=%s test/treeshake/index.webgpu.bundle.min.js.gz)
+
+ WEBGPU_NODES_FILESIZE=$(stat --format=%s build/three.webgpu.nodes.min.js)
+ gzip -k build/three.webgpu.nodes.min.js
+ WEBGPU_NODES_FILESIZE_GZIP=$(stat --format=%s build/three.webgpu.nodes.min.js.gz)
+ WEBGPU_NODES_TREESHAKEN=$(stat --format=%s test/treeshake/index.webgpu.nodes.bundle.min.js)
+ gzip -k test/treeshake/index.webgpu.nodes.bundle.min.js
+ WEBGPU_NODES_TREESHAKEN_GZIP=$(stat --format=%s test/treeshake/index.webgpu.nodes.bundle.min.js.gz)
+
PR=${{ github.event.pull_request.number }}
# write the output in a json file to upload it as artifact
- node -pe "JSON.stringify({ filesize: $FILESIZE, gzip: $FILESIZE_GZIP, treeshaken: $TREESHAKEN, treeshakenGzip: $TREESHAKEN_GZIP, pr: $PR })" > sizes.json
+ node -pe "JSON.stringify({ filesize: $WEBGL_FILESIZE, gzip: $WEBGL_FILESIZE_GZIP, treeshaken: $WEBGL_TREESHAKEN, treeshakenGzip: $WEBGL_TREESHAKEN_GZIP, filesize2: $WEBGPU_FILESIZE, gzip2: $WEBGPU_FILESIZE_GZIP, treeshaken2: $WEBGPU_TREESHAKEN, treeshakenGzip2: $WEBGPU_TREESHAKEN_GZIP, filesize3: $WEBGPU_NODES_FILESIZE, gzip3: $WEBGPU_NODES_FILESIZE_GZIP, treeshaken3: $WEBGPU_NODES_TREESHAKEN, treeshakenGzip3: $WEBGPU_NODES_TREESHAKEN_GZIP, pr: $PR })" > sizes.json
- name: Upload artifact
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: sizes
path: sizes.json
diff --git a/.github/workflows/report-size.yml b/.github/workflows/report-size.yml
index a1a6782b96f7ca..5e5a909288a472 100644
--- a/.github/workflows/report-size.yml
+++ b/.github/workflows/report-size.yml
@@ -9,7 +9,7 @@ on:
# This workflow needs to be run with "pull-requests: write" permissions to
# be able to comment on the pull request. We can't checkout the PR code
# in this workflow.
-# Reference:
+# Reference:
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
permissions:
pull-requests: write
@@ -29,7 +29,7 @@ jobs:
# Using actions/download-artifact doesn't work here
# https://github.com/actions/download-artifact/issues/60
- name: Download artifact
- uses: actions/github-script@v6
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
id: download-artifact
with:
result-encoding: string
@@ -56,11 +56,11 @@ jobs:
# This runs on the base branch of the PR, meaning "dev"
- name: Git checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
- node-version: 18
+ node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
@@ -71,69 +71,181 @@ jobs:
- name: Read sizes
id: read-size
run: |
- FILESIZE_BASE=$(stat --format=%s build/three.module.min.js)
+ WEBGL_FILESIZE_BASE=$(stat --format=%s build/three.module.min.js)
gzip -k build/three.module.min.js
- FILESIZE_BASE_GZIP=$(stat --format=%s build/three.module.min.js.gz)
- TREESHAKEN_BASE=$(stat --format=%s test/treeshake/index.bundle.min.js)
+ WEBGL_FILESIZE_BASE_GZIP=$(stat --format=%s build/three.module.min.js.gz)
+ WEBGL_TREESHAKEN_BASE=$(stat --format=%s test/treeshake/index.bundle.min.js)
gzip -k test/treeshake/index.bundle.min.js
- TREESHAKEN_BASE_GZIP=$(stat --format=%s test/treeshake/index.bundle.min.js.gz)
+ WEBGL_TREESHAKEN_BASE_GZIP=$(stat --format=%s test/treeshake/index.bundle.min.js.gz)
+
+ WEBGPU_FILESIZE_BASE=$(stat --format=%s build/three.webgpu.min.js)
+ gzip -k build/three.webgpu.min.js
+ WEBGPU_FILESIZE_BASE_GZIP=$(stat --format=%s build/three.webgpu.min.js.gz)
+ WEBGPU_TREESHAKEN_BASE=$(stat --format=%s test/treeshake/index.webgpu.bundle.min.js)
+ gzip -k test/treeshake/index.webgpu.bundle.min.js
+ WEBGPU_TREESHAKEN_BASE_GZIP=$(stat --format=%s test/treeshake/index.webgpu.bundle.min.js.gz)
+
+ WEBGPU_NODES_FILESIZE_BASE=$(stat --format=%s build/three.webgpu.nodes.min.js)
+ gzip -k build/three.webgpu.nodes.min.js
+ WEBGPU_NODES_FILESIZE_BASE_GZIP=$(stat --format=%s build/three.webgpu.nodes.min.js.gz)
+ WEBGPU_NODES_TREESHAKEN_BASE=$(stat --format=%s test/treeshake/index.webgpu.nodes.bundle.min.js)
+ gzip -k test/treeshake/index.webgpu.nodes.bundle.min.js
+ WEBGPU_NODES_TREESHAKEN_BASE_GZIP=$(stat --format=%s test/treeshake/index.webgpu.nodes.bundle.min.js.gz)
# log to console
- echo "FILESIZE_BASE=$FILESIZE_BASE"
- echo "FILESIZE_BASE_GZIP=$FILESIZE_BASE_GZIP"
- echo "TREESHAKEN_BASE=$TREESHAKEN_BASE"
- echo "TREESHAKEN_BASE_GZIP=$TREESHAKEN_BASE_GZIP"
+ echo "WEBGL_FILESIZE_BASE=$WEBGL_FILESIZE_BASE"
+ echo "WEBGL_FILESIZE_BASE_GZIP=$WEBGL_FILESIZE_BASE_GZIP"
+ echo "WEBGL_TREESHAKEN_BASE=$WEBGL_TREESHAKEN_BASE"
+ echo "WEBGL_TREESHAKEN_BASE_GZIP=$WEBGL_TREESHAKEN_BASE_GZIP"
+
+ echo "WEBGL_FILESIZE_BASE=$WEBGL_FILESIZE_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_BASE_GZIP=$WEBGL_FILESIZE_BASE_GZIP" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_BASE=$WEBGL_TREESHAKEN_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_BASE_GZIP=$WEBGL_TREESHAKEN_BASE_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_FILESIZE_BASE=$WEBGPU_FILESIZE_BASE"
+ echo "WEBGPU_FILESIZE_BASE_GZIP=$WEBGPU_FILESIZE_BASE_GZIP"
+ echo "WEBGPU_TREESHAKEN_BASE=$WEBGPU_TREESHAKEN_BASE"
+ echo "WEBGPU_TREESHAKEN_BASE_GZIP=$WEBGPU_TREESHAKEN_BASE_GZIP"
- echo "FILESIZE_BASE=$FILESIZE_BASE" >> $GITHUB_OUTPUT
- echo "FILESIZE_BASE_GZIP=$FILESIZE_BASE_GZIP" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_BASE=$TREESHAKEN_BASE" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_BASE_GZIP=$TREESHAKEN_BASE_GZIP" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_BASE=$WEBGPU_FILESIZE_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_BASE_GZIP=$WEBGPU_FILESIZE_BASE_GZIP" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_BASE=$WEBGPU_TREESHAKEN_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_BASE_GZIP=$WEBGPU_TREESHAKEN_BASE_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_NODES_FILESIZE_BASE=$WEBGPU_NODES_FILESIZE_BASE"
+ echo "WEBGPU_NODES_FILESIZE_BASE_GZIP=$WEBGPU_NODES_FILESIZE_BASE_GZIP"
+ echo "WEBGPU_NODES_TREESHAKEN_BASE=$WEBGPU_NODES_TREESHAKEN_BASE"
+ echo "WEBGPU_NODES_TREESHAKEN_BASE_GZIP=$WEBGPU_NODES_TREESHAKEN_BASE_GZIP"
+
+ echo "WEBGPU_NODES_FILESIZE_BASE=$WEBGPU_NODES_FILESIZE_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_BASE_GZIP=$WEBGPU_NODES_FILESIZE_BASE_GZIP" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_BASE=$WEBGPU_NODES_TREESHAKEN_BASE" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_BASE_GZIP=$WEBGPU_NODES_TREESHAKEN_BASE_GZIP" >> $GITHUB_OUTPUT
- name: Format sizes
id: format
# It's important these are passed as env variables.
# https://securitylab.github.com/research/github-actions-untrusted-input/
env:
- FILESIZE: ${{ fromJSON(steps.download-artifact.outputs.result).filesize }}
- FILESIZE_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).gzip }}
- FILESIZE_BASE: ${{ steps.read-size.outputs.FILESIZE_BASE }}
- FILESIZE_BASE_GZIP: ${{ steps.read-size.outputs.FILESIZE_BASE_GZIP }}
- TREESHAKEN: ${{ fromJSON(steps.download-artifact.outputs.result).treeshaken }}
- TREESHAKEN_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).treeshakenGzip }}
- TREESHAKEN_BASE: ${{ steps.read-size.outputs.TREESHAKEN_BASE }}
- TREESHAKEN_BASE_GZIP: ${{ steps.read-size.outputs.TREESHAKEN_BASE_GZIP }}
+ WEBGL_FILESIZE: ${{ fromJSON(steps.download-artifact.outputs.result).filesize }}
+ WEBGL_FILESIZE_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).gzip }}
+ WEBGL_FILESIZE_BASE: ${{ steps.read-size.outputs.WEBGL_FILESIZE_BASE }}
+ WEBGL_FILESIZE_BASE_GZIP: ${{ steps.read-size.outputs.WEBGL_FILESIZE_BASE_GZIP }}
+ WEBGL_TREESHAKEN: ${{ fromJSON(steps.download-artifact.outputs.result).treeshaken }}
+ WEBGL_TREESHAKEN_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).treeshakenGzip }}
+ WEBGL_TREESHAKEN_BASE: ${{ steps.read-size.outputs.WEBGL_TREESHAKEN_BASE }}
+ WEBGL_TREESHAKEN_BASE_GZIP: ${{ steps.read-size.outputs.WEBGL_TREESHAKEN_BASE_GZIP }}
+ WEBGPU_FILESIZE: ${{ fromJSON(steps.download-artifact.outputs.result).filesize2 }}
+ WEBGPU_FILESIZE_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).gzip2 }}
+ WEBGPU_FILESIZE_BASE: ${{ steps.read-size.outputs.WEBGPU_FILESIZE_BASE }}
+ WEBGPU_FILESIZE_BASE_GZIP: ${{ steps.read-size.outputs.WEBGPU_FILESIZE_BASE_GZIP }}
+ WEBGPU_TREESHAKEN: ${{ fromJSON(steps.download-artifact.outputs.result).treeshaken2 }}
+ WEBGPU_TREESHAKEN_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).treeshakenGzip2 }}
+ WEBGPU_TREESHAKEN_BASE: ${{ steps.read-size.outputs.WEBGPU_TREESHAKEN_BASE }}
+ WEBGPU_TREESHAKEN_BASE_GZIP: ${{ steps.read-size.outputs.WEBGPU_TREESHAKEN_BASE_GZIP }}
+ WEBGPU_NODES_FILESIZE: ${{ fromJSON(steps.download-artifact.outputs.result).filesize3 }}
+ WEBGPU_NODES_FILESIZE_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).gzip3 }}
+ WEBGPU_NODES_FILESIZE_BASE: ${{ steps.read-size.outputs.WEBGPU_NODES_FILESIZE_BASE }}
+ WEBGPU_NODES_FILESIZE_BASE_GZIP: ${{ steps.read-size.outputs.WEBGPU_NODES_FILESIZE_BASE_GZIP }}
+ WEBGPU_NODES_TREESHAKEN: ${{ fromJSON(steps.download-artifact.outputs.result).treeshaken3 }}
+ WEBGPU_NODES_TREESHAKEN_GZIP: ${{ fromJSON(steps.download-artifact.outputs.result).treeshakenGzip3 }}
+ WEBGPU_NODES_TREESHAKEN_BASE: ${{ steps.read-size.outputs.WEBGPU_NODES_TREESHAKEN_BASE }}
+ WEBGPU_NODES_TREESHAKEN_BASE_GZIP: ${{ steps.read-size.outputs.WEBGPU_NODES_TREESHAKEN_BASE_GZIP }}
run: |
- FILESIZE_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE")
- FILESIZE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE_GZIP")
- FILESIZE_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE_BASE")
- FILESIZE_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$FILESIZE_BASE_GZIP")
- FILESIZE_DIFF=$(node ./test/treeshake/utils/format-diff.js "$FILESIZE" "$FILESIZE_BASE")
- TREESHAKEN_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN")
- TREESHAKEN_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN_GZIP")
- TREESHAKEN_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN_BASE")
- TREESHAKEN_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$TREESHAKEN_BASE_GZIP")
- TREESHAKEN_DIFF=$(node ./test/treeshake/utils/format-diff.js "$TREESHAKEN" "$TREESHAKEN_BASE")
-
- echo "FILESIZE=$FILESIZE_FORM" >> $GITHUB_OUTPUT
- echo "FILESIZE_GZIP=$FILESIZE_GZIP_FORM" >> $GITHUB_OUTPUT
- echo "FILESIZE_BASE=$FILESIZE_BASE_FORM" >> $GITHUB_OUTPUT
- echo "FILESIZE_BASE_GZIP=$FILESIZE_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
- echo "FILESIZE_DIFF=$FILESIZE_DIFF" >> $GITHUB_OUTPUT
- echo "TREESHAKEN=$TREESHAKEN_FORM" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_GZIP=$TREESHAKEN_GZIP_FORM" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_BASE=$TREESHAKEN_BASE_FORM" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_BASE_GZIP=$TREESHAKEN_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
- echo "TREESHAKEN_DIFF=$TREESHAKEN_DIFF" >> $GITHUB_OUTPUT
+ WEBGL_FILESIZE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_FILESIZE")
+ WEBGL_FILESIZE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_FILESIZE_GZIP")
+ WEBGL_FILESIZE_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_FILESIZE_BASE")
+ WEBGL_FILESIZE_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_FILESIZE_BASE_GZIP")
+ WEBGL_FILESIZE_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGL_FILESIZE" "$WEBGL_FILESIZE_BASE")
+ WEBGL_FILESIZE_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGL_FILESIZE_GZIP" "$WEBGL_FILESIZE_BASE_GZIP")
+
+ WEBGL_TREESHAKEN_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_TREESHAKEN")
+ WEBGL_TREESHAKEN_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_TREESHAKEN_GZIP")
+ WEBGL_TREESHAKEN_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_TREESHAKEN_BASE")
+ WEBGL_TREESHAKEN_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGL_TREESHAKEN_BASE_GZIP")
+ WEBGL_TREESHAKEN_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGL_TREESHAKEN" "$WEBGL_TREESHAKEN_BASE")
+ WEBGL_TREESHAKEN_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGL_TREESHAKEN_GZIP" "$WEBGL_TREESHAKEN_BASE_GZIP")
+
+ WEBGPU_FILESIZE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_FILESIZE")
+ WEBGPU_FILESIZE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_FILESIZE_GZIP")
+ WEBGPU_FILESIZE_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_FILESIZE_BASE")
+ WEBGPU_FILESIZE_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_FILESIZE_BASE_GZIP")
+ WEBGPU_FILESIZE_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_FILESIZE" "$WEBGPU_FILESIZE_BASE")
+ WEBGPU_FILESIZE_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_FILESIZE_GZIP" "$WEBGPU_FILESIZE_BASE_GZIP")
+
+ WEBGPU_TREESHAKEN_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_TREESHAKEN")
+ WEBGPU_TREESHAKEN_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_TREESHAKEN_GZIP")
+ WEBGPU_TREESHAKEN_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_TREESHAKEN_BASE")
+ WEBGPU_TREESHAKEN_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_TREESHAKEN_BASE_GZIP")
+ WEBGPU_TREESHAKEN_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_TREESHAKEN" "$WEBGPU_TREESHAKEN_BASE")
+ WEBGPU_TREESHAKEN_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_TREESHAKEN_GZIP" "$WEBGPU_TREESHAKEN_BASE_GZIP")
+
+ WEBGPU_NODES_FILESIZE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_FILESIZE")
+ WEBGPU_NODES_FILESIZE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_FILESIZE_GZIP")
+ WEBGPU_NODES_FILESIZE_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_FILESIZE_BASE")
+ WEBGPU_NODES_FILESIZE_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_FILESIZE_BASE_GZIP")
+ WEBGPU_NODES_FILESIZE_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_NODES_FILESIZE" "$WEBGPU_NODES_FILESIZE_BASE")
+ WEBGPU_NODES_FILESIZE_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_NODES_FILESIZE_GZIP" "$WEBGPU_NODES_FILESIZE_BASE_GZIP")
+
+ WEBGPU_NODES_TREESHAKEN_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_TREESHAKEN")
+ WEBGPU_NODES_TREESHAKEN_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_TREESHAKEN_GZIP")
+ WEBGPU_NODES_TREESHAKEN_BASE_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_TREESHAKEN_BASE")
+ WEBGPU_NODES_TREESHAKEN_BASE_GZIP_FORM=$(node ./test/treeshake/utils/format-size.js "$WEBGPU_NODES_TREESHAKEN_BASE_GZIP")
+ WEBGPU_NODES_TREESHAKEN_DIFF=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_NODES_TREESHAKEN" "$WEBGPU_NODES_TREESHAKEN_BASE")
+ WEBGPU_NODES_TREESHAKEN_DIFF_GZIP=$(node ./test/treeshake/utils/format-diff.js "$WEBGPU_NODES_TREESHAKEN_GZIP" "$WEBGPU_NODES_TREESHAKEN_BASE_GZIP")
+
+ echo "WEBGL_FILESIZE=$WEBGL_FILESIZE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_GZIP=$WEBGL_FILESIZE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_BASE=$WEBGL_FILESIZE_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_BASE_GZIP=$WEBGL_FILESIZE_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_DIFF=$WEBGL_FILESIZE_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGL_FILESIZE_DIFF_GZIP=$WEBGL_FILESIZE_DIFF_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGL_TREESHAKEN=$WEBGL_TREESHAKEN_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_GZIP=$WEBGL_TREESHAKEN_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_BASE=$WEBGL_TREESHAKEN_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_BASE_GZIP=$WEBGL_TREESHAKEN_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_DIFF=$WEBGL_TREESHAKEN_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGL_TREESHAKEN_DIFF_GZIP=$WEBGL_TREESHAKEN_DIFF_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_FILESIZE=$WEBGPU_FILESIZE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_GZIP=$WEBGPU_FILESIZE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_BASE=$WEBGPU_FILESIZE_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_BASE_GZIP=$WEBGPU_FILESIZE_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_DIFF=$WEBGPU_FILESIZE_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGPU_FILESIZE_DIFF_GZIP=$WEBGPU_FILESIZE_DIFF_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_TREESHAKEN=$WEBGPU_TREESHAKEN_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_GZIP=$WEBGPU_TREESHAKEN_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_BASE=$WEBGPU_TREESHAKEN_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_BASE_GZIP=$WEBGPU_TREESHAKEN_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_DIFF=$WEBGPU_TREESHAKEN_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGPU_TREESHAKEN_DIFF_GZIP=$WEBGPU_TREESHAKEN_DIFF_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_NODES_FILESIZE=$WEBGPU_NODES_FILESIZE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_GZIP=$WEBGPU_NODES_FILESIZE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_BASE=$WEBGPU_NODES_FILESIZE_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_BASE_GZIP=$WEBGPU_NODES_FILESIZE_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_DIFF=$WEBGPU_NODES_FILESIZE_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_FILESIZE_DIFF_GZIP=$WEBGPU_NODES_FILESIZE_DIFF_GZIP" >> $GITHUB_OUTPUT
+
+ echo "WEBGPU_NODES_TREESHAKEN=$WEBGPU_NODES_TREESHAKEN_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_GZIP=$WEBGPU_NODES_TREESHAKEN_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_BASE=$WEBGPU_NODES_TREESHAKEN_BASE_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_BASE_GZIP=$WEBGPU_NODES_TREESHAKEN_BASE_GZIP_FORM" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_DIFF=$WEBGPU_NODES_TREESHAKEN_DIFF" >> $GITHUB_OUTPUT
+ echo "WEBGPU_NODES_TREESHAKEN_DIFF_GZIP=$WEBGPU_NODES_TREESHAKEN_DIFF_GZIP" >> $GITHUB_OUTPUT
- name: Find existing comment
- uses: peter-evans/find-comment@v2
+ uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: find-comment
with:
issue-number: ${{ fromJSON(steps.download-artifact.outputs.result).pr }}
comment-author: 'github-actions[bot]'
body-includes: Bundle size
- name: Comment on PR
- uses: peter-evans/create-or-update-comment@v3
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
issue-number: ${{ fromJSON(steps.download-artifact.outputs.result).pr }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
@@ -143,14 +255,18 @@ jobs:
_Full ESM build, minified and gzipped._
- | Filesize `${{ github.ref_name }}` | Filesize PR | Diff |
- |----------|---------|------|
- | ${{ steps.format.outputs.FILESIZE_BASE }} (${{ steps.format.outputs.FILESIZE_BASE_GZIP }}) | ${{ steps.format.outputs.FILESIZE }} (${{ steps.format.outputs.FILESIZE_GZIP }}) | ${{ steps.format.outputs.FILESIZE_DIFF }} |
+ || Before | After | Diff |
+ |:-:|:-:|:-:|:-:|
+ | WebGL | ${{ steps.format.outputs.WEBGL_FILESIZE_BASE }}
**${{ steps.format.outputs.WEBGL_FILESIZE_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGL_FILESIZE }}
**${{ steps.format.outputs.WEBGL_FILESIZE_GZIP }}** | ${{ steps.format.outputs.WEBGL_FILESIZE_DIFF }}
**${{ steps.format.outputs.WEBGL_FILESIZE_DIFF_GZIP }}** |
+ | WebGPU | ${{ steps.format.outputs.WEBGPU_FILESIZE_BASE }}
**${{ steps.format.outputs.WEBGPU_FILESIZE_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_FILESIZE }}
**${{ steps.format.outputs.WEBGPU_FILESIZE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_FILESIZE_DIFF }}
**${{ steps.format.outputs.WEBGPU_FILESIZE_DIFF_GZIP }}** |
+ | WebGPU Nodes | ${{ steps.format.outputs.WEBGPU_NODES_FILESIZE_BASE }}
**${{ steps.format.outputs.WEBGPU_NODES_FILESIZE_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_NODES_FILESIZE }}
**${{ steps.format.outputs.WEBGPU_NODES_FILESIZE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_NODES_FILESIZE_DIFF }}
**${{ steps.format.outputs.WEBGPU_NODES_FILESIZE_DIFF_GZIP }}** |
### 🌳 Bundle size after tree-shaking
_Minimal build including a renderer, camera, empty scene, and dependencies._
- | Filesize `${{ github.ref_name }}` | Filesize PR | Diff |
- |----------|---------|------|
- | ${{ steps.format.outputs.TREESHAKEN_BASE }} (${{ steps.format.outputs.TREESHAKEN_BASE_GZIP }}) | ${{ steps.format.outputs.TREESHAKEN }} (${{ steps.format.outputs.TREESHAKEN_GZIP }}) | ${{ steps.format.outputs.TREESHAKEN_DIFF }} |
+ || Before | After | Diff |
+ |:-:|:-:|:-:|:-:|
+ | WebGL | ${{ steps.format.outputs.WEBGL_TREESHAKEN_BASE }}
**${{ steps.format.outputs.WEBGL_TREESHAKEN_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGL_TREESHAKEN }}
**${{ steps.format.outputs.WEBGL_TREESHAKEN_GZIP }}** | ${{ steps.format.outputs.WEBGL_TREESHAKEN_DIFF }}
**${{ steps.format.outputs.WEBGL_TREESHAKEN_DIFF_GZIP }}** |
+ | WebGPU | ${{ steps.format.outputs.WEBGPU_TREESHAKEN_BASE }}
**${{ steps.format.outputs.WEBGPU_TREESHAKEN_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_TREESHAKEN }}
**${{ steps.format.outputs.WEBGPU_TREESHAKEN_GZIP }}** | ${{ steps.format.outputs.WEBGPU_TREESHAKEN_DIFF }}
**${{ steps.format.outputs.WEBGPU_TREESHAKEN_DIFF_GZIP }}** |
+ | WebGPU Nodes | ${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN_BASE }}
**${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN_BASE_GZIP }}** | ${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN }}
**${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN_GZIP }}** | ${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN_DIFF }}
**${{ steps.format.outputs.WEBGPU_NODES_TREESHAKEN_DIFF_GZIP }}** |
diff --git a/.gitignore b/.gitignore
index 50df59a8212b6c..9bbf0fbacf2f7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,11 +8,16 @@ npm-debug.log
.vs/
test/unit/build
+test/treeshake/index-src.bundle.min.js
test/treeshake/index.bundle.js
test/treeshake/index.bundle.min.js
-test/treeshake/index-src.bundle.min.js
+test/treeshake/index.webgpu.bundle.js
+test/treeshake/index.webgpu.bundle.min.js
+test/treeshake/index.webgpu.nodes.bundle.js
+test/treeshake/index.webgpu.nodes.bundle.min.js
test/treeshake/stats.html
test/e2e/chromium
test/e2e/output-screenshots
-**/node_modules
\ No newline at end of file
+**/node_modules
+**/docs_new
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index d07e209686512b..cf781430404b0a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License
-Copyright © 2010-2023 three.js authors
+Copyright © 2010-2025 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index a032a35c7a140f..9357702490c6e7 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,12 @@
[![NPM Package][npm]][npm-url]
[![Build Size][build-size]][build-size-url]
[![NPM Downloads][npm-downloads]][npmtrends-url]
-[![DeepScan][deepscan]][deepscan-url]
[![Discord][discord]][discord-url]
+[![DeepWiki][deepwiki]][deepwiki-url]
#### JavaScript 3D library
-The aim of the project is to create an easy to use, lightweight, cross-browser, general purpose 3D library. The current builds only include a WebGL renderer but WebGPU (experimental), SVG and CSS3D renderers are also available as addons.
+The aim of the project is to create an easy-to-use, lightweight, cross-browser, general-purpose 3D library. The current builds only include WebGL and WebGPU renderers but SVG and CSS3D renderers are also available as addons.
[Examples](https://threejs.org/examples/) —
[Docs](https://threejs.org/docs/) —
@@ -26,9 +26,11 @@ This code creates a scene, a camera, and a geometric cube, and it adds the cube
```javascript
import * as THREE from 'three';
+const width = window.innerWidth, height = window.innerHeight;
+
// init
-const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
+const camera = new THREE.PerspectiveCamera( 70, width / height, 0.01, 10 );
camera.position.z = 1;
const scene = new THREE.Scene();
@@ -40,13 +42,13 @@ const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
-renderer.setSize( window.innerWidth, window.innerHeight );
-renderer.setAnimationLoop( animation );
+renderer.setSize( width, height );
+renderer.setAnimationLoop( animate );
document.body.appendChild( renderer.domElement );
// animation
-function animation( time ) {
+function animate( time ) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
@@ -56,7 +58,7 @@ function animation( time ) {
}
```
-If everything went well, you should see [this](https://jsfiddle.net/7u84j6kp/).
+If everything goes well, you should see [this](https://jsfiddle.net/v98k6oze/).
### Cloning this repository
@@ -77,8 +79,8 @@ git clone --depth=1 https://github.com/mrdoob/three.js.git
[build-size-url]: https://bundlephobia.com/result?p=three
[npm-downloads]: https://img.shields.io/npm/dw/three
[npmtrends-url]: https://www.npmtrends.com/three
-[deepscan]: https://deepscan.io/api/teams/16600/projects/19901/branches/525701/badge/grade.svg
-[deepscan-url]: https://deepscan.io/dashboard#view=project&tid=16600&pid=19901&bid=525701
[discord]: https://img.shields.io/discord/685241246557667386
[discord-url]: https://discord.gg/56GBJwAnUS
+[deepwiki]: https://deepwiki.com/badge.svg
+[deepwiki-url]: https://deepwiki.com/mrdoob/three.js
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000000000..6b009001faf3e0
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,18 @@
+# Security Policy
+
+If you have discovered a security vulnerability in this project, please report it
+privately. **Do not disclose it as a public issue.** This gives us time to work with you
+to fix the issue before public exposure, reducing the chance that the exploit will be
+used before a patch is released.
+
+You may submit the report in the following ways:
+
+- Send an email to hello@mrdoob.com.
+
+Please provide the following information in your report:
+
+- A description of the vulnerability and its impact.
+- How to reproduce the issue.
+
+This project is maintained by volunteers on a reasonable-effort basis. As such,
+we ask that you give us 90 days to work on a fix before public exposure.
diff --git a/build/three.cjs b/build/three.cjs
index ac5e638ceb46dd..62aaf1b11d8d1f 100644
--- a/build/three.cjs
+++ b/build/three.cjs
@@ -1,215 +1,1708 @@
/**
* @license
- * Copyright 2010-2023 Three.js Authors
+ * Copyright 2010-2025 Three.js Authors
* SPDX-License-Identifier: MIT
*/
'use strict';
-const REVISION = '153dev';
+const REVISION = '178';
+/**
+ * Represents mouse buttons and interaction types in context of controls.
+ *
+ * @type {ConstantsMouse}
+ * @constant
+ */
const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
+
+/**
+ * Represents touch interaction types in context of controls.
+ *
+ * @type {ConstantsTouch}
+ * @constant
+ */
const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
+
+/**
+ * Disables face culling.
+ *
+ * @type {number}
+ * @constant
+ */
const CullFaceNone = 0;
+
+/**
+ * Culls back faces.
+ *
+ * @type {number}
+ * @constant
+ */
const CullFaceBack = 1;
+
+/**
+ * Culls front faces.
+ *
+ * @type {number}
+ * @constant
+ */
const CullFaceFront = 2;
+
+/**
+ * Culls both front and back faces.
+ *
+ * @type {number}
+ * @constant
+ */
const CullFaceFrontBack = 3;
+
+/**
+ * Gives unfiltered shadow maps - fastest, but lowest quality.
+ *
+ * @type {number}
+ * @constant
+ */
const BasicShadowMap = 0;
+
+/**
+ * Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm.
+ *
+ * @type {number}
+ * @constant
+ */
const PCFShadowMap = 1;
+
+/**
+ * Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm with
+ * better soft shadows especially when using low-resolution shadow maps.
+ *
+ * @type {number}
+ * @constant
+ */
const PCFSoftShadowMap = 2;
+
+/**
+ * Filters shadow maps using the Variance Shadow Map (VSM) algorithm.
+ * When using VSMShadowMap all shadow receivers will also cast shadows.
+ *
+ * @type {number}
+ * @constant
+ */
const VSMShadowMap = 3;
+
+/**
+ * Only front faces are rendered.
+ *
+ * @type {number}
+ * @constant
+ */
const FrontSide = 0;
+
+/**
+ * Only back faces are rendered.
+ *
+ * @type {number}
+ * @constant
+ */
const BackSide = 1;
+
+/**
+ * Both front and back faces are rendered.
+ *
+ * @type {number}
+ * @constant
+ */
const DoubleSide = 2;
-const TwoPassDoubleSide = 2; // r149
+
+/**
+ * No blending is performed which effectively disables
+ * alpha transparency.
+ *
+ * @type {number}
+ * @constant
+ */
const NoBlending = 0;
+
+/**
+ * The default blending.
+ *
+ * @type {number}
+ * @constant
+ */
const NormalBlending = 1;
+
+/**
+ * Represents additive blending.
+ *
+ * @type {number}
+ * @constant
+ */
const AdditiveBlending = 2;
+
+/**
+ * Represents subtractive blending.
+ *
+ * @type {number}
+ * @constant
+ */
const SubtractiveBlending = 3;
+
+/**
+ * Represents multiply blending.
+ *
+ * @type {number}
+ * @constant
+ */
const MultiplyBlending = 4;
+
+/**
+ * Represents custom blending.
+ *
+ * @type {number}
+ * @constant
+ */
const CustomBlending = 5;
+
+/**
+ * A `source + destination` blending equation.
+ *
+ * @type {number}
+ * @constant
+ */
const AddEquation = 100;
+
+/**
+ * A `source - destination` blending equation.
+ *
+ * @type {number}
+ * @constant
+ */
const SubtractEquation = 101;
+
+/**
+ * A `destination - source` blending equation.
+ *
+ * @type {number}
+ * @constant
+ */
const ReverseSubtractEquation = 102;
+
+/**
+ * A blend equation that uses the minimum of source and destination.
+ *
+ * @type {number}
+ * @constant
+ */
const MinEquation = 103;
+
+/**
+ * A blend equation that uses the maximum of source and destination.
+ *
+ * @type {number}
+ * @constant
+ */
const MaxEquation = 104;
+
+/**
+ * Multiplies all colors by `0`.
+ *
+ * @type {number}
+ * @constant
+ */
const ZeroFactor = 200;
+
+/**
+ * Multiplies all colors by `1`.
+ *
+ * @type {number}
+ * @constant
+ */
const OneFactor = 201;
+
+/**
+ * Multiplies all colors by the source colors.
+ *
+ * @type {number}
+ * @constant
+ */
const SrcColorFactor = 202;
+
+/**
+ * Multiplies all colors by `1` minus each source color.
+ *
+ * @type {number}
+ * @constant
+ */
const OneMinusSrcColorFactor = 203;
+
+/**
+ * Multiplies all colors by the source alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
const SrcAlphaFactor = 204;
+
+/**
+ * Multiplies all colors by 1 minus the source alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
const OneMinusSrcAlphaFactor = 205;
+
+/**
+ * Multiplies all colors by the destination alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
const DstAlphaFactor = 206;
+
+/**
+ * Multiplies all colors by `1` minus the destination alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
const OneMinusDstAlphaFactor = 207;
+
+/**
+ * Multiplies all colors by the destination color.
+ *
+ * @type {number}
+ * @constant
+ */
const DstColorFactor = 208;
+
+/**
+ * Multiplies all colors by `1` minus each destination color.
+ *
+ * @type {number}
+ * @constant
+ */
const OneMinusDstColorFactor = 209;
+
+/**
+ * Multiplies the RGB colors by the smaller of either the source alpha
+ * value or the value of `1` minus the destination alpha value. The alpha
+ * value is multiplied by `1`.
+ *
+ * @type {number}
+ * @constant
+ */
const SrcAlphaSaturateFactor = 210;
+
+/**
+ * Multiplies all colors by a constant color.
+ *
+ * @type {number}
+ * @constant
+ */
+const ConstantColorFactor = 211;
+
+/**
+ * Multiplies all colors by `1` minus a constant color.
+ *
+ * @type {number}
+ * @constant
+ */
+const OneMinusConstantColorFactor = 212;
+
+/**
+ * Multiplies all colors by a constant alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
+const ConstantAlphaFactor = 213;
+
+/**
+ * Multiplies all colors by 1 minus a constant alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
+const OneMinusConstantAlphaFactor = 214;
+
+/**
+ * Never pass.
+ *
+ * @type {number}
+ * @constant
+ */
const NeverDepth = 0;
+
+/**
+ * Always pass.
+ *
+ * @type {number}
+ * @constant
+ */
const AlwaysDepth = 1;
+
+/**
+ * Pass if the incoming value is less than the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessDepth = 2;
+
+/**
+ * Pass if the incoming value is less than or equal to the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessEqualDepth = 3;
+
+/**
+ * Pass if the incoming value equals the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const EqualDepth = 4;
+
+/**
+ * Pass if the incoming value is greater than or equal to the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterEqualDepth = 5;
+
+/**
+ * Pass if the incoming value is greater than the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterDepth = 6;
+
+/**
+ * Pass if the incoming value is not equal to the depth buffer value.
+ *
+ * @type {number}
+ * @constant
+ */
const NotEqualDepth = 7;
+
+/**
+ * Multiplies the environment map color with the surface color.
+ *
+ * @type {number}
+ * @constant
+ */
const MultiplyOperation = 0;
+
+/**
+ * Uses reflectivity to blend between the two colors.
+ *
+ * @type {number}
+ * @constant
+ */
const MixOperation = 1;
+
+/**
+ * Adds the two colors.
+ *
+ * @type {number}
+ * @constant
+ */
const AddOperation = 2;
+
+/**
+ * No tone mapping is applied.
+ *
+ * @type {number}
+ * @constant
+ */
const NoToneMapping = 0;
+
+/**
+ * Linear tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
const LinearToneMapping = 1;
+
+/**
+ * Reinhard tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
const ReinhardToneMapping = 2;
+
+/**
+ * Cineon tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
const CineonToneMapping = 3;
+
+/**
+ * ACES Filmic tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
const ACESFilmicToneMapping = 4;
+
+/**
+ * Custom tone mapping.
+ *
+ * Expects a custom implementation by modifying shader code of the material's fragment shader.
+ *
+ * @type {number}
+ * @constant
+ */
const CustomToneMapping = 5;
+/**
+ * AgX tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
+const AgXToneMapping = 6;
+
+/**
+ * Neutral tone mapping.
+ *
+ * Implementation based on the Khronos 3D Commerce Group standard tone mapping.
+ *
+ * @type {number}
+ * @constant
+ */
+const NeutralToneMapping = 7;
+
+/**
+ * The skinned mesh shares the same world space as the skeleton.
+ *
+ * @type {string}
+ * @constant
+ */
+const AttachedBindMode = 'attached';
+
+/**
+ * The skinned mesh does not share the same world space as the skeleton.
+ * This is useful when a skeleton is shared across multiple skinned meshes.
+ *
+ * @type {string}
+ * @constant
+ */
+const DetachedBindMode = 'detached';
+
+/**
+ * Maps textures using the geometry's UV coordinates.
+ *
+ * @type {number}
+ * @constant
+ */
const UVMapping = 300;
+
+/**
+ * Reflection mapping for cube textures.
+ *
+ * @type {number}
+ * @constant
+ */
const CubeReflectionMapping = 301;
+
+/**
+ * Refraction mapping for cube textures.
+ *
+ * @type {number}
+ * @constant
+ */
const CubeRefractionMapping = 302;
+
+/**
+ * Reflection mapping for equirectangular textures.
+ *
+ * @type {number}
+ * @constant
+ */
const EquirectangularReflectionMapping = 303;
+
+/**
+ * Refraction mapping for equirectangular textures.
+ *
+ * @type {number}
+ * @constant
+ */
const EquirectangularRefractionMapping = 304;
+
+/**
+ * Reflection mapping for PMREM textures.
+ *
+ * @type {number}
+ * @constant
+ */
const CubeUVReflectionMapping = 306;
+
+/**
+ * The texture will simply repeat to infinity.
+ *
+ * @type {number}
+ * @constant
+ */
const RepeatWrapping = 1000;
+
+/**
+ * The last pixel of the texture stretches to the edge of the mesh.
+ *
+ * @type {number}
+ * @constant
+ */
const ClampToEdgeWrapping = 1001;
+
+/**
+ * The texture will repeats to infinity, mirroring on each repeat.
+ *
+ * @type {number}
+ * @constant
+ */
const MirroredRepeatWrapping = 1002;
+
+/**
+ * Returns the value of the texture element that is nearest (in Manhattan distance)
+ * to the specified texture coordinates.
+ *
+ * @type {number}
+ * @constant
+ */
const NearestFilter = 1003;
+
+/**
+ * Chooses the mipmap that most closely matches the size of the pixel being textured
+ * and uses the `NearestFilter` criterion (the texel nearest to the center of the pixel)
+ * to produce a texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const NearestMipmapNearestFilter = 1004;
-const NearestMipMapNearestFilter = 1004;
+const NearestMipMapNearestFilter = 1004; // legacy
+
+/**
+ * Chooses the two mipmaps that most closely match the size of the pixel being textured and
+ * uses the `NearestFilter` criterion to produce a texture value from each mipmap.
+ * The final texture value is a weighted average of those two values.
+ *
+ * @type {number}
+ * @constant
+ */
const NearestMipmapLinearFilter = 1005;
-const NearestMipMapLinearFilter = 1005;
+const NearestMipMapLinearFilter = 1005; // legacy
+
+/**
+ * Returns the weighted average of the four texture elements that are closest to the specified
+ * texture coordinates, and can include items wrapped or repeated from other parts of a texture,
+ * depending on the values of `wrapS` and `wrapT`, and on the exact mapping.
+ *
+ * @type {number}
+ * @constant
+ */
const LinearFilter = 1006;
+
+/**
+ * Chooses the mipmap that most closely matches the size of the pixel being textured and uses
+ * the `LinearFilter` criterion (a weighted average of the four texels that are closest to the
+ * center of the pixel) to produce a texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const LinearMipmapNearestFilter = 1007;
-const LinearMipMapNearestFilter = 1007;
+const LinearMipMapNearestFilter = 1007; // legacy
+
+/**
+ * Chooses the two mipmaps that most closely match the size of the pixel being textured and uses
+ * the `LinearFilter` criterion to produce a texture value from each mipmap. The final texture value
+ * is a weighted average of those two values.
+ *
+ * @type {number}
+ * @constant
+ */
const LinearMipmapLinearFilter = 1008;
-const LinearMipMapLinearFilter = 1008;
+const LinearMipMapLinearFilter = 1008; // legacy
+
+/**
+ * An unsigned byte data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedByteType = 1009;
+
+/**
+ * A byte data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const ByteType = 1010;
+
+/**
+ * A short data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const ShortType = 1011;
+
+/**
+ * An unsigned short data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedShortType = 1012;
+
+/**
+ * An int data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const IntType = 1013;
+
+/**
+ * An unsigned int data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedIntType = 1014;
+
+/**
+ * A float data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const FloatType = 1015;
+
+/**
+ * A half float data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const HalfFloatType = 1016;
+
+/**
+ * An unsigned short 4_4_4_4 (packed) data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedShort4444Type = 1017;
+
+/**
+ * An unsigned short 5_5_5_1 (packed) data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedShort5551Type = 1018;
+
+/**
+ * An unsigned int 24_8 data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
const UnsignedInt248Type = 1020;
+
+/**
+ * An unsigned int 5_9_9_9 (packed) data type for textures.
+ *
+ * @type {number}
+ * @constant
+ */
+const UnsignedInt5999Type = 35902;
+
+/**
+ * Discards the red, green and blue components and reads just the alpha component.
+ *
+ * @type {number}
+ * @constant
+ */
const AlphaFormat = 1021;
+
+/**
+ * Discards the alpha component and reads the red, green and blue component.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGBFormat = 1022;
+
+/**
+ * Reads the red, green, blue and alpha components.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBAFormat = 1023;
-const LuminanceFormat = 1024;
-const LuminanceAlphaFormat = 1025;
+
+/**
+ * Reads each element as a single depth value, converts it to floating point, and clamps to the range `[0,1]`.
+ *
+ * @type {number}
+ * @constant
+ */
const DepthFormat = 1026;
+
+/**
+ * Reads each element is a pair of depth and stencil values. The depth component of the pair is interpreted as
+ * in `DepthFormat`. The stencil component is interpreted based on the depth + stencil internal format.
+ *
+ * @type {number}
+ * @constant
+ */
const DepthStencilFormat = 1027;
+
+/**
+ * Discards the green, blue and alpha components and reads just the red component.
+ *
+ * @type {number}
+ * @constant
+ */
const RedFormat = 1028;
+
+/**
+ * Discards the green, blue and alpha components and reads just the red component. The texels are read as integers instead of floating point.
+ *
+ * @type {number}
+ * @constant
+ */
const RedIntegerFormat = 1029;
+
+/**
+ * Discards the alpha, and blue components and reads the red, and green components.
+ *
+ * @type {number}
+ * @constant
+ */
const RGFormat = 1030;
+
+/**
+ * Discards the alpha, and blue components and reads the red, and green components. The texels are read as integers instead of floating point.
+ *
+ * @type {number}
+ * @constant
+ */
const RGIntegerFormat = 1031;
+
+/**
+ * Discards the alpha component and reads the red, green and blue component. The texels are read as integers instead of floating point.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGBIntegerFormat = 1032;
+
+/**
+ * Reads the red, green, blue and alpha components. The texels are read as integers instead of floating point.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBAIntegerFormat = 1033;
+/**
+ * A DXT1-compressed image in an RGB image format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGB_S3TC_DXT1_Format = 33776;
+
+/**
+ * A DXT1-compressed image in an RGB image format with a simple on/off alpha value.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_S3TC_DXT1_Format = 33777;
+
+/**
+ * A DXT3-compressed image in an RGBA image format. Compared to a 32-bit RGBA texture, it offers 4:1 compression.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_S3TC_DXT3_Format = 33778;
+
+/**
+ * A DXT5-compressed image in an RGBA image format. It also provides a 4:1 compression, but differs to the DXT3
+ * compression in how the alpha compression is done.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_S3TC_DXT5_Format = 33779;
+
+/**
+ * PVRTC RGB compression in 4-bit mode. One block for each 4×4 pixels.
+ *
+ * @type {number}
+ * @constant
+ */
const RGB_PVRTC_4BPPV1_Format = 35840;
+
+/**
+ * PVRTC RGB compression in 2-bit mode. One block for each 8×4 pixels.
+ *
+ * @type {number}
+ * @constant
+ */
const RGB_PVRTC_2BPPV1_Format = 35841;
+
+/**
+ * PVRTC RGBA compression in 4-bit mode. One block for each 4×4 pixels.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_PVRTC_4BPPV1_Format = 35842;
+
+/**
+ * PVRTC RGBA compression in 2-bit mode. One block for each 8×4 pixels.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_PVRTC_2BPPV1_Format = 35843;
+
+/**
+ * ETC1 RGB format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGB_ETC1_Format = 36196;
+
+/**
+ * ETC2 RGB format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGB_ETC2_Format = 37492;
+
+/**
+ * ETC2 RGBA format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ETC2_EAC_Format = 37496;
+
+/**
+ * ASTC RGBA 4x4 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_4x4_Format = 37808;
+
+/**
+ * ASTC RGBA 5x4 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_5x4_Format = 37809;
+
+/**
+ * ASTC RGBA 5x5 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_5x5_Format = 37810;
+
+/**
+ * ASTC RGBA 6x5 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_6x5_Format = 37811;
+
+/**
+ * ASTC RGBA 6x6 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_6x6_Format = 37812;
+
+/**
+ * ASTC RGBA 8x5 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_8x5_Format = 37813;
+
+/**
+ * ASTC RGBA 8x6 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_8x6_Format = 37814;
+
+/**
+ * ASTC RGBA 8x8 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_8x8_Format = 37815;
+
+/**
+ * ASTC RGBA 10x5 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_10x5_Format = 37816;
+
+/**
+ * ASTC RGBA 10x6 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_10x6_Format = 37817;
+
+/**
+ * ASTC RGBA 10x8 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_10x8_Format = 37818;
+
+/**
+ * ASTC RGBA 10x10 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_10x10_Format = 37819;
+
+/**
+ * ASTC RGBA 12x10 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_12x10_Format = 37820;
+
+/**
+ * ASTC RGBA 12x12 format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_ASTC_12x12_Format = 37821;
+
+/**
+ * BPTC RGBA format.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBA_BPTC_Format = 36492;
+
+/**
+ * BPTC Signed RGB format.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGB_BPTC_SIGNED_Format = 36494;
+
+/**
+ * BPTC Unsigned RGB format.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGB_BPTC_UNSIGNED_Format = 36495;
+
+/**
+ * RGTC1 Red format.
+ *
+ * @type {number}
+ * @constant
+ */
const RED_RGTC1_Format = 36283;
+
+/**
+ * RGTC1 Signed Red format.
+ *
+ * @type {number}
+ * @constant
+ */
const SIGNED_RED_RGTC1_Format = 36284;
-const RED_GREEN_RGTC2_Format = 36285;
+
+/**
+ * RGTC2 Red Green format.
+ *
+ * @type {number}
+ * @constant
+ */
+const RED_GREEN_RGTC2_Format = 36285;
+
+/**
+ * RGTC2 Signed Red Green format.
+ *
+ * @type {number}
+ * @constant
+ */
const SIGNED_RED_GREEN_RGTC2_Format = 36286;
+
+/**
+ * Animations are played once.
+ *
+ * @type {number}
+ * @constant
+ */
const LoopOnce = 2200;
+
+/**
+ * Animations are played with a chosen number of repetitions, each time jumping from
+ * the end of the clip directly to its beginning.
+ *
+ * @type {number}
+ * @constant
+ */
const LoopRepeat = 2201;
+
+/**
+ * Animations are played with a chosen number of repetitions, alternately playing forward
+ * and backward.
+ *
+ * @type {number}
+ * @constant
+ */
const LoopPingPong = 2202;
+
+/**
+ * Discrete interpolation mode for keyframe tracks.
+ *
+ * @type {number}
+ * @constant
+ */
const InterpolateDiscrete = 2300;
+
+/**
+ * Linear interpolation mode for keyframe tracks.
+ *
+ * @type {number}
+ * @constant
+ */
const InterpolateLinear = 2301;
+
+/**
+ * Smooth interpolation mode for keyframe tracks.
+ *
+ * @type {number}
+ * @constant
+ */
const InterpolateSmooth = 2302;
+
+/**
+ * Zero curvature ending for animations.
+ *
+ * @type {number}
+ * @constant
+ */
const ZeroCurvatureEnding = 2400;
+
+/**
+ * Zero slope ending for animations.
+ *
+ * @type {number}
+ * @constant
+ */
const ZeroSlopeEnding = 2401;
+
+/**
+ * Wrap around ending for animations.
+ *
+ * @type {number}
+ * @constant
+ */
const WrapAroundEnding = 2402;
+
+/**
+ * Default animation blend mode.
+ *
+ * @type {number}
+ * @constant
+ */
const NormalAnimationBlendMode = 2500;
+
+/**
+ * Additive animation blend mode. Can be used to layer motions on top of
+ * each other to build complex performances from smaller re-usable assets.
+ *
+ * @type {number}
+ * @constant
+ */
const AdditiveAnimationBlendMode = 2501;
+
+/**
+ * For every three vertices draw a single triangle.
+ *
+ * @type {number}
+ * @constant
+ */
const TrianglesDrawMode = 0;
+
+/**
+ * For each vertex draw a triangle from the last three vertices.
+ *
+ * @type {number}
+ * @constant
+ */
const TriangleStripDrawMode = 1;
+
+/**
+ * For each vertex draw a triangle from the first vertex and the last two vertices.
+ *
+ * @type {number}
+ * @constant
+ */
const TriangleFanDrawMode = 2;
-/** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */
-const LinearEncoding = 3000;
-/** @deprecated Use SRGBColorSpace in three.js r152+. */
-const sRGBEncoding = 3001;
+
+/**
+ * Basic depth packing.
+ *
+ * @type {number}
+ * @constant
+ */
const BasicDepthPacking = 3200;
+
+/**
+ * A depth value is packed into 32 bit RGBA.
+ *
+ * @type {number}
+ * @constant
+ */
const RGBADepthPacking = 3201;
+
+/**
+ * A depth value is packed into 24 bit RGB.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGBDepthPacking = 3202;
+
+/**
+ * A depth value is packed into 16 bit RG.
+ *
+ * @type {number}
+ * @constant
+ */
+const RGDepthPacking = 3203;
+
+/**
+ * Normal information is relative to the underlying surface.
+ *
+ * @type {number}
+ * @constant
+ */
const TangentSpaceNormalMap = 0;
+
+/**
+ * Normal information is relative to the object orientation.
+ *
+ * @type {number}
+ * @constant
+ */
const ObjectSpaceNormalMap = 1;
// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.
+
+/**
+ * No color space.
+ *
+ * @type {string}
+ * @constant
+ */
const NoColorSpace = '';
+
+/**
+ * sRGB color space.
+ *
+ * @type {string}
+ * @constant
+ */
const SRGBColorSpace = 'srgb';
+
+/**
+ * sRGB-linear color space.
+ *
+ * @type {string}
+ * @constant
+ */
const LinearSRGBColorSpace = 'srgb-linear';
-const DisplayP3ColorSpace = 'display-p3';
+/**
+ * Linear transfer function.
+ *
+ * @type {string}
+ * @constant
+ */
+const LinearTransfer = 'linear';
+
+/**
+ * sRGB transfer function.
+ *
+ * @type {string}
+ * @constant
+ */
+const SRGBTransfer = 'srgb';
+
+/**
+ * Sets the stencil buffer value to `0`.
+ *
+ * @type {number}
+ * @constant
+ */
const ZeroStencilOp = 0;
+
+/**
+ * Keeps the current value.
+ *
+ * @type {number}
+ * @constant
+ */
const KeepStencilOp = 7680;
+
+/**
+ * Sets the stencil buffer value to the specified reference value.
+ *
+ * @type {number}
+ * @constant
+ */
const ReplaceStencilOp = 7681;
+
+/**
+ * Increments the current stencil buffer value. Clamps to the maximum representable unsigned value.
+ *
+ * @type {number}
+ * @constant
+ */
const IncrementStencilOp = 7682;
+
+/**
+ * Decrements the current stencil buffer value. Clamps to `0`.
+ *
+ * @type {number}
+ * @constant
+ */
const DecrementStencilOp = 7683;
+
+/**
+ * Increments the current stencil buffer value. Wraps stencil buffer value to zero when incrementing
+ * the maximum representable unsigned value.
+ *
+ * @type {number}
+ * @constant
+ */
const IncrementWrapStencilOp = 34055;
+
+/**
+ * Decrements the current stencil buffer value. Wraps stencil buffer value to the maximum representable
+ * unsigned value when decrementing a stencil buffer value of `0`.
+ *
+ * @type {number}
+ * @constant
+ */
const DecrementWrapStencilOp = 34056;
+
+/**
+ * Inverts the current stencil buffer value bitwise.
+ *
+ * @type {number}
+ * @constant
+ */
const InvertStencilOp = 5386;
+/**
+ * Will never return true.
+ *
+ * @type {number}
+ * @constant
+ */
const NeverStencilFunc = 512;
+
+/**
+ * Will return true if the stencil reference value is less than the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessStencilFunc = 513;
+
+/**
+ * Will return true if the stencil reference value is equal to the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const EqualStencilFunc = 514;
+
+/**
+ * Will return true if the stencil reference value is less than or equal to the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessEqualStencilFunc = 515;
+
+/**
+ * Will return true if the stencil reference value is greater than the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterStencilFunc = 516;
+
+/**
+ * Will return true if the stencil reference value is not equal to the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const NotEqualStencilFunc = 517;
+
+/**
+ * Will return true if the stencil reference value is greater than or equal to the current stencil value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterEqualStencilFunc = 518;
+
+/**
+ * Will always return true.
+ *
+ * @type {number}
+ * @constant
+ */
const AlwaysStencilFunc = 519;
+/**
+ * Never pass.
+ *
+ * @type {number}
+ * @constant
+ */
const NeverCompare = 512;
+
+/**
+ * Pass if the incoming value is less than the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessCompare = 513;
+
+/**
+ * Pass if the incoming value equals the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const EqualCompare = 514;
+
+/**
+ * Pass if the incoming value is less than or equal to the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const LessEqualCompare = 515;
+
+/**
+ * Pass if the incoming value is greater than the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterCompare = 516;
+
+/**
+ * Pass if the incoming value is not equal to the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const NotEqualCompare = 517;
+
+/**
+ * Pass if the incoming value is greater than or equal to the texture value.
+ *
+ * @type {number}
+ * @constant
+ */
const GreaterEqualCompare = 518;
+
+/**
+ * Always pass.
+ *
+ * @type {number}
+ * @constant
+ */
const AlwaysCompare = 519;
+/**
+ * The contents are intended to be specified once by the application, and used many
+ * times as the source for drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const StaticDrawUsage = 35044;
+
+/**
+ * The contents are intended to be respecified repeatedly by the application, and
+ * used many times as the source for drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const DynamicDrawUsage = 35048;
+
+/**
+ * The contents are intended to be specified once by the application, and used at most
+ * a few times as the source for drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const StreamDrawUsage = 35040;
+
+/**
+ * The contents are intended to be specified once by reading data from the 3D API, and queried
+ * many times by the application.
+ *
+ * @type {number}
+ * @constant
+ */
const StaticReadUsage = 35045;
+
+/**
+ * The contents are intended to be respecified repeatedly by reading data from the 3D API, and queried
+ * many times by the application.
+ *
+ * @type {number}
+ * @constant
+ */
const DynamicReadUsage = 35049;
+
+/**
+ * The contents are intended to be specified once by reading data from the 3D API, and queried at most
+ * a few times by the application
+ *
+ * @type {number}
+ * @constant
+ */
const StreamReadUsage = 35041;
+
+/**
+ * The contents are intended to be specified once by reading data from the 3D API, and used many times as
+ * the source for WebGL drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const StaticCopyUsage = 35046;
+
+/**
+ * The contents are intended to be respecified repeatedly by reading data from the 3D API, and used many times
+ * as the source for WebGL drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const DynamicCopyUsage = 35050;
+
+/**
+ * The contents are intended to be specified once by reading data from the 3D API, and used at most a few times
+ * as the source for WebGL drawing and image specification commands.
+ *
+ * @type {number}
+ * @constant
+ */
const StreamCopyUsage = 35042;
+/**
+ * GLSL 1 shader code.
+ *
+ * @type {string}
+ * @constant
+ */
const GLSL1 = '100';
+
+/**
+ * GLSL 3 shader code.
+ *
+ * @type {string}
+ * @constant
+ */
const GLSL3 = '300 es';
-const _SRGBAFormat = 1035; // fallback for WebGL 1
+/**
+ * WebGL coordinate system.
+ *
+ * @type {number}
+ * @constant
+ */
+const WebGLCoordinateSystem = 2000;
+
+/**
+ * WebGPU coordinate system.
+ *
+ * @type {number}
+ * @constant
+ */
+const WebGPUCoordinateSystem = 2001;
+
+/**
+ * Represents the different timestamp query types.
+ *
+ * @type {ConstantsTimestampQuery}
+ * @constant
+ */
+const TimestampQuery = {
+ COMPUTE: 'compute',
+ RENDER: 'render'
+};
+
+/**
+ * Represents mouse buttons and interaction types in context of controls.
+ *
+ * @type {ConstantsInterpolationSamplingType}
+ * @constant
+ */
+const InterpolationSamplingType = {
+ PERSPECTIVE: 'perspective',
+ LINEAR: 'linear',
+ FLAT: 'flat'
+};
+
+/**
+ * Represents the different interpolation sampling modes.
+ *
+ * @type {ConstantsInterpolationSamplingMode}
+ * @constant
+ */
+const InterpolationSamplingMode = {
+ NORMAL: 'normal',
+ CENTROID: 'centroid',
+ SAMPLE: 'sample',
+ FIRST: 'first',
+ EITHER: 'either'
+};
+
+/**
+ * This type represents mouse buttons and interaction types in context of controls.
+ *
+ * @typedef {Object} ConstantsMouse
+ * @property {number} MIDDLE - The left mouse button.
+ * @property {number} LEFT - The middle mouse button.
+ * @property {number} RIGHT - The right mouse button.
+ * @property {number} ROTATE - A rotate interaction.
+ * @property {number} DOLLY - A dolly interaction.
+ * @property {number} PAN - A pan interaction.
+ **/
+
+/**
+ * This type represents touch interaction types in context of controls.
+ *
+ * @typedef {Object} ConstantsTouch
+ * @property {number} ROTATE - A rotate interaction.
+ * @property {number} PAN - A pan interaction.
+ * @property {number} DOLLY_PAN - The dolly-pan interaction.
+ * @property {number} DOLLY_ROTATE - A dolly-rotate interaction.
+ **/
+
+/**
+ * This type represents the different timestamp query types.
+ *
+ * @typedef {Object} ConstantsTimestampQuery
+ * @property {string} COMPUTE - A `compute` timestamp query.
+ * @property {string} RENDER - A `render` timestamp query.
+ **/
+
+/**
+ * Represents the different interpolation sampling types.
+ *
+ * @typedef {Object} ConstantsInterpolationSamplingType
+ * @property {string} PERSPECTIVE - Perspective-correct interpolation.
+ * @property {string} LINEAR - Linear interpolation.
+ * @property {string} FLAT - Flat interpolation.
+ */
/**
- * https://github.com/mrdoob/eventdispatcher.js/
+ * Represents the different interpolation sampling modes.
+ *
+ * @typedef {Object} ConstantsInterpolationSamplingMode
+ * @property {string} NORMAL - Normal sampling mode.
+ * @property {string} CENTROID - Centroid sampling mode.
+ * @property {string} SAMPLE - Sample-specific sampling mode.
+ * @property {string} FLAT_FIRST - Flat interpolation using the first vertex.
+ * @property {string} FLAT_EITHER - Flat interpolation using either vertex.
*/
+/**
+ * This modules allows to dispatch event objects on custom JavaScript objects.
+ *
+ * Main repository: [eventdispatcher.js]{@link https://github.com/mrdoob/eventdispatcher.js/}
+ *
+ * Code Example:
+ * ```js
+ * class Car extends EventDispatcher {
+ * start() {
+ * this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
+ * }
+ *};
+ *
+ * // Using events with the custom object
+ * const car = new Car();
+ * car.addEventListener( 'start', function ( event ) {
+ * alert( event.message );
+ * } );
+ *
+ * car.start();
+ * ```
+ */
class EventDispatcher {
+ /**
+ * Adds the given event listener to the given event type.
+ *
+ * @param {string} type - The type of event to listen to.
+ * @param {Function} listener - The function that gets called when the event is fired.
+ */
addEventListener( type, listener ) {
if ( this._listeners === undefined ) this._listeners = {};
@@ -222,7 +1715,7 @@ class EventDispatcher {
}
- if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+ if ( listeners[ type ].indexOf( listener ) === -1 ) {
listeners[ type ].push( listener );
@@ -230,28 +1723,42 @@ class EventDispatcher {
}
+ /**
+ * Returns `true` if the given event listener has been added to the given event type.
+ *
+ * @param {string} type - The type of event.
+ * @param {Function} listener - The listener to check.
+ * @return {boolean} Whether the given event listener has been added to the given event type.
+ */
hasEventListener( type, listener ) {
- if ( this._listeners === undefined ) return false;
-
const listeners = this._listeners;
- return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
+ if ( listeners === undefined ) return false;
+
+ return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== -1;
}
+ /**
+ * Removes the given event listener from the given event type.
+ *
+ * @param {string} type - The type of event.
+ * @param {Function} listener - The listener to remove.
+ */
removeEventListener( type, listener ) {
- if ( this._listeners === undefined ) return;
-
const listeners = this._listeners;
+
+ if ( listeners === undefined ) return;
+
const listenerArray = listeners[ type ];
if ( listenerArray !== undefined ) {
const index = listenerArray.indexOf( listener );
- if ( index !== - 1 ) {
+ if ( index !== -1 ) {
listenerArray.splice( index, 1 );
@@ -261,11 +1768,17 @@ class EventDispatcher {
}
+ /**
+ * Dispatches an event object.
+ *
+ * @param {Object} event - The event that gets fired.
+ */
dispatchEvent( event ) {
- if ( this._listeners === undefined ) return;
-
const listeners = this._listeners;
+
+ if ( listeners === undefined ) return;
+
const listenerArray = listeners[ event.type ];
if ( listenerArray !== undefined ) {
@@ -297,9 +1810,16 @@ let _seed = 1234567;
const DEG2RAD = Math.PI / 180;
const RAD2DEG = 180 / Math.PI;
-// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+/**
+ * Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier}
+ * (universally unique identifier).
+ *
+ * @return {string} The UUID.
+ */
function generateUUID() {
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+
const d0 = Math.random() * 0xffffffff | 0;
const d1 = Math.random() * 0xffffffff | 0;
const d2 = Math.random() * 0xffffffff | 0;
@@ -314,30 +1834,66 @@ function generateUUID() {
}
+/**
+ * Clamps the given value between min and max.
+ *
+ * @param {number} value - The value to clamp.
+ * @param {number} min - The min value.
+ * @param {number} max - The max value.
+ * @return {number} The clamped value.
+ */
function clamp( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
}
-// compute euclidean modulo of m % n
-// https://en.wikipedia.org/wiki/Modulo_operation
+/**
+ * Computes the Euclidean modulo of the given parameters that
+ * is `( ( n % m ) + m ) % m`.
+ *
+ * @param {number} n - The first parameter.
+ * @param {number} m - The second parameter.
+ * @return {number} The Euclidean modulo.
+ */
function euclideanModulo( n, m ) {
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
return ( ( n % m ) + m ) % m;
}
-// Linear mapping from range to range
+/**
+ * Performs a linear mapping from range `` to range ``
+ * for the given value.
+ *
+ * @param {number} x - The value to be mapped.
+ * @param {number} a1 - Minimum value for range A.
+ * @param {number} a2 - Maximum value for range A.
+ * @param {number} b1 - Minimum value for range B.
+ * @param {number} b2 - Maximum value for range B.
+ * @return {number} The mapped value.
+ */
function mapLinear( x, a1, a2, b1, b2 ) {
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
}
-// https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/
+/**
+ * Returns the percentage in the closed interval `[0, 1]` of the given value
+ * between the start and end point.
+ *
+ * @param {number} x - The start point
+ * @param {number} y - The end point.
+ * @param {number} value - A value between start and end.
+ * @return {number} The interpolation factor.
+ */
function inverseLerp( x, y, value ) {
+ // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/
+
if ( x !== y ) {
return ( value - x ) / ( y - x );
@@ -350,28 +1906,66 @@ function inverseLerp( x, y, value ) {
}
-// https://en.wikipedia.org/wiki/Linear_interpolation
+/**
+ * Returns a value linearly interpolated from two known points based on the given interval -
+ * `t = 0` will return `x` and `t = 1` will return `y`.
+ *
+ * @param {number} x - The start point
+ * @param {number} y - The end point.
+ * @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
+ * @return {number} The interpolated value.
+ */
function lerp( x, y, t ) {
return ( 1 - t ) * x + t * y;
}
-// http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
+/**
+ * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta
+ * time to maintain frame rate independent movement. For details, see
+ * [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}.
+ *
+ * @param {number} x - The current point.
+ * @param {number} y - The target point.
+ * @param {number} lambda - A higher lambda value will make the movement more sudden,
+ * and a lower value will make the movement more gradual.
+ * @param {number} dt - Delta time in seconds.
+ * @return {number} The interpolated value.
+ */
function damp( x, y, lambda, dt ) {
return lerp( x, y, 1 - Math.exp( - lambda * dt ) );
}
-// https://www.desmos.com/calculator/vcsjnyz7x4
+/**
+ * Returns a value that alternates between `0` and the given `length` parameter.
+ *
+ * @param {number} x - The value to pingpong.
+ * @param {number} [length=1] - The positive value the function will pingpong to.
+ * @return {number} The alternated value.
+ */
function pingpong( x, length = 1 ) {
+ // https://www.desmos.com/calculator/vcsjnyz7x4
+
return length - Math.abs( euclideanModulo( x, length * 2 ) - length );
}
-// http://en.wikipedia.org/wiki/Smoothstep
+/**
+ * Returns a value in the range `[0,1]` that represents the percentage that `x` has
+ * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to
+ * the `min` and `max`.
+ *
+ * See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details.
+ *
+ * @param {number} x - The value to evaluate based on its position between min and max.
+ * @param {number} min - The min value. Any x value below min will be `0`.
+ * @param {number} max - The max value. Any x value above max will be `1`.
+ * @return {number} The alternated value.
+ */
function smoothstep( x, min, max ) {
if ( x <= min ) return 0;
@@ -383,6 +1977,15 @@ function smoothstep( x, min, max ) {
}
+/**
+ * A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations}
+ * that has zero 1st and 2nd order derivatives at x=0 and x=1.
+ *
+ * @param {number} x - The value to evaluate based on its position between min and max.
+ * @param {number} min - The min value. Any x value below min will be `0`.
+ * @param {number} max - The max value. Any x value above max will be `1`.
+ * @return {number} The alternated value.
+ */
function smootherstep( x, min, max ) {
if ( x <= min ) return 0;
@@ -394,28 +1997,50 @@ function smootherstep( x, min, max ) {
}
-// Random integer from interval
+/**
+ * Returns a random integer from `` interval.
+ *
+ * @param {number} low - The lower value boundary.
+ * @param {number} high - The upper value boundary
+ * @return {number} A random integer.
+ */
function randInt( low, high ) {
return low + Math.floor( Math.random() * ( high - low + 1 ) );
}
-// Random float from interval
+/**
+ * Returns a random float from `` interval.
+ *
+ * @param {number} low - The lower value boundary.
+ * @param {number} high - The upper value boundary
+ * @return {number} A random float.
+ */
function randFloat( low, high ) {
return low + Math.random() * ( high - low );
}
-// Random float from <-range/2, range/2> interval
+/**
+ * Returns a random integer from `<-range/2, range/2>` interval.
+ *
+ * @param {number} range - Defines the value range.
+ * @return {number} A random float.
+ */
function randFloatSpread( range ) {
return range * ( 0.5 - Math.random() );
}
-// Deterministic pseudo-random float in the interval [ 0, 1 ]
+/**
+ * Returns a deterministic pseudo-random float in the interval `[0, 1]`.
+ *
+ * @param {number} [s] - The integer seed.
+ * @return {number} A random float.
+ */
function seededRandom( s ) {
if ( s !== undefined ) _seed = s;
@@ -432,44 +2057,81 @@ function seededRandom( s ) {
}
+/**
+ * Converts degrees to radians.
+ *
+ * @param {number} degrees - A value in degrees.
+ * @return {number} The converted value in radians.
+ */
function degToRad( degrees ) {
return degrees * DEG2RAD;
}
+/**
+ * Converts radians to degrees.
+ *
+ * @param {number} radians - A value in radians.
+ * @return {number} The converted value in degrees.
+ */
function radToDeg( radians ) {
return radians * RAD2DEG;
}
+/**
+ * Returns `true` if the given number is a power of two.
+ *
+ * @param {number} value - The value to check.
+ * @return {boolean} Whether the given number is a power of two or not.
+ */
function isPowerOfTwo( value ) {
return ( value & ( value - 1 ) ) === 0 && value !== 0;
}
+/**
+ * Returns the smallest power of two that is greater than or equal to the given number.
+ *
+ * @param {number} value - The value to find a POT for.
+ * @return {number} The smallest power of two that is greater than or equal to the given number.
+ */
function ceilPowerOfTwo( value ) {
return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
}
+/**
+ * Returns the largest power of two that is less than or equal to the given number.
+ *
+ * @param {number} value - The value to find a POT for.
+ * @return {number} The largest power of two that is less than or equal to the given number.
+ */
function floorPowerOfTwo( value ) {
return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
}
+/**
+ * Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles}
+ * defined by the given angles and order.
+ *
+ * Rotations are applied to the axes in the order specified by order:
+ * rotation by angle `a` is applied first, then by angle `b`, then by angle `c`.
+ *
+ * @param {Quaternion} q - The quaternion to set.
+ * @param {number} a - The rotation applied to the first axis, in radians.
+ * @param {number} b - The rotation applied to the second axis, in radians.
+ * @param {number} c - The rotation applied to the third axis, in radians.
+ * @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order.
+ */
function setQuaternionFromProperEuler( q, a, b, c, order ) {
- // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
-
- // rotations are applied to the axes in the order specified by 'order'
- // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
- // angles are in radians
-
const cos = Math.cos;
const sin = Math.sin;
@@ -518,6 +2180,13 @@ function setQuaternionFromProperEuler( q, a, b, c, order ) {
}
+/**
+ * Denormalizes the given value according to the given typed array.
+ *
+ * @param {number} value - The value to denormalize.
+ * @param {TypedArray} array - The typed array that defines the data type of the value.
+ * @return {number} The denormalize (float) value in the range `[0,1]`.
+ */
function denormalize( value, array ) {
switch ( array.constructor ) {
@@ -540,15 +2209,15 @@ function denormalize( value, array ) {
case Int32Array:
- return Math.max( value / 2147483647.0, - 1.0 );
+ return Math.max( value / 2147483647.0, -1 );
case Int16Array:
- return Math.max( value / 32767.0, - 1.0 );
+ return Math.max( value / 32767.0, -1 );
case Int8Array:
- return Math.max( value / 127.0, - 1.0 );
+ return Math.max( value / 127.0, -1 );
default:
@@ -558,6 +2227,13 @@ function denormalize( value, array ) {
}
+/**
+ * Normalizes the given value according to the given typed array.
+ *
+ * @param {number} value - The float value in the range `[0,1]` to normalize.
+ * @param {TypedArray} array - The typed array that defines the data type of the value.
+ * @return {number} The normalize value.
+ */
function normalize( value, array ) {
switch ( array.constructor ) {
@@ -598,44 +2274,321 @@ function normalize( value, array ) {
}
+/**
+ * @class
+ * @classdesc A collection of math utility functions.
+ * @hideconstructor
+ */
const MathUtils = {
DEG2RAD: DEG2RAD,
RAD2DEG: RAD2DEG,
+ /**
+ * Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier}
+ * (universally unique identifier).
+ *
+ * @static
+ * @method
+ * @return {string} The UUID.
+ */
generateUUID: generateUUID,
+ /**
+ * Clamps the given value between min and max.
+ *
+ * @static
+ * @method
+ * @param {number} value - The value to clamp.
+ * @param {number} min - The min value.
+ * @param {number} max - The max value.
+ * @return {number} The clamped value.
+ */
clamp: clamp,
+ /**
+ * Computes the Euclidean modulo of the given parameters that
+ * is `( ( n % m ) + m ) % m`.
+ *
+ * @static
+ * @method
+ * @param {number} n - The first parameter.
+ * @param {number} m - The second parameter.
+ * @return {number} The Euclidean modulo.
+ */
euclideanModulo: euclideanModulo,
+ /**
+ * Performs a linear mapping from range `` to range ``
+ * for the given value.
+ *
+ * @static
+ * @method
+ * @param {number} x - The value to be mapped.
+ * @param {number} a1 - Minimum value for range A.
+ * @param {number} a2 - Maximum value for range A.
+ * @param {number} b1 - Minimum value for range B.
+ * @param {number} b2 - Maximum value for range B.
+ * @return {number} The mapped value.
+ */
mapLinear: mapLinear,
+ /**
+ * Returns the percentage in the closed interval `[0, 1]` of the given value
+ * between the start and end point.
+ *
+ * @static
+ * @method
+ * @param {number} x - The start point
+ * @param {number} y - The end point.
+ * @param {number} value - A value between start and end.
+ * @return {number} The interpolation factor.
+ */
inverseLerp: inverseLerp,
+ /**
+ * Returns a value linearly interpolated from two known points based on the given interval -
+ * `t = 0` will return `x` and `t = 1` will return `y`.
+ *
+ * @static
+ * @method
+ * @param {number} x - The start point
+ * @param {number} y - The end point.
+ * @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
+ * @return {number} The interpolated value.
+ */
lerp: lerp,
+ /**
+ * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta
+ * time to maintain frame rate independent movement. For details, see
+ * [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}.
+ *
+ * @static
+ * @method
+ * @param {number} x - The current point.
+ * @param {number} y - The target point.
+ * @param {number} lambda - A higher lambda value will make the movement more sudden,
+ * and a lower value will make the movement more gradual.
+ * @param {number} dt - Delta time in seconds.
+ * @return {number} The interpolated value.
+ */
damp: damp,
+ /**
+ * Returns a value that alternates between `0` and the given `length` parameter.
+ *
+ * @static
+ * @method
+ * @param {number} x - The value to pingpong.
+ * @param {number} [length=1] - The positive value the function will pingpong to.
+ * @return {number} The alternated value.
+ */
pingpong: pingpong,
+ /**
+ * Returns a value in the range `[0,1]` that represents the percentage that `x` has
+ * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to
+ * the `min` and `max`.
+ *
+ * See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details.
+ *
+ * @static
+ * @method
+ * @param {number} x - The value to evaluate based on its position between min and max.
+ * @param {number} min - The min value. Any x value below min will be `0`.
+ * @param {number} max - The max value. Any x value above max will be `1`.
+ * @return {number} The alternated value.
+ */
smoothstep: smoothstep,
+ /**
+ * A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations}
+ * that has zero 1st and 2nd order derivatives at x=0 and x=1.
+ *
+ * @static
+ * @method
+ * @param {number} x - The value to evaluate based on its position between min and max.
+ * @param {number} min - The min value. Any x value below min will be `0`.
+ * @param {number} max - The max value. Any x value above max will be `1`.
+ * @return {number} The alternated value.
+ */
smootherstep: smootherstep,
+ /**
+ * Returns a random integer from `` interval.
+ *
+ * @static
+ * @method
+ * @param {number} low - The lower value boundary.
+ * @param {number} high - The upper value boundary
+ * @return {number} A random integer.
+ */
randInt: randInt,
+ /**
+ * Returns a random float from `` interval.
+ *
+ * @static
+ * @method
+ * @param {number} low - The lower value boundary.
+ * @param {number} high - The upper value boundary
+ * @return {number} A random float.
+ */
randFloat: randFloat,
+ /**
+ * Returns a random integer from `<-range/2, range/2>` interval.
+ *
+ * @static
+ * @method
+ * @param {number} range - Defines the value range.
+ * @return {number} A random float.
+ */
randFloatSpread: randFloatSpread,
+ /**
+ * Returns a deterministic pseudo-random float in the interval `[0, 1]`.
+ *
+ * @static
+ * @method
+ * @param {number} [s] - The integer seed.
+ * @return {number} A random float.
+ */
seededRandom: seededRandom,
+ /**
+ * Converts degrees to radians.
+ *
+ * @static
+ * @method
+ * @param {number} degrees - A value in degrees.
+ * @return {number} The converted value in radians.
+ */
degToRad: degToRad,
+ /**
+ * Converts radians to degrees.
+ *
+ * @static
+ * @method
+ * @param {number} radians - A value in radians.
+ * @return {number} The converted value in degrees.
+ */
radToDeg: radToDeg,
+ /**
+ * Returns `true` if the given number is a power of two.
+ *
+ * @static
+ * @method
+ * @param {number} value - The value to check.
+ * @return {boolean} Whether the given number is a power of two or not.
+ */
isPowerOfTwo: isPowerOfTwo,
+ /**
+ * Returns the smallest power of two that is greater than or equal to the given number.
+ *
+ * @static
+ * @method
+ * @param {number} value - The value to find a POT for.
+ * @return {number} The smallest power of two that is greater than or equal to the given number.
+ */
ceilPowerOfTwo: ceilPowerOfTwo,
+ /**
+ * Returns the largest power of two that is less than or equal to the given number.
+ *
+ * @static
+ * @method
+ * @param {number} value - The value to find a POT for.
+ * @return {number} The largest power of two that is less than or equal to the given number.
+ */
floorPowerOfTwo: floorPowerOfTwo,
+ /**
+ * Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles}
+ * defined by the given angles and order.
+ *
+ * Rotations are applied to the axes in the order specified by order:
+ * rotation by angle `a` is applied first, then by angle `b`, then by angle `c`.
+ *
+ * @static
+ * @method
+ * @param {Quaternion} q - The quaternion to set.
+ * @param {number} a - The rotation applied to the first axis, in radians.
+ * @param {number} b - The rotation applied to the second axis, in radians.
+ * @param {number} c - The rotation applied to the third axis, in radians.
+ * @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order.
+ */
setQuaternionFromProperEuler: setQuaternionFromProperEuler,
+ /**
+ * Normalizes the given value according to the given typed array.
+ *
+ * @static
+ * @method
+ * @param {number} value - The float value in the range `[0,1]` to normalize.
+ * @param {TypedArray} array - The typed array that defines the data type of the value.
+ * @return {number} The normalize value.
+ */
normalize: normalize,
+ /**
+ * Denormalizes the given value according to the given typed array.
+ *
+ * @static
+ * @method
+ * @param {number} value - The value to denormalize.
+ * @param {TypedArray} array - The typed array that defines the data type of the value.
+ * @return {number} The denormalize (float) value in the range `[0,1]`.
+ */
denormalize: denormalize
};
+/**
+ * Class representing a 2D vector. A 2D vector is an ordered pair of numbers
+ * (labeled x and y), which can be used to represent a number of things, such as:
+ *
+ * - A point in 2D space (i.e. a position on a plane).
+ * - A direction and length across a plane. In three.js the length will
+ * always be the Euclidean distance(straight-line distance) from `(0, 0)` to `(x, y)`
+ * and the direction is also measured from `(0, 0)` towards `(x, y)`.
+ * - Any arbitrary ordered pair of numbers.
+ *
+ * There are other things a 2D vector can be used to represent, such as
+ * momentum vectors, complex numbers and so on, however these are the most
+ * common uses in three.js.
+ *
+ * Iterating through a vector instance will yield its components `(x, y)` in
+ * the corresponding order.
+ * ```js
+ * const a = new THREE.Vector2( 0, 1 );
+ *
+ * //no arguments; will be initialised to (0, 0)
+ * const b = new THREE.Vector2( );
+ *
+ * const d = a.distanceTo( b );
+ * ```
+ */
class Vector2 {
+ /**
+ * Constructs a new 2D vector.
+ *
+ * @param {number} [x=0] - The x value of this vector.
+ * @param {number} [y=0] - The y value of this vector.
+ */
constructor( x = 0, y = 0 ) {
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
Vector2.prototype.isVector2 = true;
+ /**
+ * The x value of this vector.
+ *
+ * @type {number}
+ */
this.x = x;
+
+ /**
+ * The y value of this vector.
+ *
+ * @type {number}
+ */
this.y = y;
}
+ /**
+ * Alias for {@link Vector2#x}.
+ *
+ * @type {number}
+ */
get width() {
return this.x;
@@ -648,6 +2601,11 @@ class Vector2 {
}
+ /**
+ * Alias for {@link Vector2#y}.
+ *
+ * @type {number}
+ */
get height() {
return this.y;
@@ -660,6 +2618,13 @@ class Vector2 {
}
+ /**
+ * Sets the vector components.
+ *
+ * @param {number} x - The value of the x component.
+ * @param {number} y - The value of the y component.
+ * @return {Vector2} A reference to this vector.
+ */
set( x, y ) {
this.x = x;
@@ -669,6 +2634,12 @@ class Vector2 {
}
+ /**
+ * Sets the vector components to the same value.
+ *
+ * @param {number} scalar - The value to set for all vector components.
+ * @return {Vector2} A reference to this vector.
+ */
setScalar( scalar ) {
this.x = scalar;
@@ -678,6 +2649,12 @@ class Vector2 {
}
+ /**
+ * Sets the vector's x component to the given value
+ *
+ * @param {number} x - The value to set.
+ * @return {Vector2} A reference to this vector.
+ */
setX( x ) {
this.x = x;
@@ -686,6 +2663,12 @@ class Vector2 {
}
+ /**
+ * Sets the vector's y component to the given value
+ *
+ * @param {number} y - The value to set.
+ * @return {Vector2} A reference to this vector.
+ */
setY( y ) {
this.y = y;
@@ -694,6 +2677,13 @@ class Vector2 {
}
+ /**
+ * Allows to set a vector component with an index.
+ *
+ * @param {number} index - The component index. `0` equals to x, `1` equals to y.
+ * @param {number} value - The value to set.
+ * @return {Vector2} A reference to this vector.
+ */
setComponent( index, value ) {
switch ( index ) {
@@ -708,6 +2698,12 @@ class Vector2 {
}
+ /**
+ * Returns the value of the vector component which matches the given index.
+ *
+ * @param {number} index - The component index. `0` equals to x, `1` equals to y.
+ * @return {number} A vector component value.
+ */
getComponent( index ) {
switch ( index ) {
@@ -720,12 +2716,23 @@ class Vector2 {
}
+ /**
+ * Returns a new vector with copied values from this instance.
+ *
+ * @return {Vector2} A clone of this instance.
+ */
clone() {
return new this.constructor( this.x, this.y );
}
+ /**
+ * Copies the values of the given vector to this instance.
+ *
+ * @param {Vector2} v - The vector to copy.
+ * @return {Vector2} A reference to this vector.
+ */
copy( v ) {
this.x = v.x;
@@ -735,6 +2742,12 @@ class Vector2 {
}
+ /**
+ * Adds the given vector to this instance.
+ *
+ * @param {Vector2} v - The vector to add.
+ * @return {Vector2} A reference to this vector.
+ */
add( v ) {
this.x += v.x;
@@ -744,6 +2757,12 @@ class Vector2 {
}
+ /**
+ * Adds the given scalar value to all components of this instance.
+ *
+ * @param {number} s - The scalar to add.
+ * @return {Vector2} A reference to this vector.
+ */
addScalar( s ) {
this.x += s;
@@ -753,6 +2772,13 @@ class Vector2 {
}
+ /**
+ * Adds the given vectors and stores the result in this instance.
+ *
+ * @param {Vector2} a - The first vector.
+ * @param {Vector2} b - The second vector.
+ * @return {Vector2} A reference to this vector.
+ */
addVectors( a, b ) {
this.x = a.x + b.x;
@@ -762,6 +2788,13 @@ class Vector2 {
}
+ /**
+ * Adds the given vector scaled by the given factor to this instance.
+ *
+ * @param {Vector2} v - The vector.
+ * @param {number} s - The factor that scales `v`.
+ * @return {Vector2} A reference to this vector.
+ */
addScaledVector( v, s ) {
this.x += v.x * s;
@@ -771,6 +2804,12 @@ class Vector2 {
}
+ /**
+ * Subtracts the given vector from this instance.
+ *
+ * @param {Vector2} v - The vector to subtract.
+ * @return {Vector2} A reference to this vector.
+ */
sub( v ) {
this.x -= v.x;
@@ -780,6 +2819,12 @@ class Vector2 {
}
+ /**
+ * Subtracts the given scalar value from all components of this instance.
+ *
+ * @param {number} s - The scalar to subtract.
+ * @return {Vector2} A reference to this vector.
+ */
subScalar( s ) {
this.x -= s;
@@ -789,6 +2834,13 @@ class Vector2 {
}
+ /**
+ * Subtracts the given vectors and stores the result in this instance.
+ *
+ * @param {Vector2} a - The first vector.
+ * @param {Vector2} b - The second vector.
+ * @return {Vector2} A reference to this vector.
+ */
subVectors( a, b ) {
this.x = a.x - b.x;
@@ -798,6 +2850,12 @@ class Vector2 {
}
+ /**
+ * Multiplies the given vector with this instance.
+ *
+ * @param {Vector2} v - The vector to multiply.
+ * @return {Vector2} A reference to this vector.
+ */
multiply( v ) {
this.x *= v.x;
@@ -807,6 +2865,12 @@ class Vector2 {
}
+ /**
+ * Multiplies the given scalar value with all components of this instance.
+ *
+ * @param {number} scalar - The scalar to multiply.
+ * @return {Vector2} A reference to this vector.
+ */
multiplyScalar( scalar ) {
this.x *= scalar;
@@ -816,6 +2880,12 @@ class Vector2 {
}
+ /**
+ * Divides this instance by the given vector.
+ *
+ * @param {Vector2} v - The vector to divide.
+ * @return {Vector2} A reference to this vector.
+ */
divide( v ) {
this.x /= v.x;
@@ -825,12 +2895,25 @@ class Vector2 {
}
+ /**
+ * Divides this vector by the given scalar.
+ *
+ * @param {number} scalar - The scalar to divide.
+ * @return {Vector2} A reference to this vector.
+ */
divideScalar( scalar ) {
return this.multiplyScalar( 1 / scalar );
}
+ /**
+ * Multiplies this vector (with an implicit 1 as the 3rd component) by
+ * the given 3x3 matrix.
+ *
+ * @param {Matrix3} m - The matrix to apply.
+ * @return {Vector2} A reference to this vector.
+ */
applyMatrix3( m ) {
const x = this.x, y = this.y;
@@ -843,6 +2926,13 @@ class Vector2 {
}
+ /**
+ * If this vector's x or y value is greater than the given vector's x or y
+ * value, replace that value with the corresponding min value.
+ *
+ * @param {Vector2} v - The vector.
+ * @return {Vector2} A reference to this vector.
+ */
min( v ) {
this.x = Math.min( this.x, v.x );
@@ -852,6 +2942,13 @@ class Vector2 {
}
+ /**
+ * If this vector's x or y value is less than the given vector's x or y
+ * value, replace that value with the corresponding max value.
+ *
+ * @param {Vector2} v - The vector.
+ * @return {Vector2} A reference to this vector.
+ */
max( v ) {
this.x = Math.max( this.x, v.x );
@@ -861,34 +2958,69 @@ class Vector2 {
}
+ /**
+ * If this vector's x or y value is greater than the max vector's x or y
+ * value, it is replaced by the corresponding value.
+ * If this vector's x or y value is less than the min vector's x or y value,
+ * it is replaced by the corresponding value.
+ *
+ * @param {Vector2} min - The minimum x and y values.
+ * @param {Vector2} max - The maximum x and y values in the desired range.
+ * @return {Vector2} A reference to this vector.
+ */
clamp( min, max ) {
// assumes min < max, componentwise
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.x = clamp( this.x, min.x, max.x );
+ this.y = clamp( this.y, min.y, max.y );
return this;
}
+ /**
+ * If this vector's x or y values are greater than the max value, they are
+ * replaced by the max value.
+ * If this vector's x or y values are less than the min value, they are
+ * replaced by the min value.
+ *
+ * @param {number} minVal - The minimum value the components will be clamped to.
+ * @param {number} maxVal - The maximum value the components will be clamped to.
+ * @return {Vector2} A reference to this vector.
+ */
clampScalar( minVal, maxVal ) {
- this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
- this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
+ this.x = clamp( this.x, minVal, maxVal );
+ this.y = clamp( this.y, minVal, maxVal );
return this;
}
+ /**
+ * If this vector's length is greater than the max value, it is replaced by
+ * the max value.
+ * If this vector's length is less than the min value, it is replaced by the
+ * min value.
+ *
+ * @param {number} min - The minimum value the vector length will be clamped to.
+ * @param {number} max - The maximum value the vector length will be clamped to.
+ * @return {Vector2} A reference to this vector.
+ */
clampLength( min, max ) {
const length = this.length();
- return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+ return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) );
}
+ /**
+ * The components of this vector are rounded down to the nearest integer value.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
floor() {
this.x = Math.floor( this.x );
@@ -898,6 +3030,11 @@ class Vector2 {
}
+ /**
+ * The components of this vector are rounded up to the nearest integer value.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
ceil() {
this.x = Math.ceil( this.x );
@@ -907,6 +3044,11 @@ class Vector2 {
}
+ /**
+ * The components of this vector are rounded to the nearest integer value
+ *
+ * @return {Vector2} A reference to this vector.
+ */
round() {
this.x = Math.round( this.x );
@@ -916,15 +3058,26 @@ class Vector2 {
}
+ /**
+ * The components of this vector are rounded towards zero (up if negative,
+ * down if positive) to an integer value.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
roundToZero() {
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.x = Math.trunc( this.x );
+ this.y = Math.trunc( this.y );
return this;
}
+ /**
+ * Inverts this vector - i.e. sets x = -x and y = -y.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
negate() {
this.x = - this.x;
@@ -934,52 +3087,96 @@ class Vector2 {
}
+ /**
+ * Calculates the dot product of the given vector with this instance.
+ *
+ * @param {Vector2} v - The vector to compute the dot product with.
+ * @return {number} The result of the dot product.
+ */
dot( v ) {
return this.x * v.x + this.y * v.y;
}
+ /**
+ * Calculates the cross product of the given vector with this instance.
+ *
+ * @param {Vector2} v - The vector to compute the cross product with.
+ * @return {number} The result of the cross product.
+ */
cross( v ) {
return this.x * v.y - this.y * v.x;
}
+ /**
+ * Computes the square of the Euclidean length (straight-line length) from
+ * (0, 0) to (x, y). If you are comparing the lengths of vectors, you should
+ * compare the length squared instead as it is slightly more efficient to calculate.
+ *
+ * @return {number} The square length of this vector.
+ */
lengthSq() {
return this.x * this.x + this.y * this.y;
}
+ /**
+ * Computes the Euclidean length (straight-line length) from (0, 0) to (x, y).
+ *
+ * @return {number} The length of this vector.
+ */
length() {
return Math.sqrt( this.x * this.x + this.y * this.y );
}
+ /**
+ * Computes the Manhattan length of this vector.
+ *
+ * @return {number} The length of this vector.
+ */
manhattanLength() {
return Math.abs( this.x ) + Math.abs( this.y );
}
+ /**
+ * Converts this vector to a unit vector - that is, sets it equal to a vector
+ * with the same direction as this one, but with a vector length of `1`.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
normalize() {
return this.divideScalar( this.length() || 1 );
}
+ /**
+ * Computes the angle in radians of this vector with respect to the positive x-axis.
+ *
+ * @return {number} The angle in radians.
+ */
angle() {
- // computes the angle in radians with respect to the positive x-axis
-
const angle = Math.atan2( - this.y, - this.x ) + Math.PI;
return angle;
}
+ /**
+ * Returns the angle between the given vector and this instance in radians.
+ *
+ * @param {Vector2} v - The vector to compute the angle with.
+ * @return {number} The angle in radians.
+ */
angleTo( v ) {
const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
@@ -990,16 +3187,30 @@ class Vector2 {
// clamp, to handle numerical problems
- return Math.acos( clamp( theta, - 1, 1 ) );
+ return Math.acos( clamp( theta, -1, 1 ) );
}
+ /**
+ * Computes the distance from the given vector to this instance.
+ *
+ * @param {Vector2} v - The vector to compute the distance to.
+ * @return {number} The distance.
+ */
distanceTo( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
}
+ /**
+ * Computes the squared distance from the given vector to this instance.
+ * If you are just comparing the distance with another distance, you should compare
+ * the distance squared instead as it is slightly more efficient to calculate.
+ *
+ * @param {Vector2} v - The vector to compute the squared distance to.
+ * @return {number} The squared distance.
+ */
distanceToSquared( v ) {
const dx = this.x - v.x, dy = this.y - v.y;
@@ -1007,18 +3218,40 @@ class Vector2 {
}
+ /**
+ * Computes the Manhattan distance from the given vector to this instance.
+ *
+ * @param {Vector2} v - The vector to compute the Manhattan distance to.
+ * @return {number} The Manhattan distance.
+ */
manhattanDistanceTo( v ) {
return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
}
+ /**
+ * Sets this vector to a vector with the same direction as this one, but
+ * with the specified length.
+ *
+ * @param {number} length - The new length of this vector.
+ * @return {Vector2} A reference to this vector.
+ */
setLength( length ) {
return this.normalize().multiplyScalar( length );
}
+ /**
+ * Linearly interpolates between the given vector and this instance, where
+ * alpha is the percent distance along the line - alpha = 0 will be this
+ * vector, and alpha = 1 will be the given one.
+ *
+ * @param {Vector2} v - The vector to interpolate towards.
+ * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
+ * @return {Vector2} A reference to this vector.
+ */
lerp( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
@@ -1028,6 +3261,16 @@ class Vector2 {
}
+ /**
+ * Linearly interpolates between the given vectors, where alpha is the percent
+ * distance along the line - alpha = 0 will be first vector, and alpha = 1 will
+ * be the second one. The result is stored in this instance.
+ *
+ * @param {Vector2} v1 - The first vector.
+ * @param {Vector2} v2 - The second vector.
+ * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
+ * @return {Vector2} A reference to this vector.
+ */
lerpVectors( v1, v2, alpha ) {
this.x = v1.x + ( v2.x - v1.x ) * alpha;
@@ -1037,12 +3280,26 @@ class Vector2 {
}
+ /**
+ * Returns `true` if this vector is equal with the given one.
+ *
+ * @param {Vector2} v - The vector to test for equality.
+ * @return {boolean} Whether this vector is equal with the given one.
+ */
equals( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) );
}
+ /**
+ * Sets this vector's x value to be `array[ offset ]` and y
+ * value to be `array[ offset + 1 ]`.
+ *
+ * @param {Array} array - An array holding the vector component values.
+ * @param {number} [offset=0] - The offset into the array.
+ * @return {Vector2} A reference to this vector.
+ */
fromArray( array, offset = 0 ) {
this.x = array[ offset ];
@@ -1052,6 +3309,14 @@ class Vector2 {
}
+ /**
+ * Writes the components of this vector to the given array. If no array is provided,
+ * the method returns a new instance.
+ *
+ * @param {Array} [array=[]] - The target array holding the vector components.
+ * @param {number} [offset=0] - Index of the first element in the array.
+ * @return {Array} The vector components.
+ */
toArray( array = [], offset = 0 ) {
array[ offset ] = this.x;
@@ -1061,6 +3326,13 @@ class Vector2 {
}
+ /**
+ * Sets the components of this vector from the given buffer attribute.
+ *
+ * @param {BufferAttribute} attribute - The buffer attribute holding vector data.
+ * @param {number} index - The index into the attribute.
+ * @return {Vector2} A reference to this vector.
+ */
fromBufferAttribute( attribute, index ) {
this.x = attribute.getX( index );
@@ -1070,6 +3342,13 @@ class Vector2 {
}
+ /**
+ * Rotates this vector around the given center by the given angle.
+ *
+ * @param {Vector2} center - The point around which to rotate.
+ * @param {number} angle - The angle to rotate, in radians.
+ * @return {Vector2} A reference to this vector.
+ */
rotateAround( center, angle ) {
const c = Math.cos( angle ), s = Math.sin( angle );
@@ -1084,6 +3363,12 @@ class Vector2 {
}
+ /**
+ * Sets each component of this vector to a pseudo-random value between `0` and
+ * `1`, excluding `1`.
+ *
+ * @return {Vector2} A reference to this vector.
+ */
random() {
this.x = Math.random();
@@ -1102,2767 +3387,4420 @@ class Vector2 {
}
-class Matrix3 {
+/**
+ * Class for representing a Quaternion. Quaternions are used in three.js to represent rotations.
+ *
+ * Iterating through a vector instance will yield its components `(x, y, z, w)` in
+ * the corresponding order.
+ *
+ * Note that three.js expects Quaternions to be normalized.
+ * ```js
+ * const quaternion = new THREE.Quaternion();
+ * quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 );
+ *
+ * const vector = new THREE.Vector3( 1, 0, 0 );
+ * vector.applyQuaternion( quaternion );
+ * ```
+ */
+class Quaternion {
- constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+ /**
+ * Constructs a new quaternion.
+ *
+ * @param {number} [x=0] - The x value of this quaternion.
+ * @param {number} [y=0] - The y value of this quaternion.
+ * @param {number} [z=0] - The z value of this quaternion.
+ * @param {number} [w=1] - The w value of this quaternion.
+ */
+ constructor( x = 0, y = 0, z = 0, w = 1 ) {
- Matrix3.prototype.isMatrix3 = true;
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
+ this.isQuaternion = true;
- this.elements = [
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
+ }
- ];
+ /**
+ * Interpolates between two quaternions via SLERP. This implementation assumes the
+ * quaternion data are managed in flat arrays.
+ *
+ * @param {Array} dst - The destination array.
+ * @param {number} dstOffset - An offset into the destination array.
+ * @param {Array} src0 - The source array of the first quaternion.
+ * @param {number} srcOffset0 - An offset into the first source array.
+ * @param {Array} src1 - The source array of the second quaternion.
+ * @param {number} srcOffset1 - An offset into the second source array.
+ * @param {number} t - The interpolation factor in the range `[0,1]`.
+ * @see {@link Quaternion#slerp}
+ */
+ static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
- if ( n11 !== undefined ) {
+ // fuzz-free, array-based Quaternion SLERP operation
- this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 );
+ let x0 = src0[ srcOffset0 + 0 ],
+ y0 = src0[ srcOffset0 + 1 ],
+ z0 = src0[ srcOffset0 + 2 ],
+ w0 = src0[ srcOffset0 + 3 ];
- }
+ const x1 = src1[ srcOffset1 + 0 ],
+ y1 = src1[ srcOffset1 + 1 ],
+ z1 = src1[ srcOffset1 + 2 ],
+ w1 = src1[ srcOffset1 + 3 ];
- }
+ if ( t === 0 ) {
- set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+ dst[ dstOffset + 0 ] = x0;
+ dst[ dstOffset + 1 ] = y0;
+ dst[ dstOffset + 2 ] = z0;
+ dst[ dstOffset + 3 ] = w0;
+ return;
- const te = this.elements;
+ }
- te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
- te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
- te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
+ if ( t === 1 ) {
- return this;
+ dst[ dstOffset + 0 ] = x1;
+ dst[ dstOffset + 1 ] = y1;
+ dst[ dstOffset + 2 ] = z1;
+ dst[ dstOffset + 3 ] = w1;
+ return;
- }
+ }
- identity() {
+ if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
- this.set(
+ let s = 1 - t;
+ const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
+ dir = ( cos >= 0 ? 1 : -1 ),
+ sqrSin = 1 - cos * cos;
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
+ // Skip the Slerp for tiny steps to avoid numeric problems:
+ if ( sqrSin > Number.EPSILON ) {
- );
+ const sin = Math.sqrt( sqrSin ),
+ len = Math.atan2( sin, cos * dir );
- return this;
+ s = Math.sin( s * len ) / sin;
+ t = Math.sin( t * len ) / sin;
- }
+ }
- copy( m ) {
+ const tDir = t * dir;
- const te = this.elements;
- const me = m.elements;
+ x0 = x0 * s + x1 * tDir;
+ y0 = y0 * s + y1 * tDir;
+ z0 = z0 * s + z1 * tDir;
+ w0 = w0 * s + w1 * tDir;
- te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
- te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
- te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
+ // Normalize in case we just did a lerp:
+ if ( s === 1 - t ) {
- return this;
+ const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
- }
+ x0 *= f;
+ y0 *= f;
+ z0 *= f;
+ w0 *= f;
- extractBasis( xAxis, yAxis, zAxis ) {
+ }
- xAxis.setFromMatrix3Column( this, 0 );
- yAxis.setFromMatrix3Column( this, 1 );
- zAxis.setFromMatrix3Column( this, 2 );
+ }
- return this;
+ dst[ dstOffset ] = x0;
+ dst[ dstOffset + 1 ] = y0;
+ dst[ dstOffset + 2 ] = z0;
+ dst[ dstOffset + 3 ] = w0;
}
- setFromMatrix4( m ) {
-
- const me = m.elements;
+ /**
+ * Multiplies two quaternions. This implementation assumes the quaternion data are managed
+ * in flat arrays.
+ *
+ * @param {Array} dst - The destination array.
+ * @param {number} dstOffset - An offset into the destination array.
+ * @param {Array} src0 - The source array of the first quaternion.
+ * @param {number} srcOffset0 - An offset into the first source array.
+ * @param {Array} src1 - The source array of the second quaternion.
+ * @param {number} srcOffset1 - An offset into the second source array.
+ * @return {Array} The destination array.
+ * @see {@link Quaternion#multiplyQuaternions}.
+ */
+ static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
- this.set(
+ const x0 = src0[ srcOffset0 ];
+ const y0 = src0[ srcOffset0 + 1 ];
+ const z0 = src0[ srcOffset0 + 2 ];
+ const w0 = src0[ srcOffset0 + 3 ];
- me[ 0 ], me[ 4 ], me[ 8 ],
- me[ 1 ], me[ 5 ], me[ 9 ],
- me[ 2 ], me[ 6 ], me[ 10 ]
+ const x1 = src1[ srcOffset1 ];
+ const y1 = src1[ srcOffset1 + 1 ];
+ const z1 = src1[ srcOffset1 + 2 ];
+ const w1 = src1[ srcOffset1 + 3 ];
- );
+ dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
+ dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
+ dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
+ dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
- return this;
+ return dst;
}
- multiply( m ) {
+ /**
+ * The x value of this quaternion.
+ *
+ * @type {number}
+ * @default 0
+ */
+ get x() {
- return this.multiplyMatrices( this, m );
+ return this._x;
}
- premultiply( m ) {
+ set x( value ) {
- return this.multiplyMatrices( m, this );
+ this._x = value;
+ this._onChangeCallback();
}
- multiplyMatrices( a, b ) {
+ /**
+ * The y value of this quaternion.
+ *
+ * @type {number}
+ * @default 0
+ */
+ get y() {
- const ae = a.elements;
- const be = b.elements;
- const te = this.elements;
+ return this._y;
- const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
- const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
- const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
+ }
- const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
- const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
- const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
+ set y( value ) {
- te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
- te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
- te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
+ this._y = value;
+ this._onChangeCallback();
- te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
- te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
- te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
+ }
- te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
- te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
- te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
+ /**
+ * The z value of this quaternion.
+ *
+ * @type {number}
+ * @default 0
+ */
+ get z() {
- return this;
+ return this._z;
}
- multiplyScalar( s ) {
-
- const te = this.elements;
-
- te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
- te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
- te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
+ set z( value ) {
- return this;
+ this._z = value;
+ this._onChangeCallback();
}
- determinant() {
-
- const te = this.elements;
-
- const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
- d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
- g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+ /**
+ * The w value of this quaternion.
+ *
+ * @type {number}
+ * @default 1
+ */
+ get w() {
- return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
+ return this._w;
}
- invert() {
-
- const te = this.elements,
+ set w( value ) {
- n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],
- n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],
- n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],
+ this._w = value;
+ this._onChangeCallback();
- t11 = n33 * n22 - n32 * n23,
- t12 = n32 * n13 - n33 * n12,
- t13 = n23 * n12 - n22 * n13,
+ }
- det = n11 * t11 + n21 * t12 + n31 * t13;
+ /**
+ * Sets the quaternion components.
+ *
+ * @param {number} x - The x value of this quaternion.
+ * @param {number} y - The y value of this quaternion.
+ * @param {number} z - The z value of this quaternion.
+ * @param {number} w - The w value of this quaternion.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ set( x, y, z, w ) {
- if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
- const detInv = 1 / det;
+ this._onChangeCallback();
- te[ 0 ] = t11 * detInv;
- te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
- te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
+ return this;
- te[ 3 ] = t12 * detInv;
- te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
- te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
+ }
- te[ 6 ] = t13 * detInv;
- te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
- te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
+ /**
+ * Returns a new quaternion with copied values from this instance.
+ *
+ * @return {Quaternion} A clone of this instance.
+ */
+ clone() {
- return this;
+ return new this.constructor( this._x, this._y, this._z, this._w );
}
- transpose() {
+ /**
+ * Copies the values of the given quaternion to this instance.
+ *
+ * @param {Quaternion} quaternion - The quaternion to copy.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ copy( quaternion ) {
- let tmp;
- const m = this.elements;
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
- tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
- tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
- tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
+ this._onChangeCallback();
return this;
}
- getNormalMatrix( matrix4 ) {
+ /**
+ * Sets this quaternion from the rotation specified by the given
+ * Euler angles.
+ *
+ * @param {Euler} euler - The Euler angles.
+ * @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ setFromEuler( euler, update = true ) {
- return this.setFromMatrix4( matrix4 ).invert().transpose();
+ const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
- }
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
- transposeIntoArray( r ) {
+ const cos = Math.cos;
+ const sin = Math.sin;
- const m = this.elements;
+ const c1 = cos( x / 2 );
+ const c2 = cos( y / 2 );
+ const c3 = cos( z / 2 );
- r[ 0 ] = m[ 0 ];
- r[ 1 ] = m[ 3 ];
- r[ 2 ] = m[ 6 ];
- r[ 3 ] = m[ 1 ];
- r[ 4 ] = m[ 4 ];
- r[ 5 ] = m[ 7 ];
- r[ 6 ] = m[ 2 ];
- r[ 7 ] = m[ 5 ];
- r[ 8 ] = m[ 8 ];
+ const s1 = sin( x / 2 );
+ const s2 = sin( y / 2 );
+ const s3 = sin( z / 2 );
- return this;
+ switch ( order ) {
- }
+ case 'XYZ':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
- setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
+ case 'YXZ':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
- const c = Math.cos( rotation );
- const s = Math.sin( rotation );
+ case 'ZXY':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
- this.set(
- sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
- - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
- 0, 0, 1
- );
+ case 'ZYX':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
- return this;
+ case 'YZX':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
- }
+ case 'XZY':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
- //
+ default:
+ console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
- scale( sx, sy ) {
+ }
- this.premultiply( _m3.makeScale( sx, sy ) );
+ if ( update === true ) this._onChangeCallback();
return this;
}
- rotate( theta ) {
-
- this.premultiply( _m3.makeRotation( - theta ) );
+ /**
+ * Sets this quaternion from the given axis and angle.
+ *
+ * @param {Vector3} axis - The normalized axis.
+ * @param {number} angle - The angle in radians.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ setFromAxisAngle( axis, angle ) {
- return this;
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
- }
+ const halfAngle = angle / 2, s = Math.sin( halfAngle );
- translate( tx, ty ) {
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
- this.premultiply( _m3.makeTranslation( tx, ty ) );
+ this._onChangeCallback();
return this;
}
- // for 2D Transforms
+ /**
+ * Sets this quaternion from the given rotation matrix.
+ *
+ * @param {Matrix4} m - A 4x4 matrix of which the upper 3x3 of matrix is a pure rotation matrix (i.e. unscaled).
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ setFromRotationMatrix( m ) {
- makeTranslation( x, y ) {
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
- this.set(
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- 1, 0, x,
- 0, 1, y,
- 0, 0, 1
+ const te = m.elements,
- );
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
- return this;
+ trace = m11 + m22 + m33;
- }
+ if ( trace > 0 ) {
- makeRotation( theta ) {
+ const s = 0.5 / Math.sqrt( trace + 1.0 );
- // counterclockwise
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
- const c = Math.cos( theta );
- const s = Math.sin( theta );
+ } else if ( m11 > m22 && m11 > m33 ) {
- this.set(
+ const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
- c, - s, 0,
- s, c, 0,
- 0, 0, 1
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
- );
+ } else if ( m22 > m33 ) {
- return this;
+ const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
- }
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
- makeScale( x, y ) {
+ } else {
- this.set(
+ const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
- x, 0, 0,
- 0, y, 0,
- 0, 0, 1
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
- );
+ }
+
+ this._onChangeCallback();
return this;
}
- //
-
- equals( matrix ) {
+ /**
+ * Sets this quaternion to the rotation required to rotate the direction vector
+ * `vFrom` to the direction vector `vTo`.
+ *
+ * @param {Vector3} vFrom - The first (normalized) direction vector.
+ * @param {Vector3} vTo - The second (normalized) direction vector.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ setFromUnitVectors( vFrom, vTo ) {
- const te = this.elements;
- const me = matrix.elements;
+ // assumes direction vectors vFrom and vTo are normalized
- for ( let i = 0; i < 9; i ++ ) {
+ let r = vFrom.dot( vTo ) + 1;
- if ( te[ i ] !== me[ i ] ) return false;
+ if ( r < 1e-8 ) { // the epsilon value has been discussed in #31286
- }
+ // vFrom and vTo point in opposite directions
- return true;
+ r = 0;
- }
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
- fromArray( array, offset = 0 ) {
+ this._x = - vFrom.y;
+ this._y = vFrom.x;
+ this._z = 0;
+ this._w = r;
- for ( let i = 0; i < 9; i ++ ) {
+ } else {
- this.elements[ i ] = array[ i + offset ];
+ this._x = 0;
+ this._y = - vFrom.z;
+ this._z = vFrom.y;
+ this._w = r;
- }
+ }
- return this;
+ } else {
- }
+ // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
- toArray( array = [], offset = 0 ) {
+ this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
+ this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
+ this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
+ this._w = r;
- const te = this.elements;
+ }
- array[ offset ] = te[ 0 ];
- array[ offset + 1 ] = te[ 1 ];
- array[ offset + 2 ] = te[ 2 ];
+ return this.normalize();
- array[ offset + 3 ] = te[ 3 ];
- array[ offset + 4 ] = te[ 4 ];
- array[ offset + 5 ] = te[ 5 ];
+ }
- array[ offset + 6 ] = te[ 6 ];
- array[ offset + 7 ] = te[ 7 ];
- array[ offset + 8 ] = te[ 8 ];
+ /**
+ * Returns the angle between this quaternion and the given one in radians.
+ *
+ * @param {Quaternion} q - The quaternion to compute the angle with.
+ * @return {number} The angle in radians.
+ */
+ angleTo( q ) {
- return array;
+ return 2 * Math.acos( Math.abs( clamp( this.dot( q ), -1, 1 ) ) );
}
- clone() {
+ /**
+ * Rotates this quaternion by a given angular step to the given quaternion.
+ * The method ensures that the final quaternion will not overshoot `q`.
+ *
+ * @param {Quaternion} q - The target quaternion.
+ * @param {number} step - The angular step in radians.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ rotateTowards( q, step ) {
- return new this.constructor().fromArray( this.elements );
+ const angle = this.angleTo( q );
- }
+ if ( angle === 0 ) return this;
-}
+ const t = Math.min( 1, step / angle );
-const _m3 = /*@__PURE__*/ new Matrix3();
+ this.slerp( q, t );
-function arrayNeedsUint32( array ) {
+ return this;
- // assumes larger values usually on last
+ }
- for ( let i = array.length - 1; i >= 0; -- i ) {
+ /**
+ * Sets this quaternion to the identity quaternion; that is, to the
+ * quaternion that represents "no rotation".
+ *
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ identity() {
- if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
+ return this.set( 0, 0, 0, 1 );
}
- return false;
+ /**
+ * Inverts this quaternion via {@link Quaternion#conjugate}. The
+ * quaternion is assumed to have unit length.
+ *
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ invert() {
-}
+ return this.conjugate();
-const TYPED_ARRAYS = {
- Int8Array: Int8Array,
- Uint8Array: Uint8Array,
- Uint8ClampedArray: Uint8ClampedArray,
- Int16Array: Int16Array,
- Uint16Array: Uint16Array,
- Int32Array: Int32Array,
- Uint32Array: Uint32Array,
- Float32Array: Float32Array,
- Float64Array: Float64Array
-};
+ }
-function getTypedArray( type, buffer ) {
+ /**
+ * Returns the rotational conjugate of this quaternion. The conjugate of a
+ * quaternion represents the same rotation in the opposite direction about
+ * the rotational axis.
+ *
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ conjugate() {
- return new TYPED_ARRAYS[ type ]( buffer );
+ this._x *= -1;
+ this._y *= -1;
+ this._z *= -1;
-}
+ this._onChangeCallback();
-function createElementNS( name ) {
+ return this;
- return document.createElementNS( 'http://www.w3.org/1999/xhtml', name );
+ }
-}
+ /**
+ * Calculates the dot product of this quaternion and the given one.
+ *
+ * @param {Quaternion} v - The quaternion to compute the dot product with.
+ * @return {number} The result of the dot product.
+ */
+ dot( v ) {
-const _cache = {};
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
-function warnOnce( message ) {
+ }
- if ( message in _cache ) return;
+ /**
+ * Computes the squared Euclidean length (straight-line length) of this quaternion,
+ * considered as a 4 dimensional vector. This can be useful if you are comparing the
+ * lengths of two quaternions, as this is a slightly more efficient calculation than
+ * {@link Quaternion#length}.
+ *
+ * @return {number} The squared Euclidean length.
+ */
+ lengthSq() {
- _cache[ message ] = true;
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
- console.warn( message );
+ }
-}
+ /**
+ * Computes the Euclidean length (straight-line length) of this quaternion,
+ * considered as a 4 dimensional vector.
+ *
+ * @return {number} The Euclidean length.
+ */
+ length() {
-function SRGBToLinear( c ) {
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
- return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
+ }
-}
+ /**
+ * Normalizes this quaternion - that is, calculated the quaternion that performs
+ * the same rotation as this one, but has a length equal to `1`.
+ *
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ normalize() {
-function LinearToSRGB( c ) {
+ let l = this.length();
- return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
+ if ( l === 0 ) {
-}
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
-/**
- * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping
- * or clipping. Based on W3C specifications for sRGB and Display P3,
- * and ICC specifications for the D50 connection space. Values in/out
- * are _linear_ sRGB and _linear_ Display P3.
- *
- * Note that both sRGB and Display P3 use the sRGB transfer functions.
- *
- * Reference:
- * - http://www.russellcottrell.com/photo/matrixCalculator.htm
- */
+ } else {
-const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().fromArray( [
- 0.8224621, 0.0331941, 0.0170827,
- 0.1775380, 0.9668058, 0.0723974,
- - 0.0000001, 0.0000001, 0.9105199
-] );
+ l = 1 / l;
-const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().fromArray( [
- 1.2249401, - 0.0420569, - 0.0196376,
- - 0.2249404, 1.0420571, - 0.0786361,
- 0.0000001, 0.0000000, 1.0982735
-] );
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
-function DisplayP3ToLinearSRGB( color ) {
+ }
- // Display P3 uses the sRGB transfer functions
- return color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB );
+ this._onChangeCallback();
-}
+ return this;
-function LinearSRGBToDisplayP3( color ) {
+ }
- // Display P3 uses the sRGB transfer functions
- return color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB();
+ /**
+ * Multiplies this quaternion by the given one.
+ *
+ * @param {Quaternion} q - The quaternion.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ multiply( q ) {
-}
+ return this.multiplyQuaternions( this, q );
-// Conversions from to Linear-sRGB reference space.
-const TO_LINEAR = {
- [ LinearSRGBColorSpace ]: ( color ) => color,
- [ SRGBColorSpace ]: ( color ) => color.convertSRGBToLinear(),
- [ DisplayP3ColorSpace ]: DisplayP3ToLinearSRGB,
-};
+ }
-// Conversions to from Linear-sRGB reference space.
-const FROM_LINEAR = {
- [ LinearSRGBColorSpace ]: ( color ) => color,
- [ SRGBColorSpace ]: ( color ) => color.convertLinearToSRGB(),
- [ DisplayP3ColorSpace ]: LinearSRGBToDisplayP3,
-};
+ /**
+ * Pre-multiplies this quaternion by the given one.
+ *
+ * @param {Quaternion} q - The quaternion.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ premultiply( q ) {
-const ColorManagement = {
+ return this.multiplyQuaternions( q, this );
- enabled: true,
+ }
- get legacyMode() {
+ /**
+ * Multiplies the given quaternions and stores the result in this instance.
+ *
+ * @param {Quaternion} a - The first quaternion.
+ * @param {Quaternion} b - The second quaternion.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ multiplyQuaternions( a, b ) {
- console.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' );
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
- return ! this.enabled;
+ const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
- },
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
- set legacyMode( legacyMode ) {
+ this._onChangeCallback();
- console.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' );
+ return this;
- this.enabled = ! legacyMode;
+ }
- },
+ /**
+ * Performs a spherical linear interpolation between quaternions.
+ *
+ * @param {Quaternion} qb - The target quaternion.
+ * @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ slerp( qb, t ) {
- get workingColorSpace() {
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
- return LinearSRGBColorSpace;
+ const x = this._x, y = this._y, z = this._z, w = this._w;
- },
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
- set workingColorSpace( colorSpace ) {
+ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
- console.warn( 'THREE.ColorManagement: .workingColorSpace is readonly.' );
+ if ( cosHalfTheta < 0 ) {
- },
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
- convert: function ( color, sourceColorSpace, targetColorSpace ) {
+ cosHalfTheta = - cosHalfTheta;
- if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
+ } else {
- return color;
+ this.copy( qb );
}
- const sourceToLinear = TO_LINEAR[ sourceColorSpace ];
- const targetFromLinear = FROM_LINEAR[ targetColorSpace ];
+ if ( cosHalfTheta >= 1.0 ) {
- if ( sourceToLinear === undefined || targetFromLinear === undefined ) {
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
- throw new Error( `Unsupported color space conversion, "${ sourceColorSpace }" to "${ targetColorSpace }".` );
+ return this;
}
- return targetFromLinear( sourceToLinear( color ) );
-
- },
-
- fromWorkingColorSpace: function ( color, targetColorSpace ) {
+ const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
- return this.convert( color, this.workingColorSpace, targetColorSpace );
+ if ( sqrSinHalfTheta <= Number.EPSILON ) {
- },
+ const s = 1 - t;
+ this._w = s * w + t * this._w;
+ this._x = s * x + t * this._x;
+ this._y = s * y + t * this._y;
+ this._z = s * z + t * this._z;
- toWorkingColorSpace: function ( color, sourceColorSpace ) {
+ this.normalize(); // normalize calls _onChangeCallback()
- return this.convert( color, sourceColorSpace, this.workingColorSpace );
+ return this;
- },
+ }
-};
+ const sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
+ const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
+ const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
-let _canvas;
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
-class ImageUtils {
+ this._onChangeCallback();
- static getDataURL( image ) {
+ return this;
- if ( /^data:/i.test( image.src ) ) {
+ }
- return image.src;
+ /**
+ * Performs a spherical linear interpolation between the given quaternions
+ * and stores the result in this quaternion.
+ *
+ * @param {Quaternion} qa - The source quaternion.
+ * @param {Quaternion} qb - The target quaternion.
+ * @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ slerpQuaternions( qa, qb, t ) {
- }
+ return this.copy( qa ).slerp( qb, t );
- if ( typeof HTMLCanvasElement === 'undefined' ) {
+ }
- return image.src;
+ /**
+ * Sets this quaternion to a uniformly random, normalized quaternion.
+ *
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ random() {
- }
+ // Ken Shoemake
+ // Uniform random rotations
+ // D. Kirk, editor, Graphics Gems III, pages 124-132. Academic Press, New York, 1992.
- let canvas;
+ const theta1 = 2 * Math.PI * Math.random();
+ const theta2 = 2 * Math.PI * Math.random();
- if ( image instanceof HTMLCanvasElement ) {
+ const x0 = Math.random();
+ const r1 = Math.sqrt( 1 - x0 );
+ const r2 = Math.sqrt( x0 );
- canvas = image;
+ return this.set(
+ r1 * Math.sin( theta1 ),
+ r1 * Math.cos( theta1 ),
+ r2 * Math.sin( theta2 ),
+ r2 * Math.cos( theta2 ),
+ );
- } else {
+ }
- if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );
+ /**
+ * Returns `true` if this quaternion is equal with the given one.
+ *
+ * @param {Quaternion} quaternion - The quaternion to test for equality.
+ * @return {boolean} Whether this quaternion is equal with the given one.
+ */
+ equals( quaternion ) {
- _canvas.width = image.width;
- _canvas.height = image.height;
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
- const context = _canvas.getContext( '2d' );
+ }
- if ( image instanceof ImageData ) {
+ /**
+ * Sets this quaternion's components from the given array.
+ *
+ * @param {Array} array - An array holding the quaternion component values.
+ * @param {number} [offset=0] - The offset into the array.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ fromArray( array, offset = 0 ) {
- context.putImageData( image, 0, 0 );
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
- } else {
+ this._onChangeCallback();
- context.drawImage( image, 0, 0, image.width, image.height );
+ return this;
- }
+ }
- canvas = _canvas;
+ /**
+ * Writes the components of this quaternion to the given array. If no array is provided,
+ * the method returns a new instance.
+ *
+ * @param {Array} [array=[]] - The target array holding the quaternion components.
+ * @param {number} [offset=0] - Index of the first element in the array.
+ * @return {Array} The quaternion components.
+ */
+ toArray( array = [], offset = 0 ) {
- }
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
- if ( canvas.width > 2048 || canvas.height > 2048 ) {
+ return array;
- console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image );
+ }
- return canvas.toDataURL( 'image/jpeg', 0.6 );
+ /**
+ * Sets the components of this quaternion from the given buffer attribute.
+ *
+ * @param {BufferAttribute} attribute - The buffer attribute holding quaternion data.
+ * @param {number} index - The index into the attribute.
+ * @return {Quaternion} A reference to this quaternion.
+ */
+ fromBufferAttribute( attribute, index ) {
- } else {
+ this._x = attribute.getX( index );
+ this._y = attribute.getY( index );
+ this._z = attribute.getZ( index );
+ this._w = attribute.getW( index );
- return canvas.toDataURL( 'image/png' );
+ this._onChangeCallback();
- }
+ return this;
}
- static sRGBToLinear( image ) {
+ /**
+ * This methods defines the serialization result of this class. Returns the
+ * numerical elements of this quaternion in an array of format `[x, y, z, w]`.
+ *
+ * @return {Array} The serialized quaternion.
+ */
+ toJSON() {
- if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
- ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
- ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
+ return this.toArray();
- const canvas = createElementNS( 'canvas' );
+ }
- canvas.width = image.width;
- canvas.height = image.height;
+ _onChange( callback ) {
- const context = canvas.getContext( '2d' );
- context.drawImage( image, 0, 0, image.width, image.height );
+ this._onChangeCallback = callback;
- const imageData = context.getImageData( 0, 0, image.width, image.height );
- const data = imageData.data;
+ return this;
- for ( let i = 0; i < data.length; i ++ ) {
+ }
- data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;
+ _onChangeCallback() {}
- }
+ *[ Symbol.iterator ]() {
- context.putImageData( imageData, 0, 0 );
+ yield this._x;
+ yield this._y;
+ yield this._z;
+ yield this._w;
- return canvas;
+ }
- } else if ( image.data ) {
+}
- const data = image.data.slice( 0 );
+/**
+ * Class representing a 3D vector. A 3D vector is an ordered triplet of numbers
+ * (labeled x, y and z), which can be used to represent a number of things, such as:
+ *
+ * - A point in 3D space.
+ * - A direction and length in 3D space. In three.js the length will
+ * always be the Euclidean distance(straight-line distance) from `(0, 0, 0)` to `(x, y, z)`
+ * and the direction is also measured from `(0, 0, 0)` towards `(x, y, z)`.
+ * - Any arbitrary ordered triplet of numbers.
+ *
+ * There are other things a 3D vector can be used to represent, such as
+ * momentum vectors and so on, however these are the most
+ * common uses in three.js.
+ *
+ * Iterating through a vector instance will yield its components `(x, y, z)` in
+ * the corresponding order.
+ * ```js
+ * const a = new THREE.Vector3( 0, 1, 0 );
+ *
+ * //no arguments; will be initialised to (0, 0, 0)
+ * const b = new THREE.Vector3( );
+ *
+ * const d = a.distanceTo( b );
+ * ```
+ */
+class Vector3 {
- for ( let i = 0; i < data.length; i ++ ) {
+ /**
+ * Constructs a new 3D vector.
+ *
+ * @param {number} [x=0] - The x value of this vector.
+ * @param {number} [y=0] - The y value of this vector.
+ * @param {number} [z=0] - The z value of this vector.
+ */
+ constructor( x = 0, y = 0, z = 0 ) {
- if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
+ Vector3.prototype.isVector3 = true;
- data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );
+ /**
+ * The x value of this vector.
+ *
+ * @type {number}
+ */
+ this.x = x;
- } else {
+ /**
+ * The y value of this vector.
+ *
+ * @type {number}
+ */
+ this.y = y;
- // assuming float
+ /**
+ * The z value of this vector.
+ *
+ * @type {number}
+ */
+ this.z = z;
- data[ i ] = SRGBToLinear( data[ i ] );
+ }
- }
+ /**
+ * Sets the vector components.
+ *
+ * @param {number} x - The value of the x component.
+ * @param {number} y - The value of the y component.
+ * @param {number} z - The value of the z component.
+ * @return {Vector3} A reference to this vector.
+ */
+ set( x, y, z ) {
- }
+ if ( z === undefined ) z = this.z; // sprite.scale.set(x,y)
- return {
- data: data,
- width: image.width,
- height: image.height
- };
+ this.x = x;
+ this.y = y;
+ this.z = z;
- } else {
+ return this;
- console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );
- return image;
+ }
- }
+ /**
+ * Sets the vector components to the same value.
+ *
+ * @param {number} scalar - The value to set for all vector components.
+ * @return {Vector3} A reference to this vector.
+ */
+ setScalar( scalar ) {
- }
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
-}
+ return this;
-let sourceId = 0;
+ }
-class Source {
+ /**
+ * Sets the vector's x component to the given value
+ *
+ * @param {number} x - The value to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setX( x ) {
- constructor( data = null ) {
+ this.x = x;
- this.isSource = true;
+ return this;
- Object.defineProperty( this, 'id', { value: sourceId ++ } );
+ }
- this.uuid = generateUUID();
+ /**
+ * Sets the vector's y component to the given value
+ *
+ * @param {number} y - The value to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setY( y ) {
- this.data = data;
+ this.y = y;
- this.version = 0;
+ return this;
}
- set needsUpdate( value ) {
+ /**
+ * Sets the vector's z component to the given value
+ *
+ * @param {number} z - The value to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setZ( z ) {
- if ( value === true ) this.version ++;
+ this.z = z;
- }
+ return this;
- toJSON( meta ) {
+ }
- const isRootObject = ( meta === undefined || typeof meta === 'string' );
+ /**
+ * Allows to set a vector component with an index.
+ *
+ * @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z.
+ * @param {number} value - The value to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setComponent( index, value ) {
- if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) {
+ switch ( index ) {
- return meta.images[ this.uuid ];
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
}
- const output = {
- uuid: this.uuid,
- url: ''
- };
+ return this;
- const data = this.data;
-
- if ( data !== null ) {
-
- let url;
-
- if ( Array.isArray( data ) ) {
-
- // cube texture
-
- url = [];
-
- for ( let i = 0, l = data.length; i < l; i ++ ) {
-
- if ( data[ i ].isDataTexture ) {
+ }
- url.push( serializeImage( data[ i ].image ) );
+ /**
+ * Returns the value of the vector component which matches the given index.
+ *
+ * @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z.
+ * @return {number} A vector component value.
+ */
+ getComponent( index ) {
- } else {
+ switch ( index ) {
- url.push( serializeImage( data[ i ] ) );
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
- }
+ }
- }
+ }
- } else {
+ /**
+ * Returns a new vector with copied values from this instance.
+ *
+ * @return {Vector3} A clone of this instance.
+ */
+ clone() {
- // texture
+ return new this.constructor( this.x, this.y, this.z );
- url = serializeImage( data );
+ }
- }
+ /**
+ * Copies the values of the given vector to this instance.
+ *
+ * @param {Vector3} v - The vector to copy.
+ * @return {Vector3} A reference to this vector.
+ */
+ copy( v ) {
- output.url = url;
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
- }
+ return this;
- if ( ! isRootObject ) {
+ }
- meta.images[ this.uuid ] = output;
+ /**
+ * Adds the given vector to this instance.
+ *
+ * @param {Vector3} v - The vector to add.
+ * @return {Vector3} A reference to this vector.
+ */
+ add( v ) {
- }
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
- return output;
+ return this;
}
-}
-
-function serializeImage( image ) {
+ /**
+ * Adds the given scalar value to all components of this instance.
+ *
+ * @param {number} s - The scalar to add.
+ * @return {Vector3} A reference to this vector.
+ */
+ addScalar( s ) {
- if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
- ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
- ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
+ this.x += s;
+ this.y += s;
+ this.z += s;
- // default images
+ return this;
- return ImageUtils.getDataURL( image );
+ }
- } else {
+ /**
+ * Adds the given vectors and stores the result in this instance.
+ *
+ * @param {Vector3} a - The first vector.
+ * @param {Vector3} b - The second vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ addVectors( a, b ) {
- if ( image.data ) {
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
- // images of DataTexture
+ return this;
- return {
- data: Array.from( image.data ),
- width: image.width,
- height: image.height,
- type: image.data.constructor.name
- };
+ }
- } else {
+ /**
+ * Adds the given vector scaled by the given factor to this instance.
+ *
+ * @param {Vector3|Vector4} v - The vector.
+ * @param {number} s - The factor that scales `v`.
+ * @return {Vector3} A reference to this vector.
+ */
+ addScaledVector( v, s ) {
- console.warn( 'THREE.Texture: Unable to serialize Texture.' );
- return {};
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
- }
+ return this;
}
-}
-
-let textureId = 0;
+ /**
+ * Subtracts the given vector from this instance.
+ *
+ * @param {Vector3} v - The vector to subtract.
+ * @return {Vector3} A reference to this vector.
+ */
+ sub( v ) {
-class Texture extends EventDispatcher {
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
- constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {
+ return this;
- super();
+ }
- this.isTexture = true;
+ /**
+ * Subtracts the given scalar value from all components of this instance.
+ *
+ * @param {number} s - The scalar to subtract.
+ * @return {Vector3} A reference to this vector.
+ */
+ subScalar( s ) {
- Object.defineProperty( this, 'id', { value: textureId ++ } );
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
- this.uuid = generateUUID();
+ return this;
- this.name = '';
+ }
- this.source = new Source( image );
- this.mipmaps = [];
+ /**
+ * Subtracts the given vectors and stores the result in this instance.
+ *
+ * @param {Vector3} a - The first vector.
+ * @param {Vector3} b - The second vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ subVectors( a, b ) {
- this.mapping = mapping;
- this.channel = 0;
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
- this.wrapS = wrapS;
- this.wrapT = wrapT;
+ return this;
- this.magFilter = magFilter;
- this.minFilter = minFilter;
+ }
- this.anisotropy = anisotropy;
+ /**
+ * Multiplies the given vector with this instance.
+ *
+ * @param {Vector3} v - The vector to multiply.
+ * @return {Vector3} A reference to this vector.
+ */
+ multiply( v ) {
- this.format = format;
- this.internalFormat = null;
- this.type = type;
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
- this.offset = new Vector2( 0, 0 );
- this.repeat = new Vector2( 1, 1 );
- this.center = new Vector2( 0, 0 );
- this.rotation = 0;
+ return this;
- this.matrixAutoUpdate = true;
- this.matrix = new Matrix3();
+ }
- this.generateMipmaps = true;
- this.premultiplyAlpha = false;
- this.flipY = true;
- this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+ /**
+ * Multiplies the given scalar value with all components of this instance.
+ *
+ * @param {number} scalar - The scalar to multiply.
+ * @return {Vector3} A reference to this vector.
+ */
+ multiplyScalar( scalar ) {
- if ( typeof colorSpace === 'string' ) {
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
- this.colorSpace = colorSpace;
+ return this;
- } else { // @deprecated, r152
+ }
- warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );
- this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace;
+ /**
+ * Multiplies the given vectors and stores the result in this instance.
+ *
+ * @param {Vector3} a - The first vector.
+ * @param {Vector3} b - The second vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ multiplyVectors( a, b ) {
- }
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+ return this;
- this.userData = {};
+ }
- this.version = 0;
- this.onUpdate = null;
+ /**
+ * Applies the given Euler rotation to this vector.
+ *
+ * @param {Euler} euler - The Euler angles.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyEuler( euler ) {
- this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not
- this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures)
+ return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) );
}
- get image() {
+ /**
+ * Applies a rotation specified by an axis and an angle to this vector.
+ *
+ * @param {Vector3} axis - A normalized vector representing the rotation axis.
+ * @param {number} angle - The angle in radians.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyAxisAngle( axis, angle ) {
- return this.source.data;
+ return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) );
}
- set image( value = null ) {
-
- this.source.data = value;
+ /**
+ * Multiplies this vector with the given 3x3 matrix.
+ *
+ * @param {Matrix3} m - The 3x3 matrix.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyMatrix3( m ) {
- }
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
- updateMatrix() {
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
- this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
+ return this;
}
- clone() {
+ /**
+ * Multiplies this vector by the given normal matrix and normalizes
+ * the result.
+ *
+ * @param {Matrix3} m - The normal matrix.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyNormalMatrix( m ) {
- return new this.constructor().copy( this );
+ return this.applyMatrix3( m ).normalize();
}
- copy( source ) {
-
- this.name = source.name;
-
- this.source = source.source;
- this.mipmaps = source.mipmaps.slice( 0 );
+ /**
+ * Multiplies this vector (with an implicit 1 in the 4th dimension) by m, and
+ * divides by perspective.
+ *
+ * @param {Matrix4} m - The matrix to apply.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyMatrix4( m ) {
- this.mapping = source.mapping;
- this.channel = source.channel;
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
- this.wrapS = source.wrapS;
- this.wrapT = source.wrapT;
+ const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
- this.magFilter = source.magFilter;
- this.minFilter = source.minFilter;
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
- this.anisotropy = source.anisotropy;
+ return this;
- this.format = source.format;
- this.internalFormat = source.internalFormat;
- this.type = source.type;
+ }
- this.offset.copy( source.offset );
- this.repeat.copy( source.repeat );
- this.center.copy( source.center );
- this.rotation = source.rotation;
+ /**
+ * Applies the given Quaternion to this vector.
+ *
+ * @param {Quaternion} q - The Quaternion.
+ * @return {Vector3} A reference to this vector.
+ */
+ applyQuaternion( q ) {
- this.matrixAutoUpdate = source.matrixAutoUpdate;
- this.matrix.copy( source.matrix );
+ // quaternion q is assumed to have unit length
- this.generateMipmaps = source.generateMipmaps;
- this.premultiplyAlpha = source.premultiplyAlpha;
- this.flipY = source.flipY;
- this.unpackAlignment = source.unpackAlignment;
- this.colorSpace = source.colorSpace;
+ const vx = this.x, vy = this.y, vz = this.z;
+ const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
- this.userData = JSON.parse( JSON.stringify( source.userData ) );
+ // t = 2 * cross( q.xyz, v );
+ const tx = 2 * ( qy * vz - qz * vy );
+ const ty = 2 * ( qz * vx - qx * vz );
+ const tz = 2 * ( qx * vy - qy * vx );
- this.needsUpdate = true;
+ // v + q.w * t + cross( q.xyz, t );
+ this.x = vx + qw * tx + qy * tz - qz * ty;
+ this.y = vy + qw * ty + qz * tx - qx * tz;
+ this.z = vz + qw * tz + qx * ty - qy * tx;
return this;
}
- toJSON( meta ) {
+ /**
+ * Projects this vector from world space into the camera's normalized
+ * device coordinate (NDC) space.
+ *
+ * @param {Camera} camera - The camera.
+ * @return {Vector3} A reference to this vector.
+ */
+ project( camera ) {
- const isRootObject = ( meta === undefined || typeof meta === 'string' );
+ return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
- if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
+ }
- return meta.textures[ this.uuid ];
+ /**
+ * Unprojects this vector from the camera's normalized device coordinate (NDC)
+ * space into world space.
+ *
+ * @param {Camera} camera - The camera.
+ * @return {Vector3} A reference to this vector.
+ */
+ unproject( camera ) {
- }
+ return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );
- const output = {
+ }
- metadata: {
- version: 4.6,
- type: 'Texture',
- generator: 'Texture.toJSON'
- },
+ /**
+ * Transforms the direction of this vector by a matrix (the upper left 3 x 3
+ * subset of the given 4x4 matrix and then normalizes the result.
+ *
+ * @param {Matrix4} m - The matrix.
+ * @return {Vector3} A reference to this vector.
+ */
+ transformDirection( m ) {
- uuid: this.uuid,
- name: this.name,
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
- image: this.source.toJSON( meta ).uuid,
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
- mapping: this.mapping,
- channel: this.channel,
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
- repeat: [ this.repeat.x, this.repeat.y ],
- offset: [ this.offset.x, this.offset.y ],
- center: [ this.center.x, this.center.y ],
- rotation: this.rotation,
+ return this.normalize();
- wrap: [ this.wrapS, this.wrapT ],
+ }
- format: this.format,
- internalFormat: this.internalFormat,
- type: this.type,
- colorSpace: this.colorSpace,
+ /**
+ * Divides this instance by the given vector.
+ *
+ * @param {Vector3} v - The vector to divide.
+ * @return {Vector3} A reference to this vector.
+ */
+ divide( v ) {
- minFilter: this.minFilter,
- magFilter: this.magFilter,
- anisotropy: this.anisotropy,
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
- flipY: this.flipY,
+ return this;
- generateMipmaps: this.generateMipmaps,
- premultiplyAlpha: this.premultiplyAlpha,
- unpackAlignment: this.unpackAlignment
+ }
- };
+ /**
+ * Divides this vector by the given scalar.
+ *
+ * @param {number} scalar - The scalar to divide.
+ * @return {Vector3} A reference to this vector.
+ */
+ divideScalar( scalar ) {
- if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData;
+ return this.multiplyScalar( 1 / scalar );
- if ( ! isRootObject ) {
+ }
- meta.textures[ this.uuid ] = output;
+ /**
+ * If this vector's x, y or z value is greater than the given vector's x, y or z
+ * value, replace that value with the corresponding min value.
+ *
+ * @param {Vector3} v - The vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ min( v ) {
- }
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
- return output;
+ return this;
}
- dispose() {
+ /**
+ * If this vector's x, y or z value is less than the given vector's x, y or z
+ * value, replace that value with the corresponding max value.
+ *
+ * @param {Vector3} v - The vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ max( v ) {
- this.dispatchEvent( { type: 'dispose' } );
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
}
- transformUv( uv ) {
+ /**
+ * If this vector's x, y or z value is greater than the max vector's x, y or z
+ * value, it is replaced by the corresponding value.
+ * If this vector's x, y or z value is less than the min vector's x, y or z value,
+ * it is replaced by the corresponding value.
+ *
+ * @param {Vector3} min - The minimum x, y and z values.
+ * @param {Vector3} max - The maximum x, y and z values in the desired range.
+ * @return {Vector3} A reference to this vector.
+ */
+ clamp( min, max ) {
- if ( this.mapping !== UVMapping ) return uv;
+ // assumes min < max, componentwise
- uv.applyMatrix3( this.matrix );
+ this.x = clamp( this.x, min.x, max.x );
+ this.y = clamp( this.y, min.y, max.y );
+ this.z = clamp( this.z, min.z, max.z );
- if ( uv.x < 0 || uv.x > 1 ) {
+ return this;
- switch ( this.wrapS ) {
+ }
- case RepeatWrapping:
+ /**
+ * If this vector's x, y or z values are greater than the max value, they are
+ * replaced by the max value.
+ * If this vector's x, y or z values are less than the min value, they are
+ * replaced by the min value.
+ *
+ * @param {number} minVal - The minimum value the components will be clamped to.
+ * @param {number} maxVal - The maximum value the components will be clamped to.
+ * @return {Vector3} A reference to this vector.
+ */
+ clampScalar( minVal, maxVal ) {
- uv.x = uv.x - Math.floor( uv.x );
- break;
+ this.x = clamp( this.x, minVal, maxVal );
+ this.y = clamp( this.y, minVal, maxVal );
+ this.z = clamp( this.z, minVal, maxVal );
- case ClampToEdgeWrapping:
+ return this;
- uv.x = uv.x < 0 ? 0 : 1;
- break;
+ }
- case MirroredRepeatWrapping:
+ /**
+ * If this vector's length is greater than the max value, it is replaced by
+ * the max value.
+ * If this vector's length is less than the min value, it is replaced by the
+ * min value.
+ *
+ * @param {number} min - The minimum value the vector length will be clamped to.
+ * @param {number} max - The maximum value the vector length will be clamped to.
+ * @return {Vector3} A reference to this vector.
+ */
+ clampLength( min, max ) {
- if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+ const length = this.length();
- uv.x = Math.ceil( uv.x ) - uv.x;
+ return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) );
- } else {
+ }
- uv.x = uv.x - Math.floor( uv.x );
+ /**
+ * The components of this vector are rounded down to the nearest integer value.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ floor() {
- }
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
- break;
+ return this;
- }
+ }
- }
+ /**
+ * The components of this vector are rounded up to the nearest integer value.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ ceil() {
- if ( uv.y < 0 || uv.y > 1 ) {
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
- switch ( this.wrapT ) {
+ return this;
- case RepeatWrapping:
+ }
- uv.y = uv.y - Math.floor( uv.y );
- break;
+ /**
+ * The components of this vector are rounded to the nearest integer value
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ round() {
- case ClampToEdgeWrapping:
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
- uv.y = uv.y < 0 ? 0 : 1;
- break;
+ return this;
- case MirroredRepeatWrapping:
+ }
- if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+ /**
+ * The components of this vector are rounded towards zero (up if negative,
+ * down if positive) to an integer value.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ roundToZero() {
- uv.y = Math.ceil( uv.y ) - uv.y;
+ this.x = Math.trunc( this.x );
+ this.y = Math.trunc( this.y );
+ this.z = Math.trunc( this.z );
- } else {
+ return this;
- uv.y = uv.y - Math.floor( uv.y );
+ }
- }
+ /**
+ * Inverts this vector - i.e. sets x = -x, y = -y and z = -z.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ negate() {
- break;
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
- }
+ return this;
- }
+ }
- if ( this.flipY ) {
+ /**
+ * Calculates the dot product of the given vector with this instance.
+ *
+ * @param {Vector3} v - The vector to compute the dot product with.
+ * @return {number} The result of the dot product.
+ */
+ dot( v ) {
- uv.y = 1 - uv.y;
+ return this.x * v.x + this.y * v.y + this.z * v.z;
- }
+ }
- return uv;
+ // TODO lengthSquared?
- }
+ /**
+ * Computes the square of the Euclidean length (straight-line length) from
+ * (0, 0, 0) to (x, y, z). If you are comparing the lengths of vectors, you should
+ * compare the length squared instead as it is slightly more efficient to calculate.
+ *
+ * @return {number} The square length of this vector.
+ */
+ lengthSq() {
- set needsUpdate( value ) {
+ return this.x * this.x + this.y * this.y + this.z * this.z;
- if ( value === true ) {
+ }
- this.version ++;
- this.source.needsUpdate = true;
+ /**
+ * Computes the Euclidean length (straight-line length) from (0, 0, 0) to (x, y, z).
+ *
+ * @return {number} The length of this vector.
+ */
+ length() {
- }
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
}
- get encoding() { // @deprecated, r152
+ /**
+ * Computes the Manhattan length of this vector.
+ *
+ * @return {number} The length of this vector.
+ */
+ manhattanLength() {
- warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );
- return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding;
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
}
- set encoding( encoding ) { // @deprecated, r152
+ /**
+ * Converts this vector to a unit vector - that is, sets it equal to a vector
+ * with the same direction as this one, but with a vector length of `1`.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ normalize() {
- warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );
- this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;
+ return this.divideScalar( this.length() || 1 );
}
-}
+ /**
+ * Sets this vector to a vector with the same direction as this one, but
+ * with the specified length.
+ *
+ * @param {number} length - The new length of this vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ setLength( length ) {
-Texture.DEFAULT_IMAGE = null;
-Texture.DEFAULT_MAPPING = UVMapping;
-Texture.DEFAULT_ANISOTROPY = 1;
+ return this.normalize().multiplyScalar( length );
-class Vector4 {
+ }
- constructor( x = 0, y = 0, z = 0, w = 1 ) {
+ /**
+ * Linearly interpolates between the given vector and this instance, where
+ * alpha is the percent distance along the line - alpha = 0 will be this
+ * vector, and alpha = 1 will be the given one.
+ *
+ * @param {Vector3} v - The vector to interpolate towards.
+ * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
+ * @return {Vector3} A reference to this vector.
+ */
+ lerp( v, alpha ) {
- Vector4.prototype.isVector4 = true;
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
+ return this;
}
- get width() {
+ /**
+ * Linearly interpolates between the given vectors, where alpha is the percent
+ * distance along the line - alpha = 0 will be first vector, and alpha = 1 will
+ * be the second one. The result is stored in this instance.
+ *
+ * @param {Vector3} v1 - The first vector.
+ * @param {Vector3} v2 - The second vector.
+ * @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
+ * @return {Vector3} A reference to this vector.
+ */
+ lerpVectors( v1, v2, alpha ) {
- return this.z;
+ this.x = v1.x + ( v2.x - v1.x ) * alpha;
+ this.y = v1.y + ( v2.y - v1.y ) * alpha;
+ this.z = v1.z + ( v2.z - v1.z ) * alpha;
+
+ return this;
}
- set width( value ) {
+ /**
+ * Calculates the cross product of the given vector with this instance.
+ *
+ * @param {Vector3} v - The vector to compute the cross product with.
+ * @return {Vector3} The result of the cross product.
+ */
+ cross( v ) {
- this.z = value;
+ return this.crossVectors( this, v );
}
- get height() {
-
- return this.w;
+ /**
+ * Calculates the cross product of the given vectors and stores the result
+ * in this instance.
+ *
+ * @param {Vector3} a - The first vector.
+ * @param {Vector3} b - The second vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ crossVectors( a, b ) {
- }
+ const ax = a.x, ay = a.y, az = a.z;
+ const bx = b.x, by = b.y, bz = b.z;
- set height( value ) {
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
- this.w = value;
+ return this;
}
- set( x, y, z, w ) {
+ /**
+ * Projects this vector onto the given one.
+ *
+ * @param {Vector3} v - The vector to project to.
+ * @return {Vector3} A reference to this vector.
+ */
+ projectOnVector( v ) {
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
+ const denominator = v.lengthSq();
- return this;
+ if ( denominator === 0 ) return this.set( 0, 0, 0 );
+
+ const scalar = v.dot( this ) / denominator;
+
+ return this.copy( v ).multiplyScalar( scalar );
}
- setScalar( scalar ) {
+ /**
+ * Projects this vector onto a plane by subtracting this
+ * vector projected onto the plane's normal from this vector.
+ *
+ * @param {Vector3} planeNormal - The plane normal.
+ * @return {Vector3} A reference to this vector.
+ */
+ projectOnPlane( planeNormal ) {
- this.x = scalar;
- this.y = scalar;
- this.z = scalar;
- this.w = scalar;
+ _vector$c.copy( this ).projectOnVector( planeNormal );
- return this;
+ return this.sub( _vector$c );
}
- setX( x ) {
-
- this.x = x;
+ /**
+ * Reflects this vector off a plane orthogonal to the given normal vector.
+ *
+ * @param {Vector3} normal - The (normalized) normal vector.
+ * @return {Vector3} A reference to this vector.
+ */
+ reflect( normal ) {
- return this;
+ return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
}
+ /**
+ * Returns the angle between the given vector and this instance in radians.
+ *
+ * @param {Vector3} v - The vector to compute the angle with.
+ * @return {number} The angle in radians.
+ */
+ angleTo( v ) {
- setY( y ) {
+ const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
- this.y = y;
+ if ( denominator === 0 ) return Math.PI / 2;
- return this;
+ const theta = this.dot( v ) / denominator;
- }
+ // clamp, to handle numerical problems
- setZ( z ) {
+ return Math.acos( clamp( theta, -1, 1 ) );
- this.z = z;
+ }
- return this;
+ /**
+ * Computes the distance from the given vector to this instance.
+ *
+ * @param {Vector3} v - The vector to compute the distance to.
+ * @return {number} The distance.
+ */
+ distanceTo( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
}
- setW( w ) {
+ /**
+ * Computes the squared distance from the given vector to this instance.
+ * If you are just comparing the distance with another distance, you should compare
+ * the distance squared instead as it is slightly more efficient to calculate.
+ *
+ * @param {Vector3} v - The vector to compute the squared distance to.
+ * @return {number} The squared distance.
+ */
+ distanceToSquared( v ) {
- this.w = w;
+ const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
- return this;
+ return dx * dx + dy * dy + dz * dz;
}
- setComponent( index, value ) {
+ /**
+ * Computes the Manhattan distance from the given vector to this instance.
+ *
+ * @param {Vector3} v - The vector to compute the Manhattan distance to.
+ * @return {number} The Manhattan distance.
+ */
+ manhattanDistanceTo( v ) {
- switch ( index ) {
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- case 2: this.z = value; break;
- case 3: this.w = value; break;
- default: throw new Error( 'index is out of range: ' + index );
+ }
- }
+ /**
+ * Sets the vector components from the given spherical coordinates.
+ *
+ * @param {Spherical} s - The spherical coordinates.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromSpherical( s ) {
- return this;
+ return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
}
- getComponent( index ) {
+ /**
+ * Sets the vector components from the given spherical coordinates.
+ *
+ * @param {number} radius - The radius.
+ * @param {number} phi - The phi angle in radians.
+ * @param {number} theta - The theta angle in radians.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromSphericalCoords( radius, phi, theta ) {
- switch ( index ) {
+ const sinPhiRadius = Math.sin( phi ) * radius;
- case 0: return this.x;
- case 1: return this.y;
- case 2: return this.z;
- case 3: return this.w;
- default: throw new Error( 'index is out of range: ' + index );
+ this.x = sinPhiRadius * Math.sin( theta );
+ this.y = Math.cos( phi ) * radius;
+ this.z = sinPhiRadius * Math.cos( theta );
- }
+ return this;
}
- clone() {
+ /**
+ * Sets the vector components from the given cylindrical coordinates.
+ *
+ * @param {Cylindrical} c - The cylindrical coordinates.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromCylindrical( c ) {
- return new this.constructor( this.x, this.y, this.z, this.w );
+ return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
}
- copy( v ) {
+ /**
+ * Sets the vector components from the given cylindrical coordinates.
+ *
+ * @param {number} radius - The radius.
+ * @param {number} theta - The theta angle in radians.
+ * @param {number} y - The y value.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromCylindricalCoords( radius, theta, y ) {
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
- this.w = ( v.w !== undefined ) ? v.w : 1;
+ this.x = radius * Math.sin( theta );
+ this.y = y;
+ this.z = radius * Math.cos( theta );
return this;
}
- add( v ) {
+ /**
+ * Sets the vector components to the position elements of the
+ * given transformation matrix.
+ *
+ * @param {Matrix4} m - The 4x4 matrix.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromMatrixPosition( m ) {
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
- this.w += v.w;
+ const e = m.elements;
+
+ this.x = e[ 12 ];
+ this.y = e[ 13 ];
+ this.z = e[ 14 ];
return this;
}
- addScalar( s ) {
+ /**
+ * Sets the vector components to the scale elements of the
+ * given transformation matrix.
+ *
+ * @param {Matrix4} m - The 4x4 matrix.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromMatrixScale( m ) {
- this.x += s;
- this.y += s;
- this.z += s;
- this.w += s;
+ const sx = this.setFromMatrixColumn( m, 0 ).length();
+ const sy = this.setFromMatrixColumn( m, 1 ).length();
+ const sz = this.setFromMatrixColumn( m, 2 ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
return this;
}
- addVectors( a, b ) {
+ /**
+ * Sets the vector components from the specified matrix column.
+ *
+ * @param {Matrix4} m - The 4x4 matrix.
+ * @param {number} index - The column index.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromMatrixColumn( m, index ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- this.z = a.z + b.z;
- this.w = a.w + b.w;
+ return this.fromArray( m.elements, index * 4 );
- return this;
+ }
+
+ /**
+ * Sets the vector components from the specified matrix column.
+ *
+ * @param {Matrix3} m - The 3x3 matrix.
+ * @param {number} index - The column index.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromMatrix3Column( m, index ) {
+
+ return this.fromArray( m.elements, index * 3 );
}
- addScaledVector( v, s ) {
+ /**
+ * Sets the vector components from the given Euler angles.
+ *
+ * @param {Euler} e - The Euler angles to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromEuler( e ) {
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
- this.w += v.w * s;
+ this.x = e._x;
+ this.y = e._y;
+ this.z = e._z;
return this;
}
- sub( v ) {
+ /**
+ * Sets the vector components from the RGB components of the
+ * given color.
+ *
+ * @param {Color} c - The color to set.
+ * @return {Vector3} A reference to this vector.
+ */
+ setFromColor( c ) {
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
- this.w -= v.w;
+ this.x = c.r;
+ this.y = c.g;
+ this.z = c.b;
return this;
}
- subScalar( s ) {
+ /**
+ * Returns `true` if this vector is equal with the given one.
+ *
+ * @param {Vector3} v - The vector to test for equality.
+ * @return {boolean} Whether this vector is equal with the given one.
+ */
+ equals( v ) {
- this.x -= s;
- this.y -= s;
- this.z -= s;
- this.w -= s;
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ }
+
+ /**
+ * Sets this vector's x value to be `array[ offset ]`, y value to be `array[ offset + 1 ]`
+ * and z value to be `array[ offset + 2 ]`.
+ *
+ * @param {Array} array - An array holding the vector component values.
+ * @param {number} [offset=0] - The offset into the array.
+ * @return {Vector3} A reference to this vector.
+ */
+ fromArray( array, offset = 0 ) {
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
return this;
}
- subVectors( a, b ) {
+ /**
+ * Writes the components of this vector to the given array. If no array is provided,
+ * the method returns a new instance.
+ *
+ * @param {Array} [array=[]] - The target array holding the vector components.
+ * @param {number} [offset=0] - Index of the first element in the array.
+ * @return {Array} The vector components.
+ */
+ toArray( array = [], offset = 0 ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- this.z = a.z - b.z;
- this.w = a.w - b.w;
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
- return this;
+ return array;
}
- multiply( v ) {
+ /**
+ * Sets the components of this vector from the given buffer attribute.
+ *
+ * @param {BufferAttribute} attribute - The buffer attribute holding vector data.
+ * @param {number} index - The index into the attribute.
+ * @return {Vector3} A reference to this vector.
+ */
+ fromBufferAttribute( attribute, index ) {
- this.x *= v.x;
- this.y *= v.y;
- this.z *= v.z;
- this.w *= v.w;
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
return this;
}
- multiplyScalar( scalar ) {
+ /**
+ * Sets each component of this vector to a pseudo-random value between `0` and
+ * `1`, excluding `1`.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ random() {
- this.x *= scalar;
- this.y *= scalar;
- this.z *= scalar;
- this.w *= scalar;
+ this.x = Math.random();
+ this.y = Math.random();
+ this.z = Math.random();
return this;
}
- applyMatrix4( m ) {
+ /**
+ * Sets this vector to a uniformly random point on a unit sphere.
+ *
+ * @return {Vector3} A reference to this vector.
+ */
+ randomDirection() {
- const x = this.x, y = this.y, z = this.z, w = this.w;
- const e = m.elements;
+ // https://mathworld.wolfram.com/SpherePointPicking.html
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
- this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
+ const theta = Math.random() * Math.PI * 2;
+ const u = Math.random() * 2 - 1;
+ const c = Math.sqrt( 1 - u * u );
+
+ this.x = c * Math.cos( theta );
+ this.y = u;
+ this.z = c * Math.sin( theta );
return this;
}
- divideScalar( scalar ) {
+ *[ Symbol.iterator ]() {
- return this.multiplyScalar( 1 / scalar );
+ yield this.x;
+ yield this.y;
+ yield this.z;
}
- setAxisAngleFromQuaternion( q ) {
+}
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+const _vector$c = /*@__PURE__*/ new Vector3();
+const _quaternion$4 = /*@__PURE__*/ new Quaternion();
- // q is assumed to be normalized
+/**
+ * Represents a 3x3 matrix.
+ *
+ * A Note on Row-Major and Column-Major Ordering:
+ *
+ * The constructor and {@link Matrix3#set} method take arguments in
+ * [row-major]{@link https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order}
+ * order, while internally they are stored in the {@link Matrix3#elements} array in column-major order.
+ * This means that calling:
+ * ```js
+ * const m = new THREE.Matrix();
+ * m.set( 11, 12, 13,
+ * 21, 22, 23,
+ * 31, 32, 33 );
+ * ```
+ * will result in the elements array containing:
+ * ```js
+ * m.elements = [ 11, 21, 31,
+ * 12, 22, 32,
+ * 13, 23, 33 ];
+ * ```
+ * and internally all calculations are performed using column-major ordering.
+ * However, as the actual ordering makes no difference mathematically and
+ * most people are used to thinking about matrices in row-major order, the
+ * three.js documentation shows matrices in row-major order. Just bear in
+ * mind that if you are reading the source code, you'll have to take the
+ * transpose of any matrices outlined here to make sense of the calculations.
+ */
+class Matrix3 {
- this.w = 2 * Math.acos( q.w );
+ /**
+ * Constructs a new 3x3 matrix. The arguments are supposed to be
+ * in row-major order. If no arguments are provided, the constructor
+ * initializes the matrix as an identity matrix.
+ *
+ * @param {number} [n11] - 1-1 matrix element.
+ * @param {number} [n12] - 1-2 matrix element.
+ * @param {number} [n13] - 1-3 matrix element.
+ * @param {number} [n21] - 2-1 matrix element.
+ * @param {number} [n22] - 2-2 matrix element.
+ * @param {number} [n23] - 2-3 matrix element.
+ * @param {number} [n31] - 3-1 matrix element.
+ * @param {number} [n32] - 3-2 matrix element.
+ * @param {number} [n33] - 3-3 matrix element.
+ */
+ constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
- const s = Math.sqrt( 1 - q.w * q.w );
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
+ Matrix3.prototype.isMatrix3 = true;
- if ( s < 0.0001 ) {
+ /**
+ * A column-major list of matrix values.
+ *
+ * @type {Array}
+ */
+ this.elements = [
- this.x = 1;
- this.y = 0;
- this.z = 0;
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
- } else {
+ ];
- this.x = q.x / s;
- this.y = q.y / s;
- this.z = q.z / s;
+ if ( n11 !== undefined ) {
- }
+ this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 );
- return this;
+ }
}
- setAxisAngleFromRotationMatrix( m ) {
+ /**
+ * Sets the elements of the matrix.The arguments are supposed to be
+ * in row-major order.
+ *
+ * @param {number} [n11] - 1-1 matrix element.
+ * @param {number} [n12] - 1-2 matrix element.
+ * @param {number} [n13] - 1-3 matrix element.
+ * @param {number} [n21] - 2-1 matrix element.
+ * @param {number} [n22] - 2-2 matrix element.
+ * @param {number} [n23] - 2-3 matrix element.
+ * @param {number} [n31] - 3-1 matrix element.
+ * @param {number} [n32] - 3-2 matrix element.
+ * @param {number} [n33] - 3-3 matrix element.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+ const te = this.elements;
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+ te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
+ te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
+ te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
- let angle, x, y, z; // variables for result
- const epsilon = 0.01, // margin to allow for rounding errors
- epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
+ return this;
- te = m.elements,
+ }
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+ /**
+ * Sets this matrix to the 3x3 identity matrix.
+ *
+ * @return {Matrix3} A reference to this matrix.
+ */
+ identity() {
- if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
- ( Math.abs( m13 - m31 ) < epsilon ) &&
- ( Math.abs( m23 - m32 ) < epsilon ) ) {
+ this.set(
- // singularity found
- // first check for identity matrix which must have +1 for all terms
- // in leading diagonal and zero in other terms
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
- if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
- ( Math.abs( m13 + m31 ) < epsilon2 ) &&
- ( Math.abs( m23 + m32 ) < epsilon2 ) &&
- ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+ );
- // this singularity is identity matrix so angle = 0
+ return this;
- this.set( 1, 0, 0, 0 );
+ }
- return this; // zero angle, arbitrary axis
+ /**
+ * Copies the values of the given matrix to this instance.
+ *
+ * @param {Matrix3} m - The matrix to copy.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ copy( m ) {
- }
+ const te = this.elements;
+ const me = m.elements;
- // otherwise this singularity is angle = 180
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
+ te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
+ te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
- angle = Math.PI;
+ return this;
- const xx = ( m11 + 1 ) / 2;
- const yy = ( m22 + 1 ) / 2;
- const zz = ( m33 + 1 ) / 2;
- const xy = ( m12 + m21 ) / 4;
- const xz = ( m13 + m31 ) / 4;
- const yz = ( m23 + m32 ) / 4;
+ }
- if ( ( xx > yy ) && ( xx > zz ) ) {
+ /**
+ * Extracts the basis of this matrix into the three axis vectors provided.
+ *
+ * @param {Vector3} xAxis - The basis's x axis.
+ * @param {Vector3} yAxis - The basis's y axis.
+ * @param {Vector3} zAxis - The basis's z axis.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ extractBasis( xAxis, yAxis, zAxis ) {
- // m11 is the largest diagonal term
+ xAxis.setFromMatrix3Column( this, 0 );
+ yAxis.setFromMatrix3Column( this, 1 );
+ zAxis.setFromMatrix3Column( this, 2 );
- if ( xx < epsilon ) {
+ return this;
- x = 0;
- y = 0.707106781;
- z = 0.707106781;
+ }
- } else {
+ /**
+ * Set this matrix to the upper 3x3 matrix of the given 4x4 matrix.
+ *
+ * @param {Matrix4} m - The 4x4 matrix.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ setFromMatrix4( m ) {
- x = Math.sqrt( xx );
- y = xy / x;
- z = xz / x;
+ const me = m.elements;
- }
+ this.set(
- } else if ( yy > zz ) {
+ me[ 0 ], me[ 4 ], me[ 8 ],
+ me[ 1 ], me[ 5 ], me[ 9 ],
+ me[ 2 ], me[ 6 ], me[ 10 ]
- // m22 is the largest diagonal term
+ );
- if ( yy < epsilon ) {
+ return this;
- x = 0.707106781;
- y = 0;
- z = 0.707106781;
+ }
- } else {
+ /**
+ * Post-multiplies this matrix by the given 3x3 matrix.
+ *
+ * @param {Matrix3} m - The matrix to multiply with.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ multiply( m ) {
- y = Math.sqrt( yy );
- x = xy / y;
- z = yz / y;
+ return this.multiplyMatrices( this, m );
- }
+ }
- } else {
+ /**
+ * Pre-multiplies this matrix by the given 3x3 matrix.
+ *
+ * @param {Matrix3} m - The matrix to multiply with.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ premultiply( m ) {
- // m33 is the largest diagonal term so base result on this
+ return this.multiplyMatrices( m, this );
- if ( zz < epsilon ) {
+ }
- x = 0.707106781;
- y = 0.707106781;
- z = 0;
+ /**
+ * Multiples the given 3x3 matrices and stores the result
+ * in this matrix.
+ *
+ * @param {Matrix3} a - The first matrix.
+ * @param {Matrix3} b - The second matrix.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ multiplyMatrices( a, b ) {
- } else {
+ const ae = a.elements;
+ const be = b.elements;
+ const te = this.elements;
- z = Math.sqrt( zz );
- x = xz / z;
- y = yz / z;
+ const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
+ const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
+ const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
- }
+ const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
+ const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
+ const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
- }
-
- this.set( x, y, z, angle );
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
+ te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
+ te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
- return this; // return 180 deg rotation
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
+ te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
+ te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
- }
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
+ te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
+ te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
- // as we have reached here there are no singularities so we can handle normally
+ return this;
- let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
- ( m13 - m31 ) * ( m13 - m31 ) +
- ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+ }
- if ( Math.abs( s ) < 0.001 ) s = 1;
+ /**
+ * Multiplies every component of the matrix by the given scalar.
+ *
+ * @param {number} s - The scalar.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ multiplyScalar( s ) {
- // prevent divide by zero, should not happen if matrix is orthogonal and should be
- // caught by singularity test above, but I've left it in just in case
+ const te = this.elements;
- this.x = ( m32 - m23 ) / s;
- this.y = ( m13 - m31 ) / s;
- this.z = ( m21 - m12 ) / s;
- this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+ te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+ te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+ te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
return this;
}
- min( v ) {
+ /**
+ * Computes and returns the determinant of this matrix.
+ *
+ * @return {number} The determinant.
+ */
+ determinant() {
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- this.z = Math.min( this.z, v.z );
- this.w = Math.min( this.w, v.w );
+ const te = this.elements;
- return this;
+ const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+ d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+ g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+
+ return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
}
- max( v ) {
+ /**
+ * Inverts this matrix, using the [analytic method]{@link https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution}.
+ * You can not invert with a determinant of zero. If you attempt this, the method produces
+ * a zero matrix instead.
+ *
+ * @return {Matrix3} A reference to this matrix.
+ */
+ invert() {
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- this.z = Math.max( this.z, v.z );
- this.w = Math.max( this.w, v.w );
+ const te = this.elements,
- return this;
+ n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],
+ n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],
+ n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],
- }
+ t11 = n33 * n22 - n32 * n23,
+ t12 = n32 * n13 - n33 * n12,
+ t13 = n23 * n12 - n22 * n13,
- clamp( min, max ) {
+ det = n11 * t11 + n21 * t12 + n31 * t13;
- // assumes min < max, componentwise
+ if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+
+ const detInv = 1 / det;
+
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
+ te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
+
+ te[ 3 ] = t12 * detInv;
+ te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
+ te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- this.z = Math.max( min.z, Math.min( max.z, this.z ) );
- this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+ te[ 6 ] = t13 * detInv;
+ te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
+ te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
return this;
}
- clampScalar( minVal, maxVal ) {
+ /**
+ * Transposes this matrix in place.
+ *
+ * @return {Matrix3} A reference to this matrix.
+ */
+ transpose() {
+
+ let tmp;
+ const m = this.elements;
- this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
- this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
- this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
- this.w = Math.max( minVal, Math.min( maxVal, this.w ) );
+ tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+ tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+ tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
return this;
}
- clampLength( min, max ) {
-
- const length = this.length();
+ /**
+ * Computes the normal matrix which is the inverse transpose of the upper
+ * left 3x3 portion of the given 4x4 matrix.
+ *
+ * @param {Matrix4} matrix4 - The 4x4 matrix.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ getNormalMatrix( matrix4 ) {
- return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+ return this.setFromMatrix4( matrix4 ).invert().transpose();
}
- floor() {
+ /**
+ * Transposes this matrix into the supplied array, and returns itself unchanged.
+ *
+ * @param {Array} r - An array to store the transposed matrix elements.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ transposeIntoArray( r ) {
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- this.z = Math.floor( this.z );
- this.w = Math.floor( this.w );
+ const m = this.elements;
+
+ r[ 0 ] = m[ 0 ];
+ r[ 1 ] = m[ 3 ];
+ r[ 2 ] = m[ 6 ];
+ r[ 3 ] = m[ 1 ];
+ r[ 4 ] = m[ 4 ];
+ r[ 5 ] = m[ 7 ];
+ r[ 6 ] = m[ 2 ];
+ r[ 7 ] = m[ 5 ];
+ r[ 8 ] = m[ 8 ];
return this;
}
- ceil() {
+ /**
+ * Sets the UV transform matrix from offset, repeat, rotation, and center.
+ *
+ * @param {number} tx - Offset x.
+ * @param {number} ty - Offset y.
+ * @param {number} sx - Repeat x.
+ * @param {number} sy - Repeat y.
+ * @param {number} rotation - Rotation, in radians. Positive values rotate counterclockwise.
+ * @param {number} cx - Center x of rotation.
+ * @param {number} cy - Center y of rotation
+ * @return {Matrix3} A reference to this matrix.
+ */
+ setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- this.z = Math.ceil( this.z );
- this.w = Math.ceil( this.w );
+ const c = Math.cos( rotation );
+ const s = Math.sin( rotation );
+
+ this.set(
+ sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
+ - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
+ 0, 0, 1
+ );
return this;
}
- round() {
+ /**
+ * Scales this matrix with the given scalar values.
+ *
+ * @param {number} sx - The amount to scale in the X axis.
+ * @param {number} sy - The amount to scale in the Y axis.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ scale( sx, sy ) {
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- this.z = Math.round( this.z );
- this.w = Math.round( this.w );
+ this.premultiply( _m3.makeScale( sx, sy ) );
return this;
}
- roundToZero() {
+ /**
+ * Rotates this matrix by the given angle.
+ *
+ * @param {number} theta - The rotation in radians.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ rotate( theta ) {
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
- this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+ this.premultiply( _m3.makeRotation( - theta ) );
return this;
}
- negate() {
+ /**
+ * Translates this matrix by the given scalar values.
+ *
+ * @param {number} tx - The amount to translate in the X axis.
+ * @param {number} ty - The amount to translate in the Y axis.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ translate( tx, ty ) {
- this.x = - this.x;
- this.y = - this.y;
- this.z = - this.z;
- this.w = - this.w;
+ this.premultiply( _m3.makeTranslation( tx, ty ) );
return this;
}
- dot( v ) {
+ // for 2D Transforms
- return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+ /**
+ * Sets this matrix as a 2D translation transform.
+ *
+ * @param {number|Vector2} x - The amount to translate in the X axis or alternatively a translation vector.
+ * @param {number} y - The amount to translate in the Y axis.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ makeTranslation( x, y ) {
- }
+ if ( x.isVector2 ) {
- lengthSq() {
+ this.set(
- return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+ 1, 0, x.x,
+ 0, 1, x.y,
+ 0, 0, 1
- }
+ );
- length() {
+ } else {
- return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+ this.set(
- }
+ 1, 0, x,
+ 0, 1, y,
+ 0, 0, 1
- manhattanLength() {
+ );
- return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
+ }
+
+ return this;
}
- normalize() {
+ /**
+ * Sets this matrix as a 2D rotational transformation.
+ *
+ * @param {number} theta - The rotation in radians.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ makeRotation( theta ) {
- return this.divideScalar( this.length() || 1 );
+ // counterclockwise
- }
+ const c = Math.cos( theta );
+ const s = Math.sin( theta );
- setLength( length ) {
+ this.set(
- return this.normalize().multiplyScalar( length );
+ c, - s, 0,
+ s, c, 0,
+ 0, 0, 1
+
+ );
+
+ return this;
}
- lerp( v, alpha ) {
+ /**
+ * Sets this matrix as a 2D scale transform.
+ *
+ * @param {number} x - The amount to scale in the X axis.
+ * @param {number} y - The amount to scale in the Y axis.
+ * @return {Matrix3} A reference to this matrix.
+ */
+ makeScale( x, y ) {
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- this.z += ( v.z - this.z ) * alpha;
- this.w += ( v.w - this.w ) * alpha;
+ this.set(
+
+ x, 0, 0,
+ 0, y, 0,
+ 0, 0, 1
+
+ );
return this;
}
- lerpVectors( v1, v2, alpha ) {
+ /**
+ * Returns `true` if this matrix is equal with the given one.
+ *
+ * @param {Matrix3} matrix - The matrix to test for equality.
+ * @return {boolean} Whether this matrix is equal with the given one.
+ */
+ equals( matrix ) {
- this.x = v1.x + ( v2.x - v1.x ) * alpha;
- this.y = v1.y + ( v2.y - v1.y ) * alpha;
- this.z = v1.z + ( v2.z - v1.z ) * alpha;
- this.w = v1.w + ( v2.w - v1.w ) * alpha;
+ const te = this.elements;
+ const me = matrix.elements;
- return this;
+ for ( let i = 0; i < 9; i ++ ) {
- }
+ if ( te[ i ] !== me[ i ] ) return false;
- equals( v ) {
+ }
- return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+ return true;
}
+ /**
+ * Sets the elements of the matrix from the given array.
+ *
+ * @param {Array} array - The matrix elements in column-major order.
+ * @param {number} [offset=0] - Index of the first element in the array.
+ * @return {Matrix3} A reference to this matrix.
+ */
fromArray( array, offset = 0 ) {
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- this.z = array[ offset + 2 ];
- this.w = array[ offset + 3 ];
+ for ( let i = 0; i < 9; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
return this;
}
+ /**
+ * Writes the elements of this matrix to the given array. If no array is provided,
+ * the method returns a new instance.
+ *
+ * @param {Array} [array=[]] - The target array holding the matrix elements in column-major order.
+ * @param {number} [offset=0] - Index of the first element in the array.
+ * @return {Array} The matrix elements in column-major order.
+ */
toArray( array = [], offset = 0 ) {
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- array[ offset + 2 ] = this.z;
- array[ offset + 3 ] = this.w;
+ const te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+
+ array[ offset + 3 ] = te[ 3 ];
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+ array[ offset + 8 ] = te[ 8 ];
return array;
}
- fromBufferAttribute( attribute, index ) {
-
- this.x = attribute.getX( index );
- this.y = attribute.getY( index );
- this.z = attribute.getZ( index );
- this.w = attribute.getW( index );
+ /**
+ * Returns a matrix with copied values from this instance.
+ *
+ * @return {Matrix3} A clone of this instance.
+ */
+ clone() {
- return this;
+ return new this.constructor().fromArray( this.elements );
}
- random() {
+}
- this.x = Math.random();
- this.y = Math.random();
- this.z = Math.random();
- this.w = Math.random();
+const _m3 = /*@__PURE__*/ new Matrix3();
- return this;
+function arrayNeedsUint32( array ) {
- }
+ // assumes larger values usually on last
- *[ Symbol.iterator ]() {
+ for ( let i = array.length - 1; i >= 0; -- i ) {
- yield this.x;
- yield this.y;
- yield this.z;
- yield this.w;
+ if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
}
-}
+ return false;
-/*
- In options, we can specify:
- * Texture parameters for an auto-generated target texture
- * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
-*/
-class WebGLRenderTarget extends EventDispatcher {
+}
- constructor( width = 1, height = 1, options = {} ) {
+const TYPED_ARRAYS = {
+ Int8Array: Int8Array,
+ Uint8Array: Uint8Array,
+ Uint8ClampedArray: Uint8ClampedArray,
+ Int16Array: Int16Array,
+ Uint16Array: Uint16Array,
+ Int32Array: Int32Array,
+ Uint32Array: Uint32Array,
+ Float32Array: Float32Array,
+ Float64Array: Float64Array
+};
- super();
+function getTypedArray( type, buffer ) {
- this.isWebGLRenderTarget = true;
+ return new TYPED_ARRAYS[ type ]( buffer );
- this.width = width;
- this.height = height;
- this.depth = 1;
+}
- this.scissor = new Vector4( 0, 0, width, height );
- this.scissorTest = false;
+function createElementNS( name ) {
- this.viewport = new Vector4( 0, 0, width, height );
+ return document.createElementNS( 'http://www.w3.org/1999/xhtml', name );
- const image = { width: width, height: height, depth: 1 };
+}
- if ( options.encoding !== undefined ) {
+function createCanvasElement() {
- // @deprecated, r152
- warnOnce( 'THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.' );
- options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;
+ const canvas = createElementNS( 'canvas' );
+ canvas.style.display = 'block';
+ return canvas;
- }
+}
- this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );
- this.texture.isRenderTargetTexture = true;
+const _cache = {};
- this.texture.flipY = false;
- this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;
- this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null;
- this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;
+function warnOnce( message ) {
- this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
- this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false;
+ if ( message in _cache ) return;
- this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
+ _cache[ message ] = true;
- this.samples = options.samples !== undefined ? options.samples : 0;
+ console.warn( message );
- }
+}
- setSize( width, height, depth = 1 ) {
+function probeAsync( gl, sync, interval ) {
- if ( this.width !== width || this.height !== height || this.depth !== depth ) {
+ return new Promise( function ( resolve, reject ) {
- this.width = width;
- this.height = height;
- this.depth = depth;
+ function probe() {
- this.texture.image.width = width;
- this.texture.image.height = height;
- this.texture.image.depth = depth;
+ switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) {
- this.dispose();
+ case gl.WAIT_FAILED:
+ reject();
+ break;
- }
+ case gl.TIMEOUT_EXPIRED:
+ setTimeout( probe, interval );
+ break;
- this.viewport.set( 0, 0, width, height );
- this.scissor.set( 0, 0, width, height );
+ default:
+ resolve();
- }
+ }
- clone() {
+ }
- return new this.constructor().copy( this );
+ setTimeout( probe, interval );
- }
+ } );
- copy( source ) {
+}
- this.width = source.width;
- this.height = source.height;
- this.depth = source.depth;
+function toNormalizedProjectionMatrix( projectionMatrix ) {
- this.scissor.copy( source.scissor );
- this.scissorTest = source.scissorTest;
+ const m = projectionMatrix.elements;
- this.viewport.copy( source.viewport );
+ // Convert [-1, 1] to [0, 1] projection matrix
+ m[ 2 ] = 0.5 * m[ 2 ] + 0.5 * m[ 3 ];
+ m[ 6 ] = 0.5 * m[ 6 ] + 0.5 * m[ 7 ];
+ m[ 10 ] = 0.5 * m[ 10 ] + 0.5 * m[ 11 ];
+ m[ 14 ] = 0.5 * m[ 14 ] + 0.5 * m[ 15 ];
- this.texture = source.texture.clone();
- this.texture.isRenderTargetTexture = true;
+}
- // ensure image object is not shared, see #20328
+function toReversedProjectionMatrix( projectionMatrix ) {
- const image = Object.assign( {}, source.texture.image );
- this.texture.source = new Source( image );
+ const m = projectionMatrix.elements;
+ const isPerspectiveMatrix = m[ 11 ] === -1;
- this.depthBuffer = source.depthBuffer;
- this.stencilBuffer = source.stencilBuffer;
+ // Reverse [0, 1] projection matrix
+ if ( isPerspectiveMatrix ) {
- if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();
+ m[ 10 ] = - m[ 10 ] - 1;
+ m[ 14 ] = - m[ 14 ];
- this.samples = source.samples;
+ } else {
- return this;
+ m[ 10 ] = - m[ 10 ];
+ m[ 14 ] = - m[ 14 ] + 1;
}
- dispose() {
+}
- this.dispatchEvent( { type: 'dispose' } );
+const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
+ 0.4123908, 0.3575843, 0.1804808,
+ 0.2126390, 0.7151687, 0.0721923,
+ 0.0193308, 0.1191948, 0.9505322
+);
- }
+const XYZ_TO_LINEAR_REC709 = /*@__PURE__*/ new Matrix3().set(
+ 3.2409699, -1.5373832, -0.4986108,
+ -0.9692436, 1.8759675, 0.0415551,
+ 0.0556301, -0.203977, 1.0569715
+);
-}
+function createColorManagement() {
-class DataArrayTexture extends Texture {
+ const ColorManagement = {
- constructor( data = null, width = 1, height = 1, depth = 1 ) {
+ enabled: true,
- super( null );
+ workingColorSpace: LinearSRGBColorSpace,
- this.isDataArrayTexture = true;
+ /**
+ * Implementations of supported color spaces.
+ *
+ * Required:
+ * - primaries: chromaticity coordinates [ rx ry gx gy bx by ]
+ * - whitePoint: reference white [ x y ]
+ * - transfer: transfer function (pre-defined)
+ * - toXYZ: Matrix3 RGB to XYZ transform
+ * - fromXYZ: Matrix3 XYZ to RGB transform
+ * - luminanceCoefficients: RGB luminance coefficients
+ *
+ * Optional:
+ * - outputColorSpaceConfig: { drawingBufferColorSpace: ColorSpace }
+ * - workingColorSpaceConfig: { unpackColorSpace: ColorSpace }
+ *
+ * Reference:
+ * - https://www.russellcottrell.com/photo/matrixCalculator.htm
+ */
+ spaces: {},
- this.image = { data, width, height, depth };
+ convert: function ( color, sourceColorSpace, targetColorSpace ) {
- this.magFilter = NearestFilter;
- this.minFilter = NearestFilter;
+ if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
- this.wrapR = ClampToEdgeWrapping;
+ return color;
- this.generateMipmaps = false;
- this.flipY = false;
- this.unpackAlignment = 1;
+ }
- }
+ if ( this.spaces[ sourceColorSpace ].transfer === SRGBTransfer ) {
-}
+ color.r = SRGBToLinear( color.r );
+ color.g = SRGBToLinear( color.g );
+ color.b = SRGBToLinear( color.b );
-class WebGLArrayRenderTarget extends WebGLRenderTarget {
+ }
- constructor( width = 1, height = 1, depth = 1 ) {
+ if ( this.spaces[ sourceColorSpace ].primaries !== this.spaces[ targetColorSpace ].primaries ) {
- super( width, height );
+ color.applyMatrix3( this.spaces[ sourceColorSpace ].toXYZ );
+ color.applyMatrix3( this.spaces[ targetColorSpace ].fromXYZ );
- this.isWebGLArrayRenderTarget = true;
+ }
- this.depth = depth;
+ if ( this.spaces[ targetColorSpace ].transfer === SRGBTransfer ) {
- this.texture = new DataArrayTexture( null, width, height, depth );
+ color.r = LinearToSRGB( color.r );
+ color.g = LinearToSRGB( color.g );
+ color.b = LinearToSRGB( color.b );
- this.texture.isRenderTargetTexture = true;
+ }
- }
+ return color;
-}
+ },
-class Data3DTexture extends Texture {
+ workingToColorSpace: function ( color, targetColorSpace ) {
- constructor( data = null, width = 1, height = 1, depth = 1 ) {
+ return this.convert( color, this.workingColorSpace, targetColorSpace );
- // We're going to add .setXXX() methods for setting properties later.
- // Users can still set in DataTexture3D directly.
- //
- // const texture = new THREE.DataTexture3D( data, width, height, depth );
- // texture.anisotropy = 16;
- //
- // See #14839
+ },
- super( null );
+ colorSpaceToWorking: function ( color, sourceColorSpace ) {
- this.isData3DTexture = true;
+ return this.convert( color, sourceColorSpace, this.workingColorSpace );
- this.image = { data, width, height, depth };
+ },
- this.magFilter = NearestFilter;
- this.minFilter = NearestFilter;
+ getPrimaries: function ( colorSpace ) {
- this.wrapR = ClampToEdgeWrapping;
+ return this.spaces[ colorSpace ].primaries;
- this.generateMipmaps = false;
- this.flipY = false;
- this.unpackAlignment = 1;
+ },
- }
+ getTransfer: function ( colorSpace ) {
-}
+ if ( colorSpace === NoColorSpace ) return LinearTransfer;
-class WebGL3DRenderTarget extends WebGLRenderTarget {
+ return this.spaces[ colorSpace ].transfer;
- constructor( width = 1, height = 1, depth = 1 ) {
+ },
- super( width, height );
+ getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) {
- this.isWebGL3DRenderTarget = true;
+ return target.fromArray( this.spaces[ colorSpace ].luminanceCoefficients );
- this.depth = depth;
+ },
- this.texture = new Data3DTexture( null, width, height, depth );
+ define: function ( colorSpaces ) {
- this.texture.isRenderTargetTexture = true;
+ Object.assign( this.spaces, colorSpaces );
- }
+ },
-}
+ // Internal APIs
-class WebGLMultipleRenderTargets extends WebGLRenderTarget {
+ _getMatrix: function ( targetMatrix, sourceColorSpace, targetColorSpace ) {
- constructor( width = 1, height = 1, count = 1, options = {} ) {
+ return targetMatrix
+ .copy( this.spaces[ sourceColorSpace ].toXYZ )
+ .multiply( this.spaces[ targetColorSpace ].fromXYZ );
- super( width, height, options );
+ },
- this.isWebGLMultipleRenderTargets = true;
+ _getDrawingBufferColorSpace: function ( colorSpace ) {
- const texture = this.texture;
+ return this.spaces[ colorSpace ].outputColorSpaceConfig.drawingBufferColorSpace;
- this.texture = [];
+ },
- for ( let i = 0; i < count; i ++ ) {
+ _getUnpackColorSpace: function ( colorSpace = this.workingColorSpace ) {
- this.texture[ i ] = texture.clone();
- this.texture[ i ].isRenderTargetTexture = true;
+ return this.spaces[ colorSpace ].workingColorSpaceConfig.unpackColorSpace;
- }
+ },
- }
+ // Deprecated
- setSize( width, height, depth = 1 ) {
+ fromWorkingColorSpace: function ( color, targetColorSpace ) {
- if ( this.width !== width || this.height !== height || this.depth !== depth ) {
+ warnOnce( 'THREE.ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace().' ); // @deprecated, r177
- this.width = width;
- this.height = height;
- this.depth = depth;
+ return ColorManagement.workingToColorSpace( color, targetColorSpace );
- for ( let i = 0, il = this.texture.length; i < il; i ++ ) {
+ },
- this.texture[ i ].image.width = width;
- this.texture[ i ].image.height = height;
- this.texture[ i ].image.depth = depth;
+ toWorkingColorSpace: function ( color, sourceColorSpace ) {
- }
+ warnOnce( 'THREE.ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking().' ); // @deprecated, r177
- this.dispose();
+ return ColorManagement.colorSpaceToWorking( color, sourceColorSpace );
- }
+ },
- this.viewport.set( 0, 0, width, height );
- this.scissor.set( 0, 0, width, height );
+ };
- return this;
+ /******************************************************************************
+ * sRGB definitions
+ */
- }
+ const REC709_PRIMARIES = [ 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 ];
+ const REC709_LUMINANCE_COEFFICIENTS = [ 0.2126, 0.7152, 0.0722 ];
+ const D65 = [ 0.3127, 0.3290 ];
+
+ ColorManagement.define( {
+
+ [ LinearSRGBColorSpace ]: {
+ primaries: REC709_PRIMARIES,
+ whitePoint: D65,
+ transfer: LinearTransfer,
+ toXYZ: LINEAR_REC709_TO_XYZ,
+ fromXYZ: XYZ_TO_LINEAR_REC709,
+ luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
+ workingColorSpaceConfig: { unpackColorSpace: SRGBColorSpace },
+ outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
+ },
- copy( source ) {
+ [ SRGBColorSpace ]: {
+ primaries: REC709_PRIMARIES,
+ whitePoint: D65,
+ transfer: SRGBTransfer,
+ toXYZ: LINEAR_REC709_TO_XYZ,
+ fromXYZ: XYZ_TO_LINEAR_REC709,
+ luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
+ outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
+ },
- this.dispose();
+ } );
- this.width = source.width;
- this.height = source.height;
- this.depth = source.depth;
+ return ColorManagement;
- this.scissor.copy( source.scissor );
- this.scissorTest = source.scissorTest;
+}
- this.viewport.copy( source.viewport );
+const ColorManagement = /*@__PURE__*/ createColorManagement();
- this.depthBuffer = source.depthBuffer;
- this.stencilBuffer = source.stencilBuffer;
+function SRGBToLinear( c ) {
- if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();
+ return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
- this.texture.length = 0;
+}
- for ( let i = 0, il = source.texture.length; i < il; i ++ ) {
+function LinearToSRGB( c ) {
- this.texture[ i ] = source.texture[ i ].clone();
- this.texture[ i ].isRenderTargetTexture = true;
+ return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
- }
+}
- return this;
+let _canvas;
- }
+/**
+ * A class containing utility functions for images.
+ *
+ * @hideconstructor
+ */
+class ImageUtils {
-}
+ /**
+ * Returns a data URI containing a representation of the given image.
+ *
+ * @param {(HTMLImageElement|HTMLCanvasElement)} image - The image object.
+ * @param {string} [type='image/png'] - Indicates the image format.
+ * @return {string} The data URI.
+ */
+ static getDataURL( image, type = 'image/png' ) {
-class Quaternion {
+ if ( /^data:/i.test( image.src ) ) {
- constructor( x = 0, y = 0, z = 0, w = 1 ) {
+ return image.src;
- this.isQuaternion = true;
+ }
- this._x = x;
- this._y = y;
- this._z = z;
- this._w = w;
-
- }
-
- static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
-
- // fuzz-free, array-based Quaternion SLERP operation
-
- let x0 = src0[ srcOffset0 + 0 ],
- y0 = src0[ srcOffset0 + 1 ],
- z0 = src0[ srcOffset0 + 2 ],
- w0 = src0[ srcOffset0 + 3 ];
-
- const x1 = src1[ srcOffset1 + 0 ],
- y1 = src1[ srcOffset1 + 1 ],
- z1 = src1[ srcOffset1 + 2 ],
- w1 = src1[ srcOffset1 + 3 ];
-
- if ( t === 0 ) {
-
- dst[ dstOffset + 0 ] = x0;
- dst[ dstOffset + 1 ] = y0;
- dst[ dstOffset + 2 ] = z0;
- dst[ dstOffset + 3 ] = w0;
- return;
-
- }
-
- if ( t === 1 ) {
+ if ( typeof HTMLCanvasElement === 'undefined' ) {
- dst[ dstOffset + 0 ] = x1;
- dst[ dstOffset + 1 ] = y1;
- dst[ dstOffset + 2 ] = z1;
- dst[ dstOffset + 3 ] = w1;
- return;
+ return image.src;
}
- if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
+ let canvas;
- let s = 1 - t;
- const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
- dir = ( cos >= 0 ? 1 : - 1 ),
- sqrSin = 1 - cos * cos;
+ if ( image instanceof HTMLCanvasElement ) {
- // Skip the Slerp for tiny steps to avoid numeric problems:
- if ( sqrSin > Number.EPSILON ) {
+ canvas = image;
- const sin = Math.sqrt( sqrSin ),
- len = Math.atan2( sin, cos * dir );
+ } else {
- s = Math.sin( s * len ) / sin;
- t = Math.sin( t * len ) / sin;
+ if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );
- }
+ _canvas.width = image.width;
+ _canvas.height = image.height;
- const tDir = t * dir;
+ const context = _canvas.getContext( '2d' );
- x0 = x0 * s + x1 * tDir;
- y0 = y0 * s + y1 * tDir;
- z0 = z0 * s + z1 * tDir;
- w0 = w0 * s + w1 * tDir;
+ if ( image instanceof ImageData ) {
- // Normalize in case we just did a lerp:
- if ( s === 1 - t ) {
+ context.putImageData( image, 0, 0 );
- const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
+ } else {
- x0 *= f;
- y0 *= f;
- z0 *= f;
- w0 *= f;
+ context.drawImage( image, 0, 0, image.width, image.height );
}
+ canvas = _canvas;
+
}
- dst[ dstOffset ] = x0;
- dst[ dstOffset + 1 ] = y0;
- dst[ dstOffset + 2 ] = z0;
- dst[ dstOffset + 3 ] = w0;
+ return canvas.toDataURL( type );
}
- static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
-
- const x0 = src0[ srcOffset0 ];
- const y0 = src0[ srcOffset0 + 1 ];
- const z0 = src0[ srcOffset0 + 2 ];
- const w0 = src0[ srcOffset0 + 3 ];
+ /**
+ * Converts the given sRGB image data to linear color space.
+ *
+ * @param {(HTMLImageElement|HTMLCanvasElement|ImageBitmap|Object)} image - The image object.
+ * @return {HTMLCanvasElement|Object} The converted image.
+ */
+ static sRGBToLinear( image ) {
- const x1 = src1[ srcOffset1 ];
- const y1 = src1[ srcOffset1 + 1 ];
- const z1 = src1[ srcOffset1 + 2 ];
- const w1 = src1[ srcOffset1 + 3 ];
+ if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
+ ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
+ ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
- dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
- dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
- dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
- dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
+ const canvas = createElementNS( 'canvas' );
- return dst;
+ canvas.width = image.width;
+ canvas.height = image.height;
- }
+ const context = canvas.getContext( '2d' );
+ context.drawImage( image, 0, 0, image.width, image.height );
- get x() {
+ const imageData = context.getImageData( 0, 0, image.width, image.height );
+ const data = imageData.data;
- return this._x;
+ for ( let i = 0; i < data.length; i ++ ) {
- }
+ data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;
- set x( value ) {
+ }
- this._x = value;
- this._onChangeCallback();
+ context.putImageData( imageData, 0, 0 );
- }
+ return canvas;
- get y() {
+ } else if ( image.data ) {
- return this._y;
+ const data = image.data.slice( 0 );
- }
+ for ( let i = 0; i < data.length; i ++ ) {
- set y( value ) {
+ if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {
- this._y = value;
- this._onChangeCallback();
+ data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );
- }
+ } else {
- get z() {
+ // assuming float
- return this._z;
+ data[ i ] = SRGBToLinear( data[ i ] );
- }
+ }
- set z( value ) {
+ }
- this._z = value;
- this._onChangeCallback();
+ return {
+ data: data,
+ width: image.width,
+ height: image.height
+ };
- }
+ } else {
- get w() {
+ console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );
+ return image;
- return this._w;
+ }
}
- set w( value ) {
+}
- this._w = value;
- this._onChangeCallback();
+let _sourceId = 0;
- }
+/**
+ * Represents the data source of a texture.
+ *
+ * The main purpose of this class is to decouple the data definition from the texture
+ * definition so the same data can be used with multiple texture instances.
+ */
+class Source {
- set( x, y, z, w ) {
+ /**
+ * Constructs a new video texture.
+ *
+ * @param {any} [data=null] - The data definition of a texture.
+ */
+ constructor( data = null ) {
- this._x = x;
- this._y = y;
- this._z = z;
- this._w = w;
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
+ this.isSource = true;
- this._onChangeCallback();
+ /**
+ * The ID of the source.
+ *
+ * @name Source#id
+ * @type {number}
+ * @readonly
+ */
+ Object.defineProperty( this, 'id', { value: _sourceId ++ } );
- return this;
+ /**
+ * The UUID of the source.
+ *
+ * @type {string}
+ * @readonly
+ */
+ this.uuid = generateUUID();
- }
+ /**
+ * The data definition of a texture.
+ *
+ * @type {any}
+ */
+ this.data = data;
- clone() {
+ /**
+ * This property is only relevant when {@link Source#needsUpdate} is set to `true` and
+ * provides more control on how texture data should be processed. When `dataReady` is set
+ * to `false`, the engine performs the memory allocation (if necessary) but does not transfer
+ * the data into the GPU memory.
+ *
+ * @type {boolean}
+ * @default true
+ */
+ this.dataReady = true;
- return new this.constructor( this._x, this._y, this._z, this._w );
+ /**
+ * This starts at `0` and counts how many times {@link Source#needsUpdate} is set to `true`.
+ *
+ * @type {number}
+ * @readonly
+ * @default 0
+ */
+ this.version = 0;
}
- copy( quaternion ) {
-
- this._x = quaternion.x;
- this._y = quaternion.y;
- this._z = quaternion.z;
- this._w = quaternion.w;
+ getSize( target ) {
- this._onChangeCallback();
+ const data = this.data;
- return this;
+ if ( data instanceof HTMLVideoElement ) {
- }
+ target.set( data.videoWidth, data.videoHeight );
- setFromEuler( euler, update ) {
+ } else if ( data !== null ) {
- const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
+ target.set( data.width, data.height, data.depth || 0 );
- // http://www.mathworks.com/matlabcentral/fileexchange/
- // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
- // content/SpinCalc.m
+ } else {
- const cos = Math.cos;
- const sin = Math.sin;
+ target.set( 0, 0, 0 );
- const c1 = cos( x / 2 );
- const c2 = cos( y / 2 );
- const c3 = cos( z / 2 );
+ }
- const s1 = sin( x / 2 );
- const s2 = sin( y / 2 );
- const s3 = sin( z / 2 );
+ return target;
- switch ( order ) {
+ }
- case 'XYZ':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
+ /**
+ * When the property is set to `true`, the engine allocates the memory
+ * for the texture (if necessary) and triggers the actual texture upload
+ * to the GPU next time the source is used.
+ *
+ * @type {boolean}
+ * @default false
+ * @param {boolean} value
+ */
+ set needsUpdate( value ) {
- case 'YXZ':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
+ if ( value === true ) this.version ++;
- case 'ZXY':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
+ }
- case 'ZYX':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
+ /**
+ * Serializes the source into JSON.
+ *
+ * @param {?(Object|string)} meta - An optional value holding meta information about the serialization.
+ * @return {Object} A JSON object representing the serialized source.
+ * @see {@link ObjectLoader#parse}
+ */
+ toJSON( meta ) {
- case 'YZX':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
+ const isRootObject = ( meta === undefined || typeof meta === 'string' );
- case 'XZY':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
+ if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) {
- default:
- console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
+ return meta.images[ this.uuid ];
}
- if ( update !== false ) this._onChangeCallback();
-
- return this;
-
- }
-
- setFromAxisAngle( axis, angle ) {
-
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
-
- // assumes axis is normalized
-
- const halfAngle = angle / 2, s = Math.sin( halfAngle );
-
- this._x = axis.x * s;
- this._y = axis.y * s;
- this._z = axis.z * s;
- this._w = Math.cos( halfAngle );
+ const output = {
+ uuid: this.uuid,
+ url: ''
+ };
- this._onChangeCallback();
+ const data = this.data;
- return this;
+ if ( data !== null ) {
- }
+ let url;
- setFromRotationMatrix( m ) {
+ if ( Array.isArray( data ) ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+ // cube texture
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+ url = [];
- const te = m.elements,
+ for ( let i = 0, l = data.length; i < l; i ++ ) {
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+ if ( data[ i ].isDataTexture ) {
- trace = m11 + m22 + m33;
+ url.push( serializeImage( data[ i ].image ) );
- if ( trace > 0 ) {
+ } else {
- const s = 0.5 / Math.sqrt( trace + 1.0 );
+ url.push( serializeImage( data[ i ] ) );
- this._w = 0.25 / s;
- this._x = ( m32 - m23 ) * s;
- this._y = ( m13 - m31 ) * s;
- this._z = ( m21 - m12 ) * s;
+ }
- } else if ( m11 > m22 && m11 > m33 ) {
+ }
- const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+ } else {
- this._w = ( m32 - m23 ) / s;
- this._x = 0.25 * s;
- this._y = ( m12 + m21 ) / s;
- this._z = ( m13 + m31 ) / s;
+ // texture
- } else if ( m22 > m33 ) {
+ url = serializeImage( data );
- const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+ }
- this._w = ( m13 - m31 ) / s;
- this._x = ( m12 + m21 ) / s;
- this._y = 0.25 * s;
- this._z = ( m23 + m32 ) / s;
+ output.url = url;
- } else {
+ }
- const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+ if ( ! isRootObject ) {
- this._w = ( m21 - m12 ) / s;
- this._x = ( m13 + m31 ) / s;
- this._y = ( m23 + m32 ) / s;
- this._z = 0.25 * s;
+ meta.images[ this.uuid ] = output;
}
- this._onChangeCallback();
-
- return this;
+ return output;
}
- setFromUnitVectors( vFrom, vTo ) {
-
- // assumes direction vectors vFrom and vTo are normalized
-
- let r = vFrom.dot( vTo ) + 1;
+}
- if ( r < Number.EPSILON ) {
+function serializeImage( image ) {
- // vFrom and vTo point in opposite directions
+ if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
+ ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
+ ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
- r = 0;
+ // default images
- if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+ return ImageUtils.getDataURL( image );
- this._x = - vFrom.y;
- this._y = vFrom.x;
- this._z = 0;
- this._w = r;
+ } else {
- } else {
+ if ( image.data ) {
- this._x = 0;
- this._y = - vFrom.z;
- this._z = vFrom.y;
- this._w = r;
+ // images of DataTexture
- }
+ return {
+ data: Array.from( image.data ),
+ width: image.width,
+ height: image.height,
+ type: image.data.constructor.name
+ };
} else {
- // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
-
- this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
- this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
- this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
- this._w = r;
+ console.warn( 'THREE.Texture: Unable to serialize Texture.' );
+ return {};
}
- return this.normalize();
-
}
- angleTo( q ) {
-
- return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) );
+}
- }
+let _textureId = 0;
- rotateTowards( q, step ) {
+const _tempVec3 = /*@__PURE__*/ new Vector3();
- const angle = this.angleTo( q );
+/**
+ * Base class for all textures.
+ *
+ * Note: After the initial use of a texture, its dimensions, format, and type
+ * cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one.
+ *
+ * @augments EventDispatcher
+ */
+class Texture extends EventDispatcher {
- if ( angle === 0 ) return this;
+ /**
+ * Constructs a new texture.
+ *
+ * @param {?Object} [image=Texture.DEFAULT_IMAGE] - The image holding the texture data.
+ * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping.
+ * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
+ * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
+ * @param {number} [magFilter=LinearFilter] - The mag filter value.
+ * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value.
+ * @param {number} [format=RGBAFormat] - The texture format.
+ * @param {number} [type=UnsignedByteType] - The texture type.
+ * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value.
+ * @param {string} [colorSpace=NoColorSpace] - The color space.
+ */
+ constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {
- const t = Math.min( 1, step / angle );
+ super();
- this.slerp( q, t );
+ /**
+ * This flag can be used for type testing.
+ *
+ * @type {boolean}
+ * @readonly
+ * @default true
+ */
+ this.isTexture = true;
- return this;
+ /**
+ * The ID of the texture.
+ *
+ * @name Texture#id
+ * @type {number}
+ * @readonly
+ */
+ Object.defineProperty( this, 'id', { value: _textureId ++ } );
- }
+ /**
+ * The UUID of the material.
+ *
+ * @type {string}
+ * @readonly
+ */
+ this.uuid = generateUUID();
- identity() {
+ /**
+ * The name of the material.
+ *
+ * @type {string}
+ */
+ this.name = '';
- return this.set( 0, 0, 0, 1 );
+ /**
+ * The data definition of a texture. A reference to the data source can be
+ * shared across textures. This is often useful in context of spritesheets
+ * where multiple textures render the same data but with different texture
+ * transformations.
+ *
+ * @type {Source}
+ */
+ this.source = new Source( image );
- }
+ /**
+ * An array holding user-defined mipmaps.
+ *
+ * @type {Array