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); });