├── .travis.yml ├── .gitignore ├── .eslintrc.json ├── README.md ├── index.js ├── script └── rollup.config.js ├── LICENSE ├── package.json ├── test ├── utils.spec.js ├── bits.spec.js ├── tap.js ├── color3.spec.js ├── color4.spec.js ├── mat2.spec.js ├── mat23.spec.js ├── mat3.spec.js ├── vec4.spec.js ├── mat4.spec.js └── vec2.spec.js └── lib ├── utils.js ├── color3.js ├── bits.js ├── color4.js ├── mat2.js ├── mat23.js ├── vec2.js └── vec4.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "6" 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | cache: 11 | - directories: 12 | - node_modules 13 | 14 | install: 15 | - npm install 16 | 17 | script: npm test 18 | 19 | notifications: 20 | email: false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # mac-osx files 2 | .DS_store 3 | profile 4 | 5 | # visual-studio files 6 | *.ncb *.sln 7 | *.suo 8 | *.vcproj.*.user 9 | *.pdb 10 | *.idb 11 | *.csproj 12 | *.csproj.user 13 | 14 | # visual-studio code 15 | .vscode/ 16 | 17 | # exvim files 18 | *.err 19 | *.exvim 20 | .exvim.*/ 21 | 22 | # webstorm 23 | .idea 24 | 25 | # log files 26 | *.log 27 | 28 | # project files 29 | node_modules 30 | bower_components 31 | dist 32 | yarn.lock 33 | package-lock.json 34 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "rules": { 4 | "comma-dangle": 0, 5 | "no-console": 0, 6 | "no-constant-condition": 0, 7 | "semi": 1 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 6, 11 | "sourceType": "module", 12 | "ecmaFeatures": { 13 | "jsx": true 14 | } 15 | }, 16 | "env": { 17 | "browser": true, 18 | "node": true, 19 | "es6": true, 20 | "mocha": true 21 | }, 22 | "plugins": [ 23 | ], 24 | "globals": { 25 | "deprecate": false, 26 | "helper": false, 27 | "tap": false, 28 | "unused": false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## vmath 2 | 3 | [](https://travis-ci.org/gamedev-js/vmath) 4 | 5 | Yet another gl-matrix but smaller (without SIMD) and faster (use Hidden class instead of Float32Array). 6 | 7 | ## Why? 8 | 9 | - Hidden classes + inline caching is much faster than Array/Float32Array. 10 | - Remove SIMD for smaller footprint. 11 | - Make sure using the column-major matrix for all calculation. 12 | 13 | ## Install 14 | 15 | ```bash 16 | npm install vmath 17 | ``` 18 | 19 | ## Documentation 20 | 21 | - [API](./api.md) 22 | 23 | ## License 24 | 25 | MIT © 2017 Johnny Wu -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export * from './lib/utils'; 2 | 3 | // NOTE: there is no syntax for: export {* as bits} from './lib/bits'; 4 | import * as bits_ from './lib/bits'; 5 | export let bits = bits_; 6 | 7 | export { default as vec2 } from './lib/vec2'; 8 | export { default as vec3 } from './lib/vec3'; 9 | export { default as vec4 } from './lib/vec4'; 10 | export { default as quat } from './lib/quat'; 11 | export { default as mat2 } from './lib/mat2'; 12 | export { default as mat23 } from './lib/mat23'; 13 | export { default as mat3 } from './lib/mat3'; 14 | export { default as mat4 } from './lib/mat4'; 15 | export { default as color3 } from './lib/color3'; 16 | export { default as color4 } from './lib/color4'; -------------------------------------------------------------------------------- /script/rollup.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fsJetpack = require('fs-jetpack'); 4 | const buble = require('rollup-plugin-buble'); 5 | const pjson = require('../package.json'); 6 | 7 | let banner = ` 8 | /* 9 | * ${pjson.name} v${pjson.version} 10 | * (c) ${new Date().getFullYear()} @Johnny Wu 11 | * Released under the MIT License. 12 | */ 13 | `; 14 | 15 | let dest = './dist'; 16 | let file = 'vmath'; 17 | let name = 'vmath'; 18 | let sourcemap = true; 19 | let globals = {}; 20 | 21 | // clear directory 22 | fsJetpack.dir(dest, { empty: true }); 23 | 24 | module.exports = { 25 | input: './index.js', 26 | external: [], 27 | plugins: [ 28 | buble() 29 | ], 30 | output: [ 31 | { 32 | file: `${dest}/${file}.dev.js`, 33 | format: 'iife', 34 | name, 35 | banner, 36 | sourcemap, 37 | globals, 38 | }, 39 | { 40 | file: `${dest}/${file}.js`, 41 | format: 'cjs', 42 | name, 43 | banner, 44 | sourcemap, 45 | globals, 46 | }, 47 | ], 48 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 Johnny Wu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vmath", 3 | "version": "1.4.8", 4 | "description": "Yet another gl-matrix: faster and smaller", 5 | "main": "dist/vmath.js", 6 | "module": "index.js", 7 | "jsnext:main": "index.js", 8 | "scripts": { 9 | "prepare": "npm run build", 10 | "pretest": "npm run build:dev", 11 | "build": "npm run build:dev && npm run build:min", 12 | "build:dev": "rollup -c ./script/rollup.config.js", 13 | "build:min": "uglifyjs ./dist/vmath.dev.js --mangle --source-map url=vmath.min.js.map -o ./dist/vmath.min.js", 14 | "docs": "documentation build ./index.js -f md -o api.md", 15 | "test": "tap test/*.spec.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/gamedev-js/vmath.git" 20 | }, 21 | "keywords": [ 22 | "math", 23 | "algebre", 24 | "vector", 25 | "matrix" 26 | ], 27 | "author": "jwu", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/gamedev-js/vmath/issues" 31 | }, 32 | "homepage": "https://github.com/gamedev-js/vmath/issues", 33 | "dependencies": {}, 34 | "devDependencies": { 35 | "documentation": "^4.0.0-beta.19", 36 | "fs-jetpack": "^0.13.2", 37 | "rollup": "^0.54.1", 38 | "rollup-plugin-buble": "^0.18.0", 39 | "tap": "^10.3.0", 40 | "uglify-js": "^3.3.7" 41 | }, 42 | "files": [ 43 | "dist", 44 | "lib", 45 | "index.js" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/utils.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const utils = require('../dist/vmath'); 3 | 4 | tap.test('utils', t => { 5 | t.test('equals', t => { 6 | let r0 = utils.equals(1.0, 0.0); 7 | let r1 = utils.equals(1.0, 1.0); 8 | let r2 = utils.equals(1.0 + utils.EPSILON / 2, 1.0); 9 | 10 | t.equal(r0, false); 11 | t.equal(r1, true); 12 | t.equal(r2, true); 13 | 14 | t.end(); 15 | }); 16 | 17 | t.test('approx', t => { 18 | let r0 = utils.approx(1.0, 0.0); 19 | let r1 = utils.approx(1.0, 1.01, 0.1); 20 | let r2 = utils.approx(1.0 + utils.EPSILON / 2, 1.0); 21 | 22 | t.equal(r0, false); 23 | t.equal(r1, true); 24 | t.equal(r2, true); 25 | 26 | t.end(); 27 | }); 28 | 29 | t.test('clamp', t => { 30 | t.equal(utils.clamp(0, -1, 1), 0); 31 | t.equal(utils.clamp(10, -1, 1), 1); 32 | t.equal(utils.clamp(-10, -1, 1), -1); 33 | 34 | t.end(); 35 | }); 36 | 37 | t.test('clamp01', t => { 38 | t.equal(utils.clamp01(0), 0); 39 | t.equal(utils.clamp01(10), 1); 40 | t.equal(utils.clamp01(-10), 0); 41 | 42 | t.end(); 43 | }); 44 | 45 | t.test('lerp', t => { 46 | t.equal(utils.lerp(0, 1, 0.2), 0.2); 47 | t.equal(utils.lerp(-10, 10, 0.5), 0); 48 | t.equal(utils.lerp(0, 1, 1), 1); 49 | t.equal(utils.lerp(0, 1, 0), 0); 50 | 51 | t.end(); 52 | }); 53 | 54 | t.test('toRadian', t => { 55 | t.approx(utils.toRadian(180), Math.PI); 56 | t.end(); 57 | }); 58 | 59 | t.test('toDegree', t => { 60 | t.approx(utils.toDegree(Math.PI), 180); 61 | t.end(); 62 | }); 63 | 64 | t.test('randomRange', t => { 65 | let r = utils.randomRange(-10, 10); 66 | t.assert(r >= -10 && r <= 10); 67 | t.end(); 68 | }); 69 | 70 | t.test('randomRangeInt', t => { 71 | let r = utils.randomRangeInt(-10, 10); 72 | t.assert(r >= -10 && r <= 10); 73 | t.end(); 74 | }); 75 | 76 | t.test('randomRangeInt', t => { 77 | let r = utils.randomRangeInt(-10, 10); 78 | t.assert(r >= -10 && r <= 10); 79 | t.end(); 80 | }); 81 | 82 | t.test('nextPow2', t => { 83 | t.equal(utils.nextPow2(10), 16); 84 | t.equal(utils.nextPow2(245), 256); 85 | t.end(); 86 | }); 87 | 88 | t.end(); 89 | }); 90 | 91 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const _d2r = Math.PI / 180.0; 2 | const _r2d = 180.0 / Math.PI; 3 | 4 | /** 5 | * @property {number} EPSILON 6 | */ 7 | export const EPSILON = 0.000001; 8 | 9 | /** 10 | * Tests whether or not the arguments have approximately the same value, within an absolute 11 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less 12 | * than or equal to 1.0, and a relative tolerance is used for larger values) 13 | * 14 | * @param {Number} a The first number to test. 15 | * @param {Number} b The second number to test. 16 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise. 17 | */ 18 | export function equals(a, b) { 19 | return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); 20 | } 21 | 22 | /** 23 | * Tests whether or not the arguments have approximately the same value by given maxDiff 24 | * 25 | * @param {Number} a The first number to test. 26 | * @param {Number} b The second number to test. 27 | * @param {Number} maxDiff Maximum difference. 28 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise. 29 | */ 30 | export function approx(a, b, maxDiff) { 31 | maxDiff = maxDiff || EPSILON; 32 | return Math.abs(a - b) <= maxDiff; 33 | } 34 | 35 | /** 36 | * Clamps a value between a minimum float and maximum float value. 37 | * 38 | * @method clamp 39 | * @param {number} val 40 | * @param {number} min 41 | * @param {number} max 42 | * @return {number} 43 | */ 44 | export function clamp(val, min, max) { 45 | return val < min ? min : val > max ? max : val; 46 | } 47 | 48 | /** 49 | * Clamps a value between 0 and 1. 50 | * 51 | * @method clamp01 52 | * @param {number} val 53 | * @return {number} 54 | */ 55 | export function clamp01(val) { 56 | return val < 0 ? 0 : val > 1 ? 1 : val; 57 | } 58 | 59 | /** 60 | * @method lerp 61 | * @param {number} from 62 | * @param {number} to 63 | * @param {number} ratio - the interpolation coefficient 64 | * @return {number} 65 | */ 66 | export function lerp(from, to, ratio) { 67 | return from + (to - from) * ratio; 68 | } 69 | 70 | /** 71 | * Convert Degree To Radian 72 | * 73 | * @param {Number} a Angle in Degrees 74 | */ 75 | export function toRadian(a) { 76 | return a * _d2r; 77 | } 78 | 79 | /** 80 | * Convert Radian To Degree 81 | * 82 | * @param {Number} a Angle in Radian 83 | */ 84 | export function toDegree(a) { 85 | return a * _r2d; 86 | } 87 | 88 | /** 89 | * @method random 90 | */ 91 | export const random = Math.random; 92 | 93 | /** 94 | * Returns a floating-point random number between min (inclusive) and max (exclusive). 95 | * 96 | * @method randomRange 97 | * @param {number} min 98 | * @param {number} max 99 | * @return {number} the random number 100 | */ 101 | export function randomRange(min, max) { 102 | return Math.random() * (max - min) + min; 103 | } 104 | 105 | /** 106 | * Returns a random integer between min (inclusive) and max (exclusive). 107 | * 108 | * @method randomRangeInt 109 | * @param {number} min 110 | * @param {number} max 111 | * @return {number} the random integer 112 | */ 113 | export function randomRangeInt(min, max) { 114 | return Math.floor(randomRange(min, max)); 115 | } 116 | 117 | /** 118 | * Returns the next power of two for the value 119 | * 120 | * @method nextPow2 121 | * @param {number} val 122 | * @return {number} the the next power of two 123 | */ 124 | export function nextPow2(val) { 125 | --val; 126 | val = (val >> 1) | val; 127 | val = (val >> 2) | val; 128 | val = (val >> 4) | val; 129 | val = (val >> 8) | val; 130 | val = (val >> 16) | val; 131 | ++val; 132 | 133 | return val; 134 | } -------------------------------------------------------------------------------- /test/bits.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { bits } = require('../dist/vmath'); 3 | 4 | const INT_MAX = bits.INT_MAX, INT_MIN = bits.INT_MIN; 5 | 6 | tap.test('bits', t => { 7 | t.test('sign', t => { 8 | t.equal(bits.sign(-100), -1); 9 | t.equal(bits.sign(100), 1); 10 | t.equal(bits.sign(0), 0); 11 | t.equal(bits.sign(bits.INT_MAX), 1); 12 | t.equal(bits.sign(bits.INT_MIN), -1); 13 | t.end(); 14 | }); 15 | 16 | t.test('abs', t => { 17 | t.equal(bits.abs(0), 0); 18 | t.equal(bits.abs(1), 1); 19 | t.equal(bits.abs(-1), 1); 20 | t.equal(bits.abs(bits.INT_MAX), bits.INT_MAX); 21 | t.equal(bits.abs(-bits.INT_MAX), bits.INT_MAX); 22 | t.end(); 23 | }); 24 | 25 | t.test('min', t => { 26 | t.equal(bits.min(0, 0), 0); 27 | t.equal(bits.min(-1, 1), -1); 28 | t.equal(bits.min(INT_MAX, INT_MAX), INT_MAX); 29 | t.equal(bits.min(INT_MIN, INT_MIN), INT_MIN); 30 | t.equal(bits.min(INT_MAX, INT_MIN), INT_MIN); 31 | t.end(); 32 | }); 33 | 34 | t.test('max', t => { 35 | t.equal(bits.max(0, 0), 0); 36 | t.equal(bits.max(-1, 1), 1); 37 | t.equal(bits.max(INT_MAX, INT_MAX), INT_MAX); 38 | t.equal(bits.max(INT_MIN, INT_MIN), INT_MIN); 39 | t.equal(bits.max(INT_MAX, INT_MIN), INT_MAX); 40 | t.end(); 41 | }); 42 | 43 | t.test('isPow2', t => { 44 | t.ok(!bits.isPow2(0)); 45 | for (var i = 0; i < 31; ++i) { 46 | t.ok(bits.isPow2((1 << i))); 47 | } 48 | t.ok(!bits.isPow2(100)); 49 | t.ok(!bits.isPow2(0x7fffffff)); 50 | t.ok(!bits.isPow2(-1000000)); 51 | t.end(); 52 | }); 53 | 54 | t.test('log2', t => { 55 | for (var i = 0; i < 31; ++i) { 56 | if (i > 0) { 57 | t.equal(bits.log2((1 << i) - 1), i - 1); 58 | t.equal(bits.log2((1 << i) + 1), i); 59 | } 60 | t.equal(bits.log2((1 << i)), i); 61 | } 62 | t.end(); 63 | }); 64 | 65 | t.test('popCount', t => { 66 | t.equal(bits.popCount(0), 0); 67 | t.equal(bits.popCount(1), 1); 68 | t.equal(bits.popCount(-1), 32); 69 | for (var i = 0; i < 31; ++i) { 70 | t.equal(bits.popCount(1 << i), 1); 71 | t.equal(bits.popCount((1 << i) - 1), i); 72 | } 73 | t.equal(bits.popCount(0xf0f00f0f), 16); 74 | t.end(); 75 | }); 76 | 77 | t.test('countTrailingZeros', t => { 78 | t.equal(bits.countTrailingZeros(0), 32); 79 | t.equal(bits.countTrailingZeros(1), 0); 80 | t.equal(bits.countTrailingZeros(-1), 0); 81 | for (var i = 0; i < 31; ++i) { 82 | t.equal(bits.countTrailingZeros(1 << i), i); 83 | if (i > 0) { 84 | t.equal(bits.countTrailingZeros((1 << i) - 1), 0); 85 | } 86 | } 87 | t.equal(bits.countTrailingZeros(0xf81700), 8); 88 | t.end(); 89 | }); 90 | 91 | t.test('nextPow2', t => { 92 | for (var i = 0; i < 31; ++i) { 93 | if (i !== 1) { 94 | t.equal(bits.nextPow2((1 << i) - 1), 1 << i); 95 | } 96 | t.equal(bits.nextPow2((1 << i)), 1 << i); 97 | if (i < 30) { 98 | t.equal(bits.nextPow2((1 << i) + 1), 1 << (i + 1)); 99 | } 100 | } 101 | t.end(); 102 | }); 103 | 104 | t.test('prevPow2', t => { 105 | for (var i = 0; i < 31; ++i) { 106 | if (i > 0) { 107 | t.equal(bits.prevPow2((1 << i) - 1), 1 << (i - 1)); 108 | } 109 | t.equal(bits.prevPow2((1 << i)), 1 << i); 110 | if (0 < i && i < 30) { 111 | t.equal(bits.prevPow2((1 << i) + 1), 1 << i, 'i=' + i + ', ' + ((1 << i) + 1)); 112 | } 113 | } 114 | t.end(); 115 | }); 116 | 117 | t.test('parity', t => { 118 | t.equal(bits.parity(1), 1); 119 | t.equal(bits.parity(0), 0); 120 | t.equal(bits.parity(0xf), 0); 121 | t.equal(bits.parity(0x10f), 1); 122 | t.end(); 123 | }); 124 | 125 | t.test('reverse', t => { 126 | t.equal(bits.reverse(0), 0); 127 | t.equal(bits.reverse(-1), -1); 128 | t.end(); 129 | }); 130 | 131 | t.test('nextCombination', t => { 132 | t.equal(bits.nextCombination(1), 2); 133 | t.equal(bits.nextCombination(0x300), 0x401); 134 | t.end(); 135 | }); 136 | 137 | t.test('interleave2', t => { 138 | for (let x = 0; x < 5; ++x) { 139 | for (let y = 0; y < 5; ++y) { 140 | let h = bits.interleave2(x, y); 141 | t.equal(bits.deinterleave2(h, 0), x); 142 | t.equal(bits.deinterleave2(h, 1), y); 143 | } 144 | } 145 | t.end(); 146 | }); 147 | 148 | t.test('interleave3', t => { 149 | for (let x = 0; x <= 5; ++x) { 150 | for (let y = 0; y <= 5; ++y) { 151 | for (let z = 0; z <= 5; ++z) { 152 | let h = bits.interleave3(x, y, z); 153 | t.equal(bits.deinterleave3(h, 0), x); 154 | t.equal(bits.deinterleave3(h, 1), y); 155 | t.equal(bits.deinterleave3(h, 2), z); 156 | } 157 | } 158 | } 159 | t.end(); 160 | }); 161 | 162 | t.end(); 163 | }); -------------------------------------------------------------------------------- /lib/color3.js: -------------------------------------------------------------------------------- 1 | import { EPSILON } from './utils'; 2 | 3 | let _tmp = new Array(3); 4 | 5 | class _color3 { 6 | constructor(r, g, b) { 7 | this.r = r; 8 | this.g = g; 9 | this.b = b; 10 | } 11 | 12 | toJSON() { 13 | _tmp[0] = this.r; 14 | _tmp[1] = this.g; 15 | _tmp[2] = this.b; 16 | 17 | return _tmp; 18 | } 19 | } 20 | 21 | /** 22 | * @class Color 23 | * @name color3 24 | */ 25 | let color3 = {}; 26 | 27 | /** 28 | * Creates a new color 29 | * 30 | * @returns {color3} a new color 31 | */ 32 | color3.create = function () { 33 | return new _color3(1, 1, 1); 34 | }; 35 | 36 | /** 37 | * Creates a new color initialized with the given values 38 | * 39 | * @param {Number} r red component 40 | * @param {Number} g green component 41 | * @param {Number} b blue component 42 | * @returns {color3} a new color 43 | * @function 44 | */ 45 | color3.new = function (r, g, b) { 46 | return new _color3(r, g, b); 47 | }; 48 | 49 | /** 50 | * Creates a new color initialized with values from an existing quaternion 51 | * 52 | * @param {color3} a color to clone 53 | * @returns {color3} a new color 54 | * @function 55 | */ 56 | color3.clone = function (a) { 57 | return new _color3(a.r, a.g, a.b, a.a); 58 | }; 59 | 60 | /** 61 | * Copy the values from one color to another 62 | * 63 | * @param {color3} out the receiving color 64 | * @param {color3} a the source color 65 | * @returns {color3} out 66 | * @function 67 | */ 68 | color3.copy = function (out, a) { 69 | out.r = a.r; 70 | out.g = a.g; 71 | out.b = a.b; 72 | return out; 73 | }; 74 | 75 | /** 76 | * Set the components of a color to the given values 77 | * 78 | * @param {color3} out the receiving color 79 | * @param {Number} r red component 80 | * @param {Number} g green component 81 | * @param {Number} b blue component 82 | * @returns {color3} out 83 | * @function 84 | */ 85 | color3.set = function (out, r, g, b) { 86 | out.r = r; 87 | out.g = g; 88 | out.b = b; 89 | return out; 90 | }; 91 | 92 | /** 93 | * Set from hex 94 | * 95 | * @param {color3} out the receiving color 96 | * @param {Number} hex 97 | * @returns {color3} out 98 | * @function 99 | */ 100 | color3.fromHex = function (out, hex) { 101 | let r = ((hex >> 16)) / 255.0; 102 | let g = ((hex >> 8) & 0xff) / 255.0; 103 | let b = ((hex) & 0xff) / 255.0; 104 | 105 | out.r = r; 106 | out.g = g; 107 | out.b = b; 108 | return out; 109 | }; 110 | 111 | /** 112 | * Adds two color's 113 | * 114 | * @param {color3} out the receiving color 115 | * @param {color3} a the first operand 116 | * @param {color3} b the second operand 117 | * @returns {color3} out 118 | * @function 119 | */ 120 | color3.add = function (out, a, b) { 121 | out.r = a.r + b.r; 122 | out.g = a.g + b.g; 123 | out.b = a.b + b.b; 124 | return out; 125 | }; 126 | 127 | /** 128 | * Subtracts color b from color a 129 | * 130 | * @param {color3} out the receiving color 131 | * @param {color3} a the first operand 132 | * @param {color3} b the second operand 133 | * @returns {color3} out 134 | */ 135 | color3.subtract = function (out, a, b) { 136 | out.r = a.r - b.r; 137 | out.g = a.g - b.g; 138 | out.b = a.b - b.b; 139 | return out; 140 | }; 141 | 142 | /** 143 | * Alias for {@link color3.subtract} 144 | * @function 145 | */ 146 | color3.sub = color3.subtract; 147 | 148 | /** 149 | * Multiplies two color's 150 | * 151 | * @param {color3} out the receiving color 152 | * @param {color3} a the first operand 153 | * @param {color3} b the second operand 154 | * @returns {color3} out 155 | * @function 156 | */ 157 | color3.multiply = function (out, a, b) { 158 | out.r = a.r * b.r; 159 | out.g = a.g * b.g; 160 | out.b = a.b * b.b; 161 | return out; 162 | }; 163 | 164 | /** 165 | * Alias for {@link color3.multiply} 166 | * @function 167 | */ 168 | color3.mul = color3.multiply; 169 | 170 | /** 171 | * Divides two color's 172 | * 173 | * @param {color3} out the receiving vector 174 | * @param {color3} a the first operand 175 | * @param {color3} b the second operand 176 | * @returns {color3} out 177 | */ 178 | color3.divide = function (out, a, b) { 179 | out.r = a.r / b.r; 180 | out.g = a.g / b.g; 181 | out.b = a.b / b.b; 182 | return out; 183 | }; 184 | 185 | /** 186 | * Alias for {@link color3.divide} 187 | * @function 188 | */ 189 | color3.div = color3.divide; 190 | 191 | 192 | /** 193 | * Scales a color by a scalar number 194 | * 195 | * @param {color3} out the receiving vector 196 | * @param {color3} a the vector to scale 197 | * @param {Number} b amount to scale the vector by 198 | * @returns {color3} out 199 | * @function 200 | */ 201 | color3.scale = function (out, a, b) { 202 | out.r = a.r * b; 203 | out.g = a.g * b; 204 | out.b = a.b * b; 205 | return out; 206 | }; 207 | 208 | /** 209 | * Performs a linear interpolation between two color's 210 | * 211 | * @param {color3} out the receiving color 212 | * @param {color3} a the first operand 213 | * @param {color3} b the second operand 214 | * @param {Number} t interpolation amount between the two inputs 215 | * @returns {color3} out 216 | * @function 217 | */ 218 | color3.lerp = function (out, a, b, t) { 219 | let ar = a.r, 220 | ag = a.g, 221 | ab = a.b; 222 | out.r = ar + t * (b.r - ar); 223 | out.g = ag + t * (b.g - ag); 224 | out.b = ab + t * (b.b - ab); 225 | return out; 226 | }; 227 | 228 | /** 229 | * Returns a string representation of a color 230 | * 231 | * @param {color3} a vector to represent as a string 232 | * @returns {String} string representation of the vector 233 | */ 234 | color3.str = function (a) { 235 | return `color3(${a.r}, ${a.g}, ${a.b})`; 236 | }; 237 | 238 | /** 239 | * Returns typed array 240 | * 241 | * @param {array} out 242 | * @param {color3} a 243 | * @returns {array} 244 | */ 245 | color3.array = function (out, a) { 246 | out[0] = a.r; 247 | out[1] = a.g; 248 | out[2] = a.b; 249 | 250 | return out; 251 | }; 252 | 253 | /** 254 | * Returns whether or not the color have exactly the same elements in the same position (when compared with ===) 255 | * 256 | * @param {color3} a The first color3. 257 | * @param {color3} b The second color3. 258 | * @returns {Boolean} True if the colors are equal, false otherwise. 259 | */ 260 | color3.exactEquals = function (a, b) { 261 | return a.r === b.r && a.g === b.g && a.b === b.b; 262 | }; 263 | 264 | /** 265 | * Returns whether or not the colors have approximately the same elements in the same position. 266 | * 267 | * @param {color3} a The first color3. 268 | * @param {color3} b The second color3. 269 | * @returns {Boolean} True if the colors are equal, false otherwise. 270 | */ 271 | color3.equals = function (a, b) { 272 | let a0 = a.r, a1 = a.g, a2 = a.b; 273 | let b0 = b.r, b1 = b.g, b2 = b.b; 274 | return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 275 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && 276 | Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2))); 277 | }; 278 | 279 | /** 280 | * Returns the hex value 281 | * 282 | * @param {color3} a The color 283 | * @returns {Number} 284 | */ 285 | color3.hex = function (a) { 286 | return (a.r * 255) << 16 | (a.g * 255) << 8 | (a.b * 255); 287 | }; 288 | 289 | export default color3; -------------------------------------------------------------------------------- /lib/bits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bit twiddling hacks for JavaScript. 3 | * 4 | * Author: Mikola Lysenko 5 | * 6 | * Ported from Stanford bit twiddling hack library: 7 | * http://graphics.stanford.edu/~seander/bithacks.html 8 | */ 9 | 10 | 'use strict'; 11 | 12 | // Number of bits in an integer 13 | export const INT_BITS = 32; 14 | export const INT_MAX = 0x7fffffff; 15 | export const INT_MIN = -1<<(INT_BITS-1); 16 | 17 | /** 18 | * Returns -1, 0, +1 depending on sign of x 19 | * 20 | * @param {number} v 21 | * @returns {number} 22 | */ 23 | export function sign(v) { 24 | return (v > 0) - (v < 0); 25 | } 26 | 27 | /** 28 | * Computes absolute value of integer 29 | * 30 | * @param {number} v 31 | * @returns {number} 32 | */ 33 | export function abs(v) { 34 | let mask = v >> (INT_BITS-1); 35 | return (v ^ mask) - mask; 36 | } 37 | 38 | /** 39 | * Computes minimum of integers x and y 40 | * 41 | * @param {number} x 42 | * @param {number} y 43 | * @returns {number} 44 | */ 45 | export function min(x, y) { 46 | return y ^ ((x ^ y) & -(x < y)); 47 | } 48 | 49 | /** 50 | * Computes maximum of integers x and y 51 | * 52 | * @param {number} x 53 | * @param {number} y 54 | * @returns {number} 55 | */ 56 | export function max(x, y) { 57 | return x ^ ((x ^ y) & -(x < y)); 58 | } 59 | 60 | /** 61 | * Checks if a number is a power of two 62 | * 63 | * @param {number} v 64 | * @returns {boolean} 65 | */ 66 | export function isPow2(v) { 67 | return !(v & (v-1)) && (!!v); 68 | } 69 | 70 | /** 71 | * Computes log base 2 of v 72 | * 73 | * @param {number} v 74 | * @returns {number} 75 | */ 76 | export function log2(v) { 77 | let r, shift; 78 | r = (v > 0xFFFF) << 4; v >>>= r; 79 | shift = (v > 0xFF ) << 3; v >>>= shift; r |= shift; 80 | shift = (v > 0xF ) << 2; v >>>= shift; r |= shift; 81 | shift = (v > 0x3 ) << 1; v >>>= shift; r |= shift; 82 | return r | (v >> 1); 83 | } 84 | 85 | /** 86 | * Computes log base 10 of v 87 | * 88 | * @param {number} v 89 | * @returns {number} 90 | */ 91 | export function log10(v) { 92 | return (v >= 1000000000) ? 9 : (v >= 100000000) ? 8 : (v >= 10000000) ? 7 : 93 | (v >= 1000000) ? 6 : (v >= 100000) ? 5 : (v >= 10000) ? 4 : 94 | (v >= 1000) ? 3 : (v >= 100) ? 2 : (v >= 10) ? 1 : 0; 95 | } 96 | 97 | /** 98 | * Counts number of bits 99 | * 100 | * @param {number} v 101 | * @returns {number} 102 | */ 103 | export function popCount(v) { 104 | v = v - ((v >>> 1) & 0x55555555); 105 | v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); 106 | return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24; 107 | } 108 | 109 | /** 110 | * Counts number of trailing zeros 111 | * 112 | * @param {number} v 113 | * @returns {number} 114 | */ 115 | export function countTrailingZeros(v) { 116 | let c = 32; 117 | v &= -v; 118 | if (v) c--; 119 | if (v & 0x0000FFFF) c -= 16; 120 | if (v & 0x00FF00FF) c -= 8; 121 | if (v & 0x0F0F0F0F) c -= 4; 122 | if (v & 0x33333333) c -= 2; 123 | if (v & 0x55555555) c -= 1; 124 | return c; 125 | } 126 | 127 | /** 128 | * Rounds to next power of 2 129 | * 130 | * @param {number} v 131 | * @returns {number} 132 | */ 133 | export function nextPow2(v) { 134 | v += v === 0; 135 | --v; 136 | v |= v >>> 1; 137 | v |= v >>> 2; 138 | v |= v >>> 4; 139 | v |= v >>> 8; 140 | v |= v >>> 16; 141 | return v + 1; 142 | } 143 | 144 | /** 145 | * Rounds down to previous power of 2 146 | * 147 | * @param {number} v 148 | * @returns {number} 149 | */ 150 | export function prevPow2(v) { 151 | v |= v >>> 1; 152 | v |= v >>> 2; 153 | v |= v >>> 4; 154 | v |= v >>> 8; 155 | v |= v >>> 16; 156 | return v - (v>>>1); 157 | } 158 | 159 | /** 160 | * Computes parity of word 161 | * 162 | * @param {number} v 163 | * @returns {number} 164 | */ 165 | export function parity(v) { 166 | v ^= v >>> 16; 167 | v ^= v >>> 8; 168 | v ^= v >>> 4; 169 | v &= 0xf; 170 | return (0x6996 >>> v) & 1; 171 | } 172 | 173 | const REVERSE_TABLE = new Array(256); 174 | 175 | (function(tab) { 176 | for(let i=0; i<256; ++i) { 177 | let v = i, r = i, s = 7; 178 | for (v >>>= 1; v; v >>>= 1) { 179 | r <<= 1; 180 | r |= v & 1; 181 | --s; 182 | } 183 | tab[i] = (r << s) & 0xff; 184 | } 185 | })(REVERSE_TABLE); 186 | 187 | /** 188 | * Reverse bits in a 32 bit word 189 | * 190 | * @param {number} v 191 | * @returns {number} 192 | */ 193 | export function reverse(v) { 194 | return (REVERSE_TABLE[v & 0xff] << 24) | 195 | (REVERSE_TABLE[(v >>> 8) & 0xff] << 16) | 196 | (REVERSE_TABLE[(v >>> 16) & 0xff] << 8) | 197 | REVERSE_TABLE[(v >>> 24) & 0xff]; 198 | } 199 | 200 | /** 201 | * Interleave bits of 2 coordinates with 16 bits. Useful for fast quadtree codes 202 | * 203 | * @param {number} x 204 | * @param {number} y 205 | * @returns {number} 206 | */ 207 | export function interleave2(x, y) { 208 | x &= 0xFFFF; 209 | x = (x | (x << 8)) & 0x00FF00FF; 210 | x = (x | (x << 4)) & 0x0F0F0F0F; 211 | x = (x | (x << 2)) & 0x33333333; 212 | x = (x | (x << 1)) & 0x55555555; 213 | 214 | y &= 0xFFFF; 215 | y = (y | (y << 8)) & 0x00FF00FF; 216 | y = (y | (y << 4)) & 0x0F0F0F0F; 217 | y = (y | (y << 2)) & 0x33333333; 218 | y = (y | (y << 1)) & 0x55555555; 219 | 220 | return x | (y << 1); 221 | } 222 | 223 | /** 224 | * Extracts the nth interleaved component 225 | * 226 | * @param {number} v 227 | * @param {number} n 228 | * @returns {number} 229 | */ 230 | export function deinterleave2(v, n) { 231 | v = (v >>> n) & 0x55555555; 232 | v = (v | (v >>> 1)) & 0x33333333; 233 | v = (v | (v >>> 2)) & 0x0F0F0F0F; 234 | v = (v | (v >>> 4)) & 0x00FF00FF; 235 | v = (v | (v >>> 16)) & 0x000FFFF; 236 | return (v << 16) >> 16; 237 | } 238 | 239 | /** 240 | * Interleave bits of 3 coordinates, each with 10 bits. Useful for fast octree codes 241 | * 242 | * @param {number} x 243 | * @param {number} y 244 | * @param {number} z 245 | * @returns {number} 246 | */ 247 | export function interleave3(x, y, z) { 248 | x &= 0x3FF; 249 | x = (x | (x<<16)) & 4278190335; 250 | x = (x | (x<<8)) & 251719695; 251 | x = (x | (x<<4)) & 3272356035; 252 | x = (x | (x<<2)) & 1227133513; 253 | 254 | y &= 0x3FF; 255 | y = (y | (y<<16)) & 4278190335; 256 | y = (y | (y<<8)) & 251719695; 257 | y = (y | (y<<4)) & 3272356035; 258 | y = (y | (y<<2)) & 1227133513; 259 | x |= (y << 1); 260 | 261 | z &= 0x3FF; 262 | z = (z | (z<<16)) & 4278190335; 263 | z = (z | (z<<8)) & 251719695; 264 | z = (z | (z<<4)) & 3272356035; 265 | z = (z | (z<<2)) & 1227133513; 266 | 267 | return x | (z << 2); 268 | } 269 | 270 | /** 271 | * Extracts nth interleaved component of a 3-tuple 272 | * 273 | * @param {number} v 274 | * @param {number} n 275 | * @returns {number} 276 | */ 277 | export function deinterleave3(v, n) { 278 | v = (v >>> n) & 1227133513; 279 | v = (v | (v>>>2)) & 3272356035; 280 | v = (v | (v>>>4)) & 251719695; 281 | v = (v | (v>>>8)) & 4278190335; 282 | v = (v | (v>>>16)) & 0x3FF; 283 | return (v<<22)>>22; 284 | } 285 | 286 | /** 287 | * Computes next combination in colexicographic order (this is mistakenly called nextPermutation on the bit twiddling hacks page) 288 | * 289 | * @param {number} v 290 | * @returns {number} 291 | */ 292 | export function nextCombination(v) { 293 | let t = v | (v - 1); 294 | return (t + 1) | (((~t & -~t) - 1) >>> (countTrailingZeros(v) + 1)); 295 | } -------------------------------------------------------------------------------- /test/tap.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | 3 | function approx(a, b, maxDiff) { 4 | maxDiff = maxDiff || 0.000001; 5 | return Math.abs(a - b) <= maxDiff; 6 | } 7 | 8 | tap.Test.prototype.addAssert('approx', 3, function (found, wanted, maxDifferent, message, extra ) { 9 | let diff = Math.abs(found - wanted); 10 | 11 | maxDifferent = maxDifferent || 0.0001; 12 | message = message || `should be approximate (${maxDifferent})`; 13 | 14 | if ( diff <= maxDifferent ) { 15 | return this.pass(message, extra); 16 | } 17 | 18 | extra.found = found; 19 | extra.wanted = wanted; 20 | extra.compare = '~='; 21 | 22 | return this.fail(message, extra); 23 | }); 24 | 25 | tap.Test.prototype.addAssert('deepApprox', 3, function (found, wanted, maxDifferent, message, extra ) { 26 | maxDifferent = maxDifferent || 0.0001; 27 | message = message || `should be approximate (${maxDifferent})`; 28 | 29 | for ( let name in found ) { 30 | let diff = Math.abs(found[name] - wanted[name]); 31 | 32 | if (diff > maxDifferent) { 33 | extra.found = found; 34 | extra.wanted = wanted; 35 | extra.compare = '~='; 36 | 37 | return this.fail(message, extra); 38 | } 39 | } 40 | 41 | return this.pass(message, extra); 42 | }); 43 | 44 | tap.Test.prototype.addAssert('approxArray', 3, function (found, wanted, maxDifferent, message, extra ) { 45 | if ( found.length !== wanted.length ) { 46 | return this.fail(message, extra); 47 | } 48 | 49 | maxDifferent = maxDifferent || 0.0001; 50 | message = message || `should be approximate (${maxDifferent})`; 51 | 52 | for ( let i = 0; i < found.length; ++i ) { 53 | let diff = Math.abs(found[i] - wanted[i]); 54 | if (diff <= maxDifferent) { 55 | return this.pass(message, extra); 56 | } 57 | } 58 | 59 | extra.found = found; 60 | extra.wanted = wanted; 61 | extra.compare = '~='; 62 | 63 | return this.fail(message, extra); 64 | }); 65 | 66 | tap.Test.prototype.addAssert('notApprox', 3, function (found, wanted, maxDifferent, message, extra ) { 67 | let diff = Math.abs(found - wanted); 68 | 69 | maxDifferent = maxDifferent || 0.0001; 70 | message = message || `should be not approximate (${maxDifferent})`; 71 | 72 | if ( diff > maxDifferent ) { 73 | return this.pass(message, extra); 74 | } 75 | 76 | extra.found = found; 77 | extra.wanted = wanted; 78 | extra.compare = '!~='; 79 | 80 | return this.fail(message, extra); 81 | }); 82 | 83 | tap.Test.prototype.addAssert('equal_v2', 2, function (found, wanted, message, extra ) { 84 | let result = approx(found.x, wanted[0]) && approx(found.y, wanted[1]); 85 | 86 | if ( result ) { 87 | return this.pass(message, extra); 88 | } 89 | 90 | extra.found = found; 91 | extra.wanted = wanted; 92 | extra.compare = '~='; 93 | 94 | return this.fail(message, extra); 95 | }); 96 | 97 | tap.Test.prototype.addAssert('equal_v3', 2, function (found, wanted, message, extra ) { 98 | let result = approx(found.x, wanted[0]) && approx(found.y, wanted[1]) && approx(found.z, wanted[2]); 99 | 100 | if ( result ) { 101 | return this.pass(message, extra); 102 | } 103 | 104 | extra.found = found; 105 | extra.wanted = wanted; 106 | extra.compare = '~='; 107 | 108 | return this.fail(message, extra); 109 | }); 110 | 111 | tap.Test.prototype.addAssert('equal_v4', 2, function (found, wanted, message, extra ) { 112 | let result = approx(found.x, wanted[0]) && approx(found.y, wanted[1]) && approx(found.z, wanted[2]) && approx(found.w, wanted[3]); 113 | 114 | if ( result ) { 115 | return this.pass(message, extra); 116 | } 117 | 118 | extra.found = found; 119 | extra.wanted = wanted; 120 | extra.compare = '~='; 121 | 122 | return this.fail(message, extra); 123 | }); 124 | 125 | tap.Test.prototype.addAssert('equal_c3', 2, function (found, wanted, message, extra ) { 126 | let result = approx(found.r, wanted[0]) && approx(found.g, wanted[1]) && approx(found.b, wanted[2]); 127 | 128 | if ( result ) { 129 | return this.pass(message, extra); 130 | } 131 | 132 | extra.found = found; 133 | extra.wanted = wanted; 134 | extra.compare = '~='; 135 | 136 | return this.fail(message, extra); 137 | }); 138 | 139 | tap.Test.prototype.addAssert('equal_c4', 2, function (found, wanted, message, extra ) { 140 | let result = approx(found.r, wanted[0]) && approx(found.g, wanted[1]) && approx(found.b, wanted[2]) && approx(found.a, wanted[3]); 141 | 142 | if ( result ) { 143 | return this.pass(message, extra); 144 | } 145 | 146 | extra.found = found; 147 | extra.wanted = wanted; 148 | extra.compare = '~='; 149 | 150 | return this.fail(message, extra); 151 | }); 152 | 153 | tap.Test.prototype.addAssert('equal_m2', 2, function (found, wanted, message, extra ) { 154 | let result = 155 | approx(found.m00, wanted[0]) && 156 | approx(found.m01, wanted[1]) && 157 | approx(found.m02, wanted[2]) && 158 | approx(found.m03, wanted[3]) 159 | ; 160 | 161 | if ( result ) { 162 | return this.pass(message, extra); 163 | } 164 | 165 | extra.found = found; 166 | extra.wanted = wanted; 167 | extra.compare = '~='; 168 | 169 | return this.fail(message, extra); 170 | }); 171 | 172 | tap.Test.prototype.addAssert('equal_m23', 2, function (found, wanted, message, extra ) { 173 | let result = 174 | approx(found.m00, wanted[0]) && 175 | approx(found.m01, wanted[1]) && 176 | approx(found.m02, wanted[2]) && 177 | approx(found.m03, wanted[3]) && 178 | approx(found.m04, wanted[4]) && 179 | approx(found.m05, wanted[5]) 180 | ; 181 | 182 | if ( result ) { 183 | return this.pass(message, extra); 184 | } 185 | 186 | extra.found = found; 187 | extra.wanted = wanted; 188 | extra.compare = '~='; 189 | 190 | return this.fail(message, extra); 191 | }); 192 | 193 | tap.Test.prototype.addAssert('equal_m3', 2, function (found, wanted, message, extra ) { 194 | let result = 195 | approx(found.m00, wanted[0]) && 196 | approx(found.m01, wanted[1]) && 197 | approx(found.m02, wanted[2]) && 198 | approx(found.m03, wanted[3]) && 199 | approx(found.m04, wanted[4]) && 200 | approx(found.m05, wanted[5]) && 201 | approx(found.m06, wanted[6]) && 202 | approx(found.m07, wanted[7]) && 203 | approx(found.m08, wanted[8]) 204 | ; 205 | 206 | if ( result ) { 207 | return this.pass(message, extra); 208 | } 209 | 210 | extra.found = found; 211 | extra.wanted = wanted; 212 | extra.compare = '~='; 213 | 214 | return this.fail(message, extra); 215 | }); 216 | 217 | tap.Test.prototype.addAssert('equal_m4', 2, function (found, wanted, message, extra ) { 218 | let result = 219 | approx(found.m00, wanted[0]) && 220 | approx(found.m01, wanted[1]) && 221 | approx(found.m02, wanted[2]) && 222 | approx(found.m03, wanted[3]) && 223 | approx(found.m04, wanted[4]) && 224 | approx(found.m05, wanted[5]) && 225 | approx(found.m06, wanted[6]) && 226 | approx(found.m07, wanted[7]) && 227 | approx(found.m08, wanted[8]) && 228 | approx(found.m09, wanted[9]) && 229 | approx(found.m10, wanted[10]) && 230 | approx(found.m11, wanted[11]) && 231 | approx(found.m12, wanted[12]) && 232 | approx(found.m13, wanted[13]) && 233 | approx(found.m14, wanted[14]) && 234 | approx(found.m15, wanted[15]) 235 | ; 236 | 237 | if ( result ) { 238 | return this.pass(message, extra); 239 | } 240 | 241 | extra.found = found; 242 | extra.wanted = wanted; 243 | extra.compare = '~='; 244 | 245 | return this.fail(message, extra); 246 | }); 247 | 248 | module.exports = tap; -------------------------------------------------------------------------------- /lib/color4.js: -------------------------------------------------------------------------------- 1 | import { EPSILON } from './utils'; 2 | 3 | let _tmp = new Array(4); 4 | 5 | class _color4 { 6 | constructor(r, g, b, a) { 7 | this.r = r; 8 | this.g = g; 9 | this.b = b; 10 | this.a = a; 11 | } 12 | 13 | toJSON() { 14 | _tmp[0] = this.r; 15 | _tmp[1] = this.g; 16 | _tmp[2] = this.b; 17 | _tmp[3] = this.a; 18 | 19 | return _tmp; 20 | } 21 | } 22 | 23 | /** 24 | * @class Color 25 | * @name color4 26 | */ 27 | let color4 = {}; 28 | 29 | /** 30 | * Creates a new color 31 | * 32 | * @returns {color4} a new color 33 | */ 34 | color4.create = function () { 35 | return new _color4(1, 1, 1, 1); 36 | }; 37 | 38 | /** 39 | * Creates a new color initialized with the given values 40 | * 41 | * @param {Number} r red component 42 | * @param {Number} g green component 43 | * @param {Number} b blue component 44 | * @param {Number} a alpha component 45 | * @returns {color4} a new color 46 | * @function 47 | */ 48 | color4.new = function (r, g, b, a) { 49 | return new _color4(r, g, b, a); 50 | }; 51 | 52 | /** 53 | * Creates a new color initialized with values from an existing quaternion 54 | * 55 | * @param {color4} a color to clone 56 | * @returns {color4} a new color 57 | * @function 58 | */ 59 | color4.clone = function (a) { 60 | return new _color4(a.r, a.g, a.b, a.a); 61 | }; 62 | 63 | /** 64 | * Copy the values from one color to another 65 | * 66 | * @param {color4} out the receiving color 67 | * @param {color4} a the source color 68 | * @returns {color4} out 69 | * @function 70 | */ 71 | color4.copy = function (out, a) { 72 | out.r = a.r; 73 | out.g = a.g; 74 | out.b = a.b; 75 | out.a = a.a; 76 | return out; 77 | }; 78 | 79 | /** 80 | * Set the components of a color to the given values 81 | * 82 | * @param {color4} out the receiving color 83 | * @param {Number} r red component 84 | * @param {Number} g green component 85 | * @param {Number} b blue component 86 | * @param {Number} a alpha component 87 | * @returns {color4} out 88 | * @function 89 | */ 90 | color4.set = function (out, r, g, b, a) { 91 | out.r = r; 92 | out.g = g; 93 | out.b = b; 94 | out.a = a; 95 | return out; 96 | }; 97 | 98 | /** 99 | * Set from hex 100 | * 101 | * @param {color4} out the receiving color 102 | * @param {Number} hex 103 | * @returns {color4} out 104 | * @function 105 | */ 106 | color4.fromHex = function (out, hex) { 107 | let r = ((hex >> 24)) / 255.0; 108 | let g = ((hex >> 16) & 0xff) / 255.0; 109 | let b = ((hex >> 8) & 0xff) / 255.0; 110 | let a = ((hex) & 0xff) / 255.0; 111 | 112 | out.r = r; 113 | out.g = g; 114 | out.b = b; 115 | out.a = a; 116 | return out; 117 | }; 118 | 119 | /** 120 | * Adds two color's 121 | * 122 | * @param {color4} out the receiving color 123 | * @param {color4} a the first operand 124 | * @param {color4} b the second operand 125 | * @returns {color4} out 126 | * @function 127 | */ 128 | color4.add = function (out, a, b) { 129 | out.r = a.r + b.r; 130 | out.g = a.g + b.g; 131 | out.b = a.b + b.b; 132 | out.a = a.a + b.a; 133 | return out; 134 | }; 135 | 136 | /** 137 | * Subtracts color b from color a 138 | * 139 | * @param {color4} out the receiving color 140 | * @param {color4} a the first operand 141 | * @param {color4} b the second operand 142 | * @returns {color4} out 143 | */ 144 | color4.subtract = function (out, a, b) { 145 | out.r = a.r - b.r; 146 | out.g = a.g - b.g; 147 | out.b = a.b - b.b; 148 | out.a = a.a - b.a; 149 | return out; 150 | }; 151 | 152 | /** 153 | * Alias for {@link color4.subtract} 154 | * @function 155 | */ 156 | color4.sub = color4.subtract; 157 | 158 | /** 159 | * Multiplies two color's 160 | * 161 | * @param {color4} out the receiving color 162 | * @param {color4} a the first operand 163 | * @param {color4} b the second operand 164 | * @returns {color4} out 165 | * @function 166 | */ 167 | color4.multiply = function (out, a, b) { 168 | out.r = a.r * b.r; 169 | out.g = a.g * b.g; 170 | out.b = a.b * b.b; 171 | out.a = a.a * b.a; 172 | return out; 173 | }; 174 | 175 | /** 176 | * Alias for {@link color4.multiply} 177 | * @function 178 | */ 179 | color4.mul = color4.multiply; 180 | 181 | /** 182 | * Divides two color's 183 | * 184 | * @param {color4} out the receiving vector 185 | * @param {color4} a the first operand 186 | * @param {color4} b the second operand 187 | * @returns {color4} out 188 | */ 189 | color4.divide = function (out, a, b) { 190 | out.r = a.r / b.r; 191 | out.g = a.g / b.g; 192 | out.b = a.b / b.b; 193 | out.a = a.a / b.a; 194 | return out; 195 | }; 196 | 197 | /** 198 | * Alias for {@link color4.divide} 199 | * @function 200 | */ 201 | color4.div = color4.divide; 202 | 203 | 204 | /** 205 | * Scales a color by a scalar number 206 | * 207 | * @param {color4} out the receiving vector 208 | * @param {color4} a the vector to scale 209 | * @param {Number} b amount to scale the vector by 210 | * @returns {color4} out 211 | * @function 212 | */ 213 | color4.scale = function (out, a, b) { 214 | out.r = a.r * b; 215 | out.g = a.g * b; 216 | out.b = a.b * b; 217 | out.a = a.a * b; 218 | return out; 219 | }; 220 | 221 | /** 222 | * Performs a linear interpolation between two color's 223 | * 224 | * @param {color4} out the receiving color 225 | * @param {color4} a the first operand 226 | * @param {color4} b the second operand 227 | * @param {Number} t interpolation amount between the two inputs 228 | * @returns {color4} out 229 | * @function 230 | */ 231 | color4.lerp = function (out, a, b, t) { 232 | let ar = a.r, 233 | ag = a.g, 234 | ab = a.b, 235 | aa = a.a; 236 | out.r = ar + t * (b.r - ar); 237 | out.g = ag + t * (b.g - ag); 238 | out.b = ab + t * (b.b - ab); 239 | out.a = aa + t * (b.a - aa); 240 | return out; 241 | }; 242 | 243 | /** 244 | * Returns a string representation of a color 245 | * 246 | * @param {color4} a vector to represent as a string 247 | * @returns {String} string representation of the vector 248 | */ 249 | color4.str = function (a) { 250 | return `color4(${a.r}, ${a.g}, ${a.b}, ${a.a})`; 251 | }; 252 | 253 | /** 254 | * Returns typed array 255 | * 256 | * @param {array} out 257 | * @param {color4} a 258 | * @returns {array} 259 | */ 260 | color4.array = function (out, a) { 261 | out[0] = a.r; 262 | out[1] = a.g; 263 | out[2] = a.b; 264 | out[3] = a.a; 265 | 266 | return out; 267 | }; 268 | 269 | /** 270 | * Returns whether or not the color have exactly the same elements in the same position (when compared with ===) 271 | * 272 | * @param {color4} a The first color4. 273 | * @param {color4} b The second color4. 274 | * @returns {Boolean} True if the colors are equal, false otherwise. 275 | */ 276 | color4.exactEquals = function (a, b) { 277 | return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a; 278 | }; 279 | 280 | /** 281 | * Returns whether or not the colors have approximately the same elements in the same position. 282 | * 283 | * @param {color4} a The first color4. 284 | * @param {color4} b The second color4. 285 | * @returns {Boolean} True if the colors are equal, false otherwise. 286 | */ 287 | color4.equals = function (a, b) { 288 | let a0 = a.r, a1 = a.g, a2 = a.b, a3 = a.a; 289 | let b0 = b.r, b1 = b.g, b2 = b.b, b3 = b.a; 290 | return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 291 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && 292 | Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && 293 | Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3))); 294 | }; 295 | 296 | /** 297 | * Returns the hex value 298 | * 299 | * @param {color4} a The color 300 | * @returns {Number} 301 | */ 302 | color4.hex = function (a) { 303 | return ((a.r * 255) << 24 | (a.g * 255) << 16 | (a.b * 255) << 8 | a.a * 255) >>> 0; 304 | }; 305 | 306 | export default color4; -------------------------------------------------------------------------------- /test/color3.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const {color3} = require('../dist/vmath'); 3 | 4 | tap.test('color3', t => { 5 | let out, colorA, colorB, result; 6 | 7 | t.beforeEach(done => { 8 | colorA = color3.new(1, 2, 3); 9 | colorB = color3.new(4, 5, 6); 10 | out = color3.new(0, 0, 0); 11 | 12 | done(); 13 | }); 14 | 15 | t.test('create', t => { 16 | result = color3.create(); 17 | 18 | t.equal_c3(result, [1, 1, 1]); 19 | 20 | t.end(); 21 | }); 22 | 23 | t.test('clone', t => { 24 | result = color3.clone(colorA); 25 | 26 | t.deepEqual(result, colorA); 27 | 28 | t.end(); 29 | }); 30 | 31 | t.test('new', t => { 32 | result = color3.new(1, 2, 3); 33 | 34 | t.equal_c3(result, [1, 2, 3]); 35 | 36 | t.end(); 37 | }); 38 | 39 | t.test('copy', t => { 40 | result = color3.copy(out, colorA); 41 | 42 | t.equal_c3(out, [1, 2, 3]); 43 | t.equal(result, out); 44 | 45 | t.end(); 46 | }); 47 | 48 | t.test('set', t => { 49 | result = color3.set(out, 1, 2, 3); 50 | 51 | t.equal_c3(out, [1, 2, 3]); 52 | t.equal(result, out); 53 | 54 | t.end(); 55 | }); 56 | 57 | t.test('fromHex', t => { 58 | result = color3.fromHex(out, 0x7fff7f); 59 | 60 | t.equal_c3(out, [127/255, 1, 127/255]); 61 | t.equal(result, out); 62 | 63 | t.end(); 64 | }); 65 | 66 | t.test('add', t => { 67 | t.test('with a separate output color', t => { 68 | result = color3.add(out, colorA, colorB); 69 | 70 | t.equal_c3(out, [5, 7, 9]); 71 | t.equal(result, out); 72 | t.equal_c3(colorA, [1, 2, 3]); 73 | t.equal_c3(colorB, [4, 5, 6]); 74 | 75 | t.end(); 76 | }); 77 | 78 | t.test('when colorA is the output color', t => { 79 | result = color3.add(colorA, colorA, colorB); 80 | 81 | t.equal_c3(colorA, [5, 7, 9]); 82 | t.equal(result, colorA); 83 | t.equal_c3(colorB, [4, 5, 6]); 84 | 85 | t.end(); 86 | }); 87 | 88 | t.test('when colorB is the output color', t => { 89 | result = color3.add(colorB, colorA, colorB); 90 | 91 | t.equal_c3(colorB, [5, 7, 9]); 92 | t.equal(result, colorB); 93 | t.equal_c3(colorA, [1, 2, 3]); 94 | 95 | t.end(); 96 | }); 97 | 98 | t.end(); 99 | }); 100 | 101 | t.test('subtract', t => { 102 | t.equal(color3.sub, color3.subtract); 103 | 104 | t.test('with a separate output color', t => { 105 | result = color3.subtract(out, colorA, colorB); 106 | 107 | t.equal_c3(out, [-3, -3, -3]); 108 | t.equal(result, out); 109 | t.equal_c3(colorA, [1, 2, 3]); 110 | t.equal_c3(colorB, [4, 5, 6]); 111 | 112 | t.end(); 113 | }); 114 | 115 | t.test('when colorA is the output color', t => { 116 | result = color3.subtract(colorA, colorA, colorB); 117 | 118 | t.equal_c3(colorA, [-3, -3, -3]); 119 | t.equal(result, colorA); 120 | t.equal_c3(colorB, [4, 5, 6]); 121 | 122 | t.end(); 123 | }); 124 | 125 | t.test('when colorB is the output color', t => { 126 | result = color3.subtract(colorB, colorA, colorB); 127 | 128 | t.equal_c3(colorB, [-3, -3, -3]); 129 | t.equal(result, colorB); 130 | t.equal_c3(colorA, [1, 2, 3]); 131 | 132 | t.end(); 133 | }); 134 | 135 | t.end(); 136 | }); 137 | 138 | t.test('multiply', t => { 139 | t.equal(color3.mul, color3.multiply); 140 | 141 | t.test('with a separate output color', t => { 142 | result = color3.multiply(out, colorA, colorB); 143 | 144 | t.equal_c3(out, [4, 10, 18]); 145 | t.equal(result, out); 146 | t.equal_c3(colorA, [1, 2, 3]); 147 | t.equal_c3(colorB, [4, 5, 6]); 148 | 149 | t.end(); 150 | }); 151 | 152 | t.test('when colorA is the output color', t => { 153 | result = color3.multiply(colorA, colorA, colorB); 154 | 155 | t.equal_c3(colorA, [4, 10, 18]); 156 | t.equal(result, colorA); 157 | t.equal_c3(colorB, [4, 5, 6]); 158 | 159 | t.end(); 160 | }); 161 | 162 | t.test('when colorB is the output color', t => { 163 | result = color3.multiply(colorB, colorA, colorB); 164 | 165 | t.equal_c3(colorB, [4, 10, 18]); 166 | t.equal(result, colorB); 167 | t.equal_c3(colorA, [1, 2, 3]); 168 | 169 | t.end(); 170 | }); 171 | 172 | t.end(); 173 | }); 174 | 175 | t.test('divide', t => { 176 | t.equal(color3.div, color3.divide); 177 | 178 | t.test('with a separate output color', t => { 179 | result = color3.divide(out, colorA, colorB); 180 | 181 | t.equal_c3(out, [0.25, 0.4, 0.5]); 182 | t.equal(result, out); 183 | t.equal_c3(colorA, [1, 2, 3]); 184 | t.equal_c3(colorB, [4, 5, 6]); 185 | 186 | t.end(); 187 | }); 188 | 189 | t.test('when colorA is the output color', t => { 190 | result = color3.divide(colorA, colorA, colorB); 191 | 192 | t.equal_c3(colorA, [0.25, 0.4, 0.5]); 193 | t.equal(result, colorA); 194 | t.equal_c3(colorB, [4, 5, 6]); 195 | 196 | t.end(); 197 | }); 198 | 199 | t.test('when colorB is the output color', t => { 200 | result = color3.divide(colorB, colorA, colorB); 201 | 202 | t.equal_c3(colorB, [0.25, 0.4, 0.5]); 203 | t.equal(result, colorB); 204 | t.equal_c3(colorA, [1, 2, 3]); 205 | 206 | t.end(); 207 | }); 208 | 209 | t.end(); 210 | }); 211 | 212 | 213 | t.test('scale', t => { 214 | t.test('with a separate output color', t => { 215 | result = color3.scale(out, colorA, 2); 216 | 217 | t.equal_c3(out, [2, 4, 6]); 218 | t.equal(result, out); 219 | t.equal_c3(colorA, [1, 2, 3]); 220 | 221 | t.end(); 222 | }); 223 | 224 | t.test('when colorA is the output color', t => { 225 | result = color3.scale(colorA, colorA, 2); 226 | 227 | t.equal_c3(colorA, [2, 4, 6]); 228 | t.equal(result, colorA); 229 | 230 | t.end(); 231 | }); 232 | 233 | t.end(); 234 | }); 235 | 236 | t.test('lerp', t => { 237 | t.test('with a separate output color', t => { 238 | result = color3.lerp(out, colorA, colorB, 0.5); 239 | 240 | t.equal_c3(out, [2.5, 3.5, 4.5]); 241 | t.equal(result, out); 242 | t.equal_c3(colorA, [1, 2, 3]); 243 | t.equal_c3(colorB, [4, 5, 6]); 244 | 245 | t.end(); 246 | }); 247 | 248 | t.test('when colorA is the output color', t => { 249 | result = color3.lerp(colorA, colorA, colorB, 0.5); 250 | 251 | t.equal_c3(colorA, [2.5, 3.5, 4.5]); 252 | t.equal(result, colorA); 253 | t.equal_c3(colorB, [4, 5, 6]); 254 | 255 | t.end(); 256 | }); 257 | 258 | t.test('when colorB is the output color', t => { 259 | result = color3.lerp(colorB, colorA, colorB, 0.5); 260 | 261 | t.equal_c3(colorB, [2.5, 3.5, 4.5]); 262 | t.equal(result, colorB); 263 | t.equal_c3(colorA, [1, 2, 3]); 264 | 265 | t.end(); 266 | }); 267 | 268 | t.end(); 269 | }); 270 | 271 | t.test('str', t => { 272 | result = color3.str(colorA); 273 | t.equal(result, 'color3(1, 2, 3)'); 274 | 275 | t.end(); 276 | }); 277 | 278 | t.test('array', t => { 279 | result = color3.array([], colorA); 280 | 281 | t.deepEqual(result, new Float32Array([1, 2, 3])); 282 | 283 | t.end(); 284 | }); 285 | 286 | t.test('exactEquals', t => { 287 | let vecC, r0, r1; 288 | color3.set(colorA, 0, 1, 2); 289 | color3.set(colorB, 0, 1, 2); 290 | vecC = color3.new(1, 2, 3); 291 | r0 = color3.exactEquals(colorA, colorB); 292 | r1 = color3.exactEquals(colorA, vecC); 293 | 294 | t.equal(r0, true); 295 | t.equal(r1, false); 296 | t.equal_c3(colorA, [0, 1, 2]); 297 | t.equal_c3(colorB, [0, 1, 2]); 298 | 299 | t.end(); 300 | }); 301 | 302 | t.test('equals', t => { 303 | let vecC, vecD, r0, r1, r2; 304 | color3.set(colorA, 0, 1, 2); 305 | color3.set(colorB, 0, 1, 2); 306 | vecC = color3.new(1, 2, 3); 307 | vecD = color3.new(1e-16, 1, 2); 308 | r0 = color3.equals(colorA, colorB); 309 | r1 = color3.equals(colorA, vecC); 310 | r2 = color3.equals(colorA, vecD); 311 | 312 | t.equal(r0, true); 313 | t.equal(r1, false); 314 | t.equal(r2, true); 315 | t.equal_c3(colorA, [0, 1, 2]); 316 | t.equal_c3(colorB, [0, 1, 2]); 317 | 318 | t.end(); 319 | }); 320 | 321 | t.test('hex', t => { 322 | color3.set(colorA, 0.5, 1, 0.5); 323 | 324 | t.equal(color3.hex(colorA), 0x7fff7f); 325 | 326 | t.end(); 327 | }); 328 | 329 | t.test('JSON.stringify', t => { 330 | t.equal( 331 | JSON.stringify({ colorA, colorB }), 332 | '{"colorA":[1,2,3],"colorB":[4,5,6]}' 333 | ); 334 | 335 | t.end(); 336 | }); 337 | 338 | t.end(); 339 | }); -------------------------------------------------------------------------------- /test/color4.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { color4 } = require('../dist/vmath'); 3 | 4 | tap.test('color4', t => { 5 | let out, colorA, colorB, result; 6 | 7 | t.beforeEach(done => { 8 | colorA = color4.new(1, 2, 3, 4); 9 | colorB = color4.new(5, 6, 7, 8); 10 | out = color4.new(0, 0, 0, 0); 11 | 12 | done(); 13 | }); 14 | 15 | t.test('create', t => { 16 | result = color4.create(); 17 | 18 | t.equal_c4(result, [1, 1, 1, 1]); 19 | 20 | t.end(); 21 | }); 22 | 23 | t.test('clone', t => { 24 | result = color4.clone(colorA); 25 | 26 | t.deepEqual(result, colorA); 27 | 28 | t.end(); 29 | }); 30 | 31 | t.test('new', t => { 32 | result = color4.new(1, 2, 3, 4); 33 | 34 | t.equal_c4(result, [1, 2, 3, 4]); 35 | 36 | t.end(); 37 | }); 38 | 39 | t.test('copy', t => { 40 | result = color4.copy(out, colorA); 41 | 42 | t.equal_c4(out, [1, 2, 3, 4]); 43 | t.equal(result, out); 44 | 45 | t.end(); 46 | }); 47 | 48 | t.test('set', t => { 49 | result = color4.set(out, 1, 2, 3, 4); 50 | 51 | t.equal_c4(out, [1, 2, 3, 4]); 52 | t.equal(result, out); 53 | 54 | t.end(); 55 | }); 56 | 57 | t.test('fromHex', t => { 58 | result = color4.fromHex(out, 0x7fff7fff); 59 | 60 | t.equal_c4(out, [127/255, 1, 127/255, 1]); 61 | t.equal(result, out); 62 | 63 | t.end(); 64 | }); 65 | 66 | t.test('add', t => { 67 | t.test('with a separate output color', t => { 68 | result = color4.add(out, colorA, colorB); 69 | 70 | t.equal_c4(out, [6, 8, 10, 12]); 71 | t.equal(result, out); 72 | t.equal_c4(colorA, [1, 2, 3, 4]); 73 | t.equal_c4(colorB, [5, 6, 7, 8]); 74 | 75 | t.end(); 76 | }); 77 | 78 | t.test('when colorA is the output color', t => { 79 | result = color4.add(colorA, colorA, colorB); 80 | 81 | t.equal_c4(colorA, [6, 8, 10, 12]); 82 | t.equal(result, colorA); 83 | t.equal_c4(colorB, [5, 6, 7, 8]); 84 | 85 | t.end(); 86 | }); 87 | 88 | t.test('when colorB is the output color', t => { 89 | result = color4.add(colorB, colorA, colorB); 90 | 91 | t.equal_c4(colorB, [6, 8, 10, 12]); 92 | t.equal(result, colorB); 93 | t.equal_c4(colorA, [1, 2, 3, 4]); 94 | 95 | t.end(); 96 | }); 97 | 98 | t.end(); 99 | }); 100 | 101 | t.test('subtract', t => { 102 | t.equal(color4.sub, color4.subtract); 103 | 104 | t.test('with a separate output color', t => { 105 | result = color4.subtract(out, colorA, colorB); 106 | 107 | t.equal_c4(out, [-4, -4, -4, -4]); 108 | t.equal(result, out); 109 | t.equal_c4(colorA, [1, 2, 3, 4]); 110 | t.equal_c4(colorB, [5, 6, 7, 8]); 111 | 112 | t.end(); 113 | }); 114 | 115 | t.test('when colorA is the output color', t => { 116 | result = color4.subtract(colorA, colorA, colorB); 117 | 118 | t.equal_c4(colorA, [-4, -4, -4, -4]); 119 | t.equal(result, colorA); 120 | t.equal_c4(colorB, [5, 6, 7, 8]); 121 | 122 | t.end(); 123 | }); 124 | 125 | t.test('when colorB is the output color', t => { 126 | result = color4.subtract(colorB, colorA, colorB); 127 | 128 | t.equal_c4(colorB, [-4, -4, -4, -4]); 129 | t.equal(result, colorB); 130 | t.equal_c4(colorA, [1, 2, 3, 4]); 131 | 132 | t.end(); 133 | }); 134 | 135 | t.end(); 136 | }); 137 | 138 | t.test('multiply', t => { 139 | t.equal(color4.mul, color4.multiply); 140 | 141 | t.test('with a separate output color', t => { 142 | result = color4.multiply(out, colorA, colorB); 143 | 144 | t.equal_c4(out, [5, 12, 21, 32]); 145 | t.equal(result, out); 146 | t.equal_c4(colorA, [1, 2, 3, 4]); 147 | t.equal_c4(colorB, [5, 6, 7, 8]); 148 | 149 | t.end(); 150 | }); 151 | 152 | t.test('when colorA is the output color', t => { 153 | result = color4.multiply(colorA, colorA, colorB); 154 | 155 | t.equal_c4(colorA, [5, 12, 21, 32]); 156 | t.equal(result, colorA); 157 | t.equal_c4(colorB, [5, 6, 7, 8]); 158 | 159 | t.end(); 160 | }); 161 | 162 | t.test('when colorB is the output color', t => { 163 | result = color4.multiply(colorB, colorA, colorB); 164 | 165 | t.equal_c4(colorB, [5, 12, 21, 32]); 166 | t.equal(result, colorB); 167 | t.equal_c4(colorA, [1, 2, 3, 4]); 168 | 169 | t.end(); 170 | }); 171 | 172 | t.end(); 173 | }); 174 | 175 | t.test('divide', t => { 176 | t.equal(color4.div, color4.divide); 177 | 178 | t.test('with a separate output color', t => { 179 | result = color4.divide(out, colorA, colorB); 180 | 181 | t.equal_c4(out, [0.2, 0.333333, 0.428571, 0.5]); 182 | t.equal(result, out); 183 | t.equal_c4(colorA, [1, 2, 3, 4]); 184 | t.equal_c4(colorB, [5, 6, 7, 8]); 185 | 186 | t.end(); 187 | }); 188 | 189 | t.test('when colorA is the output color', t => { 190 | result = color4.divide(colorA, colorA, colorB); 191 | 192 | t.equal_c4(colorA, [0.2, 0.333333, 0.428571, 0.5]); 193 | t.equal(result, colorA); 194 | t.equal_c4(colorB, [5, 6, 7, 8]); 195 | 196 | t.end(); 197 | }); 198 | 199 | t.test('when colorB is the output color', t => { 200 | result = color4.divide(colorB, colorA, colorB); 201 | 202 | t.equal_c4(colorB, [0.2, 0.333333, 0.428571, 0.5]); 203 | t.equal(result, colorB); 204 | t.equal_c4(colorA, [1, 2, 3, 4]); 205 | 206 | t.end(); 207 | }); 208 | 209 | t.end(); 210 | }); 211 | 212 | t.test('scale', t => { 213 | t.test('with a separate output color', t => { 214 | result = color4.scale(out, colorA, 2); 215 | 216 | t.equal_c4(out, [2, 4, 6, 8]); 217 | t.equal(result, out); 218 | t.equal_c4(colorA, [1, 2, 3, 4]); 219 | 220 | t.end(); 221 | }); 222 | 223 | t.test('when colorA is the output color', t => { 224 | result = color4.scale(colorA, colorA, 2); 225 | 226 | t.equal_c4(colorA, [2, 4, 6, 8]); 227 | t.equal(result, colorA); 228 | 229 | t.end(); 230 | }); 231 | 232 | t.end(); 233 | }); 234 | 235 | t.test('lerp', t => { 236 | t.test('with a separate output color', t => { 237 | result = color4.lerp(out, colorA, colorB, 0.5); 238 | 239 | t.equal_c4(out, [3, 4, 5, 6]); 240 | t.equal(result, out); 241 | t.equal_c4(colorA, [1, 2, 3, 4]); 242 | t.equal_c4(colorB, [5, 6, 7, 8]); 243 | 244 | t.end(); 245 | }); 246 | 247 | t.test('when colorA is the output color', t => { 248 | result = color4.lerp(colorA, colorA, colorB, 0.5); 249 | 250 | t.equal_c4(colorA, [3, 4, 5, 6]); 251 | t.equal(result, colorA); 252 | t.equal_c4(colorB, [5, 6, 7, 8]); 253 | 254 | t.end(); 255 | }); 256 | 257 | t.test('when colorB is the output color', t => { 258 | result = color4.lerp(colorB, colorA, colorB, 0.5); 259 | 260 | t.equal_c4(colorB, [3, 4, 5, 6]); 261 | t.equal(result, colorB); 262 | t.equal_c4(colorA, [1, 2, 3, 4]); 263 | 264 | t.end(); 265 | }); 266 | 267 | t.end(); 268 | }); 269 | 270 | t.test('str', t => { 271 | result = color4.str(colorA); 272 | 273 | t.equal(result, 'color4(1, 2, 3, 4)'); 274 | 275 | t.end(); 276 | }); 277 | 278 | t.test('array', t => { 279 | result = color4.array([], colorA); 280 | 281 | t.deepEqual(result, new Float32Array([1, 2, 3, 4])); 282 | 283 | t.end(); 284 | }); 285 | 286 | t.test('exactEquals', t => { 287 | color4.set(colorA, 0, 1, 2, 3); 288 | color4.set(colorB, 0, 1, 2, 3); 289 | let colorC = color4.new(1, 2, 3, 4); 290 | let r0 = color4.exactEquals(colorA, colorB); 291 | let r1 = color4.exactEquals(colorA, colorC); 292 | 293 | t.equal(r0, true); 294 | t.equal(r1, false); 295 | t.equal_c4(colorA, [0, 1, 2, 3]); 296 | t.equal_c4(colorB, [0, 1, 2, 3]); 297 | 298 | t.end(); 299 | }); 300 | 301 | t.test('equals', t => { 302 | color4.set(colorA, 0, 1, 2, 3); 303 | color4.set(colorB, 0, 1, 2, 3); 304 | 305 | let colorC = color4.new(1, 2, 3, 4); 306 | let vecD = color4.new(1e-16, 1, 2, 3); 307 | let r0 = color4.equals(colorA, colorB); 308 | let r1 = color4.equals(colorA, colorC); 309 | let r2 = color4.equals(colorA, vecD); 310 | 311 | t.equal(r0, true); 312 | t.equal(r1, false); 313 | t.equal(r2, true); 314 | t.equal_c4(colorA, [0, 1, 2, 3]); 315 | t.equal_c4(colorB, [0, 1, 2, 3]); 316 | 317 | t.end(); 318 | }); 319 | 320 | t.test('hex', t => { 321 | color4.set(colorA, 0.5, 1, 0.5, 1); 322 | 323 | t.equal(color4.hex(colorA), 0x7fff7fff); 324 | 325 | color4.set(colorA, 1, 1, 1, 0); 326 | 327 | t.equal(color4.hex(colorA), 0xffffff00); 328 | 329 | t.end(); 330 | }); 331 | 332 | t.test('JSON.stringify', t => { 333 | t.equal( 334 | JSON.stringify({ colorA, colorB }), 335 | '{"colorA":[1,2,3,4],"colorB":[5,6,7,8]}' 336 | ); 337 | 338 | t.end(); 339 | }); 340 | 341 | t.end(); 342 | }); 343 | -------------------------------------------------------------------------------- /test/mat2.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { vec2, mat2 } = require('../dist/vmath'); 3 | 4 | tap.test('mat2', t => { 5 | let out = mat2.create(); 6 | let matA = mat2.create(); 7 | let matB = mat2.create(); 8 | let identity = mat2.create(); 9 | let result = mat2.create(); 10 | 11 | t.beforeEach(done => { 12 | out = mat2.new(0, 0, 0, 0); 13 | matA = mat2.new(1, 2, 3, 4); 14 | matB = mat2.new(5, 6, 7, 8); 15 | identity = mat2.new(1, 0, 0, 1); 16 | 17 | done(); 18 | }); 19 | 20 | t.test('create', t => { 21 | result = mat2.create(); 22 | 23 | t.deepEqual(result, identity); 24 | 25 | t.end(); 26 | }); 27 | 28 | t.test('clone', t => { 29 | result = mat2.clone(matA); 30 | 31 | t.deepEqual(result, matA); 32 | 33 | t.end(); 34 | }); 35 | 36 | t.test('copy', t => { 37 | result = mat2.copy(out, matA); 38 | 39 | t.deepEqual(out, matA); 40 | t.equal(result, out); 41 | 42 | t.end(); 43 | }); 44 | 45 | t.test('identity', t => { 46 | result = mat2.identity(out); 47 | 48 | t.deepEqual(result, identity); 49 | t.equal(result, out); 50 | 51 | t.end(); 52 | }); 53 | 54 | t.test('transpose', t => { 55 | t.test('with a separate output matrix', t => { 56 | result = mat2.transpose(out, matA); 57 | 58 | t.equal_m2(out, [1, 3, 2, 4]); 59 | t.equal_m2(matA, [1, 2, 3, 4]); 60 | 61 | t.end(); 62 | }); 63 | 64 | t.test('when matA is the output matrix', t => { 65 | result = mat2.transpose(matA, matA); 66 | 67 | t.equal_m2(matA, [1, 3, 2, 4]); 68 | t.equal(result, matA); 69 | 70 | t.end(); 71 | }); 72 | 73 | t.end(); 74 | }); 75 | 76 | t.test('invert', t => { 77 | t.test('with a separate output matrix', t => { 78 | result = mat2.invert(out, matA); 79 | 80 | t.equal_m2(out, [-2, 1, 1.5, -0.5]); 81 | t.equal(result, out); 82 | t.equal_m2(matA, [1, 2, 3, 4]); 83 | 84 | t.end(); 85 | }); 86 | 87 | t.test('when matA is the output matrix', t => { 88 | result = mat2.invert(matA, matA); 89 | 90 | t.equal_m2(matA, [-2, 1, 1.5, -0.5]); 91 | t.equal(result, matA); 92 | 93 | t.end(); 94 | }); 95 | 96 | t.end(); 97 | }); 98 | 99 | t.test('adjoint', t => { 100 | t.test('with a separate output matrix', t => { 101 | result = mat2.adjoint(out, matA); 102 | 103 | t.equal_m2(out, [4, -2, -3, 1]); 104 | t.equal(result, out); 105 | t.equal_m2(matA, [1, 2, 3, 4]); 106 | 107 | t.end(); 108 | }); 109 | 110 | t.test('when matA is the output matrix', t => { 111 | result = mat2.adjoint(matA, matA); 112 | 113 | t.equal_m2(matA, [4, -2, -3, 1]); 114 | t.equal(result, matA); 115 | 116 | t.end(); 117 | }); 118 | 119 | t.end(); 120 | }); 121 | 122 | t.test('determinant', t => { 123 | result = mat2.determinant(matA); 124 | 125 | t.equal(result, -2); 126 | 127 | t.end(); 128 | }); 129 | 130 | t.test('multiply', t => { 131 | t.equal(mat2.mul, mat2.multiply); 132 | 133 | t.test('with a separate output matrix', t => { 134 | result = mat2.multiply(out, matA, matB); 135 | 136 | t.equal_m2(out, [23, 34, 31, 46]); 137 | t.equal(result, out); 138 | t.equal_m2(matA, [1, 2, 3, 4]); 139 | t.equal_m2(matB, [5, 6, 7, 8]); 140 | 141 | t.end(); 142 | }); 143 | 144 | t.test('when matA is the output matrix', t => { 145 | result = mat2.multiply(matA, matA, matB); 146 | 147 | t.equal_m2(matA, [23, 34, 31, 46]); 148 | t.equal(result, matA); 149 | t.equal_m2(matB, [5, 6, 7, 8]); 150 | 151 | t.end(); 152 | }); 153 | 154 | t.test('when matB is the output matrix', t => { 155 | result = mat2.multiply(matB, matA, matB); 156 | 157 | t.equal_m2(matB, [23, 34, 31, 46]); 158 | t.equal(result, matB); 159 | t.equal_m2(matA, [1, 2, 3, 4]); 160 | 161 | t.end(); 162 | }); 163 | 164 | t.end(); 165 | }); 166 | 167 | t.test('rotate', t => { 168 | t.test('with a separate output matrix', t => { 169 | result = mat2.rotate(out, matA, Math.PI * 0.5); 170 | 171 | t.equal_m2(out, [3, 4, -1, -2]); 172 | t.equal(result, out); 173 | t.equal_m2(matA, [1, 2, 3, 4]); 174 | 175 | t.end(); 176 | }); 177 | 178 | t.test('when matA is the output matrix', t => { 179 | result = mat2.rotate(matA, matA, Math.PI * 0.5); 180 | 181 | t.equal_m2(matA, [3, 4, -1, -2]); 182 | t.equal(result, matA); 183 | 184 | t.end(); 185 | }); 186 | 187 | t.end(); 188 | }); 189 | 190 | t.test('scale', t => { 191 | let vecA = vec2.create(); 192 | t.beforeEach(done => { 193 | vec2.set(vecA, 2, 3); 194 | done(); 195 | }); 196 | 197 | t.test('with a separate output matrix', t => { 198 | result = mat2.scale(out, matA, vecA); 199 | 200 | t.equal_m2(out, [2, 4, 9, 12]); 201 | t.equal(result, out); 202 | t.equal_m2(matA, [1, 2, 3, 4]); 203 | 204 | t.end(); 205 | }); 206 | 207 | t.test('when matA is the output matrix', t => { 208 | result = mat2.scale(matA, matA, vecA); 209 | 210 | t.equal_m2(matA, [2, 4, 9, 12]); 211 | t.equal(result, matA); 212 | 213 | t.end(); 214 | }); 215 | 216 | t.end(); 217 | }); 218 | 219 | t.test('str', t => { 220 | result = mat2.str(matA); 221 | 222 | t.equal(result, 'mat2(1, 2, 3, 4)'); 223 | 224 | t.end(); 225 | }); 226 | 227 | t.test('array', t => { 228 | result = mat2.array([], matA); 229 | 230 | t.deepEqual(result, new Float32Array([1,2,3,4])); 231 | 232 | t.end(); 233 | }); 234 | 235 | t.test('frob', t => { 236 | result = mat2.frob(matA); 237 | 238 | t.equal(result, Math.sqrt(Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(3, 2) + Math.pow(4, 2))); 239 | 240 | t.end(); 241 | }); 242 | 243 | t.test('LDU', t => { 244 | let L = mat2.create(); 245 | let D = mat2.create(); 246 | let U = mat2.create(); 247 | mat2.LDU(L, D, U, mat2.new(4, 3, 6, 3)); 248 | 249 | let L_result = mat2.create(); L_result.m02 = 1.5; 250 | let D_result = mat2.create(); 251 | let U_result = mat2.create(); 252 | U_result.m00 = 4; U_result.m01 = 3; U_result.m03 = -1.5; 253 | 254 | t.deepApprox(L, L_result); 255 | t.deepApprox(D, D_result); 256 | t.deepApprox(U, U_result); 257 | 258 | t.end(); 259 | }); 260 | 261 | t.test('add', t => { 262 | t.test('with a separate output matrix', t => { 263 | result = mat2.add(out, matA, matB); 264 | 265 | t.equal_m2(out, [6, 8, 10, 12]); 266 | t.equal(result, out); 267 | t.equal_m2(matA, [1, 2, 3, 4]); 268 | t.equal_m2(matB, [5, 6, 7, 8]); 269 | 270 | t.end(); 271 | }); 272 | 273 | t.test('when matA is the output matrix', t => { 274 | result = mat2.add(matA, matA, matB); 275 | 276 | t.equal_m2(matA, [6, 8, 10, 12]); 277 | t.equal(result, matA); 278 | t.equal_m2(matB, [5, 6, 7, 8]); 279 | 280 | t.end(); 281 | }); 282 | 283 | t.test('when matB is the output matrix', t => { 284 | result = mat2.add(matB, matA, matB); 285 | 286 | t.equal_m2(matB, [6, 8, 10, 12]); 287 | t.equal(result, matB); 288 | t.equal_m2(matA, [1, 2, 3, 4]); 289 | 290 | t.end(); 291 | }); 292 | 293 | t.end(); 294 | }); 295 | 296 | t.test('subtract', t => { 297 | t.equal(mat2.sub, mat2.subtract); 298 | 299 | t.test('with a separate output matrix', t => { 300 | result = mat2.subtract(out, matA, matB); 301 | 302 | t.equal_m2(out, [-4, -4, -4, -4]); 303 | t.equal(result, out); 304 | t.equal_m2(matA, [1, 2, 3, 4]); 305 | t.equal_m2(matB, [5, 6, 7, 8]); 306 | 307 | t.end(); 308 | }); 309 | 310 | t.test('when matA is the output matrix', t => { 311 | result = mat2.subtract(matA, matA, matB); 312 | 313 | t.equal_m2(matA, [-4, -4, -4, -4]); 314 | t.equal(result, matA); 315 | t.equal_m2(matB, [5, 6, 7, 8]); 316 | 317 | t.end(); 318 | }); 319 | 320 | t.test('when matB is the output matrix', t => { 321 | result = mat2.subtract(matB, matA, matB); 322 | 323 | t.equal_m2(matB, [-4, -4, -4, -4]); 324 | t.equal(result, matB); 325 | t.equal_m2(matA, [1, 2, 3, 4]); 326 | 327 | t.end(); 328 | }); 329 | 330 | t.end(); 331 | }); 332 | 333 | t.test('new', t => { 334 | result = mat2.new(1, 2, 3, 4); 335 | 336 | t.equal_m2(result, [1, 2, 3, 4]); 337 | 338 | t.end(); 339 | }); 340 | 341 | t.test('set', t => { 342 | result = mat2.set(out, 1, 2, 3, 4); 343 | 344 | t.equal(result, out); 345 | t.equal_m2(result, [1, 2, 3, 4]); 346 | 347 | t.end(); 348 | }); 349 | 350 | t.test('multiplyScalar', t => { 351 | t.test('with a separate output matrix', t => { 352 | result = mat2.multiplyScalar(out, matA, 2); 353 | 354 | t.equal_m2(out, [2, 4, 6, 8]); 355 | t.equal(result, out); 356 | t.equal_m2(matA, [1, 2, 3, 4]); 357 | 358 | t.end(); 359 | }); 360 | 361 | t.test('when matA is the output matrix', t => { 362 | result = mat2.multiplyScalar(matA, matA, 2); 363 | 364 | t.equal(result, matA); 365 | t.equal_m2(matA, [2, 4, 6, 8]); 366 | 367 | t.end(); 368 | }); 369 | 370 | t.end(); 371 | }); 372 | 373 | t.test('multiplyScalarAndAdd', t => { 374 | t.test('with a separate output matrix', t => { 375 | result = mat2.multiplyScalarAndAdd(out, matA, matB, 0.5); 376 | 377 | t.equal_m2(out, [3.5, 5, 6.5, 8]); 378 | t.equal(result, out); 379 | t.equal_m2(matA, [1, 2, 3, 4]); 380 | t.equal_m2(matB, [5, 6, 7, 8]); 381 | 382 | t.end(); 383 | }); 384 | 385 | t.test('when matA is the output matrix', t => { 386 | result = mat2.multiplyScalarAndAdd(matA, matA, matB, 0.5); 387 | 388 | t.equal_m2(matA, [3.5, 5, 6.5, 8]); 389 | t.equal(result, matA); 390 | t.equal_m2(matB, [5, 6, 7, 8]); 391 | 392 | t.end(); 393 | }); 394 | 395 | t.test('when matB is the output matrix', t => { 396 | result = mat2.multiplyScalarAndAdd(matB, matA, matB, 0.5); 397 | 398 | t.equal_m2(matB, [3.5, 5, 6.5, 8]); 399 | t.equal(result, matB); 400 | t.equal_m2(matA, [1, 2, 3, 4]); 401 | 402 | t.end(); 403 | }); 404 | 405 | t.end(); 406 | }); 407 | 408 | t.test('exactEquals', t => { 409 | mat2.set(matA, 0, 1, 2, 3); 410 | mat2.set(matB, 0, 1, 2, 3); 411 | let matC = mat2.new(1, 2, 3, 4); 412 | let r0 = mat2.exactEquals(matA, matB); 413 | let r1 = mat2.exactEquals(matA, matC); 414 | 415 | t.equal(r0, true); 416 | t.equal(r1, false); 417 | t.equal_m2(matA, [0, 1, 2, 3]); 418 | t.equal_m2(matB, [0, 1, 2, 3]); 419 | 420 | t.end(); 421 | }); 422 | 423 | t.test('equals', t => { 424 | mat2.set(matA, 0, 1, 2, 3); 425 | mat2.set(matB, 0, 1, 2, 3); 426 | let matC = mat2.new(1, 2, 3, 4); 427 | let matD = mat2.new(1e-16, 1, 2, 3); 428 | let r0 = mat2.equals(matA, matB); 429 | let r1 = mat2.equals(matA, matC); 430 | let r2 = mat2.equals(matA, matD); 431 | 432 | t.equal(r0, true); 433 | t.equal(r1, false); 434 | t.equal(r2, true); 435 | t.equal_m2(matA, [0, 1, 2, 3]); 436 | t.equal_m2(matB, [0, 1, 2, 3]); 437 | 438 | t.end(); 439 | }); 440 | 441 | t.test('JSON.stringify', t => { 442 | t.equal( 443 | JSON.stringify({ matA, matB }), 444 | '{"matA":[1,2,3,4],"matB":[5,6,7,8]}' 445 | ); 446 | 447 | t.end(); 448 | }); 449 | 450 | t.end(); 451 | }); 452 | -------------------------------------------------------------------------------- /test/mat23.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { vec2, mat23 } = require('../dist/vmath'); 3 | 4 | tap.test('mat23', t => { 5 | let out = mat23.create(); 6 | let matA = mat23.create(); 7 | let matB = mat23.create(); 8 | let identity = mat23.create(); 9 | let result = mat23.create(); 10 | let oldA = mat23.create(); 11 | let oldB = mat23.create(); 12 | 13 | t.beforeEach(done => { 14 | matA = mat23.new( 15 | 1, 2, 16 | 3, 4, 17 | 5, 6 18 | ); 19 | 20 | oldA = mat23.new( 21 | 1, 2, 22 | 3, 4, 23 | 5, 6 24 | ); 25 | 26 | matB = mat23.new( 27 | 7, 8, 28 | 9, 10, 29 | 11, 12 30 | ); 31 | 32 | oldB = mat23.new( 33 | 7, 8, 34 | 9, 10, 35 | 11, 12 36 | ); 37 | 38 | out = mat23.new( 39 | 0, 0, 40 | 0, 0, 41 | 0, 0 42 | ); 43 | 44 | identity = mat23.new( 45 | 1, 0, 46 | 0, 1, 47 | 0, 0 48 | ); 49 | 50 | done(); 51 | }); 52 | 53 | t.test('create', t => { 54 | result = mat23.create(); 55 | 56 | t.deepEqual(result, identity); 57 | 58 | t.end(); 59 | }); 60 | 61 | t.test('clone', t => { 62 | result = mat23.clone(matA); 63 | 64 | t.deepEqual(result, matA); 65 | 66 | t.end(); 67 | }); 68 | 69 | t.test('copy', t => { 70 | result = mat23.copy(out, matA); 71 | 72 | t.deepEqual(out, matA); 73 | t.equal(result, out); 74 | 75 | t.end(); 76 | }); 77 | 78 | t.test('identity', t => { 79 | result = mat23.identity(out); 80 | 81 | t.deepEqual(result, identity); 82 | t.equal(result, out); 83 | 84 | t.end(); 85 | }); 86 | 87 | t.test('invert', t => { 88 | t.test('with a separate output matrix', t => { 89 | result = mat23.invert(out, matA); 90 | 91 | t.equal_m23(out, [-2, 1, 1.5, -0.5, 1, -2]); 92 | t.equal(result, out); 93 | t.deepEqual(matA, oldA); 94 | 95 | t.end(); 96 | }); 97 | 98 | t.test('when matA is the output matrix', t => { 99 | result = mat23.invert(matA, matA); 100 | 101 | t.equal_m23(matA, [-2, 1, 1.5, -0.5, 1, -2]); 102 | t.equal(result, matA); 103 | 104 | t.end(); 105 | }); 106 | 107 | t.end(); 108 | }); 109 | 110 | t.test('determinant', t => { 111 | result = mat23.determinant(matA); 112 | 113 | t.equal(result, -2); 114 | 115 | t.end(); 116 | }); 117 | 118 | t.test('multiply', t => { 119 | t.equal(mat23.mul, mat23.multiply); 120 | 121 | t.test('with a separate output matrix', t => { 122 | result = mat23.multiply(out, matA, matB); 123 | 124 | t.equal_m23(out, [31, 46, 39, 58, 52, 76]); 125 | t.equal(result, out); 126 | t.deepEqual(matA, oldA); 127 | t.deepEqual(matB, oldB); 128 | 129 | t.end(); 130 | }); 131 | 132 | t.test('when matA is the output matrix', t => { 133 | result = mat23.multiply(matA, matA, matB); 134 | 135 | t.equal_m23(matA, [31, 46, 39, 58, 52, 76]); 136 | t.equal(result, matA); 137 | t.deepEqual(matB, oldB); 138 | 139 | t.end(); 140 | }); 141 | 142 | t.test('when matB is the output matrix', t => { 143 | result = mat23.multiply(matB, matA, matB); 144 | 145 | t.equal_m23(matB, [31, 46, 39, 58, 52, 76]); 146 | t.equal(result, matB); 147 | t.deepEqual(matA, oldA); 148 | 149 | t.end(); 150 | }); 151 | 152 | t.end(); 153 | }); 154 | 155 | t.test('rotate', t => { 156 | t.test('with a separate output matrix', t => { 157 | result = mat23.rotate(out, matA, Math.PI * 0.5); 158 | 159 | t.equal_m23(out, [3, 4, -1, -2, 5, 6]); 160 | t.equal(result, out); 161 | t.deepEqual(matA, oldA); 162 | 163 | t.end(); 164 | }); 165 | 166 | t.test('when matA is the output matrix', t => { 167 | result = mat23.rotate(matA, matA, Math.PI * 0.5); 168 | 169 | t.equal_m23(matA, [3, 4, -1, -2, 5, 6]); 170 | t.equal(result, matA); 171 | 172 | t.end(); 173 | }); 174 | 175 | t.end(); 176 | }); 177 | 178 | t.test('scale', t => { 179 | let vecA = vec2.create(); 180 | t.beforeEach(done => { 181 | vec2.set(vecA, 2, 3); 182 | done(); 183 | }); 184 | 185 | t.test('with a separate output matrix', t => { 186 | result = mat23.scale(out, matA, vecA); 187 | 188 | t.equal_m23(out, [2, 4, 9, 12, 5, 6]); 189 | t.equal(result, out); 190 | t.deepEqual(matA, oldA); 191 | 192 | t.end(); 193 | }); 194 | 195 | t.test('when matA is the output matrix', t => { 196 | result = mat23.scale(matA, matA, vecA); 197 | 198 | t.equal_m23(matA, [2, 4, 9, 12, 5, 6]); 199 | t.equal(result, matA); 200 | 201 | t.end(); 202 | }); 203 | 204 | t.end(); 205 | }); 206 | 207 | t.test('translate', t => { 208 | let vecA = vec2.create(); 209 | t.beforeEach(done => { 210 | vec2.set(vecA, 2, 3); 211 | done(); 212 | }); 213 | 214 | t.test('with a separate output matrix', t => { 215 | result = mat23.translate(out, matA, vecA); 216 | 217 | t.equal_m23(out, [1, 2, 3, 4, 16, 22]); 218 | t.equal(result, out); 219 | t.deepEqual(matA, oldA); 220 | 221 | t.end(); 222 | }); 223 | 224 | t.test('when matA is the output matrix', t => { 225 | result = mat23.translate(matA, matA, vecA); 226 | 227 | t.equal_m23(matA, [1, 2, 3, 4, 16, 22]); 228 | t.equal(result, matA); 229 | 230 | t.end(); 231 | }); 232 | 233 | t.end(); 234 | }); 235 | 236 | t.test('fromRTS', t => { 237 | let vecA = vec2.create(); 238 | t.beforeEach(done => { 239 | vec2.set(vecA, 2, 3); 240 | done(); 241 | }); 242 | 243 | t.test('with a separate output matrix', t => { 244 | result = mat23.fromRTS(out, Math.PI * 0.5, vecA, vecA); 245 | t.equal_m23(out, [0, 2, -3, 0, 2, 3]); 246 | t.equal(result, out); 247 | t.end(); 248 | }); 249 | 250 | t.end(); 251 | }); 252 | 253 | t.test('str', t => { 254 | result = mat23.str(matA); 255 | 256 | t.equal(result, 'mat23(1, 2, 3, 4, 5, 6)'); 257 | 258 | t.end(); 259 | }); 260 | 261 | t.test('array', t => { 262 | result = mat23.array([], matA); 263 | 264 | t.deepEqual(result, new Float32Array([1, 2, 3, 4, 5, 6])); 265 | 266 | t.end(); 267 | }); 268 | 269 | t.test('frob', t => { 270 | result = mat23.frob(matA); 271 | 272 | t.equal(result, Math.sqrt(Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(3, 2) + Math.pow(4, 2) + Math.pow(5, 2) + Math.pow(6, 2) + 1)); 273 | 274 | t.end(); 275 | }); 276 | 277 | t.test('add', t => { 278 | t.test('with a separate output matrix', t => { 279 | result = mat23.add(out, matA, matB); 280 | 281 | t.equal_m23(out, [8, 10, 12, 14, 16, 18]); 282 | t.equal(result, out); 283 | t.deepEqual(matA, oldA); 284 | t.deepEqual(matB, oldB); 285 | 286 | t.end(); 287 | }); 288 | 289 | t.test('when matA is the output matrix', t => { 290 | result = mat23.add(matA, matA, matB); 291 | 292 | t.equal_m23(matA, [8, 10, 12, 14, 16, 18]); 293 | t.equal(result, matA); 294 | t.deepEqual(matB, oldB); 295 | 296 | t.end(); 297 | }); 298 | 299 | t.test('when matB is the output matrix', t => { 300 | result = mat23.add(matB, matA, matB); 301 | 302 | t.equal_m23(matB, [8, 10, 12, 14, 16, 18]); 303 | t.equal(result, matB); 304 | t.deepEqual(matA, oldA); 305 | 306 | t.end(); 307 | }); 308 | 309 | t.end(); 310 | }); 311 | 312 | t.test('subtract', t => { 313 | t.equal(mat23.sub, mat23.subtract); 314 | 315 | t.test('with a separate output matrix', t => { 316 | result = mat23.subtract(out, matA, matB); 317 | 318 | t.equal_m23(out, [-6, -6, -6, -6, -6, -6]); 319 | t.equal(result, out); 320 | t.deepEqual(matA, oldA); 321 | t.deepEqual(matB, oldB); 322 | 323 | t.end(); 324 | }); 325 | 326 | t.test('when matA is the output matrix', t => { 327 | result = mat23.subtract(matA, matA, matB); 328 | 329 | t.equal_m23(matA, [-6, -6, -6, -6, -6, -6]); 330 | t.equal(result, matA); 331 | t.deepEqual(matB, oldB); 332 | 333 | t.end(); 334 | }); 335 | 336 | t.test('when matB is the output matrix', t => { 337 | result = mat23.subtract(matB, matA, matB); 338 | 339 | t.equal_m23(matB, [-6, -6, -6, -6, -6, -6]); 340 | t.equal(result, matB); 341 | t.deepEqual(matA, oldA); 342 | 343 | t.end(); 344 | }); 345 | 346 | t.end(); 347 | }); 348 | 349 | t.test('new', t => { 350 | result = mat23.new(1, 2, 3, 4, 5, 6); 351 | 352 | t.equal_m23(result, [1, 2, 3, 4, 5, 6]); 353 | 354 | t.end(); 355 | }); 356 | 357 | t.test('set', t => { 358 | result = mat23.set(out, 1, 2, 3, 4, 5, 6); 359 | 360 | t.equal_m23(out, [1, 2, 3, 4, 5, 6]); 361 | t.equal(result, out); 362 | 363 | t.end(); 364 | }); 365 | 366 | t.test('multiplyScalar', t => { 367 | t.test('with a separate output matrix', t => { 368 | result = mat23.multiplyScalar(out, matA, 2); 369 | 370 | t.equal_m23(out, [2, 4, 6, 8, 10, 12]); 371 | t.equal(result, out); 372 | t.equal_m23(matA, [1, 2, 3, 4, 5, 6]); 373 | 374 | t.end(); 375 | }); 376 | 377 | t.test('when matA is the output matrix', t => { 378 | result = mat23.multiplyScalar(matA, matA, 2); 379 | 380 | t.equal_m23(matA, [2, 4, 6, 8, 10, 12]); 381 | t.equal(result, matA); 382 | 383 | t.end(); 384 | }); 385 | 386 | t.end(); 387 | }); 388 | 389 | t.test('multiplyScalarAndAdd', t => { 390 | t.test('with a separate output matrix', t => { 391 | result = mat23.multiplyScalarAndAdd(out, matA, matB, 0.5); 392 | 393 | t.equal_m23(out, [4.5, 6, 7.5, 9, 10.5, 12]); 394 | t.equal(result, out); 395 | t.equal_m23(matA, [1, 2, 3, 4, 5, 6]); 396 | t.equal_m23(matB, [7, 8, 9, 10, 11, 12]); 397 | 398 | t.end(); 399 | }); 400 | 401 | t.test('when matA is the output matrix', t => { 402 | result = mat23.multiplyScalarAndAdd(matA, matA, matB, 0.5); 403 | 404 | t.equal_m23(matA, [4.5, 6, 7.5, 9, 10.5, 12]); 405 | t.equal(result, matA); 406 | t.equal_m23(matB, [7, 8, 9, 10, 11, 12]); 407 | 408 | t.end(); 409 | }); 410 | 411 | t.test('when matB is the output matrix', t => { 412 | result = mat23.multiplyScalarAndAdd(matB, matA, matB, 0.5); 413 | 414 | t.equal_m23(matB, [4.5, 6, 7.5, 9, 10.5, 12]); 415 | t.equal(result, matB); 416 | t.equal_m23(matA, [1, 2, 3, 4, 5, 6]); 417 | 418 | t.end(); 419 | }); 420 | 421 | t.end(); 422 | }); 423 | 424 | t.test('exactEquals', t => { 425 | mat23.set(matA, 0, 1, 2, 3, 4, 5); 426 | mat23.set(matB, 0, 1, 2, 3, 4, 5); 427 | let matC = mat23.new(1, 2, 3, 4, 5, 6); 428 | let r0 = mat23.exactEquals(matA, matB); 429 | let r1 = mat23.exactEquals(matA, matC); 430 | 431 | t.equal(r0, true); 432 | t.equal(r1, false); 433 | t.equal_m23(matA, [0, 1, 2, 3, 4, 5]); 434 | t.equal_m23(matB, [0, 1, 2, 3, 4, 5]); 435 | 436 | t.end(); 437 | }); 438 | 439 | t.test('equals', t => { 440 | mat23.set(matA, 0, 1, 2, 3, 4, 5); 441 | mat23.set(matB, 0, 1, 2, 3, 4, 5); 442 | let matC = mat23.new(1, 2, 3, 4, 5, 6); 443 | let matD = mat23.new(1e-16, 1, 2, 3, 4, 5); 444 | let r0 = mat23.equals(matA, matB); 445 | let r1 = mat23.equals(matA, matC); 446 | let r2 = mat23.equals(matA, matD); 447 | 448 | t.equal(r0, true); 449 | t.equal(r1, false); 450 | t.equal(r2, true); 451 | t.equal_m23(matA, [0, 1, 2, 3, 4, 5]); 452 | t.equal_m23(matB, [0, 1, 2, 3, 4, 5]); 453 | 454 | t.end(); 455 | }); 456 | 457 | t.test('JSON.stringify', t => { 458 | t.equal( 459 | JSON.stringify({ matA, matB }), 460 | '{"matA":[1,2,3,4,5,6],"matB":[7,8,9,10,11,12]}' 461 | ); 462 | 463 | t.end(); 464 | }); 465 | 466 | t.end(); 467 | }); 468 | -------------------------------------------------------------------------------- /lib/mat2.js: -------------------------------------------------------------------------------- 1 | import { EPSILON } from './utils'; 2 | 3 | let _tmp = new Array(4); 4 | 5 | class _mat2 { 6 | constructor(m00, m01, m02, m03) { 7 | this.m00 = m00; 8 | this.m01 = m01; 9 | this.m02 = m02; 10 | this.m03 = m03; 11 | } 12 | 13 | toJSON() { 14 | _tmp[0] = this.m00; 15 | _tmp[1] = this.m01; 16 | _tmp[2] = this.m02; 17 | _tmp[3] = this.m03; 18 | 19 | return _tmp; 20 | } 21 | } 22 | 23 | /** 24 | * @class 2x2 Matrix 25 | * @name mat2 26 | */ 27 | let mat2 = {}; 28 | 29 | /** 30 | * Creates a new identity mat2 31 | * 32 | * @returns {mat2} a new 2x2 matrix 33 | */ 34 | mat2.create = function() { 35 | return new _mat2(1, 0, 0, 1); 36 | }; 37 | 38 | /** 39 | * Create a new mat2 with the given values 40 | * 41 | * @param {Number} m00 Component in column 0, row 0 position (index 0) 42 | * @param {Number} m01 Component in column 0, row 1 position (index 1) 43 | * @param {Number} m10 Component in column 1, row 0 position (index 2) 44 | * @param {Number} m11 Component in column 1, row 1 position (index 3) 45 | * @returns {mat2} out A new 2x2 matrix 46 | */ 47 | mat2.new = function (m00, m01, m10, m11) { 48 | return new _mat2(m00, m01, m10, m11); 49 | }; 50 | 51 | /** 52 | * Creates a new mat2 initialized with values from an existing matrix 53 | * 54 | * @param {mat2} a matrix to clone 55 | * @returns {mat2} a new 2x2 matrix 56 | */ 57 | mat2.clone = function (a) { 58 | return new _mat2(a.m00, a.m01, a.m02, a.m03); 59 | }; 60 | 61 | /** 62 | * Copy the values from one mat2 to another 63 | * 64 | * @param {mat2} out the receiving matrix 65 | * @param {mat2} a the source matrix 66 | * @returns {mat2} out 67 | */ 68 | mat2.copy = function (out, a) { 69 | out.m00 = a.m00; 70 | out.m01 = a.m01; 71 | out.m02 = a.m02; 72 | out.m03 = a.m03; 73 | return out; 74 | }; 75 | 76 | /** 77 | * Set a mat2 to the identity matrix 78 | * 79 | * @param {mat2} out the receiving matrix 80 | * @returns {mat2} out 81 | */ 82 | mat2.identity = function (out) { 83 | out.m00 = 1; 84 | out.m01 = 0; 85 | out.m02 = 0; 86 | out.m03 = 1; 87 | return out; 88 | }; 89 | 90 | /** 91 | * Set the components of a mat2 to the given values 92 | * 93 | * @param {mat2} out the receiving matrix 94 | * @param {Number} m00 Component in column 0, row 0 position (index 0) 95 | * @param {Number} m01 Component in column 0, row 1 position (index 1) 96 | * @param {Number} m10 Component in column 1, row 0 position (index 2) 97 | * @param {Number} m11 Component in column 1, row 1 position (index 3) 98 | * @returns {mat2} out 99 | */ 100 | mat2.set = function (out, m00, m01, m10, m11) { 101 | out.m00 = m00; 102 | out.m01 = m01; 103 | out.m02 = m10; 104 | out.m03 = m11; 105 | return out; 106 | }; 107 | 108 | 109 | /** 110 | * Transpose the values of a mat2 111 | * 112 | * @param {mat2} out the receiving matrix 113 | * @param {mat2} a the source matrix 114 | * @returns {mat2} out 115 | */ 116 | mat2.transpose = function (out, a) { 117 | // If we are transposing ourselves we can skip a few steps but have to cache some values 118 | if (out === a) { 119 | let a1 = a.m01; 120 | out.m01 = a.m02; 121 | out.m02 = a1; 122 | } else { 123 | out.m00 = a.m00; 124 | out.m01 = a.m02; 125 | out.m02 = a.m01; 126 | out.m03 = a.m03; 127 | } 128 | 129 | return out; 130 | }; 131 | 132 | /** 133 | * Inverts a mat2 134 | * 135 | * @param {mat2} out the receiving matrix 136 | * @param {mat2} a the source matrix 137 | * @returns {mat2} out 138 | */ 139 | mat2.invert = function (out, a) { 140 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03; 141 | 142 | // Calculate the determinant 143 | let det = a0 * a3 - a2 * a1; 144 | 145 | if (!det) { 146 | return null; 147 | } 148 | det = 1.0 / det; 149 | 150 | out.m00 = a3 * det; 151 | out.m01 = -a1 * det; 152 | out.m02 = -a2 * det; 153 | out.m03 = a0 * det; 154 | 155 | return out; 156 | }; 157 | 158 | /** 159 | * Calculates the adjugate of a mat2 160 | * 161 | * @param {mat2} out the receiving matrix 162 | * @param {mat2} a the source matrix 163 | * @returns {mat2} out 164 | */ 165 | mat2.adjoint = function (out, a) { 166 | // Caching this value is nessecary if out == a 167 | let a0 = a.m00; 168 | out.m00 = a.m03; 169 | out.m01 = -a.m01; 170 | out.m02 = -a.m02; 171 | out.m03 = a0; 172 | 173 | return out; 174 | }; 175 | 176 | /** 177 | * Calculates the determinant of a mat2 178 | * 179 | * @param {mat2} a the source matrix 180 | * @returns {Number} determinant of a 181 | */ 182 | mat2.determinant = function (a) { 183 | return a.m00 * a.m03 - a.m02 * a.m01; 184 | }; 185 | 186 | /** 187 | * Multiplies two mat2's 188 | * 189 | * @param {mat2} out the receiving matrix 190 | * @param {mat2} a the first operand 191 | * @param {mat2} b the second operand 192 | * @returns {mat2} out 193 | */ 194 | mat2.multiply = function (out, a, b) { 195 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03; 196 | let b0 = b.m00, b1 = b.m01, b2 = b.m02, b3 = b.m03; 197 | out.m00 = a0 * b0 + a2 * b1; 198 | out.m01 = a1 * b0 + a3 * b1; 199 | out.m02 = a0 * b2 + a2 * b3; 200 | out.m03 = a1 * b2 + a3 * b3; 201 | return out; 202 | }; 203 | 204 | /** 205 | * Alias for {@link mat2.multiply} 206 | * @function 207 | */ 208 | mat2.mul = mat2.multiply; 209 | 210 | /** 211 | * Rotates a mat2 by the given angle 212 | * 213 | * @param {mat2} out the receiving matrix 214 | * @param {mat2} a the matrix to rotate 215 | * @param {Number} rad the angle to rotate the matrix by 216 | * @returns {mat2} out 217 | */ 218 | mat2.rotate = function (out, a, rad) { 219 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, 220 | s = Math.sin(rad), 221 | c = Math.cos(rad); 222 | out.m00 = a0 * c + a2 * s; 223 | out.m01 = a1 * c + a3 * s; 224 | out.m02 = a0 * -s + a2 * c; 225 | out.m03 = a1 * -s + a3 * c; 226 | return out; 227 | }; 228 | 229 | /** 230 | * Scales the mat2 by the dimensions in the given vec2 231 | * 232 | * @param {mat2} out the receiving matrix 233 | * @param {mat2} a the matrix to rotate 234 | * @param {vec2} v the vec2 to scale the matrix by 235 | * @returns {mat2} out 236 | **/ 237 | mat2.scale = function (out, a, v) { 238 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, 239 | v0 = v.x, v1 = v.y; 240 | out.m00 = a0 * v0; 241 | out.m01 = a1 * v0; 242 | out.m02 = a2 * v1; 243 | out.m03 = a3 * v1; 244 | return out; 245 | }; 246 | 247 | /** 248 | * Creates a matrix from a given angle 249 | * This is equivalent to (but much faster than): 250 | * 251 | * mat2.identity(dest); 252 | * mat2.rotate(dest, dest, rad); 253 | * 254 | * @param {mat2} out mat2 receiving operation result 255 | * @param {Number} rad the angle to rotate the matrix by 256 | * @returns {mat2} out 257 | */ 258 | mat2.fromRotation = function (out, rad) { 259 | let s = Math.sin(rad), 260 | c = Math.cos(rad); 261 | out.m00 = c; 262 | out.m01 = s; 263 | out.m02 = -s; 264 | out.m03 = c; 265 | return out; 266 | }; 267 | 268 | /** 269 | * Creates a matrix from a vector scaling 270 | * This is equivalent to (but much faster than): 271 | * 272 | * mat2.identity(dest); 273 | * mat2.scale(dest, dest, vec); 274 | * 275 | * @param {mat2} out mat2 receiving operation result 276 | * @param {vec2} v Scaling vector 277 | * @returns {mat2} out 278 | */ 279 | mat2.fromScaling = function (out, v) { 280 | out.m00 = v.x; 281 | out.m01 = 0; 282 | out.m02 = 0; 283 | out.m03 = v.y; 284 | return out; 285 | }; 286 | 287 | /** 288 | * Returns a string representation of a mat2 289 | * 290 | * @param {mat2} a matrix to represent as a string 291 | * @returns {String} string representation of the matrix 292 | */ 293 | mat2.str = function (a) { 294 | return `mat2(${a.m00}, ${a.m01}, ${a.m02}, ${a.m03})`; 295 | }; 296 | 297 | /** 298 | * Returns typed array 299 | * 300 | * @param {array} out 301 | * @param {mat2} m 302 | * @returns {array} 303 | */ 304 | mat2.array = function (out, m) { 305 | out[0] = m.m00; 306 | out[1] = m.m01; 307 | out[2] = m.m02; 308 | out[3] = m.m03; 309 | 310 | return out; 311 | }; 312 | 313 | /** 314 | * Returns Frobenius norm of a mat2 315 | * 316 | * @param {mat2} a the matrix to calculate Frobenius norm of 317 | * @returns {Number} Frobenius norm 318 | */ 319 | mat2.frob = function (a) { 320 | return (Math.sqrt(Math.pow(a.m00, 2) + Math.pow(a.m01, 2) + Math.pow(a.m02, 2) + Math.pow(a.m03, 2))); 321 | }; 322 | 323 | /** 324 | * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix 325 | * @param {mat2} L the lower triangular matrix 326 | * @param {mat2} D the diagonal matrix 327 | * @param {mat2} U the upper triangular matrix 328 | * @param {mat2} a the input matrix to factorize 329 | */ 330 | 331 | mat2.LDU = function (L, D, U, a) { 332 | L.m02 = a.m02 / a.m00; 333 | U.m00 = a.m00; 334 | U.m01 = a.m01; 335 | U.m03 = a.m03 - L.m02 * U.m01; 336 | }; 337 | 338 | /** 339 | * Adds two mat2's 340 | * 341 | * @param {mat2} out the receiving matrix 342 | * @param {mat2} a the first operand 343 | * @param {mat2} b the second operand 344 | * @returns {mat2} out 345 | */ 346 | mat2.add = function (out, a, b) { 347 | out.m00 = a.m00 + b.m00; 348 | out.m01 = a.m01 + b.m01; 349 | out.m02 = a.m02 + b.m02; 350 | out.m03 = a.m03 + b.m03; 351 | return out; 352 | }; 353 | 354 | /** 355 | * Subtracts matrix b from matrix a 356 | * 357 | * @param {mat2} out the receiving matrix 358 | * @param {mat2} a the first operand 359 | * @param {mat2} b the second operand 360 | * @returns {mat2} out 361 | */ 362 | mat2.subtract = function (out, a, b) { 363 | out.m00 = a.m00 - b.m00; 364 | out.m01 = a.m01 - b.m01; 365 | out.m02 = a.m02 - b.m02; 366 | out.m03 = a.m03 - b.m03; 367 | return out; 368 | }; 369 | 370 | /** 371 | * Alias for {@link mat2.subtract} 372 | * @function 373 | */ 374 | mat2.sub = mat2.subtract; 375 | 376 | /** 377 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) 378 | * 379 | * @param {mat2} a The first matrix. 380 | * @param {mat2} b The second matrix. 381 | * @returns {Boolean} True if the matrices are equal, false otherwise. 382 | */ 383 | mat2.exactEquals = function (a, b) { 384 | return a.m00 === b.m00 && a.m01 === b.m01 && a.m02 === b.m02 && a.m03 === b.m03; 385 | }; 386 | 387 | /** 388 | * Returns whether or not the matrices have approximately the same elements in the same position. 389 | * 390 | * @param {mat2} a The first matrix. 391 | * @param {mat2} b The second matrix. 392 | * @returns {Boolean} True if the matrices are equal, false otherwise. 393 | */ 394 | mat2.equals = function (a, b) { 395 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03; 396 | let b0 = b.m00, b1 = b.m01, b2 = b.m02, b3 = b.m03; 397 | return ( 398 | Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 399 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && 400 | Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && 401 | Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) 402 | ); 403 | }; 404 | 405 | /** 406 | * Multiply each element of the matrix by a scalar. 407 | * 408 | * @param {mat2} out the receiving matrix 409 | * @param {mat2} a the matrix to scale 410 | * @param {Number} b amount to scale the matrix's elements by 411 | * @returns {mat2} out 412 | */ 413 | mat2.multiplyScalar = function (out, a, b) { 414 | out.m00 = a.m00 * b; 415 | out.m01 = a.m01 * b; 416 | out.m02 = a.m02 * b; 417 | out.m03 = a.m03 * b; 418 | return out; 419 | }; 420 | 421 | /** 422 | * Adds two mat2's after multiplying each element of the second operand by a scalar value. 423 | * 424 | * @param {mat2} out the receiving vector 425 | * @param {mat2} a the first operand 426 | * @param {mat2} b the second operand 427 | * @param {Number} scale the amount to scale b's elements by before adding 428 | * @returns {mat2} out 429 | */ 430 | mat2.multiplyScalarAndAdd = function (out, a, b, scale) { 431 | out.m00 = a.m00 + (b.m00 * scale); 432 | out.m01 = a.m01 + (b.m01 * scale); 433 | out.m02 = a.m02 + (b.m02 * scale); 434 | out.m03 = a.m03 + (b.m03 * scale); 435 | return out; 436 | }; 437 | 438 | export default mat2; -------------------------------------------------------------------------------- /test/mat3.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { vec2, vec3, quat, mat3, mat4, toRadian } = require('../dist/vmath'); 3 | 4 | tap.test('mat3', t => { 5 | let out = mat3.create(); 6 | let matA = mat3.create(); 7 | let matB = mat3.create(); 8 | let identity = mat3.create(); 9 | let result = mat3.create(); 10 | 11 | t.beforeEach(done => { 12 | matA = mat3.new( 13 | 1, 0, 0, 14 | 0, 1, 0, 15 | 1, 2, 1 16 | ); 17 | 18 | matB = mat3.new( 19 | 1, 0, 0, 20 | 0, 1, 0, 21 | 3, 4, 1 22 | ); 23 | 24 | out = mat3.new( 25 | 0, 0, 0, 26 | 0, 0, 0, 27 | 0, 0, 0 28 | ); 29 | 30 | identity = mat3.new( 31 | 1, 0, 0, 32 | 0, 1, 0, 33 | 0, 0, 1 34 | ); 35 | 36 | done(); 37 | }); 38 | 39 | t.test('normalFromMat4', t => { 40 | matA = mat4.new( 41 | 1, 0, 0, 0, 42 | 0, 1, 0, 0, 43 | 0, 0, 1, 0, 44 | 0, 0, 0, 1 45 | ); 46 | result = mat3.normalFromMat4(out, matA); 47 | 48 | t.equal(result, out); 49 | 50 | mat4.translate(matA, matA, vec3.new(2, 4, 6)); 51 | mat4.rotateX(matA, matA, Math.PI / 2); 52 | result = mat3.normalFromMat4(out, matA); 53 | 54 | t.equal_m3(result, [ 55 | 1, 0, 0, 56 | 0, 0, 1, 57 | 0,-1, 0 58 | ]); 59 | 60 | mat4.scale(matA, matA, vec3.new(2, 3, 4)); 61 | result = mat3.normalFromMat4(out, matA); 62 | 63 | t.equal_m3(result, [ 64 | 0.5, 0, 0, 65 | 0, 0, 0.333333, 66 | 0, -0.25, 0 67 | ]); 68 | 69 | t.end(); 70 | }); 71 | 72 | t.test('fromQuat', t => { 73 | let q = quat.new(0, -0.7071067811865475, 0, 0.7071067811865475); 74 | result = mat3.fromQuat(out, q); 75 | 76 | t.equal(result, out); 77 | t.deepApprox( 78 | vec3.transformMat3(vec3.create(), vec3.new(0, 0, -1), out), 79 | vec3.transformQuat(vec3.create(), vec3.new(0, 0, -1), q) 80 | ); 81 | t.equal_v3( 82 | vec3.transformMat3(vec3.create(), vec3.new(0, 0, -1), out), 83 | [1, 0, 0] 84 | ); 85 | 86 | t.end(); 87 | }); 88 | 89 | t.test('fromViewUp', t => { 90 | let v = vec3.new(0.5, 0, 0.5); 91 | vec3.normalize(v,v); 92 | result = mat3.fromViewUp(out, v); 93 | 94 | let q = quat.create(); 95 | quat.rotateY(q, q, toRadian(45)); 96 | mat3.fromQuat(matA, q); 97 | 98 | t.assert(result !== null); 99 | t.equal(result, out); 100 | t.equal_m3(result, mat3.array([], matA)); 101 | 102 | t.end(); 103 | }); 104 | 105 | t.test('fromMat4', t => { 106 | result = mat3.fromMat4(out, mat4.new( 107 | 1, 2, 3, 4, 108 | 5, 6, 7, 8, 109 | 9, 10, 11, 12, 110 | 13, 14, 15, 16 111 | )); 112 | 113 | t.equal(result, out); 114 | t.equal_m3(out, [ 115 | 1, 2, 3, 116 | 5, 6, 7, 117 | 9, 10, 11 118 | ]); 119 | 120 | t.end(); 121 | }); 122 | 123 | t.test('scale', t => { 124 | result = mat3.scale(out, matA, vec2.new(2, 2)); 125 | 126 | t.equal(result, out); 127 | t.equal_m3(out, [ 128 | 2, 0, 0, 129 | 0, 2, 0, 130 | 1, 2, 1 131 | ]); 132 | 133 | t.end(); 134 | }); 135 | 136 | t.test('create', t => { 137 | result = mat3.create(); 138 | 139 | t.deepEqual(result, identity); 140 | 141 | t.end(); 142 | }); 143 | 144 | t.test('clone', t => { 145 | result = mat3.clone(matA); 146 | 147 | t.deepEqual(result, matA); 148 | 149 | t.end(); 150 | }); 151 | 152 | t.test('copy', t => { 153 | result = mat3.copy(out, matA); 154 | 155 | t.deepEqual(out, matA); 156 | t.equal(result, out); 157 | 158 | t.end(); 159 | }); 160 | 161 | t.test('identity', t => { 162 | result = mat3.identity(out); 163 | 164 | t.deepEqual(result, identity); 165 | t.equal(result, out); 166 | 167 | t.end(); 168 | }); 169 | 170 | t.test('transpose', t => { 171 | t.test('with a separate output matrix', t => { 172 | result = mat3.transpose(out, matA); 173 | 174 | t.equal_m3(out, [ 175 | 1, 0, 1, 176 | 0, 1, 2, 177 | 0, 0, 1 178 | ]); 179 | t.equal(result, out); 180 | t.equal_m3(matA, [ 181 | 1, 0, 0, 182 | 0, 1, 0, 183 | 1, 2, 1 184 | ]); 185 | 186 | t.end(); 187 | }); 188 | 189 | t.test('when matA is the output matrix', t => { 190 | result = mat3.transpose(matA, matA); 191 | 192 | t.equal_m3(matA, [ 193 | 1, 0, 1, 194 | 0, 1, 2, 195 | 0, 0, 1 196 | ]); 197 | t.equal(result, matA); 198 | 199 | t.end(); 200 | }); 201 | 202 | t.end(); 203 | }); 204 | 205 | t.test('invert', t => { 206 | t.test('with a separate output matrix', t => { 207 | result = mat3.invert(out, matA); 208 | 209 | t.equal_m3(out, [ 210 | 1, 0, 0, 211 | 0, 1, 0, 212 | -1, -2, 1 213 | ]); 214 | t.equal(result, out); 215 | t.equal_m3(matA, [ 216 | 1, 0, 0, 217 | 0, 1, 0, 218 | 1, 2, 1 219 | ]); 220 | 221 | t.end(); 222 | }); 223 | 224 | t.test('when matA is the output matrix', t => { 225 | result = mat3.invert(matA, matA); 226 | 227 | t.equal_m3(matA, [ 228 | 1, 0, 0, 229 | 0, 1, 0, 230 | -1, -2, 1 231 | ]); 232 | t.equal(result, matA); 233 | 234 | t.end(); 235 | }); 236 | 237 | t.end(); 238 | }); 239 | 240 | t.test('adjoint', t => { 241 | t.test('with a separate output matrix', t => { 242 | result = mat3.adjoint(out, matA); 243 | 244 | t.equal_m3(out, [ 245 | 1, 0, 0, 246 | 0, 1, 0, 247 | -1, -2, 1 248 | ]); 249 | t.equal(result, out); 250 | t.equal_m3(matA, [ 251 | 1, 0, 0, 252 | 0, 1, 0, 253 | 1, 2, 1 254 | ]); 255 | 256 | t.end(); 257 | }); 258 | 259 | t.test('when matA is the output matrix', t => { 260 | result = mat3.adjoint(matA, matA); 261 | 262 | t.equal_m3(matA, [ 263 | 1, 0, 0, 264 | 0, 1, 0, 265 | -1, -2, 1 266 | ]); 267 | t.equal(result, matA); 268 | 269 | t.end(); 270 | }); 271 | 272 | t.end(); 273 | }); 274 | 275 | t.test('determinant', t => { 276 | result = mat3.determinant(matA); 277 | 278 | t.equal(result, 1); 279 | 280 | t.end(); 281 | }); 282 | 283 | t.test('multiply', t => { 284 | t.equal(mat3.mul, mat3.multiply); 285 | 286 | t.test('with a separate output matrix', t => { 287 | result = mat3.multiply(out, matA, matB); 288 | 289 | t.equal_m3(out, [ 290 | 1, 0, 0, 291 | 0, 1, 0, 292 | 4, 6, 1 293 | ]); 294 | t.equal(result, out); 295 | t.equal_m3(matA, [ 296 | 1, 0, 0, 297 | 0, 1, 0, 298 | 1, 2, 1 299 | ]); 300 | t.equal_m3(matB, [ 301 | 1, 0, 0, 302 | 0, 1, 0, 303 | 3, 4, 1 304 | ]); 305 | 306 | t.end(); 307 | }); 308 | 309 | t.test('when matA is the output matrix', t => { 310 | result = mat3.multiply(matA, matA, matB); 311 | 312 | t.equal_m3(matA, [ 313 | 1, 0, 0, 314 | 0, 1, 0, 315 | 4, 6, 1 316 | ]); 317 | t.equal(result, matA); 318 | t.equal_m3(matB, [ 319 | 1, 0, 0, 320 | 0, 1, 0, 321 | 3, 4, 1 322 | ]); 323 | 324 | t.end(); 325 | }); 326 | 327 | t.test('when matB is the output matrix', t => { 328 | result = mat3.multiply(matB, matA, matB); 329 | 330 | t.equal_m3(matB, [ 331 | 1, 0, 0, 332 | 0, 1, 0, 333 | 4, 6, 1 334 | ]); 335 | t.equal_m3(matA, [ 336 | 1, 0, 0, 337 | 0, 1, 0, 338 | 1, 2, 1 339 | ]); 340 | 341 | t.end(); 342 | }); 343 | 344 | t.end(); 345 | }); 346 | 347 | t.test('str', t => { 348 | result = mat3.str(matA); 349 | 350 | t.equal(result, 'mat3(1, 0, 0, 0, 1, 0, 1, 2, 1)'); 351 | 352 | t.end(); 353 | }); 354 | 355 | t.test('array', t => { 356 | result = mat3.array([], matA); 357 | 358 | t.deepEqual(result, new Float32Array([1, 0, 0, 0, 1, 0, 1, 2, 1])); 359 | 360 | t.end(); 361 | }); 362 | 363 | t.test('frob', t => { 364 | result = mat3.frob(matA); 365 | 366 | t.equal(result, Math.sqrt(Math.pow(1, 2) + Math.pow(0, 2) + Math.pow(0, 2) + Math.pow(0, 2) + Math.pow(1, 2) + Math.pow(0, 2) + Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(1, 2))); 367 | 368 | t.end(); 369 | }); 370 | 371 | t.test('add', t => { 372 | t.beforeEach(done => { 373 | mat3.set(matA, 1, 2, 3, 4, 5, 6, 7, 8, 9); 374 | mat3.set(matB, 10, 11, 12, 13, 14, 15, 16, 17, 18); 375 | 376 | done(); 377 | }); 378 | 379 | t.test('with a separate output matrix', t => { 380 | result = mat3.add(out, matA, matB); 381 | 382 | t.equal_m3(out, [11, 13, 15, 17, 19, 21, 23, 25, 27]); 383 | t.equal(result, out); 384 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 385 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 386 | 387 | t.end(); 388 | }); 389 | 390 | t.test('when matA is the output matrix', t => { 391 | result = mat3.add(matA, matA, matB); 392 | 393 | t.equal_m3(matA, [11, 13, 15, 17, 19, 21, 23, 25, 27]); 394 | t.equal(result, matA); 395 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 396 | 397 | t.end(); 398 | }); 399 | 400 | t.test('when matB is the output matrix', t => { 401 | result = mat3.add(matB, matA, matB); 402 | 403 | t.equal_m3(matB, [11, 13, 15, 17, 19, 21, 23, 25, 27]); 404 | t.equal(result, matB); 405 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 406 | 407 | t.end(); 408 | }); 409 | 410 | t.end(); 411 | }); 412 | 413 | t.test('subtract', t => { 414 | t.beforeEach(done => { 415 | mat3.set(matA, 1, 2, 3, 4, 5, 6, 7, 8, 9); 416 | mat3.set(matB, 10, 11, 12, 13, 14, 15, 16, 17, 18); 417 | 418 | done(); 419 | }); 420 | 421 | t.equal(mat3.sub, mat3.subtract); 422 | 423 | t.test('with a separate output matrix', t => { 424 | result = mat3.subtract(out, matA, matB); 425 | 426 | t.equal_m3(out, [-9, -9, -9, -9, -9, -9, -9, -9, -9]); 427 | t.equal(result, out); 428 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 429 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 430 | 431 | t.end(); 432 | }); 433 | 434 | t.test('when matA is the output matrix', t => { 435 | result = mat3.subtract(matA, matA, matB); 436 | 437 | t.equal_m3(matA, [-9, -9, -9, -9, -9, -9, -9, -9, -9]); 438 | t.equal(result, matA); 439 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 440 | 441 | t.end(); 442 | }); 443 | 444 | t.test('when matB is the output matrix', t => { 445 | result = mat3.subtract(matB, matA, matB); 446 | 447 | t.equal_m3(matB, [-9, -9, -9, -9, -9, -9, -9, -9, -9]); 448 | t.equal(result, matB); 449 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 450 | 451 | t.end(); 452 | }); 453 | 454 | t.end(); 455 | }); 456 | 457 | t.test('new', t => { 458 | result = mat3.new(1, 2, 3, 4, 5, 6, 7, 8, 9); 459 | 460 | t.equal_m3(result, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 461 | 462 | t.end(); 463 | }); 464 | 465 | t.test('set', t => { 466 | result = mat3.set(out, 1, 2, 3, 4, 5, 6, 7, 8, 9); 467 | 468 | t.equal_m3(out, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 469 | t.equal(result, out); 470 | 471 | t.end(); 472 | }); 473 | 474 | t.test('multiplyScalar', t => { 475 | t.beforeEach(done => { 476 | mat3.set(matA, 1, 2, 3, 4, 5, 6, 7, 8, 9); 477 | 478 | done(); 479 | }); 480 | 481 | t.test('with a separate output matrix', t => { 482 | result = mat3.multiplyScalar(out, matA, 2); 483 | 484 | t.equal_m3(out, [2, 4, 6, 8, 10, 12, 14, 16, 18]); 485 | t.equal(result, out); 486 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 487 | 488 | t.end(); 489 | }); 490 | 491 | t.test('when matA is the output matrix', t => { 492 | result = mat3.multiplyScalar(matA, matA, 2); 493 | 494 | t.equal_m3(matA, [2, 4, 6, 8, 10, 12, 14, 16, 18]); 495 | t.equal(result, matA); 496 | 497 | t.end(); 498 | }); 499 | 500 | t.end(); 501 | }); 502 | 503 | t.test('multiplyScalarAndAdd', t => { 504 | t.beforeEach(done => { 505 | mat3.set(matA, 1, 2, 3, 4, 5, 6, 7, 8, 9); 506 | mat3.set(matB, 10, 11, 12, 13, 14, 15, 16, 17, 18); 507 | 508 | done(); 509 | }); 510 | 511 | t.test('with a separate output matrix', t => { 512 | result = mat3.multiplyScalarAndAdd(out, matA, matB, 0.5); 513 | 514 | t.equal_m3(out, [6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); 515 | t.equal(result, out); 516 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 517 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 518 | 519 | t.end(); 520 | }); 521 | 522 | t.test('when matA is the output matrix', t => { 523 | result = mat3.multiplyScalarAndAdd(matA, matA, matB, 0.5); 524 | 525 | t.equal_m3(matA, [6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); 526 | t.equal(result, matA); 527 | t.equal_m3(matB, [10, 11, 12, 13, 14, 15, 16, 17, 18]); 528 | 529 | t.end(); 530 | }); 531 | 532 | t.test('when matB is the output matrix', t => { 533 | result = mat3.multiplyScalarAndAdd(matB, matA, matB, 0.5); 534 | 535 | t.equal_m3(matB, [6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); 536 | t.equal(result, matB); 537 | t.equal_m3(matA, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 538 | 539 | t.end(); 540 | }); 541 | 542 | t.end(); 543 | }); 544 | 545 | t.test('exactEquals', t => { 546 | mat3.set(matA, 0, 1, 2, 3, 4, 5, 6, 7, 8); 547 | mat3.set(matB, 0, 1, 2, 3, 4, 5, 6, 7, 8); 548 | let matC = mat3.new(1, 2, 3, 4, 5, 6, 7, 8, 9); 549 | let r0 = mat3.exactEquals(matA, matB); 550 | let r1 = mat3.exactEquals(matA, matC); 551 | 552 | t.equal(r0, true); 553 | t.equal(r1, false); 554 | t.equal_m3(matA, [0, 1, 2, 3, 4, 5, 6, 7, 8]); 555 | t.equal_m3(matB, [0, 1, 2, 3, 4, 5, 6, 7, 8]); 556 | 557 | t.end(); 558 | }); 559 | 560 | t.test('equals', t => { 561 | mat3.set(matA, 0, 1, 2, 3, 4, 5, 6, 7, 8); 562 | mat3.set(matB, 0, 1, 2, 3, 4, 5, 6, 7, 8); 563 | let matC = mat3.new(1, 2, 3, 4, 5, 6, 7, 8, 9); 564 | let matD = mat3.new(1e-16, 1, 2, 3, 4, 5, 6, 7, 8); 565 | let r0 = mat3.equals(matA, matB); 566 | let r1 = mat3.equals(matA, matC); 567 | let r2 = mat3.equals(matA, matD); 568 | 569 | t.equal(r0, true); 570 | t.equal(r1, false); 571 | t.equal(r2, true); 572 | t.equal_m3(matA, [0, 1, 2, 3, 4, 5, 6, 7, 8]); 573 | t.equal_m3(matB, [0, 1, 2, 3, 4, 5, 6, 7, 8]); 574 | 575 | t.end(); 576 | }); 577 | 578 | t.test('JSON.stringify', t => { 579 | t.equal( 580 | JSON.stringify({ matA, matB }), 581 | '{"matA":[1,0,0,0,1,0,1,2,1],"matB":[1,0,0,0,1,0,3,4,1]}' 582 | ); 583 | 584 | t.end(); 585 | }); 586 | 587 | t.end(); 588 | }); 589 | -------------------------------------------------------------------------------- /lib/mat23.js: -------------------------------------------------------------------------------- 1 | import { EPSILON } from './utils'; 2 | 3 | let _tmp = new Array(6); 4 | 5 | class _mat23 { 6 | constructor(m00, m01, m02, m03, m04, m05) { 7 | this.m00 = m00; 8 | this.m01 = m01; 9 | this.m02 = m02; 10 | this.m03 = m03; 11 | this.m04 = m04; 12 | this.m05 = m05; 13 | } 14 | 15 | toJSON() { 16 | _tmp[0] = this.m00; 17 | _tmp[1] = this.m01; 18 | _tmp[2] = this.m02; 19 | _tmp[3] = this.m03; 20 | _tmp[4] = this.m04; 21 | _tmp[5] = this.m05; 22 | 23 | return _tmp; 24 | } 25 | } 26 | 27 | /** 28 | * @class 2x3 Matrix 29 | * @name mat23 30 | * 31 | * @description 32 | * A mat23 contains six elements defined as: 33 | *
34 | * [a, c, tx, 35 | * b, d, ty] 36 | *37 | * This is a short form for the 3x3 matrix: 38 | *
39 | * [a, c, tx, 40 | * b, d, ty, 41 | * 0, 0, 1] 42 | *43 | * The last row is ignored so the array is shorter and operations are faster. 44 | */ 45 | let mat23 = {}; 46 | 47 | /** 48 | * Creates a new identity mat23 49 | * 50 | * @returns {mat23} a new 2x3 matrix 51 | */ 52 | mat23.create = function () { 53 | return new _mat23( 54 | 1, 0, 55 | 0, 1, 56 | 0, 0 57 | ); 58 | }; 59 | 60 | /** 61 | * Create a new mat23 with the given values 62 | * 63 | * @param {Number} a Component A (index 0) 64 | * @param {Number} b Component B (index 1) 65 | * @param {Number} c Component C (index 2) 66 | * @param {Number} d Component D (index 3) 67 | * @param {Number} tx Component TX (index 4) 68 | * @param {Number} ty Component TY (index 5) 69 | * @returns {mat23} A new mat23 70 | */ 71 | mat23.new = function (a, b, c, d, tx, ty) { 72 | return new _mat23( 73 | a, b, 74 | c, d, 75 | tx, ty 76 | ); 77 | }; 78 | 79 | /** 80 | * Creates a new mat23 initialized with values from an existing matrix 81 | * 82 | * @param {mat23} a matrix to clone 83 | * @returns {mat23} a new 2x3 matrix 84 | */ 85 | mat23.clone = function (a) { 86 | return new _mat23( 87 | a.m00, a.m01, 88 | a.m02, a.m03, 89 | a.m04, a.m05 90 | ); 91 | }; 92 | 93 | /** 94 | * Copy the values from one mat23 to another 95 | * 96 | * @param {mat23} out the receiving matrix 97 | * @param {mat23} a the source matrix 98 | * @returns {mat23} out 99 | */ 100 | mat23.copy = function (out, a) { 101 | out.m00 = a.m00; 102 | out.m01 = a.m01; 103 | out.m02 = a.m02; 104 | out.m03 = a.m03; 105 | out.m04 = a.m04; 106 | out.m05 = a.m05; 107 | return out; 108 | }; 109 | 110 | /** 111 | * Set a mat23 to the identity matrix 112 | * 113 | * @param {mat23} out the receiving matrix 114 | * @returns {mat23} out 115 | */ 116 | mat23.identity = function (out) { 117 | out.m00 = 1; 118 | out.m01 = 0; 119 | out.m02 = 0; 120 | out.m03 = 1; 121 | out.m04 = 0; 122 | out.m05 = 0; 123 | return out; 124 | }; 125 | 126 | /** 127 | * Set the components of a mat23 to the given values 128 | * 129 | * @param {mat23} out the receiving matrix 130 | * @param {Number} a Component A (index 0) 131 | * @param {Number} b Component B (index 1) 132 | * @param {Number} c Component C (index 2) 133 | * @param {Number} d Component D (index 3) 134 | * @param {Number} tx Component TX (index 4) 135 | * @param {Number} ty Component TY (index 5) 136 | * @returns {mat23} out 137 | */ 138 | mat23.set = function (out, a, b, c, d, tx, ty) { 139 | out.m00 = a; 140 | out.m01 = b; 141 | out.m02 = c; 142 | out.m03 = d; 143 | out.m04 = tx; 144 | out.m05 = ty; 145 | return out; 146 | }; 147 | 148 | /** 149 | * Inverts a mat23 150 | * 151 | * @param {mat23} out the receiving matrix 152 | * @param {mat23} a the source matrix 153 | * @returns {mat23} out 154 | */ 155 | mat23.invert = function (out, a) { 156 | let aa = a.m00, ab = a.m01, ac = a.m02, ad = a.m03, 157 | atx = a.m04, aty = a.m05; 158 | 159 | let det = aa * ad - ab * ac; 160 | if (!det) { 161 | return null; 162 | } 163 | det = 1.0 / det; 164 | 165 | out.m00 = ad * det; 166 | out.m01 = -ab * det; 167 | out.m02 = -ac * det; 168 | out.m03 = aa * det; 169 | out.m04 = (ac * aty - ad * atx) * det; 170 | out.m05 = (ab * atx - aa * aty) * det; 171 | return out; 172 | }; 173 | 174 | /** 175 | * Calculates the determinant of a mat23 176 | * 177 | * @param {mat23} a the source matrix 178 | * @returns {Number} determinant of a 179 | */ 180 | mat23.determinant = function (a) { 181 | return a.m00 * a.m03 - a.m01 * a.m02; 182 | }; 183 | 184 | /** 185 | * Multiplies two mat23's 186 | * 187 | * @param {mat23} out the receiving matrix 188 | * @param {mat23} a the first operand 189 | * @param {mat23} b the second operand 190 | * @returns {mat23} out 191 | */ 192 | mat23.multiply = function (out, a, b) { 193 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, a4 = a.m04, a5 = a.m05, 194 | b0 = b.m00, b1 = b.m01, b2 = b.m02, b3 = b.m03, b4 = b.m04, b5 = b.m05; 195 | out.m00 = a0 * b0 + a2 * b1; 196 | out.m01 = a1 * b0 + a3 * b1; 197 | out.m02 = a0 * b2 + a2 * b3; 198 | out.m03 = a1 * b2 + a3 * b3; 199 | out.m04 = a0 * b4 + a2 * b5 + a4; 200 | out.m05 = a1 * b4 + a3 * b5 + a5; 201 | return out; 202 | }; 203 | 204 | /** 205 | * Alias for {@link mat23.multiply} 206 | * @function 207 | */ 208 | mat23.mul = mat23.multiply; 209 | 210 | /** 211 | * Rotates a mat23 by the given angle 212 | * 213 | * @param {mat23} out the receiving matrix 214 | * @param {mat23} a the matrix to rotate 215 | * @param {Number} rad the angle to rotate the matrix by 216 | * @returns {mat23} out 217 | */ 218 | mat23.rotate = function (out, a, rad) { 219 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, a4 = a.m04, a5 = a.m05, 220 | s = Math.sin(rad), 221 | c = Math.cos(rad); 222 | out.m00 = a0 * c + a2 * s; 223 | out.m01 = a1 * c + a3 * s; 224 | out.m02 = a0 * -s + a2 * c; 225 | out.m03 = a1 * -s + a3 * c; 226 | out.m04 = a4; 227 | out.m05 = a5; 228 | return out; 229 | }; 230 | 231 | /** 232 | * Scales the mat23 by the dimensions in the given vec2 233 | * 234 | * @param {mat23} out the receiving matrix 235 | * @param {mat23} a the matrix to translate 236 | * @param {vec2} v the vec2 to scale the matrix by 237 | * @returns {mat23} out 238 | **/ 239 | mat23.scale = function (out, a, v) { 240 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, a4 = a.m04, a5 = a.m05, 241 | v0 = v.x, v1 = v.y; 242 | out.m00 = a0 * v0; 243 | out.m01 = a1 * v0; 244 | out.m02 = a2 * v1; 245 | out.m03 = a3 * v1; 246 | out.m04 = a4; 247 | out.m05 = a5; 248 | return out; 249 | }; 250 | 251 | /** 252 | * Translates the mat23 by the dimensions in the given vec2 253 | * 254 | * @param {mat23} out the receiving matrix 255 | * @param {mat23} a the matrix to translate 256 | * @param {vec2} v the vec2 to translate the matrix by 257 | * @returns {mat23} out 258 | **/ 259 | mat23.translate = function (out, a, v) { 260 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, a4 = a.m04, a5 = a.m05, 261 | v0 = v.x, v1 = v.y; 262 | out.m00 = a0; 263 | out.m01 = a1; 264 | out.m02 = a2; 265 | out.m03 = a3; 266 | out.m04 = a0 * v0 + a2 * v1 + a4; 267 | out.m05 = a1 * v0 + a3 * v1 + a5; 268 | return out; 269 | }; 270 | 271 | /** 272 | * Creates a matrix from a given angle 273 | * This is equivalent to (but much faster than): 274 | * 275 | * mat23.identity(dest); 276 | * mat23.rotate(dest, dest, rad); 277 | * 278 | * @param {mat23} out mat23 receiving operation result 279 | * @param {Number} rad the angle to rotate the matrix by 280 | * @returns {mat23} out 281 | */ 282 | mat23.fromRotation = function (out, rad) { 283 | let s = Math.sin(rad), c = Math.cos(rad); 284 | out.m00 = c; 285 | out.m01 = s; 286 | out.m02 = -s; 287 | out.m03 = c; 288 | out.m04 = 0; 289 | out.m05 = 0; 290 | return out; 291 | }; 292 | 293 | /** 294 | * Creates a matrix from a vector scaling 295 | * This is equivalent to (but much faster than): 296 | * 297 | * mat23.identity(dest); 298 | * mat23.scale(dest, dest, vec); 299 | * 300 | * @param {mat23} out mat23 receiving operation result 301 | * @param {vec2} v Scaling vector 302 | * @returns {mat23} out 303 | */ 304 | mat23.fromScaling = function (out, v) { 305 | out.m00 = v.m00; 306 | out.m01 = 0; 307 | out.m02 = 0; 308 | out.m03 = v.m01; 309 | out.m04 = 0; 310 | out.m05 = 0; 311 | return out; 312 | }; 313 | 314 | /** 315 | * Creates a matrix from a vector translation 316 | * This is equivalent to (but much faster than): 317 | * 318 | * mat23.identity(dest); 319 | * mat23.translate(dest, dest, vec); 320 | * 321 | * @param {mat23} out mat23 receiving operation result 322 | * @param {vec2} v Translation vector 323 | * @returns {mat23} out 324 | */ 325 | mat23.fromTranslation = function (out, v) { 326 | out.m00 = 1; 327 | out.m01 = 0; 328 | out.m02 = 0; 329 | out.m03 = 1; 330 | out.m04 = v.x; 331 | out.m05 = v.y; 332 | return out; 333 | }; 334 | 335 | /** 336 | * Creates a matrix from a rotation, vector translation and vector scale 337 | * This is equivalent to (but faster than): 338 | * 339 | * mat23.identity(dest); 340 | * mat23.translate(dest, vec); 341 | * let tmp = mat23.create(); 342 | * mat23.fromRotation(tmp, rot); 343 | * mat23.multiply(dest, dest, tmp); 344 | * mat23.fromScaling(tmp, scale); 345 | * mat23.multiply(dest, dest, tmp); 346 | * 347 | * @param {mat23} out mat23 receiving operation result 348 | * @param {number} r Rotation radian 349 | * @param {vec2} v Translation vector 350 | * @param {vec2} s Scaling vector 351 | * @returns {mat23} out 352 | */ 353 | mat23.fromRTS = function (out, r, t, s) { 354 | let sr = Math.sin(r), cr = Math.cos(r); 355 | out.m00 = cr * s.x; 356 | out.m01 = sr * s.x; 357 | out.m02 = -sr * s.y; 358 | out.m03 = cr * s.y; 359 | out.m04 = t.x; 360 | out.m05 = t.y; 361 | return out; 362 | }; 363 | 364 | /** 365 | * Returns a string representation of a mat23 366 | * 367 | * @param {mat23} a matrix to represent as a string 368 | * @returns {String} string representation of the matrix 369 | */ 370 | mat23.str = function (a) { 371 | return `mat23(${a.m00}, ${a.m01}, ${a.m02}, ${a.m03}, ${a.m04}, ${a.m05})`; 372 | }; 373 | 374 | /** 375 | * Returns typed array 376 | * 377 | * @param {array} out 378 | * @param {mat23} m 379 | * @returns {array} 380 | */ 381 | mat23.array = function (out, m) { 382 | out[0] = m.m00; 383 | out[1] = m.m01; 384 | out[2] = m.m02; 385 | out[3] = m.m03; 386 | out[4] = m.m04; 387 | out[5] = m.m05; 388 | 389 | return out; 390 | }; 391 | 392 | /** 393 | * Returns typed array to 16 float array 394 | * 395 | * @param {array} out 396 | * @param {mat23} m 397 | * @returns {array} 398 | */ 399 | mat23.array4x4 = function (out, m) { 400 | out[0] = m.m00; 401 | out[1] = m.m01; 402 | out[2] = 0; 403 | out[3] = 0; 404 | out[4] = m.m02; 405 | out[5] = m.m03; 406 | out[6] = 0; 407 | out[7] = 0; 408 | out[8] = 0; 409 | out[9] = 0; 410 | out[10] = 1; 411 | out[11] = 0; 412 | out[12] = m.m04; 413 | out[13] = m.m05; 414 | out[14] = 0; 415 | out[15] = 1; 416 | 417 | return out; 418 | }; 419 | 420 | /** 421 | * Returns Frobenius norm of a mat23 422 | * 423 | * @param {mat23} a the matrix to calculate Frobenius norm of 424 | * @returns {Number} Frobenius norm 425 | */ 426 | mat23.frob = function (a) { 427 | return (Math.sqrt(Math.pow(a.m00, 2) + Math.pow(a.m01, 2) + Math.pow(a.m02, 2) + Math.pow(a.m03, 2) + Math.pow(a.m04, 2) + Math.pow(a.m05, 2) + 1)); 428 | }; 429 | 430 | /** 431 | * Adds two mat23's 432 | * 433 | * @param {mat23} out the receiving matrix 434 | * @param {mat23} a the first operand 435 | * @param {mat23} b the second operand 436 | * @returns {mat23} out 437 | */ 438 | mat23.add = function (out, a, b) { 439 | out.m00 = a.m00 + b.m00; 440 | out.m01 = a.m01 + b.m01; 441 | out.m02 = a.m02 + b.m02; 442 | out.m03 = a.m03 + b.m03; 443 | out.m04 = a.m04 + b.m04; 444 | out.m05 = a.m05 + b.m05; 445 | return out; 446 | }; 447 | 448 | /** 449 | * Subtracts matrix b from matrix a 450 | * 451 | * @param {mat23} out the receiving matrix 452 | * @param {mat23} a the first operand 453 | * @param {mat23} b the second operand 454 | * @returns {mat23} out 455 | */ 456 | mat23.subtract = function (out, a, b) { 457 | out.m00 = a.m00 - b.m00; 458 | out.m01 = a.m01 - b.m01; 459 | out.m02 = a.m02 - b.m02; 460 | out.m03 = a.m03 - b.m03; 461 | out.m04 = a.m04 - b.m04; 462 | out.m05 = a.m05 - b.m05; 463 | return out; 464 | }; 465 | 466 | /** 467 | * Alias for {@link mat23.subtract} 468 | * @function 469 | */ 470 | mat23.sub = mat23.subtract; 471 | 472 | /** 473 | * Multiply each element of the matrix by a scalar. 474 | * 475 | * @param {mat23} out the receiving matrix 476 | * @param {mat23} a the matrix to scale 477 | * @param {Number} b amount to scale the matrix's elements by 478 | * @returns {mat23} out 479 | */ 480 | mat23.multiplyScalar = function (out, a, b) { 481 | out.m00 = a.m00 * b; 482 | out.m01 = a.m01 * b; 483 | out.m02 = a.m02 * b; 484 | out.m03 = a.m03 * b; 485 | out.m04 = a.m04 * b; 486 | out.m05 = a.m05 * b; 487 | return out; 488 | }; 489 | 490 | /** 491 | * Adds two mat23's after multiplying each element of the second operand by a scalar value. 492 | * 493 | * @param {mat23} out the receiving vector 494 | * @param {mat23} a the first operand 495 | * @param {mat23} b the second operand 496 | * @param {Number} scale the amount to scale b's elements by before adding 497 | * @returns {mat23} out 498 | */ 499 | mat23.multiplyScalarAndAdd = function (out, a, b, scale) { 500 | out.m00 = a.m00 + (b.m00 * scale); 501 | out.m01 = a.m01 + (b.m01 * scale); 502 | out.m02 = a.m02 + (b.m02 * scale); 503 | out.m03 = a.m03 + (b.m03 * scale); 504 | out.m04 = a.m04 + (b.m04 * scale); 505 | out.m05 = a.m05 + (b.m05 * scale); 506 | return out; 507 | }; 508 | 509 | /** 510 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) 511 | * 512 | * @param {mat23} a The first matrix. 513 | * @param {mat23} b The second matrix. 514 | * @returns {Boolean} True if the matrices are equal, false otherwise. 515 | */ 516 | mat23.exactEquals = function (a, b) { 517 | return a.m00 === b.m00 && a.m01 === b.m01 && a.m02 === b.m02 && a.m03 === b.m03 && a.m04 === b.m04 && a.m05 === b.m05; 518 | }; 519 | 520 | /** 521 | * Returns whether or not the matrices have approximately the same elements in the same position. 522 | * 523 | * @param {mat23} a The first matrix. 524 | * @param {mat23} b The second matrix. 525 | * @returns {Boolean} True if the matrices are equal, false otherwise. 526 | */ 527 | mat23.equals = function (a, b) { 528 | let a0 = a.m00, a1 = a.m01, a2 = a.m02, a3 = a.m03, a4 = a.m04, a5 = a.m05; 529 | let b0 = b.m00, b1 = b.m01, b2 = b.m02, b3 = b.m03, b4 = b.m04, b5 = b.m05; 530 | return ( 531 | Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 532 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && 533 | Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && 534 | Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && 535 | Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && 536 | Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) 537 | ); 538 | }; 539 | 540 | export default mat23; -------------------------------------------------------------------------------- /lib/vec2.js: -------------------------------------------------------------------------------- 1 | import { EPSILON, random } from './utils'; 2 | 3 | let _tmp = new Array(2); 4 | 5 | class _vec2 { 6 | constructor(x, y) { 7 | this.x = x; 8 | this.y = y; 9 | } 10 | 11 | toJSON() { 12 | _tmp[0] = this.x; 13 | _tmp[1] = this.y; 14 | 15 | return _tmp; 16 | } 17 | } 18 | 19 | /** 20 | * @class 2 Dimensional Vector 21 | * @name vec2 22 | */ 23 | let vec2 = {}; 24 | 25 | /** 26 | * Creates a new, empty vec2 27 | * 28 | * @returns {vec2} a new 2D vector 29 | */ 30 | vec2.create = function () { 31 | return new _vec2(0, 0); 32 | }; 33 | 34 | /** 35 | * Creates a new vec2 initialized with the given values 36 | * 37 | * @param {Number} x X component 38 | * @param {Number} y Y component 39 | * @returns {vec2} a new 2D vector 40 | */ 41 | vec2.new = function (x, y) { 42 | return new _vec2(x, y); 43 | }; 44 | 45 | /** 46 | * Creates a new vec2 initialized with values from an existing vector 47 | * 48 | * @param {vec2} a vector to clone 49 | * @returns {vec2} a new 2D vector 50 | */ 51 | vec2.clone = function (a) { 52 | return new _vec2(a.x, a.y); 53 | }; 54 | 55 | /** 56 | * Copy the values from one vec2 to another 57 | * 58 | * @param {vec2} out the receiving vector 59 | * @param {vec2} a the source vector 60 | * @returns {vec2} out 61 | */ 62 | vec2.copy = function (out, a) { 63 | out.x = a.x; 64 | out.y = a.y; 65 | return out; 66 | }; 67 | 68 | /** 69 | * Set the components of a vec2 to the given values 70 | * 71 | * @param {vec2} out the receiving vector 72 | * @param {Number} x X component 73 | * @param {Number} y Y component 74 | * @returns {vec2} out 75 | */ 76 | vec2.set = function (out, x, y) { 77 | out.x = x; 78 | out.y = y; 79 | return out; 80 | }; 81 | 82 | /** 83 | * Adds two vec2's 84 | * 85 | * @param {vec2} out the receiving vector 86 | * @param {vec2} a the first operand 87 | * @param {vec2} b the second operand 88 | * @returns {vec2} out 89 | */ 90 | vec2.add = function (out, a, b) { 91 | out.x = a.x + b.x; 92 | out.y = a.y + b.y; 93 | return out; 94 | }; 95 | 96 | /** 97 | * Subtracts vector b from vector a 98 | * 99 | * @param {vec2} out the receiving vector 100 | * @param {vec2} a the first operand 101 | * @param {vec2} b the second operand 102 | * @returns {vec2} out 103 | */ 104 | vec2.subtract = function (out, a, b) { 105 | out.x = a.x - b.x; 106 | out.y = a.y - b.y; 107 | return out; 108 | }; 109 | 110 | /** 111 | * Alias for {@link vec2.subtract} 112 | * @function 113 | */ 114 | vec2.sub = vec2.subtract; 115 | 116 | /** 117 | * Multiplies two vec2's 118 | * 119 | * @param {vec2} out the receiving vector 120 | * @param {vec2} a the first operand 121 | * @param {vec2} b the second operand 122 | * @returns {vec2} out 123 | */ 124 | vec2.multiply = function (out, a, b) { 125 | out.x = a.x * b.x; 126 | out.y = a.y * b.y; 127 | return out; 128 | }; 129 | 130 | /** 131 | * Alias for {@link vec2.multiply} 132 | * @function 133 | */ 134 | vec2.mul = vec2.multiply; 135 | 136 | /** 137 | * Divides two vec2's 138 | * 139 | * @param {vec2} out the receiving vector 140 | * @param {vec2} a the first operand 141 | * @param {vec2} b the second operand 142 | * @returns {vec2} out 143 | */ 144 | vec2.divide = function (out, a, b) { 145 | out.x = a.x / b.x; 146 | out.y = a.y / b.y; 147 | return out; 148 | }; 149 | 150 | /** 151 | * Alias for {@link vec2.divide} 152 | * @function 153 | */ 154 | vec2.div = vec2.divide; 155 | 156 | /** 157 | * Math.ceil the components of a vec2 158 | * 159 | * @param {vec2} out the receiving vector 160 | * @param {vec2} a vector to ceil 161 | * @returns {vec2} out 162 | */ 163 | vec2.ceil = function (out, a) { 164 | out.x = Math.ceil(a.x); 165 | out.y = Math.ceil(a.y); 166 | return out; 167 | }; 168 | 169 | /** 170 | * Math.floor the components of a vec2 171 | * 172 | * @param {vec2} out the receiving vector 173 | * @param {vec2} a vector to floor 174 | * @returns {vec2} out 175 | */ 176 | vec2.floor = function (out, a) { 177 | out.x = Math.floor(a.x); 178 | out.y = Math.floor(a.y); 179 | return out; 180 | }; 181 | 182 | /** 183 | * Returns the minimum of two vec2's 184 | * 185 | * @param {vec2} out the receiving vector 186 | * @param {vec2} a the first operand 187 | * @param {vec2} b the second operand 188 | * @returns {vec2} out 189 | */ 190 | vec2.min = function (out, a, b) { 191 | out.x = Math.min(a.x, b.x); 192 | out.y = Math.min(a.y, b.y); 193 | return out; 194 | }; 195 | 196 | /** 197 | * Returns the maximum of two vec2's 198 | * 199 | * @param {vec2} out the receiving vector 200 | * @param {vec2} a the first operand 201 | * @param {vec2} b the second operand 202 | * @returns {vec2} out 203 | */ 204 | vec2.max = function (out, a, b) { 205 | out.x = Math.max(a.x, b.x); 206 | out.y = Math.max(a.y, b.y); 207 | return out; 208 | }; 209 | 210 | /** 211 | * Math.round the components of a vec2 212 | * 213 | * @param {vec2} out the receiving vector 214 | * @param {vec2} a vector to round 215 | * @returns {vec2} out 216 | */ 217 | vec2.round = function (out, a) { 218 | out.x = Math.round(a.x); 219 | out.y = Math.round(a.y); 220 | return out; 221 | }; 222 | 223 | /** 224 | * Scales a vec2 by a scalar number 225 | * 226 | * @param {vec2} out the receiving vector 227 | * @param {vec2} a the vector to scale 228 | * @param {Number} b amount to scale the vector by 229 | * @returns {vec2} out 230 | */ 231 | vec2.scale = function (out, a, b) { 232 | out.x = a.x * b; 233 | out.y = a.y * b; 234 | return out; 235 | }; 236 | 237 | /** 238 | * Adds two vec2's after scaling the second operand by a scalar value 239 | * 240 | * @param {vec2} out the receiving vector 241 | * @param {vec2} a the first operand 242 | * @param {vec2} b the second operand 243 | * @param {Number} scale the amount to scale b by before adding 244 | * @returns {vec2} out 245 | */ 246 | vec2.scaleAndAdd = function (out, a, b, scale) { 247 | out.x = a.x + (b.x * scale); 248 | out.y = a.y + (b.y * scale); 249 | return out; 250 | }; 251 | 252 | /** 253 | * Calculates the euclidian distance between two vec2's 254 | * 255 | * @param {vec2} a the first operand 256 | * @param {vec2} b the second operand 257 | * @returns {Number} distance between a and b 258 | */ 259 | vec2.distance = function (a, b) { 260 | let x = b.x - a.x, 261 | y = b.y - a.y; 262 | return Math.sqrt(x * x + y * y); 263 | }; 264 | 265 | /** 266 | * Alias for {@link vec2.distance} 267 | * @function 268 | */ 269 | vec2.dist = vec2.distance; 270 | 271 | /** 272 | * Calculates the squared euclidian distance between two vec2's 273 | * 274 | * @param {vec2} a the first operand 275 | * @param {vec2} b the second operand 276 | * @returns {Number} squared distance between a and b 277 | */ 278 | vec2.squaredDistance = function (a, b) { 279 | let x = b.x - a.x, 280 | y = b.y - a.y; 281 | return x * x + y * y; 282 | }; 283 | 284 | /** 285 | * Alias for {@link vec2.squaredDistance} 286 | * @function 287 | */ 288 | vec2.sqrDist = vec2.squaredDistance; 289 | 290 | /** 291 | * Calculates the length of a vec2 292 | * 293 | * @param {vec2} a vector to calculate length of 294 | * @returns {Number} length of a 295 | */ 296 | vec2.length = function (a) { 297 | let x = a.x, 298 | y = a.y; 299 | return Math.sqrt(x * x + y * y); 300 | }; 301 | 302 | /** 303 | * Alias for {@link vec2.length} 304 | * @function 305 | */ 306 | vec2.len = vec2.length; 307 | 308 | /** 309 | * Calculates the squared length of a vec2 310 | * 311 | * @param {vec2} a vector to calculate squared length of 312 | * @returns {Number} squared length of a 313 | */ 314 | vec2.squaredLength = function (a) { 315 | let x = a.x, 316 | y = a.y; 317 | return x * x + y * y; 318 | }; 319 | 320 | /** 321 | * Alias for {@link vec2.squaredLength} 322 | * @function 323 | */ 324 | vec2.sqrLen = vec2.squaredLength; 325 | 326 | /** 327 | * Negates the components of a vec2 328 | * 329 | * @param {vec2} out the receiving vector 330 | * @param {vec2} a vector to negate 331 | * @returns {vec2} out 332 | */ 333 | vec2.negate = function (out, a) { 334 | out.x = -a.x; 335 | out.y = -a.y; 336 | return out; 337 | }; 338 | 339 | /** 340 | * Returns the inverse of the components of a vec2 341 | * 342 | * @param {vec2} out the receiving vector 343 | * @param {vec2} a vector to invert 344 | * @returns {vec2} out 345 | */ 346 | vec2.inverse = function (out, a) { 347 | out.x = 1.0 / a.x; 348 | out.y = 1.0 / a.y; 349 | return out; 350 | }; 351 | 352 | /** 353 | * Returns the inverse of the components of a vec2 safely 354 | * 355 | * @param {vec2} out the receiving vector 356 | * @param {vec2} a vector to invert 357 | * @returns {vec2} out 358 | */ 359 | vec2.inverseSafe = function (out, a) { 360 | let x = a.x, 361 | y = a.y; 362 | 363 | if (Math.abs(x) < EPSILON) { 364 | out.x = 0; 365 | } else { 366 | out.x = 1.0 / x; 367 | } 368 | 369 | if (Math.abs(y) < EPSILON) { 370 | out.y = 0; 371 | } else { 372 | out.y = 1.0 / a.y; 373 | } 374 | 375 | return out; 376 | }; 377 | 378 | /** 379 | * Normalize a vec2 380 | * 381 | * @param {vec2} out the receiving vector 382 | * @param {vec2} a vector to normalize 383 | * @returns {vec2} out 384 | */ 385 | vec2.normalize = function (out, a) { 386 | let x = a.x, 387 | y = a.y; 388 | let len = x * x + y * y; 389 | if (len > 0) { 390 | //TODO: evaluate use of glm_invsqrt here? 391 | len = 1 / Math.sqrt(len); 392 | out.x = a.x * len; 393 | out.y = a.y * len; 394 | } 395 | return out; 396 | }; 397 | 398 | /** 399 | * Calculates the dot product of two vec2's 400 | * 401 | * @param {vec2} a the first operand 402 | * @param {vec2} b the second operand 403 | * @returns {Number} dot product of a and b 404 | */ 405 | vec2.dot = function (a, b) { 406 | return a.x * b.x + a.y * b.y; 407 | }; 408 | 409 | /** 410 | * Computes the cross product of two vec2's 411 | * Note that the cross product must by definition produce a 3D vector 412 | * 413 | * @param {vec3} out the receiving vector 414 | * @param {vec2} a the first operand 415 | * @param {vec2} b the second operand 416 | * @returns {vec3} out 417 | */ 418 | vec2.cross = function (out, a, b) { 419 | let z = a.x * b.y - a.y * b.x; 420 | out.x = out.y = 0; 421 | out.z = z; 422 | return out; 423 | }; 424 | 425 | /** 426 | * Performs a linear interpolation between two vec2's 427 | * 428 | * @param {vec2} out the receiving vector 429 | * @param {vec2} a the first operand 430 | * @param {vec2} b the second operand 431 | * @param {Number} t interpolation amount between the two inputs 432 | * @returns {vec2} out 433 | */ 434 | vec2.lerp = function (out, a, b, t) { 435 | let ax = a.x, 436 | ay = a.y; 437 | out.x = ax + t * (b.x - ax); 438 | out.y = ay + t * (b.y - ay); 439 | return out; 440 | }; 441 | 442 | /** 443 | * Generates a random vector with the given scale 444 | * 445 | * @param {vec2} out the receiving vector 446 | * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned 447 | * @returns {vec2} out 448 | */ 449 | vec2.random = function (out, scale) { 450 | scale = scale || 1.0; 451 | let r = random() * 2.0 * Math.PI; 452 | out.x = Math.cos(r) * scale; 453 | out.y = Math.sin(r) * scale; 454 | return out; 455 | }; 456 | 457 | /** 458 | * Transforms the vec2 with a mat2 459 | * 460 | * @param {vec2} out the receiving vector 461 | * @param {vec2} a the vector to transform 462 | * @param {mat2} m matrix to transform with 463 | * @returns {vec2} out 464 | */ 465 | vec2.transformMat2 = function (out, a, m) { 466 | let x = a.x, 467 | y = a.y; 468 | out.x = m.m00 * x + m.m02 * y; 469 | out.y = m.m01 * x + m.m03 * y; 470 | return out; 471 | }; 472 | 473 | /** 474 | * Transforms the vec2 with a mat23 475 | * 476 | * @param {vec2} out the receiving vector 477 | * @param {vec2} a the vector to transform 478 | * @param {mat23} m matrix to transform with 479 | * @returns {vec2} out 480 | */ 481 | vec2.transformMat23 = function (out, a, m) { 482 | let x = a.x, 483 | y = a.y; 484 | out.x = m.m00 * x + m.m02 * y + m.m04; 485 | out.y = m.m01 * x + m.m03 * y + m.m05; 486 | return out; 487 | }; 488 | 489 | /** 490 | * Transforms the vec2 with a mat3 491 | * 3rd vector component is implicitly '1' 492 | * 493 | * @param {vec2} out the receiving vector 494 | * @param {vec2} a the vector to transform 495 | * @param {mat3} m matrix to transform with 496 | * @returns {vec2} out 497 | */ 498 | vec2.transformMat3 = function (out, a, m) { 499 | let x = a.x, 500 | y = a.y; 501 | out.x = m.m00 * x + m.m03 * y + m.m06; 502 | out.y = m.m01 * x + m.m04 * y + m.m07; 503 | return out; 504 | }; 505 | 506 | /** 507 | * Transforms the vec2 with a mat4 508 | * 3rd vector component is implicitly '0' 509 | * 4th vector component is implicitly '1' 510 | * 511 | * @param {vec2} out the receiving vector 512 | * @param {vec2} a the vector to transform 513 | * @param {mat4} m matrix to transform with 514 | * @returns {vec2} out 515 | */ 516 | vec2.transformMat4 = function (out, a, m) { 517 | let x = a.x, 518 | y = a.y; 519 | out.x = m.m00 * x + m.m04 * y + m.m12; 520 | out.y = m.m01 * x + m.m05 * y + m.m13; 521 | return out; 522 | }; 523 | 524 | /** 525 | * Perform some operation over an array of vec2s. 526 | * 527 | * @param {Array} a the array of vectors to iterate over 528 | * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed 529 | * @param {Number} offset Number of elements to skip at the beginning of the array 530 | * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array 531 | * @param {Function} fn Function to call for each vector in the array 532 | * @param {Object} [arg] additional argument to pass to fn 533 | * @returns {Array} a 534 | * @function 535 | */ 536 | vec2.forEach = (function () { 537 | let vec = vec2.create(); 538 | 539 | return function (a, stride, offset, count, fn, arg) { 540 | let i, l; 541 | if (!stride) { 542 | stride = 2; 543 | } 544 | 545 | if (!offset) { 546 | offset = 0; 547 | } 548 | 549 | if (count) { 550 | l = Math.min((count * stride) + offset, a.length); 551 | } else { 552 | l = a.length; 553 | } 554 | 555 | for (i = offset; i < l; i += stride) { 556 | vec.x = a[i]; vec.y = a[i + 1]; 557 | fn(vec, vec, arg); 558 | a[i] = vec.x; a[i + 1] = vec.y; 559 | } 560 | 561 | return a; 562 | }; 563 | })(); 564 | 565 | /** 566 | * Returns a string representation of a vector 567 | * 568 | * @param {vec2} a vector to represent as a string 569 | * @returns {String} string representation of the vector 570 | */ 571 | vec2.str = function (a) { 572 | return `vec2(${a.x}, ${a.y})`; 573 | }; 574 | 575 | /** 576 | * Returns typed array 577 | * 578 | * @param {array} out 579 | * @param {vec2} v 580 | * @returns {array} 581 | */ 582 | vec2.array = function (out, v) { 583 | out[0] = v.x; 584 | out[1] = v.y; 585 | 586 | return out; 587 | }; 588 | 589 | /** 590 | * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===) 591 | * 592 | * @param {vec2} a The first vector. 593 | * @param {vec2} b The second vector. 594 | * @returns {Boolean} True if the vectors are equal, false otherwise. 595 | */ 596 | vec2.exactEquals = function (a, b) { 597 | return a.x === b.x && a.y === b.y; 598 | }; 599 | 600 | /** 601 | * Returns whether or not the vectors have approximately the same elements in the same position. 602 | * 603 | * @param {vec2} a The first vector. 604 | * @param {vec2} b The second vector. 605 | * @returns {Boolean} True if the vectors are equal, false otherwise. 606 | */ 607 | vec2.equals = function (a, b) { 608 | let a0 = a.x, a1 = a.y; 609 | let b0 = b.x, b1 = b.y; 610 | return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 611 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1))); 612 | }; 613 | 614 | export default vec2; -------------------------------------------------------------------------------- /lib/vec4.js: -------------------------------------------------------------------------------- 1 | import { EPSILON, random } from './utils'; 2 | 3 | let _tmp = new Array(4); 4 | 5 | class _vec4 { 6 | constructor(x, y, z, w) { 7 | this.x = x; 8 | this.y = y; 9 | this.z = z; 10 | this.w = w; 11 | } 12 | 13 | toJSON() { 14 | _tmp[0] = this.x; 15 | _tmp[1] = this.y; 16 | _tmp[2] = this.z; 17 | _tmp[3] = this.w; 18 | 19 | return _tmp; 20 | } 21 | } 22 | 23 | /** 24 | * @class 4 Dimensional Vector 25 | * @name vec4 26 | */ 27 | let vec4 = {}; 28 | 29 | /** 30 | * Creates a new, empty vec4 31 | * 32 | * @returns {vec4} a new 4D vector 33 | */ 34 | vec4.create = function () { 35 | return new _vec4(0, 0, 0, 0); 36 | }; 37 | 38 | /** 39 | * Creates a new vec4 initialized with the given values 40 | * 41 | * @param {Number} x X component 42 | * @param {Number} y Y component 43 | * @param {Number} z Z component 44 | * @param {Number} w W component 45 | * @returns {vec4} a new 4D vector 46 | */ 47 | vec4.new = function (x, y, z, w) { 48 | return new _vec4(x, y, z, w); 49 | }; 50 | 51 | /** 52 | * Creates a new vec4 initialized with values from an existing vector 53 | * 54 | * @param {vec4} a vector to clone 55 | * @returns {vec4} a new 4D vector 56 | */ 57 | vec4.clone = function (a) { 58 | return new _vec4(a.x, a.y, a.z, a.w); 59 | }; 60 | 61 | /** 62 | * Copy the values from one vec4 to another 63 | * 64 | * @param {vec4} out the receiving vector 65 | * @param {vec4} a the source vector 66 | * @returns {vec4} out 67 | */ 68 | vec4.copy = function (out, a) { 69 | out.x = a.x; 70 | out.y = a.y; 71 | out.z = a.z; 72 | out.w = a.w; 73 | return out; 74 | }; 75 | 76 | /** 77 | * Set the components of a vec4 to the given values 78 | * 79 | * @param {vec4} out the receiving vector 80 | * @param {Number} x X component 81 | * @param {Number} y Y component 82 | * @param {Number} z Z component 83 | * @param {Number} w W component 84 | * @returns {vec4} out 85 | */ 86 | vec4.set = function (out, x, y, z, w) { 87 | out.x = x; 88 | out.y = y; 89 | out.z = z; 90 | out.w = w; 91 | return out; 92 | }; 93 | 94 | /** 95 | * Adds two vec4's 96 | * 97 | * @param {vec4} out the receiving vector 98 | * @param {vec4} a the first operand 99 | * @param {vec4} b the second operand 100 | * @returns {vec4} out 101 | */ 102 | vec4.add = function (out, a, b) { 103 | out.x = a.x + b.x; 104 | out.y = a.y + b.y; 105 | out.z = a.z + b.z; 106 | out.w = a.w + b.w; 107 | return out; 108 | }; 109 | 110 | /** 111 | * Subtracts vector b from vector a 112 | * 113 | * @param {vec4} out the receiving vector 114 | * @param {vec4} a the first operand 115 | * @param {vec4} b the second operand 116 | * @returns {vec4} out 117 | */ 118 | vec4.subtract = function (out, a, b) { 119 | out.x = a.x - b.x; 120 | out.y = a.y - b.y; 121 | out.z = a.z - b.z; 122 | out.w = a.w - b.w; 123 | return out; 124 | }; 125 | 126 | /** 127 | * Alias for {@link vec4.subtract} 128 | * @function 129 | */ 130 | vec4.sub = vec4.subtract; 131 | 132 | /** 133 | * Multiplies two vec4's 134 | * 135 | * @param {vec4} out the receiving vector 136 | * @param {vec4} a the first operand 137 | * @param {vec4} b the second operand 138 | * @returns {vec4} out 139 | */ 140 | vec4.multiply = function (out, a, b) { 141 | out.x = a.x * b.x; 142 | out.y = a.y * b.y; 143 | out.z = a.z * b.z; 144 | out.w = a.w * b.w; 145 | return out; 146 | }; 147 | 148 | /** 149 | * Alias for {@link vec4.multiply} 150 | * @function 151 | */ 152 | vec4.mul = vec4.multiply; 153 | 154 | /** 155 | * Divides two vec4's 156 | * 157 | * @param {vec4} out the receiving vector 158 | * @param {vec4} a the first operand 159 | * @param {vec4} b the second operand 160 | * @returns {vec4} out 161 | */ 162 | vec4.divide = function (out, a, b) { 163 | out.x = a.x / b.x; 164 | out.y = a.y / b.y; 165 | out.z = a.z / b.z; 166 | out.w = a.w / b.w; 167 | return out; 168 | }; 169 | 170 | /** 171 | * Alias for {@link vec4.divide} 172 | * @function 173 | */ 174 | vec4.div = vec4.divide; 175 | 176 | /** 177 | * Math.ceil the components of a vec4 178 | * 179 | * @param {vec4} out the receiving vector 180 | * @param {vec4} a vector to ceil 181 | * @returns {vec4} out 182 | */ 183 | vec4.ceil = function (out, a) { 184 | out.x = Math.ceil(a.x); 185 | out.y = Math.ceil(a.y); 186 | out.z = Math.ceil(a.z); 187 | out.w = Math.ceil(a.w); 188 | return out; 189 | }; 190 | 191 | /** 192 | * Math.floor the components of a vec4 193 | * 194 | * @param {vec4} out the receiving vector 195 | * @param {vec4} a vector to floor 196 | * @returns {vec4} out 197 | */ 198 | vec4.floor = function (out, a) { 199 | out.x = Math.floor(a.x); 200 | out.y = Math.floor(a.y); 201 | out.z = Math.floor(a.z); 202 | out.w = Math.floor(a.w); 203 | return out; 204 | }; 205 | 206 | /** 207 | * Returns the minimum of two vec4's 208 | * 209 | * @param {vec4} out the receiving vector 210 | * @param {vec4} a the first operand 211 | * @param {vec4} b the second operand 212 | * @returns {vec4} out 213 | */ 214 | vec4.min = function (out, a, b) { 215 | out.x = Math.min(a.x, b.x); 216 | out.y = Math.min(a.y, b.y); 217 | out.z = Math.min(a.z, b.z); 218 | out.w = Math.min(a.w, b.w); 219 | return out; 220 | }; 221 | 222 | /** 223 | * Returns the maximum of two vec4's 224 | * 225 | * @param {vec4} out the receiving vector 226 | * @param {vec4} a the first operand 227 | * @param {vec4} b the second operand 228 | * @returns {vec4} out 229 | */ 230 | vec4.max = function (out, a, b) { 231 | out.x = Math.max(a.x, b.x); 232 | out.y = Math.max(a.y, b.y); 233 | out.z = Math.max(a.z, b.z); 234 | out.w = Math.max(a.w, b.w); 235 | return out; 236 | }; 237 | 238 | /** 239 | * Math.round the components of a vec4 240 | * 241 | * @param {vec4} out the receiving vector 242 | * @param {vec4} a vector to round 243 | * @returns {vec4} out 244 | */ 245 | vec4.round = function (out, a) { 246 | out.x = Math.round(a.x); 247 | out.y = Math.round(a.y); 248 | out.z = Math.round(a.z); 249 | out.w = Math.round(a.w); 250 | return out; 251 | }; 252 | 253 | /** 254 | * Scales a vec4 by a scalar number 255 | * 256 | * @param {vec4} out the receiving vector 257 | * @param {vec4} a the vector to scale 258 | * @param {Number} b amount to scale the vector by 259 | * @returns {vec4} out 260 | */ 261 | vec4.scale = function (out, a, b) { 262 | out.x = a.x * b; 263 | out.y = a.y * b; 264 | out.z = a.z * b; 265 | out.w = a.w * b; 266 | return out; 267 | }; 268 | 269 | /** 270 | * Adds two vec4's after scaling the second operand by a scalar value 271 | * 272 | * @param {vec4} out the receiving vector 273 | * @param {vec4} a the first operand 274 | * @param {vec4} b the second operand 275 | * @param {Number} scale the amount to scale b by before adding 276 | * @returns {vec4} out 277 | */ 278 | vec4.scaleAndAdd = function (out, a, b, scale) { 279 | out.x = a.x + (b.x * scale); 280 | out.y = a.y + (b.y * scale); 281 | out.z = a.z + (b.z * scale); 282 | out.w = a.w + (b.w * scale); 283 | return out; 284 | }; 285 | 286 | /** 287 | * Calculates the euclidian distance between two vec4's 288 | * 289 | * @param {vec4} a the first operand 290 | * @param {vec4} b the second operand 291 | * @returns {Number} distance between a and b 292 | */ 293 | vec4.distance = function (a, b) { 294 | let x = b.x - a.x, 295 | y = b.y - a.y, 296 | z = b.z - a.z, 297 | w = b.w - a.w; 298 | return Math.sqrt(x * x + y * y + z * z + w * w); 299 | }; 300 | 301 | /** 302 | * Alias for {@link vec4.distance} 303 | * @function 304 | */ 305 | vec4.dist = vec4.distance; 306 | 307 | /** 308 | * Calculates the squared euclidian distance between two vec4's 309 | * 310 | * @param {vec4} a the first operand 311 | * @param {vec4} b the second operand 312 | * @returns {Number} squared distance between a and b 313 | */ 314 | vec4.squaredDistance = function (a, b) { 315 | let x = b.x - a.x, 316 | y = b.y - a.y, 317 | z = b.z - a.z, 318 | w = b.w - a.w; 319 | return x * x + y * y + z * z + w * w; 320 | }; 321 | 322 | /** 323 | * Alias for {@link vec4.squaredDistance} 324 | * @function 325 | */ 326 | vec4.sqrDist = vec4.squaredDistance; 327 | 328 | /** 329 | * Calculates the length of a vec4 330 | * 331 | * @param {vec4} a vector to calculate length of 332 | * @returns {Number} length of a 333 | */ 334 | vec4.length = function (a) { 335 | let x = a.x, 336 | y = a.y, 337 | z = a.z, 338 | w = a.w; 339 | return Math.sqrt(x * x + y * y + z * z + w * w); 340 | }; 341 | 342 | /** 343 | * Alias for {@link vec4.length} 344 | * @function 345 | */ 346 | vec4.len = vec4.length; 347 | 348 | /** 349 | * Calculates the squared length of a vec4 350 | * 351 | * @param {vec4} a vector to calculate squared length of 352 | * @returns {Number} squared length of a 353 | */ 354 | vec4.squaredLength = function (a) { 355 | let x = a.x, 356 | y = a.y, 357 | z = a.z, 358 | w = a.w; 359 | return x * x + y * y + z * z + w * w; 360 | }; 361 | 362 | /** 363 | * Alias for {@link vec4.squaredLength} 364 | * @function 365 | */ 366 | vec4.sqrLen = vec4.squaredLength; 367 | 368 | /** 369 | * Negates the components of a vec4 370 | * 371 | * @param {vec4} out the receiving vector 372 | * @param {vec4} a vector to negate 373 | * @returns {vec4} out 374 | */ 375 | vec4.negate = function (out, a) { 376 | out.x = -a.x; 377 | out.y = -a.y; 378 | out.z = -a.z; 379 | out.w = -a.w; 380 | return out; 381 | }; 382 | 383 | /** 384 | * Returns the inverse of the components of a vec4 385 | * 386 | * @param {vec4} out the receiving vector 387 | * @param {vec4} a vector to invert 388 | * @returns {vec4} out 389 | */ 390 | vec4.inverse = function (out, a) { 391 | out.x = 1.0 / a.x; 392 | out.y = 1.0 / a.y; 393 | out.z = 1.0 / a.z; 394 | out.w = 1.0 / a.w; 395 | return out; 396 | }; 397 | 398 | /** 399 | * Returns the inverse of the components of a vec4 safely 400 | * 401 | * @param {vec4} out the receiving vector 402 | * @param {vec4} a vector to invert 403 | * @returns {vec4} out 404 | */ 405 | vec4.inverseSafe = function (out, a) { 406 | let x = a.x, 407 | y = a.y, 408 | z = a.z, 409 | w = a.w; 410 | 411 | if (Math.abs(x) < EPSILON) { 412 | out.x = 0; 413 | } else { 414 | out.x = 1.0 / x; 415 | } 416 | 417 | if (Math.abs(y) < EPSILON) { 418 | out.y = 0; 419 | } else { 420 | out.y = 1.0 / y; 421 | } 422 | 423 | if (Math.abs(z) < EPSILON) { 424 | out.z = 0; 425 | } else { 426 | out.z = 1.0 / z; 427 | } 428 | 429 | if (Math.abs(w) < EPSILON) { 430 | out.w = 0; 431 | } else { 432 | out.w = 1.0 / w; 433 | } 434 | 435 | return out; 436 | }; 437 | 438 | /** 439 | * Normalize a vec4 440 | * 441 | * @param {vec4} out the receiving vector 442 | * @param {vec4} a vector to normalize 443 | * @returns {vec4} out 444 | */ 445 | vec4.normalize = function (out, a) { 446 | let x = a.x, 447 | y = a.y, 448 | z = a.z, 449 | w = a.w; 450 | let len = x * x + y * y + z * z + w * w; 451 | if (len > 0) { 452 | len = 1 / Math.sqrt(len); 453 | out.x = x * len; 454 | out.y = y * len; 455 | out.z = z * len; 456 | out.w = w * len; 457 | } 458 | return out; 459 | }; 460 | 461 | /** 462 | * Calculates the dot product of two vec4's 463 | * 464 | * @param {vec4} a the first operand 465 | * @param {vec4} b the second operand 466 | * @returns {Number} dot product of a and b 467 | */ 468 | vec4.dot = function (a, b) { 469 | return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 470 | }; 471 | 472 | /** 473 | * Performs a linear interpolation between two vec4's 474 | * 475 | * @param {vec4} out the receiving vector 476 | * @param {vec4} a the first operand 477 | * @param {vec4} b the second operand 478 | * @param {Number} t interpolation amount between the two inputs 479 | * @returns {vec4} out 480 | */ 481 | vec4.lerp = function (out, a, b, t) { 482 | let ax = a.x, 483 | ay = a.y, 484 | az = a.z, 485 | aw = a.w; 486 | out.x = ax + t * (b.x - ax); 487 | out.y = ay + t * (b.y - ay); 488 | out.z = az + t * (b.z - az); 489 | out.w = aw + t * (b.w - aw); 490 | return out; 491 | }; 492 | 493 | /** 494 | * Generates a random vector with the given scale 495 | * 496 | * @param {vec4} out the receiving vector 497 | * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned 498 | * @returns {vec4} out 499 | */ 500 | vec4.random = function (out, scale) { 501 | scale = scale || 1.0; 502 | 503 | //TODO: This is a pretty awful way of doing this. Find something better. 504 | out.x = random(); 505 | out.y = random(); 506 | out.z = random(); 507 | out.w = random(); 508 | vec4.normalize(out, out); 509 | vec4.scale(out, out, scale); 510 | return out; 511 | }; 512 | 513 | /** 514 | * Transforms the vec4 with a mat4. 515 | * 516 | * @param {vec4} out the receiving vector 517 | * @param {vec4} a the vector to transform 518 | * @param {mat4} m matrix to transform with 519 | * @returns {vec4} out 520 | */ 521 | vec4.transformMat4 = function (out, a, m) { 522 | let x = a.x, y = a.y, z = a.z, w = a.w; 523 | out.x = m.m00 * x + m.m04 * y + m.m08 * z + m.m12 * w; 524 | out.y = m.m01 * x + m.m05 * y + m.m09 * z + m.m13 * w; 525 | out.z = m.m02 * x + m.m06 * y + m.m10 * z + m.m14 * w; 526 | out.w = m.m03 * x + m.m07 * y + m.m11 * z + m.m15 * w; 527 | return out; 528 | }; 529 | 530 | /** 531 | * Transforms the vec4 with a quat 532 | * 533 | * @param {vec4} out the receiving vector 534 | * @param {vec4} a the vector to transform 535 | * @param {quat} q quaternion to transform with 536 | * @returns {vec4} out 537 | */ 538 | vec4.transformQuat = function (out, a, q) { 539 | let x = a.x, y = a.y, z = a.z; 540 | let qx = q.x, qy = q.y, qz = q.z, qw = q.w; 541 | 542 | // calculate quat * vec 543 | let ix = qw * x + qy * z - qz * y; 544 | let iy = qw * y + qz * x - qx * z; 545 | let iz = qw * z + qx * y - qy * x; 546 | let iw = -qx * x - qy * y - qz * z; 547 | 548 | // calculate result * inverse quat 549 | out.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; 550 | out.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; 551 | out.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; 552 | out.w = a.w; 553 | return out; 554 | }; 555 | 556 | /** 557 | * Perform some operation over an array of vec4s. 558 | * 559 | * @param {Array} a the array of vectors to iterate over 560 | * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed 561 | * @param {Number} offset Number of elements to skip at the beginning of the array 562 | * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array 563 | * @param {Function} fn Function to call for each vector in the array 564 | * @param {Object} [arg] additional argument to pass to fn 565 | * @returns {Array} a 566 | * @function 567 | */ 568 | vec4.forEach = (function () { 569 | let vec = vec4.create(); 570 | 571 | return function (a, stride, offset, count, fn, arg) { 572 | let i, l; 573 | if (!stride) { 574 | stride = 4; 575 | } 576 | 577 | if (!offset) { 578 | offset = 0; 579 | } 580 | 581 | if (count) { 582 | l = Math.min((count * stride) + offset, a.length); 583 | } else { 584 | l = a.length; 585 | } 586 | 587 | for (i = offset; i < l; i += stride) { 588 | vec.x = a[i]; vec.y = a[i + 1]; vec.z = a[i + 2]; vec.w = a[i + 3]; 589 | fn(vec, vec, arg); 590 | a[i] = vec.x; a[i + 1] = vec.y; a[i + 2] = vec.z; a[i + 3] = vec.w; 591 | } 592 | 593 | return a; 594 | }; 595 | })(); 596 | 597 | /** 598 | * Returns a string representation of a vector 599 | * 600 | * @param {vec4} a vector to represent as a string 601 | * @returns {String} string representation of the vector 602 | */ 603 | vec4.str = function (a) { 604 | return `vec4(${a.x}, ${a.y}, ${a.z}, ${a.w})`; 605 | }; 606 | 607 | /** 608 | * Returns typed array 609 | * 610 | * @param {array} out 611 | * @param {vec4} v 612 | * @returns {array} 613 | */ 614 | vec4.array = function (out, v) { 615 | out[0] = v.x; 616 | out[1] = v.y; 617 | out[2] = v.z; 618 | out[3] = v.w; 619 | 620 | return out; 621 | }; 622 | 623 | /** 624 | * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===) 625 | * 626 | * @param {vec4} a The first vector. 627 | * @param {vec4} b The second vector. 628 | * @returns {Boolean} True if the vectors are equal, false otherwise. 629 | */ 630 | vec4.exactEquals = function (a, b) { 631 | return a.x === b.x && a.y === b.y && a.z === b.z && a.w === b.w; 632 | }; 633 | 634 | /** 635 | * Returns whether or not the vectors have approximately the same elements in the same position. 636 | * 637 | * @param {vec4} a The first vector. 638 | * @param {vec4} b The second vector. 639 | * @returns {Boolean} True if the vectors are equal, false otherwise. 640 | */ 641 | vec4.equals = function (a, b) { 642 | let a0 = a.x, a1 = a.y, a2 = a.z, a3 = a.w; 643 | let b0 = b.x, b1 = b.y, b2 = b.z, b3 = b.w; 644 | return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && 645 | Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && 646 | Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && 647 | Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3))); 648 | }; 649 | 650 | export default vec4; -------------------------------------------------------------------------------- /test/vec4.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { vec3, vec4 } = require('../dist/vmath'); 3 | 4 | tap.test('vec4', t => { 5 | let out, vecA, vecB, result; 6 | 7 | t.beforeEach(done => { 8 | vecA = vec4.new(1, 2, 3, 4); 9 | vecB = vec4.new(5, 6, 7, 8); 10 | out = vec4.new(0, 0, 0, 0); 11 | 12 | done(); 13 | }); 14 | 15 | t.test('create', t => { 16 | result = vec4.create(); 17 | 18 | t.equal_v4(result, [0, 0, 0, 0]); 19 | 20 | t.end(); 21 | }); 22 | 23 | t.test('clone', t => { 24 | result = vec4.clone(vecA); 25 | 26 | t.deepEqual(result, vecA); 27 | 28 | t.end(); 29 | }); 30 | 31 | t.test('new', t => { 32 | result = vec4.new(1, 2, 3, 4); 33 | 34 | t.equal_v4(result, [1, 2, 3, 4]); 35 | 36 | t.end(); 37 | }); 38 | 39 | t.test('copy', t => { 40 | result = vec4.copy(out, vecA); 41 | 42 | t.equal_v4(out, [1, 2, 3, 4]); 43 | t.equal(result, out); 44 | 45 | t.end(); 46 | }); 47 | 48 | t.test('set', t => { 49 | result = vec4.set(out, 1, 2, 3, 4); 50 | 51 | t.equal_v4(out, [1, 2, 3, 4]); 52 | t.equal(result, out); 53 | 54 | t.end(); 55 | }); 56 | 57 | t.test('add', t => { 58 | t.test('with a separate output vector', t => { 59 | result = vec4.add(out, vecA, vecB); 60 | 61 | t.equal_v4(out, [6, 8, 10, 12]); 62 | t.equal(result, out); 63 | t.equal_v4(vecA, [1, 2, 3, 4]); 64 | t.equal_v4(vecB, [5, 6, 7, 8]); 65 | 66 | t.end(); 67 | }); 68 | 69 | t.test('when vecA is the output vector', t => { 70 | result = vec4.add(vecA, vecA, vecB); 71 | 72 | t.equal_v4(vecA, [6, 8, 10, 12]); 73 | t.equal(result, vecA); 74 | t.equal_v4(vecB, [5, 6, 7, 8]); 75 | 76 | t.end(); 77 | }); 78 | 79 | t.test('when vecB is the output vector', t => { 80 | result = vec4.add(vecB, vecA, vecB); 81 | 82 | t.equal_v4(vecB, [6, 8, 10, 12]); 83 | t.equal(result, vecB); 84 | t.equal_v4(vecA, [1, 2, 3, 4]); 85 | 86 | t.end(); 87 | }); 88 | 89 | t.end(); 90 | }); 91 | 92 | t.test('subtract', t => { 93 | t.equal(vec4.sub, vec4.subtract); 94 | 95 | t.test('with a separate output vector', t => { 96 | result = vec4.subtract(out, vecA, vecB); 97 | 98 | t.equal_v4(out, [-4, -4, -4, -4]); 99 | t.equal(result, out); 100 | t.equal_v4(vecA, [1, 2, 3, 4]); 101 | t.equal_v4(vecB, [5, 6, 7, 8]); 102 | 103 | t.end(); 104 | }); 105 | 106 | t.test('when vecA is the output vector', t => { 107 | result = vec4.subtract(vecA, vecA, vecB); 108 | 109 | t.equal_v4(vecA, [-4, -4, -4, -4]); 110 | t.equal(result, vecA); 111 | t.equal_v4(vecB, [5, 6, 7, 8]); 112 | 113 | t.end(); 114 | }); 115 | 116 | t.test('when vecB is the output vector', t => { 117 | result = vec4.subtract(vecB, vecA, vecB); 118 | 119 | t.equal_v4(vecB, [-4, -4, -4, -4]); 120 | t.equal(result, vecB); 121 | t.equal_v4(vecA, [1, 2, 3, 4]); 122 | 123 | t.end(); 124 | }); 125 | 126 | t.end(); 127 | }); 128 | 129 | t.test('multiply', t => { 130 | t.equal(vec4.mul, vec4.multiply); 131 | 132 | t.test('with a separate output vector', t => { 133 | result = vec4.multiply(out, vecA, vecB); 134 | 135 | t.equal_v4(out, [5, 12, 21, 32]); 136 | t.equal(result, out); 137 | t.equal_v4(vecA, [1, 2, 3, 4]); 138 | t.equal_v4(vecB, [5, 6, 7, 8]); 139 | 140 | t.end(); 141 | }); 142 | 143 | t.test('when vecA is the output vector', t => { 144 | result = vec4.multiply(vecA, vecA, vecB); 145 | 146 | t.equal_v4(vecA, [5, 12, 21, 32]); 147 | t.equal(result, vecA); 148 | t.equal_v4(vecB, [5, 6, 7, 8]); 149 | 150 | t.end(); 151 | }); 152 | 153 | t.test('when vecB is the output vector', t => { 154 | result = vec4.multiply(vecB, vecA, vecB); 155 | 156 | t.equal_v4(vecB, [5, 12, 21, 32]); 157 | t.equal(result, vecB); 158 | t.equal_v4(vecA, [1, 2, 3, 4]); 159 | 160 | t.end(); 161 | }); 162 | 163 | t.end(); 164 | }); 165 | 166 | t.test('divide', t => { 167 | t.equal(vec4.div, vec4.divide); 168 | 169 | t.test('with a separate output vector', t => { 170 | result = vec4.divide(out, vecA, vecB); 171 | 172 | t.equal_v4(out, [0.2, 0.333333, 0.428571, 0.5]); 173 | t.equal(result, out); 174 | t.equal_v4(vecA, [1, 2, 3, 4]); 175 | t.equal_v4(vecB, [5, 6, 7, 8]); 176 | 177 | t.end(); 178 | }); 179 | 180 | t.test('when vecA is the output vector', t => { 181 | result = vec4.divide(vecA, vecA, vecB); 182 | 183 | t.equal_v4(vecA, [0.2, 0.333333, 0.428571, 0.5]); 184 | t.equal(result, vecA); 185 | t.equal_v4(vecB, [5, 6, 7, 8]); 186 | 187 | t.end(); 188 | }); 189 | 190 | t.test('when vecB is the output vector', t => { 191 | result = vec4.divide(vecB, vecA, vecB); 192 | 193 | t.equal_v4(vecB, [0.2, 0.333333, 0.428571, 0.5]); 194 | t.equal(result, vecB); 195 | t.equal_v4(vecA, [1, 2, 3, 4]); 196 | 197 | t.end(); 198 | }); 199 | 200 | t.end(); 201 | }); 202 | 203 | t.test('ceil', t => { 204 | t.beforeEach(done => { 205 | vec4.set(vecA, Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2); 206 | done(); 207 | }); 208 | 209 | t.test('with a separate output vector', t => { 210 | result = vec4.ceil(out, vecA); 211 | 212 | t.equal_v4(out, [3, 4, 2, 1]); 213 | t.equal(result, out); 214 | t.equal_v4(vecA, [Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2]); 215 | 216 | t.end(); 217 | }); 218 | 219 | t.test('when vecA is the output vector', t => { 220 | result = vec4.ceil(vecA, vecA); 221 | 222 | t.equal_v4(vecA, [3, 4, 2, 1]); 223 | t.equal(result, vecA); 224 | 225 | t.end(); 226 | }); 227 | 228 | t.end(); 229 | }); 230 | 231 | t.test('floor', t => { 232 | t.beforeEach(done => { 233 | vec4.set(vecA, Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2); 234 | done(); 235 | }); 236 | 237 | t.test('with a separate output vector', t => { 238 | result = vec4.floor(out, vecA); 239 | 240 | t.equal_v4(out, [2, 3, 1, 0]); 241 | t.equal(result, out); 242 | t.equal_v4(vecA, [Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2]); 243 | 244 | t.end(); 245 | }); 246 | 247 | t.test('when vecA is the output vector', t => { 248 | result = vec4.floor(vecA, vecA); 249 | 250 | t.equal_v4(vecA, [2, 3, 1, 0]); 251 | t.equal(result, vecA); 252 | 253 | t.end(); 254 | }); 255 | 256 | t.end(); 257 | }); 258 | 259 | t.test('min', t => { 260 | t.beforeEach(done => { 261 | vec4.set(vecA, 1, 3, 1, 3); 262 | vec4.set(vecB, 3, 1, 3, 1); 263 | 264 | done(); 265 | }); 266 | 267 | t.test('with a separate output vector', t => { 268 | result = vec4.min(out, vecA, vecB); 269 | 270 | t.equal_v4(out, [1, 1, 1, 1]); 271 | t.equal(result, out); 272 | t.equal_v4(vecA, [1, 3, 1, 3]); 273 | t.equal_v4(vecB, [3, 1, 3, 1]); 274 | 275 | t.end(); 276 | }); 277 | 278 | t.test('when vecA is the output vector', t => { 279 | result = vec4.min(vecA, vecA, vecB); 280 | 281 | t.equal_v4(vecA, [1, 1, 1, 1]); 282 | t.equal(result, vecA); 283 | t.equal_v4(vecB, [3, 1, 3, 1]); 284 | 285 | t.end(); 286 | }); 287 | 288 | t.test('when vecB is the output vector', t => { 289 | result = vec4.min(vecB, vecA, vecB); 290 | 291 | t.equal_v4(vecB, [1, 1, 1, 1]); 292 | t.equal(result, vecB); 293 | t.equal_v4(vecA, [1, 3, 1, 3]); 294 | 295 | t.end(); 296 | }); 297 | 298 | t.end(); 299 | }); 300 | 301 | t.test('max', t => { 302 | t.beforeEach(done => { 303 | vec4.set(vecA, 1, 3, 1, 3); 304 | vec4.set(vecB, 3, 1, 3, 1); 305 | done(); 306 | }); 307 | 308 | t.test('with a separate output vector', t => { 309 | result = vec4.max(out, vecA, vecB); 310 | 311 | t.equal_v4(out, [3, 3, 3, 3]); 312 | t.equal(result, out); 313 | t.equal_v4(vecA, [1, 3, 1, 3]); 314 | t.equal_v4(vecB, [3, 1, 3, 1]); 315 | 316 | t.end(); 317 | }); 318 | 319 | t.test('when vecA is the output vector', t => { 320 | result = vec4.max(vecA, vecA, vecB); 321 | 322 | t.equal_v4(vecA, [3, 3, 3, 3]); 323 | t.equal(result, vecA); 324 | t.equal_v4(vecB, [3, 1, 3, 1]); 325 | 326 | t.end(); 327 | }); 328 | 329 | t.test('when vecB is the output vector', t => { 330 | result = vec4.max(vecB, vecA, vecB); 331 | 332 | t.equal_v4(vecB, [3, 3, 3, 3]); 333 | t.equal(result, vecB); 334 | t.equal_v4(vecA, [1, 3, 1, 3]); 335 | 336 | t.end(); 337 | }); 338 | 339 | t.end(); 340 | }); 341 | 342 | t.test('round', t => { 343 | t.beforeEach(done => { 344 | vec4.set(vecA, Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2); 345 | done(); 346 | }); 347 | 348 | t.test('with a separate output vector', t => { 349 | result = vec4.round(out, vecA); 350 | 351 | t.equal_v4(out, [3, 3, 1, 1]); 352 | t.equal(result, out); 353 | t.equal_v4(vecA, [Math.E, Math.PI, Math.SQRT2, Math.SQRT1_2]); 354 | 355 | t.end(); 356 | }); 357 | 358 | t.test('when vecA is the output vector', t => { 359 | result = vec4.round(vecA, vecA); 360 | 361 | t.equal_v4(vecA, [3, 3, 1, 1]); 362 | t.equal(result, vecA); 363 | 364 | t.end(); 365 | }); 366 | 367 | t.end(); 368 | }); 369 | 370 | t.test('scale', t => { 371 | t.test('with a separate output vector', t => { 372 | result = vec4.scale(out, vecA, 2); 373 | 374 | t.equal_v4(out, [2, 4, 6, 8]); 375 | t.equal(result, out); 376 | t.equal_v4(vecA, [1, 2, 3, 4]); 377 | 378 | t.end(); 379 | }); 380 | 381 | t.test('when vecA is the output vector', t => { 382 | result = vec4.scale(vecA, vecA, 2); 383 | 384 | t.equal_v4(vecA, [2, 4, 6, 8]); 385 | t.equal(result, vecA); 386 | 387 | t.end(); 388 | }); 389 | 390 | t.end(); 391 | }); 392 | 393 | t.test('scaleAndAdd', t => { 394 | t.test('with a separate output vector', t => { 395 | result = vec4.scaleAndAdd(out, vecA, vecB, 0.5); 396 | 397 | t.equal_v4(out, [3.5, 5, 6.5, 8]); 398 | t.equal(result, out); 399 | t.equal_v4(vecA, [1, 2, 3, 4]); 400 | t.equal_v4(vecB, [5, 6, 7, 8]); 401 | 402 | t.end(); 403 | }); 404 | 405 | t.test('when vecA is the output vector', t => { 406 | result = vec4.scaleAndAdd(vecA, vecA, vecB, 0.5); 407 | 408 | t.equal_v4(vecA, [3.5, 5, 6.5, 8]); 409 | t.equal(result, vecA); 410 | t.equal_v4(vecB, [5, 6, 7, 8]); 411 | 412 | t.end(); 413 | }); 414 | 415 | t.test('when vecB is the output vector', t => { 416 | result = vec4.scaleAndAdd(vecB, vecA, vecB, 0.5); 417 | 418 | t.equal_v4(vecB, [3.5, 5, 6.5, 8]); 419 | t.equal(result, vecB); 420 | t.equal_v4(vecA, [1, 2, 3, 4]); 421 | 422 | t.end(); 423 | }); 424 | 425 | t.end(); 426 | }); 427 | 428 | t.test('distance', t => { 429 | t.equal(vec4.dist, vec4.distance); 430 | 431 | result = vec4.distance(vecA, vecB); 432 | 433 | t.approx(result, 8); 434 | 435 | t.end(); 436 | }); 437 | 438 | t.test('squaredDistance', t => { 439 | t.equal(vec4.sqrDist, vec4.squaredDistance); 440 | 441 | result = vec4.squaredDistance(vecA, vecB); 442 | 443 | t.equal(result, 64); 444 | 445 | t.end(); 446 | }); 447 | 448 | t.test('length', t => { 449 | t.equal(vec4.len, vec4.length); 450 | 451 | result = vec4.length(vecA); 452 | 453 | t.approx(result, 5.477225); 454 | 455 | t.end(); 456 | }); 457 | 458 | t.test('squaredLength', t => { 459 | t.equal(vec4.sqrLen, vec4.squaredLength); 460 | 461 | result = vec4.squaredLength(vecA); 462 | 463 | t.equal(result, 30); 464 | 465 | t.end(); 466 | }); 467 | 468 | t.test('negate', t => { 469 | t.test('with a separate output vector', t => { 470 | result = vec4.negate(out, vecA); 471 | 472 | t.equal_v4(out, [-1, -2, -3, -4]); 473 | t.equal(result, out); 474 | t.equal_v4(vecA, [1, 2, 3, 4]); 475 | 476 | t.end(); 477 | }); 478 | 479 | t.test('when vecA is the output vector', t => { 480 | result = vec4.negate(vecA, vecA); 481 | 482 | t.equal_v4(vecA, [-1, -2, -3, -4]); 483 | t.equal(result, vecA); 484 | 485 | t.end(); 486 | }); 487 | 488 | t.end(); 489 | }); 490 | 491 | t.test('inverse', t => { 492 | vec4.set(vecA, 0.1, 0.2, 0.3, 0.4); 493 | 494 | result = vec4.inverse(out, vecA); 495 | 496 | t.equal_v4(out, [10, 5, 10 / 3, 2.5]); 497 | t.equal(result, out); 498 | 499 | t.end(); 500 | }); 501 | 502 | t.test('inverseSafe', t => { 503 | vec4.set(vecA, 0.1, 0.2, 0.3, 0.4); 504 | vec4.set(vecB, 0.0000000001, -0.0000000001, 0.1, 0.2); 505 | 506 | result = vec4.inverseSafe(out, vecA); 507 | t.equal_v4(out, [10, 5, 10 / 3, 2.5]); 508 | t.equal(result, out); 509 | 510 | result = vec4.inverseSafe(out, vecB); 511 | t.equal_v4(out, [0, 0, 10, 5]); 512 | t.equal(result, out); 513 | 514 | t.end(); 515 | }); 516 | 517 | t.test('normalize', t => { 518 | t.beforeEach(done => { 519 | vec4.set(vecA, 5, 0, 0, 0); 520 | done(); 521 | }); 522 | 523 | t.test('with a separate output vector', t => { 524 | result = vec4.normalize(out, vecA); 525 | 526 | t.equal_v4(out, [1, 0, 0, 0]); 527 | t.equal(result, out); 528 | t.equal_v4(vecA, [5, 0, 0, 0]); 529 | 530 | t.end(); 531 | }); 532 | 533 | t.test('when vecA is the output vector', t => { 534 | result = vec4.normalize(vecA, vecA); 535 | 536 | t.equal_v4(vecA, [1, 0, 0, 0]); 537 | t.equal(result, vecA); 538 | 539 | t.end(); 540 | }); 541 | 542 | t.end(); 543 | }); 544 | 545 | t.test('dot', t => { 546 | result = vec4.dot(vecA, vecB); 547 | 548 | t.equal(result, 70); 549 | t.equal_v4(vecA, [1, 2, 3, 4]); 550 | t.equal_v4(vecB, [5, 6, 7, 8]); 551 | 552 | t.end(); 553 | }); 554 | 555 | t.test('lerp', t => { 556 | t.test('with a separate output vector', t => { 557 | result = vec4.lerp(out, vecA, vecB, 0.5); 558 | 559 | t.equal_v4(out, [3, 4, 5, 6]); 560 | t.equal(result, out); 561 | t.equal_v4(vecA, [1, 2, 3, 4]); 562 | t.equal_v4(vecB, [5, 6, 7, 8]); 563 | 564 | t.end(); 565 | }); 566 | 567 | t.test('when vecA is the output vector', t => { 568 | result = vec4.lerp(vecA, vecA, vecB, 0.5); 569 | 570 | t.equal_v4(vecA, [3, 4, 5, 6]); 571 | t.equal(result, vecA); 572 | t.equal_v4(vecB, [5, 6, 7, 8]); 573 | 574 | t.end(); 575 | }); 576 | 577 | t.test('when vecB is the output vector', t => { 578 | result = vec4.lerp(vecB, vecA, vecB, 0.5); 579 | 580 | t.equal_v4(vecB, [3, 4, 5, 6]); 581 | t.equal(result, vecB); 582 | t.equal_v4(vecA, [1, 2, 3, 4]); 583 | 584 | t.end(); 585 | }); 586 | 587 | t.end(); 588 | }); 589 | 590 | t.test('random', t => { 591 | t.test('with no scale', t => { 592 | result = vec4.random(out); 593 | 594 | t.approx(vec4.length(out), 1.0); 595 | t.equal(result, out); 596 | 597 | t.end(); 598 | }); 599 | 600 | t.test('with a scale', t => { 601 | result = vec4.random(out, 5.0); 602 | 603 | t.approx(vec4.length(out), 5.0); 604 | t.equal(result, out); 605 | 606 | t.end(); 607 | }); 608 | 609 | t.end(); 610 | }); 611 | 612 | t.test('forEach', t => { 613 | let vecArray; 614 | 615 | t.beforeEach(done => { 616 | vecArray = [ 617 | 1, 2, 3, 4, 618 | 5, 6, 7, 8, 619 | 0, 0, 0, 0 620 | ]; 621 | 622 | done(); 623 | }); 624 | 625 | t.test('when performing operations that take no extra arguments', t => { 626 | result = vec4.forEach(vecArray, 0, 0, 0, vec4.normalize); 627 | 628 | t.approxArray(vecArray, [ 629 | 0.182574, 0.365148, 0.547722, 0.730296, 630 | 0.379049, 0.454858, 0.530668, 0.606478, 631 | 0, 0, 0, 0 632 | ]); 633 | t.equal(result, vecArray); 634 | 635 | t.end(); 636 | }); 637 | 638 | t.test('when performing operations that takes one extra arguments', t => { 639 | result = vec4.forEach(vecArray, 0, 0, 0, vec4.add, vecA); 640 | 641 | t.approxArray(vecArray, [ 642 | 2, 4, 6, 8, 643 | 6, 8, 10, 12, 644 | 1, 2, 3, 4 645 | ]); 646 | t.equal(result, vecArray); 647 | t.equal_v4(vecA, [1, 2, 3, 4]); 648 | 649 | t.end(); 650 | }); 651 | 652 | t.test('when specifying an offset', t => { 653 | result = vec4.forEach(vecArray, 0, 4, 0, vec4.add, vecA); 654 | 655 | t.approxArray(vecArray, [ 656 | 1, 2, 3, 4, 657 | 6, 8, 10, 12, 658 | 1, 2, 3, 4 659 | ]); 660 | t.equal(result, vecArray); 661 | t.equal_v4(vecA, [1, 2, 3, 4]); 662 | 663 | t.end(); 664 | }); 665 | 666 | t.test('when specifying a count', t => { 667 | result = vec4.forEach(vecArray, 0, 0, 2, vec4.add, vecA); 668 | 669 | t.approxArray(vecArray, [ 670 | 2, 4, 6, 8, 671 | 6, 8, 10, 12, 672 | 0, 0, 0, 0 673 | ]); 674 | t.equal(result, vecArray); 675 | t.equal_v4(vecA, [1, 2, 3, 4]); 676 | 677 | t.end(); 678 | }); 679 | 680 | t.test('when specifying a stride', t => { 681 | result = vec4.forEach(vecArray, 8, 0, 0, vec4.add, vecA); 682 | 683 | t.approxArray(vecArray, [ 684 | 2, 4, 6, 8, 685 | 5, 6, 7, 8, 686 | 1, 2, 3, 4 687 | ]); 688 | t.equal(result, vecArray); 689 | t.equal_v4(vecA, [1, 2, 3, 4]); 690 | 691 | t.end(); 692 | }); 693 | 694 | t.test('when calling a function that does not modify the out variable', t => { 695 | result = vec3.forEach(vecArray, 0, 0, 0, function (out, vec) { }); 696 | 697 | t.approxArray(vecArray, [ 698 | 1, 2, 3, 4, 699 | 5, 6, 7, 8, 700 | 0, 0, 0, 0 701 | ]); 702 | t.equal(result, vecArray); 703 | 704 | t.end(); 705 | }); 706 | 707 | t.end(); 708 | }); 709 | 710 | t.test('str', t => { 711 | result = vec4.str(vecA); 712 | 713 | t.equal(result, 'vec4(1, 2, 3, 4)'); 714 | 715 | t.end(); 716 | }); 717 | 718 | t.test('array', t => { 719 | result = vec4.array([], vecA); 720 | 721 | t.deepEqual(result, new Float32Array([1, 2, 3, 4])); 722 | 723 | t.end(); 724 | }); 725 | 726 | t.test('exactEquals', t => { 727 | vec4.set(vecA, 0, 1, 2, 3); 728 | vec4.set(vecB, 0, 1, 2, 3); 729 | let vecC = vec4.new(1, 2, 3, 4); 730 | let r0 = vec4.exactEquals(vecA, vecB); 731 | let r1 = vec4.exactEquals(vecA, vecC); 732 | 733 | t.equal(r0, true); 734 | t.equal(r1, false); 735 | t.equal_v4(vecA, [0, 1, 2, 3]); 736 | t.equal_v4(vecB, [0, 1, 2, 3]); 737 | 738 | t.end(); 739 | }); 740 | 741 | t.test('equals', t => { 742 | vec4.set(vecA, 0, 1, 2, 3); 743 | vec4.set(vecB, 0, 1, 2, 3); 744 | 745 | let vecC = vec4.new(1, 2, 3, 4); 746 | let vecD = vec4.new(1e-16, 1, 2, 3); 747 | let r0 = vec4.equals(vecA, vecB); 748 | let r1 = vec4.equals(vecA, vecC); 749 | let r2 = vec4.equals(vecA, vecD); 750 | 751 | t.equal(r0, true); 752 | t.equal(r1, false); 753 | t.equal(r2, true); 754 | t.equal_v4(vecA, [0, 1, 2, 3]); 755 | t.equal_v4(vecB, [0, 1, 2, 3]); 756 | 757 | t.end(); 758 | }); 759 | 760 | t.test('JSON.stringify', t => { 761 | t.equal( 762 | JSON.stringify({ vecA, vecB }), 763 | '{"vecA":[1,2,3,4],"vecB":[5,6,7,8]}' 764 | ); 765 | 766 | t.end(); 767 | }); 768 | 769 | t.end(); 770 | }); 771 | -------------------------------------------------------------------------------- /test/mat4.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const { vec3, vec4, quat, mat3, mat4 } = require('../dist/vmath'); 3 | 4 | tap.test('mat4', t => { 5 | let out = mat4.create(); 6 | let matA = mat4.create(); 7 | let matB = mat4.create(); 8 | let identity = mat4.create(); 9 | let result = mat4.create(); 10 | 11 | t.beforeEach(done => { 12 | matA = mat4.new( 13 | 1, 0, 0, 0, 14 | 0, 1, 0, 0, 15 | 0, 0, 1, 0, 16 | 1, 2, 3, 1 17 | ); 18 | 19 | matB = mat4.new( 20 | 1, 0, 0, 0, 21 | 0, 1, 0, 0, 22 | 0, 0, 1, 0, 23 | 4, 5, 6, 1 24 | ); 25 | 26 | out = mat4.new( 27 | 0, 0, 0, 0, 28 | 0, 0, 0, 0, 29 | 0, 0, 0, 0, 30 | 0, 0, 0, 0 31 | ); 32 | 33 | identity = mat4.new( 34 | 1, 0, 0, 0, 35 | 0, 1, 0, 0, 36 | 0, 0, 1, 0, 37 | 0, 0, 0, 1 38 | ); 39 | 40 | done(); 41 | }); 42 | 43 | t.test('create', t => { 44 | result = mat4.create(); 45 | 46 | t.deepEqual(result, identity); 47 | 48 | t.end(); 49 | }); 50 | 51 | t.test('clone', t => { 52 | result = mat4.clone(matA); 53 | 54 | t.deepEqual(result, matA); 55 | 56 | t.end(); 57 | }); 58 | 59 | t.test('copy', t => { 60 | result = mat4.copy(out, matA); 61 | 62 | t.deepEqual(out, matA); 63 | t.equal(result, out); 64 | 65 | t.end(); 66 | }); 67 | 68 | t.test('identity', t => { 69 | result = mat4.identity(out); 70 | 71 | t.deepEqual(result, identity); 72 | t.equal(result, out); 73 | 74 | t.end(); 75 | }); 76 | 77 | t.test('transpose', t => { 78 | t.test('with a separate output matrix', t => { 79 | result = mat4.transpose(out, matA); 80 | 81 | t.equal_m4(out, [ 82 | 1, 0, 0, 1, 83 | 0, 1, 0, 2, 84 | 0, 0, 1, 3, 85 | 0, 0, 0, 1 86 | ]); 87 | t.equal(result, out); 88 | t.equal_m4(matA, [ 89 | 1, 0, 0, 0, 90 | 0, 1, 0, 0, 91 | 0, 0, 1, 0, 92 | 1, 2, 3, 1 93 | ]); 94 | 95 | t.end(); 96 | }); 97 | 98 | t.test('when matA is the output matrix', t => { 99 | result = mat4.transpose(matA, matA); 100 | 101 | t.equal_m4(matA, [ 102 | 1, 0, 0, 1, 103 | 0, 1, 0, 2, 104 | 0, 0, 1, 3, 105 | 0, 0, 0, 1 106 | ]); 107 | t.equal(result, matA); 108 | 109 | t.end(); 110 | }); 111 | 112 | t.end(); 113 | }); 114 | 115 | t.test('invert', t => { 116 | t.test('with a separate output matrix', t => { 117 | result = mat4.invert(out, matA); 118 | 119 | t.equal_m4(out, [ 120 | 1, 0, 0, 0, 121 | 0, 1, 0, 0, 122 | 0, 0, 1, 0, 123 | -1, -2, -3, 1 124 | ]); 125 | t.equal(result, out); 126 | t.equal_m4(matA, [ 127 | 1, 0, 0, 0, 128 | 0, 1, 0, 0, 129 | 0, 0, 1, 0, 130 | 1, 2, 3, 1 131 | ]); 132 | 133 | t.end(); 134 | }); 135 | 136 | t.test('when matA is the output matrix', t => { 137 | result = mat4.invert(matA, matA); 138 | 139 | t.equal_m4(matA, [ 140 | 1, 0, 0, 0, 141 | 0, 1, 0, 0, 142 | 0, 0, 1, 0, 143 | -1, -2, -3, 1 144 | ]); 145 | t.equal(result, matA); 146 | 147 | t.end(); 148 | }); 149 | 150 | t.end(); 151 | }); 152 | 153 | t.test('adjoint', t => { 154 | t.test('with a separate output matrix', t => { 155 | result = mat4.adjoint(out, matA); 156 | 157 | t.equal_m4(out, [ 158 | 1, 0, 0, 0, 159 | 0, 1, 0, 0, 160 | 0, 0, 1, 0, 161 | -1, -2, -3, 1 162 | ]); 163 | t.equal(result, out); 164 | t.equal_m4(matA, [ 165 | 1, 0, 0, 0, 166 | 0, 1, 0, 0, 167 | 0, 0, 1, 0, 168 | 1, 2, 3, 1 169 | ]); 170 | 171 | t.end(); 172 | }); 173 | 174 | t.test('when matA is the output matrix', t => { 175 | result = mat4.adjoint(matA, matA); 176 | 177 | t.equal_m4(matA, [ 178 | 1, 0, 0, 0, 179 | 0, 1, 0, 0, 180 | 0, 0, 1, 0, 181 | -1, -2, -3, 1 182 | ]); 183 | t.equal(result, matA); 184 | 185 | t.end(); 186 | }); 187 | 188 | t.end(); 189 | }); 190 | 191 | t.test('determinant', t => { 192 | result = mat4.determinant(matA); 193 | 194 | t.equal(result, 1); 195 | 196 | t.end(); 197 | }); 198 | 199 | t.test('multiply', t => { 200 | t.equal(mat4.mul, mat4.multiply); 201 | 202 | t.test('with a separate output matrix', t => { 203 | result = mat4.multiply(out, matA, matB); 204 | 205 | t.equal_m4(out, [ 206 | 1, 0, 0, 0, 207 | 0, 1, 0, 0, 208 | 0, 0, 1, 0, 209 | 5, 7, 9, 1 210 | ]); 211 | t.equal(result, out); 212 | t.equal_m4(matA, [ 213 | 1, 0, 0, 0, 214 | 0, 1, 0, 0, 215 | 0, 0, 1, 0, 216 | 1, 2, 3, 1 217 | ]); 218 | t.equal_m4(matB, [ 219 | 1, 0, 0, 0, 220 | 0, 1, 0, 0, 221 | 0, 0, 1, 0, 222 | 4, 5, 6, 1 223 | ]); 224 | 225 | t.end(); 226 | }); 227 | 228 | t.test('when matA is the output matrix', t => { 229 | result = mat4.multiply(matA, matA, matB); 230 | 231 | t.equal_m4(matA, [ 232 | 1, 0, 0, 0, 233 | 0, 1, 0, 0, 234 | 0, 0, 1, 0, 235 | 5, 7, 9, 1 236 | ]); 237 | t.equal(result, matA); 238 | t.equal_m4(matB, [ 239 | 1, 0, 0, 0, 240 | 0, 1, 0, 0, 241 | 0, 0, 1, 0, 242 | 4, 5, 6, 1 243 | ]); 244 | 245 | t.end(); 246 | }); 247 | 248 | t.test('when matB is the output matrix', t => { 249 | result = mat4.multiply(matB, matA, matB); 250 | 251 | t.equal_m4(matB, [ 252 | 1, 0, 0, 0, 253 | 0, 1, 0, 0, 254 | 0, 0, 1, 0, 255 | 5, 7, 9, 1 256 | ]); 257 | t.equal(result, matB); 258 | t.equal_m4(matA, [ 259 | 1, 0, 0, 0, 260 | 0, 1, 0, 0, 261 | 0, 0, 1, 0, 262 | 1, 2, 3, 1 263 | ]); 264 | 265 | t.end(); 266 | }); 267 | 268 | t.end(); 269 | }); 270 | 271 | t.test('translate', t => { 272 | t.test('with a separate output matrix', t => { 273 | result = mat4.translate(out, matA, vec3.new(4, 5, 6)); 274 | 275 | t.equal_m4(out, [ 276 | 1, 0, 0, 0, 277 | 0, 1, 0, 0, 278 | 0, 0, 1, 0, 279 | 5, 7, 9, 1 280 | ]); 281 | t.equal(result, out); 282 | t.equal_m4(matA, [ 283 | 1, 0, 0, 0, 284 | 0, 1, 0, 0, 285 | 0, 0, 1, 0, 286 | 1, 2, 3, 1 287 | ]); 288 | 289 | t.end(); 290 | }); 291 | 292 | t.test('when matA is the output matrix', t => { 293 | result = mat4.translate(matA, matA, vec3.new(4, 5, 6)); 294 | 295 | t.equal_m4(matA, [ 296 | 1, 0, 0, 0, 297 | 0, 1, 0, 0, 298 | 0, 0, 1, 0, 299 | 5, 7, 9, 1 300 | ]); 301 | t.equal(result, matA); 302 | 303 | t.end(); 304 | }); 305 | 306 | t.end(); 307 | }); 308 | 309 | t.test('scale', t => { 310 | t.test('with a separate output matrix', t => { 311 | result = mat4.scale(out, matA, vec3.new(4, 5, 6)); 312 | 313 | t.equal_m4(out, [ 314 | 4, 0, 0, 0, 315 | 0, 5, 0, 0, 316 | 0, 0, 6, 0, 317 | 1, 2, 3, 1 318 | ]); 319 | t.equal(result, out); 320 | t.equal_m4(matA, [ 321 | 1, 0, 0, 0, 322 | 0, 1, 0, 0, 323 | 0, 0, 1, 0, 324 | 1, 2, 3, 1 325 | ]); 326 | 327 | t.end(); 328 | }); 329 | 330 | t.test('when matA is the output matrix', t => { 331 | result = mat4.scale(matA, matA, vec3.new(4, 5, 6)); 332 | 333 | t.equal_m4(matA, [ 334 | 4, 0, 0, 0, 335 | 0, 5, 0, 0, 336 | 0, 0, 6, 0, 337 | 1, 2, 3, 1 338 | ]); 339 | t.equal(result, matA); 340 | 341 | t.end(); 342 | }); 343 | 344 | t.end(); 345 | }); 346 | 347 | t.test('rotate', t => { 348 | let rad = Math.PI * 0.5; 349 | let axis = vec3.new(1, 0, 0); 350 | 351 | t.test('with a separate output matrix', t => { 352 | result = mat4.rotate(out, matA, rad, axis); 353 | 354 | t.equal_m4(out, [ 355 | 1, 0, 0, 0, 356 | 0, Math.cos(rad), Math.sin(rad), 0, 357 | 0, -Math.sin(rad), Math.cos(rad), 0, 358 | 1, 2, 3, 1 359 | ]); 360 | t.equal(result, out); 361 | t.equal_m4(matA, [ 362 | 1, 0, 0, 0, 363 | 0, 1, 0, 0, 364 | 0, 0, 1, 0, 365 | 1, 2, 3, 1 366 | ]); 367 | 368 | t.end(); 369 | }); 370 | 371 | t.test('when matA is the output matrix', t => { 372 | result = mat4.rotate(matA, matA, rad, axis); 373 | 374 | t.equal_m4(matA, [ 375 | 1, 0, 0, 0, 376 | 0, Math.cos(rad), Math.sin(rad), 0, 377 | 0, -Math.sin(rad), Math.cos(rad), 0, 378 | 1, 2, 3, 1 379 | ]); 380 | t.equal(result, matA); 381 | 382 | t.end(); 383 | }); 384 | 385 | t.end(); 386 | }); 387 | 388 | t.test('rotateX', t => { 389 | var rad = Math.PI * 0.5; 390 | 391 | t.test('with a separate output matrix', t => { 392 | result = mat4.rotateX(out, matA, rad); 393 | 394 | t.equal_m4(out, [ 395 | 1, 0, 0, 0, 396 | 0, Math.cos(rad), Math.sin(rad), 0, 397 | 0, -Math.sin(rad), Math.cos(rad), 0, 398 | 1, 2, 3, 1 399 | ]); 400 | t.equal(result, out); 401 | t.equal_m4(matA, [ 402 | 1, 0, 0, 0, 403 | 0, 1, 0, 0, 404 | 0, 0, 1, 0, 405 | 1, 2, 3, 1 406 | ]); 407 | 408 | t.end(); 409 | }); 410 | 411 | t.test('when matA is the output matrix', t => { 412 | result = mat4.rotateX(matA, matA, rad); 413 | 414 | t.equal_m4(matA, [ 415 | 1, 0, 0, 0, 416 | 0, Math.cos(rad), Math.sin(rad), 0, 417 | 0, -Math.sin(rad), Math.cos(rad), 0, 418 | 1, 2, 3, 1 419 | ]); 420 | t.equal(result, matA); 421 | 422 | t.end(); 423 | }); 424 | 425 | t.end(); 426 | }); 427 | 428 | t.test('rotateY', t => { 429 | let rad = Math.PI * 0.5; 430 | 431 | t.test('with a separate output matrix', t => { 432 | result = mat4.rotateY(out, matA, rad); 433 | 434 | t.equal_m4(out, [ 435 | Math.cos(rad), 0, -Math.sin(rad), 0, 436 | 0, 1, 0, 0, 437 | Math.sin(rad), 0, Math.cos(rad), 0, 438 | 1, 2, 3, 1 439 | ]); 440 | t.equal(result, out); 441 | t.equal_m4(matA, [ 442 | 1, 0, 0, 0, 443 | 0, 1, 0, 0, 444 | 0, 0, 1, 0, 445 | 1, 2, 3, 1 446 | ]); 447 | 448 | t.end(); 449 | }); 450 | 451 | t.test('when matA is the output matrix', t => { 452 | result = mat4.rotateY(matA, matA, rad); 453 | 454 | t.equal_m4(matA, [ 455 | Math.cos(rad), 0, -Math.sin(rad), 0, 456 | 0, 1, 0, 0, 457 | Math.sin(rad), 0, Math.cos(rad), 0, 458 | 1, 2, 3, 1 459 | ]); 460 | t.equal(result, matA); 461 | 462 | t.end(); 463 | }); 464 | 465 | t.end(); 466 | }); 467 | 468 | t.test('rotateZ', t => { 469 | let rad = Math.PI * 0.5; 470 | 471 | t.test('with a separate output matrix', t => { 472 | result = mat4.rotateZ(out, matA, rad); 473 | 474 | t.equal_m4(out, [ 475 | Math.cos(rad), Math.sin(rad), 0, 0, 476 | -Math.sin(rad), Math.cos(rad), 0, 0, 477 | 0, 0, 1, 0, 478 | 1, 2, 3, 1 479 | ]); 480 | t.equal(result, out); 481 | t.equal_m4(matA, [ 482 | 1, 0, 0, 0, 483 | 0, 1, 0, 0, 484 | 0, 0, 1, 0, 485 | 1, 2, 3, 1 486 | ]); 487 | 488 | t.end(); 489 | }); 490 | 491 | t.test('when matA is the output matrix', t => { 492 | result = mat4.rotateZ(matA, matA, rad); 493 | 494 | t.equal_m4(matA, [ 495 | Math.cos(rad), Math.sin(rad), 0, 0, 496 | -Math.sin(rad), Math.cos(rad), 0, 0, 497 | 0, 0, 1, 0, 498 | 1, 2, 3, 1 499 | ]); 500 | t.equal(result, matA); 501 | 502 | t.end(); 503 | }); 504 | 505 | t.end(); 506 | }); 507 | 508 | // TODO: fromRT 509 | 510 | t.test('getTranslation', t => { 511 | t.test('from the identity matrix', t => { 512 | result = vec3.new(1, 2, 3); 513 | out = vec3.new(1, 2, 3); 514 | result = mat4.getTranslation(out, identity); 515 | 516 | t.equal(result, out); 517 | t.equal_v3(result, [0, 0, 0]); 518 | 519 | t.end(); 520 | }); 521 | 522 | t.test('from a translation-only matrix', t => { 523 | result = vec3.new(1, 2, 3); 524 | out = vec3.new(1, 2, 3); 525 | result = mat4.getTranslation(out, matB); 526 | 527 | t.equal_v3(out, [4, 5, 6]); 528 | 529 | t.end(); 530 | }); 531 | 532 | t.test('from a translation and rotation matrix', t => { 533 | let q = quat.create(); 534 | let v = vec3.new(5, 6, 7); 535 | q = quat.fromAxisAngle(q, [0.26726124, 0.534522474, 0.8017837], 0.55); 536 | mat4.fromRT(out, q, v); 537 | 538 | result = vec3.create(); 539 | mat4.getTranslation(result, out); 540 | 541 | t.equal_v3(result, [5, 6, 7]); 542 | 543 | t.end(); 544 | }); 545 | 546 | t.end(); 547 | }); 548 | 549 | t.test('getScaling', t => { 550 | t.test('from the identity matrix', t => { 551 | result = mat4.getScaling(out, identity); 552 | 553 | t.equal(result, out); 554 | t.equal_v3(result, [1, 1, 1]); 555 | 556 | t.end(); 557 | }); 558 | 559 | t.test('from a scale-only matrix', t => { 560 | let v = vec3.new(4, 5, 6); 561 | result = vec3.new(1, 2, 3); 562 | out = vec3.new(1, 2, 3); 563 | mat4.fromScaling(matA, v); 564 | result = mat4.getScaling(out, matA); 565 | 566 | t.equal_v3(out, [4, 5, 6]); 567 | 568 | t.end(); 569 | }); 570 | 571 | t.test('from a translation and rotation matrix', t => { 572 | let q = quat.create(); 573 | let v = vec3.new(5, 6, 7); 574 | q = quat.fromAxisAngle(q, vec3.new(1, 0, 0), 0.5); 575 | mat4.fromRT(out, q, v); 576 | 577 | result = vec3.new(1, 2, 3); 578 | mat4.getScaling(result, out); 579 | 580 | t.equal_v3(result, [1, 1, 1]); 581 | 582 | t.end(); 583 | }); 584 | 585 | t.test('from a translation, rotation and scale matrix', t => { 586 | let R = quat.create(); 587 | let T = vec3.new(1, 2, 3); 588 | let S = vec3.new(5, 6, 7); 589 | R = quat.fromAxisAngle(R, vec3.new(0, 1, 0), 0.7); 590 | mat4.fromRTS(out, R, T, S); 591 | result = vec3.new(5, 6, 7); 592 | mat4.getScaling(result, out); 593 | 594 | t.equal_v3(result, [5, 6, 7]); 595 | 596 | t.end(); 597 | }); 598 | 599 | t.end(); 600 | }); 601 | 602 | t.test('getRotation', t => { 603 | t.test('from the identity matrix', t => { 604 | result = quat.new(1, 2, 3, 4); 605 | out = quat.new(1, 2, 3, 4); 606 | result = mat4.getRotation(out, identity); 607 | 608 | t.equal(result, out); 609 | 610 | let unitQuat = quat.create(); 611 | quat.identity(unitQuat); 612 | t.deepEqual(result, unitQuat); 613 | 614 | t.end(); 615 | }); 616 | 617 | t.test('from a translation-only matrix', t => { 618 | result = quat.new(1, 2, 3, 4); 619 | out = quat.new(1, 2, 3, 4); 620 | result = mat4.getRotation(out, matB); 621 | 622 | let unitQuat = quat.create(); 623 | quat.identity(unitQuat); 624 | t.deepEqual(result, unitQuat); 625 | 626 | t.end(); 627 | }); 628 | 629 | t.test('from a translation and rotation matrix', t => { 630 | let q = quat.create(); 631 | let outVec = vec3.new(5, 6, 7); 632 | let testVec = vec3.new(1, 5, 2); 633 | let ang = 0.78972; 634 | 635 | vec3.normalize(testVec, testVec); 636 | q = quat.fromAxisAngle(q, testVec, ang); 637 | mat4.fromRT(out, q, outVec); 638 | 639 | result = quat.new(2, 3, 4, 6); 640 | mat4.getRotation(result, out); 641 | 642 | let outaxis = vec3.create(); 643 | let outangle = quat.getAxisAngle(outaxis, result); 644 | 645 | t.deepApprox(outaxis, testVec); 646 | t.deepApprox(outangle, ang); 647 | 648 | t.end(); 649 | }); 650 | 651 | t.end(); 652 | }); 653 | 654 | t.test('frustum', t => { 655 | result = mat4.frustum(out, -1, 1, -1, 1, -1, 1); 656 | 657 | t.equal_m4(result, [ 658 | -1, 0, 0, 0, 659 | 0, -1, 0, 0, 660 | 0, 0, 0, -1, 661 | 0, 0, 1, 0 662 | ]); 663 | t.equal(result, out); 664 | 665 | t.end(); 666 | }); 667 | 668 | t.test('perspective', t => { 669 | let fovy = Math.PI * 0.5; 670 | result = mat4.perspective(out, fovy, 1, 0, 1); 671 | 672 | t.equal_m4(result, [ 673 | 1, 0, 0, 0, 674 | 0, 1, 0, 0, 675 | 0, 0, -1, -1, 676 | 0, 0, 0, 0 677 | ]); 678 | t.equal(result, out); 679 | 680 | result = mat4.perspective(out, 45 * Math.PI / 180.0, 640 / 480, 0.1, 200); 681 | t.equal_m4(result, [ 682 | 1.81066, 0, 0, 0, 683 | 0, 2.414213, 0, 0, 684 | 0, 0, -1.001, -1, 685 | 0, 0, -0.2001, 0 686 | ]); 687 | 688 | t.end(); 689 | }); 690 | 691 | t.test('ortho', t => { 692 | result = mat4.ortho(out, -1, 1, -1, 1, -1, 1); 693 | 694 | t.equal_m4(result, [ 695 | 1, 0, 0, 0, 696 | 0, 1, 0, 0, 697 | 0, 0, -1, 0, 698 | 0, 0, 0, 1 699 | ]); 700 | t.equal(result, out); 701 | 702 | t.end(); 703 | }); 704 | 705 | t.test('lookAt', t => { 706 | let eye = vec3.new(0, 0, 1); 707 | let center = vec3.new(0, 0, -1); 708 | let up = vec3.new(0, 1, 0); 709 | let view = vec3.new(0, 0, 1); 710 | let right = vec3.new(1, 0, 0); 711 | 712 | t.test('looking down', t => { 713 | view = vec3.new(0, -1, 0); 714 | up = vec3.new(0, 0, -1); 715 | right = vec3.new(1, 0, 0); 716 | result = mat4.lookAt(out, vec3.new(0, 0, 0), view, up); 717 | 718 | t.equal(result, out); 719 | 720 | result = vec3.transformMat4(vec3.create(), view, out); 721 | t.equal_v3(result, [0, 0, -1]); 722 | 723 | result = vec3.transformMat4(vec3.create(), up, out); 724 | t.equal_v3(result, [0, 1, 0]); 725 | 726 | result = vec3.transformMat4(vec3.create(), right, out); 727 | t.equal_v3(result, [1, 0, 0]); 728 | 729 | t.end(); 730 | }); 731 | 732 | t.test('#74', t => { 733 | mat4.lookAt(out, 734 | vec3.new(0, 2, 0), 735 | vec3.new(0, 0.6, 0), 736 | vec3.new(0, 0, -1) 737 | ); 738 | 739 | result = vec3.transformMat4(vec3.create(), vec3.new(0, 2, -1), out); 740 | t.equal_v3(result, [0, 1, 0]); 741 | 742 | result = vec3.transformMat4(vec3.create(), vec3.new(1, 2, 0), out); 743 | t.equal_v3(result, [1, 0, 0]); 744 | 745 | result = vec3.transformMat4(vec3.create(), vec3.new(0, 1, 0), out); 746 | t.equal_v3(result, [0, 0, -1]); 747 | 748 | t.end(); 749 | }); 750 | 751 | eye = vec3.new(0, 0, 1); 752 | center = vec3.new(0, 0, -1); 753 | up = vec3.new(0, 1, 0); 754 | result = mat4.lookAt(out, eye, center, up); 755 | 756 | t.equal(result, out); 757 | t.equal_m4(result, [ 758 | 1, 0, 0, 0, 759 | 0, 1, 0, 0, 760 | 0, 0, 1, 0, 761 | 0, 0, -1, 1 762 | ]); 763 | 764 | t.end(); 765 | }); 766 | 767 | t.test('str', t => { 768 | result = mat4.str(matA); 769 | 770 | t.equal(result, 'mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1)'); 771 | 772 | t.end(); 773 | }); 774 | 775 | t.test('array', t => { 776 | result = mat4.array([], matA); 777 | 778 | t.deepEqual(result, new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1])); 779 | 780 | t.end(); 781 | }); 782 | 783 | t.test('frob', t => { 784 | result = mat4.frob(matA); 785 | 786 | t.equal(result, Math.sqrt(Math.pow(1, 2) + Math.pow(1, 2) + Math.pow(1, 2) + Math.pow(1, 2) + Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(3, 2))); 787 | 788 | t.end(); 789 | }); 790 | 791 | t.test('JSON.stringify', t => { 792 | t.equal( 793 | JSON.stringify({ matA, matB }), 794 | '{"matA":[1,0,0,0,0,1,0,0,0,0,1,0,1,2,3,1],"matB":[1,0,0,0,0,1,0,0,0,0,1,0,4,5,6,1]}' 795 | ); 796 | 797 | t.end(); 798 | }); 799 | 800 | t.end(); 801 | }); 802 | -------------------------------------------------------------------------------- /test/vec2.spec.js: -------------------------------------------------------------------------------- 1 | const tap = require('./tap'); 2 | const {vec2} = require('../dist/vmath'); 3 | 4 | tap.test('vec2', t => { 5 | let out, vecA, vecB, result; 6 | 7 | t.beforeEach(done => { 8 | vecA = vec2.new(1,2); 9 | vecB = vec2.new(3,4); 10 | out = vec2.new(0,0); 11 | 12 | done(); 13 | }); 14 | 15 | t.test('create', t => { 16 | result = vec2.create(); 17 | t.equal_v2(result, [0, 0]); 18 | 19 | t.end(); 20 | }); 21 | 22 | t.test('new', t => { 23 | result = vec2.new(1, 2); 24 | t.equal_v2(result, [1, 2]); 25 | 26 | t.end(); 27 | }); 28 | 29 | t.test('clone', t => { 30 | result = vec2.clone(vecA); 31 | t.deepEqual(result, vecA); 32 | 33 | t.end(); 34 | }); 35 | 36 | t.test('copy', t => { 37 | result = vec2.copy(out, vecA); 38 | t.deepEqual(result, vecA); 39 | t.equal(out, result); 40 | 41 | t.end(); 42 | }); 43 | 44 | t.test('set', t => { 45 | result = vec2.set(out, 1, 2); 46 | t.equal_v2(result, [1, 2]); 47 | t.equal(result, out); 48 | 49 | t.end(); 50 | }); 51 | 52 | t.test('add', t => { 53 | t.test('with a separate output vector', t => { 54 | result = vec2.add(out, vecA, vecB); 55 | t.equal_v2(out, [4, 6]); 56 | t.equal(result, out); 57 | t.equal_v2(vecA, [1, 2]); 58 | t.equal_v2(vecB, [3, 4]); 59 | 60 | t.end(); 61 | }); 62 | 63 | t.test('when vecA is the output vector', t => { 64 | result = vec2.add(vecA, vecA, vecB); 65 | t.equal_v2(vecA, [4, 6]); 66 | t.equal(result, vecA); 67 | t.equal_v2(vecB, [3, 4]); 68 | 69 | t.end(); 70 | }); 71 | 72 | t.test('when vecB is the output vector', t => { 73 | result = vec2.add(vecB, vecA, vecB); 74 | t.equal_v2(vecB, [4, 6]); 75 | t.equal(result, vecB); 76 | t.equal_v2(vecA, [1, 2]); 77 | 78 | t.end(); 79 | }); 80 | 81 | t.end(); 82 | }); 83 | 84 | t.test('subtract', t => { 85 | t.equal(vec2.sub, vec2.subtract); 86 | 87 | t.test('with a separate output vector', t => { 88 | result = vec2.sub(out, vecA, vecB); 89 | t.equal_v2(out, [-2, -2]); 90 | t.equal(result, out); 91 | t.equal_v2(vecA, [1, 2]); 92 | t.equal_v2(vecB, [3, 4]); 93 | 94 | t.end(); 95 | }); 96 | 97 | t.test('when vecA is the output vector', t => { 98 | result = vec2.sub(vecA, vecA, vecB); 99 | t.equal_v2(vecA, [-2, -2]); 100 | t.equal(result, vecA); 101 | t.equal_v2(vecB, [3, 4]); 102 | 103 | t.end(); 104 | }); 105 | 106 | t.test('when vecB is the output vector', t => { 107 | result = vec2.sub(vecB, vecA, vecB); 108 | t.equal_v2(vecB, [-2, -2]); 109 | t.equal(result, vecB); 110 | t.equal_v2(vecA, [1, 2]); 111 | 112 | t.end(); 113 | }); 114 | 115 | t.end(); 116 | }); 117 | 118 | t.test('multiply', t => { 119 | t.equal(vec2.mul, vec2.multiply); 120 | 121 | t.test('with a separate output vector', t => { 122 | result = vec2.mul(out, vecA, vecB); 123 | t.equal_v2(out, [3, 8]); 124 | t.equal(result, out); 125 | t.equal_v2(vecA, [1, 2]); 126 | t.equal_v2(vecB, [3, 4]); 127 | 128 | t.end(); 129 | }); 130 | 131 | t.test('when vecA is the output vector', t => { 132 | result = vec2.mul(vecA, vecA, vecB); 133 | t.equal_v2(vecA, [3, 8]); 134 | t.equal(result, vecA); 135 | t.equal_v2(vecB, [3, 4]); 136 | 137 | t.end(); 138 | }); 139 | 140 | t.test('when vecB is the output vector', t => { 141 | result = vec2.mul(vecB, vecA, vecB); 142 | t.equal_v2(vecB, [3, 8]); 143 | t.equal(result, vecB); 144 | t.equal_v2(vecA, [1, 2]); 145 | 146 | t.end(); 147 | }); 148 | 149 | t.end(); 150 | }); 151 | 152 | t.test('divide', t => { 153 | t.equal(vec2.div, vec2.divide); 154 | 155 | t.test('with a separate output vector', t => { 156 | result = vec2.div(out, vecA, vecB); 157 | t.equal_v2(out, [0.3333333, 0.5]); 158 | t.equal(result, out); 159 | t.equal_v2(vecA, [1, 2]); 160 | t.equal_v2(vecB, [3, 4]); 161 | 162 | t.end(); 163 | }); 164 | 165 | t.test('when vecA is the output vector', t => { 166 | result = vec2.div(vecA, vecA, vecB); 167 | t.equal_v2(vecA, [0.3333333, 0.5]); 168 | t.equal(result, vecA); 169 | t.equal_v2(vecB, [3, 4]); 170 | 171 | t.end(); 172 | }); 173 | 174 | t.test('when vecB is the output vector', t => { 175 | result = vec2.div(vecB, vecA, vecB); 176 | t.equal_v2(vecB, [0.3333333, 0.5]); 177 | t.equal(result, vecB); 178 | t.equal_v2(vecA, [1, 2]); 179 | 180 | t.end(); 181 | }); 182 | 183 | t.end(); 184 | }); 185 | 186 | t.test('ceil', t => { 187 | t.test('with a separate output vector', t => { 188 | vec2.set(vecA, Math.E, Math.PI); 189 | result = vec2.ceil(out, vecA); 190 | 191 | t.equal_v2(out, [3, 4]); 192 | t.equal(result, out); 193 | t.equal_v2(vecA, [Math.E, Math.PI]); 194 | 195 | t.end(); 196 | }); 197 | 198 | t.test('when vecA is the output vector', t => { 199 | vec2.set(vecA, Math.E, Math.PI); 200 | result = vec2.ceil(vecA, vecA); 201 | 202 | t.equal_v2(vecA, [3, 4]); 203 | t.equal(result, vecA); 204 | 205 | t.end(); 206 | }); 207 | 208 | t.end(); 209 | }); 210 | 211 | t.test('floor', t => { 212 | t.test('with a separate output vector', t => { 213 | vec2.set(vecA, Math.E, Math.PI); 214 | result = vec2.floor(out, vecA); 215 | 216 | t.equal_v2(out, [2, 3]); 217 | t.equal(result, out); 218 | t.equal_v2(vecA, [Math.E, Math.PI]); 219 | 220 | t.end(); 221 | }); 222 | 223 | t.test('when vecA is the output vector', t => { 224 | vec2.set(vecA, Math.E, Math.PI); 225 | result = vec2.floor(vecA, vecA); 226 | 227 | t.equal_v2(vecA, [2, 3]); 228 | t.equal(result, vecA); 229 | 230 | t.end(); 231 | }); 232 | 233 | t.end(); 234 | }); 235 | 236 | t.test('min', t => { 237 | t.beforeEach(done => { 238 | vec2.set(vecA, 1, 4); 239 | vec2.set(vecB, 3, 2); 240 | 241 | done(); 242 | }); 243 | 244 | t.test('with a separate output vector', t => { 245 | result = vec2.min(out, vecA, vecB); 246 | 247 | t.equal_v2(out, [1, 2]); 248 | t.equal(result, out); 249 | t.equal_v2(vecA, [1, 4]); 250 | t.equal_v2(vecB, [3, 2]); 251 | 252 | t.end(); 253 | }); 254 | 255 | t.test('when vecA is the output vector', t => { 256 | result = vec2.min(vecA, vecA, vecB); 257 | 258 | t.equal_v2(vecA, [1, 2]); 259 | t.equal(result, vecA); 260 | t.equal_v2(vecB, [3, 2]); 261 | 262 | t.end(); 263 | }); 264 | 265 | t.test('when vecB is the output vector', t => { 266 | result = vec2.min(vecB, vecA, vecB); 267 | 268 | t.equal_v2(vecB, [1, 2]); 269 | t.equal(result, vecB); 270 | t.equal_v2(vecA, [1, 4]); 271 | 272 | t.end(); 273 | }); 274 | 275 | t.end(); 276 | }); 277 | 278 | t.test('max', t => { 279 | t.beforeEach(done => { 280 | vec2.set(vecA, 1, 4); 281 | vec2.set(vecB, 3, 2); 282 | 283 | done(); 284 | }); 285 | 286 | t.test('with a separate output vector', t => { 287 | result = vec2.max(out, vecA, vecB); 288 | 289 | t.equal_v2(out, [3, 4]); 290 | t.equal(result, out); 291 | t.equal_v2(vecA, [1, 4]); 292 | t.equal_v2(vecB, [3, 2]); 293 | 294 | t.end(); 295 | }); 296 | 297 | t.test('when vecA is the output vector', t => { 298 | result = vec2.max(vecA, vecA, vecB); 299 | 300 | t.equal_v2(vecA, [3, 4]); 301 | t.equal(result, vecA); 302 | t.equal_v2(vecB, [3, 2]); 303 | 304 | t.end(); 305 | }); 306 | 307 | t.test('when vecB is the output vector', t => { 308 | result = vec2.max(vecB, vecA, vecB); 309 | 310 | t.equal_v2(vecB, [3, 4]); 311 | t.equal(result, vecB); 312 | t.equal_v2(vecA, [1, 4]); 313 | 314 | t.end(); 315 | }); 316 | 317 | t.end(); 318 | }); 319 | 320 | t.test('round', t => { 321 | t.beforeEach(done => { 322 | vec2.set(vecA, Math.E, Math.PI); 323 | 324 | done(); 325 | }); 326 | 327 | t.test('with a separate output vector', t => { 328 | result = vec2.round(out, vecA); 329 | 330 | t.equal_v2(out, [3, 3]); 331 | t.equal(result, out); 332 | t.equal_v2(vecA, [Math.E, Math.PI]); 333 | 334 | t.end(); 335 | }); 336 | 337 | t.test('when vecA is the output vector', t => { 338 | result = vec2.round(vecA, vecA); 339 | 340 | t.equal_v2(vecA, [3, 3]); 341 | t.equal(result, vecA); 342 | 343 | t.end(); 344 | }); 345 | 346 | t.end(); 347 | }); 348 | 349 | t.test('scale', t => { 350 | t.test('with a separate output vector', t => { 351 | result = vec2.scale(out, vecA, 2); 352 | 353 | t.equal_v2(out, [2, 4]); 354 | t.equal(result, out); 355 | t.equal_v2(vecA, [1, 2]); 356 | 357 | t.end(); 358 | }); 359 | 360 | t.test('when vecA is the output vector', t => { 361 | result = vec2.scale(vecA, vecA, 2); 362 | 363 | t.equal_v2(vecA, [2, 4]); 364 | t.equal(result, vecA); 365 | 366 | t.end(); 367 | }); 368 | 369 | t.end(); 370 | }); 371 | 372 | t.test('scaleAndAdd', t => { 373 | t.test('with a separate output vector', t => { 374 | result = vec2.scaleAndAdd(out, vecA, vecB, 0.5); 375 | 376 | t.equal_v2(out, [2.5, 4]); 377 | t.equal(result, out); 378 | t.equal_v2(vecA, [1, 2]); 379 | t.equal_v2(vecB, [3, 4]); 380 | 381 | t.end(); 382 | }); 383 | 384 | t.test('when vecA is the output vector', t => { 385 | result = vec2.scaleAndAdd(vecA, vecA, vecB, 0.5); 386 | 387 | t.equal_v2(vecA, [2.5, 4]); 388 | t.equal(result, vecA); 389 | t.equal_v2(vecB, [3, 4]); 390 | 391 | t.end(); 392 | }); 393 | 394 | t.test('when vecB is the output vector', t => { 395 | result = vec2.scaleAndAdd(vecB, vecA, vecB, 0.5); 396 | 397 | t.equal_v2(vecB, [2.5, 4]); 398 | t.equal(result, vecB); 399 | t.equal_v2(vecA, [1, 2]); 400 | 401 | t.end(); 402 | }); 403 | 404 | t.end(); 405 | }); 406 | 407 | t.test('distance', t => { 408 | t.equal(vec2.dist, vec2.distance); 409 | 410 | result = vec2.distance(vecA, vecB); 411 | t.approx(result, 2.828427); 412 | 413 | t.end(); 414 | }); 415 | 416 | t.test('squaredDistance', t => { 417 | t.equal(vec2.sqrDist, vec2.squaredDistance); 418 | 419 | result = vec2.squaredDistance(vecA, vecB); 420 | t.approx(result, 8); 421 | 422 | t.end(); 423 | }); 424 | 425 | t.test('length', t => { 426 | t.equal(vec2.len, vec2.length); 427 | 428 | result = vec2.length(vecA); 429 | t.approx(result, 2.236067); 430 | 431 | t.end(); 432 | }); 433 | 434 | t.test('squaredLength', t => { 435 | t.equal(vec2.sqrLen, vec2.squaredLength); 436 | 437 | result = vec2.squaredLength(vecA); 438 | t.equal(result, 5); 439 | 440 | t.end(); 441 | }); 442 | 443 | t.test('negate', t => { 444 | t.test('with a separate output vector', t => { 445 | result = vec2.negate(out, vecA); 446 | 447 | t.equal_v2(out, [-1, -2]); 448 | t.equal(result, out); 449 | t.equal_v2(vecA, [1, 2]); 450 | 451 | t.end(); 452 | }); 453 | 454 | t.test('when vecA is the output vector', t => { 455 | result = vec2.negate(vecA, vecA); 456 | 457 | t.equal_v2(vecA, [-1, -2]); 458 | t.equal(result, vecA); 459 | 460 | t.end(); 461 | }); 462 | 463 | t.end(); 464 | }); 465 | 466 | t.test('inverse', t => { 467 | vec2.set(vecA, 0.1, 0.2); 468 | 469 | result = vec2.inverse(out, vecA); 470 | t.equal_v2(out, [10, 5]); 471 | t.equal(result, out); 472 | t.equal_v2(vecA, [0.1, 0.2]); 473 | 474 | t.end(); 475 | }); 476 | 477 | t.test('inverseSafe', t => { 478 | vec2.set(vecA, 0.1, 0.2); 479 | vec2.set(vecB, 0.0000000001, -0.0000000001); 480 | 481 | result = vec2.inverseSafe(out, vecA); 482 | t.equal_v2(out, [10, 5]); 483 | t.equal(result, out); 484 | t.equal_v2(vecA, [0.1, 0.2]); 485 | 486 | result = vec2.inverseSafe(out, vecB); 487 | t.equal_v2(out, [0, 0]); 488 | t.equal(result, out); 489 | t.equal_v2(vecB, [0.0000000001, -0.0000000001]); 490 | 491 | t.end(); 492 | }); 493 | 494 | t.test('normalize', t => { 495 | t.beforeEach(done => { 496 | vec2.set(vecA, 5, 0); 497 | done(); 498 | }); 499 | 500 | t.test('with a separate output vector', t => { 501 | result = vec2.normalize(out, vecA); 502 | 503 | t.equal_v2(out, [1, 0]); 504 | t.equal(result, out); 505 | t.equal_v2(vecA, [5, 0]); 506 | 507 | t.end(); 508 | }); 509 | 510 | t.test('when vecA is the output vector', t => { 511 | result = vec2.normalize(vecA, vecA); 512 | 513 | t.equal_v2(vecA, [1, 0]); 514 | t.equal(result, vecA); 515 | 516 | t.end(); 517 | }); 518 | 519 | t.end(); 520 | }); 521 | 522 | t.test('dot', t => { 523 | result = vec2.dot(vecA, vecB); 524 | t.equal(result, 11); 525 | t.equal_v2(vecA, [1, 2]); 526 | t.equal_v2(vecB, [3, 4]); 527 | 528 | t.end(); 529 | }); 530 | 531 | t.test('cross', t => { 532 | let out3 = {}; 533 | result = vec2.cross(out3, vecA, vecB); 534 | 535 | t.deepEqual(out3, { x: 0, y: 0, z: -2}); 536 | t.equal(result, out3); 537 | t.equal_v2(vecA, [1, 2]); 538 | t.equal_v2(vecB, [3, 4]); 539 | 540 | t.end(); 541 | }); 542 | 543 | t.test('lerp', t => { 544 | t.test('with a separate output vector', t => { 545 | result = vec2.lerp(out, vecA, vecB, 0.5); 546 | 547 | t.equal_v2(out, [2, 3]); 548 | t.equal(result, out); 549 | t.equal_v2(vecA, [1, 2]); 550 | t.equal_v2(vecB, [3, 4]); 551 | 552 | t.end(); 553 | }); 554 | 555 | t.test('when vecA is the output vector', t => { 556 | result = vec2.lerp(vecA, vecA, vecB, 0.5); 557 | 558 | t.equal_v2(vecA, [2, 3]); 559 | t.equal(result, vecA); 560 | t.equal_v2(vecB, [3, 4]); 561 | 562 | t.end(); 563 | }); 564 | 565 | t.test('when vecB is the output vector', t => { 566 | result = vec2.lerp(vecB, vecA, vecB, 0.5); 567 | 568 | t.equal_v2(vecB, [2, 3]); 569 | t.equal(result, vecB); 570 | t.equal_v2(vecA, [1, 2]); 571 | 572 | t.end(); 573 | }); 574 | 575 | t.end(); 576 | }); 577 | 578 | t.test('random', t => { 579 | t.test('with no scale', t => { 580 | result = vec2.random(out); 581 | 582 | t.approx(vec2.length(out), 1.0); 583 | t.equal(result, out); 584 | 585 | t.end(); 586 | }); 587 | 588 | t.test('with a scale', t => { 589 | result = vec2.random(out, 5.0); 590 | 591 | t.approx(vec2.length(out), 5.0); 592 | t.equal(result, out); 593 | 594 | t.end(); 595 | }); 596 | 597 | t.end(); 598 | }); 599 | 600 | t.test('transformMat2', t => { 601 | let matA = { 602 | m00: 1, m01: 2, m02: 3, m03: 4 603 | }; 604 | 605 | t.test('with a separate output vector', t => { 606 | result = vec2.transformMat2(out, vecA, matA); 607 | 608 | t.equal_v2(out, [7, 10]); 609 | t.equal(result, out); 610 | t.equal_v2(vecA, [1, 2]); 611 | t.equal_m2(matA, [1, 2, 3, 4]); 612 | 613 | t.end(); 614 | }); 615 | 616 | t.test('when vecA is the output vector', t => { 617 | result = vec2.transformMat2(vecA, vecA, matA); 618 | 619 | t.equal_v2(vecA, [7, 10]); 620 | t.equal(result, vecA); 621 | t.equal_m2(matA, [1, 2, 3, 4]); 622 | 623 | t.end(); 624 | }); 625 | 626 | t.end(); 627 | }); 628 | 629 | t.test('transformMat23', t => { 630 | let matA = { 631 | m00: 1, m01: 2, m02: 3, m03: 4, m04: 5, m05: 6 632 | }; 633 | 634 | t.test('with a separate output vector', t => { 635 | result = vec2.transformMat23(out, vecA, matA); 636 | 637 | t.equal_v2(out, [12, 16]); 638 | t.equal(result, out); 639 | t.equal_v2(vecA, [1, 2]); 640 | t.equal_m23(matA, [1, 2, 3, 4, 5, 6]); 641 | 642 | t.end(); 643 | }); 644 | 645 | t.test('when vecA is the output vector', t => { 646 | result = vec2.transformMat23(vecA, vecA, matA); 647 | 648 | t.equal_v2(vecA, [12, 16]); 649 | t.equal(result, vecA); 650 | t.equal_m23(matA, [1, 2, 3, 4, 5, 6]); 651 | 652 | t.end(); 653 | }); 654 | 655 | t.end(); 656 | }); 657 | 658 | t.test('forEach', t => { 659 | let vecArray = []; 660 | t.beforeEach(done => { 661 | vecArray = [ 662 | 1, 2, 663 | 3, 4, 664 | 0, 0 665 | ]; 666 | 667 | done(); 668 | }); 669 | 670 | t.test('when performing operations that take no extra arguments', t => { 671 | result = vec2.forEach(vecArray, 0, 0, 0, vec2.normalize); 672 | 673 | t.approxArray(vecArray, [ 674 | 0.447214, 0.894427, 675 | 0.6, 0.8, 676 | 0, 0 677 | ]); 678 | t.equal(result, vecArray); 679 | 680 | t.end(); 681 | }); 682 | 683 | t.test('when performing operations that takes one extra arguments', t => { 684 | result = vec2.forEach(vecArray, 0, 0, 0, vec2.add, vecA); 685 | 686 | t.deepEqual(vecArray, [ 687 | 2, 4, 688 | 4, 6, 689 | 1, 2 690 | ]); 691 | t.equal(result, vecArray); 692 | t.equal_v2(vecA, [1, 2]); 693 | 694 | t.end(); 695 | }); 696 | 697 | t.test('when specifying an offset', t => { 698 | result = vec2.forEach(vecArray, 0, 2, 0, vec2.add, vecA); 699 | 700 | t.deepEqual(vecArray, [ 701 | 1, 2, 702 | 4, 6, 703 | 1, 2 704 | ]); 705 | t.equal(result, vecArray); 706 | t.equal_v2(vecA, [1, 2]); 707 | 708 | t.end(); 709 | }); 710 | 711 | t.test('when specifying a count', t => { 712 | result = vec2.forEach(vecArray, 0, 0, 2, vec2.add, vecA); 713 | 714 | t.deepEqual(vecArray, [ 715 | 2, 4, 716 | 4, 6, 717 | 0, 0 718 | ]); 719 | t.equal(result, vecArray); 720 | t.equal_v2(vecA, [1, 2]); 721 | 722 | t.end(); 723 | }); 724 | 725 | t.test('when specifying a stride', t => { 726 | result = vec2.forEach(vecArray, 4, 0, 0, vec2.add, vecA); 727 | 728 | t.deepEqual(vecArray, [ 729 | 2, 4, 730 | 3, 4, 731 | 1, 2 732 | ]); 733 | t.equal(result, vecArray); 734 | t.equal_v2(vecA, [1, 2]); 735 | 736 | t.end(); 737 | }); 738 | 739 | t.test('when calling a function that does not modify the out variable', t => { 740 | result = vec2.forEach(vecArray, 0, 0, 0, function (out, vec) { }); 741 | 742 | t.deepEqual(vecArray, [ 743 | 1, 2, 744 | 3, 4, 745 | 0, 0, 746 | ]); 747 | t.equal(result, vecArray); 748 | 749 | t.end(); 750 | }); 751 | 752 | t.end(); 753 | }); 754 | 755 | t.test('str', t => { 756 | result = vec2.str(vecA); 757 | 758 | t.equal(result, 'vec2(1, 2)'); 759 | t.end(); 760 | }); 761 | 762 | t.test('array', t => { 763 | result = vec2.array([], vecA); 764 | 765 | t.deepEqual(result, new Float32Array([1, 2])); 766 | 767 | t.end(); 768 | }); 769 | 770 | t.test('exactEquals', t => { 771 | vec2.set(vecA, 0, 1); 772 | vec2.set(vecB, 0, 1); 773 | let vecC = vec2.new(0, 1); 774 | 775 | let r0 = vec2.exactEquals(vecA, vecB); 776 | let r1 = vec2.exactEquals(vecA, vecC); 777 | 778 | t.equal(r0, true); 779 | t.equal(r1, true); 780 | t.equal_v2(vecA, [0, 1]); 781 | t.equal_v2(vecB, [0, 1]); 782 | 783 | t.end(); 784 | }); 785 | 786 | t.test('equals', t => { 787 | vec2.set(vecA, 0, 1); 788 | vec2.set(vecB, 0, 1); 789 | let vecC = vec2.new(1, 2); 790 | let vecD = vec2.new(1e-16, 1); 791 | let r0 = vec2.equals(vecA, vecB); 792 | let r1 = vec2.equals(vecA, vecC); 793 | let r2 = vec2.equals(vecA, vecD); 794 | 795 | t.equal(r0, true); 796 | t.equal(r1, false); 797 | t.equal(r2, true); 798 | t.equal_v2(vecA, [0, 1]); 799 | t.equal_v2(vecB, [0, 1]); 800 | 801 | t.end(); 802 | }); 803 | 804 | t.test('JSON.stringify', t => { 805 | t.equal( 806 | JSON.stringify({ vecA, vecB }), 807 | '{"vecA":[1,2],"vecB":[3,4]}' 808 | ); 809 | 810 | t.end(); 811 | }); 812 | 813 | t.end(); 814 | }); --------------------------------------------------------------------------------