From a4523368546b538806f339a3c0fd2cd8ba65d8b3 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Sat, 1 Jun 2024 04:00:28 +0900 Subject: [PATCH] v3.0.0 This version changes the types of functions so they return a specific type where as the old functions returned a union type old mat4.identity() -> Float32Array | Float64Array | number[] new mat4.identity() -> Float32Array But, these new funcitons will also return what you pass them mat4.identity([]) -> number[]; mat4.identity(new Float64Array(16)) -> Float64Array Further, they still take varied input mat4.lookAt( [4, 2, 9], // position [0, 0, 0], // target [0, 1, 0], // up ) -> Float32Array mat4.lookAt( someFloat64Array,, // position someFloat32Array, // target [0, 1, 0], // up ) -> Float32Array `setDefaultType` is removed. Instead there are 3 versions of every type and every API mat4 (generates Float32Array by default) mat4d (generates Float64Array by default) mat4n (generates number[] by default) similarly mat3, mat3d, mat3n vec4, vec4d, vec4n vec3, vec3d, vec3n vec2, vec2d, vec2n quad, quadd, quadn There are also types Mat4 = Float32Array Mat4d = Float64Array Mat4n = number[] And all the corresponding other types by default, if you use mat4, mat3, vec3, then they generate Float32Array which you means you can pass them directly to any function that takes a Float32Array like `device.queue.writeBuffer` etc... 1. ## Mat4, Vec3, etc are specific types You can't do this const position: Vec3 = [10, 20, 30]; // error, Vec3 is Float32Array You can do this const position: Vec3n = [10, 20, 30]; Or just don't type it const positon = [10, 20, 30]; 2. ## Bloat There's a ton of types and a ton of functions. The code isn't bigger. Only the names of the types and typed APIs and all their funcitons. --- README.md | 76 +++ package-lock.json | 316 ++------- package.json | 2 +- src/mat3-impl.ts | 538 ++++++++------- src/mat3.ts | 12 +- src/mat4-impl.ts | 1162 ++++++++++++++++---------------- src/mat4.ts | 11 +- src/quat-impl.ts | 554 ++++++++------- src/quat.ts | 65 +- src/types.ts | 20 + src/vec2-impl.ts | 438 +++++++----- src/vec2.ts | 79 +-- src/vec3-impl.ts | 580 +++++++++------- src/vec3.ts | 62 +- src/vec4-impl.ts | 498 ++++++++------ src/vec4.ts | 66 +- src/wgpu-matrix.ts | 158 ++++- test/tests/mat3-test.js | 122 ++-- test/tests/mat4-test.js | 216 +++--- test/tests/quat-test.js | 35 +- test/tests/vec2-test.js | 367 +++++----- test/tests/vec3-test.js | 407 +++++------ test/tests/vec4-test.js | 287 ++++---- test/tests/wgpu-matrix-test.js | 24 +- 24 files changed, 3079 insertions(+), 3016 deletions(-) create mode 100644 src/types.ts diff --git a/README.md b/README.md index 30b2b9e..7958c85 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,82 @@ As the saying goes [*premature optimization is the root of all evil.*](https://s ## Migration +### 2.x -> 3.x + +In JavaScript there should be no difference in the API except for the removable of `setDefaultType`. + +In TypeScript, 3.x should mostly be type compatible with 2.x. +3.x is an attempt to fix the casting that was necessary in 2.x. + +```js +// 2.x +device.queue.writeData(buffer, 0, mat4.identity() as Float32Array); // sadness! 😭 + +// 3.x +device.queue.writeData(buffer, 0, mat4.identity()); // Yay! 🎉 +``` + +In TypeScript the differences are as follows + +#### Functions have a default type but return what is passed to them as the dst. + +In 3.x each function has a default type but if you pass it +a destination it returns the type of the destination + +```ts +mat4.identity() // returns Float32Array +mat4.identity(new Float32Array(16)); // returns Float32Array +mat4.identity(new Float64Array(16)); // returns Float32Array +mat4.identity(new Array(16)); // returns number[] +``` + +### Types are specific + +```ts +const a: Mat4 = ...; // a = Float32Array +const b: Mat4d = ...; // b = Float64Array +const c: Mat4n = ...; // c = number[] +``` + +This is means code like this + +```ts +const position: Mat4 = [10, 20, 30]; +``` + +No longer works because `Mat4` is a `Float32Array`. + +**BUT, functions take any of the normal types as an argument just like they used to** + +```js +const position = [10, 20, 30]; // number[] +const target = vec3.create(1, 2, 3); // Float32Array +const up = new Float64Array([0, 1, 0]); // Float64Array + +// Works fine, even those types are different, just like 2.x did +const view = mat4.lookAt(position, target, up); // Float32Array +``` + +If you really want types for each concrete type there's + +* `Float32Array` types: `Mat3`, `Mat4`, `Quat`, `Vec2`, `Vec3`, `Vec4` +* `Float64Array` types: `Mat3d`, `Mat4d`, `Quatd`, `Vec2d`, `Vec3d`, `Vec4d`, +* `number[]` types: `Mat3n`, `Mat4n`, `Quatn`, `Vec2n`, `Vec3n`, `Vec4n` + +### There are 3 sets of functions, each one returning a different default + +```ts +mat4.identity() // returns Float32Array +mat4d.identity() // returns Float64Array +mat4n.identity() // returns number[] +``` + +Similarly there's `mat3d`, `mat3n`, `quatd`, `quatn`, +`vec2d`, `vec2n`, `vec3d`, `vec3n`, `vec4d`, `vec4n`. + +**Note: that in general you're unlikely to need any of these. Just use the +same ones you were using in 2.x** + ### 1.x -> 2.x * [`mat4.lookAt`](https://wgpu-matrix.org/docs/functions/mat4.lookAt.html) diff --git a/package-lock.json b/package-lock.json index d465b6e..6776146 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "rollup": "^4.17.2", "showdown": "^2.1.0", "tslib": "^2.6.2", - "typedoc": "^0.25.13", + "typedoc": "^0.26.0-beta.2", "typescript": "^5.4.5" } }, @@ -289,32 +289,6 @@ } } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", @@ -328,174 +302,11 @@ "darwin" ] }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] + "node_modules/@shikijs/core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.6.1.tgz", + "integrity": "sha512-CqYyepN4SnBopaoXYwng4NO8riB5ask/LTCkhOFq+GNGtr2X+aKeD767eYdqYukeixEUvv4bXdyTYVaogj7KBw==", + "dev": true }, "node_modules/@tsconfig/recommended": { "version": "1.0.6", @@ -783,12 +594,6 @@ "node": ">=8" } }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2295,12 +2100,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2323,6 +2122,15 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2366,18 +2174,29 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, - "bin": { - "marked": "bin/marked.js" + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, - "engines": { - "node": ">= 12" + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2771,6 +2590,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -3141,15 +2969,12 @@ } }, "node_modules/shiki": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.6.1.tgz", + "integrity": "sha512-1Pu/A1rtsG6HZvQm4W0NExQ45e02og+rPog7PDaFDiMumZgOYnZIu4JtGQeAIfMwdbKSjJQoCUr79vDLKUUxWA==", "dev": true, "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" + "@shikijs/core": "1.6.1" } }, "node_modules/showdown": { @@ -3407,24 +3232,25 @@ } }, "node_modules/typedoc": { - "version": "0.25.13", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz", - "integrity": "sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==", + "version": "0.26.0-beta.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.0-beta.2.tgz", + "integrity": "sha512-sFVUArw13cF8K7jeayUXyVCby9pRsEdjkQSvgnRxL8Wzuow9G7xlrXBhWU1BFBoGK8RC25hXD/4oJmMQhnnEnQ==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.3", - "shiki": "^0.14.7" + "markdown-it": "^14.1.0", + "minimatch": "^9.0.4", + "shiki": "^1.6.0", + "yaml": "^2.4.2" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 16" + "node": ">= 18" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -3464,6 +3290,12 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3500,18 +3332,6 @@ "node": ">= 0.8" } }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3574,6 +3394,18 @@ "node": ">=10" } }, + "node_modules/yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 08964a6..5418a22 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "rollup": "^4.17.2", "showdown": "^2.1.0", "tslib": "^2.6.2", - "typedoc": "^0.25.13", + "typedoc": "^0.26.0-beta.2", "typescript": "^5.4.5" } } diff --git a/src/mat3-impl.ts b/src/mat3-impl.ts index 74c97f9..4b1db03 100644 --- a/src/mat3-impl.ts +++ b/src/mat3-impl.ts @@ -21,61 +21,22 @@ */ import * as utils from './utils.js'; -import { Quat } from './quat'; -import { Mat3 } from './mat3'; -import { Mat4 } from './mat4'; -import Vec2, * as vec2 from './vec2-impl'; +import { QuatArg } from './quat'; +import { Mat3Arg, Mat3Type } from './mat3'; +import { Mat4Arg } from './mat4'; +import { Vec2Arg } from './vec2'; +import { getAPI as getVec2API } from './vec2-impl'; +import { BaseArgType } from './types'; -export default Mat3; +export { Mat3Arg, Mat3Type }; -export type Mat3LikeCtor = new (n: number) => Mat3; +type Mat3Ctor = new (n: number) => T; /** - * 3x3 Matrix math math functions. - * - * Almost all functions take an optional `dst` argument. If it is not passed in the - * functions will create a new matrix. In other words you can do this - * - * const mat = mat3.translation([1, 2, 3]); // Creates a new translation matrix - * - * or - * - * const mat = mat3.create(); - * mat3.translation([1, 2, 3], mat); // Puts translation matrix in mat. - * - * The first style is often easier but depending on where it's used it generates garbage where - * as there is almost never allocation with the second style. - * - * It is always save to pass any matrix as the destination. So for example - * - * const mat = mat3.identity(); - * const trans = mat3.translation([1, 2, 3]); - * mat3.multiply(mat, trans, mat); // Multiplies mat * trans and puts result in mat. - * - */ -let MatType: Mat3LikeCtor = Float32Array; - -// This mess is because with Mat3 we have 3 unused elements. -// For Float32Array and Float64Array that's not an issue -// but for Array it's troublesome -const ctorMap = new Map Mat3>([ - [Float32Array, () => new Float32Array(12)], - [Float64Array, () => new Float64Array(12)], - [Array, () => new Array(12).fill(0)], -]); -let newMat3: () => Mat3 = ctorMap.get(Float32Array)!; - -/** - * Sets the type this library creates for a Mat3 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Mat3 - */ -export function setDefaultType(ctor: new (n: number) => Mat3) { - const oldType = MatType; - MatType = ctor; - newMat3 = ctorMap.get(ctor)!; - return oldType; -} + * Generates a typed API for Mat3 + * */ +function getAPIImpl(Ctor: Mat3Ctor) { + const vec2 = getVec2API(Ctor); /** * Create a Mat3 from values @@ -89,17 +50,6 @@ export function setDefaultType(ctor: new (n: number) => Mat3) { * const m = mat3.clone(someJSArray); * ``` * - * Note: a consequence of the implementation is if your Mat3Type = `Array` - * instead of `Float32Array` or `Float64Array` then any values you - * don't pass in will be undefined. Usually this is not an issue since - * (a) using `Array` is rare and (b) using `mat3.create` is usually used - * to create a Mat3 to be filled out as in - * - * ``` - * const m = mat3.create(); - * mat3.perspective(fov, aspect, near, far, m); - * ``` - * * @param v0 - value for element 0 * @param v1 - value for element 1 * @param v2 - value for element 2 @@ -111,34 +61,34 @@ export function setDefaultType(ctor: new (n: number) => Mat3) { * @param v8 - value for element 8 * @returns matrix created from values. */ -export function create( +function create( v0?: number, v1?: number, v2?: number, v3?: number, v4?: number, v5?: number, - v6?: number, v7?: number, v8?: number): Mat3 { - const dst = newMat3(); + v6?: number, v7?: number, v8?: number) { + const newDst = new Ctor(12); // to make the array homogenous - dst[3] = 0; - dst[7] = 0; - dst[11] = 0; + newDst[3] = 0; + newDst[7] = 0; + newDst[11] = 0; if (v0 !== undefined) { - dst[0] = v0; + newDst[0] = v0; if (v1 !== undefined) { - dst[1] = v1; + newDst[1] = v1; if (v2 !== undefined) { - dst[2] = v2; + newDst[2] = v2; if (v3 !== undefined) { - dst[4] = v3; + newDst[4] = v3; if (v4 !== undefined) { - dst[5] = v4; + newDst[5] = v4; if (v5 !== undefined) { - dst[6] = v5; + newDst[6] = v5; if (v6 !== undefined) { - dst[8] = v6; + newDst[8] = v6; if (v7 !== undefined) { - dst[9] = v7; + newDst[9] = v7; if (v8 !== undefined) { - dst[10] = v8; + newDst[10] = v8; } } } @@ -149,7 +99,7 @@ export function create( } } - return dst; + return newDst; } /** @@ -168,17 +118,17 @@ export function create( * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat3 set from values. */ -export function set( +function set( v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, - v6: number, v7: number, v8: number, dst?: Mat3) { - dst = dst || newMat3(); + v6: number, v7: number, v8: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[0] = v0; dst[1] = v1; dst[ 2] = v2; dst[ 3] = 0; - dst[4] = v3; dst[5] = v4; dst[ 6] = v5; dst[ 7] = 0; - dst[8] = v6; dst[9] = v7; dst[10] = v8; dst[11] = 0; + newDst[0] = v0; newDst[1] = v1; newDst[ 2] = v2; newDst[ 3] = 0; + newDst[4] = v3; newDst[5] = v4; newDst[ 6] = v5; newDst[ 7] = 0; + newDst[8] = v6; newDst[9] = v7; newDst[10] = v8; newDst[11] = 0; - return dst; + return newDst; } /** @@ -187,12 +137,12 @@ export function set( * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat3 made from m4 */ -export function fromMat4(m4: Mat4, dst?: Mat3): Mat3 { - dst = dst || newMat3(); - dst[0] = m4[0]; dst[1] = m4[1]; dst[ 2] = m4[ 2]; dst[ 3] = 0; - dst[4] = m4[4]; dst[5] = m4[5]; dst[ 6] = m4[ 6]; dst[ 7] = 0; - dst[8] = m4[8]; dst[9] = m4[9]; dst[10] = m4[10]; dst[11] = 0; - return dst; +function fromMat4(m4: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + newDst[0] = m4[0]; newDst[1] = m4[1]; newDst[ 2] = m4[ 2]; newDst[ 3] = 0; + newDst[4] = m4[4]; newDst[5] = m4[5]; newDst[ 6] = m4[ 6]; newDst[ 7] = 0; + newDst[8] = m4[8]; newDst[9] = m4[9]; newDst[10] = m4[10]; newDst[11] = 0; + return newDst; } /** @@ -201,8 +151,8 @@ export function fromMat4(m4: Mat4, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat3 made from q */ -export function fromQuat(q: Quat, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function fromQuat(q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const x = q[0]; const y = q[1]; const z = q[2]; const w = q[3]; const x2 = x + x; const y2 = y + y; const z2 = z + z; @@ -217,11 +167,11 @@ export function fromQuat(q: Quat, dst?: Mat3): Mat3 { const wy = w * y2; const wz = w * z2; - dst[ 0] = 1 - yy - zz; dst[ 1] = yx + wz; dst[ 2] = zx - wy; dst[ 3] = 0; - dst[ 4] = yx - wz; dst[ 5] = 1 - xx - zz; dst[ 6] = zy + wx; dst[ 7] = 0; - dst[ 8] = zx + wy; dst[ 9] = zy - wx; dst[10] = 1 - xx - yy; dst[11] = 0; + newDst[ 0] = 1 - yy - zz; newDst[ 1] = yx + wz; newDst[ 2] = zx - wy; newDst[ 3] = 0; + newDst[ 4] = yx - wz; newDst[ 5] = 1 - xx - zz; newDst[ 6] = zy + wx; newDst[ 7] = 0; + newDst[ 8] = zx + wy; newDst[ 9] = zy - wx; newDst[10] = 1 - xx - yy; newDst[11] = 0; - return dst; + return newDst; } /** @@ -230,14 +180,14 @@ export function fromQuat(q: Quat, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns -m. */ -export function negate(m: Mat3, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function negate(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = -m[ 0]; dst[ 1] = -m[ 1]; dst[ 2] = -m[ 2]; - dst[ 4] = -m[ 4]; dst[ 5] = -m[ 5]; dst[ 6] = -m[ 6]; - dst[ 8] = -m[ 8]; dst[ 9] = -m[ 9]; dst[10] = -m[10]; + newDst[ 0] = -m[ 0]; newDst[ 1] = -m[ 1]; newDst[ 2] = -m[ 2]; + newDst[ 4] = -m[ 4]; newDst[ 5] = -m[ 5]; newDst[ 6] = -m[ 6]; + newDst[ 8] = -m[ 8]; newDst[ 9] = -m[ 9]; newDst[10] = -m[10]; - return dst; + return newDst; } /** @@ -247,14 +197,14 @@ export function negate(m: Mat3, dst?: Mat3): Mat3 { * @param dst - The matrix. If not passed a new one is created. * @returns A copy of m. */ -export function copy(m: Mat3, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function copy(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = m[ 0]; dst[ 1] = m[ 1]; dst[ 2] = m[ 2]; - dst[ 4] = m[ 4]; dst[ 5] = m[ 5]; dst[ 6] = m[ 6]; - dst[ 8] = m[ 8]; dst[ 9] = m[ 9]; dst[10] = m[10]; + newDst[ 0] = m[ 0]; newDst[ 1] = m[ 1]; newDst[ 2] = m[ 2]; + newDst[ 4] = m[ 4]; newDst[ 5] = m[ 5]; newDst[ 6] = m[ 6]; + newDst[ 8] = m[ 8]; newDst[ 9] = m[ 9]; newDst[10] = m[10]; - return dst; + return newDst; } /** @@ -264,7 +214,7 @@ export function copy(m: Mat3, dst?: Mat3): Mat3 { * @param dst - The matrix. If not passed a new one is created. * @returns A copy of m. */ -export const clone = copy; +const clone = copy; /** * Check if 2 matrices are approximately equal @@ -272,7 +222,7 @@ export const clone = copy; * @param b Operand matrix. * @returns true if matrices are approximately equal */ -export function equalsApproximately(a: Mat3, b: Mat3): boolean { +function equalsApproximately(a: Mat3Arg, b: Mat3Arg): boolean { return Math.abs(a[ 0] - b[ 0]) < utils.EPSILON && Math.abs(a[ 1] - b[ 1]) < utils.EPSILON && Math.abs(a[ 2] - b[ 2]) < utils.EPSILON && @@ -290,7 +240,7 @@ export function equalsApproximately(a: Mat3, b: Mat3): boolean { * @param b Operand matrix. * @returns true if matrices are exactly equal */ -export function equals(a: Mat3, b: Mat3): boolean { +function equals(a: Mat3Arg, b: Mat3Arg): boolean { return a[ 0] === b[ 0] && a[ 1] === b[ 1] && a[ 2] === b[ 2] && @@ -308,14 +258,14 @@ export function equals(a: Mat3, b: Mat3): boolean { * @param dst - matrix to hold result. If not passed a new one is created. * @returns A 3-by-3 identity matrix. */ -export function identity(dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function identity(dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; - dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; - return dst; + return newDst; } /** @@ -324,9 +274,9 @@ export function identity(dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The transpose of m. */ -export function transpose(m: Mat3, dst?: Mat3): Mat3 { - dst = dst || newMat3(); - if (dst === m) { +function transpose(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + if (newDst === m) { let t: number; // 0 1 2 @@ -345,7 +295,7 @@ export function transpose(m: Mat3, dst?: Mat3): Mat3 { m[6] = m[9]; m[9] = t; - return dst; + return newDst; } const m00 = m[0 * 4 + 0]; @@ -358,11 +308,11 @@ export function transpose(m: Mat3, dst?: Mat3): Mat3 { const m21 = m[2 * 4 + 1]; const m22 = m[2 * 4 + 2]; - dst[ 0] = m00; dst[ 1] = m10; dst[ 2] = m20; - dst[ 4] = m01; dst[ 5] = m11; dst[ 6] = m21; - dst[ 8] = m02; dst[ 9] = m12; dst[10] = m22; + newDst[ 0] = m00; newDst[ 1] = m10; newDst[ 2] = m20; + newDst[ 4] = m01; newDst[ 5] = m11; newDst[ 6] = m21; + newDst[ 8] = m02; newDst[ 9] = m12; newDst[10] = m22; - return dst; + return newDst; } /** @@ -371,8 +321,8 @@ export function transpose(m: Mat3, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The inverse of m. */ -export function inverse(m: Mat3, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function inverse(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; @@ -390,17 +340,17 @@ export function inverse(m: Mat3, dst?: Mat3): Mat3 { const invDet = 1 / (m00 * b01 + m01 * b11 + m02 * b21); - dst[ 0] = b01 * invDet; - dst[ 1] = (-m22 * m01 + m02 * m21) * invDet; - dst[ 2] = ( m12 * m01 - m02 * m11) * invDet; - dst[ 4] = b11 * invDet; - dst[ 5] = ( m22 * m00 - m02 * m20) * invDet; - dst[ 6] = (-m12 * m00 + m02 * m10) * invDet; - dst[ 8] = b21 * invDet; - dst[ 9] = (-m21 * m00 + m01 * m20) * invDet; - dst[10] = ( m11 * m00 - m01 * m10) * invDet; - - return dst; + newDst[ 0] = b01 * invDet; + newDst[ 1] = (-m22 * m01 + m02 * m21) * invDet; + newDst[ 2] = ( m12 * m01 - m02 * m11) * invDet; + newDst[ 4] = b11 * invDet; + newDst[ 5] = ( m22 * m00 - m02 * m20) * invDet; + newDst[ 6] = (-m12 * m00 + m02 * m10) * invDet; + newDst[ 8] = b21 * invDet; + newDst[ 9] = (-m21 * m00 + m01 * m20) * invDet; + newDst[10] = ( m11 * m00 - m01 * m10) * invDet; + + return newDst; } /** @@ -408,7 +358,7 @@ export function inverse(m: Mat3, dst?: Mat3): Mat3 { * @param m - the matrix * @returns the determinant */ -export function determinant(m: Mat3): number { +function determinant(m: Mat3Arg): number { const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; const m02 = m[0 * 4 + 2]; @@ -430,7 +380,7 @@ export function determinant(m: Mat3): number { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The inverse of m. */ -export const invert = inverse; +const invert = inverse; /** * Multiplies two 3-by-3 matrices with a on the left and b on the right @@ -439,8 +389,8 @@ export const invert = inverse; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix product of a and b. */ -export function multiply(a: Mat3, b: Mat3, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function multiply(a: Mat3Arg, b: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const a00 = a[0]; const a01 = a[1]; @@ -461,17 +411,17 @@ export function multiply(a: Mat3, b: Mat3, dst?: Mat3): Mat3 { const b21 = b[ 8 + 1]; const b22 = b[ 8 + 2]; - dst[ 0] = a00 * b00 + a10 * b01 + a20 * b02; - dst[ 1] = a01 * b00 + a11 * b01 + a21 * b02; - dst[ 2] = a02 * b00 + a12 * b01 + a22 * b02; - dst[ 4] = a00 * b10 + a10 * b11 + a20 * b12; - dst[ 5] = a01 * b10 + a11 * b11 + a21 * b12; - dst[ 6] = a02 * b10 + a12 * b11 + a22 * b12; - dst[ 8] = a00 * b20 + a10 * b21 + a20 * b22; - dst[ 9] = a01 * b20 + a11 * b21 + a21 * b22; - dst[10] = a02 * b20 + a12 * b21 + a22 * b22; - - return dst; + newDst[ 0] = a00 * b00 + a10 * b01 + a20 * b02; + newDst[ 1] = a01 * b00 + a11 * b01 + a21 * b02; + newDst[ 2] = a02 * b00 + a12 * b01 + a22 * b02; + newDst[ 4] = a00 * b10 + a10 * b11 + a20 * b12; + newDst[ 5] = a01 * b10 + a11 * b11 + a21 * b12; + newDst[ 6] = a02 * b10 + a12 * b11 + a22 * b12; + newDst[ 8] = a00 * b20 + a10 * b21 + a20 * b22; + newDst[ 9] = a01 * b20 + a11 * b21 + a21 * b22; + newDst[10] = a02 * b20 + a12 * b21 + a22 * b22; + + return newDst; } /** @@ -481,7 +431,7 @@ export function multiply(a: Mat3, b: Mat3, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix product of a and b. */ -export const mul = multiply; +const mul = multiply; /** * Sets the translation component of a 3-by-3 matrix to the given @@ -491,20 +441,20 @@ export const mul = multiply; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix with translation set. */ -export function setTranslation(a: Mat3, v: Vec2, dst?: Mat3): Mat3 { - dst = dst || identity(); - if (a !== dst) { - dst[ 0] = a[ 0]; - dst[ 1] = a[ 1]; - dst[ 2] = a[ 2]; - dst[ 4] = a[ 4]; - dst[ 5] = a[ 5]; - dst[ 6] = a[ 6]; +function setTranslation(a: Mat3Arg, v: Vec2Arg, dst?: T) { + const newDst = (dst ?? identity()) as T; + if (a !== newDst) { + newDst[ 0] = a[ 0]; + newDst[ 1] = a[ 1]; + newDst[ 2] = a[ 2]; + newDst[ 4] = a[ 4]; + newDst[ 5] = a[ 5]; + newDst[ 6] = a[ 6]; } - dst[ 8] = v[0]; - dst[ 9] = v[1]; - dst[10] = 1; - return dst; + newDst[ 8] = v[0]; + newDst[ 9] = v[1]; + newDst[10] = 1; + return newDst; } /** @@ -514,11 +464,11 @@ export function setTranslation(a: Mat3, v: Vec2, dst?: Mat3): Mat3 { * @param dst - vector to hold result. If not passed a new one is created. * @returns The translation component of m. */ -export function getTranslation(m: Mat3, dst?: Vec2): Vec2 { - dst = dst || vec2.create(); - dst[0] = m[8]; - dst[1] = m[9]; - return dst; +function getTranslation(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? vec2.create()) as T; + newDst[0] = m[8]; + newDst[1] = m[9]; + return newDst; } /** @@ -527,12 +477,12 @@ export function getTranslation(m: Mat3, dst?: Vec2): Vec2 { * @param axis - The axis 0 = x, 1 = y, * @returns The axis component of m. */ -export function getAxis(m: Mat3, axis: number, dst?: Vec2): Vec2 { - dst = dst || vec2.create(); +function getAxis(m: Mat3Arg, axis: number, dst?: T) { + const newDst = (dst ?? vec2.create()) as T; const off = axis * 4; - dst[0] = m[off + 0]; - dst[1] = m[off + 1]; - return dst; + newDst[0] = m[off + 0]; + newDst[1] = m[off + 1]; + return newDst; } /** @@ -543,33 +493,32 @@ export function getAxis(m: Mat3, axis: number, dst?: Vec2): Vec2 { * @param dst - The matrix to set. If not passed a new one is created. * @returns The matrix with axis set. */ -export function setAxis(m: Mat3, v: Vec2, axis: number, dst?: Mat3): Mat3 { - if (dst !== m) { - dst = copy(m, dst); - } +function setAxis(m: Mat3Arg, v: Vec2Arg, axis: number, dst?: T) { + const newDst = (dst === m ? m : copy(m, dst)) as T; + const off = axis * 4; - dst[off + 0] = v[0]; - dst[off + 1] = v[1]; - return dst; + newDst[off + 0] = v[0]; + newDst[off + 1] = v[1]; + return newDst; } -/** - * Returns the scaling component of the matrix - * @param m - The Matrix - * @param dst - The vector to set. If not passed a new one is created. - */ -export function getScaling(m: Mat3, dst?: Vec2): Vec2 { - dst = dst || vec2.create(); +///** +// * Returns the scaling component of the matrix +// * @param m - The Matrix +// * @param dst - The vector to set. If not passed a new one is created. +// */ +function getScaling(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? vec2.create()); const xx = m[0]; const xy = m[1]; const yx = m[4]; const yy = m[5]; - dst[0] = Math.sqrt(xx * xx + xy * xy); - dst[1] = Math.sqrt(yx * yx + yy * yy); + newDst[0] = Math.sqrt(xx * xx + xy * xy); + newDst[1] = Math.sqrt(yx * yx + yy * yy); - return dst; + return newDst; } /** @@ -578,14 +527,14 @@ export function getScaling(m: Mat3, dst?: Vec2): Vec2 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The translation matrix. */ -export function translation(v: Vec2, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function translation(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; - dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; - dst[ 8] = v[0]; dst[ 9] = v[1]; dst[10] = 1; + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; + newDst[ 8] = v[0]; newDst[ 9] = v[1]; newDst[10] = 1; - return dst; + return newDst; } /** @@ -595,8 +544,8 @@ export function translation(v: Vec2, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The translated matrix. */ -export function translate(m: Mat3, v: Vec2, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function translate(m: Mat3Arg, v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const v0 = v[0]; const v1 = v[1]; @@ -611,20 +560,20 @@ export function translate(m: Mat3, v: Vec2, dst?: Mat3): Mat3 { const m21 = m[2 * 4 + 1]; const m22 = m[2 * 4 + 2]; - if (m !== dst) { - dst[ 0] = m00; - dst[ 1] = m01; - dst[ 2] = m02; - dst[ 4] = m10; - dst[ 5] = m11; - dst[ 6] = m12; + if (m !== newDst) { + newDst[ 0] = m00; + newDst[ 1] = m01; + newDst[ 2] = m02; + newDst[ 4] = m10; + newDst[ 5] = m11; + newDst[ 6] = m12; } - dst[ 8] = m00 * v0 + m10 * v1 + m20; - dst[ 9] = m01 * v0 + m11 * v1 + m21; - dst[10] = m02 * v0 + m12 * v1 + m22; + newDst[ 8] = m00 * v0 + m10 * v1 + m20; + newDst[ 9] = m01 * v0 + m11 * v1 + m21; + newDst[10] = m02 * v0 + m12 * v1 + m22; - return dst; + return newDst; } /** @@ -633,17 +582,17 @@ export function translate(m: Mat3, v: Vec2, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotation matrix. */ -export function rotation(angleInRadians: number, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function rotation(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c; dst[ 1] = s; dst[ 2] = 0; - dst[ 4] = -s; dst[ 5] = c; dst[ 6] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; + newDst[ 0] = c; newDst[ 1] = s; newDst[ 2] = 0; + newDst[ 4] = -s; newDst[ 5] = c; newDst[ 6] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; - return dst; + return newDst; } /** @@ -653,8 +602,8 @@ export function rotation(angleInRadians: number, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export function rotate(m: Mat3, angleInRadians: number, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function rotate(m: Mat3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; @@ -665,22 +614,22 @@ export function rotate(m: Mat3, angleInRadians: number, dst?: Mat3): Mat3 { const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c * m00 + s * m10; - dst[ 1] = c * m01 + s * m11; - dst[ 2] = c * m02 + s * m12; + newDst[ 0] = c * m00 + s * m10; + newDst[ 1] = c * m01 + s * m11; + newDst[ 2] = c * m02 + s * m12; - dst[ 4] = c * m10 - s * m00; - dst[ 5] = c * m11 - s * m01; - dst[ 6] = c * m12 - s * m02; + newDst[ 4] = c * m10 - s * m00; + newDst[ 5] = c * m11 - s * m01; + newDst[ 6] = c * m12 - s * m02; - if (m !== dst) { - dst[ 8] = m[ 8]; - dst[ 9] = m[ 9]; - dst[10] = m[10]; + if (m !== newDst) { + newDst[ 8] = m[ 8]; + newDst[ 9] = m[ 9]; + newDst[10] = m[10]; } - return dst; + return newDst; } /** @@ -692,14 +641,14 @@ export function rotate(m: Mat3, angleInRadians: number, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaling matrix. */ -export function scaling(v: Vec2, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function scaling(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = v[0]; dst[ 1] = 0; dst[ 2] = 0; - dst[ 4] = 0; dst[ 5] = v[1]; dst[ 6] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; + newDst[ 0] = v[0]; newDst[ 1] = 0; newDst[ 2] = 0; + newDst[ 4] = 0; newDst[ 5] = v[1]; newDst[ 6] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; - return dst; + return newDst; } /** @@ -712,27 +661,27 @@ export function scaling(v: Vec2, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaled matrix. */ -export function scale(m: Mat3, v: Vec2, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function scale(m: Mat3Arg, v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; const v0 = v[0]; const v1 = v[1]; - dst[ 0] = v0 * m[0 * 4 + 0]; - dst[ 1] = v0 * m[0 * 4 + 1]; - dst[ 2] = v0 * m[0 * 4 + 2]; + newDst[ 0] = v0 * m[0 * 4 + 0]; + newDst[ 1] = v0 * m[0 * 4 + 1]; + newDst[ 2] = v0 * m[0 * 4 + 2]; - dst[ 4] = v1 * m[1 * 4 + 0]; - dst[ 5] = v1 * m[1 * 4 + 1]; - dst[ 6] = v1 * m[1 * 4 + 2]; + newDst[ 4] = v1 * m[1 * 4 + 0]; + newDst[ 5] = v1 * m[1 * 4 + 1]; + newDst[ 6] = v1 * m[1 * 4 + 2]; - if (m !== dst) { - dst[ 8] = m[ 8]; - dst[ 9] = m[ 9]; - dst[10] = m[10]; + if (m !== newDst) { + newDst[ 8] = m[ 8]; + newDst[ 9] = m[ 9]; + newDst[10] = m[10]; } - return dst; + return newDst; } /** @@ -741,14 +690,14 @@ export function scale(m: Mat3, v: Vec2, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaling matrix. */ -export function uniformScaling(s: number, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function uniformScaling(s: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = s; dst[ 1] = 0; dst[ 2] = 0; - dst[ 4] = 0; dst[ 5] = s; dst[ 6] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; + newDst[ 0] = s; newDst[ 1] = 0; newDst[ 2] = 0; + newDst[ 4] = 0; newDst[ 5] = s; newDst[ 6] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; - return dst; + return newDst; } /** @@ -759,22 +708,69 @@ export function uniformScaling(s: number, dst?: Mat3): Mat3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaled matrix. */ -export function uniformScale(m: Mat3, s: number, dst?: Mat3): Mat3 { - dst = dst || newMat3(); +function uniformScale(m: Mat3Arg, s: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; - dst[ 0] = s * m[0 * 4 + 0]; - dst[ 1] = s * m[0 * 4 + 1]; - dst[ 2] = s * m[0 * 4 + 2]; + newDst[ 0] = s * m[0 * 4 + 0]; + newDst[ 1] = s * m[0 * 4 + 1]; + newDst[ 2] = s * m[0 * 4 + 2]; - dst[ 4] = s * m[1 * 4 + 0]; - dst[ 5] = s * m[1 * 4 + 1]; - dst[ 6] = s * m[1 * 4 + 2]; + newDst[ 4] = s * m[1 * 4 + 0]; + newDst[ 5] = s * m[1 * 4 + 1]; + newDst[ 6] = s * m[1 * 4 + 2]; - if (m !== dst) { - dst[ 8] = m[ 8]; - dst[ 9] = m[ 9]; - dst[10] = m[10]; + if (m !== newDst) { + newDst[ 8] = m[ 8]; + newDst[ 9] = m[ 9]; + newDst[10] = m[10]; } - return dst; + return newDst; +} + +return { + clone, + create, + set, + fromMat4, + fromQuat, + negate, + copy, + equalsApproximately, + equals, + identity, + transpose, + inverse, + invert, + determinant, + mul, + multiply, + setTranslation, + getTranslation, + getAxis, + setAxis, + getScaling, + translation, + translate, + rotation, + rotate, + scaling, + scale, + uniformScaling, + uniformScale, +}; + +} + +type API = ReturnType>; + +const cache = new Map(); + +export function getAPI(Ctor: Mat3Ctor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; } diff --git a/src/mat3.ts b/src/mat3.ts index d9a68fd..86b7b49 100644 --- a/src/mat3.ts +++ b/src/mat3.ts @@ -20,9 +20,15 @@ * DEALINGS IN THE SOFTWARE. */ +import { BaseArgType } from "./types"; + /** * A JavaScript array with 12 values, a Float32Array with 12 values, or a Float64Array with 12 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link mat3.setDefaultType}. */ -export type Mat3 = number[] | Float32Array | Float64Array; +export type Mat3Arg = BaseArgType; + +/** + * A specific concrete 3x3 element vector. + */ +export type Mat3Type = T; + diff --git a/src/mat4-impl.ts b/src/mat4-impl.ts index a5c4a2e..6822b02 100644 --- a/src/mat4-impl.ts +++ b/src/mat4-impl.ts @@ -1,18 +1,25 @@ - -import { Mat3 } from './mat3'; -import { Mat4 } from './mat4'; -import { Quat } from './quat'; -import Vec3, * as vec3 from './vec3-impl'; +import { Mat4Arg, Mat4Type } from './mat4'; +import { Mat3Arg } from './mat3'; +import { QuatArg } from './quat'; +import { Vec3Arg } from './vec3'; +import { getAPI as getVec3API } from './vec3-impl'; +import { BaseArgType } from './types'; import * as utils from './utils'; -export default Mat4; +export { Mat4Arg, Mat4Type }; + +type Mat4Ctor = new (n: number) => T; -export type Mat4LikeCtor = new (n: number) => Mat4; +/** + * Generates a typed API for Mat4 + * */ +function getAPIImpl(Ctor: Mat4Ctor) { + const vec3 = getVec3API(Ctor); /** * 4x4 Matrix math math functions. * - * Almost all functions take an optional `dst` argument. If it is not passed in the + * Almost all functions take an optional `newDst` argument. If it is not passed in the * functions will create a new matrix. In other words you can do this * * const mat = mat4.translation([1, 2, 3]); // Creates a new translation matrix @@ -32,18 +39,7 @@ export type Mat4LikeCtor = new (n: number) => Mat4; * mat4.multiply(mat, trans, mat); // Multiplies mat * trans and puts result in mat. * */ -let MatType: Mat4LikeCtor = Float32Array; -/** - * Sets the type this library creates for a Mat4 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Mat4 - */ -export function setDefaultType(ctor: new (n: number) => Mat4) { - const oldType = MatType; - MatType = ctor; - return oldType; -} /** * Create a Mat4 from values @@ -57,17 +53,6 @@ export function setDefaultType(ctor: new (n: number) => Mat4) { * const m = mat4.clone(someJSArray); * ``` * - * Note: a consequence of the implementation is if your Mat4Type = `Array` - * instead of `Float32Array` or `Float64Array` then any values you - * don't pass in will be undefined. Usually this is not an issue since - * (a) using `Array` is rare and (b) using `mat4.create` is usually used - * to create a Mat4 to be filled out as in - * - * ``` - * const m = mat4.create(); - * mat4.perspective(fov, aspect, near, far, m); - * ``` - * * @param v0 - value for element 0 * @param v1 - value for element 1 * @param v2 - value for element 2 @@ -86,44 +71,44 @@ export function setDefaultType(ctor: new (n: number) => Mat4) { * @param v15 - value for element 15 * @returns created from values. */ -export function create( +function create( v0?: number, v1?: number, v2?: number, v3?: number, v4?: number, v5?: number, v6?: number, v7?: number, v8?: number, v9?: number, v10?: number, v11?: number, - v12?: number, v13?: number, v14?: number, v15?: number): Mat4 { - const dst = new MatType(16); + v12?: number, v13?: number, v14?: number, v15?: number) { + const newDst = new Ctor(16); if (v0 !== undefined) { - dst[0] = v0; + newDst[0] = v0; if (v1 !== undefined) { - dst[1] = v1; + newDst[1] = v1; if (v2 !== undefined) { - dst[2] = v2; + newDst[2] = v2; if (v3 !== undefined) { - dst[3] = v3; + newDst[3] = v3; if (v4 !== undefined) { - dst[4] = v4; + newDst[4] = v4; if (v5 !== undefined) { - dst[5] = v5; + newDst[5] = v5; if (v6 !== undefined) { - dst[6] = v6; + newDst[6] = v6; if (v7 !== undefined) { - dst[7] = v7; + newDst[7] = v7; if (v8 !== undefined) { - dst[8] = v8; + newDst[8] = v8; if (v9 !== undefined) { - dst[9] = v9; + newDst[9] = v9; if (v10 !== undefined) { - dst[10] = v10; + newDst[10] = v10; if (v11 !== undefined) { - dst[11] = v11; + newDst[11] = v11; if (v12 !== undefined) { - dst[12] = v12; + newDst[12] = v12; if (v13 !== undefined) { - dst[13] = v13; + newDst[13] = v13; if (v14 !== undefined) { - dst[14] = v14; + newDst[14] = v14; if (v15 !== undefined) { - dst[15] = v15; + newDst[15] = v15; } } } @@ -140,7 +125,7 @@ export function create( } } } - return dst; + return newDst; } /** @@ -166,20 +151,20 @@ export function create( * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat4 created from values. */ -export function set( +function set( v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, - dst?: Mat4): Mat4 { - dst = dst || new MatType(16); + dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = v0; dst[ 1] = v1; dst[ 2] = v2; dst[ 3] = v3; - dst[ 4] = v4; dst[ 5] = v5; dst[ 6] = v6; dst[ 7] = v7; - dst[ 8] = v8; dst[ 9] = v9; dst[10] = v10; dst[11] = v11; - dst[12] = v12; dst[13] = v13; dst[14] = v14; dst[15] = v15; + newDst[ 0] = v0; newDst[ 1] = v1; newDst[ 2] = v2; newDst[ 3] = v3; + newDst[ 4] = v4; newDst[ 5] = v5; newDst[ 6] = v6; newDst[ 7] = v7; + newDst[ 8] = v8; newDst[ 9] = v9; newDst[10] = v10; newDst[11] = v11; + newDst[12] = v12; newDst[13] = v13; newDst[14] = v14; newDst[15] = v15; - return dst; + return newDst; } /** @@ -188,15 +173,15 @@ export function set( * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat4 made from m3 */ -export function fromMat3(m3: Mat3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function fromMat3(m3: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = m3[0]; dst[ 1] = m3[1]; dst[ 2] = m3[ 2]; dst[ 3] = 0; - dst[ 4] = m3[4]; dst[ 5] = m3[5]; dst[ 6] = m3[ 6]; dst[ 7] = 0; - dst[ 8] = m3[8]; dst[ 9] = m3[9]; dst[10] = m3[10]; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = m3[0]; newDst[ 1] = m3[1]; newDst[ 2] = m3[ 2]; newDst[ 3] = 0; + newDst[ 4] = m3[4]; newDst[ 5] = m3[5]; newDst[ 6] = m3[ 6]; newDst[ 7] = 0; + newDst[ 8] = m3[8]; newDst[ 9] = m3[9]; newDst[10] = m3[10]; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -205,8 +190,8 @@ export function fromMat3(m3: Mat3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns Mat4 made from q */ -export function fromQuat(q: Quat, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function fromQuat(q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const x = q[0]; const y = q[1]; const z = q[2]; const w = q[3]; const x2 = x + x; const y2 = y + y; const z2 = z + z; @@ -221,12 +206,12 @@ export function fromQuat(q: Quat, dst?: Mat4): Mat4 { const wy = w * y2; const wz = w * z2; - dst[ 0] = 1 - yy - zz; dst[ 1] = yx + wz; dst[ 2] = zx - wy; dst[ 3] = 0; - dst[ 4] = yx - wz; dst[ 5] = 1 - xx - zz; dst[ 6] = zy + wx; dst[ 7] = 0; - dst[ 8] = zx + wy; dst[ 9] = zy - wx; dst[10] = 1 - xx - yy; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = 1 - yy - zz; newDst[ 1] = yx + wz; newDst[ 2] = zx - wy; newDst[ 3] = 0; + newDst[ 4] = yx - wz; newDst[ 5] = 1 - xx - zz; newDst[ 6] = zy + wx; newDst[ 7] = 0; + newDst[ 8] = zx + wy; newDst[ 9] = zy - wx; newDst[10] = 1 - xx - yy; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -235,15 +220,15 @@ export function fromQuat(q: Quat, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns -m. */ -export function negate(m: Mat4, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function negate(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = -m[ 0]; dst[ 1] = -m[ 1]; dst[ 2] = -m[ 2]; dst[ 3] = -m[ 3]; - dst[ 4] = -m[ 4]; dst[ 5] = -m[ 5]; dst[ 6] = -m[ 6]; dst[ 7] = -m[ 7]; - dst[ 8] = -m[ 8]; dst[ 9] = -m[ 9]; dst[10] = -m[10]; dst[11] = -m[11]; - dst[12] = -m[12]; dst[13] = -m[13]; dst[14] = -m[14]; dst[15] = -m[15]; + newDst[ 0] = -m[ 0]; newDst[ 1] = -m[ 1]; newDst[ 2] = -m[ 2]; newDst[ 3] = -m[ 3]; + newDst[ 4] = -m[ 4]; newDst[ 5] = -m[ 5]; newDst[ 6] = -m[ 6]; newDst[ 7] = -m[ 7]; + newDst[ 8] = -m[ 8]; newDst[ 9] = -m[ 9]; newDst[10] = -m[10]; newDst[11] = -m[11]; + newDst[12] = -m[12]; newDst[13] = -m[13]; newDst[14] = -m[14]; newDst[15] = -m[15]; - return dst; + return newDst; } /** @@ -253,15 +238,15 @@ export function negate(m: Mat4, dst?: Mat4): Mat4 { * @param dst - The matrix. If not passed a new one is created. * @returns A copy of m. */ -export function copy(m: Mat4, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function copy(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = m[ 0]; dst[ 1] = m[ 1]; dst[ 2] = m[ 2]; dst[ 3] = m[ 3]; - dst[ 4] = m[ 4]; dst[ 5] = m[ 5]; dst[ 6] = m[ 6]; dst[ 7] = m[ 7]; - dst[ 8] = m[ 8]; dst[ 9] = m[ 9]; dst[10] = m[10]; dst[11] = m[11]; - dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; + newDst[ 0] = m[ 0]; newDst[ 1] = m[ 1]; newDst[ 2] = m[ 2]; newDst[ 3] = m[ 3]; + newDst[ 4] = m[ 4]; newDst[ 5] = m[ 5]; newDst[ 6] = m[ 6]; newDst[ 7] = m[ 7]; + newDst[ 8] = m[ 8]; newDst[ 9] = m[ 9]; newDst[10] = m[10]; newDst[11] = m[11]; + newDst[12] = m[12]; newDst[13] = m[13]; newDst[14] = m[14]; newDst[15] = m[15]; - return dst; + return newDst; } /** @@ -271,7 +256,7 @@ export function copy(m: Mat4, dst?: Mat4): Mat4 { * @param dst - The matrix. If not passed a new one is created. * @returns A copy of m. */ -export const clone = copy; +const clone = copy; /** * Check if 2 matrices are approximately equal @@ -279,7 +264,7 @@ export const clone = copy; * @param b - Operand matrix. * @returns true if matrices are approximately equal */ -export function equalsApproximately(a: Mat4, b: Mat4): boolean { +function equalsApproximately(a: Mat4Arg, b: Mat4Arg): boolean { return Math.abs(a[ 0] - b[ 0]) < utils.EPSILON && Math.abs(a[ 1] - b[ 1]) < utils.EPSILON && Math.abs(a[ 2] - b[ 2]) < utils.EPSILON && @@ -304,7 +289,7 @@ export function equalsApproximately(a: Mat4, b: Mat4): boolean { * @param b - Operand matrix. * @returns true if matrices are exactly equal */ -export function equals(a: Mat4, b: Mat4): boolean { +function equals(a: Mat4Arg, b: Mat4Arg): boolean { return a[ 0] === b[ 0] && a[ 1] === b[ 1] && a[ 2] === b[ 2] && @@ -329,15 +314,15 @@ export function equals(a: Mat4, b: Mat4): boolean { * @param dst - matrix to hold result. If not passed a new one is created. * @returns A 4-by-4 identity matrix. */ -export function identity(dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function identity(dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -346,9 +331,9 @@ export function identity(dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The transpose of m. */ -export function transpose(m: Mat4, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - if (dst === m) { +function transpose(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; + if (newDst === m) { let t; t = m[1]; @@ -374,7 +359,7 @@ export function transpose(m: Mat4, dst?: Mat4): Mat4 { t = m[11]; m[11] = m[14]; m[14] = t; - return dst; + return newDst; } const m00 = m[0 * 4 + 0]; @@ -394,12 +379,12 @@ export function transpose(m: Mat4, dst?: Mat4): Mat4 { const m32 = m[3 * 4 + 2]; const m33 = m[3 * 4 + 3]; - dst[ 0] = m00; dst[ 1] = m10; dst[ 2] = m20; dst[ 3] = m30; - dst[ 4] = m01; dst[ 5] = m11; dst[ 6] = m21; dst[ 7] = m31; - dst[ 8] = m02; dst[ 9] = m12; dst[10] = m22; dst[11] = m32; - dst[12] = m03; dst[13] = m13; dst[14] = m23; dst[15] = m33; + newDst[ 0] = m00; newDst[ 1] = m10; newDst[ 2] = m20; newDst[ 3] = m30; + newDst[ 4] = m01; newDst[ 5] = m11; newDst[ 6] = m21; newDst[ 7] = m31; + newDst[ 8] = m02; newDst[ 9] = m12; newDst[10] = m22; newDst[11] = m32; + newDst[12] = m03; newDst[13] = m13; newDst[14] = m23; newDst[15] = m33; - return dst; + return newDst; } /** @@ -408,8 +393,8 @@ export function transpose(m: Mat4, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The inverse of m. */ -export function inverse(m: Mat4, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function inverse(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; @@ -463,36 +448,36 @@ export function inverse(m: Mat4, dst?: Mat4): Mat4 { const d = 1 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); - dst[ 0] = d * t0; - dst[ 1] = d * t1; - dst[ 2] = d * t2; - dst[ 3] = d * t3; - dst[ 4] = d * ((tmp1 * m10 + tmp2 * m20 + tmp5 * m30) - + newDst[ 0] = d * t0; + newDst[ 1] = d * t1; + newDst[ 2] = d * t2; + newDst[ 3] = d * t3; + newDst[ 4] = d * ((tmp1 * m10 + tmp2 * m20 + tmp5 * m30) - (tmp0 * m10 + tmp3 * m20 + tmp4 * m30)); - dst[ 5] = d * ((tmp0 * m00 + tmp7 * m20 + tmp8 * m30) - + newDst[ 5] = d * ((tmp0 * m00 + tmp7 * m20 + tmp8 * m30) - (tmp1 * m00 + tmp6 * m20 + tmp9 * m30)); - dst[ 6] = d * ((tmp3 * m00 + tmp6 * m10 + tmp11 * m30) - + newDst[ 6] = d * ((tmp3 * m00 + tmp6 * m10 + tmp11 * m30) - (tmp2 * m00 + tmp7 * m10 + tmp10 * m30)); - dst[ 7] = d * ((tmp4 * m00 + tmp9 * m10 + tmp10 * m20) - + newDst[ 7] = d * ((tmp4 * m00 + tmp9 * m10 + tmp10 * m20) - (tmp5 * m00 + tmp8 * m10 + tmp11 * m20)); - dst[ 8] = d * ((tmp12 * m13 + tmp15 * m23 + tmp16 * m33) - + newDst[ 8] = d * ((tmp12 * m13 + tmp15 * m23 + tmp16 * m33) - (tmp13 * m13 + tmp14 * m23 + tmp17 * m33)); - dst[ 9] = d * ((tmp13 * m03 + tmp18 * m23 + tmp21 * m33) - + newDst[ 9] = d * ((tmp13 * m03 + tmp18 * m23 + tmp21 * m33) - (tmp12 * m03 + tmp19 * m23 + tmp20 * m33)); - dst[10] = d * ((tmp14 * m03 + tmp19 * m13 + tmp22 * m33) - + newDst[10] = d * ((tmp14 * m03 + tmp19 * m13 + tmp22 * m33) - (tmp15 * m03 + tmp18 * m13 + tmp23 * m33)); - dst[11] = d * ((tmp17 * m03 + tmp20 * m13 + tmp23 * m23) - + newDst[11] = d * ((tmp17 * m03 + tmp20 * m13 + tmp23 * m23) - (tmp16 * m03 + tmp21 * m13 + tmp22 * m23)); - dst[12] = d * ((tmp14 * m22 + tmp17 * m32 + tmp13 * m12) - + newDst[12] = d * ((tmp14 * m22 + tmp17 * m32 + tmp13 * m12) - (tmp16 * m32 + tmp12 * m12 + tmp15 * m22)); - dst[13] = d * ((tmp20 * m32 + tmp12 * m02 + tmp19 * m22) - + newDst[13] = d * ((tmp20 * m32 + tmp12 * m02 + tmp19 * m22) - (tmp18 * m22 + tmp21 * m32 + tmp13 * m02)); - dst[14] = d * ((tmp18 * m12 + tmp23 * m32 + tmp15 * m02) - + newDst[14] = d * ((tmp18 * m12 + tmp23 * m32 + tmp15 * m02) - (tmp22 * m32 + tmp14 * m02 + tmp19 * m12)); - dst[15] = d * ((tmp22 * m22 + tmp16 * m02 + tmp21 * m12) - + newDst[15] = d * ((tmp22 * m22 + tmp16 * m02 + tmp21 * m12) - (tmp20 * m12 + tmp23 * m22 + tmp17 * m02)); - return dst; + return newDst; } /** @@ -500,7 +485,7 @@ export function inverse(m: Mat4, dst?: Mat4): Mat4 { * @param m - the matrix * @returns the determinant */ -export function determinant(m: Mat4): number { +function determinant(m: Mat4Arg): number { const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; const m02 = m[0 * 4 + 2]; @@ -549,7 +534,7 @@ export function determinant(m: Mat4): number { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The inverse of m. */ -export const invert = inverse; +const invert = inverse; /** * Multiplies two 4-by-4 matrices with a on the left and b on the right @@ -558,8 +543,8 @@ export const invert = inverse; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix product of a and b. */ -export function multiply(a: Mat4, b: Mat4, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function multiply(a: Mat4Arg, b: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const a00 = a[0]; const a01 = a[1]; @@ -594,24 +579,24 @@ export function multiply(a: Mat4, b: Mat4, dst?: Mat4): Mat4 { const b32 = b[12 + 2]; const b33 = b[12 + 3]; - dst[ 0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; - dst[ 1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; - dst[ 2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; - dst[ 3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; - dst[ 4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; - dst[ 5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; - dst[ 6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; - dst[ 7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; - dst[ 8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; - dst[ 9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; - dst[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; - dst[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; - dst[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; - dst[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; - dst[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; - dst[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; - - return dst; + newDst[ 0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; + newDst[ 1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; + newDst[ 2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; + newDst[ 3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; + newDst[ 4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; + newDst[ 5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; + newDst[ 6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; + newDst[ 7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; + newDst[ 8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; + newDst[ 9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; + newDst[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; + newDst[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; + newDst[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; + newDst[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; + newDst[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; + newDst[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; + + return newDst; } /** @@ -621,7 +606,7 @@ export function multiply(a: Mat4, b: Mat4, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix product of a and b. */ -export const mul = multiply; +const mul = multiply; /** * Sets the translation component of a 4-by-4 matrix to the given @@ -631,42 +616,42 @@ export const mul = multiply; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The matrix with translation set. */ -export function setTranslation(a: Mat4, v: Vec3, dst?: Mat4): Mat4 { - dst = dst || identity(); - if (a !== dst) { - dst[ 0] = a[ 0]; - dst[ 1] = a[ 1]; - dst[ 2] = a[ 2]; - dst[ 3] = a[ 3]; - dst[ 4] = a[ 4]; - dst[ 5] = a[ 5]; - dst[ 6] = a[ 6]; - dst[ 7] = a[ 7]; - dst[ 8] = a[ 8]; - dst[ 9] = a[ 9]; - dst[10] = a[10]; - dst[11] = a[11]; +function setTranslation(a: Mat4Arg, v: Vec3Arg, dst?: T) { + const newDst = (dst ?? identity()) as T; + if (a !== newDst) { + newDst[ 0] = a[ 0]; + newDst[ 1] = a[ 1]; + newDst[ 2] = a[ 2]; + newDst[ 3] = a[ 3]; + newDst[ 4] = a[ 4]; + newDst[ 5] = a[ 5]; + newDst[ 6] = a[ 6]; + newDst[ 7] = a[ 7]; + newDst[ 8] = a[ 8]; + newDst[ 9] = a[ 9]; + newDst[10] = a[10]; + newDst[11] = a[11]; } - dst[12] = v[0]; - dst[13] = v[1]; - dst[14] = v[2]; - dst[15] = 1; - return dst; + newDst[12] = v[0]; + newDst[13] = v[1]; + newDst[14] = v[2]; + newDst[15] = 1; + return newDst; } -/** - * Returns the translation component of a 4-by-4 matrix as a vector with 3 - * entries. - * @param m - The matrix. - * @param dst - vector to hold result. If not passed a new one is created. - * @returns The translation component of m. - */ -export function getTranslation(m: Mat4, dst?: Vec3): Vec3 { - dst = dst || vec3.create(); - dst[0] = m[12]; - dst[1] = m[13]; - dst[2] = m[14]; - return dst; +///** +// * Returns the translation component of a 4-by-4 matrix as a vector with 3 +// * entries. +// * @param m - The matrix. +// * @param dst - vector to hold result. If not passed a new one is created. +// * @returns The translation component of m. +// */ +function getTranslation(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? vec3.create()) as T; + newDst[0] = m[12]; + newDst[1] = m[13]; + newDst[2] = m[14]; + return newDst; } /** @@ -675,13 +660,13 @@ export function getTranslation(m: Mat4, dst?: Vec3): Vec3 { * @param axis - The axis 0 = x, 1 = y, 2 = z; * @returns The axis component of m. */ -export function getAxis(m: Mat4, axis: number, dst?: Vec3): Vec3 { - dst = dst || vec3.create(); +function getAxis(m: Mat4Arg, axis: number, dst?: T) { + const newDst = (dst ?? vec3.create()); const off = axis * 4; - dst[0] = m[off + 0]; - dst[1] = m[off + 1]; - dst[2] = m[off + 2]; - return dst; + newDst[0] = m[off + 0]; + newDst[1] = m[off + 1]; + newDst[2] = m[off + 2]; + return newDst; } /** @@ -692,24 +677,23 @@ export function getAxis(m: Mat4, axis: number, dst?: Vec3): Vec3 { * @param dst - The matrix to set. If not passed a new one is created. * @returns The matrix with axis set. */ -export function setAxis(m: Mat4, v: Vec3, axis: number, dst: Mat4): Mat4 { - if (dst !== m) { - dst = copy(m, dst); - } +function setAxis(m: Mat4Arg, v: Vec3Arg, axis: number, dst: T) { + const newDst = (dst === m) ? dst : copy(m, dst); + const off = axis * 4; - dst[off + 0] = v[0]; - dst[off + 1] = v[1]; - dst[off + 2] = v[2]; - return dst; + newDst[off + 0] = v[0]; + newDst[off + 1] = v[1]; + newDst[off + 2] = v[2]; + return newDst; } -/** - * Returns the scaling component of the matrix - * @param m - The Matrix - * @param dst - The vector to set. If not passed a new one is created. - */ -export function getScaling(m: Mat4, dst?: Vec3): Vec3 { - dst = dst || vec3.create(); +///** +// * Returns the scaling component of the matrix +// * @param m - The Matrix +// * @param dst - The vector to set. If not passed a new one is created. +// */ +function getScaling(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? vec3.create()) as T; const xx = m[0]; const xy = m[1]; @@ -721,11 +705,11 @@ export function getScaling(m: Mat4, dst?: Vec3): Vec3 { const zy = m[9]; const zz = m[10]; - dst[0] = Math.sqrt(xx * xx + xy * xy + xz * xz); - dst[1] = Math.sqrt(yx * yx + yy * yy + yz * yz); - dst[2] = Math.sqrt(zx * zx + zy * zy + zz * zz); + newDst[0] = Math.sqrt(xx * xx + xy * xy + xz * xz); + newDst[1] = Math.sqrt(yx * yx + yy * yy + yz * yz); + newDst[2] = Math.sqrt(zx * zx + zy * zy + zz * zz); - return dst; + return newDst; } /** @@ -753,39 +737,39 @@ export function getScaling(m: Mat4, dst?: Vec3): Vec3 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The perspective matrix. */ -export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear: number, zFar: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function perspective(fieldOfViewYInRadians: number, aspect: number, zNear: number, zFar: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewYInRadians); - dst[0] = f / aspect; - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; + newDst[0] = f / aspect; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 0; - dst[4] = 0; - dst[5] = f; - dst[6] = 0; - dst[7] = 0; + newDst[4] = 0; + newDst[5] = f; + newDst[6] = 0; + newDst[7] = 0; - dst[8] = 0; - dst[9] = 0; - dst[11] = -1; + newDst[8] = 0; + newDst[9] = 0; + newDst[11] = -1; - dst[12] = 0; - dst[13] = 0; - dst[15] = 0; + newDst[12] = 0; + newDst[13] = 0; + newDst[15] = 0; if (Number.isFinite(zFar)) { const rangeInv = 1 / (zNear - zFar); - dst[10] = zFar * rangeInv; - dst[14] = zFar * zNear * rangeInv; + newDst[10] = zFar * rangeInv; + newDst[14] = zFar * zNear * rangeInv; } else { - dst[10] = -1; - dst[14] = -zNear; + newDst[10] = -1; + newDst[14] = -zNear; } - return dst; + return newDst; } /** @@ -808,39 +792,39 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear * of the far clipping plane. (default = Infinity) * @param dst - matrix to hold result. If not passed a new one is created. * @returns The perspective matrix. - */export function perspectiveReverseZ(fieldOfViewYInRadians: number, aspect: number, zNear: number, zFar = Infinity, dst?: Mat4) { - dst = dst || new MatType(16); + */function perspectiveReverseZ(fieldOfViewYInRadians: number, aspect: number, zNear: number, zFar = Infinity, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const f = 1 / Math.tan(fieldOfViewYInRadians * 0.5); - dst[ 0] = f / aspect; - dst[ 1] = 0; - dst[ 2] = 0; - dst[ 3] = 0; + newDst[ 0] = f / aspect; + newDst[ 1] = 0; + newDst[ 2] = 0; + newDst[ 3] = 0; - dst[ 4] = 0; - dst[ 5] = f; - dst[ 6] = 0; - dst[ 7] = 0; + newDst[ 4] = 0; + newDst[ 5] = f; + newDst[ 6] = 0; + newDst[ 7] = 0; - dst[ 8] = 0; - dst[ 9] = 0; - dst[11] = -1; + newDst[ 8] = 0; + newDst[ 9] = 0; + newDst[11] = -1; - dst[12] = 0; - dst[13] = 0; - dst[15] = 0; + newDst[12] = 0; + newDst[13] = 0; + newDst[15] = 0; if (zFar === Infinity) { - dst[10] = 0; - dst[14] = zNear; + newDst[10] = 0; + newDst[14] = zNear; } else { const rangeInv = 1 / (zFar - zNear); - dst[10] = zNear * rangeInv; - dst[14] = zFar * zNear * rangeInv; + newDst[10] = zNear * rangeInv; + newDst[14] = zFar * zNear * rangeInv; } - return dst; + return newDst; } /** @@ -858,30 +842,30 @@ export function perspective(fieldOfViewYInRadians: number, aspect: number, zNear * @param dst - Output matrix. If not passed a new one is created. * @returns The orthographic projection matrix. */ -export function ortho(left: number, right: number, bottom: number, top: number, near: number, far: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - - dst[0] = 2 / (right - left); - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; - - dst[4] = 0; - dst[5] = 2 / (top - bottom); - dst[6] = 0; - dst[7] = 0; - - dst[8] = 0; - dst[9] = 0; - dst[10] = 1 / (near - far); - dst[11] = 0; - - dst[12] = (right + left) / (left - right); - dst[13] = (top + bottom) / (bottom - top); - dst[14] = near / (near - far); - dst[15] = 1; - - return dst; +function ortho(left: number, right: number, bottom: number, top: number, near: number, far: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; + + newDst[0] = 2 / (right - left); + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 0; + + newDst[4] = 0; + newDst[5] = 2 / (top - bottom); + newDst[6] = 0; + newDst[7] = 0; + + newDst[8] = 0; + newDst[9] = 0; + newDst[10] = 1 / (near - far); + newDst[11] = 0; + + newDst[12] = (right + left) / (left - right); + newDst[13] = (top + bottom) / (bottom - top); + newDst[14] = near / (near - far); + newDst[15] = 1; + + return newDst; } /** @@ -902,31 +886,31 @@ export function ortho(left: number, right: number, bottom: number, top: number, * @param dst - Output matrix. If not passed a new one is created. * @returns The perspective projection matrix. */ -export function frustum(left: number, right: number, bottom: number, top: number, near: number, far: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function frustum(left: number, right: number, bottom: number, top: number, near: number, far: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const dx = (right - left); const dy = (top - bottom); const dz = (near - far); - dst[ 0] = 2 * near / dx; - dst[ 1] = 0; - dst[ 2] = 0; - dst[ 3] = 0; - dst[ 4] = 0; - dst[ 5] = 2 * near / dy; - dst[ 6] = 0; - dst[ 7] = 0; - dst[ 8] = (left + right) / dx; - dst[ 9] = (top + bottom) / dy; - dst[10] = far / dz; - dst[11] = -1; - dst[12] = 0; - dst[13] = 0; - dst[14] = near * far / dz; - dst[15] = 0; - - return dst; + newDst[ 0] = 2 * near / dx; + newDst[ 1] = 0; + newDst[ 2] = 0; + newDst[ 3] = 0; + newDst[ 4] = 0; + newDst[ 5] = 2 * near / dy; + newDst[ 6] = 0; + newDst[ 7] = 0; + newDst[ 8] = (left + right) / dx; + newDst[ 9] = (top + bottom) / dy; + newDst[10] = far / dz; + newDst[11] = -1; + newDst[12] = 0; + newDst[13] = 0; + newDst[14] = near * far / dz; + newDst[15] = 0; + + return newDst; } /** @@ -947,42 +931,42 @@ export function frustum(left: number, right: number, bottom: number, top: number * @param dst - Output matrix. If not passed a new one is created. * @returns The perspective projection matrix. */ -export function frustumReverseZ(left: number, right: number, bottom: number, top: number, near: number, far = Infinity, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function frustumReverseZ(left: number, right: number, bottom: number, top: number, near: number, far = Infinity, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const dx = (right - left); const dy = (top - bottom); - dst[ 0] = 2 * near / dx; - dst[ 1] = 0; - dst[ 2] = 0; - dst[ 3] = 0; - dst[ 4] = 0; - dst[ 5] = 2 * near / dy; - dst[ 6] = 0; - dst[ 7] = 0; - dst[ 8] = (left + right) / dx; - dst[ 9] = (top + bottom) / dy; - dst[11] = -1; - dst[12] = 0; - dst[13] = 0; - dst[15] = 0; + newDst[ 0] = 2 * near / dx; + newDst[ 1] = 0; + newDst[ 2] = 0; + newDst[ 3] = 0; + newDst[ 4] = 0; + newDst[ 5] = 2 * near / dy; + newDst[ 6] = 0; + newDst[ 7] = 0; + newDst[ 8] = (left + right) / dx; + newDst[ 9] = (top + bottom) / dy; + newDst[11] = -1; + newDst[12] = 0; + newDst[13] = 0; + newDst[15] = 0; if (far === Infinity) { - dst[10] = 0; - dst[14] = near; + newDst[10] = 0; + newDst[14] = near; } else { const rangeInv = 1 / (far - near); - dst[10] = near * rangeInv; - dst[14] = far * near * rangeInv; + newDst[10] = near * rangeInv; + newDst[14] = far * near * rangeInv; } - return dst; + return newDst; } -let xAxis: Vec3; -let yAxis: Vec3; -let zAxis: Vec3; +const xAxis = vec3.create(); +const yAxis = vec3.create(); +const zAxis = vec3.create(); /** * Computes a 4-by-4 aim transformation. @@ -998,23 +982,19 @@ let zAxis: Vec3; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The aim matrix. */ -export function aim(position: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - - xAxis = xAxis || vec3.create(); - yAxis = yAxis || vec3.create(); - zAxis = zAxis || vec3.create(); +function aim(position: Vec3Arg, target: Vec3Arg, up: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; vec3.normalize(vec3.subtract(target, position, zAxis), zAxis); vec3.normalize(vec3.cross(up, zAxis, xAxis), xAxis); vec3.normalize(vec3.cross(zAxis, xAxis, yAxis), yAxis); - dst[ 0] = xAxis[0]; dst[ 1] = xAxis[1]; dst[ 2] = xAxis[2]; dst[ 3] = 0; - dst[ 4] = yAxis[0]; dst[ 5] = yAxis[1]; dst[ 6] = yAxis[2]; dst[ 7] = 0; - dst[ 8] = zAxis[0]; dst[ 9] = zAxis[1]; dst[10] = zAxis[2]; dst[11] = 0; - dst[12] = position[0]; dst[13] = position[1]; dst[14] = position[2]; dst[15] = 1; + newDst[ 0] = xAxis[0]; newDst[ 1] = xAxis[1]; newDst[ 2] = xAxis[2]; newDst[ 3] = 0; + newDst[ 4] = yAxis[0]; newDst[ 5] = yAxis[1]; newDst[ 6] = yAxis[2]; newDst[ 7] = 0; + newDst[ 8] = zAxis[0]; newDst[ 9] = zAxis[1]; newDst[10] = zAxis[2]; newDst[11] = 0; + newDst[12] = position[0]; newDst[13] = position[1]; newDst[14] = position[2]; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1031,23 +1011,19 @@ export function aim(position: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The aim matrix. */ -export function cameraAim(eye: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - - xAxis = xAxis || vec3.create(); - yAxis = yAxis || vec3.create(); - zAxis = zAxis || vec3.create(); +function cameraAim(eye: Vec3Arg, target: Vec3Arg, up: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; vec3.normalize(vec3.subtract(eye, target, zAxis), zAxis); vec3.normalize(vec3.cross(up, zAxis, xAxis), xAxis); vec3.normalize(vec3.cross(zAxis, xAxis, yAxis), yAxis); - dst[ 0] = xAxis[0]; dst[ 1] = xAxis[1]; dst[ 2] = xAxis[2]; dst[ 3] = 0; - dst[ 4] = yAxis[0]; dst[ 5] = yAxis[1]; dst[ 6] = yAxis[2]; dst[ 7] = 0; - dst[ 8] = zAxis[0]; dst[ 9] = zAxis[1]; dst[10] = zAxis[2]; dst[11] = 0; - dst[12] = eye[0]; dst[13] = eye[1]; dst[14] = eye[2]; dst[15] = 1; + newDst[ 0] = xAxis[0]; newDst[ 1] = xAxis[1]; newDst[ 2] = xAxis[2]; newDst[ 3] = 0; + newDst[ 4] = yAxis[0]; newDst[ 5] = yAxis[1]; newDst[ 6] = yAxis[2]; newDst[ 7] = 0; + newDst[ 8] = zAxis[0]; newDst[ 9] = zAxis[1]; newDst[10] = zAxis[2]; newDst[11] = 0; + newDst[12] = eye[0]; newDst[13] = eye[1]; newDst[14] = eye[2]; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1062,27 +1038,23 @@ export function cameraAim(eye: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The look-at matrix. */ -export function lookAt(eye: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - - xAxis = xAxis || vec3.create(); - yAxis = yAxis || vec3.create(); - zAxis = zAxis || vec3.create(); +function lookAt(eye: Vec3Arg, target: Vec3Arg, up: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; vec3.normalize(vec3.subtract(eye, target, zAxis), zAxis); vec3.normalize(vec3.cross(up, zAxis, xAxis), xAxis); vec3.normalize(vec3.cross(zAxis, xAxis, yAxis), yAxis); - dst[ 0] = xAxis[0]; dst[ 1] = yAxis[0]; dst[ 2] = zAxis[0]; dst[ 3] = 0; - dst[ 4] = xAxis[1]; dst[ 5] = yAxis[1]; dst[ 6] = zAxis[1]; dst[ 7] = 0; - dst[ 8] = xAxis[2]; dst[ 9] = yAxis[2]; dst[10] = zAxis[2]; dst[11] = 0; + newDst[ 0] = xAxis[0]; newDst[ 1] = yAxis[0]; newDst[ 2] = zAxis[0]; newDst[ 3] = 0; + newDst[ 4] = xAxis[1]; newDst[ 5] = yAxis[1]; newDst[ 6] = zAxis[1]; newDst[ 7] = 0; + newDst[ 8] = xAxis[2]; newDst[ 9] = yAxis[2]; newDst[10] = zAxis[2]; newDst[11] = 0; - dst[12] = -(xAxis[0] * eye[0] + xAxis[1] * eye[1] + xAxis[2] * eye[2]); - dst[13] = -(yAxis[0] * eye[0] + yAxis[1] * eye[1] + yAxis[2] * eye[2]); - dst[14] = -(zAxis[0] * eye[0] + zAxis[1] * eye[1] + zAxis[2] * eye[2]); - dst[15] = 1; + newDst[12] = -(xAxis[0] * eye[0] + xAxis[1] * eye[1] + xAxis[2] * eye[2]); + newDst[13] = -(yAxis[0] * eye[0] + yAxis[1] * eye[1] + yAxis[2] * eye[2]); + newDst[14] = -(zAxis[0] * eye[0] + zAxis[1] * eye[1] + zAxis[2] * eye[2]); + newDst[15] = 1; - return dst; + return newDst; } /** @@ -1092,15 +1064,15 @@ export function lookAt(eye: Vec3, target: Vec3, up: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The translation matrix. */ -export function translation(v: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function translation(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; - dst[12] = v[0]; dst[13] = v[1]; dst[14] = v[2]; dst[15] = 1; + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; newDst[11] = 0; + newDst[12] = v[0]; newDst[13] = v[1]; newDst[14] = v[2]; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1111,8 +1083,8 @@ export function translation(v: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The translated matrix. */ -export function translate(m: Mat4, v: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function translate(m: Mat4Arg, v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const v0 = v[0]; const v1 = v[1]; @@ -1134,27 +1106,27 @@ export function translate(m: Mat4, v: Vec3, dst?: Mat4): Mat4 { const m32 = m[3 * 4 + 2]; const m33 = m[3 * 4 + 3]; - if (m !== dst) { - dst[ 0] = m00; - dst[ 1] = m01; - dst[ 2] = m02; - dst[ 3] = m03; - dst[ 4] = m10; - dst[ 5] = m11; - dst[ 6] = m12; - dst[ 7] = m13; - dst[ 8] = m20; - dst[ 9] = m21; - dst[10] = m22; - dst[11] = m23; + if (m !== newDst) { + newDst[ 0] = m00; + newDst[ 1] = m01; + newDst[ 2] = m02; + newDst[ 3] = m03; + newDst[ 4] = m10; + newDst[ 5] = m11; + newDst[ 6] = m12; + newDst[ 7] = m13; + newDst[ 8] = m20; + newDst[ 9] = m21; + newDst[10] = m22; + newDst[11] = m23; } - dst[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30; - dst[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31; - dst[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32; - dst[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33; + newDst[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30; + newDst[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31; + newDst[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32; + newDst[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33; - return dst; + return newDst; } /** @@ -1163,18 +1135,18 @@ export function translate(m: Mat4, v: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotation matrix. */ -export function rotationX(angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotationX(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = c; dst[ 6] = s; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = -s; dst[10] = c; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = c; newDst[ 6] = s; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = -s; newDst[10] = c; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1185,8 +1157,8 @@ export function rotationX(angleInRadians: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export function rotateX(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotateX(m: Mat4Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const m10 = m[4]; const m11 = m[5]; @@ -1199,27 +1171,27 @@ export function rotateX(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[4] = c * m10 + s * m20; - dst[5] = c * m11 + s * m21; - dst[6] = c * m12 + s * m22; - dst[7] = c * m13 + s * m23; - dst[8] = c * m20 - s * m10; - dst[9] = c * m21 - s * m11; - dst[10] = c * m22 - s * m12; - dst[11] = c * m23 - s * m13; - - if (m !== dst) { - dst[ 0] = m[ 0]; - dst[ 1] = m[ 1]; - dst[ 2] = m[ 2]; - dst[ 3] = m[ 3]; - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; + newDst[4] = c * m10 + s * m20; + newDst[5] = c * m11 + s * m21; + newDst[6] = c * m12 + s * m22; + newDst[7] = c * m13 + s * m23; + newDst[8] = c * m20 - s * m10; + newDst[9] = c * m21 - s * m11; + newDst[10] = c * m22 - s * m12; + newDst[11] = c * m23 - s * m13; + + if (m !== newDst) { + newDst[ 0] = m[ 0]; + newDst[ 1] = m[ 1]; + newDst[ 2] = m[ 2]; + newDst[ 3] = m[ 3]; + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; + return newDst; } /** @@ -1228,18 +1200,18 @@ export function rotateX(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotation matrix. */ -export function rotationY(angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotationY(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c; dst[ 1] = 0; dst[ 2] = -s; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = s; dst[ 9] = 0; dst[10] = c; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = c; newDst[ 1] = 0; newDst[ 2] = -s; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = s; newDst[ 9] = 0; newDst[10] = c; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1250,8 +1222,8 @@ export function rotationY(angleInRadians: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export function rotateY(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotateY(m: Mat4Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; @@ -1264,27 +1236,27 @@ export function rotateY(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c * m00 - s * m20; - dst[ 1] = c * m01 - s * m21; - dst[ 2] = c * m02 - s * m22; - dst[ 3] = c * m03 - s * m23; - dst[ 8] = c * m20 + s * m00; - dst[ 9] = c * m21 + s * m01; - dst[10] = c * m22 + s * m02; - dst[11] = c * m23 + s * m03; - - if (m !== dst) { - dst[ 4] = m[ 4]; - dst[ 5] = m[ 5]; - dst[ 6] = m[ 6]; - dst[ 7] = m[ 7]; - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; + newDst[ 0] = c * m00 - s * m20; + newDst[ 1] = c * m01 - s * m21; + newDst[ 2] = c * m02 - s * m22; + newDst[ 3] = c * m03 - s * m23; + newDst[ 8] = c * m20 + s * m00; + newDst[ 9] = c * m21 + s * m01; + newDst[10] = c * m22 + s * m02; + newDst[11] = c * m23 + s * m03; + + if (m !== newDst) { + newDst[ 4] = m[ 4]; + newDst[ 5] = m[ 5]; + newDst[ 6] = m[ 6]; + newDst[ 7] = m[ 7]; + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; + return newDst; } /** @@ -1293,18 +1265,18 @@ export function rotateY(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotation matrix. */ -export function rotationZ(angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotationZ(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c; dst[ 1] = s; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = -s; dst[ 5] = c; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = c; newDst[ 1] = s; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = -s; newDst[ 5] = c; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = 1; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1315,8 +1287,8 @@ export function rotationZ(angleInRadians: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export function rotateZ(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function rotateZ(m: Mat4Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const m00 = m[0 * 4 + 0]; const m01 = m[0 * 4 + 1]; @@ -1329,27 +1301,27 @@ export function rotateZ(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { const c = Math.cos(angleInRadians); const s = Math.sin(angleInRadians); - dst[ 0] = c * m00 + s * m10; - dst[ 1] = c * m01 + s * m11; - dst[ 2] = c * m02 + s * m12; - dst[ 3] = c * m03 + s * m13; - dst[ 4] = c * m10 - s * m00; - dst[ 5] = c * m11 - s * m01; - dst[ 6] = c * m12 - s * m02; - dst[ 7] = c * m13 - s * m03; - - if (m !== dst) { - dst[ 8] = m[ 8]; - dst[ 9] = m[ 9]; - dst[10] = m[10]; - dst[11] = m[11]; - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; + newDst[ 0] = c * m00 + s * m10; + newDst[ 1] = c * m01 + s * m11; + newDst[ 2] = c * m02 + s * m12; + newDst[ 3] = c * m03 + s * m13; + newDst[ 4] = c * m10 - s * m00; + newDst[ 5] = c * m11 - s * m01; + newDst[ 6] = c * m12 - s * m02; + newDst[ 7] = c * m13 - s * m03; + + if (m !== newDst) { + newDst[ 8] = m[ 8]; + newDst[ 9] = m[ 9]; + newDst[10] = m[10]; + newDst[11] = m[11]; + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; + return newDst; } /** @@ -1362,8 +1334,8 @@ export function rotateZ(m: Mat4, angleInRadians: number, dst?: Mat4): Mat4 { * @returns A matrix which rotates angle radians * around the axis. */ -export function axisRotation(axis: Vec3, angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function axisRotation(axis: Vec3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; let x = axis[0]; let y = axis[1]; @@ -1379,24 +1351,24 @@ export function axisRotation(axis: Vec3, angleInRadians: number, dst?: Mat4): Ma const s = Math.sin(angleInRadians); const oneMinusCosine = 1 - c; - dst[ 0] = xx + (1 - xx) * c; - dst[ 1] = x * y * oneMinusCosine + z * s; - dst[ 2] = x * z * oneMinusCosine - y * s; - dst[ 3] = 0; - dst[ 4] = x * y * oneMinusCosine - z * s; - dst[ 5] = yy + (1 - yy) * c; - dst[ 6] = y * z * oneMinusCosine + x * s; - dst[ 7] = 0; - dst[ 8] = x * z * oneMinusCosine + y * s; - dst[ 9] = y * z * oneMinusCosine - x * s; - dst[10] = zz + (1 - zz) * c; - dst[11] = 0; - dst[12] = 0; - dst[13] = 0; - dst[14] = 0; - dst[15] = 1; - - return dst; + newDst[ 0] = xx + (1 - xx) * c; + newDst[ 1] = x * y * oneMinusCosine + z * s; + newDst[ 2] = x * z * oneMinusCosine - y * s; + newDst[ 3] = 0; + newDst[ 4] = x * y * oneMinusCosine - z * s; + newDst[ 5] = yy + (1 - yy) * c; + newDst[ 6] = y * z * oneMinusCosine + x * s; + newDst[ 7] = 0; + newDst[ 8] = x * z * oneMinusCosine + y * s; + newDst[ 9] = y * z * oneMinusCosine - x * s; + newDst[10] = zz + (1 - zz) * c; + newDst[11] = 0; + newDst[12] = 0; + newDst[13] = 0; + newDst[14] = 0; + newDst[15] = 1; + + return newDst; } /** @@ -1409,7 +1381,7 @@ export function axisRotation(axis: Vec3, angleInRadians: number, dst?: Mat4): Ma * @returns A matrix which rotates angle radians * around the axis. */ -export const rotation = axisRotation; +const rotation = axisRotation; /** * Rotates the given 4-by-4 matrix around the given axis by the @@ -1421,8 +1393,8 @@ export const rotation = axisRotation; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export function axisRotate(m: Mat4, axis: Vec3, angleInRadians: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function axisRotate(m: Mat4Arg, axis: Vec3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; let x = axis[0]; let y = axis[1]; @@ -1461,27 +1433,27 @@ export function axisRotate(m: Mat4, axis: Vec3, angleInRadians: number, dst?: Ma const m22 = m[10]; const m23 = m[11]; - dst[ 0] = r00 * m00 + r01 * m10 + r02 * m20; - dst[ 1] = r00 * m01 + r01 * m11 + r02 * m21; - dst[ 2] = r00 * m02 + r01 * m12 + r02 * m22; - dst[ 3] = r00 * m03 + r01 * m13 + r02 * m23; - dst[ 4] = r10 * m00 + r11 * m10 + r12 * m20; - dst[ 5] = r10 * m01 + r11 * m11 + r12 * m21; - dst[ 6] = r10 * m02 + r11 * m12 + r12 * m22; - dst[ 7] = r10 * m03 + r11 * m13 + r12 * m23; - dst[ 8] = r20 * m00 + r21 * m10 + r22 * m20; - dst[ 9] = r20 * m01 + r21 * m11 + r22 * m21; - dst[10] = r20 * m02 + r21 * m12 + r22 * m22; - dst[11] = r20 * m03 + r21 * m13 + r22 * m23; - - if (m !== dst) { - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; + newDst[ 0] = r00 * m00 + r01 * m10 + r02 * m20; + newDst[ 1] = r00 * m01 + r01 * m11 + r02 * m21; + newDst[ 2] = r00 * m02 + r01 * m12 + r02 * m22; + newDst[ 3] = r00 * m03 + r01 * m13 + r02 * m23; + newDst[ 4] = r10 * m00 + r11 * m10 + r12 * m20; + newDst[ 5] = r10 * m01 + r11 * m11 + r12 * m21; + newDst[ 6] = r10 * m02 + r11 * m12 + r12 * m22; + newDst[ 7] = r10 * m03 + r11 * m13 + r12 * m23; + newDst[ 8] = r20 * m00 + r21 * m10 + r22 * m20; + newDst[ 9] = r20 * m01 + r21 * m11 + r22 * m21; + newDst[10] = r20 * m02 + r21 * m12 + r22 * m22; + newDst[11] = r20 * m03 + r21 * m13 + r22 * m23; + + if (m !== newDst) { + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; + return newDst; } /** @@ -1494,7 +1466,7 @@ export function axisRotate(m: Mat4, axis: Vec3, angleInRadians: number, dst?: Ma * @param dst - matrix to hold result. If not passed a new one is created. * @returns The rotated matrix. */ -export const rotate = axisRotate; +const rotate = axisRotate; /** * Creates a 4-by-4 matrix which scales in each dimension by an amount given by @@ -1505,15 +1477,15 @@ export const rotate = axisRotate; * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaling matrix. */ -export function scaling(v: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function scaling(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = v[0]; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = v[1]; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = v[2]; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = v[0]; newDst[ 1] = 0; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = v[1]; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = v[2]; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1526,34 +1498,34 @@ export function scaling(v: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaled matrix. */ -export function scale(m: Mat4, v: Vec3, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function scale(m: Mat4Arg, v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; - dst[ 0] = v0 * m[0 * 4 + 0]; - dst[ 1] = v0 * m[0 * 4 + 1]; - dst[ 2] = v0 * m[0 * 4 + 2]; - dst[ 3] = v0 * m[0 * 4 + 3]; - dst[ 4] = v1 * m[1 * 4 + 0]; - dst[ 5] = v1 * m[1 * 4 + 1]; - dst[ 6] = v1 * m[1 * 4 + 2]; - dst[ 7] = v1 * m[1 * 4 + 3]; - dst[ 8] = v2 * m[2 * 4 + 0]; - dst[ 9] = v2 * m[2 * 4 + 1]; - dst[10] = v2 * m[2 * 4 + 2]; - dst[11] = v2 * m[2 * 4 + 3]; - - if (m !== dst) { - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; + newDst[ 0] = v0 * m[0 * 4 + 0]; + newDst[ 1] = v0 * m[0 * 4 + 1]; + newDst[ 2] = v0 * m[0 * 4 + 2]; + newDst[ 3] = v0 * m[0 * 4 + 3]; + newDst[ 4] = v1 * m[1 * 4 + 0]; + newDst[ 5] = v1 * m[1 * 4 + 1]; + newDst[ 6] = v1 * m[1 * 4 + 2]; + newDst[ 7] = v1 * m[1 * 4 + 3]; + newDst[ 8] = v2 * m[2 * 4 + 0]; + newDst[ 9] = v2 * m[2 * 4 + 1]; + newDst[10] = v2 * m[2 * 4 + 2]; + newDst[11] = v2 * m[2 * 4 + 3]; + + if (m !== newDst) { + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; + return newDst; } /** @@ -1562,15 +1534,15 @@ export function scale(m: Mat4, v: Vec3, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaling matrix. */ -export function uniformScaling(s: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); +function uniformScaling(s: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; - dst[ 0] = s; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; - dst[ 4] = 0; dst[ 5] = s; dst[ 6] = 0; dst[ 7] = 0; - dst[ 8] = 0; dst[ 9] = 0; dst[10] = s; dst[11] = 0; - dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; + newDst[ 0] = s; newDst[ 1] = 0; newDst[ 2] = 0; newDst[ 3] = 0; + newDst[ 4] = 0; newDst[ 5] = s; newDst[ 6] = 0; newDst[ 7] = 0; + newDst[ 8] = 0; newDst[ 9] = 0; newDst[10] = s; newDst[11] = 0; + newDst[12] = 0; newDst[13] = 0; newDst[14] = 0; newDst[15] = 1; - return dst; + return newDst; } /** @@ -1580,28 +1552,92 @@ export function uniformScaling(s: number, dst?: Mat4): Mat4 { * @param dst - matrix to hold result. If not passed a new one is created. * @returns The scaled matrix. */ -export function uniformScale(m: Mat4, s: number, dst?: Mat4): Mat4 { - dst = dst || new MatType(16); - - dst[ 0] = s * m[0 * 4 + 0]; - dst[ 1] = s * m[0 * 4 + 1]; - dst[ 2] = s * m[0 * 4 + 2]; - dst[ 3] = s * m[0 * 4 + 3]; - dst[ 4] = s * m[1 * 4 + 0]; - dst[ 5] = s * m[1 * 4 + 1]; - dst[ 6] = s * m[1 * 4 + 2]; - dst[ 7] = s * m[1 * 4 + 3]; - dst[ 8] = s * m[2 * 4 + 0]; - dst[ 9] = s * m[2 * 4 + 1]; - dst[10] = s * m[2 * 4 + 2]; - dst[11] = s * m[2 * 4 + 3]; - - if (m !== dst) { - dst[12] = m[12]; - dst[13] = m[13]; - dst[14] = m[14]; - dst[15] = m[15]; +function uniformScale(m: Mat4Arg, s: number, dst?: T) { + const newDst = (dst ?? new Ctor(16)) as T; + + newDst[ 0] = s * m[0 * 4 + 0]; + newDst[ 1] = s * m[0 * 4 + 1]; + newDst[ 2] = s * m[0 * 4 + 2]; + newDst[ 3] = s * m[0 * 4 + 3]; + newDst[ 4] = s * m[1 * 4 + 0]; + newDst[ 5] = s * m[1 * 4 + 1]; + newDst[ 6] = s * m[1 * 4 + 2]; + newDst[ 7] = s * m[1 * 4 + 3]; + newDst[ 8] = s * m[2 * 4 + 0]; + newDst[ 9] = s * m[2 * 4 + 1]; + newDst[10] = s * m[2 * 4 + 2]; + newDst[11] = s * m[2 * 4 + 3]; + + if (m !== newDst) { + newDst[12] = m[12]; + newDst[13] = m[13]; + newDst[14] = m[14]; + newDst[15] = m[15]; } - return dst; -} \ No newline at end of file + return newDst; +} + +return { + create, + set, + fromMat3, + fromQuat, + negate, + copy, + clone, + equalsApproximately, + equals, + identity, + transpose, + inverse, + determinant, + invert, + multiply, + mul, + setTranslation, + getTranslation, + getAxis, + setAxis, + getScaling, + perspective, + perspectiveReverseZ, + ortho, + frustum, + frustumReverseZ, + aim, + cameraAim, + lookAt, + translation, + translate, + rotationX, + rotateX, + rotationY, + rotateY, + rotationZ, + rotateZ, + axisRotation, + rotation, + axisRotate, + rotate, + scaling, + scale, + uniformScaling, + uniformScale, +}; + +} + + +type API = ReturnType>; + +const cache = new Map(); + +export function getAPI(Ctor: Mat4Ctor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; +} diff --git a/src/mat4.ts b/src/mat4.ts index 2711397..6dcc899 100644 --- a/src/mat4.ts +++ b/src/mat4.ts @@ -19,10 +19,15 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +import { BaseArgType } from "./types"; /** * A JavaScript array with 16 values, a Float32Array with 16 values, or a Float64Array with 16 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link mat4.setDefaultType}. */ -export type Mat4 = number[] | Float32Array | Float64Array; +export type Mat4Arg = BaseArgType; + +/** + * A specific concrete 4x4 Matrix Type + */ +export type Mat4Type = T; + diff --git a/src/quat-impl.ts b/src/quat-impl.ts index bd55da9..09144ce 100644 --- a/src/quat-impl.ts +++ b/src/quat-impl.ts @@ -20,16 +20,49 @@ * DEALINGS IN THE SOFTWARE. */ import * as utils from './utils.js'; -import { Quat, create, setDefaultType, QuatType } from './quat'; -import { Mat3 } from './mat3.js'; -import { Mat4 } from './mat4.js'; -import { Vec3 } from './vec3.js'; -import * as vec3 from './vec3-impl.js'; +import { QuatArg, QuatType } from './quat'; +import { Mat3Arg } from './mat3.js'; +import { Mat4Arg } from './mat4.js'; +import { Vec3Arg } from './vec3.js'; +import { getAPI as getVec3API } from './vec3-impl'; +import { BaseArgType } from './types'; + +export { QuatArg, QuatType }; + +type QuatCtor = new (n: number) => T; export type RotationOrder = 'xyz' | 'xzy' | 'yxz' | 'yzx' | 'zxy' | 'zyx'; -export default Quat; -export { create, setDefaultType }; +/** + * Generates am typed API for Qud + * */ +function getAPIImpl(Ctor: QuatCtor) { + const vec3 = getVec3API(Ctor); + +/** + * Creates a quat4; may be called with x, y, z to set initial values. + * @param x - Initial x value. + * @param y - Initial y value. + * @param z - Initial z value. + * @param w - Initial w value. + * @returns the created vector + */ +function create(x?: number, y?: number, z?: number, w?: number) { + const newDst = new Ctor(4); + if (x !== undefined) { + newDst[0] = x; + if (y !== undefined) { + newDst[1] = y; + if (z !== undefined) { + newDst[2] = z; + if (w !== undefined) { + newDst[3] = w; + } + } + } + } + return newDst; +} /** * Creates a Quat; may be called with x, y, z to set initial values. (same as create) @@ -39,7 +72,7 @@ export { create, setDefaultType }; * @param z - Initial w value. * @returns the created vector */ -export const fromValues = create; +const fromValues = create; /** * Sets the values of a Quat @@ -52,15 +85,15 @@ export const fromValues = create; * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ -export function set(x: number, y: number, z: number, w: number, dst?: Quat) { - dst = dst || new QuatType(4); +function set(x: number, y: number, z: number, w: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = x; - dst[1] = y; - dst[2] = z; - dst[3] = w; + newDst[0] = x; + newDst[1] = y; + newDst[2] = z; + newDst[3] = w; - return dst; + return newDst; } /** @@ -72,18 +105,18 @@ export function set(x: number, y: number, z: number, w: number, dst?: Quat) { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The quaternion that represents the given axis and angle **/ -export function fromAxisAngle(axis: Vec3, angleInRadians: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function fromAxisAngle(axis: Vec3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const halfAngle = angleInRadians * 0.5; const s = Math.sin(halfAngle); - dst[0] = s * axis[0]; - dst[1] = s * axis[1]; - dst[2] = s * axis[2]; - dst[3] = Math.cos(halfAngle); + newDst[0] = s * axis[0]; + newDst[1] = s * axis[1]; + newDst[2] = s * axis[2]; + newDst[3] = Math.cos(halfAngle); - return dst; + return newDst; } /** @@ -92,22 +125,22 @@ export function fromAxisAngle(axis: Vec3, angleInRadians: number, dst?: Quat): Q * @param dst - Vec3 to hold result. If not passed in a new one is created. * @return angle and axis */ -export function toAxisAngle(q: Quat, dst?: Vec3): { angle: number, axis: Vec3 } { - dst = dst || vec3.create(4); +function toAxisAngle(q: QuatArg, dst?: T): { angle: number, axis: T } { + const newDst = (dst ?? vec3.create(3)) as T; const angle = Math.acos(q[3]) * 2; const s = Math.sin(angle * 0.5); if (s > utils.EPSILON) { - dst[0] = q[0] / s; - dst[1] = q[1] / s; - dst[2] = q[2] / s; + newDst[0] = q[0] / s; + newDst[1] = q[1] / s; + newDst[2] = q[2] / s; } else { - dst[0] = 1; - dst[1] = 0; - dst[2] = 0; + newDst[0] = 1; + newDst[1] = 0; + newDst[2] = 0; } - return { angle, axis: dst }; + return { angle, axis: newDst }; } /** @@ -116,7 +149,7 @@ export function toAxisAngle(q: Quat, dst?: Vec3): { angle: number, axis: Vec3 } * @param b - quaternion b * @return angle in radians between the two quaternions */ -export function angle(a: Quat, b: Quat) { +function angle(a: QuatArg, b: QuatArg) { const d = dot(a, b); return Math.acos(2 * d * d - 1); } @@ -129,8 +162,8 @@ export function angle(a: Quat, b: Quat) { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export function multiply(a: Quat, b: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function multiply(a: QuatArg, b: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const ax = a[0]; const ay = a[1]; @@ -142,12 +175,12 @@ export function multiply(a: Quat, b: Quat, dst?: Quat): Quat { const bz = b[2]; const bw = b[3]; - dst[0] = ax * bw + aw * bx + ay * bz - az * by; - dst[1] = ay * bw + aw * by + az * bx - ax * bz; - dst[2] = az * bw + aw * bz + ax * by - ay * bx; - dst[3] = aw * bw - ax * bx - ay * by - az * bz; + newDst[0] = ax * bw + aw * bx + ay * bz - az * by; + newDst[1] = ay * bw + aw * by + az * bx - ax * bz; + newDst[2] = az * bw + aw * bz + ax * by - ay * bx; + newDst[3] = aw * bw - ax * bx - ay * by - az * bz; - return dst; + return newDst; } /** @@ -158,7 +191,7 @@ export function multiply(a: Quat, b: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export const mul = multiply; +const mul = multiply; /** * Rotates the given quaternion around the X axis by the given angle. @@ -167,8 +200,8 @@ export const mul = multiply; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export function rotateX(q: Quat, angleInRadians: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function rotateX(q: QuatArg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const halfAngle = angleInRadians * 0.5; @@ -180,12 +213,12 @@ export function rotateX(q: Quat, angleInRadians: number, dst?: Quat): Quat { const bx = Math.sin(halfAngle); const bw = Math.cos(halfAngle); - dst[0] = qx * bw + qw * bx; - dst[1] = qy * bw + qz * bx; - dst[2] = qz * bw - qy * bx; - dst[3] = qw * bw - qx * bx; + newDst[0] = qx * bw + qw * bx; + newDst[1] = qy * bw + qz * bx; + newDst[2] = qz * bw - qy * bx; + newDst[3] = qw * bw - qx * bx; - return dst; + return newDst; } /** @@ -195,8 +228,8 @@ export function rotateX(q: Quat, angleInRadians: number, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export function rotateY(q: Quat, angleInRadians: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function rotateY(q: QuatArg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const halfAngle = angleInRadians * 0.5; @@ -208,12 +241,12 @@ export function rotateY(q: Quat, angleInRadians: number, dst?: Quat): Quat { const by = Math.sin(halfAngle); const bw = Math.cos(halfAngle); - dst[0] = qx * bw - qz * by; - dst[1] = qy * bw + qw * by; - dst[2] = qz * bw + qx * by; - dst[3] = qw * bw - qy * by; + newDst[0] = qx * bw - qz * by; + newDst[1] = qy * bw + qw * by; + newDst[2] = qz * bw + qx * by; + newDst[3] = qw * bw - qy * by; - return dst; + return newDst; } /** @@ -223,8 +256,8 @@ export function rotateY(q: Quat, angleInRadians: number, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export function rotateZ(q: Quat, angleInRadians: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function rotateZ(q: QuatArg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const halfAngle = angleInRadians * 0.5; @@ -236,12 +269,12 @@ export function rotateZ(q: Quat, angleInRadians: number, dst?: Quat): Quat { const bz = Math.sin(halfAngle); const bw = Math.cos(halfAngle); - dst[0] = qx * bw + qy * bz; - dst[1] = qy * bw - qx * bz; - dst[2] = qz * bw + qw * bz; - dst[3] = qw * bw - qz * bz; + newDst[0] = qx * bw + qy * bz; + newDst[1] = qy * bw - qx * bz; + newDst[2] = qz * bw + qw * bz; + newDst[3] = qw * bw - qz * bz; - return dst; + return newDst; } /** @@ -253,8 +286,8 @@ export function rotateZ(q: Quat, angleInRadians: number, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the result of a * b */ -export function slerp(a: Quat, b: Quat, t: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function slerp(a: QuatArg, b: QuatArg, t: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const ax = a[0]; const ay = a[1]; @@ -289,12 +322,12 @@ export function slerp(a: Quat, b: Quat, t: number, dst?: Quat): Quat { scale1 = t; } - dst[0] = scale0 * ax + scale1 * bx; - dst[1] = scale0 * ay + scale1 * by; - dst[2] = scale0 * az + scale1 * bz; - dst[3] = scale0 * aw + scale1 * bw; + newDst[0] = scale0 * ax + scale1 * bx; + newDst[1] = scale0 * ay + scale1 * by; + newDst[2] = scale0 * az + scale1 * bz; + newDst[3] = scale0 * aw + scale1 * bw; - return dst; + return newDst; } /** @@ -303,8 +336,8 @@ export function slerp(a: Quat, b: Quat, t: number, dst?: Quat): Quat { * @param q - quaternion to compute the inverse of * @returns A quaternion that is the result of a * b */ -export function inverse(q: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function inverse(q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const a0 = q[0]; const a1 = q[1]; @@ -314,12 +347,12 @@ export function inverse(q: Quat, dst?: Quat): Quat { const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; const invDot = dot ? 1 / dot : 0; - dst[0] = -a0 * invDot; - dst[1] = -a1 * invDot; - dst[2] = -a2 * invDot; - dst[3] = a3 * invDot; + newDst[0] = -a0 * invDot; + newDst[1] = -a1 * invDot; + newDst[2] = -a2 * invDot; + newDst[3] = a3 * invDot; - return dst; + return newDst; } /** @@ -331,15 +364,15 @@ export function inverse(q: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The conjugate of q */ -export function conjugate(q: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function conjugate(q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = -q[0]; - dst[1] = -q[1]; - dst[2] = -q[2]; - dst[3] = q[3]; + newDst[0] = -q[0]; + newDst[1] = -q[1]; + newDst[2] = -q[2]; + newDst[3] = q[3]; - return dst; + return newDst; } /** @@ -351,8 +384,8 @@ export function conjugate(q: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns the result */ -export function fromMat(m: Mat3 | Mat4, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function fromMat(m: Mat3Arg | Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; /* 0 1 2 @@ -371,12 +404,12 @@ export function fromMat(m: Mat3 | Mat4, dst?: Quat): Quat { if (trace > 0.0) { // |w| > 1/2, may as well choose w > 1/2 const root = Math.sqrt(trace + 1); // 2w - dst[3] = 0.5 * root; + newDst[3] = 0.5 * root; const invRoot = 0.5 / root; // 1/(4w) - dst[0] = (m[6] - m[9]) * invRoot; - dst[1] = (m[8] - m[2]) * invRoot; - dst[2] = (m[1] - m[4]) * invRoot; + newDst[0] = (m[6] - m[9]) * invRoot; + newDst[1] = (m[8] - m[2]) * invRoot; + newDst[2] = (m[1] - m[4]) * invRoot; } else { // |w| <= 1/2 let i = 0; @@ -392,16 +425,16 @@ export function fromMat(m: Mat3 | Mat4, dst?: Quat): Quat { const k = (i + 2) % 3; const root = Math.sqrt(m[i * 4 + i] - m[j * 4 + j] - m[k * 4 + k] + 1.0); - dst[i] = 0.5 * root; + newDst[i] = 0.5 * root; const invRoot = 0.5 / root; - dst[3] = (m[j * 4 + k] - m[k * 4 + j]) * invRoot; - dst[j] = (m[j * 4 + i] + m[i * 4 + j]) * invRoot; - dst[k] = (m[k * 4 + i] + m[i * 4 + k]) * invRoot; + newDst[3] = (m[j * 4 + k] - m[k * 4 + j]) * invRoot; + newDst[j] = (m[j * 4 + i] + m[i * 4 + j]) * invRoot; + newDst[k] = (m[k * 4 + i] + m[i * 4 + k]) * invRoot; } - return dst; + return newDst; } /** @@ -414,13 +447,13 @@ export function fromMat(m: Mat3 | Mat4, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion representing the same rotation as the euler angles applied in the given order */ -export function fromEuler( +function fromEuler( xAngleInRadians: number, yAngleInRadians: number, zAngleInRadians: number, order: RotationOrder, - dst?: Quat) { - dst = dst || new QuatType(4); + dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const xHalfAngle = xAngleInRadians * 0.5; const yHalfAngle = yAngleInRadians * 0.5; @@ -435,52 +468,52 @@ export function fromEuler( switch (order) { case 'xyz': - dst[0] = sx * cy * cz + cx * sy * sz; - dst[1] = cx * sy * cz - sx * cy * sz; - dst[2] = cx * cy * sz + sx * sy * cz; - dst[3] = cx * cy * cz - sx * sy * sz; + newDst[0] = sx * cy * cz + cx * sy * sz; + newDst[1] = cx * sy * cz - sx * cy * sz; + newDst[2] = cx * cy * sz + sx * sy * cz; + newDst[3] = cx * cy * cz - sx * sy * sz; break; case 'xzy': - dst[0] = sx * cy * cz - cx * sy * sz; - dst[1] = cx * sy * cz - sx * cy * sz; - dst[2] = cx * cy * sz + sx * sy * cz; - dst[3] = cx * cy * cz + sx * sy * sz; + newDst[0] = sx * cy * cz - cx * sy * sz; + newDst[1] = cx * sy * cz - sx * cy * sz; + newDst[2] = cx * cy * sz + sx * sy * cz; + newDst[3] = cx * cy * cz + sx * sy * sz; break; case 'yxz': - dst[0] = sx * cy * cz + cx * sy * sz; - dst[1] = cx * sy * cz - sx * cy * sz; - dst[2] = cx * cy * sz - sx * sy * cz; - dst[3] = cx * cy * cz + sx * sy * sz; + newDst[0] = sx * cy * cz + cx * sy * sz; + newDst[1] = cx * sy * cz - sx * cy * sz; + newDst[2] = cx * cy * sz - sx * sy * cz; + newDst[3] = cx * cy * cz + sx * sy * sz; break; case 'yzx': - dst[0] = sx * cy * cz + cx * sy * sz; - dst[1] = cx * sy * cz + sx * cy * sz; - dst[2] = cx * cy * sz - sx * sy * cz; - dst[3] = cx * cy * cz - sx * sy * sz; + newDst[0] = sx * cy * cz + cx * sy * sz; + newDst[1] = cx * sy * cz + sx * cy * sz; + newDst[2] = cx * cy * sz - sx * sy * cz; + newDst[3] = cx * cy * cz - sx * sy * sz; break; case 'zxy': - dst[0] = sx * cy * cz - cx * sy * sz; - dst[1] = cx * sy * cz + sx * cy * sz; - dst[2] = cx * cy * sz + sx * sy * cz; - dst[3] = cx * cy * cz - sx * sy * sz; + newDst[0] = sx * cy * cz - cx * sy * sz; + newDst[1] = cx * sy * cz + sx * cy * sz; + newDst[2] = cx * cy * sz + sx * sy * cz; + newDst[3] = cx * cy * cz - sx * sy * sz; break; case 'zyx': - dst[0] = sx * cy * cz - cx * sy * sz; - dst[1] = cx * sy * cz + sx * cy * sz; - dst[2] = cx * cy * sz - sx * sy * cz; - dst[3] = cx * cy * cz + sx * sy * sz; + newDst[0] = sx * cy * cz - cx * sy * sz; + newDst[1] = cx * sy * cz + sx * cy * sz; + newDst[2] = cx * cy * sz - sx * sy * cz; + newDst[3] = cx * cy * cz + sx * sy * sz; break; default: throw new Error(`Unknown rotation order: ${order}`); } - return dst; + return newDst; } /** @@ -490,15 +523,15 @@ export function fromEuler( * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is a copy of q */ -export function copy(q: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function copy(q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = q[0]; - dst[1] = q[1]; - dst[2] = q[2]; - dst[3] = q[3]; + newDst[0] = q[0]; + newDst[1] = q[1]; + newDst[2] = q[2]; + newDst[3] = q[3]; - return dst; + return newDst; } /** @@ -508,7 +541,7 @@ export function copy(q: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A copy of q. */ -export const clone = copy; +const clone = copy; /** * Adds two quaternions; assumes a and b have the same dimension. @@ -517,15 +550,15 @@ export const clone = copy; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the sum of a and b. */ -export function add(a: Quat, b: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function add(a: QuatArg, b: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + b[0]; - dst[1] = a[1] + b[1]; - dst[2] = a[2] + b[2]; - dst[3] = a[3] + b[3]; + newDst[0] = a[0] + b[0]; + newDst[1] = a[1] + b[1]; + newDst[2] = a[2] + b[2]; + newDst[3] = a[3] + b[3]; - return dst; + return newDst; } /** @@ -535,15 +568,15 @@ export function add(a: Quat, b: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the difference of a and b. */ -export function subtract(a: Quat, b: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function subtract(a: QuatArg, b: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] - b[0]; - dst[1] = a[1] - b[1]; - dst[2] = a[2] - b[2]; - dst[3] = a[3] - b[3]; + newDst[0] = a[0] - b[0]; + newDst[1] = a[1] - b[1]; + newDst[2] = a[2] - b[2]; + newDst[3] = a[3] - b[3]; - return dst; + return newDst; } /** @@ -553,7 +586,7 @@ export function subtract(a: Quat, b: Quat, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns A quaternion that is the difference of a and b. */ -export const sub = subtract; +const sub = subtract; /** * Multiplies a quaternion by a scalar. @@ -562,15 +595,15 @@ export const sub = subtract; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The scaled quaternion. */ -export function mulScalar(v: Quat, k: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function mulScalar(v: QuatArg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = v[0] * k; - dst[1] = v[1] * k; - dst[2] = v[2] * k; - dst[3] = v[3] * k; + newDst[0] = v[0] * k; + newDst[1] = v[1] * k; + newDst[2] = v[2] * k; + newDst[3] = v[3] * k; - return dst; + return newDst; } /** @@ -580,7 +613,7 @@ export function mulScalar(v: Quat, k: number, dst?: Quat): Quat { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The scaled quaternion. */ -export const scale = mulScalar; +const scale = mulScalar; /** * Divides a vector by a scalar. @@ -589,15 +622,15 @@ export const scale = mulScalar; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The scaled quaternion. */ -export function divScalar(v: Quat, k: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function divScalar(v: QuatArg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = v[0] / k; - dst[1] = v[1] / k; - dst[2] = v[2] / k; - dst[3] = v[3] / k; + newDst[0] = v[0] / k; + newDst[1] = v[1] / k; + newDst[2] = v[2] / k; + newDst[3] = v[3] / k; - return dst; + return newDst; } /** @@ -606,7 +639,7 @@ export function divScalar(v: Quat, k: number, dst?: Quat): Quat { * @param b - Operand quaternion. * @returns dot product */ -export function dot(a: Quat, b: Quat): number { +function dot(a: QuatArg, b: QuatArg): number { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]) + (a[3] * b[3]); } @@ -620,15 +653,15 @@ export function dot(a: Quat, b: Quat): number { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ -export function lerp(a: Quat, b: Quat, t: number, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function lerp(a: QuatArg, b: QuatArg, t: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + t * (b[0] - a[0]); - dst[1] = a[1] + t * (b[1] - a[1]); - dst[2] = a[2] + t * (b[2] - a[2]); - dst[3] = a[3] + t * (b[3] - a[3]); + newDst[0] = a[0] + t * (b[0] - a[0]); + newDst[1] = a[1] + t * (b[1] - a[1]); + newDst[2] = a[2] + t * (b[2] - a[2]); + newDst[3] = a[3] + t * (b[3] - a[3]); - return dst; + return newDst; } /** @@ -636,7 +669,7 @@ export function lerp(a: Quat, b: Quat, t: number, dst?: Quat): Quat { * @param v - quaternion. * @returns length of quaternion. */ -export function length(v: Quat): number { +function length(v: QuatArg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -649,14 +682,14 @@ export function length(v: Quat): number { * @param v - quaternion. * @returns length of quaternion. */ -export const len = length; +const len = length; /** * Computes the square of the length of quaternion * @param v - quaternion. * @returns square of the length of quaternion. */ -export function lengthSq(v: Quat): number { +function lengthSq(v: QuatArg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -669,7 +702,7 @@ export function lengthSq(v: Quat): number { * @param v - quaternion. * @returns square of the length of quaternion. */ -export const lenSq = lengthSq; +const lenSq = lengthSq; /** * Divides a quaternion by its Euclidean length and returns the quotient. @@ -677,8 +710,8 @@ export const lenSq = lengthSq; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns The normalized quaternion. */ -export function normalize(v: Quat, dst?: Quat): Quat { - dst = dst || new QuatType(4); +function normalize(v: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const v0 = v[0]; const v1 = v[1]; @@ -687,18 +720,18 @@ export function normalize(v: Quat, dst?: Quat): Quat { const len = Math.sqrt(v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3); if (len > 0.00001) { - dst[0] = v0 / len; - dst[1] = v1 / len; - dst[2] = v2 / len; - dst[3] = v3 / len; + newDst[0] = v0 / len; + newDst[1] = v1 / len; + newDst[2] = v2 / len; + newDst[3] = v3 / len; } else { - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 0; } - return dst; + return newDst; } /** @@ -707,7 +740,7 @@ export function normalize(v: Quat, dst?: Quat): Quat { * @param b - Operand quaternion. * @returns true if quaternions are approximately equal */ -export function equalsApproximately(a: Quat, b: Quat): boolean { +function equalsApproximately(a: QuatArg, b: QuatArg): boolean { return Math.abs(a[0] - b[0]) < utils.EPSILON && Math.abs(a[1] - b[1]) < utils.EPSILON && Math.abs(a[2] - b[2]) < utils.EPSILON && @@ -720,7 +753,7 @@ export function equalsApproximately(a: Quat, b: Quat): boolean { * @param b - Operand quaternion. * @returns true if quaternions are exactly equal */ -export function equals(a: Quat, b: Quat): boolean { +function equals(a: QuatArg, b: QuatArg): boolean { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; } @@ -729,20 +762,20 @@ export function equals(a: Quat, b: Quat): boolean { * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns an identity quaternion */ -export function identity(dst?: Quat): Quat { - dst = dst || new QuatType(4); +function identity(dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; - dst[3] = 1; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 1; - return dst; + return newDst; } -let tempVec3: Vec3; -let xUnitVec3: Vec3; -let yUnitVec3: Vec3; +const tempVec3 = vec3.create(); +const xUnitVec3 = vec3.create(); +const yUnitVec3 = vec3.create(); /** * Computes a quaternion to represent the shortest rotation from one vector to another. @@ -752,12 +785,8 @@ let yUnitVec3: Vec3; * @param dst - quaternion to hold result. If not passed in a new one is created. * @returns the result */ -export function rotationTo(aUnit: Vec3, bUnit: Vec3, dst?: Quat): Quat { - dst = dst || new QuatType(4); - - tempVec3 = tempVec3 || vec3.create(); - xUnitVec3 = xUnitVec3 || vec3.create(1, 0, 0); - yUnitVec3 = yUnitVec3 || vec3.create(0, 1, 0); +function rotationTo(aUnit: Vec3Arg, bUnit: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const dot = vec3.dot(aUnit, bUnit); if (dot < -0.999999) { @@ -767,30 +796,30 @@ export function rotationTo(aUnit: Vec3, bUnit: Vec3, dst?: Quat): Quat { } vec3.normalize(tempVec3, tempVec3); - fromAxisAngle(tempVec3, Math.PI, dst); + fromAxisAngle(tempVec3, Math.PI, newDst); - return dst; + return newDst; } else if (dot > 0.999999) { - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; - dst[3] = 1; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 1; - return dst; + return newDst; } else { vec3.cross(aUnit, bUnit, tempVec3); - dst[0] = tempVec3[0]; - dst[1] = tempVec3[1]; - dst[2] = tempVec3[2]; - dst[3] = 1 + dot; + newDst[0] = tempVec3[0]; + newDst[1] = tempVec3[1]; + newDst[2] = tempVec3[2]; + newDst[3] = 1 + dot; - return normalize(dst, dst); + return normalize(newDst, newDst); } } -let tempQuat1: Quat; -let tempQuat2: Quat; +const tempQuat1 = new Ctor(4); +const tempQuat2 = new Ctor(4); /** * Performs a spherical linear interpolation with two control points @@ -802,21 +831,94 @@ let tempQuat2: Quat; * @param t - Interpolation coefficient 0 to 1 * @returns result */ -export function sqlerp( - a: Quat, - b: Quat, - c: Quat, - d: Quat, +function sqlerp( + a: QuatArg, + b: QuatArg, + c: QuatArg, + d: QuatArg, t: number, - dst?: Quat): Quat { - dst = dst || new QuatType(4); - - tempQuat1 = tempQuat1 || new QuatType(4); - tempQuat2 = tempQuat2 || new QuatType(4); + dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; slerp(a, d, t, tempQuat1); slerp(b, c, t, tempQuat2); - slerp(tempQuat1, tempQuat2, 2 * t * (1 - t), dst); + slerp(tempQuat1, tempQuat2, 2 * t * (1 - t), newDst); + + return newDst; +} - return dst; +return { + create, + fromValues, + set, + fromAxisAngle, + toAxisAngle, + angle, + multiply, + mul, + rotateX, + rotateY, + rotateZ, + slerp, + inverse, + conjugate, + fromMat, + fromEuler, + copy, + clone, + add, + subtract, + sub, + mulScalar, + scale, + divScalar, + dot, + lerp, + length, + len, + lengthSq, + lenSq, + normalize, + equalsApproximately, + equals, + identity, + rotationTo, + sqlerp, +}; + +} + +type API = ReturnType>; + +const cache = new Map(); + +/** + * + * Quat4 math functions. + * + * Almost all functions take an optional `newDst` argument. If it is not passed in the + * functions will create a new `Quat4`. In other words you can do this + * + * const v = quat4.cross(v1, v2); // Creates a new Quat4 with the cross product of v1 x v2. + * + * or + * + * const v = quat4.create(); + * quat4.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v + * + * The first style is often easier but depending on where it's used it generates garbage where + * as there is almost never allocation with the second style. + * + * It is always safe to pass any vector as the destination. So for example + * + * quat4.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 + * + */ +export function getAPI(Ctor: QuatCtor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; } diff --git a/src/quat.ts b/src/quat.ts index e211dea..f8935f7 100644 --- a/src/quat.ts +++ b/src/quat.ts @@ -20,70 +20,15 @@ * DEALINGS IN THE SOFTWARE. */ -/** - * A JavaScript array with 4 values, Float32Array with 4 values, or a Float64Array with 4 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link quat.setDefaultType}. - */ -export type Quat = number[] | Float32Array | Float64Array; +import { BaseArgType } from "./types"; /** - * - * Quat4 math functions. - * - * Almost all functions take an optional `dst` argument. If it is not passed in the - * functions will create a new `Quat4`. In other words you can do this - * - * const v = quat4.cross(v1, v2); // Creates a new Quat4 with the cross product of v1 x v2. - * - * or - * - * const v = quat4.create(); - * quat4.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v - * - * The first style is often easier but depending on where it's used it generates garbage where - * as there is almost never allocation with the second style. - * - * It is always safe to pass any vector as the destination. So for example - * - * quat4.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 - * + * A JavaScript array with 4 values, a Float32Array with 4 values, or a Float64Array with 4 values. */ - -export let QuatType: new (n: number) => Quat = Float32Array; +export type QuatArg = BaseArgType; /** - * Sets the type this library creates for a Quat4 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Quat4 + * A specific concrete 4x4 Matrix Type */ -export function setDefaultType(ctor: new (n: number) => Quat) { - const oldType = QuatType; - QuatType = ctor; - return oldType; -} +export type QuatType = T; -/** - * Creates a quat4; may be called with x, y, z to set initial values. - * @param x - Initial x value. - * @param y - Initial y value. - * @param z - Initial z value. - * @param w - Initial w value. - * @returns the created vector - */ -export function create(x?: number, y?: number, z?: number, w?: number): Quat { - const dst = new QuatType(4); - if (x !== undefined) { - dst[0] = x; - if (y !== undefined) { - dst[1] = y; - if (z !== undefined) { - dst[2] = z; - if (w !== undefined) { - dst[3] = w; - } - } - } - } - return dst; -} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..87cb587 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,20 @@ +/** + * The types you can pass to most functions that take an + * array of numbers. + */ +export type BaseArgType = Float32Array | Float64Array | number[]; + +function wrapConstructor any>( + OriginalConstructor: T, + modifier: (instance: InstanceType) => void +): T { + return class extends OriginalConstructor { + constructor(...args: any[]) { + super(...args); + modifier(this as InstanceType); + } + } as T; // Type assertion is necessary here +} + +export const ZeroArray = wrapConstructor(Array, a => a.fill(0)); + diff --git a/src/vec2-impl.ts b/src/vec2-impl.ts index d22113e..474b8e9 100644 --- a/src/vec2-impl.ts +++ b/src/vec2-impl.ts @@ -20,13 +20,47 @@ * DEALINGS IN THE SOFTWARE. */ import * as utils from './utils.js'; -import { Mat3 } from './mat3'; -import { Mat4 } from './mat4'; -import { Vec2, create, setDefaultType, VecType } from './vec2'; -import { Vec3, VecType as Vec3Type } from './vec3'; +import { Mat3Arg } from './mat3'; +import { Mat4Arg } from './mat4'; +import { Vec2Arg, Vec2Type } from './vec2'; +import { Vec3Arg } from './vec3'; +import { BaseArgType } from './types'; -export default Vec2; -export { create, setDefaultType }; +export { Vec2Arg, Vec2Type }; + +type Vec2Ctor = new (n: number) => T; + +/** + * Generates am typed API for Vec3 + */ +function getAPIImpl(Ctor: Vec2Ctor) { + +/** + * Creates a Vec2; may be called with x, y, z to set initial values. + * + * Note: Since passing in a raw JavaScript array + * is valid in all circumstances, if you want to + * force a JavaScript array into a Vec2's specified type + * it would be faster to use + * + * ``` + * const v = vec2.clone(someJSArray); + * ``` + * + * @param x - Initial x value. + * @param y - Initial y value. + * @returns the created vector + */ +function create(x = 0, y = 0) { + const newDst = new Ctor(2); + if (x !== undefined) { + newDst[0] = x; + if (y !== undefined) { + newDst[1] = y; + } + } + return newDst; +} /** * Creates a Vec2; may be called with x, y, z to set initial values. (same as create) @@ -34,7 +68,7 @@ export { create, setDefaultType }; * @param y - Initial y value. * @returns the created vector */ -export const fromValues = create; +const fromValues = create; /** * Sets the values of a Vec2 @@ -45,13 +79,13 @@ export const fromValues = create; * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ -export function set(x: number, y: number, dst?: Vec2) { - dst = dst || new VecType(2); +function set(x: number, y: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = x; - dst[1] = y; + newDst[0] = x; + newDst[1] = y; - return dst; + return newDst; } /** @@ -60,13 +94,13 @@ export function set(x: number, y: number, dst?: Vec2) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the ceil of each element of v. */ -export function ceil(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function ceil(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.ceil(v[0]); - dst[1] = Math.ceil(v[1]); + newDst[0] = Math.ceil(v[0]); + newDst[1] = Math.ceil(v[1]); - return dst; + return newDst; } /** @@ -75,13 +109,13 @@ export function ceil(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the floor of each element of v. */ -export function floor(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function floor(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.floor(v[0]); - dst[1] = Math.floor(v[1]); + newDst[0] = Math.floor(v[0]); + newDst[1] = Math.floor(v[1]); - return dst; + return newDst; } /** @@ -90,13 +124,13 @@ export function floor(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the round of each element of v. */ -export function round(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function round(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.round(v[0]); - dst[1] = Math.round(v[1]); + newDst[0] = Math.round(v[0]); + newDst[1] = Math.round(v[1]); - return dst; + return newDst; } /** @@ -107,13 +141,13 @@ export function round(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that the clamped value of each element of v. */ -export function clamp(v: Vec2, min = 0, max = 1, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function clamp(v: Vec2Arg, min = 0, max = 1, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.min(max, Math.max(min, v[0])); - dst[1] = Math.min(max, Math.max(min, v[1])); + newDst[0] = Math.min(max, Math.max(min, v[0])); + newDst[1] = Math.min(max, Math.max(min, v[1])); - return dst; + return newDst; } /** @@ -123,13 +157,13 @@ export function clamp(v: Vec2, min = 0, max = 1, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a and b. */ -export function add(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function add(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] + b[0]; - dst[1] = a[1] + b[1]; + newDst[0] = a[0] + b[0]; + newDst[1] = a[1] + b[1]; - return dst; + return newDst; } /** @@ -140,13 +174,13 @@ export function add(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a + b * scale. */ -export function addScaled(a: Vec2, b: Vec2, scale: number, dst?: Vec2) { - dst = dst || new VecType(2); +function addScaled(a: Vec2Arg, b: Vec2Arg, scale: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] + b[0] * scale; - dst[1] = a[1] + b[1] * scale; + newDst[0] = a[0] + b[0] * scale; + newDst[1] = a[1] + b[1] * scale; - return dst; + return newDst; } /** @@ -155,7 +189,7 @@ export function addScaled(a: Vec2, b: Vec2, scale: number, dst?: Vec2) { * @param b - Operand vector. * @returns The angle in radians between the 2 vectors. */ -export function angle(a: Vec2, b: Vec2): number { +function angle(a: Vec2Arg, b: Vec2Arg): number { const ax = a[0]; const ay = a[1]; const bx = b[0]; @@ -174,13 +208,13 @@ export function angle(a: Vec2, b: Vec2): number { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export function subtract(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function subtract(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] - b[0]; - dst[1] = a[1] - b[1]; + newDst[0] = a[0] - b[0]; + newDst[1] = a[1] - b[1]; - return dst; + return newDst; } /** @@ -190,7 +224,7 @@ export function subtract(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export const sub = subtract; +const sub = subtract; /** * Check if 2 vectors are approximately equal @@ -198,7 +232,7 @@ export const sub = subtract; * @param b - Operand vector. * @returns true if vectors are approximately equal */ -export function equalsApproximately(a: Vec2, b: Vec2): boolean { +function equalsApproximately(a: Vec2Arg, b: Vec2Arg): boolean { return Math.abs(a[0] - b[0]) < utils.EPSILON && Math.abs(a[1] - b[1]) < utils.EPSILON; } @@ -209,7 +243,7 @@ export function equalsApproximately(a: Vec2, b: Vec2): boolean { * @param b - Operand vector. * @returns true if vectors are exactly equal */ -export function equals(a: Vec2, b: Vec2): boolean { +function equals(a: Vec2Arg, b: Vec2Arg): boolean { return a[0] === b[0] && a[1] === b[1]; } @@ -223,13 +257,13 @@ export function equals(a: Vec2, b: Vec2): boolean { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ -export function lerp(a: Vec2, b: Vec2, t: number, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function lerp(a: Vec2Arg, b: Vec2Arg, t: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] + t * (b[0] - a[0]); - dst[1] = a[1] + t * (b[1] - a[1]); + newDst[0] = a[0] + t * (b[0] - a[0]); + newDst[1] = a[1] + t * (b[1] - a[1]); - return dst; + return newDst; } /** @@ -242,13 +276,13 @@ export function lerp(a: Vec2, b: Vec2, t: number, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns the linear interpolated result. */ -export function lerpV(a: Vec2, b: Vec2, t: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function lerpV(a: Vec2Arg, b: Vec2Arg, t: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] + t[0] * (b[0] - a[0]); - dst[1] = a[1] + t[1] * (b[1] - a[1]); + newDst[0] = a[0] + t[0] * (b[0] - a[0]); + newDst[1] = a[1] + t[1] * (b[1] - a[1]); - return dst; + return newDst; } /** @@ -260,13 +294,13 @@ export function lerpV(a: Vec2, b: Vec2, t: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The max components vector. */ -export function max(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function max(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.max(a[0], b[0]); - dst[1] = Math.max(a[1], b[1]); + newDst[0] = Math.max(a[0], b[0]); + newDst[1] = Math.max(a[1], b[1]); - return dst; + return newDst; } /** @@ -278,13 +312,13 @@ export function max(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The min components vector. */ -export function min(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function min(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = Math.min(a[0], b[0]); - dst[1] = Math.min(a[1], b[1]); + newDst[0] = Math.min(a[0], b[0]); + newDst[1] = Math.min(a[1], b[1]); - return dst; + return newDst; } /** @@ -294,13 +328,13 @@ export function min(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function mulScalar(v: Vec2, k: number, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function mulScalar(v: Vec2Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = v[0] * k; - dst[1] = v[1] * k; + newDst[0] = v[0] * k; + newDst[1] = v[1] * k; - return dst; + return newDst; } /** @@ -310,7 +344,7 @@ export function mulScalar(v: Vec2, k: number, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export const scale = mulScalar; +const scale = mulScalar; /** * Divides a vector by a scalar. @@ -319,13 +353,13 @@ export const scale = mulScalar; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function divScalar(v: Vec2, k: number, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function divScalar(v: Vec2Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = v[0] / k; - dst[1] = v[1] / k; + newDst[0] = v[0] / k; + newDst[1] = v[1] / k; - return dst; + return newDst; } /** @@ -334,13 +368,13 @@ export function divScalar(v: Vec2, k: number, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export function inverse(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function inverse(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = 1 / v[0]; - dst[1] = 1 / v[1]; + newDst[0] = 1 / v[0]; + newDst[1] = 1 / v[1]; - return dst; + return newDst; } /** @@ -349,7 +383,7 @@ export function inverse(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export const invert = inverse; +const invert = inverse; /** * Computes the cross product of two vectors; assumes both vectors have @@ -359,14 +393,14 @@ export const invert = inverse; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of a cross b. */ -export function cross(a: Vec2, b: Vec2, dst?: Vec3): Vec3 { - dst = dst || new Vec3Type(3); +function cross(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const z = a[0] * b[1] - a[1] * b[0]; - dst[0] = 0; - dst[1] = 0; - dst[2] = z; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = z; - return dst; + return newDst; } /** @@ -376,7 +410,7 @@ export function cross(a: Vec2, b: Vec2, dst?: Vec3): Vec3 { * @param b - Operand vector. * @returns dot product */ -export function dot(a: Vec2, b: Vec2): number { +function dot(a: Vec2Arg, b: Vec2Arg): number { return a[0] * b[0] + a[1] * b[1]; } @@ -385,7 +419,7 @@ export function dot(a: Vec2, b: Vec2): number { * @param v - vector. * @returns length of vector. */ -export function length(v: Vec2): number { +function length(v: Vec2Arg): number { const v0 = v[0]; const v1 = v[1]; return Math.sqrt(v0 * v0 + v1 * v1); @@ -396,14 +430,14 @@ export function length(v: Vec2): number { * @param v - vector. * @returns length of vector. */ -export const len = length; +const len = length; /** * Computes the square of the length of vector * @param v - vector. * @returns square of the length of vector. */ -export function lengthSq(v: Vec2): number { +function lengthSq(v: Vec2Arg): number { const v0 = v[0]; const v1 = v[1]; return v0 * v0 + v1 * v1; @@ -414,7 +448,7 @@ export function lengthSq(v: Vec2): number { * @param v - vector. * @returns square of the length of vector. */ -export const lenSq = lengthSq; +const lenSq = lengthSq; /** * Computes the distance between 2 points @@ -422,7 +456,7 @@ export const lenSq = lengthSq; * @param b - vector. * @returns distance between a and b */ -export function distance(a: Vec2, b: Vec2): number { +function distance(a: Vec2Arg, b: Vec2Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; return Math.sqrt(dx * dx + dy * dy); @@ -434,7 +468,7 @@ export function distance(a: Vec2, b: Vec2): number { * @param b - vector. * @returns distance between a and b */ -export const dist = distance; +const dist = distance; /** * Computes the square of the distance between 2 points @@ -442,7 +476,7 @@ export const dist = distance; * @param b - vector. * @returns square of the distance between a and b */ -export function distanceSq(a: Vec2, b: Vec2): number { +function distanceSq(a: Vec2Arg, b: Vec2Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; return dx * dx + dy * dy; @@ -454,7 +488,7 @@ export function distanceSq(a: Vec2, b: Vec2): number { * @param b - vector. * @returns square of the distance between a and b */ -export const distSq = distanceSq; +const distSq = distanceSq; /** * Divides a vector by its Euclidean length and returns the quotient. @@ -462,22 +496,22 @@ export const distSq = distanceSq; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The normalized vector. */ -export function normalize(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function normalize(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; const v0 = v[0]; const v1 = v[1]; const len = Math.sqrt(v0 * v0 + v1 * v1); if (len > 0.00001) { - dst[0] = v0 / len; - dst[1] = v1 / len; + newDst[0] = v0 / len; + newDst[1] = v1 / len; } else { - dst[0] = 0; - dst[1] = 0; + newDst[0] = 0; + newDst[1] = 0; } - return dst; + return newDst; } /** @@ -486,13 +520,13 @@ export function normalize(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns -v. */ -export function negate(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function negate(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = -v[0]; - dst[1] = -v[1]; + newDst[0] = -v[0]; + newDst[1] = -v[1]; - return dst; + return newDst; } /** @@ -502,13 +536,13 @@ export function negate(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export function copy(v: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function copy(v: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = v[0]; - dst[1] = v[1]; + newDst[0] = v[0]; + newDst[1] = v[1]; - return dst; + return newDst; } /** @@ -518,7 +552,7 @@ export function copy(v: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export const clone = copy; +const clone = copy; /** * Multiplies a vector by another vector (component-wise); assumes a and @@ -528,13 +562,13 @@ export const clone = copy; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export function multiply(a: Vec2, b: Vec2, dst?: Vec2) { - dst = dst || new VecType(2); +function multiply(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] * b[0]; - dst[1] = a[1] * b[1]; + newDst[0] = a[0] * b[0]; + newDst[1] = a[1] * b[1]; - return dst; + return newDst; } /** @@ -545,7 +579,7 @@ export function multiply(a: Vec2, b: Vec2, dst?: Vec2) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export const mul = multiply; +const mul = multiply; /** * Divides a vector by another vector (component-wise); assumes a and @@ -555,13 +589,13 @@ export const mul = multiply; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export function divide(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function divide(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = a[0] / b[0]; - dst[1] = a[1] / b[1]; + newDst[0] = a[0] / b[0]; + newDst[1] = a[1] / b[1]; - return dst; + return newDst; } /** @@ -572,7 +606,7 @@ export function divide(a: Vec2, b: Vec2, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export const div = divide; +const div = divide; /** * Creates a random unit vector * scale @@ -580,14 +614,14 @@ export const div = divide; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The random vector. */ -export function random(scale = 1, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function random(scale = 1, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; const angle = Math.random() * 2 * Math.PI; - dst[0] = Math.cos(angle) * scale; - dst[1] = Math.sin(angle) * scale; + newDst[0] = Math.cos(angle) * scale; + newDst[1] = Math.sin(angle) * scale; - return dst; + return newDst; } /** @@ -595,13 +629,13 @@ export function random(scale = 1, dst?: Vec2): Vec2 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The zeroed vector. */ -export function zero(dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function zero(dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; - dst[0] = 0; - dst[1] = 0; + newDst[0] = 0; + newDst[1] = 0; - return dst; + return newDst; } @@ -612,16 +646,16 @@ export function zero(dst?: Vec2): Vec2 { * @param dst - optional Vec2 to store result. If not passed a new one is created. * @returns the transformed vector */ -export function transformMat4(v: Vec2, m: Mat4, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function transformMat4(v: Vec2Arg, m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; const x = v[0]; const y = v[1]; - dst[0] = x * m[0] + y * m[4] + m[12]; - dst[1] = x * m[1] + y * m[5] + m[13]; + newDst[0] = x * m[0] + y * m[4] + m[12]; + newDst[1] = x * m[1] + y * m[5] + m[13]; - return dst; + return newDst; } /** @@ -632,16 +666,16 @@ export function transformMat4(v: Vec2, m: Mat4, dst?: Vec2): Vec2 { * @param dst - optional Vec2 to store result. If not passed a new one is created. * @returns the transformed vector */ -export function transformMat3(v: Vec2, m: Mat3, dst?: Vec2): Vec2 { - dst = dst || new VecType(2); +function transformMat3(v: Vec2Arg, m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; const x = v[0]; const y = v[1]; - dst[0] = m[0] * x + m[4] * y + m[8]; - dst[1] = m[1] * x + m[5] * y + m[9]; + newDst[0] = m[0] * x + m[4] * y + m[8]; + newDst[1] = m[1] * x + m[5] * y + m[9]; - return dst; + return newDst; } /** @@ -652,8 +686,8 @@ export function transformMat3(v: Vec2, m: Mat3, dst?: Vec2): Vec2 { * @param rad The angle of rotation in radians * @returns the rotated vector */ -export function rotate(a: Vec2, b: Vec2, rad: number, dst?: Vec2) { - dst = dst || new VecType(2); +function rotate(a: Vec2Arg, b: Vec2Arg, rad: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; // Translate point to the origin const p0 = a[0] - b[0]; @@ -662,10 +696,10 @@ export function rotate(a: Vec2, b: Vec2, rad: number, dst?: Vec2) { const cosC = Math.cos(rad); //perform rotation and translate to correct position - dst[0] = p0 * cosC - p1 * sinC + b[0]; - dst[1] = p0 * sinC + p1 * cosC + b[1]; + newDst[0] = p0 * cosC - p1 * sinC + b[0]; + newDst[1] = p0 * sinC + p1 * cosC + b[1]; - return dst; + return newDst; } /** @@ -675,10 +709,10 @@ export function rotate(a: Vec2, b: Vec2, rad: number, dst?: Vec2) { * @param len The length of the resulting vector * @returns The lengthened vector */ -export function setLength(a: Vec2, len: number, dst?: Vec2) { - dst = dst || new VecType(2); - normalize(a, dst); - return mulScalar(dst, len, dst); +function setLength(a: Vec2Arg, len: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; + normalize(a, newDst); + return mulScalar(newDst, len, newDst); } /** @@ -688,14 +722,14 @@ export function setLength(a: Vec2, len: number, dst?: Vec2) { * @param maxLen The longest length of the resulting vector * @returns The vector, shortened to maxLen if it's too long */ -export function truncate(a: Vec2, maxLen: number, dst?: Vec2) { - dst = dst || new VecType(2); +function truncate(a: Vec2Arg, maxLen: number, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; if (length(a) > maxLen) { - return setLength(a, maxLen, dst); + return setLength(a, maxLen, newDst); } - return copy(a, dst); + return copy(a, newDst); } /** @@ -705,7 +739,73 @@ export function truncate(a: Vec2, maxLen: number, dst?: Vec2) { * @param b Endpoint 2 * @returns The vector exactly residing between endpoints 1 and 2 */ -export function midpoint(a: Vec2, b: Vec2, dst?: Vec2) { - dst = dst || new VecType(2); - return lerp(a, b, 0.5, dst); +function midpoint(a: Vec2Arg, b: Vec2Arg, dst?: T) { + const newDst = (dst ?? new Ctor(2)) as T; + return lerp(a, b, 0.5, newDst); +} + +return { + create, + fromValues, + set, + ceil, + floor, + round, + clamp, + add, + addScaled, + angle, + subtract, + sub, + equalsApproximately, + equals, + lerp, + lerpV, + max, + min, + mulScalar, + scale, + divScalar, + inverse, + invert, + cross, + dot, + length, + len, + lengthSq, + lenSq, + distance, + dist, + distanceSq, + distSq, + normalize, + negate, + copy, + clone, + multiply, + mul, + divide, + div, + random, + zero, + transformMat4, + transformMat3, + rotate, + setLength, + truncate, + midpoint, +}; +} + +type API = ReturnType>; + +const cache = new Map(); + +export function getAPI(Ctor: Vec2Ctor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; } diff --git a/src/vec2.ts b/src/vec2.ts index 59766bf..f314859 100644 --- a/src/vec2.ts +++ b/src/vec2.ts @@ -20,83 +20,14 @@ * DEALINGS IN THE SOFTWARE. */ -/** - * A JavaScript array with 2 values, Float32Array with 2 values, or a Float64Array with 2 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link vec2.setDefaultType}. - */ -export type Vec2 = number[] | Float32Array | Float64Array; - -/** - * - * Vec2 math functions. - * - * Almost all functions take an optional `dst` argument. If it is not passed in the - * functions will create a new Vec2. In other words you can do this - * - * const v = vec2.cross(v1, v2); // Creates a new Vec2 with the cross product of v1 x v2. - * - * or - * - * const v = vec2.create(); - * vec2.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v - * - * The first style is often easier but depending on where it's used it generates garbage where - * as there is almost never allocation with the second style. - * - * It is always safe to pass any vector as the destination. So for example - * - * vec2.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 - * - */ - -export let VecType: new (n: number) => Vec2 = Float32Array; +import { BaseArgType } from "./types"; /** - * Sets the type this library creates for a Vec2 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Vec2 + * A JavaScript array with 2 values, a Float32Array with 2 values, or a Float64Array with 2 values. */ -export function setDefaultType(ctor: new (n: number) => Vec2) { - const oldType = VecType; - VecType = ctor; - return oldType; -} +export type Vec2Arg = BaseArgType; /** - * Creates a Vec2; may be called with x, y, z to set initial values. - * - * Note: Since passing in a raw JavaScript array - * is valid in all circumstances, if you want to - * force a JavaScript array into a Vec2's specified type - * it would be faster to use - * - * ``` - * const v = vec2.clone(someJSArray); - * ``` - * - * Note: a consequence of the implementation is if your Vec2Type = `Array` - * instead of `Float32Array` or `Float64Array` then any values you - * don't pass in will be undefined. Usually this is not an issue since - * (a) using `Array` is rare and (b) using `vec2.create` is usually used - * to create a Vec2 to be filled out as in - * - * ``` - * const sum = vec2.create(); - * vec2.add(v1, v2, sum); - * ``` - * - * @param x - Initial x value. - * @param y - Initial y value. - * @returns the created vector + * A specific concrete 2 element vector. */ -export function create(x = 0, y = 0): Vec2 { - const dst = new VecType(2); - if (x !== undefined) { - dst[0] = x; - if (y !== undefined) { - dst[1] = y; - } - } - return dst; -} +export type Vec2Type = T; diff --git a/src/vec3-impl.ts b/src/vec3-impl.ts index 34d43a0..08d2a81 100644 --- a/src/vec3-impl.ts +++ b/src/vec3-impl.ts @@ -20,13 +20,41 @@ * DEALINGS IN THE SOFTWARE. */ import * as utils from './utils.js'; -import { Vec3, create, setDefaultType, VecType } from './vec3'; -import { Mat3 } from './mat3'; -import { Mat4 } from './mat4'; -import { Quat } from './quat'; +import { Vec3Arg, Vec3Type } from './vec3'; +import { Mat3Arg } from './mat3'; +import { Mat4Arg } from './mat4'; +import { QuatArg } from './quat'; +import { BaseArgType } from './types'; -export default Vec3; -export { create, setDefaultType }; +export { Vec3Arg, Vec3Type }; + +type Vec3Ctor = new (n: number) => T; + +/** + * Generates am typed API for Vec3 + * */ +function getAPIImpl(Ctor: Vec3Ctor) { + +/** + * Creates a vec3; may be called with x, y, z to set initial values. + * @param x - Initial x value. + * @param y - Initial y value. + * @param z - Initial z value. + * @returns the created vector + */ +function create(x?: number, y?: number, z?: number) { + const newDst = new Ctor(3); + if (x !== undefined) { + newDst[0] = x; + if (y !== undefined) { + newDst[1] = y; + if (z !== undefined) { + newDst[2] = z; + } + } + } + return newDst; +} /** * Creates a vec3; may be called with x, y, z to set initial values. (same as create) @@ -35,7 +63,7 @@ export { create, setDefaultType }; * @param z - Initial z value. * @returns the created vector */ -export const fromValues = create; +const fromValues = create; /** * Sets the values of a Vec3 @@ -47,14 +75,14 @@ export const fromValues = create; * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ -export function set(x: number, y: number, z: number, dst?: Vec3) { - dst = dst || new VecType(3); +function set(x: number, y: number, z: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = x; - dst[1] = y; - dst[2] = z; + newDst[0] = x; + newDst[1] = y; + newDst[2] = z; - return dst; + return newDst; } /** @@ -63,14 +91,14 @@ export function set(x: number, y: number, z: number, dst?: Vec3) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the ceil of each element of v. */ -export function ceil(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function ceil(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.ceil(v[0]); - dst[1] = Math.ceil(v[1]); - dst[2] = Math.ceil(v[2]); + newDst[0] = Math.ceil(v[0]); + newDst[1] = Math.ceil(v[1]); + newDst[2] = Math.ceil(v[2]); - return dst; + return newDst; } /** @@ -79,14 +107,14 @@ export function ceil(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the floor of each element of v. */ -export function floor(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function floor(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.floor(v[0]); - dst[1] = Math.floor(v[1]); - dst[2] = Math.floor(v[2]); + newDst[0] = Math.floor(v[0]); + newDst[1] = Math.floor(v[1]); + newDst[2] = Math.floor(v[2]); - return dst; + return newDst; } /** @@ -95,14 +123,14 @@ export function floor(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the round of each element of v. */ -export function round(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function round(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.round(v[0]); - dst[1] = Math.round(v[1]); - dst[2] = Math.round(v[2]); + newDst[0] = Math.round(v[0]); + newDst[1] = Math.round(v[1]); + newDst[2] = Math.round(v[2]); - return dst; + return newDst; } /** @@ -113,14 +141,14 @@ export function round(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that the clamped value of each element of v. */ -export function clamp(v: Vec3, min = 0, max = 1, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function clamp(v: Vec3Arg, min = 0, max = 1, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.min(max, Math.max(min, v[0])); - dst[1] = Math.min(max, Math.max(min, v[1])); - dst[2] = Math.min(max, Math.max(min, v[2])); + newDst[0] = Math.min(max, Math.max(min, v[0])); + newDst[1] = Math.min(max, Math.max(min, v[1])); + newDst[2] = Math.min(max, Math.max(min, v[2])); - return dst; + return newDst; } /** @@ -130,14 +158,14 @@ export function clamp(v: Vec3, min = 0, max = 1, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a and b. */ -export function add(a: Vec3, b: Vec3, dst?: Vec3) { - dst = dst || new VecType(3); +function add(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] + b[0]; - dst[1] = a[1] + b[1]; - dst[2] = a[2] + b[2]; + newDst[0] = a[0] + b[0]; + newDst[1] = a[1] + b[1]; + newDst[2] = a[2] + b[2]; - return dst; + return newDst; } /** @@ -148,14 +176,14 @@ export function add(a: Vec3, b: Vec3, dst?: Vec3) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a + b * scale. */ -export function addScaled(a: Vec3, b: Vec3, scale: number, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function addScaled(a: Vec3Arg, b: Vec3Arg, scale: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] + b[0] * scale; - dst[1] = a[1] + b[1] * scale; - dst[2] = a[2] + b[2] * scale; + newDst[0] = a[0] + b[0] * scale; + newDst[1] = a[1] + b[1] * scale; + newDst[2] = a[2] + b[2] * scale; - return dst; + return newDst; } /** @@ -164,7 +192,7 @@ export function addScaled(a: Vec3, b: Vec3, scale: number, dst?: Vec3): Vec3 { * @param b - Operand vector. * @returns The angle in radians between the 2 vectors. */ -export function angle(a: Vec3, b: Vec3): number { +function angle(a: Vec3Arg, b: Vec3Arg): number { const ax = a[0]; const ay = a[1]; const az = a[2]; @@ -185,14 +213,14 @@ export function angle(a: Vec3, b: Vec3): number { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export function subtract(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function subtract(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] - b[0]; - dst[1] = a[1] - b[1]; - dst[2] = a[2] - b[2]; + newDst[0] = a[0] - b[0]; + newDst[1] = a[1] - b[1]; + newDst[2] = a[2] - b[2]; - return dst; + return newDst; } /** @@ -202,7 +230,7 @@ export function subtract(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export const sub = subtract; +const sub = subtract; /** * Check if 2 vectors are approximately equal @@ -210,7 +238,7 @@ export const sub = subtract; * @param b - Operand vector. * @returns true if vectors are approximately equal */ -export function equalsApproximately(a: Vec3, b: Vec3): boolean { +function equalsApproximately(a: Vec3Arg, b: Vec3Arg): boolean { return Math.abs(a[0] - b[0]) < utils.EPSILON && Math.abs(a[1] - b[1]) < utils.EPSILON && Math.abs(a[2] - b[2]) < utils.EPSILON; @@ -222,7 +250,7 @@ export function equalsApproximately(a: Vec3, b: Vec3): boolean { * @param b - Operand vector. * @returns true if vectors are exactly equal */ -export function equals(a: Vec3, b: Vec3): boolean { +function equals(a: Vec3Arg, b: Vec3Arg): boolean { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]; } @@ -236,14 +264,14 @@ export function equals(a: Vec3, b: Vec3): boolean { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ -export function lerp(a: Vec3, b: Vec3, t: number, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function lerp(a: Vec3Arg, b: Vec3Arg, t: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] + t * (b[0] - a[0]); - dst[1] = a[1] + t * (b[1] - a[1]); - dst[2] = a[2] + t * (b[2] - a[2]); + newDst[0] = a[0] + t * (b[0] - a[0]); + newDst[1] = a[1] + t * (b[1] - a[1]); + newDst[2] = a[2] + t * (b[2] - a[2]); - return dst; + return newDst; } /** @@ -256,14 +284,14 @@ export function lerp(a: Vec3, b: Vec3, t: number, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns the linear interpolated result. */ -export function lerpV(a: Vec3, b: Vec3, t: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function lerpV(a: Vec3Arg, b: Vec3Arg, t: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] + t[0] * (b[0] - a[0]); - dst[1] = a[1] + t[1] * (b[1] - a[1]); - dst[2] = a[2] + t[2] * (b[2] - a[2]); + newDst[0] = a[0] + t[0] * (b[0] - a[0]); + newDst[1] = a[1] + t[1] * (b[1] - a[1]); + newDst[2] = a[2] + t[2] * (b[2] - a[2]); - return dst; + return newDst; } /** @@ -275,14 +303,14 @@ export function lerpV(a: Vec3, b: Vec3, t: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The max components vector. */ -export function max(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function max(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.max(a[0], b[0]); - dst[1] = Math.max(a[1], b[1]); - dst[2] = Math.max(a[2], b[2]); + newDst[0] = Math.max(a[0], b[0]); + newDst[1] = Math.max(a[1], b[1]); + newDst[2] = Math.max(a[2], b[2]); - return dst; + return newDst; } /** @@ -294,14 +322,14 @@ export function max(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The min components vector. */ -export function min(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function min(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = Math.min(a[0], b[0]); - dst[1] = Math.min(a[1], b[1]); - dst[2] = Math.min(a[2], b[2]); + newDst[0] = Math.min(a[0], b[0]); + newDst[1] = Math.min(a[1], b[1]); + newDst[2] = Math.min(a[2], b[2]); - return dst; + return newDst; } /** @@ -311,14 +339,14 @@ export function min(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function mulScalar(v: Vec3, k: number, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function mulScalar(v: Vec3Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = v[0] * k; - dst[1] = v[1] * k; - dst[2] = v[2] * k; + newDst[0] = v[0] * k; + newDst[1] = v[1] * k; + newDst[2] = v[2] * k; - return dst; + return newDst; } /** @@ -328,7 +356,7 @@ export function mulScalar(v: Vec3, k: number, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export const scale = mulScalar; +const scale = mulScalar; /** * Divides a vector by a scalar. @@ -337,14 +365,14 @@ export const scale = mulScalar; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function divScalar(v: Vec3, k: number, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function divScalar(v: Vec3Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = v[0] / k; - dst[1] = v[1] / k; - dst[2] = v[2] / k; + newDst[0] = v[0] / k; + newDst[1] = v[1] / k; + newDst[2] = v[2] / k; - return dst; + return newDst; } /** @@ -353,14 +381,14 @@ export function divScalar(v: Vec3, k: number, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export function inverse(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function inverse(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = 1 / v[0]; - dst[1] = 1 / v[1]; - dst[2] = 1 / v[2]; + newDst[0] = 1 / v[0]; + newDst[1] = 1 / v[1]; + newDst[2] = 1 / v[2]; - return dst; + return newDst; } /** @@ -369,7 +397,7 @@ export function inverse(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export const invert = inverse; +const invert = inverse; /** * Computes the cross product of two vectors; assumes both vectors have @@ -379,16 +407,16 @@ export const invert = inverse; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of a cross b. */ -export function cross(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function cross(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const t1 = a[2] * b[0] - a[0] * b[2]; const t2 = a[0] * b[1] - a[1] * b[0]; - dst[0] = a[1] * b[2] - a[2] * b[1]; - dst[1] = t1; - dst[2] = t2; + newDst[0] = a[1] * b[2] - a[2] * b[1]; + newDst[1] = t1; + newDst[2] = t2; - return dst; + return newDst; } /** @@ -398,7 +426,7 @@ export function cross(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { * @param b - Operand vector. * @returns dot product */ -export function dot(a: Vec3, b: Vec3): number { +function dot(a: Vec3Arg, b: Vec3Arg): number { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); } @@ -407,7 +435,7 @@ export function dot(a: Vec3, b: Vec3): number { * @param v - vector. * @returns length of vector. */ -export function length(v: Vec3): number { +function length(v: Vec3Arg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -419,14 +447,14 @@ export function length(v: Vec3): number { * @param v - vector. * @returns length of vector. */ -export const len = length; +const len = length; /** * Computes the square of the length of vector * @param v - vector. * @returns square of the length of vector. */ -export function lengthSq(v: Vec3): number { +function lengthSq(v: Vec3Arg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -438,7 +466,7 @@ export function lengthSq(v: Vec3): number { * @param v - vector. * @returns square of the length of vector. */ -export const lenSq = lengthSq; +const lenSq = lengthSq; /** * Computes the distance between 2 points @@ -446,7 +474,7 @@ export const lenSq = lengthSq; * @param b - vector. * @returns distance between a and b */ -export function distance(a: Vec3, b: Vec3): number { +function distance(a: Vec3Arg, b: Vec3Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; @@ -459,7 +487,7 @@ export function distance(a: Vec3, b: Vec3): number { * @param b - vector. * @returns distance between a and b */ -export const dist = distance; +const dist = distance; /** * Computes the square of the distance between 2 points @@ -467,7 +495,7 @@ export const dist = distance; * @param b - vector. * @returns square of the distance between a and b */ -export function distanceSq(a: Vec3, b: Vec3): number { +function distanceSq(a: Vec3Arg, b: Vec3Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; @@ -480,7 +508,7 @@ export function distanceSq(a: Vec3, b: Vec3): number { * @param b - vector. * @returns square of the distance between a and b */ -export const distSq = distanceSq; +const distSq = distanceSq; /** * Divides a vector by its Euclidean length and returns the quotient. @@ -488,8 +516,8 @@ export const distSq = distanceSq; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The normalized vector. */ -export function normalize(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function normalize(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const v0 = v[0]; const v1 = v[1]; @@ -497,17 +525,17 @@ export function normalize(v: Vec3, dst?: Vec3): Vec3 { const len = Math.sqrt(v0 * v0 + v1 * v1 + v2 * v2); if (len > 0.00001) { - dst[0] = v0 / len; - dst[1] = v1 / len; - dst[2] = v2 / len; + newDst[0] = v0 / len; + newDst[1] = v1 / len; + newDst[2] = v2 / len; } else { - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; } - return dst; + return newDst; } /** @@ -516,14 +544,14 @@ export function normalize(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns -v. */ -export function negate(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function negate(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = -v[0]; - dst[1] = -v[1]; - dst[2] = -v[2]; + newDst[0] = -v[0]; + newDst[1] = -v[1]; + newDst[2] = -v[2]; - return dst; + return newDst; } /** @@ -533,14 +561,14 @@ export function negate(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export function copy(v: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function copy(v: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = v[0]; - dst[1] = v[1]; - dst[2] = v[2]; + newDst[0] = v[0]; + newDst[1] = v[1]; + newDst[2] = v[2]; - return dst; + return newDst; } /** @@ -550,7 +578,7 @@ export function copy(v: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export const clone = copy; +const clone = copy; /** * Multiplies a vector by another vector (component-wise); assumes a and @@ -560,14 +588,14 @@ export const clone = copy; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export function multiply(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function multiply(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] * b[0]; - dst[1] = a[1] * b[1]; - dst[2] = a[2] * b[2]; + newDst[0] = a[0] * b[0]; + newDst[1] = a[1] * b[1]; + newDst[2] = a[2] * b[2]; - return dst; + return newDst; } /** @@ -578,7 +606,7 @@ export function multiply(a: Vec3, b: Vec3, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export const mul = multiply; +const mul = multiply; /** * Divides a vector by another vector (component-wise); assumes a and @@ -588,14 +616,14 @@ export const mul = multiply; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export function divide(a: Vec3, b: Vec3, dst?: Vec3) { - dst = dst || new VecType(3); +function divide(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = a[0] / b[0]; - dst[1] = a[1] / b[1]; - dst[2] = a[2] / b[2]; + newDst[0] = a[0] / b[0]; + newDst[1] = a[1] / b[1]; + newDst[2] = a[2] / b[2]; - return dst; + return newDst; } /** @@ -606,7 +634,7 @@ export function divide(a: Vec3, b: Vec3, dst?: Vec3) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export const div = divide; +const div = divide; /** * Creates a random vector @@ -614,17 +642,17 @@ export const div = divide; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The random vector. */ -export function random(scale = 1, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function random(scale = 1, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const angle = Math.random() * 2 * Math.PI; const z = Math.random() * 2 - 1; const zScale = Math.sqrt(1 - z * z) * scale; - dst[0] = Math.cos(angle) * zScale; - dst[1] = Math.sin(angle) * zScale; - dst[2] = z * scale; + newDst[0] = Math.cos(angle) * zScale; + newDst[1] = Math.sin(angle) * zScale; + newDst[2] = z * scale; - return dst; + return newDst; } /** @@ -632,14 +660,14 @@ export function random(scale = 1, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The zeroed vector. */ -export function zero(dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function zero(dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; - return dst; + return newDst; } @@ -650,40 +678,40 @@ export function zero(dst?: Vec3): Vec3 { * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed vector */ -export function transformMat4(v: Vec3, m: Mat4, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function transformMat4(v: Vec3Arg, m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const x = v[0]; const y = v[1]; const z = v[2]; const w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1; - dst[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; - dst[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; - dst[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + newDst[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + newDst[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + newDst[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; - return dst; + return newDst; } /** - * Transform vec4 by upper 3x3 matrix inside 4x4 matrix. + * Transform vec3 by upper 3x3 matrix inside 4x4 matrix. * @param v - The direction. * @param m - The matrix. - * @param dst - optional Vec3 to store result. If not passed a new one is created. + * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns The transformed vector. */ -export function transformMat4Upper3x3(v: Vec3, m: Mat4, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function transformMat4Upper3x3(v: Vec3Arg, m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; - dst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0]; - dst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1]; - dst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2]; + newDst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0]; + newDst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1]; + newDst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2]; - return dst; + return newDst; } /** @@ -694,18 +722,18 @@ export function transformMat4Upper3x3(v: Vec3, m: Mat4, dst?: Vec3): Vec3 { * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed vector */ -export function transformMat3(v: Vec3, m: Mat3, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function transformMat3(v: Vec3Arg, m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const x = v[0]; const y = v[1]; const z = v[2]; - dst[0] = x * m[0] + y * m[4] + z * m[8]; - dst[1] = x * m[1] + y * m[5] + z * m[9]; - dst[2] = x * m[2] + y * m[6] + z * m[10]; + newDst[0] = x * m[0] + y * m[4] + z * m[8]; + newDst[1] = x * m[1] + y * m[5] + z * m[9]; + newDst[2] = x * m[2] + y * m[6] + z * m[10]; - return dst; + return newDst; } /** @@ -715,8 +743,8 @@ export function transformMat3(v: Vec3, m: Mat3, dst?: Vec3): Vec3 { * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed */ -export function transformQuat(v: Vec3, q: Quat, dst?: Vec3): Vec3 { - dst = dst || new VecType(3); +function transformQuat(v: Vec3Arg, q: QuatArg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const qx = q[0]; const qy = q[1]; @@ -731,11 +759,11 @@ export function transformQuat(v: Vec3, q: Quat, dst?: Vec3): Vec3 { const uvY = qz * x - qx * z; const uvZ = qx * y - qy * x; - dst[0] = x + uvX * w2 + (qy * uvZ - qz * uvY) * 2; - dst[1] = y + uvY * w2 + (qz * uvX - qx * uvZ) * 2; - dst[2] = z + uvZ * w2 + (qx * uvY - qy * uvX) * 2; + newDst[0] = x + uvX * w2 + (qy * uvZ - qz * uvY) * 2; + newDst[1] = y + uvY * w2 + (qz * uvX - qx * uvZ) * 2; + newDst[2] = z + uvZ * w2 + (qx * uvY - qy * uvX) * 2; - return dst; + return newDst; } /** @@ -745,12 +773,12 @@ export function transformQuat(v: Vec3, q: Quat, dst?: Vec3): Vec3 { * @param dst - vector to hold result. If not passed a new one is created. * @returns The translation component of m. */ -export function getTranslation(m: Mat3, dst?: Vec3) { - dst = dst || new VecType(3); - dst[0] = m[12]; - dst[1] = m[13]; - dst[2] = m[14]; - return dst; +function getTranslation(m: Mat3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; + newDst[0] = m[12]; + newDst[1] = m[13]; + newDst[2] = m[14]; + return newDst; } /** * Returns an axis of a 4x4 matrix as a vector with 3 entries @@ -758,21 +786,21 @@ export function getTranslation(m: Mat3, dst?: Vec3) { * @param axis - The axis 0 = x, 1 = y, 2 = z; * @returns The axis component of m. */ -export function getAxis(m: Mat4, axis: number, dst?: Vec3) { - dst = dst || new VecType(3); +function getAxis(m: Mat4Arg, axis: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const off = axis * 4; - dst[0] = m[off + 0]; - dst[1] = m[off + 1]; - dst[2] = m[off + 2]; - return dst; + newDst[0] = m[off + 0]; + newDst[1] = m[off + 1]; + newDst[2] = m[off + 2]; + return newDst; } /** * Returns the scaling component of the matrix * @param m - The Matrix * @param dst - The vector to set. If not passed a new one is created. */ -export function getScaling(m: Mat4, dst: Vec3) { - dst = dst || new VecType(3); +function getScaling(m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const xx = m[0]; const xy = m[1]; const xz = m[2]; @@ -782,10 +810,10 @@ export function getScaling(m: Mat4, dst: Vec3) { const zx = m[8]; const zy = m[9]; const zz = m[10]; - dst[0] = Math.sqrt(xx * xx + xy * xy + xz * xz); - dst[1] = Math.sqrt(yx * yx + yy * yy + yz * yz); - dst[2] = Math.sqrt(zx * zx + zy * zy + zz * zz); - return dst; + newDst[0] = Math.sqrt(xx * xx + xy * xy + xz * xz); + newDst[1] = Math.sqrt(yx * yx + yy * yy + yz * yz); + newDst[2] = Math.sqrt(zx * zx + zy * zy + zz * zz); + return newDst; } /** @@ -797,8 +825,8 @@ export function getScaling(m: Mat4, dst: Vec3) { * @param dst - The vector to set. If not passed a new one is created. * @returns the rotated vector */ -export function rotateX(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { - dst = dst || new VecType(3); +function rotateX(a: Vec3Arg, b: Vec3Arg, rad: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const p = []; const r = []; @@ -813,11 +841,11 @@ export function rotateX(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); //translate to correct position - dst[0] = r[0] + b[0]; - dst[1] = r[1] + b[1]; - dst[2] = r[2] + b[2]; + newDst[0] = r[0] + b[0]; + newDst[1] = r[1] + b[1]; + newDst[2] = r[2] + b[2]; - return dst; + return newDst; } /** @@ -829,8 +857,8 @@ export function rotateX(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { * @param dst - The vector to set. If not passed a new one is created. * @returns the rotated vector */ -export function rotateY(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { - dst = dst || new VecType(3); +function rotateY(a: Vec3Arg, b: Vec3Arg, rad: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const p = []; const r = []; @@ -845,11 +873,11 @@ export function rotateY(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); // translate to correct position - dst[0] = r[0] + b[0]; - dst[1] = r[1] + b[1]; - dst[2] = r[2] + b[2]; + newDst[0] = r[0] + b[0]; + newDst[1] = r[1] + b[1]; + newDst[2] = r[2] + b[2]; - return dst; + return newDst; } /** @@ -861,8 +889,8 @@ export function rotateY(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { * @param dst - The vector to set. If not passed a new one is created. * @returns {vec3} out */ -export function rotateZ(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { - dst = dst || new VecType(3); +function rotateZ(a: Vec3Arg, b: Vec3Arg, rad: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; const p = []; const r = []; @@ -877,11 +905,11 @@ export function rotateZ(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { r[2] = p[2]; // translate to correct position - dst[0] = r[0] + b[0]; - dst[1] = r[1] + b[1]; - dst[2] = r[2] + b[2]; + newDst[0] = r[0] + b[0]; + newDst[1] = r[1] + b[1]; + newDst[2] = r[2] + b[2]; - return dst; + return newDst; } /** @@ -891,10 +919,10 @@ export function rotateZ(a: Vec3, b: Vec3, rad: number, dst?: Vec3) { * @param len The length of the resulting vector * @returns The lengthened vector */ -export function setLength(a: Vec3, len: number, dst?: Vec3) { - dst = dst || new VecType(3); - normalize(a, dst); - return mulScalar(dst, len, dst); +function setLength(a: Vec3Arg, len: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; + normalize(a, newDst); + return mulScalar(newDst, len, newDst); } /** @@ -904,14 +932,14 @@ export function setLength(a: Vec3, len: number, dst?: Vec3) { * @param maxLen The longest length of the resulting vector * @returns The vector, shortened to maxLen if it's too long */ -export function truncate(a: Vec3, maxLen: number, dst?: Vec3) { - dst = dst || new VecType(3); +function truncate(a: Vec3Arg, maxLen: number, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; if (length(a) > maxLen) { - return setLength(a, maxLen, dst); + return setLength(a, maxLen, newDst); } - return copy(a, dst); + return copy(a, newDst); } /** @@ -921,7 +949,81 @@ export function truncate(a: Vec3, maxLen: number, dst?: Vec3) { * @param b Endpoint 2 * @returns The vector exactly residing between endpoints 1 and 2 */ -export function midpoint(a: Vec3, b: Vec3, dst?: Vec3) { - dst = dst || new VecType(3); - return lerp(a, b, 0.5, dst); +function midpoint(a: Vec3Arg, b: Vec3Arg, dst?: T) { + const newDst = (dst ?? new Ctor(3)) as T; + return lerp(a, b, 0.5, newDst); +} + +return { + create, + fromValues, + set, + ceil, + floor, + round, + clamp, + add, + addScaled, + angle, + subtract, + sub, + equalsApproximately, + equals, + lerp, + lerpV, + max, + min, + mulScalar, + scale, + divScalar, + inverse, + invert, + cross, + dot, + length, + len, + lengthSq, + lenSq, + distance, + dist, + distanceSq, + distSq, + normalize, + negate, + copy, + clone, + multiply, + mul, + divide, + div, + random, + zero, + transformMat4, + transformMat4Upper3x3, + transformMat3, + transformQuat, + getTranslation, + getAxis, + getScaling, + rotateX, + rotateY, + rotateZ, + setLength, + truncate, + midpoint, +}; + +} + +type API = ReturnType>; + +const cache = new Map(); + +export function getAPI(Ctor: Vec3Ctor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; } diff --git a/src/vec3.ts b/src/vec3.ts index 62fe04d..d5501d3 100644 --- a/src/vec3.ts +++ b/src/vec3.ts @@ -20,66 +20,14 @@ * DEALINGS IN THE SOFTWARE. */ -/** - * A JavaScript array with 3 values, Float32Array with 3 values, or a Float64Array with 3 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link vec3.setDefaultType}. - */ -export type Vec3 = number[] | Float32Array | Float64Array; - -/** - * - * Vec3 math functions. - * - * Almost all functions take an optional `dst` argument. If it is not passed in the - * functions will create a new `Vec3`. In other words you can do this - * - * const v = vec3.cross(v1, v2); // Creates a new Vec3 with the cross product of v1 x v2. - * - * or - * - * const v = vec3.create(); - * vec3.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v - * - * The first style is often easier but depending on where it's used it generates garbage where - * as there is almost never allocation with the second style. - * - * It is always safe to pass any vector as the destination. So for example - * - * vec3.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 - * - */ - -export let VecType: new (n: number) => Vec3 = Float32Array; +import { BaseArgType } from "./types"; /** - * Sets the type this library creates for a Vec3 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Vec3 + * A JavaScript array with 3 values, a Float32Array with 3 values, or a Float64Array with 3 values. */ -export function setDefaultType(ctor: new (n: number) => Vec3) { - const oldType = VecType; - VecType = ctor; - return oldType; -} +export type Vec3Arg = BaseArgType; /** - * Creates a vec3; may be called with x, y, z to set initial values. - * @param x - Initial x value. - * @param y - Initial y value. - * @param z - Initial z value. - * @returns the created vector + * A specific concrete 3 element vector. */ -export function create(x?: number, y?: number, z?: number): Vec3 { - const dst = new VecType(3); - if (x !== undefined) { - dst[0] = x; - if (y !== undefined) { - dst[1] = y; - if (z !== undefined) { - dst[2] = z; - } - } - } - return dst; -} \ No newline at end of file +export type Vec3Type = T; diff --git a/src/vec4-impl.ts b/src/vec4-impl.ts index c5c52c2..e6f6d48 100644 --- a/src/vec4-impl.ts +++ b/src/vec4-impl.ts @@ -20,11 +20,43 @@ * DEALINGS IN THE SOFTWARE. */ import * as utils from './utils.js'; -import { Vec4, create, setDefaultType, VecType } from './vec4'; -import { Mat4 } from './mat4'; +import { Vec4Arg, Vec4Type } from './vec4'; +import { Mat4Arg } from './mat4'; +import { BaseArgType } from './types'; -export default Vec4; -export { create, setDefaultType }; +export { Vec4Arg, Vec4Type }; + +type Vec4Ctor = new (n: number) => T; + +/** + * Generates am typed API for Vec4 + * */ +function getAPIImpl(Ctor: Vec4Ctor) { + +/** + * Creates a vec4; may be called with x, y, z to set initial values. + * @param x - Initial x value. + * @param y - Initial y value. + * @param z - Initial z value. + * @param w - Initial w value. + * @returns the created vector + */ +function create(x?: number, y?: number, z?: number, w?: number) { + const newDst = new Ctor(4); + if (x !== undefined) { + newDst[0] = x; + if (y !== undefined) { + newDst[1] = y; + if (z !== undefined) { + newDst[2] = z; + if (w !== undefined) { + newDst[3] = w; + } + } + } + } + return newDst; +} /** * Creates a vec4; may be called with x, y, z to set initial values. (same as create) @@ -34,7 +66,7 @@ export { create, setDefaultType }; * @param z - Initial w value. * @returns the created vector */ -export const fromValues = create; +const fromValues = create; /** * Sets the values of a Vec4 @@ -47,15 +79,15 @@ export const fromValues = create; * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ -export function set(x: number, y: number, z: number, w: number, dst?: Vec4) { - dst = dst || new VecType(4); +function set(x: number, y: number, z: number, w: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = x; - dst[1] = y; - dst[2] = z; - dst[3] = w; + newDst[0] = x; + newDst[1] = y; + newDst[2] = z; + newDst[3] = w; - return dst; + return newDst; } /** @@ -64,15 +96,15 @@ export function set(x: number, y: number, z: number, w: number, dst?: Vec4) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the ceil of each element of v. */ -export function ceil(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function ceil(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.ceil(v[0]); - dst[1] = Math.ceil(v[1]); - dst[2] = Math.ceil(v[2]); - dst[3] = Math.ceil(v[3]); + newDst[0] = Math.ceil(v[0]); + newDst[1] = Math.ceil(v[1]); + newDst[2] = Math.ceil(v[2]); + newDst[3] = Math.ceil(v[3]); - return dst; + return newDst; } /** @@ -81,15 +113,15 @@ export function ceil(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the floor of each element of v. */ -export function floor(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function floor(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.floor(v[0]); - dst[1] = Math.floor(v[1]); - dst[2] = Math.floor(v[2]); - dst[3] = Math.floor(v[3]); + newDst[0] = Math.floor(v[0]); + newDst[1] = Math.floor(v[1]); + newDst[2] = Math.floor(v[2]); + newDst[3] = Math.floor(v[3]); - return dst; + return newDst; } /** @@ -98,15 +130,15 @@ export function floor(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the round of each element of v. */ -export function round(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function round(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.round(v[0]); - dst[1] = Math.round(v[1]); - dst[2] = Math.round(v[2]); - dst[3] = Math.round(v[3]); + newDst[0] = Math.round(v[0]); + newDst[1] = Math.round(v[1]); + newDst[2] = Math.round(v[2]); + newDst[3] = Math.round(v[3]); - return dst; + return newDst; } /** @@ -117,15 +149,15 @@ export function round(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that the clamped value of each element of v. */ -export function clamp(v: Vec4, min = 0, max = 1, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function clamp(v: Vec4Arg, min = 0, max = 1, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.min(max, Math.max(min, v[0])); - dst[1] = Math.min(max, Math.max(min, v[1])); - dst[2] = Math.min(max, Math.max(min, v[2])); - dst[3] = Math.min(max, Math.max(min, v[3])); + newDst[0] = Math.min(max, Math.max(min, v[0])); + newDst[1] = Math.min(max, Math.max(min, v[1])); + newDst[2] = Math.min(max, Math.max(min, v[2])); + newDst[3] = Math.min(max, Math.max(min, v[3])); - return dst; + return newDst; } /** @@ -135,15 +167,15 @@ export function clamp(v: Vec4, min = 0, max = 1, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a and b. */ -export function add(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function add(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + b[0]; - dst[1] = a[1] + b[1]; - dst[2] = a[2] + b[2]; - dst[3] = a[3] + b[3]; + newDst[0] = a[0] + b[0]; + newDst[1] = a[1] + b[1]; + newDst[2] = a[2] + b[2]; + newDst[3] = a[3] + b[3]; - return dst; + return newDst; } /** @@ -154,15 +186,15 @@ export function add(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a + b * scale. */ -export function addScaled(a: Vec4, b: Vec4, scale: number, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function addScaled(a: Vec4Arg, b: Vec4Arg, scale: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + b[0] * scale; - dst[1] = a[1] + b[1] * scale; - dst[2] = a[2] + b[2] * scale; - dst[3] = a[3] + b[3] * scale; + newDst[0] = a[0] + b[0] * scale; + newDst[1] = a[1] + b[1] * scale; + newDst[2] = a[2] + b[2] * scale; + newDst[3] = a[3] + b[3] * scale; - return dst; + return newDst; } /** @@ -172,15 +204,15 @@ export function addScaled(a: Vec4, b: Vec4, scale: number, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export function subtract(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function subtract(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] - b[0]; - dst[1] = a[1] - b[1]; - dst[2] = a[2] - b[2]; - dst[3] = a[3] - b[3]; + newDst[0] = a[0] - b[0]; + newDst[1] = a[1] - b[1]; + newDst[2] = a[2] - b[2]; + newDst[3] = a[3] - b[3]; - return dst; + return newDst; } /** @@ -190,7 +222,7 @@ export function subtract(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ -export const sub = subtract; +const sub = subtract; /** * Check if 2 vectors are approximately equal @@ -198,7 +230,7 @@ export const sub = subtract; * @param b - Operand vector. * @returns true if vectors are approximately equal */ -export function equalsApproximately(a: Vec4, b: Vec4): boolean { +function equalsApproximately(a: Vec4Arg, b: Vec4Arg): boolean { return Math.abs(a[0] - b[0]) < utils.EPSILON && Math.abs(a[1] - b[1]) < utils.EPSILON && Math.abs(a[2] - b[2]) < utils.EPSILON && @@ -211,7 +243,7 @@ export function equalsApproximately(a: Vec4, b: Vec4): boolean { * @param b - Operand vector. * @returns true if vectors are exactly equal */ -export function equals(a: Vec4, b: Vec4): boolean { +function equals(a: Vec4Arg, b: Vec4Arg): boolean { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; } @@ -225,15 +257,15 @@ export function equals(a: Vec4, b: Vec4): boolean { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ -export function lerp(a: Vec4, b: Vec4, t: number, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function lerp(a: Vec4Arg, b: Vec4Arg, t: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + t * (b[0] - a[0]); - dst[1] = a[1] + t * (b[1] - a[1]); - dst[2] = a[2] + t * (b[2] - a[2]); - dst[3] = a[3] + t * (b[3] - a[3]); + newDst[0] = a[0] + t * (b[0] - a[0]); + newDst[1] = a[1] + t * (b[1] - a[1]); + newDst[2] = a[2] + t * (b[2] - a[2]); + newDst[3] = a[3] + t * (b[3] - a[3]); - return dst; + return newDst; } /** @@ -246,15 +278,15 @@ export function lerp(a: Vec4, b: Vec4, t: number, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns the linear interpolated result. */ -export function lerpV(a: Vec4, b: Vec4, t: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function lerpV(a: Vec4Arg, b: Vec4Arg, t: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] + t[0] * (b[0] - a[0]); - dst[1] = a[1] + t[1] * (b[1] - a[1]); - dst[2] = a[2] + t[2] * (b[2] - a[2]); - dst[3] = a[3] + t[3] * (b[3] - a[3]); + newDst[0] = a[0] + t[0] * (b[0] - a[0]); + newDst[1] = a[1] + t[1] * (b[1] - a[1]); + newDst[2] = a[2] + t[2] * (b[2] - a[2]); + newDst[3] = a[3] + t[3] * (b[3] - a[3]); - return dst; + return newDst; } /** @@ -266,15 +298,15 @@ export function lerpV(a: Vec4, b: Vec4, t: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The max components vector. */ -export function max(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function max(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.max(a[0], b[0]); - dst[1] = Math.max(a[1], b[1]); - dst[2] = Math.max(a[2], b[2]); - dst[3] = Math.max(a[3], b[3]); + newDst[0] = Math.max(a[0], b[0]); + newDst[1] = Math.max(a[1], b[1]); + newDst[2] = Math.max(a[2], b[2]); + newDst[3] = Math.max(a[3], b[3]); - return dst; + return newDst; } /** @@ -286,15 +318,15 @@ export function max(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The min components vector. */ -export function min(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function min(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = Math.min(a[0], b[0]); - dst[1] = Math.min(a[1], b[1]); - dst[2] = Math.min(a[2], b[2]); - dst[3] = Math.min(a[3], b[3]); + newDst[0] = Math.min(a[0], b[0]); + newDst[1] = Math.min(a[1], b[1]); + newDst[2] = Math.min(a[2], b[2]); + newDst[3] = Math.min(a[3], b[3]); - return dst; + return newDst; } /** @@ -304,15 +336,15 @@ export function min(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function mulScalar(v: Vec4, k: number, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function mulScalar(v: Vec4Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = v[0] * k; - dst[1] = v[1] * k; - dst[2] = v[2] * k; - dst[3] = v[3] * k; + newDst[0] = v[0] * k; + newDst[1] = v[1] * k; + newDst[2] = v[2] * k; + newDst[3] = v[3] * k; - return dst; + return newDst; } /** @@ -322,7 +354,7 @@ export function mulScalar(v: Vec4, k: number, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export const scale = mulScalar; +const scale = mulScalar; /** * Divides a vector by a scalar. @@ -331,15 +363,15 @@ export const scale = mulScalar; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ -export function divScalar(v: Vec4, k: number, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function divScalar(v: Vec4Arg, k: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = v[0] / k; - dst[1] = v[1] / k; - dst[2] = v[2] / k; - dst[3] = v[3] / k; + newDst[0] = v[0] / k; + newDst[1] = v[1] / k; + newDst[2] = v[2] / k; + newDst[3] = v[3] / k; - return dst; + return newDst; } /** @@ -348,15 +380,15 @@ export function divScalar(v: Vec4, k: number, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export function inverse(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function inverse(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = 1 / v[0]; - dst[1] = 1 / v[1]; - dst[2] = 1 / v[2]; - dst[3] = 1 / v[3]; + newDst[0] = 1 / v[0]; + newDst[1] = 1 / v[1]; + newDst[2] = 1 / v[2]; + newDst[3] = 1 / v[3]; - return dst; + return newDst; } /** @@ -365,7 +397,7 @@ export function inverse(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ -export const invert = inverse; +const invert = inverse; /** * Computes the dot product of two vectors @@ -373,7 +405,7 @@ export const invert = inverse; * @param b - Operand vector. * @returns dot product */ -export function dot(a: Vec4, b: Vec4): number { +function dot(a: Vec4Arg, b: Vec4Arg): number { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]) + (a[3] * b[3]); } @@ -382,7 +414,7 @@ export function dot(a: Vec4, b: Vec4): number { * @param v - vector. * @returns length of vector. */ -export function length(v: Vec4): number { +function length(v: Vec4Arg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -395,14 +427,14 @@ export function length(v: Vec4): number { * @param v - vector. * @returns length of vector. */ -export const len = length; +const len = length; /** * Computes the square of the length of vector * @param v - vector. * @returns square of the length of vector. */ -export function lengthSq(v: Vec4): number { +function lengthSq(v: Vec4Arg): number { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; @@ -415,7 +447,7 @@ export function lengthSq(v: Vec4): number { * @param v - vector. * @returns square of the length of vector. */ -export const lenSq = lengthSq; +const lenSq = lengthSq; /** * Computes the distance between 2 points @@ -423,7 +455,7 @@ export const lenSq = lengthSq; * @param b - vector. * @returns distance between a and b */ -export function distance(a: Vec4, b: Vec4): number { +function distance(a: Vec4Arg, b: Vec4Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; @@ -437,7 +469,7 @@ export function distance(a: Vec4, b: Vec4): number { * @param b - vector. * @returns distance between a and b */ -export const dist = distance; +const dist = distance; /** * Computes the square of the distance between 2 points @@ -445,7 +477,7 @@ export const dist = distance; * @param b - vector. * @returns square of the distance between a and b */ -export function distanceSq(a: Vec4, b: Vec4): number { +function distanceSq(a: Vec4Arg, b: Vec4Arg): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; @@ -459,7 +491,7 @@ export function distanceSq(a: Vec4, b: Vec4): number { * @param b - vector. * @returns square of the distance between a and b */ -export const distSq = distanceSq; +const distSq = distanceSq; /** * Divides a vector by its Euclidean length and returns the quotient. @@ -467,8 +499,8 @@ export const distSq = distanceSq; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The normalized vector. */ -export function normalize(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function normalize(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const v0 = v[0]; const v1 = v[1]; @@ -477,18 +509,18 @@ export function normalize(v: Vec4, dst?: Vec4): Vec4 { const len = Math.sqrt(v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3); if (len > 0.00001) { - dst[0] = v0 / len; - dst[1] = v1 / len; - dst[2] = v2 / len; - dst[3] = v3 / len; + newDst[0] = v0 / len; + newDst[1] = v1 / len; + newDst[2] = v2 / len; + newDst[3] = v3 / len; } else { - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 0; } - return dst; + return newDst; } /** @@ -497,15 +529,15 @@ export function normalize(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns -v. */ -export function negate(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function negate(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = -v[0]; - dst[1] = -v[1]; - dst[2] = -v[2]; - dst[3] = -v[3]; + newDst[0] = -v[0]; + newDst[1] = -v[1]; + newDst[2] = -v[2]; + newDst[3] = -v[3]; - return dst; + return newDst; } /** @@ -515,15 +547,15 @@ export function negate(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export function copy(v: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function copy(v: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = v[0]; - dst[1] = v[1]; - dst[2] = v[2]; - dst[3] = v[3]; + newDst[0] = v[0]; + newDst[1] = v[1]; + newDst[2] = v[2]; + newDst[3] = v[3]; - return dst; + return newDst; } /** @@ -533,7 +565,7 @@ export function copy(v: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ -export const clone = copy; +const clone = copy; /** * Multiplies a vector by another vector (component-wise); assumes a and @@ -543,15 +575,15 @@ export const clone = copy; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export function multiply(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function multiply(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] * b[0]; - dst[1] = a[1] * b[1]; - dst[2] = a[2] * b[2]; - dst[3] = a[3] * b[3]; + newDst[0] = a[0] * b[0]; + newDst[1] = a[1] * b[1]; + newDst[2] = a[2] * b[2]; + newDst[3] = a[3] * b[3]; - return dst; + return newDst; } /** @@ -562,7 +594,7 @@ export function multiply(a: Vec4, b: Vec4, dst?: Vec4): Vec4 { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ -export const mul = multiply; +const mul = multiply; /** * Divides a vector by another vector (component-wise); assumes a and @@ -572,15 +604,15 @@ export const mul = multiply; * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export function divide(a: Vec4, b: Vec4, dst?: Vec4) { - dst = dst || new VecType(4); +function divide(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = a[0] / b[0]; - dst[1] = a[1] / b[1]; - dst[2] = a[2] / b[2]; - dst[3] = a[3] / b[3]; + newDst[0] = a[0] / b[0]; + newDst[1] = a[1] / b[1]; + newDst[2] = a[2] / b[2]; + newDst[3] = a[3] / b[3]; - return dst; + return newDst; } /** @@ -591,22 +623,22 @@ export function divide(a: Vec4, b: Vec4, dst?: Vec4) { * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ -export const div = divide; +const div = divide; /** * Zero's a vector * @param dst - vector to hold result. If not passed in a new one is created. * @returns The zeroed vector. */ -export function zero(dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function zero(dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; - dst[0] = 0; - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; + newDst[0] = 0; + newDst[1] = 0; + newDst[2] = 0; + newDst[3] = 0; - return dst; + return newDst; } @@ -617,20 +649,20 @@ export function zero(dst?: Vec4): Vec4 { * @param dst - optional vec4 to store result. If not passed a new one is created. * @returns the transformed vector */ -export function transformMat4(v: Vec4, m: Mat4, dst?: Vec4): Vec4 { - dst = dst || new VecType(4); +function transformMat4(v: Vec4Arg, m: Mat4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; const x = v[0]; const y = v[1]; const z = v[2]; const w = v[3]; - dst[0] = m[0] * x + m[4] * y + m[ 8] * z + m[12] * w; - dst[1] = m[1] * x + m[5] * y + m[ 9] * z + m[13] * w; - dst[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; - dst[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + newDst[0] = m[0] * x + m[4] * y + m[ 8] * z + m[12] * w; + newDst[1] = m[1] * x + m[5] * y + m[ 9] * z + m[13] * w; + newDst[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + newDst[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; - return dst; + return newDst; } @@ -641,10 +673,10 @@ export function transformMat4(v: Vec4, m: Mat4, dst?: Vec4): Vec4 { * @param len The length of the resulting vector * @returns The lengthened vector */ -export function setLength(a: Vec4, len: number, dst?: Vec4) { - dst = dst || new VecType(4); - normalize(a, dst); - return mulScalar(dst, len, dst); +function setLength(a: Vec4Arg, len: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; + normalize(a, newDst); + return mulScalar(newDst, len, newDst); } /** @@ -654,14 +686,14 @@ export function setLength(a: Vec4, len: number, dst?: Vec4) { * @param maxLen The longest length of the resulting vector * @returns The vector, shortened to maxLen if it's too long */ -export function truncate(a: Vec4, maxLen: number, dst?: Vec4) { - dst = dst || new VecType(4); +function truncate(a: Vec4Arg, maxLen: number, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; if (length(a) > maxLen) { - return setLength(a, maxLen, dst); + return setLength(a, maxLen, newDst); } - return copy(a, dst); + return copy(a, newDst); } /** @@ -671,7 +703,91 @@ export function truncate(a: Vec4, maxLen: number, dst?: Vec4) { * @param b Endpoint 2 * @returns The vector exactly residing between endpoints 1 and 2 */ -export function midpoint(a: Vec4, b: Vec4, dst?: Vec4) { - dst = dst || new VecType(4); - return lerp(a, b, 0.5, dst); +function midpoint(a: Vec4Arg, b: Vec4Arg, dst?: T) { + const newDst = (dst ?? new Ctor(4)) as T; + return lerp(a, b, 0.5, newDst); +} + +return { + create, + fromValues, + set, + ceil, + floor, + round, + clamp, + add, + addScaled, + subtract, + sub, + equalsApproximately, + equals, + lerp, + lerpV, + max, + min, + mulScalar, + scale, + divScalar, + inverse, + invert, + dot, + length, + len, + lengthSq, + lenSq, + distance, + dist, + distanceSq, + distSq, + normalize, + negate, + copy, + clone, + multiply, + mul, + divide, + div, + zero, + transformMat4, + setLength, + truncate, + midpoint, +}; +} + +type API = ReturnType>; + +const cache = new Map(); + +/** + * + * Vec4 math functions. + * + * Almost all functions take an optional `newDst` argument. If it is not passed in the + * functions will create a new `Vec4`. In other words you can do this + * + * const v = vec4.cross(v1, v2); // Creates a new Vec4 with the cross product of v1 x v2. + * + * or + * + * const v = vec4.create(); + * vec4.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v + * + * The first style is often easier but depending on where it's used it generates garbage where + * as there is almost never allocation with the second style. + * + * It is always safe to pass any vector as the destination. So for example + * + * vec4.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 + * + */ +export function getAPI(Ctor: Vec4Ctor) { + let api = cache.get(Ctor); + if (!api) { + api = getAPIImpl(Ctor); + cache.set(Ctor, api); + } + return api as API; } + diff --git a/src/vec4.ts b/src/vec4.ts index 77c47b0..2fad69a 100644 --- a/src/vec4.ts +++ b/src/vec4.ts @@ -20,70 +20,14 @@ * DEALINGS IN THE SOFTWARE. */ -/** - * A JavaScript array with 4 values, Float32Array with 4 values, or a Float64Array with 4 values. - * When created by the library will create the default type which is `Float32Array` - * but can be set by calling {@link vec4.setDefaultType}. - */ -export type Vec4 = number[] | Float32Array | Float64Array; - -/** - * - * Vec4 math functions. - * - * Almost all functions take an optional `dst` argument. If it is not passed in the - * functions will create a new `Vec4`. In other words you can do this - * - * const v = vec4.cross(v1, v2); // Creates a new Vec4 with the cross product of v1 x v2. - * - * or - * - * const v = vec4.create(); - * vec4.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v - * - * The first style is often easier but depending on where it's used it generates garbage where - * as there is almost never allocation with the second style. - * - * It is always safe to pass any vector as the destination. So for example - * - * vec4.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1 - * - */ - -export let VecType: new (n: number) => Vec4 = Float32Array; +import { BaseArgType } from "./types"; /** - * Sets the type this library creates for a Vec4 - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` - * @returns previous constructor for Vec4 + * A JavaScript array with 4 values, a Float32Array with 4 values, or a Float64Array with 4 values. */ -export function setDefaultType(ctor: new (n: number) => Vec4) { - const oldType = VecType; - VecType = ctor; - return oldType; -} +export type Vec4Arg = BaseArgType; /** - * Creates a vec4; may be called with x, y, z to set initial values. - * @param x - Initial x value. - * @param y - Initial y value. - * @param z - Initial z value. - * @param w - Initial w value. - * @returns the created vector + * A specific concrete 4 element vector. */ -export function create(x?: number, y?: number, z?: number, w?: number): Vec4 { - const dst = new VecType(4); - if (x !== undefined) { - dst[0] = x; - if (y !== undefined) { - dst[1] = y; - if (z !== undefined) { - dst[2] = z; - if (w !== undefined) { - dst[3] = w; - } - } - } - } - return dst; -} \ No newline at end of file +export type Vec4Type = T; diff --git a/src/wgpu-matrix.ts b/src/wgpu-matrix.ts index c9687f5..9bc0826 100644 --- a/src/wgpu-matrix.ts +++ b/src/wgpu-matrix.ts @@ -1,43 +1,137 @@ -import Mat3, * as mat3 from './mat3-impl'; -import Mat4, * as mat4 from './mat4-impl'; -import Quat, * as quat from './quat-impl'; -import Vec2, * as vec2 from './vec2-impl'; -import Vec3, * as vec3 from './vec3-impl'; -import Vec4, * as vec4 from './vec4-impl'; +import {BaseArgType, ZeroArray} from './types'; +import {Mat3Arg, Mat3Type, getAPI as getMat3API} from './mat3-impl'; +import {Mat4Arg, Mat4Type, getAPI as getMat4API} from './mat4-impl'; +import {QuatArg, QuatType, getAPI as getQuatAPI, RotationOrder} from './quat-impl'; +import {Vec2Arg, Vec2Type, getAPI as getVec2API} from './vec2-impl'; +import {Vec3Arg, Vec3Type, getAPI as getVec3API} from './vec3-impl'; +import {Vec4Arg, Vec4Type, getAPI as getVec4API} from './vec4-impl'; import * as utils from './utils'; +export { + RotationOrder, + utils, + + BaseArgType, + + Mat3Arg, + Mat4Arg, + QuatArg, + Vec2Arg, + Vec3Arg, + Vec4Arg, + + Mat3Type, + Mat4Type, + QuatType, + Vec2Type, + Vec3Type, + Vec4Type, +}; + +export type BaseCtor = new (n: number) => T; + +export type Mat3 = Mat3Type; +export type Mat4 = Mat4Type; +export type Quat = QuatType; +export type Vec2 = Vec2Type; +export type Vec3 = Vec3Type; +export type Vec4 = Vec4Type; + +export type Mat3d = Mat3Type; +export type Mat4d = Mat4Type; +export type Quatd = QuatType; +export type Vec2d = Vec2Type; +export type Vec3d = Vec3Type; +export type Vec4d = Vec4Type; + +export type Mat3n = Mat3Type; +export type Mat4n = Mat4Type; +export type Quatn = QuatType; +export type Vec2n = Vec2Type; +export type Vec3n = Vec3Type; +export type Vec4n = Vec4Type; + /** - * Sets the type this library creates for all types - * - * example: - * - * ``` - * setDefaultType(Float64Array); - * ``` - * - * @param ctor - the constructor for the type. Either `Float32Array`, `Float64Array`, or `Array` + * Generate wgpu-matrix API for type */ -export function setDefaultType(ctor: new (n: number) => Float32Array | Float64Array | number[]) { - mat3.setDefaultType(ctor); - mat4.setDefaultType(ctor); - quat.setDefaultType(ctor); - vec2.setDefaultType(ctor); - vec3.setDefaultType(ctor); - vec4.setDefaultType(ctor); +function wgpuMatrixAPI< + Mat3 extends BaseArgType, + Mat4 extends BaseArgType, + Quat extends BaseArgType, + Vec2 extends BaseArgType, + Vec3 extends BaseArgType, + Vec4 extends BaseArgType, +>( + Mat3Ctor: BaseCtor, + Mat4Ctor: BaseCtor, + QuatCtor: BaseCtor, + Vec2Ctor: BaseCtor, + Vec3Ctor: BaseCtor, + Vec4Ctor: BaseCtor, +) { + return { + /** @namespace mat4 */ + mat4: getMat4API(Mat3Ctor), + /** @namespace mat3 */ + mat3: getMat3API(Mat4Ctor), + /** @namespace quat */ + quat: getQuatAPI(QuatCtor), + /** @namespace vec2 */ + vec2: getVec2API(Vec2Ctor), + /** @namespace vec3 */ + vec3: getVec3API(Vec3Ctor), + /** @namespace vec4 */ + vec4: getVec4API(Vec4Ctor), + }; } -export { - Mat3, - mat3, - Mat4, +export const { + /** @namespace */ mat4, - Quat, + /** @namespace */ + mat3, + /** @namespace */ quat, - utils, - Vec2, + /** @namespace */ vec2, - Vec3, + /** @namespace */ vec3, - Vec4, + /** @namespace */ vec4, -}; \ No newline at end of file +} = wgpuMatrixAPI< + Mat3, Mat4, Quat, Vec2, Vec3, Vec4>( + Float32Array, Float32Array, Float32Array, Float32Array, Float32Array, Float32Array); + +export const { + /** @namespace */ + mat4: mat4d, + /** @namespace */ + mat3: mat3d, + /** @namespace */ + quat: quatd, + /** @namespace */ + vec2: vec2d, + /** @namespace */ + vec3: vec3d, + /** @namespace */ + vec4: vec4d, +} = wgpuMatrixAPI< + Mat3d, Mat4d, Quatd, Vec2d, Vec3d, Vec4d>( + Float64Array, Float64Array, Float64Array, Float64Array, Float64Array, Float64Array); + +export const { + /** @namespace */ + mat4: mat4n, + /** @namespace */ + mat3: mat3n, + /** @namespace */ + quat: quatn, + /** @namespace */ + vec2: vec2n, + /** @namespace */ + vec3: vec3n, + /** @namespace */ + vec4: vec4n, +} = wgpuMatrixAPI< + Mat3n, Mat4n, Quatn, Vec2n, Vec3n, Vec4n>( + ZeroArray, Array, Array, Array, Array, Array); diff --git a/test/tests/mat3-test.js b/test/tests/mat3-test.js index 49f8ada..7008c11 100644 --- a/test/tests/mat3-test.js +++ b/test/tests/mat3-test.js @@ -1,15 +1,13 @@ -import {mat3, mat4, quat, utils} from '../../dist/2.x/wgpu-matrix.module.js'; +import {mat3, mat3d, mat3n, mat4, quat, utils} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, assertFalsy, - assertIsArray, - assertInstanceOf, assertStrictEqual, assertStrictNotEqual, assertTruthy, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; function assertMat3Equal(a, b) { if (!mat3.equals(a, b)) { @@ -31,10 +29,6 @@ function check(Type) { 8, 9, 10, 0, ]; - before(function () { - mat3.setDefaultType(Type); - }); - function testM3WithoutDest(func, expected, ...args) { const d = func(...args); assertMat3EqualApproximately(d, expected); @@ -100,15 +94,15 @@ function check(Type) { -4, -5, -6, -7, -8, -9, -10, -11, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.negate(m, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.negate(m, newDst); }, expected); }); it('should copy', () => { const expected = m; - testMat3WithAndWithoutDest((dst) => { - const result = mat3.copy(m, dst); + testMat3WithAndWithoutDest((newDst) => { + const result = mat3.copy(m, newDst); assertStrictNotEqual(result, m); return result; }, expected); @@ -146,8 +140,8 @@ function check(Type) { it('should clone', () => { const expected = m; - testMat3WithAndWithoutDest((dst) => { - const result = mat3.clone(m, dst); + testMat3WithAndWithoutDest((newDst) => { + const result = mat3.clone(m, newDst); assertStrictNotEqual(result, m); return result; }, expected); @@ -155,8 +149,8 @@ function check(Type) { it('should set', () => { const expected = [2, 3, 4, 0, 22, 33, 44, 0, 222, 333, 444, 0]; - testMat3WithAndWithoutDest((v0, v1, v2, v3, v4, v5, v6, v7, v8, dst) => { - return mat3.set(v0, v1, v2, v3, v4, v5, v6, v7, v8, dst); + testMat3WithAndWithoutDest((v0, v1, v2, v3, v4, v5, v6, v7, v8, newDst) => { + return mat3.set(v0, v1, v2, v3, v4, v5, v6, v7, v8, newDst); }, expected, 2, 3, 4, 22, 33, 44, 222, 333, 444); }); @@ -167,8 +161,8 @@ function check(Type) { 0, 0, 1, 0, 0, 0, 0, 1, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.identity(dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.identity(newDst); }, expected); }); @@ -179,8 +173,8 @@ function check(Type) { 2, 6, 10, 14, 3, 7, 11, 15, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.transpose(m, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.transpose(m, newDst); }, expected); }); @@ -204,8 +198,8 @@ function check(Type) { m2[2 * 4 + 0] * m[0 * 4 + 2] + m2[2 * 4 + 1] * m[1 * 4 + 2] + m2[2 * 4 + 2] * m[2 * 4 + 2], m2[2 * 4 + 0] * m[0 * 4 + 3] + m2[2 * 4 + 1] * m[1 * 4 + 3] + m2[2 * 4 + 2] * m[2 * 4 + 3], ]; - testMat3WithAndWithoutDest((dst) => { - return fn(m, m2, dst); + testMat3WithAndWithoutDest((newDst) => { + return fn(m, m2, newDst); }, expected); } @@ -258,8 +252,8 @@ function check(Type) { }, ]; for (const {m, expected} of tests) { - testMat3WithAndWithoutDest((dst) => { - return fn(m, dst); + testMat3WithAndWithoutDest((newDst) => { + return fn(m, newDst); }, expected); } } @@ -312,15 +306,15 @@ function check(Type) { 4, 5, 6, 0, 11, 22, 1, 1, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.setTranslation(m, [11, 22], dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.setTranslation(m, [11, 22], newDst); }, expected); }); it('should get translation', () => { const expected = [8, 9]; - testV2WithAndWithoutDest((dst) => { - return mat3.getTranslation(m, dst); + testV2WithAndWithoutDest((newDst) => { + return mat3.getTranslation(m, newDst); }, expected); }); @@ -329,8 +323,8 @@ function check(Type) { [0, 1], [4, 5], ].forEach((expected, ndx) => { - testV2WithAndWithoutDest((dst) => { - return mat3.getAxis(m, ndx, dst); + testV2WithAndWithoutDest((newDst) => { + return mat3.getAxis(m, ndx, newDst); }, expected); }); }); @@ -348,8 +342,8 @@ function check(Type) { 8, 9, 10, 0, ], ].forEach((expected, ndx) => { - testMat3WithAndWithoutDest((dst) => { - return mat3.setAxis(m, [11, 22], ndx, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.setAxis(m, [11, 22], ndx, newDst); }, expected); }); }); @@ -364,8 +358,8 @@ function check(Type) { Math.sqrt(1 * 1 + 2 * 2), Math.sqrt(5 * 5 + 6 * 6), ]; - testV2WithAndWithoutDest((dst) => { - return mat3.getScaling(m, dst); + testV2WithAndWithoutDest((newDst) => { + return mat3.getScaling(m, newDst); }, expected); }); @@ -375,8 +369,8 @@ function check(Type) { 0, 1, 0, 0, 2, 3, 1, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.translation([2, 3], dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.translation([2, 3], newDst); }, expected); }); @@ -388,8 +382,8 @@ function check(Type) { 9 + 1 * 2 + 5 * 3, 10 + 2 * 2 + 6 * 3, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.translate(m, [2, 3], dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.translate(m, [2, 3], newDst); }, expected); }); @@ -402,20 +396,18 @@ function check(Type) { -s, c, 0, 0, 0, 0, 1, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.rotation(angle, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotation(angle, newDst); }, expected); }); it('should rotate', () => { const angle = 1.23; // switch to Array type to keep precision high for expected - const oldType = mat3.setDefaultType(Array); - const expected = mat3.multiply(m, mat3.rotation(angle)); - mat3.setDefaultType(oldType); + const expected = mat3n.multiply(m, mat3.rotation(angle)); - testMat3WithAndWithoutDest((dst) => { - return mat3.rotate(m, angle, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotate(m, angle, newDst); }, expected); }); @@ -425,8 +417,8 @@ function check(Type) { 0, 3, 0, 0, 0, 0, 1, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.scaling([2, 3], dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.scaling([2, 3], newDst); }, expected); }); @@ -436,8 +428,8 @@ function check(Type) { 12, 15, 18, 0, 8, 9, 10, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.scale(m, [2, 3], dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.scale(m, [2, 3], newDst); }, expected); }); @@ -447,8 +439,8 @@ function check(Type) { 0, 2, 0, 0, 0, 0, 1, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.uniformScaling(2, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.uniformScaling(2, newDst); }, expected); }); @@ -458,8 +450,8 @@ function check(Type) { 8, 10, 12, 0, 8, 9, 10, 0, ]; - testMat3WithAndWithoutDest((dst) => { - return mat3.uniformScale(m, 2, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.uniformScale(m, 2, newDst); }, expected); }); @@ -469,13 +461,13 @@ function check(Type) { 5, 6, 7, 0, 9, 10, 11, 0, ]; - testMat3WithAndWithoutDest((dst) => { + testMat3WithAndWithoutDest((newDst) => { const m4 = mat4.create( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - return mat3.fromMat4(m4, dst); + return mat3.fromMat4(m4, newDst); }, expected); }); @@ -489,8 +481,8 @@ function check(Type) { { q: quat.fromEuler(0, 0, Math.PI / 2, 'xyz'), expected: mat3.fromMat4(mat4.rotationZ(Math.PI / 2)), }, ]; for (const {q, expected} of tests) { - testMat3WithAndWithoutDest((dst) => { - return mat3.fromQuat(q, dst); + testMat3WithAndWithoutDest((newDst) => { + return mat3.fromQuat(q, newDst); }, expected); } }); @@ -499,18 +491,8 @@ function check(Type) { } describe('mat3', () => { - - it('should set default type', () => { - mat3.setDefaultType(Array); - let d = mat3.identity(); - assertIsArray(d); - mat3.setDefaultType(Float32Array); - d = mat3.identity(); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); + check(mat3n); + check(mat3); + check(mat3d); }); diff --git a/test/tests/mat4-test.js b/test/tests/mat4-test.js index 58f7050..1ac02dd 100644 --- a/test/tests/mat4-test.js +++ b/test/tests/mat4-test.js @@ -1,18 +1,18 @@ -import {mat4, mat3, quat, utils, vec3} from '../../dist/2.x/wgpu-matrix.module.js'; +import {mat4, mat4d, mat4n, mat3, quat, utils, vec3} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, assertEqualApproximately, assertFalsy, - assertIsArray, - assertInstanceOf, assertStrictEqual, assertStrictNotEqual, assertTruthy, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; + + +function check(mat4, Type) { -function check(Type) { describe('using ' + Type, () => { const m = [ 0, 1, 2, 3, @@ -21,10 +21,6 @@ function check(Type) { 12, 13, 14, 15, ]; - before(function () { - mat4.setDefaultType(Type); - }); - function testMat4WithoutDest(func, expected, ...args) { const d = func(...args); assertEqualApproximately(d, expected); @@ -39,8 +35,8 @@ function check(Type) { } function testMat4WithAndWithoutDest(func, expected, ...args) { - if (Type === Float32Array) { - expected = new Float32Array(expected); + if (mat4.identity() instanceof Float32Array) { + //expected = new Float32Array(expected); } testMat4WithoutDest(func, expected, ...args); testMat4WithDest(func, expected, ...args); @@ -48,14 +44,14 @@ function check(Type) { function testVec3WithoutDest(func, expected) { const d = func(); - assertEqual(d, expected); + assertEqualApproximately(d, expected, 2e7); } function testVec3WithDest(func, expected) { const d = new Float32Array(3); const c = func(d); assertStrictEqual(c, d); - assertEqual(c, expected); + assertEqualApproximately(c, expected, 2e7); } function testVec3WithAndWithoutDest(func, expected) { @@ -80,7 +76,7 @@ function check(Type) { it('should create', () => { for (let i = 0; i <= 16; ++i) { const expected = mat4.clone(new Array(16).fill(0).map((_, ndx) => ndx < i ? ndx + 1 : 0)); - const args = new Array(Type === Array ? 16 : i).fill(0).map((_, ndx) => ndx < i ? ndx + 1 : 0); + const args = new Array(Array.isArray(mat4.identity()) ? 16 : i).fill(0).map((_, ndx) => ndx < i ? ndx + 1 : 0); const m = mat4.create(...args); assertEqual(m, expected); } @@ -93,15 +89,15 @@ function check(Type) { -8, -9, -10, -11, -12, -13, -14, -15, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.negate(m, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.negate(m, newDst); }, expected); }); it('should copy', () => { const expected = m; - testMat4WithAndWithoutDest((dst) => { - const result = mat4.copy(m, dst); + testMat4WithAndWithoutDest((newDst) => { + const result = mat4.copy(m, newDst); assertStrictNotEqual(result, m); return result; }, expected); @@ -135,8 +131,8 @@ function check(Type) { it('should clone', () => { const expected = m; - testMat4WithAndWithoutDest((dst) => { - const result = mat4.clone(m, dst); + testMat4WithAndWithoutDest((newDst) => { + const result = mat4.clone(m, newDst); assertStrictNotEqual(result, m); return result; }, expected); @@ -144,8 +140,8 @@ function check(Type) { it('should set', () => { const expected = [2, 3, 4, 5, 22, 33, 44, 55, 222, 333, 444, 555, 2222, 3333, 4444, 5555]; - testMat4WithAndWithoutDest((v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, dst) => { - return mat4.set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, dst); + testMat4WithAndWithoutDest((v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, newDst) => { + return mat4.set(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, newDst); }, expected, 2, 3, 4, 5, 22, 33, 44, 55, 222, 333, 444, 555, 2222, 3333, 4444, 5555); }); @@ -156,8 +152,8 @@ function check(Type) { 0, 0, 1, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.identity(dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.identity(newDst); }, expected); }); @@ -168,8 +164,8 @@ function check(Type) { 2, 6, 10, 14, 3, 7, 11, 15, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.transpose(m, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.transpose(m, newDst); }, expected); }); @@ -198,8 +194,8 @@ function check(Type) { m2[3 * 4 + 0] * m[0 * 4 + 2] + m2[3 * 4 + 1] * m[1 * 4 + 2] + m2[3 * 4 + 2] * m[2 * 4 + 2] + m2[3 * 4 + 3] * m[3 * 4 + 2], m2[3 * 4 + 0] * m[0 * 4 + 3] + m2[3 * 4 + 1] * m[1 * 4 + 3] + m2[3 * 4 + 2] * m[2 * 4 + 3] + m2[3 * 4 + 3] * m[3 * 4 + 3], ]; - testMat4WithAndWithoutDest((dst) => { - return fn(m, m2, dst); + testMat4WithAndWithoutDest((newDst) => { + return fn(m, m2, newDst); }, expected); } @@ -236,8 +232,8 @@ function check(Type) { 0.375, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return fn(m, dst); + testMat4WithAndWithoutDest((newDst) => { + return fn(m, newDst); }, expected); } @@ -304,15 +300,15 @@ function check(Type) { 8, 9, 10, 11, 11, 22, 33, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.setTranslation(m, [11, 22, 33], dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.setTranslation(m, [11, 22, 33], newDst); }, expected); }); it('should get translation', () => { const expected = [12, 13, 14]; - testVec3WithAndWithoutDest((dst) => { - return mat4.getTranslation(m, dst); + testVec3WithAndWithoutDest((newDst) => { + return mat4.getTranslation(m, newDst); }, expected); }); @@ -322,8 +318,8 @@ function check(Type) { [4, 5, 6], [8, 9, 10], ].forEach((expected, ndx) => { - testVec3WithAndWithoutDest((dst) => { - return mat4.getAxis(m, ndx, dst); + testVec3WithAndWithoutDest((newDst) => { + return mat4.getAxis(m, ndx, newDst); }, expected); }); }); @@ -349,8 +345,8 @@ function check(Type) { 12, 13, 14, 15, ], ].forEach((expected, ndx) => { - testMat4WithAndWithoutDest((dst) => { - return mat4.setAxis(m, [11, 22, 33], ndx, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.setAxis(m, [11, 22, 33], ndx, newDst); }, expected); }); }); @@ -366,9 +362,9 @@ function check(Type) { Math.sqrt(1 * 1 + 2 * 2 + 3 * 3), Math.sqrt(5 * 5 + 6 * 6 + 7 * 7), Math.sqrt(9 * 9 + 10 * 10 + 11 * 11), - ]; - testVec3WithAndWithoutDest((dst) => { - return mat4.getScaling(m, dst); + ].map(v => new Type([v])[0]); + testVec3WithAndWithoutDest((newDst) => { + return mat4.getScaling(m, newDst); }, expected); }); @@ -400,8 +396,8 @@ function check(Type) { zNear * zFar * rangeInv, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.perspective(fov, aspect, zNear, zFar, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.perspective(fov, aspect, zNear, zFar, newDst); }, expected); }); @@ -432,8 +428,8 @@ function check(Type) { -zNear, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.perspective(fov, aspect, zNear, zFar, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.perspective(fov, aspect, zNear, zFar, newDst); }, expected); }); @@ -502,8 +498,8 @@ function check(Type) { zFar * zNear * rangeInv, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, newDst); }, expected); }); @@ -545,8 +541,8 @@ function check(Type) { zNear, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.perspectiveReverseZ(fov, aspect, zNear, zFar, newDst); }, expected); }); @@ -591,8 +587,8 @@ function check(Type) { near / (near - far), 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.ortho(left, right, bottom, top, near, far, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.ortho(left, right, bottom, top, near, far, newDst); }, expected); }); @@ -638,8 +634,8 @@ function check(Type) { near * far / dz, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.frustum(left, right, bottom, top, near, far, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.frustum(left, right, bottom, top, near, far, newDst); }, expected); }); @@ -689,8 +685,8 @@ function check(Type) { near * far / dz, 0, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.frustumReverseZ(left, right, bottom, top, near, far, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.frustumReverseZ(left, right, bottom, top, near, far, newDst); }, expected); }); @@ -738,13 +734,13 @@ function check(Type) { 0.4364357888698578, -0.8017837405204773, 0, - 1.4901161193847656e-7, + Type === Float32Array ? 1.4901161193847656e-7 : -4.440892098500626e-16, 0, 3.74165740609169, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.lookAt(eye, target, up, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.lookAt(eye, target, up, newDst); }, expected); }); @@ -851,14 +847,14 @@ function check(Type) { camExpected, }, i) => { it(`should make aim matrix ${i}`, () => { - testMat4WithAndWithoutDest((dst) => { - return mat4.aim(position, target, up, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.aim(position, target, up, newDst); }, expected); }); it(`should make cameraAim matrix ${i}`, () => { - testMat4WithAndWithoutDest((dst) => { - return mat4.cameraAim(position, target, up, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.cameraAim(position, target, up, newDst); }, camExpected); }); }); @@ -871,8 +867,8 @@ function check(Type) { 0, 0, 1, 0, 2, 3, 4, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.translation([2, 3, 4], dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.translation([2, 3, 4], newDst); }, expected); }); @@ -886,8 +882,8 @@ function check(Type) { 14 + 2 * 2 + 6 * 3 + 10 * 4, 15 + 3 * 2 + 7 * 3 + 11 * 4, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.translate(m, [2, 3, 4], dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.translate(m, [2, 3, 4], newDst); }, expected); }); @@ -901,20 +897,18 @@ function check(Type) { 0, -s, c, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.rotationX(angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotationX(angle, newDst); }, expected); }); it('should rotate x', () => { const angle = 1.23; // switch to Array type to keep precision high for expected - const oldType = mat4.setDefaultType(Array); - const expected = mat4.multiply(m, mat4.rotationX(angle)); - mat4.setDefaultType(oldType); + const expected = mat4.multiply(m, mat4.rotationX(angle, [])); - testMat4WithAndWithoutDest((dst) => { - return mat4.rotateX(m, angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotateX(m, angle, newDst); }, expected); }); @@ -928,20 +922,18 @@ function check(Type) { s, 0, c, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.rotationY(angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotationY(angle, newDst); }, expected); }); it('should rotate y', () => { const angle = 1.23; // switch to Array type to keep precision high for expected - const oldType = mat4.setDefaultType(Array); - const expected = mat4.multiply(m, mat4.rotationY(angle)); - mat4.setDefaultType(oldType); + const expected = mat4.multiply(m, mat4.rotationY(angle, new Array(16))); - testMat4WithAndWithoutDest((dst) => { - return mat4.rotateY(m, angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotateY(m, angle, newDst); }, expected); }); @@ -955,20 +947,18 @@ function check(Type) { 0, 0, 1, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.rotationZ(angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotationZ(angle, newDst); }, expected); }); it('should rotate z', () => { const angle = 1.23; // switch to Array type to keep precision high for expected - const oldType = mat4.setDefaultType(Array); - const expected = mat4.multiply(m, mat4.rotationZ(angle)); - mat4.setDefaultType(oldType); + const expected = mat4.multiply(m, mat4.rotationZ(angle, new Array(16))); - testMat4WithAndWithoutDest((dst) => { - return mat4.rotateZ(m, angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.rotateZ(m, angle, newDst); }, expected); }); @@ -1006,8 +996,8 @@ function check(Type) { 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.axisRotation(axis, angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.axisRotation(axis, angle, newDst); }, expected); }); @@ -1015,12 +1005,10 @@ function check(Type) { const axis = [0.5, 0.6, -0.7]; const angle = 1.23; // switch to Array type to keep precision high for expected - const oldType = mat4.setDefaultType(Array); - const expected = mat4.multiply(m, mat4.axisRotation(axis, angle)); - mat4.setDefaultType(oldType); + const expected = mat4.multiply(m, mat4.axisRotation(axis, angle, new Array(16))); - testMat4WithAndWithoutDest((dst) => { - return mat4.axisRotate(m, axis, angle, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.axisRotate(m, axis, angle, newDst); }, expected); }); @@ -1031,8 +1019,8 @@ function check(Type) { 0, 0, 4, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.scaling([2, 3, 4], dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.scaling([2, 3, 4], newDst); }, expected); }); @@ -1043,8 +1031,8 @@ function check(Type) { 32, 36, 40, 44, 12, 13, 14, 15, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.scale(m, [2, 3, 4], dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.scale(m, [2, 3, 4], newDst); }, expected); }); @@ -1055,8 +1043,8 @@ function check(Type) { 0, 0, 2, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.uniformScaling(2, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.uniformScaling(2, newDst); }, expected); }); @@ -1067,8 +1055,8 @@ function check(Type) { 16, 18, 20, 22, 12, 13, 14, 15, ]; - testMat4WithAndWithoutDest((dst) => { - return mat4.uniformScale(m, 2, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.uniformScale(m, 2, newDst); }, expected); }); @@ -1079,9 +1067,9 @@ function check(Type) { 7, 8, 9, 0, 0, 0, 0, 1, ]; - testMat4WithAndWithoutDest((dst) => { + testMat4WithAndWithoutDest((newDst) => { const m3 = mat3.create(1, 2, 3, 4, 5, 6, 7, 8, 9); - return mat4.fromMat3(m3, dst); + return mat4.fromMat3(m3, newDst); }, expected); }); @@ -1095,8 +1083,8 @@ function check(Type) { { q: quat.fromEuler(0, 0, Math.PI / 2, 'xyz'), expected: mat4.rotationZ(Math.PI / 2), }, ]; for (const {q, expected} of tests) { - testMat4WithAndWithoutDest((dst) => { - return mat4.fromQuat(q, dst); + testMat4WithAndWithoutDest((newDst) => { + return mat4.fromQuat(q, newDst); }, expected); } }); @@ -1105,18 +1093,8 @@ function check(Type) { } describe('mat4', () => { - - it('should set default type', () => { - mat4.setDefaultType(Array); - let d = mat4.identity(); - assertIsArray(d); - mat4.setDefaultType(Float32Array); - d = mat4.identity(); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); + check(mat4n, Array); + check(mat4, Float32Array); + check(mat4d, Float64Array); }); diff --git a/test/tests/quat-test.js b/test/tests/quat-test.js index 0f9343a..2f93a44 100644 --- a/test/tests/quat-test.js +++ b/test/tests/quat-test.js @@ -1,4 +1,4 @@ -import {mat3, mat4, quat, utils, vec3} from '../../dist/2.x/wgpu-matrix.module.js'; +import {mat3, mat4, quat, quatn, quatd, utils, vec3} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, @@ -6,12 +6,11 @@ import { assertInstanceOf, assertStrictEqual, assertStrictNotEqual, - assertIsArray, assertTruthy, assertFalsy, assertDeepEqualApproximately, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; // Note: quat.create is use extensively in these tests but that is NOT idiomatic! // Idiomatic usage use to use raw JS arrays where convenient. For example @@ -22,12 +21,8 @@ import {describe, it, before} from '../mocha-support.js'; // The reason quat.create is used in the tests is to make sure we are working // with the specified default type when testing. -function check(Type) { - describe('using ' + Type, () => { - - before(() => { - quat.setDefaultType(Type); - }); +function check(quat, Type) { + describe('using ' + quat, () => { function clone(v) { return v.slice ? v.slice() : v; @@ -456,25 +451,9 @@ function check(Type) { describe('quat', () => { - it('should set default type', () => { - quat.setDefaultType(Array); - let d = quat.create(1, 2, 3, 4); - assertIsArray(d); - - d = quat.add([1, 2, 3, 4], [5, 6, 7, 8]); - assertIsArray(d); - - quat.setDefaultType(Float32Array); - d = quat.create(1, 2, 3, 4); - assertInstanceOf(d, Float32Array); - - d = quat.add([1, 2, 3, 4], [5, 6, 7, 8]); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); + check(quatn, Array); + check(quat, Float32Array); + check(quatd, Float64Array); }); diff --git a/test/tests/vec2-test.js b/test/tests/vec2-test.js index 187cf8a..c2a636c 100644 --- a/test/tests/vec2-test.js +++ b/test/tests/vec2-test.js @@ -1,18 +1,16 @@ -import {vec2, utils} from '../../dist/2.x/wgpu-matrix.module.js'; +import {vec2, vec2n, vec2d, utils} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, assertInstanceOf, - assertLessThan, assertStrictEqual, assertStrictNotEqual, - assertIsArray, assertEqualApproximately, assertTruthy, assertFalsy, assertArrayEqualApproximately, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; // Note: vec2.create is use extensively in these tests but that is NOT idiomatic! // Idiomatic usage use to use raw JS arrays where convenient. For example @@ -22,78 +20,82 @@ import {describe, it, before} from '../mocha-support.js'; // The reason vec2.create is used in the tests is to make sure we are working // with the specified default type when testing. -function check(Type) { +function check(vec2, Type) { describe('using ' + Type, () => { - before(() => { - vec2.setDefaultType(Type); - }); - function clone(c) { return c.length ? c.slice() : c; } - function elementsEqual(a, b) { - assertStrictEqual(a.length, b.length); - for (let i = 0; i < a.length; ++i) { - const diff = Math.abs(a[i] - b[i]); - assertLessThan(diff, 0.0000001); - } - } + //function elementsEqual(a, b) { + // assertStrictEqual(a.length, b.length); + // for (let i = 0; i < a.length; ++i) { + // const diff = Math.abs(a[i] - b[i]); + // assertLessThan(diff, 0.0000001); + // } + //} - function testV2WithoutDest(func, expected, ...args) { + function testV2WithoutDest(func, expected, tolerance, ...args) { const v = args.shift(); const d = func(clone(v), ...args); - assertEqual(d, expected); + assertEqualApproximately(d, expected, tolerance); assertInstanceOf(d, Type); } - function testV2WithDest(func, expected, ...args) { + function testV2WithDest(func, expected, tolerance, ...args) { const firstArg = args.shift(); // clone expected so we can check it wasn't modified expected = vec2.clone(expected); - let d = vec2.create(); + const d = vec2.create(); // clone v to make sure it's the correct type - let c = func(clone(firstArg), ...args, d); + const c = func(clone(firstArg), ...args, d); assertStrictEqual(c, d); - assertEqual(c, expected); - - // test if we pass same vector as source and dest we get - // correct result - if (firstArg.length) { - d = clone(firstArg); - // clone args to make sure we don't overwrite first arg - const bOrig = args.map(b => clone(b)); - c = func(d, ...args, d); - assertStrictEqual(c, d); - elementsEqual(c, expected); - args.forEach((b, ndx) => { - assertEqual(b, bOrig[ndx]); - }); - } + assertEqualApproximately(c, expected, tolerance); + + //// test if we pass same vector as source and dest we get + //// correct result + //if (firstArg.length) { + // d = clone(firstArg); + // // clone args to make sure we don't overwrite first arg + // const bOrig = args.map(b => clone(b)); + // c = func(d, ...args, d); + // assertStrictEqual(c, d); + // elementsEqual(c, expected); + // args.forEach((b, ndx) => { + // assertEqual(b, bOrig[ndx]); + // }); + //} + + //// test if we pass operand as dest we get correct result + //if (args.length > 0 && firstArg.length) { + // d = vec2.clone(args[0]); + // // clone v to make sure it is not overwritten + // const vOrig = vec2.clone(firstArg); + // c = func(firstArg, d, d); + // elementsEqual(c, expected); + // assertEqual(firstArg, vOrig); + // assertStrictEqual(c, d); + //} + } - // test if we pass operand as dest we get correct result - if (args.length > 0 && firstArg.length) { - d = vec2.clone(args[0]); - // clone v to make sure it is not overwritten - const vOrig = vec2.clone(firstArg); - c = func(firstArg, d, d); - elementsEqual(c, expected); - assertEqual(firstArg, vOrig); - assertStrictEqual(c, d); - } + function testV2WithAndWithoutDestImpl(func, expected, tolerance, ...args) { + expected = vec2.clone(expected); + testV2WithoutDest(func, expected, tolerance, ...args); + testV2WithDest(func, expected, tolerance, ...args); } function testV2WithAndWithoutDest(func, expected, ...args) { - expected = vec2.clone(expected); - testV2WithoutDest(func, expected, ...args); - testV2WithDest(func, expected, ...args); + testV2WithAndWithoutDestImpl(func, expected, 0, ...args); + } + + function testV2WithAndWithoutDestApprox(func, expected, ...args) { + testV2WithAndWithoutDestImpl(func, expected, 1e7, ...args); } it('should add', () => { const expected = [3, 5]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.add(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.add(a, b, newDst); }, expected, [1, 2], [2, 3]); }); @@ -117,36 +119,36 @@ function check(Type) { it('should compute ceil', () => { const expected = [2, -1]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.ceil(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.ceil(a, newDst); }, expected, [1.1, -1.1]); }); it('should compute floor', () => { const expected = [1, -2]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.floor(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.floor(a, newDst); }, expected, [1.1, -1.1]); }); it('should compute round', () => { const expected = [1, -1]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.round(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.round(a, newDst); }, expected, [1.1, -1.1]); }); it('should clamp', () => { { const expected = [1, 0]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.clamp(a, 0, 1, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.clamp(a, 0, 1, newDst); }, expected, [2, -1]); } { const expected = [-10, 5]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.clamp(a, -10, 5, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.clamp(a, -10, 5, newDst); }, expected, [-22, 50]); } }); @@ -164,71 +166,71 @@ function check(Type) { it('should subtract', () => { const expected = [-2, -3]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.subtract(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.subtract(a, b, newDst); }, expected, [2, 3], [4, 6]); }); it('should sub', () => { const expected = [-2, -3]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.sub(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.sub(a, b, newDst); }, expected, [2, 3], [4, 6]); }); it('should lerp', () => { const expected = [3, 4.5]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.lerp(a, b, 0.5, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.lerp(a, b, 0.5, newDst); }, expected, [2, 3], [4, 6]); }); it('should lerp under 0', () => { const expected = [0.5, 1.5]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.lerp(a, b, -0.5, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.lerp(a, b, -0.5, newDst); }, expected, [1, 3], [2, 6]); }); it('should lerp over 0', () => { const expected = [2.5, 7.5]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.lerp(a, b, 1.5, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.lerp(a, b, 1.5, newDst); }, expected, [1, 3], [2, 6]); }); it('should multiply by scalar', () => { const expected = [4, 6]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.mulScalar(a, 2, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.mulScalar(a, 2, newDst); }, expected, [2, 3]); }); it('should scale', () => { const expected = [4, 6]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.scale(a, 2, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.scale(a, 2, newDst); }, expected, [2, 3]); }); it('should add scaled', () => { const expected = [10, 15]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.addScaled(a, [4, 6], 2, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.addScaled(a, [4, 6], 2, newDst); }, expected, [2, 3]); }); it('should divide by scalar', () => { const expected = [0.5, 1.5]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.divScalar(a, 2, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.divScalar(a, 2, newDst); }, expected, [1, 3]); }); it('should inverse', () => { const expected = [1 / 3, 1 / -4]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.inverse(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.inverse(a, newDst); }, expected, [3, -4]); }); @@ -311,23 +313,23 @@ function check(Type) { 2 / length, 3 / length, ]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.normalize(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.normalize(a, newDst); }, expected, [2, 3]); }); it('should negate', () => { const expected = [-2, 3]; - testV2WithAndWithoutDest((a, dst) => { - return vec2.negate(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + return vec2.negate(a, newDst); }, expected, [2, -3]); }); it('should copy', () => { const expected = [2, 3]; const v = vec2.create(2, 3); - testV2WithAndWithoutDest((a, dst) => { - const result = vec2.copy(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + const result = vec2.copy(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [2, 3]); @@ -336,8 +338,8 @@ function check(Type) { it('should clone', () => { const expected = [2, 3]; const v = vec2.create(2, 3); - testV2WithAndWithoutDest((a, dst) => { - const result = vec2.clone(a, dst); + testV2WithAndWithoutDest((a, newDst) => { + const result = vec2.clone(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [2, 3]); @@ -345,36 +347,36 @@ function check(Type) { it('should set', () => { const expected = [2, 3]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.set(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.set(a, b, newDst); }, expected, 2, 3); }); it('should multiply', () => { const expected = [8, 18]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.multiply(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.multiply(a, b, newDst); }, expected, [2, 3], [4, 6]); }); it('should mul', () => { const expected = [8, 18]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.mul(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.mul(a, b, newDst); }, expected, [2, 3], [4, 6]); }); it('should divide', () => { const expected = [2 / 3, 3 / 4]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.divide(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.divide(a, b, newDst); }, expected, [2, 3], [3, 4]); }); it('should div', () => { const expected = [2 / 3, 3 / 4]; - testV2WithAndWithoutDest((a, b, dst) => { - return vec2.div(a, b, dst); + testV2WithAndWithoutDest((a, b, newDst) => { + return vec2.div(a, b, newDst); }, expected, [2, 3], [3, 4]); }); @@ -402,38 +404,38 @@ function check(Type) { /* it('should transform by 2x2', () => { const expected = [14, 21]; - testV2WithAndWithoutDest((a, dst) => { + testV2WithAndWithoutDest((a, newDst) => { const m = [ 4, 3, 2, 5, ]; - return vec2.transformMat2(a, m, dst); + return vec2.transformMat2(a, m, newDst); }, expected, [2, 3]); }); */ it('should transform by 3x3', () => { const expected = [16, 17]; - testV2WithAndWithoutDest((a, dst) => { + testV2WithAndWithoutDest((a, newDst) => { const m = [ 4, 0, 0, 11, 0, 5, 0, 12, 8, 2, 0, 13, ]; - return vec2.transformMat3(a, m, dst); + return vec2.transformMat3(a, m, newDst); }, expected, [2, 3]); }); it('should transform by 4x4', () => { const expected = [6, 11]; - testV2WithAndWithoutDest((a, dst) => { + testV2WithAndWithoutDest((a, newDst) => { const m = [ 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 4, 5, 6, 1, ]; - return vec2.transformMat4(a, m, dst); + return vec2.transformMat4(a, m, newDst); }, expected, [2, 3]); }); @@ -446,111 +448,92 @@ function check(Type) { assertEqual(v2, [0, 0]); }); - }); -} - -describe('vec2', () => { - - it('should set default type', () => { - vec2.setDefaultType(Array); - let d = vec2.create(1, 2, 3); - assertIsArray(d); - - d = vec2.add([1, 2], [3, 4]); - assertIsArray(d); - - vec2.setDefaultType(Float32Array); - d = vec2.create(1, 2, 3); - assertInstanceOf(d, Float32Array); - - d = vec2.add([1, 2], [3, 4]); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); - - describe('rotate', function() { - describe('rotation around world origin [0, 0, 0]', function() { - let vecA, vecB, result; - beforeEach(function () { - vecA = [0, 1]; - vecB = [0, 0]; - result = vec2.rotate(vecA, vecB, Math.PI); + describe('rotate', function () { + describe('rotation around world origin [0, 0]', function () { + it("should return the rotated vector", function () { + const expected = [0, -1]; + testV2WithAndWithoutDestApprox((a, b, angle, newDst) => { + return vec2.rotate(a, b, angle, newDst); + }, expected, [0, 1], [0, 0], Math.PI); + }); }); - it("should return the rotated vector", function () { - assertEqualApproximately(result, [0, -1]); + describe('rotation around an arbitrary origin', function () { + it("should return the rotated vector", function () { + const expected = [-6, -5]; + testV2WithAndWithoutDestApprox((a, b, angle, newDst) => { + return vec2.rotate(a, b, angle, newDst); + }, expected, [6, -5], [0, -5], Math.PI); + }); }); }); - describe('rotation around an arbitrary origin', function () { - let vecA, vecB, result; - beforeEach(function () { - vecA = [6, -5]; - vecB = [0, -5]; - result = vec2.rotate(vecA, vecB, Math.PI); - }); - it("should return the rotated vector", function () { - assertEqualApproximately(result, [-6, -5]); + + describe('setLength', function () { + describe('set the length of a provided direction vector', function () { + it("should return the lengthened vector", function () { + const expected = [10.323759005323593, 10.323759005323593]; + testV2WithAndWithoutDestApprox( + (a, len, newDst) => vec2.setLength(a, len, newDst), + expected, + [1, 1], 14.6); + }); }); }); - }); - describe('setLength', function() { - describe('set the length of a provided direction vector', function() { - let vecA, result; - beforeEach(function () { - vecA = [1, 1]; - result = vec2.setLength(vecA, 14.6); - }); - it("should return the lengthend vector", function () { - assertEqualApproximately(result, [10.323759005323593, 10.323759005323593]); - assertEqualApproximately(vec2.length(result), 14.6); + describe('truncate', function () { + describe('limit a vector to a max length', function () { + + it("should shorten the vector", function () { + const expected = [2.82842712474619, 2.82842712474619]; + testV2WithAndWithoutDestApprox( + (a, len, newDst) => vec2.truncate(a, len, newDst), + expected, + [10.323759005323593, 10.323759005323593], + 4.0); + }); + + it("should preserve the vector when shorter than maxLen", function () { + const expected = [11, 12]; + testV2WithAndWithoutDestApprox( + (a, len, newDst) => vec2.truncate(a, len, newDst), + expected, + [11, 12], + 14.6); + }); }); }); - }); - describe('truncate', function() { - describe('limit a vector to a max length', function() { - let vecA; + describe('midpoint', function () { + describe('return the midpoint between 2 vectors', function () { - beforeEach(function () { - vecA = [10.323759005323593, 10.323759005323593]; - }); + it("should return the midpoint", function () { + const expected = [5, 5]; + testV2WithAndWithoutDest( + (a, b, newDst) => vec2.midpoint(a, b, newDst), + expected, + [0, 0], [10, 10] + ); + }); - it("should shorten the vector", function () { - const result = vec2.truncate(vecA, 4.0); - assertEqualApproximately(result, [2.82842712474619, 2.82842712474619]); - assertEqualApproximately(vec2.length(result), 4.0); - }); + it("should handle negatives", function () { + const expected = [10, 10]; + testV2WithAndWithoutDest( + (a, b, newDst) => vec2.midpoint(a, b, newDst), + expected, + [-10, -20], [30, 40]); + }); - it("should preserve the vector when shorter than maxLen", function () { - const result = vec2.truncate(vecA, 18.0); - assertEqualApproximately(result, [10.323759005323593, 10.323759005323593]); - assertEqualApproximately(vec2.length(result), 14.6); }); }); - }); - describe('midpoint', function() { - describe('return the midpoint between 2 vectors', function() { - it("should return the midpoint", function () { - const vecA = [ 0, 0 ] - const vecB = [ 10, 10 ] - const result = vec2.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 5, 5 ]); - }); + }); +} - it("should handle negatives", function () { - const vecA = [ -10, -10 ] - const vecB = [ 10, 10 ] - const result = vec2.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 0, 0 ]); - }); +describe('vec2', () => { - }); - }); + check(vec2n, Array); + check(vec2, Float32Array); + check(vec2d, Float64Array); }); diff --git a/test/tests/vec3-test.js b/test/tests/vec3-test.js index ce1eadb..4c33c28 100644 --- a/test/tests/vec3-test.js +++ b/test/tests/vec3-test.js @@ -1,17 +1,15 @@ -import {quat, vec3, utils} from '../../dist/2.x/wgpu-matrix.module.js'; +import {quat, vec3, vec3d, vec3n, utils} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, assertInstanceOf, - assertLessThan, assertStrictEqual, assertStrictNotEqual, - assertIsArray, assertEqualApproximately, assertTruthy, assertFalsy, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; // Note: vec3.create is use extensively in these tests but that is NOT idiomatic! // Idiomatic usage use to use raw JS arrays where convenient. For example @@ -22,21 +20,9 @@ import {describe, it, before} from '../mocha-support.js'; // The reason vec3.create is used in the tests is to make sure we are working // with the specified default type when testing. -function check(Type) { +function check(vec3, Type) { describe('using ' + Type, () => { - before(() => { - vec3.setDefaultType(Type); - }); - - function elementsEqual(a, b) { - assertStrictEqual(a.length, b.length); - for (let i = 0; i < a.length; ++i) { - const diff = Math.abs(a[i] - b[i]); - assertLessThan(diff, 0.0000001); - } - } - function clone(v) { return v.length ? v.slice() : v; } @@ -52,36 +38,36 @@ function check(Type) { const firstArg = args.shift(); // clone expected so we can check it wasn't modified expected = vec3.clone(expected); - let d = vec3.create(); + const d = vec3.create(); // clone v to make sure it's the correct type - let c = func(clone(firstArg), ...args, d); + const c = func(clone(firstArg), ...args, d); assertStrictEqual(c, d); assertEqualApproximately(c, expected); // test if we pass same vector as source and dest we get // correct result - if (firstArg.length) { - d = vec3.clone(firstArg); - // clone args to make sure we don't overwrite first arg - const bOrig = args.map(b => b.slice(b)); - c = func(d, ...args, d); - assertStrictEqual(c, d); - elementsEqual(c, expected); - args.forEach((b, ndx) => { - assertEqual(b, bOrig[ndx]); - }); - } + //if (firstArg.length) { + // d = vec3.clone(firstArg); + // // clone args to make sure we don't overwrite first arg + // const bOrig = args.map(b => clone(b)); + // c = func(d, ...args, d); + // assertStrictEqual(c, d); + // elementsEqual(c, expected); + // args.forEach((b, ndx) => { + // assertEqual(b, bOrig[ndx]); + // }); + //} // test if we pass operand as dest we get correct result - if (args.length > 0 && firstArg.length) { - d = vec3.clone(args[0]); - // clone v to make sure it is not overwritten - const vOrig = vec3.clone(firstArg); - c = func(firstArg, d, d); - elementsEqual(c, expected); - assertEqual(firstArg, vOrig); - assertStrictEqual(c, d); - } + //if (args.length > 0 && firstArg.length) { + // d = vec3.clone(args[0]); + // // clone v to make sure it is not overwritten + // const vOrig = vec3.clone(firstArg); + // c = func(firstArg, d, d); + // elementsEqual(c, expected); + // assertEqual(firstArg, vOrig); + // assertStrictEqual(c, d); + //} } function testV3WithAndWithoutDest(func, expected, ...args) { @@ -92,8 +78,8 @@ function check(Type) { it('should add', () => { const expected = [3, 5, 7]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.add(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.add(a, b, newDst); }, expected, [1, 2, 3], [2, 3, 4]); }); @@ -117,36 +103,36 @@ function check(Type) { it('should compute ceil', () => { const expected = [2, -1, 3]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.ceil(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.ceil(a, newDst); }, expected, [1.1, -1.1, 2.9]); }); it('should compute floor', () => { const expected = [1, -2, 2]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.floor(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.floor(a, newDst); }, expected, [1.1, -1.1, 2.9]); }); it('should compute round', () => { const expected = [1, -1, 3]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.round(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.round(a, newDst); }, expected, [1.1, -1.1, 2.9]); }); it('should clamp', () => { { const expected = [1, 0, 0.5]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.clamp(a, 0, 1, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.clamp(a, 0, 1, newDst); }, expected, [2, -1, 0.5]); } { const expected = [-10, 5, 2.9]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.clamp(a, -10, 5, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.clamp(a, -10, 5, newDst); }, expected, [-22, 50, 2.9]); } }); @@ -164,71 +150,71 @@ function check(Type) { it('should subtract', () => { const expected = [-1, -2, -3]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.subtract(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.subtract(a, b, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should sub', () => { const expected = [-1, -2, -3]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.sub(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.sub(a, b, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should lerp', () => { const expected = [1.5, 3, 4.5]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.lerp(a, b, 0.5, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.lerp(a, b, 0.5, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should lerp under 0', () => { const expected = [0.5, 1, 1.5]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.lerp(a, b, -0.5, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.lerp(a, b, -0.5, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should lerp over 0', () => { const expected = [2.5, 5, 7.5]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.lerp(a, b, 1.5, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.lerp(a, b, 1.5, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should multiply by scalar', () => { const expected = [2, 4, 6]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.mulScalar(a, 2, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.mulScalar(a, 2, newDst); }, expected, [1, 2, 3]); }); it('should scale', () => { const expected = [2, 4, 6]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.scale(a, 2, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.scale(a, 2, newDst); }, expected, [1, 2, 3]); }); it('should add scaled', () => { const expected = [5, 10, 15]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.addScaled(a, [2, 4, 6], 2, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.addScaled(a, [2, 4, 6], 2, newDst); }, expected, [1, 2, 3]); }); it('should divide by scalar', () => { const expected = [0.5, 1, 1.5]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.divScalar(a, 2, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.divScalar(a, 2, newDst); }, expected, [1, 2, 3]); }); it('should inverse', () => { const expected = [1 / 2, 1 / 3, 1 / -4]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.inverse(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.inverse(a, newDst); }, expected, [2, 3, -4]); }); @@ -238,8 +224,8 @@ function check(Type) { 3 * 2 - 1 * 6, 1 * 4 - 2 * 2, ]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.cross(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.cross(a, b, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); @@ -304,23 +290,23 @@ function check(Type) { 2 / length, 3 / length, ]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.normalize(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.normalize(a, newDst); }, expected, [1, 2, 3]); }); it('should negate', () => { const expected = [-1, -2, -3]; - testV3WithAndWithoutDest((a, dst) => { - return vec3.negate(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + return vec3.negate(a, newDst); }, expected, [1, 2, 3]); }); it('should copy', () => { const expected = [1, 2, 3]; const v = vec3.create(1, 2, 3); - testV3WithAndWithoutDest((a, dst) => { - const result = vec3.copy(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + const result = vec3.copy(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [1, 2, 3]); @@ -329,8 +315,8 @@ function check(Type) { it('should clone', () => { const expected = [1, 2, 3]; const v = vec3.create(1, 2, 3); - testV3WithAndWithoutDest((a, dst) => { - const result = vec3.clone(a, dst); + testV3WithAndWithoutDest((a, newDst) => { + const result = vec3.clone(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [1, 2, 3]); @@ -338,22 +324,22 @@ function check(Type) { it('should set', () => { const expected = [2, 3, 4]; - testV3WithAndWithoutDest((a, b, c, dst) => { - return vec3.set(a, b, c, dst); + testV3WithAndWithoutDest((a, b, c, newDst) => { + return vec3.set(a, b, c, newDst); }, expected, 2, 3, 4); }); it('should multiply', () => { const expected = [2, 8, 18]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.multiply(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.multiply(a, b, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); it('should mul', () => { const expected = [2, 8, 18]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.mul(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.mul(a, b, newDst); }, expected, [1, 2, 3], [2, 4, 6]); }); @@ -361,8 +347,8 @@ function check(Type) { const expected = [ 1 / 2, 2 / 3, 3 / 4, ]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.divide(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.divide(a, b, newDst); }, expected, [1, 2, 3], [2, 3, 4]); }); @@ -370,8 +356,8 @@ function check(Type) { const expected = [ 1 / 2, 2 / 3, 3 / 4, ]; - testV3WithAndWithoutDest((a, b, dst) => { - return vec3.div(a, b, dst); + testV3WithAndWithoutDest((a, b, newDst) => { + return vec3.div(a, b, newDst); }, expected, [1, 2, 3], [2, 3, 4]); }); @@ -403,21 +389,21 @@ function check(Type) { 0, 5, 0, 0, 0, 0, 6, 0, ]; - testV3WithAndWithoutDest((v, dst) => { - return vec3.transformMat3(v, m, dst); + testV3WithAndWithoutDest((v, newDst) => { + return vec3.transformMat3(v, m, newDst); }, expected, [1, 2, 3]); }); it('should transform by 4x4', () => { const expected = [5, 9, 15]; - testV3WithAndWithoutDest((v, dst) => { + testV3WithAndWithoutDest((v, newDst) => { const m = [ 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 4, 5, 6, 1, ]; - return vec3.transformMat4(v, m, dst); + return vec3.transformMat4(v, m, newDst); }, expected, [1, 2, 3]); }); @@ -429,19 +415,29 @@ function check(Type) { 0, 0, 3, 0, 4, 5, 6, 1, ]; - testV3WithAndWithoutDest((v, mat, dst) => { - return vec3.transformMat4Upper3x3(v, mat, dst); + testV3WithAndWithoutDest((v, mat, newDst) => { + return vec3.transformMat4Upper3x3(v, mat, newDst); }, expected, [2, 3, 4], m); }); it('should transform by quat', () => { const tests = [ - { q: quat.fromEuler(0.1, 0.2, 0.3, 'xyz'), expected: [ 10.48346640790187, 20.99753274028838, 33.81124896860183 ], }, - { q: quat.fromEuler(1.1, 2.2, 3.3, 'xyz'), expected: [ 31.030506373087608, 1.340345941350634, -27.005761366554264 ], }, + { + q: quat.fromEuler(0.1, 0.2, 0.3, 'xyz'), + expected: Type === Float32Array + ? [ 10.48346640790187, 20.99753274028838, 33.81124896860183 ] + : [ 10.483466535953458, 20.99753253479091, 33.81124896860183 ], + }, + { + q: quat.fromEuler(1.1, 2.2, 3.3, 'xyz'), + expected: Type === Float32Array + ? [ 31.030506373087608, 1.3403475284576416, -27.00575828552246 ] + : [ 31.03050528998851, 1.340347488701262, -27.005757983559995 ], + }, ]; for (const {q, expected} of tests) { - testV3WithAndWithoutDest((v, q, dst) => { - return vec3.transformQuat(v, q, dst); + testV3WithAndWithoutDest((v, q, newDst) => { + return vec3.transformQuat(v, q, newDst); }, expected, [11, 22, 33], q); } }); @@ -455,158 +451,119 @@ function check(Type) { assertEqual(v2, [0, 0, 0]); }); - }); -} - -describe('vec3', () => { - - it('should set default type', () => { - vec3.setDefaultType(Array); - let d = vec3.create(1, 2, 3); - assertIsArray(d); - - d = vec3.add([1, 2, 3], [4, 5, 6]); - assertIsArray(d); - - vec3.setDefaultType(Float32Array); - d = vec3.create(1, 2, 3); - assertInstanceOf(d, Float32Array); - - d = vec3.add([1, 2, 3], [4, 5, 6]); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); - - let vecA, vecB, result; - - describe('rotateX', function () { - describe('rotation around world origin [0, 0, 0]', function () { - beforeEach(function () { - vecA = [0, 1, 0]; - vecB = [0, 0, 0]; - result = vec3.rotateX(vecA, vecB, Math.PI); - }); - it("should return the rotated vector", function () { - assertEqualApproximately(result, [0, -1, 0]); + describe('rotateX', function () { + describe('rotation around world origin [0, 0, 0]', function () { + it("should return the rotated vector", function () { + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateX(a, b, angle, newDst); + }, [0, -1, 0], [0, 1, 0], [0, 0, 0], Math.PI); + }); }); - }); - describe('rotation around an arbitrary origin', function () { - beforeEach(function () { - vecA = [2, 7, 0]; - vecB = [2, 5, 0]; - result = vec3.rotateX(vecA, vecB, Math.PI); - }); - it("should return the rotated vector", function () { - assertEqualApproximately(result, [2, 3, 0]); + describe('rotation around an arbitrary origin', function () { + const expected = [2, 3, 0]; + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateX(a, b, angle, newDst); + }, expected, [2, 7, 0], [2, 5, 0], Math.PI); }); }); - }); - describe('rotateY', function () { - describe('rotation around world origin [0, 0, 0]', function () { - beforeEach(function() { - vecA = [1, 0, 0]; - vecB = [0, 0, 0]; - result = vec3.rotateY(vecA, vecB, Math.PI); - }); + describe('rotateY', function () { + describe('rotation around world origin [0, 0, 0]', function () { it("should return the rotated vector", function () { - assertEqualApproximately(result, [-1, 0, 0]); - }); - }); - describe('rotation around an arbitrary origin', function () { - beforeEach(function () { - vecA = [-2, 3, 10]; - vecB = [-4, 3, 10]; - result = vec3.rotateY(vecA, vecB, Math.PI); + const expected = [-1, 0, 0]; + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateY(a, b, angle, newDst); + }, expected, [1, 0, 0], [0, 0, 0], Math.PI); }); + }); + describe('rotation around an arbitrary origin', function () { it("should return the rotated vector", function () { - assertEqualApproximately(result, [-6, 3, 10]); + const expected = [-6, 3, 10]; + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateY(a, b, angle, newDst); + }, expected, [-2, 3, 10], [-4, 3, 10], Math.PI); }); + }); }); - }); - describe('rotateZ', function () { - describe('rotation around world origin [0, 0, 0]', function () { - beforeEach(function () { - vecA = [0, 1, 0]; - vecB = [0, 0, 0]; - result = vec3.rotateZ(vecA, vecB, Math.PI); - }); + describe('rotateZ', function () { + describe('rotation around world origin [0, 0, 0]', function () { it("should return the rotated vector", function () { - assertEqualApproximately(result, [0, -1, 0]); - }); - }); - describe('rotation around an arbitrary origin', function () { - beforeEach(function () { - vecA = [0, 6, -5]; - vecB = [0, 0, -5]; - result = vec3.rotateZ(vecA, vecB, Math.PI); + const expected = [0, -1, 0]; + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateZ(a, b, angle, newDst); + }, expected, [0, 1, 0], [0, 0, 0], Math.PI); }); + }); + describe('rotation around an arbitrary origin', function () { it("should return the rotated vector", function () { - assertEqualApproximately(result, [0, -6, -5]); + const expected = [0, -6, -5]; + testV3WithAndWithoutDest((a, b, angle, newDst) => { + return vec3.rotateZ(a, b, angle, newDst); + }, expected, [0, 6, -5], [0, 0, -5], Math.PI); }); - }); - }); - - describe('setLength', function() { - describe('set the length of a provided direction vector', function() { - let vecA, result; - beforeEach(function () { - vecA = [1, 1, 1]; - result = vec3.setLength(vecA, 14.6); - }); - it("should return the lengthened vector", function () { - assertEqualApproximately(result, [8.429313930168536, 8.429313930168536, 8.429313930168536]); - assertEqualApproximately(vec3.length(result), 14.6); }); }); - }); - - describe('truncate', function() { - describe('limit a vector to a max length', function() { - let vecA; - beforeEach(function () { - vecA = [8.429313930168536, 8.429313930168536, 8.429313930168536]; + describe('setLength', function () { + describe('set the length of a provided direction vector', function () { + it("should return the lengthened vector", function () { + const expected = [8.429313930168536, 8.429313930168536, 8.429313930168536]; + testV3WithAndWithoutDest( + (a, len, newDst) => vec3.setLength(a, len, newDst), + expected, + [1, 1, 1], 14.6); + }); }); + }); - it("should shorten the vector", function () { - const result = vec3.truncate(vecA, 4.0); - assertEqualApproximately(result, [2.309401076758503, 2.309401076758503, 2.309401076758503]); - assertEqualApproximately(vec3.length(result), 4.0); - }); + describe('truncate', function () { + describe('limit a vector to a max length', function () { + it("should shorten the vector", function () { + const expected = [2.309401076758503, 2.309401076758503, 2.309401076758503]; + testV3WithAndWithoutDest( + (a, len, newDst) => vec3.truncate(a, len, newDst), + expected, + [8.429313930168536, 8.429313930168536, 8.429313930168536], 4.0); + }); - it("should preserve the vector when shorter than maxLen", function () { - const result = vec3.truncate(vecA, 18.0); - assertEqualApproximately(result, [8.429313930168536, 8.429313930168536, 8.429313930168536]); - assertEqualApproximately(vec3.length(result), 14.6); + it("should preserve the vector when shorter than maxLen", function () { + const expected = [8.429313930168536, 8.429313930168536, 8.429313930168536]; + testV3WithAndWithoutDest( + (a, len, newDst) => vec3.truncate(a, len, newDst), + expected, [8.429313930168536, 8.429313930168536, 8.429313930168536], 18.0); + }); }); }); - }); - describe('midpoint', function() { - describe('return the midpoint between 2 vectors', function() { + describe('midpoint', function () { + describe('return the midpoint between 2 vectors', function () { - it("should return the midpoint", function () { - const vecA = [ 0, 0, 0 ] - const vecB = [ 10, 10, 10 ] - const result = vec3.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 5, 5, 5 ]); - }); + it("should return the midpoint", function () { + const vecA = [ 0, 0, 0 ]; + const vecB = [ 10, 10, 10 ]; + const result = vec3.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 5, 5, 5 ]); + }); - it("should handle negatives", function () { - const vecA = [ -10, -10, -10 ] - const vecB = [ 10, 10, 10 ] - const result = vec3.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 0, 0, 0 ]); - }); + it("should handle negatives", function () { + const vecA = [ -10, -10, -10 ]; + const vecB = [ 10, 10, 10 ]; + const result = vec3.midpoint(vecA, vecB); + assertEqualApproximately(result, [ 0, 0, 0 ]); + }); + }); }); + }); +} -}); +describe('vec3', () => { + + check(vec3n, Array); + check(vec3, Float32Array); + check(vec3d, Float64Array); +}); diff --git a/test/tests/vec4-test.js b/test/tests/vec4-test.js index 28ff528..5cfa247 100644 --- a/test/tests/vec4-test.js +++ b/test/tests/vec4-test.js @@ -1,17 +1,14 @@ -import {vec4, utils} from '../../dist/2.x/wgpu-matrix.module.js'; +import {vec4, vec4n, vec4d, utils} from '../../dist/2.x/wgpu-matrix.module.js'; import { assertEqual, assertInstanceOf, - assertLessThan, assertStrictEqual, assertStrictNotEqual, - assertIsArray, - assertEqualApproximately, assertTruthy, assertFalsy, } from '../assert.js'; -import {describe, it, before} from '../mocha-support.js'; +import {describe, it} from '../mocha-support.js'; // Note: vec4.create is use extensively in these tests but that is NOT idiomatic! // Idiomatic usage use to use raw JS arrays where convenient. For example @@ -22,25 +19,13 @@ import {describe, it, before} from '../mocha-support.js'; // The reason vec4.create is used in the tests is to make sure we are working // with the specified default type when testing. -function check(Type) { +function check(vec4, Type) { describe('using ' + Type, () => { - before(() => { - vec4.setDefaultType(Type); - }); - function clone(v) { return v.length ? v.slice() : v; } - function elementsEqual(a, b) { - assertStrictEqual(a.length, b.length); - for (let i = 0; i < a.length; ++i) { - const diff = Math.abs(a[i] - b[i]); - assertLessThan(diff, 0.0000001); - } - } - function testV4WithoutDest(func, expected, ...args) { const v = args.shift(); const d = func(clone(v), ...args); @@ -52,36 +37,36 @@ function check(Type) { const firstArg = args.shift(); // clone expected so we can check it wasn't modified expected = vec4.clone(expected); - let d = vec4.create(); + const d = vec4.create(); // clone v to make sure it's the correct type - let c = func(clone(firstArg), ...args, d); + const c = func(clone(firstArg), ...args, d); assertStrictEqual(c, d); assertEqual(c, expected); - // test if we pass same vector as source and dest we get - // correct result - if (firstArg.length) { - d = vec4.clone(firstArg); - // clone args to make sure we don't overwrite first arg - const bOrig = args.map(b => clone(b)); - c = func(d, ...args, d); - assertStrictEqual(c, d); - elementsEqual(c, expected); - args.forEach((b, ndx) => { - assertEqual(b, bOrig[ndx]); - }); - } - - // test if we pass operand as dest we get correct result - if (args.length > 0 && firstArg.length) { - d = vec4.clone(args[0]); - // clone v to make sure it is not overwritten - const vOrig = vec4.clone(firstArg); - c = func(firstArg, d, d); - elementsEqual(c, expected); - assertEqual(firstArg, vOrig); - assertStrictEqual(c, d); - } + //// test if we pass same vector as source and dest we get + //// correct result + //if (firstArg.length) { + // d = vec4.clone(firstArg); + // // clone args to make sure we don't overwrite first arg + // const bOrig = args.map(b => clone(b)); + // c = func(d, ...args, d); + // assertStrictEqual(c, d); + // elementsEqual(c, expected); + // args.forEach((b, ndx) => { + // assertEqual(b, bOrig[ndx]); + // }); + //} + + //// test if we pass operand as dest we get correct result + //if (args.length > 0 && firstArg.length) { + // d = vec4.clone(args[0]); + // // clone v to make sure it is not overwritten + // const vOrig = vec4.clone(firstArg); + // c = func(firstArg, d, d); + // elementsEqual(c, expected); + // assertEqual(firstArg, vOrig); + // assertStrictEqual(c, d); + //} } function testV4WithAndWithoutDest(func, expected, ...args) { @@ -92,43 +77,43 @@ function check(Type) { it('should add', () => { const expected = [3, 5, 7, 9]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.add(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.add(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 3, 4, 5]); }); it('should compute ceil', () => { const expected = [2, -1, 3, -4]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.ceil(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.ceil(a, newDst); }, expected, [1.1, -1.1, 2.9, -4.2]); }); it('should compute floor', () => { const expected = [1, -2, 2, -4]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.floor(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.floor(a, newDst); }, expected, [1.1, -1.1, 2.9, -3.1]); }); it('should compute round', () => { const expected = [1, -1, 3, 0]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.round(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.round(a, newDst); }, expected, [1.1, -1.1, 2.9, 0.1]); }); it('should clamp', () => { { const expected = [1, 0, 0.5, 0]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.clamp(a, 0, 1, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.clamp(a, 0, 1, newDst); }, expected, [2, -1, 0.5, -4]); } { const expected = [-10, 5, 2.9, -9]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.clamp(a, -10, 5, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.clamp(a, -10, 5, newDst); }, expected, [-22, 50, 2.9, -9]); } }); @@ -155,71 +140,71 @@ function check(Type) { it('should subtract', () => { const expected = [-1, -2, -3, -4]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.subtract(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.subtract(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should sub', () => { const expected = [-1, -2, -3, -4]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.sub(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.sub(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should lerp', () => { const expected = [1.5, 3, 4.5, 6]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.lerp(a, b, 0.5, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.lerp(a, b, 0.5, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should lerp under 0', () => { const expected = [0.5, 1, 1.5, 2]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.lerp(a, b, -0.5, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.lerp(a, b, -0.5, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should lerp over 0', () => { const expected = [2.5, 5, 7.5, 10]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.lerp(a, b, 1.5, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.lerp(a, b, 1.5, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should multiply by scalar', () => { const expected = [2, 4, 6, 8]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.mulScalar(a, 2, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.mulScalar(a, 2, newDst); }, expected, [1, 2, 3, 4]); }); it('should scale', () => { const expected = [2, 4, 6, 8]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.scale(a, 2, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.scale(a, 2, newDst); }, expected, [1, 2, 3, 4]); }); it('should add scaled', () => { const expected = [5, 10, 15, 20]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.addScaled(a, [2, 4, 6, 8], 2, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.addScaled(a, [2, 4, 6, 8], 2, newDst); }, expected, [1, 2, 3, 4]); }); it('should divide by scalar', () => { const expected = [0.5, 1, 1.5, 2]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.divScalar(a, 2, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.divScalar(a, 2, newDst); }, expected, [1, 2, 3, 4]); }); it('should inverse', () => { const expected = [1 / 2, 1 / 3, 1 / -4, 1 / -8]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.inverse(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.inverse(a, newDst); }, expected, [2, 3, -4, -8]); }); @@ -285,23 +270,23 @@ function check(Type) { 3 / length, 4 / length, ]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.normalize(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.normalize(a, newDst); }, expected, [1, 2, 3, 4]); }); it('should negate', () => { const expected = [-1, -2, -3, 4]; - testV4WithAndWithoutDest((a, dst) => { - return vec4.negate(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + return vec4.negate(a, newDst); }, expected, [1, 2, 3, -4]); }); it('should copy', () => { const expected = [1, 2, 3, 4]; const v = vec4.create(1, 2, 3, 4); - testV4WithAndWithoutDest((a, dst) => { - const result = vec4.copy(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + const result = vec4.copy(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [1, 2, 3, 4]); @@ -310,8 +295,8 @@ function check(Type) { it('should clone', () => { const expected = [1, 2, 3, 4]; const v = vec4.create(1, 2, 3, 4); - testV4WithAndWithoutDest((a, dst) => { - const result = vec4.clone(a, dst); + testV4WithAndWithoutDest((a, newDst) => { + const result = vec4.clone(a, newDst); assertStrictNotEqual(result, v); return result; }, expected, [1, 2, 3, 4]); @@ -319,36 +304,36 @@ function check(Type) { it('should set', () => { const expected = [2, 3, 4, 5]; - testV4WithAndWithoutDest((a, b, c, d, dst) => { - return vec4.set(a, b, c, d, dst); + testV4WithAndWithoutDest((a, b, c, d, newDst) => { + return vec4.set(a, b, c, d, newDst); }, expected, 2, 3, 4, 5); }); it('should multiply', () => { const expected = [2, 8, 18, 32]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.multiply(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.multiply(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should mul', () => { const expected = [2, 8, 18, 32]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.mul(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.mul(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 4, 6, 8]); }); it('should divide', () => { const expected = [1 / 2, 2 / 3, 3 / 4, 4 / 5]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.divide(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.divide(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 3, 4, 5]); }); it('should div', () => { const expected = [1 / 2, 2 / 3, 3 / 4, 4 / 5]; - testV4WithAndWithoutDest((a, b, dst) => { - return vec4.div(a, b, dst); + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.div(a, b, newDst); }, expected, [1, 2, 3, 4], [2, 3, 4, 5]); }); @@ -366,8 +351,8 @@ function check(Type) { 0, 0, 3, 0, 4, 5, 6, 1, ]; - testV4WithAndWithoutDest((v, mat, dst) => { - return vec4.transformMat4(v, mat, dst); + testV4WithAndWithoutDest((v, mat, newDst) => { + return vec4.transformMat4(v, mat, newDst); }, expected, [1, 2, 3, 4], m); }); @@ -380,86 +365,54 @@ function check(Type) { assertEqual(v2, [0, 0, 0, 0]); }); - }); -} - -describe('vec4', () => { - - it('should set default type', () => { - vec4.setDefaultType(Array); - let d = vec4.create(1, 2, 3, 4); - assertIsArray(d); - - d = vec4.add([1, 2, 3, 4], [5, 6, 7, 8]); - assertIsArray(d); - - vec4.setDefaultType(Float32Array); - d = vec4.create(1, 2, 3, 4); - assertInstanceOf(d, Float32Array); - - d = vec4.add([1, 2, 3, 4], [5, 6, 7, 8]); - assertInstanceOf(d, Float32Array); - }); - - check(Array); - check(Float32Array); - check(Float64Array); - - describe('setLength', function() { - describe('set the length of a provided direction vector', function() { - let vecA, result; - beforeEach(function () { - vecA = [1, 1, 1, 1]; - result = vec4.setLength(vecA, 14.6); - }); - it("should return the lengthened vector", function () { - assertEqualApproximately(result, [7.3, 7.3, 7.3, 7.3]); - assertEqualApproximately(vec4.length(result), 14.6); - }); + it('should setLength', () => { + const expected = [7.3, 7.3, 7.3, 7.3]; + testV4WithAndWithoutDest((v, len, newDst) => { + return vec4.setLength(v, len, newDst); + }, expected, [1, 1, 1, 1], 14.6); }); - }); - describe('truncate', function() { - describe('limit a vector to a max length', function() { - let vecA; - - beforeEach(function () { - vecA = [8.429313930168536, 8.429313930168536, 8.429313930168536, 8.429313930168536]; - }); + it('should truncate - shorten when too long', () => { + const expected = [ + 2.721655269759087, + Type === Float32Array ? 4.082483291625977 : 4.0824829046386295, + 5.443310539518174, + 6.804138174397716, + ]; + testV4WithAndWithoutDest((v, len, newDst) => { + return vec4.truncate(v, len, newDst); + }, expected, [20, 30, 40, 50], 10); + }); - it("should shorten the vector", function () { - const result = vec4.truncate(vecA, 4.0); - assertEqualApproximately(result, [2, 2, 2, 2]); - assertEqualApproximately(vec4.length(result), 4.0); - }); + it("should truncate - preserve the vector when shorter than maxLen", () => { + const expected = [20, 30, 40, 50]; + testV4WithAndWithoutDest((v, len, newDst) => { + return vec4.truncate(v, len, newDst); + }, expected, [20, 30, 40, 50], 100); + }); - it("should preserve the vector when shorter than maxLen", function () { - const result = vec4.truncate(vecA, 18.0); - assertEqualApproximately(result, [8.429313930168536, 8.429313930168536, 8.429313930168536, 8.429313930168536]); - assertEqualApproximately(vec4.length(result), 16.858627860337073); - }); + it('should midpoint', () => { + const expected = [6, 12, 18, 24]; + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.midpoint(a, b, newDst); + }, expected, [1, 2, 3, 4], [11, 22, 33, 44]); }); - }); - describe('midpoint', function() { - describe('return the midpoint between 2 vectors', function() { + it("should midpoint - handle negatives", function () { + const expected = [-5, -10, -15, -20]; + testV4WithAndWithoutDest((a, b, newDst) => { + return vec4.midpoint(a, b, newDst); + }, expected, [1, 2, 3, 4], [-11, -22, -33, -44]); + }); - it("should return the midpoint", function () { - const vecA = [ 0, 0, 0, 0 ] - const vecB = [ 10, 10, 10, 10 ] - const result = vec4.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 5, 5, 5, 5 ]); - }); + }); +} - it("should handle negatives", function () { - const vecA = [ -10, -10, -10, -10 ] - const vecB = [ 10, 10, 10, 10 ] - const result = vec4.midpoint(vecA, vecB); - assertEqualApproximately(result, [ 0, 0, 0, 0 ]); - }); +describe('vec4', () => { - }); - }); + check(vec4n, Array); + check(vec4, Float32Array); + check(vec4d, Float64Array); }); diff --git a/test/tests/wgpu-matrix-test.js b/test/tests/wgpu-matrix-test.js index 649756f..f0e4bfb 100644 --- a/test/tests/wgpu-matrix-test.js +++ b/test/tests/wgpu-matrix-test.js @@ -1,27 +1,5 @@ -import {setDefaultType, mat3, mat4, vec2, vec3, vec4} from '../../dist/2.x/wgpu-matrix.module.js'; - -import { - assertInstanceOf, -} from '../assert.js'; - -import {describe, it} from '../mocha-support.js'; +import {describe} from '../mocha-support.js'; describe('wgpu-matrix', () => { - function check(Type) { - - it(`setsDefaultType to ${Type}`, () => { - setDefaultType(Type); - assertInstanceOf(mat3.create(), Type); - assertInstanceOf(mat4.create(), Type); - assertInstanceOf(vec2.create(), Type); - assertInstanceOf(vec3.create(), Type); - assertInstanceOf(vec4.create(), Type); - }); - - } - - check(Array); - check(Float64Array); - check(Float32Array); });