├── demo ├── assets │ ├── readme.md │ └── matrix.wasm ├── index.tsx ├── base.scss ├── index.html ├── testBenchmark.ts ├── testRealworld.ts ├── compare ├── App.tsx └── tests.ts ├── .gitignore ├── postcss.config.js ├── Cargo.toml ├── src ├── lib.rs ├── common.rs ├── utils.rs ├── matrix2.rs ├── matrix2d.rs ├── vector2.rs ├── vector4.rs ├── vector3.rs ├── quaternion.rs ├── matrix3.rs └── quaternion2.rs ├── tsconfig.json ├── LICENSE ├── .gitattributes ├── server.dev.js ├── tslintConfig.js ├── karma.config.js ├── Benchmark.md ├── package.json ├── webpack.dev.config.js ├── webpack.pd.config.js ├── spec ├── spec-helper.ts ├── mat2d-spec.ts ├── mat2-spec.ts └── mat3-spec.ts ├── convert └── convert.js ├── wasm-opt.js ├── README.md ├── Cargo.lock └── tslint.json /demo/assets/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/assets/matrix.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtysky/gl-matrix-wasm/HEAD/demo/assets/matrix.wasm -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | node_modules 4 | *.log 5 | dist 6 | pkg 7 | bin 8 | .vscode 9 | convert/rust 10 | lib 11 | reports 12 | 13 | /target 14 | **/*.rs.bk 15 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-smart-import')({ /* ...options */ }), 4 | require('precss')({ /* ...options */ }), 5 | require('autoprefixer')({ /* ...options */ }) 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /demo/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : index.tsx 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 2018-6-8 15:52:44 5 | * @Description: 6 | */ 7 | import * as React from 'react'; 8 | import * as ReactDOM from 'react-dom'; 9 | 10 | import App from './App'; 11 | import './base.scss'; 12 | 13 | ReactDOM.render( 14 | , 15 | document.getElementById('container') 16 | ); 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gl-matrix-wasm" 3 | version = "0.1.0" 4 | authors = ["dtysky "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies.wasm-bindgen] 11 | version = "0.2.45" 12 | features = [ 13 | "nightly" 14 | ] 15 | 16 | [profile.release] 17 | # Tell `rustc` to optimize for small code size. 18 | opt-level = "s" 19 | lto = true 20 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : lib.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | // #[macro_use] 8 | // pub mod utils; 9 | pub mod common; 10 | pub mod matrix2; 11 | pub mod matrix2d; 12 | pub mod matrix3; 13 | pub mod matrix4; 14 | pub mod quaternion; 15 | pub mod quaternion2; 16 | pub mod vector2; 17 | pub mod vector3; 18 | pub mod vector4; 19 | -------------------------------------------------------------------------------- /demo/base.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : base.scss 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 2018-6-8 16:03:33 5 | * @Description: 6 | */ 7 | html, body, #container { 8 | padding: 0; 9 | margin: 0; 10 | outline: none; 11 | } 12 | 13 | #container { 14 | padding: 2% 10%; 15 | width: 80%; 16 | 17 | .test { 18 | border-top: 1px solid black; 19 | 20 | &-js { 21 | color: red; 22 | } 23 | 24 | &-wasm { 25 | color: green; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : common.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | pub static EPSILON: f32 = 0.0001; 10 | 11 | pub static PI: f32 = 3.141592653589793; 12 | 13 | pub static INFINITY: f32 = 1.0_f32 / 0.0_f32; 14 | 15 | pub static NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; 16 | 17 | #[wasm_bindgen] 18 | extern "C" { 19 | #[wasm_bindgen(js_namespace = Math)] 20 | fn random() -> f32; 21 | } 22 | 23 | pub fn RANDOM() -> f32 { 24 | random() 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "sourceMap": true, 5 | "noImplicitAny": false, 6 | "module": "esnext", 7 | "target": "es5", 8 | "jsx": "react", 9 | "lib": ["es2017", "dom"], 10 | "skipLibCheck": true, 11 | "baseUrl": "./", 12 | "paths": { 13 | "gl-matrix-wasm/pkg/gl_matrix_wasm.split": ["./pkg/gl_matrix_wasm.d.ts"], 14 | "gl-matrix-wasm": ["./pkg/gl_matrix_wasm.d.ts"] 15 | } 16 | }, 17 | "include": [ 18 | "pkg/**/*.ts", 19 | "spec/**/*.ts", 20 | "demo/**/*.tsx", 21 | "demo/**/*.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : utils.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::vector3::*; 10 | 11 | #[wasm_bindgen] 12 | extern "C" { 13 | #[wasm_bindgen(js_namespace = console)] 14 | fn log(s: &str); 15 | #[wasm_bindgen(js_namespace = console, js_name = log)] 16 | fn log_u32(a: u32); 17 | #[wasm_bindgen(js_namespace = console, js_name = log)] 18 | fn log_many(a: &str, b: &str); 19 | } 20 | 21 | #[macro_export] 22 | macro_rules! console_log { 23 | ($($t:tt)*) => (log(&format_args!($($t)*).to_string())) 24 | } 25 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gl-matrix-wasm 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/testBenchmark.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : testBenchmark.ts 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 6/15/2019, 11:33:09 AM 5 | * @Description: 6 | */ 7 | import * as benchmark from 'benchmark'; 8 | 9 | export default async function testBenchmark(): Promise { 10 | const tests = (await import('./tests')).tests; 11 | 12 | const suite = new benchmark.Suite(); 13 | 14 | tests.forEach(t => { 15 | suite.add(`${t.name}(WASM)`, t.wasm); 16 | suite.add(`${t.name}(JS)`, t.js); 17 | }); 18 | 19 | let result = ''; 20 | return new Promise(resolve => { 21 | suite 22 | .on('cycle', function(event) { 23 | const r = String(event.target) + '\n'; 24 | result += r; 25 | console.log(r); 26 | }) 27 | .on('complete', () => { 28 | resolve(result); 29 | }) 30 | .run({async: true}); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /demo/testRealworld.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : testRealworld.ts 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 6/15/2019, 11:33:20 AM 5 | * @Description: 6 | */ 7 | export async function test(t: {name: string, js: Function, wasm: Function}, times: number) { 8 | let ts = performance.now(); 9 | const result = {name: t.name, js: 0, wasm: 0}; 10 | for (let index = 0; index < times; index += 1) { 11 | t.wasm(); 12 | } 13 | result.wasm = performance.now() - ts; 14 | 15 | for (let index = 0; index < times; index += 1) { 16 | t.js(); 17 | } 18 | result.js = performance.now() - ts; 19 | 20 | return result; 21 | } 22 | 23 | export default async function testRealworld(times: number): Promise<{name: string, js: number, wasm: number}[]> { 24 | const tests = (await import('./tests')).tests; 25 | 26 | const result = []; 27 | 28 | return new Promise(resolve => { 29 | tests.forEach(async (t, index) => { 30 | const r = await test(t, times); 31 | result.push(r); 32 | 33 | if (index === tests.length - 1) { 34 | resolve(result); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tianyu Dai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # behavior for image files 8 | # 9 | # image files are treated as binary by default. 10 | ############################################################################### 11 | *.jpg binary 12 | *.png binary 13 | *.gif binary 14 | *.bmp binary 15 | *.bin binary 16 | *.gltf binary 17 | 18 | ############################################################################### 19 | # diff behavior for common document formats 20 | # 21 | # Convert binary document formats to text before diffing them. This feature 22 | # is only available from the command line. Turn it on by uncommenting the 23 | # entries below. 24 | ############################################################################### 25 | #*.doc diff=astextplain 26 | #*.DOC diff=astextplain 27 | #*.docx diff=astextplain 28 | #*.DOCX diff=astextplain 29 | #*.dot diff=astextplain 30 | #*.DOT diff=astextplain 31 | #*.pdf diff=astextplain 32 | #*.PDF diff=astextplain 33 | #*.rtf diff=astextplain 34 | #*.RTF diff=astextplain 35 | spec/*.ts linguist-detectable=false 36 | -------------------------------------------------------------------------------- /server.dev.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : server.dev.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 2018-6-8 15:57:09 6 | * @Description: 7 | */ 8 | const path = require('path'); 9 | const fs = require('fs'); 10 | const webpack = require('webpack'); 11 | const WebpackDevServer = require('webpack-dev-server'); 12 | 13 | const config = require('./webpack.dev.config'); 14 | const express = require('express'); 15 | const app = new express(); 16 | const port = 8888; 17 | const proxyPort = port + 1; 18 | 19 | app.use('/assets', 20 | express.static(path.resolve(__dirname, './demo/assets')) 21 | ); 22 | 23 | if (process.env.DEV_BUILD) { 24 | config.plugins.splice(3, 1); 25 | } 26 | 27 | const devServer = () => { 28 | const server = new WebpackDevServer(webpack(config), { 29 | compress: true, 30 | progress: true, 31 | hot: true, 32 | open: true, 33 | publicPath: config.output.publicPath, 34 | contentBase: path.resolve(__dirname), 35 | watchContentBase: false, 36 | watchOptions: { 37 | ignored: ['node_modules', 'target/**/*'] 38 | }, 39 | https: false, 40 | overlay: true, 41 | historyApiFallback: true, 42 | proxy: [{ 43 | context: ['/assets'], 44 | target: `http://localhost:${proxyPort}` 45 | }] 46 | }); 47 | 48 | server.listen(port, '0.0.0.0', (error) => { 49 | if (error) { 50 | console.log('webpack dev server failed', error); 51 | } 52 | console.info('==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.', port, port); 53 | }); 54 | } 55 | 56 | app.listen(proxyPort, function(error) { 57 | if (error) { 58 | console.error(`Start proxy server error.`); 59 | } else { 60 | console.info('Proxy server for assets is started...'); 61 | } 62 | }); 63 | 64 | devServer(); 65 | -------------------------------------------------------------------------------- /demo/compare: -------------------------------------------------------------------------------- 1 | const m1 = math.Matrix4.fromValues(7, 3, 6, 9, 2, 3, 2, 5, 1, 9, 5, 8, 3, 7, 2, 2); 2 | const m2 = math.Matrix4.fromValues(2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1); 3 | const m3 = math.Matrix4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); 4 | const m1j = mathJS.mat4.create(); 5 | const m2j = mathJS.mat4.create(); 6 | const m3j = mathJS.mat4.create(); 7 | 8 | const memory = new WebAssembly.Memory({ initial: 256 }); 9 | const imports = { 10 | global: { 11 | NaN: 5, 12 | Infinity: 6 13 | }, 14 | env: { 15 | memoryBase: 0, 16 | memory: memory, 17 | } 18 | }; 19 | let r: any = await fetch('/assets/matrix.wasm'); 20 | r = await r.arrayBuffer(); 21 | console.log(imports); 22 | r = await WebAssembly.instantiate(r, imports); 23 | const ins = r.instance; 24 | console.log(r); 25 | const multiply = ins.exports.__Z8multiplyPfS_S_; 26 | 27 | const a = new Float32Array(memory.buffer, 1048576, 16); 28 | const b = new Float32Array(memory.buffer, 1048576 + 18 * 4, 16); 29 | const c = new Float32Array(memory.buffer, 1048576 + 36 * 4, 16); 30 | 31 | a.set([7, 3, 6, 9, 2, 3, 2, 5, 1, 9, 5, 8, 3, 7, 2, 2]); 32 | b.set([2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1]); 33 | c.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); 34 | 35 | const runTimes = 10000; 36 | let ts = performance.now(); 37 | for (let index = 0; index < runTimes; index++) { 38 | math.Matrix4.multiply(m3, m1, m2); 39 | } 40 | console.log('RUST', performance.now() - ts); 41 | ts = performance.now(); 42 | for (let i = 0; i < runTimes; i++) { 43 | multiply(1048576 + 36 * 4, 1048576, 1048576 + 18 * 4); 44 | } 45 | console.log('CPP', performance.now() - ts); 46 | ts = performance.now(); 47 | for (let index = 0; index < runTimes; index++) { 48 | mathJS.mat4.multiply(m1j, m2j, m3j); 49 | } 50 | console.log('JS', performance.now() - ts); -------------------------------------------------------------------------------- /tslintConfig.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : tslintConfig.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 2018-5-10 16:54:04 6 | * @Description: 7 | */ 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | 11 | module.exports = { 12 | configuration: { 13 | rulesDirectory: path.resolve(__dirname, 'node_modules/tslint-microsoft-contrib'), 14 | configuration: JSON.parse(fs.readFileSync(path.resolve(__dirname, 'tslint.json'))) 15 | }, 16 | 17 | // enables type checked rules like 'for-in-array' 18 | // uses tsconfig.json from current working directory 19 | typeCheck: true, 20 | 21 | // can specify a custom config file relative to current directory 22 | // 'tslint-custom.json' 23 | configFile: false, 24 | 25 | // tslint errors are displayed by default as warnings 26 | // set emitErrors to true to display them as errors 27 | emitErrors: false, 28 | 29 | // tslint does not interrupt the compilation by default 30 | // if you want any file with tslint errors to fail 31 | // set failOnHint to true 32 | failOnHint: true, 33 | 34 | // name of your formatter (optional) 35 | // formatter: 'yourformatter', 36 | 37 | // path to directory containing formatter (optional) 38 | // formattersDirectory: 'node_modules/tslint-loader/formatters/', 39 | 40 | // These options are useful if you want to save output to files 41 | // for your continuous integration server 42 | fileOutput: { 43 | // The directory where each file's report is saved 44 | dir: './report/tslint', 45 | 46 | // The extension to use for each report's filename. Defaults to 'txt' 47 | ext: 'xml', 48 | 49 | // If true, all files are removed from the report directory at the beginning of run 50 | clean: true, 51 | 52 | // A string to include at the top of every report file. 53 | // Useful for some report formats. 54 | header: '\n', 55 | 56 | // A string to include at the bottom of every report file. 57 | // Useful for some report formats. 58 | footer: '' 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : karma.conf..js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Link : dtysky.moe 6 | * @Date : 2019/6/7 下午9:41:32 7 | */ 8 | const path = require('path'); 9 | const outPath = path.resolve(__dirname, 'dist'); 10 | 11 | module.exports = function(config) { 12 | config.set({ 13 | 14 | basePath: '', 15 | 16 | frameworks: ['mocha'], 17 | 18 | files: [ 19 | 'spec/**/*-spec.ts' 20 | ], 21 | 22 | port: 9876, 23 | 24 | logLevel: config.LOG_INFO, 25 | 26 | colors: true, 27 | 28 | autoWatch: true, 29 | 30 | browsers: ['Chrome'], 31 | 32 | webpack: { 33 | output: { 34 | path: outPath, 35 | filename: '[name].[hash].js', 36 | webassemblyModuleFilename: '[modulehash].wasm', 37 | publicPath: './' 38 | }, 39 | 40 | resolve: { 41 | extensions: ['.ts', '.tsx', '.js', '.md', '.wasm'] 42 | }, 43 | 44 | mode: 'development', 45 | 46 | module: { 47 | rules: [ 48 | { 49 | enforce: 'pre', 50 | test: /\.(ts|js)$/, 51 | use: [ 52 | { 53 | loader: 'awesome-typescript-loader' 54 | } 55 | ], 56 | exclude: /node_modules/ 57 | }, 58 | { 59 | test: /\.wasm$/, 60 | type: 'webassembly/experimental' 61 | }, 62 | { 63 | test: /\.(css|scss)$/, 64 | use: [ 65 | { 66 | loader: 'style-loader' 67 | }, 68 | { 69 | loader: 'css-loader' 70 | }, 71 | { 72 | loader: 'postcss-loader' 73 | }, 74 | { 75 | loader: 'sass-loader' 76 | } 77 | ] 78 | }, 79 | { 80 | test: /\.md$/, 81 | use: [ 82 | { 83 | loader: 'raw-loader' 84 | } 85 | ] 86 | } 87 | ] 88 | }, 89 | 90 | optimization: { 91 | splitChunks: { 92 | minChunks: 2 93 | } 94 | }, 95 | }, 96 | 97 | preprocessors: { 98 | 'spec/**/*.ts': ['webpack'] 99 | }, 100 | 101 | karmaTypescriptConfig: { 102 | reports: { 103 | 'html': 'reports/coverage' 104 | } 105 | }, 106 | 107 | singleRun: true 108 | }) 109 | }; 110 | -------------------------------------------------------------------------------- /Benchmark.md: -------------------------------------------------------------------------------- 1 | # Benchmark(Matrix4, 2015 RMBP, Chrome) 2 | 3 | multiply(WASM) x 16,854,499 ops/sec ±1.69% (57 runs sampled) 4 | multiply(JS) x 11,531,234 ops/sec ±1.68% (57 runs sampled) 5 | 6 | fromRotationTranslationScale(WASM) x 23,856,918 ops/sec ±1.92% (59 runs sampled) 7 | fromRotationTranslationScale(JS) x 19,409,172 ops/sec ±2.04% (57 runs sampled) 8 | 9 | create(WASM) x 777,045 ops/sec ±1.40% (42 runs sampled) 10 | create(JS) x 14,290,306 ops/sec ±1.97% (60 runs sampled) 11 | 12 | clone(WASM) x 639,096 ops/sec ±4.35% (44 runs sampled) 13 | clone(JS) x 10,756,018 ops/sec ±2.17% (58 runs sampled) 14 | 15 | copy(WASM) x 30,181,487 ops/sec ±1.95% (58 runs sampled) 16 | copy(JS) x 21,462,134 ops/sec ±1.81% (58 runs sampled) 17 | 18 | fromValues(WASM) x 535,998 ops/sec ±3.53% (37 runs sampled) 19 | fromValues(JS) x 12,594,008 ops/sec ±2.13% (60 runs sampled) 20 | 21 | set(WASM) x 14,491,076 ops/sec ±1.85% (59 runs sampled) 22 | set(JS) x 26,017,141 ops/sec ±1.84% (58 runs sampled) 23 | 24 | getElements(WASM) x 82,019,949 ops/sec ±5.11% (52 runs sampled) 25 | getElements(JS) x 78,669,371 ops/sec ±5.39% (49 runs sampled) 26 | 27 | add(WASM) x 23,637,588 ops/sec ±1.77% (58 runs sampled) 28 | add(JS) x 13,540,795 ops/sec ±1.55% (56 runs sampled) 29 | 30 | sub(WASM) x 24,021,824 ops/sec ±1.76% (58 runs sampled) 31 | sub(JS) x 14,201,044 ops/sec ±1.29% (59 runs sampled) 32 | 33 | identity(WASM) x 35,785,034 ops/sec ±2.42% (57 runs sampled) 34 | identity(JS) x 25,593,068 ops/sec ±2.03% (55 runs sampled) 35 | 36 | transpose(WASM) x 27,352,164 ops/sec ±2.03% (58 runs sampled) 37 | transpose(JS) x 20,822,856 ops/sec ±2.05% (57 runs sampled) 38 | 39 | invert(WASM) x 17,258,766 ops/sec ±1.57% (59 runs sampled) 40 | invert(JS) x 11,898,150 ops/sec ±1.42% (58 runs sampled) 41 | 42 | adjoint(WASM) x 18,084,176 ops/sec ±1.68% (60 runs sampled) 43 | adjoint(JS) x 11,614,897 ops/sec ±1.07% (59 runs sampled) 44 | 45 | determinant(WASM) x 28,644,773 ops/sec ±1.80% (58 runs sampled) 46 | determinant(JS) x 28,516,174 ops/sec ±2.55% (57 runs sampled) 47 | 48 | translate(WASM) x 22,924,839 ops/sec ±1.59% (60 runs sampled) 49 | translate(JS) x 15,140,558 ops/sec ±2.11% (55 runs sampled) 50 | 51 | scale(WASM) x 25,101,108 ops/sec ±2.20% (56 runs sampled) 52 | scale(JS) x 17,273,866 ops/sec ±1.68% (58 runs sampled) 53 | 54 | rotate(WASM) x 12,504,970 ops/sec ±1.23% (57 runs sampled) 55 | rotate(JS) x 8,130,842 ops/sec ±1.23% (59 runs sampled) 56 | 57 | fromRotationTranslationScaleOrigin(WASM) x 19,989,752 ops/sec ±1.85% (58 runs sampled) 58 | fromRotationTranslationScaleOrigin(JS) x 15,336,144 ops/sec ±1.21% (60 runs sampled) 59 | 60 | lookAt(WASM) x 23,311,849 ops/sec ±1.53% (59 runs sampled) 61 | lookAt(JS) x 17,898,447 ops/sec ±1.73% (58 runs sampled) 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gl-matrix-wasm", 3 | "version": "0.8.0", 4 | "description": "Port gl-matrix to WebAssembly by rust, wasm-bindgen and wasm-pack.", 5 | "main": "pkg/gl_matrix_wasm.js", 6 | "types": "pkg/gl_matrix_wasm.d.ts", 7 | "scripts": { 8 | "dev": "node server.dev.js", 9 | "demo-build": "npm run build && webpack -p --config webpack.pd.config.js", 10 | "build": "wasm-pack build --release --target web && node ./wasm-opt.js", 11 | "prepublish": "npm run build", 12 | "test-wsl": "export CHROME_BIN='/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe' && export TEMP='/mnt/c/temp' && karma start karma.config.js", 13 | "test": "karma start karma.config.js", 14 | "dev-build": "export DEV_BUILD=true && npm run build && node server.dev.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/dtysky/gl-matrix-wasm.git" 19 | }, 20 | "keywords": [ 21 | "Math", 22 | "3D", 23 | "Matrix", 24 | "gl-matrix" 25 | ], 26 | "authors": [ 27 | { 28 | "name": "Tianyu Dai (dtysky)", 29 | "email": "dtysky@outlook.com" 30 | } 31 | ], 32 | "license": "MIT", 33 | "files": [ 34 | "pkg", 35 | "README.md" 36 | ], 37 | "bugs": { 38 | "url": "https://github.com/dtysky/gl-matrix-wasm/issues" 39 | }, 40 | "homepage": "https://github.com/dtysky/gl-matrix-wasm#readme", 41 | "devDependencies": { 42 | "@types/benchmark": "^1.0.31", 43 | "@types/gl-matrix": "^2.4.5", 44 | "@types/mocha": "^5.2.6", 45 | "@types/node": "^6.0.52", 46 | "@types/react": "^16.3.13", 47 | "@types/react-dom": "^16.0.5", 48 | "@wasm-tool/wasm-pack-plugin": "^0.1.6", 49 | "autoprefixer": "^7.2.4", 50 | "awesome-typescript-loader": "^5.2.1", 51 | "binaryen": "^58.0.0", 52 | "clean-webpack-plugin": "^0.1.19", 53 | "compression-webpack-plugin": "^3.0.0", 54 | "copy-webpack-plugin": "^5.0.3", 55 | "css-loader": "^0.28.8", 56 | "es6-object-assign": "^1.1.0", 57 | "gl-matrix": "^3.0.0", 58 | "html-webpack-plugin": "^3.1.0", 59 | "karma": "^4.1.0", 60 | "karma-chrome-launcher": "^2.2.0", 61 | "karma-mocha": "^1.3.0", 62 | "mocha": "^5.0.0", 63 | "node-sass": "^4.12.0", 64 | "postcss-loader": "^2.0.10", 65 | "postcss-smart-import": "^0.7.6", 66 | "precss": "^2.0.0", 67 | "react": "^16.3.2", 68 | "react-dom": "^16.3.2", 69 | "react-hot-loader": "^3.1.3", 70 | "sass-loader": "^6.0.6", 71 | "stats.js": "^0.17.0", 72 | "style-loader": "^0.21.0", 73 | "tslint": "^4.1.1", 74 | "tslint-loader": "^3.5.3", 75 | "tslint-microsoft-contrib": "^4.0.0", 76 | "typescript": "^3.1.0", 77 | "webpack": "^4.28.4", 78 | "webpack-cli": "^3.1.2", 79 | "webpack-dev-server": "^3.1.0" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /demo/App.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : App.tsx 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 2018/12/11 下午10:29:56 5 | * @Description: 6 | */ 7 | import * as React from 'react'; 8 | 9 | import * as math from 'gl-matrix-wasm'; 10 | import testBenchmark from './testBenchmark'; 11 | import testRealworld, {test} from './testRealworld'; 12 | 13 | interface IStateTypes { 14 | [key: string]: {js: number, wasm: number}; 15 | } 16 | 17 | const rrName = 'Real world: (mul x 4 -> fromRTS x 1 -> getElements) x 1000 x 60 / s'; 18 | 19 | export default class App extends React.Component<{}, IStateTypes> { 20 | public state: IStateTypes = {}; 21 | private names: string[] = []; 22 | 23 | public async componentDidMount() { 24 | await math.init(); 25 | const {tests} = (await import('./tests')); 26 | this.names = tests.map(t => t.name); 27 | this.names.splice(0, 0, rrName); 28 | // const math = await import('gl-matrix-wasm/pkg/gl_matrix_wasm.split'); 29 | 30 | // const v1 = math.Vector4.fromValues(1, 0, 0, 0); 31 | // const v2 = math.Vector4.fromValues(0, 1, 0, 0); 32 | // const v3 = math.Vector4.fromValues(0, 0, 1, 0); 33 | // const v2j = mathJS.vec4.fromValues(0, 1, 0, 0); 34 | // const v1j = mathJS.vec4.fromValues(1, 0, 0, 0); 35 | // const v3j = mathJS.vec4.fromValues(0, 0, 1, 0); 36 | // console.log(math.Vector4.add(v1, v2, v3).elements); 37 | 38 | 39 | const state = {}; 40 | const result = await testRealworld(10000); 41 | result.forEach(r => { 42 | state[r.name] = {js: r.js, wasm: r.wasm}; 43 | }); 44 | this.setState(state); 45 | 46 | this.testEverySec(); 47 | } 48 | 49 | public testEverySec = async () => { 50 | const {multiplyTest, fromRotationTranslationScaleTest, getElementsTest} = (await import('./tests')); 51 | 52 | const result = await test({ 53 | name: rrName, 54 | js: () => { 55 | for (let index = 0; index < 4; index += 1) { 56 | multiplyTest.js(); 57 | } 58 | fromRotationTranslationScaleTest.js(); 59 | getElementsTest.js(); 60 | }, 61 | wasm: () => { 62 | for (let index = 0; index < 4; index += 1) { 63 | multiplyTest.wasm(); 64 | } 65 | fromRotationTranslationScaleTest.wasm(); 66 | getElementsTest.wasm(); 67 | } 68 | }, 1000 * 60); 69 | 70 | this.setState({[rrName]: result}); 71 | setTimeout(this.testEverySec, 1000); 72 | } 73 | 74 | public render() { 75 | return ( 76 | 77 | Matrix4(10000x) 78 | 79 | { 80 | this.names.map(name => this.state[name] && ( 81 | 82 | {name} 83 | Wasm: {this.state[name].wasm}ms 84 | JS: {this.state[name].js}ms 85 | 86 | )) 87 | } 88 | 89 | 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : webpack.config.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 2018-6-8 15:55:42 6 | * @Description: 7 | */ 8 | const webpack = require('webpack'); 9 | const path = require('path'); 10 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 11 | const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin'); 12 | 13 | module.exports = { 14 | entry: { 15 | main: [ 16 | 'react-hot-loader/patch', 17 | 'webpack-dev-server/client?/', 18 | 'webpack/hot/dev-server', 19 | path.resolve(__dirname, './demo/index.tsx') 20 | ] 21 | }, 22 | 23 | output: { 24 | path: path.resolve(__dirname), 25 | filename: 'main.js', 26 | webassemblyModuleFilename: '[modulehash].wasm', 27 | publicPath: '/' 28 | }, 29 | 30 | mode: 'development', 31 | 32 | resolve: { 33 | extensions: ['.ts', '.tsx', '.js', '.wasm'], 34 | alias: { 35 | 'gl-matrix-wasm/pkg/gl_matrix_wasm.split': '../pkg/gl_matrix_wasm', 36 | 'gl-matrix-wasm': '../pkg/gl_matrix_wasm' 37 | } 38 | }, 39 | 40 | externals: { 41 | 'fs': true, 42 | 'path': true, 43 | benchmark: 'Benchmark' 44 | }, 45 | 46 | module: { 47 | rules: [ 48 | { 49 | enforce: 'pre', 50 | test: /\.tsx?$/, 51 | use: [ 52 | { 53 | loader: 'react-hot-loader/webpack' 54 | }, 55 | { 56 | loader: 'awesome-typescript-loader' 57 | }, 58 | { 59 | loader: 'tslint-loader', 60 | query: { 61 | configFile: path.resolve(__dirname, './tslintConfig.js') 62 | } 63 | } 64 | ], 65 | exclude: /node_modules/ 66 | }, 67 | { 68 | test: /\.wasm$/, 69 | type: 'webassembly/experimental' 70 | }, 71 | { 72 | test: /\.(css|scss)$/, 73 | use: [ 74 | { 75 | loader: 'style-loader' 76 | }, 77 | { 78 | loader: 'css-loader' 79 | }, 80 | { 81 | loader: 'postcss-loader' 82 | }, 83 | { 84 | loader: 'sass-loader' 85 | } 86 | ] 87 | }, 88 | { 89 | test: /\.md$/, 90 | use: [ 91 | { 92 | loader: 'raw-loader' 93 | } 94 | ] 95 | }, 96 | { 97 | test: /\.(png|jpg|gif|svg|mp4)$/, 98 | use: { 99 | loader: 'url-loader', 100 | query: { 101 | limit: 15000 102 | } 103 | } 104 | } 105 | ] 106 | }, 107 | 108 | plugins: [ 109 | new webpack.DefinePlugin({ 110 | NODE_ENV: JSON.stringify('development') 111 | }), 112 | new webpack.HotModuleReplacementPlugin(), 113 | new HtmlWebpackPlugin({ 114 | template: './demo/index.html' 115 | }), 116 | new WasmPackPlugin({ 117 | crateDirectory: path.resolve(__dirname, './'), 118 | forceMode: 'development' 119 | }) 120 | ] 121 | }; 122 | -------------------------------------------------------------------------------- /webpack.pd.config.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : webpack.pd.confg.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 2018-6-8 16:18:56 6 | * @Description: 7 | */ 8 | const webpack = require('webpack'); 9 | const path = require('path'); 10 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 11 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 12 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 13 | const CompressionPlugin = require('compression-webpack-plugin'); 14 | 15 | const outPath = path.resolve(__dirname, 'dist'); 16 | 17 | module.exports = { 18 | entry: { 19 | main: path.resolve(__dirname, './demo/index.tsx'), 20 | 'react-packet': ['react', 'react-dom', 'gl-matrix'] 21 | }, 22 | 23 | output: { 24 | path: outPath, 25 | filename: '[name].[hash].js', 26 | webassemblyModuleFilename: '[modulehash].wasm', 27 | publicPath: './' 28 | }, 29 | 30 | resolve: { 31 | extensions: ['.ts', '.tsx', '.js', '.md', '.wasm'], 32 | alias: { 33 | 'gl-matrix-wasm/pkg/gl_matrix_wasm.split': '../pkg/gl_matrix_wasm.split', 34 | 'gl-matrix-wasm': '../pkg/gl_matrix_wasm' 35 | } 36 | }, 37 | 38 | mode: 'production', 39 | 40 | externals: { 41 | 'fs': true, 42 | 'path': true, 43 | benchmark: 'Benchmark' 44 | }, 45 | 46 | module: { 47 | rules: [ 48 | { 49 | enforce: 'pre', 50 | test: /\.tsx?$/, 51 | use: [ 52 | { 53 | loader: 'awesome-typescript-loader' 54 | } 55 | ], 56 | exclude: /node_modules/ 57 | }, 58 | { 59 | test: /\.wasm$/, 60 | type: 'webassembly/experimental' 61 | }, 62 | { 63 | test: /\.(css|scss)$/, 64 | use: [ 65 | { 66 | loader: 'style-loader' 67 | }, 68 | { 69 | loader: 'css-loader' 70 | }, 71 | { 72 | loader: 'postcss-loader' 73 | }, 74 | { 75 | loader: 'sass-loader' 76 | } 77 | ] 78 | }, 79 | { 80 | test: /\.md$/, 81 | use: [ 82 | { 83 | loader: 'raw-loader' 84 | } 85 | ] 86 | }, 87 | { 88 | test: /\.(png|jpg|gif|svg|mp4)$/, 89 | use: { 90 | loader: 'url-loader', 91 | query: { 92 | limit: 15000 93 | } 94 | } 95 | } 96 | ] 97 | }, 98 | 99 | optimization: { 100 | splitChunks: { 101 | minChunks: 2, 102 | cacheGroups: { // 这里开始设置缓存的 chunks 103 | 'react-packet': { // key 为entry中定义的 入口名称 104 | chunks: 'initial', // 必须三选一: "initial" | "all" | "async"(默认就是异步) 105 | test: /react/, // 正则规则验证,如果符合就提取 chunk 106 | name: 'react-packet', // 要缓存的 分隔出来的 chunk 名称 107 | enforce: true 108 | } 109 | } 110 | } 111 | }, 112 | 113 | plugins: [ 114 | new CleanWebpackPlugin( 115 | ['*'], 116 | {root: outPath} 117 | ), 118 | new CopyWebpackPlugin( 119 | [ 120 | { 121 | from: path.resolve(__dirname, './demo/assets'), 122 | to: path.resolve(__dirname, './dist/assets') 123 | } 124 | ] 125 | ), 126 | new webpack.DefinePlugin({ 127 | NODE_ENV: JSON.stringify('production') 128 | }), 129 | new HtmlWebpackPlugin({ 130 | template: './demo/index.html' 131 | }), 132 | new CompressionPlugin({ 133 | filename: "[path]", 134 | algorithm: "gzip", 135 | test: /\.js$|\.css$/, 136 | threshold: 10240, 137 | minRatio: 0.8 138 | }) 139 | ] 140 | }; 141 | -------------------------------------------------------------------------------- /demo/tests.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : tests.ts 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Date : 6/15/2019, 2:40:30 PM 5 | * @Description: 6 | */ 7 | import * as math from 'gl-matrix-wasm'; 8 | import * as mathJS from 'gl-matrix'; 9 | 10 | const ms = [ 11 | math.Matrix4.fromValues(7, 3, 6, 9, 2, 3, 2, 5, 1, 9, 5, 8, 3, 7, 2, 2), 12 | math.Matrix4.fromValues(2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1), 13 | math.Matrix4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) 14 | ]; 15 | const v = math.Vector3.fromValues(1, 1, 1); 16 | const q = math.Quaternion.fromValues(0, 0, 0, 1); 17 | 18 | const mjs = [ 19 | mathJS.mat4.fromValues(7, 3, 6, 9, 2, 3, 2, 5, 1, 9, 5, 8, 3, 7, 2, 2), 20 | mathJS.mat4.fromValues(2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1), 21 | mathJS.mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) 22 | ]; 23 | const vj = mathJS.vec3.fromValues(1, 1, 1); 24 | const qj = mathJS.quat.fromValues(0, 0, 0, 1); 25 | 26 | export const multiplyTest = { 27 | name: 'multiply', 28 | wasm: () => math.Matrix4.multiply(ms[2], ms[0], ms[1]), 29 | js: () => mathJS.mat4.multiply(mjs[2], mjs[0], mjs[1]) 30 | }; 31 | 32 | export const fromRotationTranslationScaleTest = { 33 | name: 'fromRotationTranslationScale', 34 | wasm: () => math.Matrix4.fromRotationTranslationScale(ms[1], q, v, v), 35 | js: () => mathJS.mat4.fromRotationTranslationScale(mjs[1], qj, vj, vj) 36 | }; 37 | 38 | export const getElementsTest = { 39 | name: 'getElements', 40 | wasm: () => ms[1].elements, 41 | js: () => mjs[1] 42 | }; 43 | 44 | export const tests = [ 45 | multiplyTest, 46 | fromRotationTranslationScaleTest, 47 | { 48 | name: 'create', 49 | wasm: () => math.Matrix4.create(), 50 | js: () => mathJS.mat4.create() 51 | }, 52 | { 53 | name: 'clone', 54 | wasm: () => math.Matrix4.clone(ms[0]), 55 | js: () => mathJS.mat4.clone(mjs[0]) 56 | }, 57 | { 58 | name: 'copy', 59 | wasm: () => math.Matrix4.copy(ms[1], ms[0]), 60 | js: () => mathJS.mat4.copy(mjs[1], mjs[0]) 61 | }, 62 | { 63 | name: 'fromValues', 64 | wasm: () => math.Matrix4.fromValues(2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1), 65 | js: () => mathJS.mat4.fromValues(2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1) 66 | }, 67 | { 68 | name: 'set', 69 | wasm: () => math.Matrix4.set(ms[1], 2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1), 70 | js: () => mathJS.mat4.set(mjs[1], 2, 5, 7, 8, 4, 8, 3, 9, 2, 5, 4, 9, 5, 6, 3, 1) 71 | }, 72 | getElementsTest, 73 | { 74 | name: 'add', 75 | wasm: () => math.Matrix4.add(ms[2], ms[0], ms[1]), 76 | js: () => mathJS.mat4.add(mjs[2], mjs[0], mjs[1]) 77 | }, 78 | { 79 | name: 'sub', 80 | wasm: () => math.Matrix4.sub(ms[2], ms[0], ms[1]), 81 | js: () => mathJS.mat4.sub(mjs[2], mjs[0], mjs[1]) 82 | }, 83 | { 84 | name: 'identity', 85 | wasm: () => math.Matrix4.identity(ms[1]), 86 | js: () => mathJS.mat4.identity(mjs[1]) 87 | }, 88 | { 89 | name: 'transpose', 90 | wasm: () => math.Matrix4.transpose(ms[1], ms[0]), 91 | js: () => mathJS.mat4.transpose(mjs[1], mjs[0]) 92 | }, 93 | { 94 | name: 'invert', 95 | wasm: () => math.Matrix4.invert(ms[1], ms[0]), 96 | js: () => mathJS.mat4.invert(mjs[1], mjs[0]) 97 | }, 98 | { 99 | name: 'adjoint', 100 | wasm: () => math.Matrix4.adjoint(ms[1], ms[0]), 101 | js: () => mathJS.mat4.adjoint(mjs[1], mjs[0]) 102 | }, 103 | { 104 | name: 'determinant', 105 | wasm: () => math.Matrix4.determinant(ms[1]), 106 | js: () => mathJS.mat4.determinant(mjs[1]) 107 | }, 108 | { 109 | name: 'translate', 110 | wasm: () => math.Matrix4.translate(ms[1], ms[0], v), 111 | js: () => mathJS.mat4.translate(mjs[1], mjs[0], vj) 112 | }, 113 | { 114 | name: 'scale', 115 | wasm: () => math.Matrix4.scale(ms[1], ms[0], v), 116 | js: () => mathJS.mat4.scale(mjs[1], mjs[0], vj) 117 | }, 118 | { 119 | name: 'rotate', 120 | wasm: () => math.Matrix4.rotate(ms[1], ms[0], Math.PI, v), 121 | js: () => mathJS.mat4.rotate(mjs[1], mjs[0], Math.PI, vj) 122 | }, 123 | { 124 | name: 'fromRotationTranslationScaleOrigin', 125 | wasm: () => math.Matrix4.fromRotationTranslationScaleOrigin(ms[1], q, v, v, v), 126 | js: () => mathJS.mat4.fromRotationTranslationScaleOrigin(mjs[1], qj, vj, vj, vj) 127 | }, 128 | { 129 | name: 'lookAt', 130 | wasm: () => math.Matrix4.lookAt(ms[1], v, v, v), 131 | js: () => mathJS.mat4.lookAt(mjs[1], vj, vj, vj) 132 | } 133 | ]; 134 | -------------------------------------------------------------------------------- /spec/spec-helper.ts: -------------------------------------------------------------------------------- 1 | const EPSILON = 0.00001; 2 | const assert = require('assert'); 3 | 4 | export function expect(e) { 5 | 6 | function expected(e, message, a) { 7 | assert.fail(e, a, `expected ${JSON.stringify(e)} ${message} ${JSON.stringify(a)}`); 8 | } 9 | 10 | return { 11 | toBe: function (a) { 12 | assert.strictEqual(e, a); 13 | }, 14 | toEqual: function (a) { 15 | Math.abs(e - a) <= EPSILON; 16 | }, 17 | toBeDefined: function () { 18 | assert.notStrictEqual(e, undefined); 19 | }, 20 | toBeTruthy: function () { 21 | assert(e); 22 | }, 23 | toBeFalsy: function () { 24 | assert(!e); 25 | }, 26 | toBeNull: function () { 27 | assert.strictEqual(e, null); 28 | }, 29 | not: { 30 | toBe: function (a) { 31 | assert.notStrictEqual(e, a); 32 | }, 33 | 34 | toBeEqualish: function (a) { 35 | if (typeof (e) == 'number') 36 | assert(Math.abs(e - a) >= EPSILON); 37 | 38 | if (e.ptr) { 39 | e = e.elements; 40 | } 41 | if (a.ptr) { 42 | a = a.elements; 43 | } 44 | if (e.length != a.length) 45 | return; 46 | for (let i = 0; i < e.length; i++) { 47 | if (isNaN(e[i]) !== isNaN(a[i])) 48 | return; 49 | if (Math.abs(e[i] - a[i]) >= EPSILON) 50 | return; 51 | } 52 | 53 | assert.fail(e, a); 54 | } 55 | 56 | }, 57 | toBeGreaterThan: function (a) { 58 | assert(e > a); 59 | }, 60 | toBeLessThan: function (a) { 61 | assert(e < a); 62 | }, 63 | /* 64 | Returns true if `actual` has the same length as `expected`, and 65 | if each element of both arrays is within 0.000001 of each other. 66 | This is a way to check for "equal enough" conditions, as a way 67 | of working around floating point imprecision. 68 | */ 69 | toBeEqualish: function (a) { 70 | if (typeof (e) == 'number') { 71 | if (isNaN(e) !== isNaN(a)) 72 | expected(e, "to be equalish to", a); 73 | if (Math.abs(e - a) >= EPSILON) 74 | expected(e, "to be equalish to", a); 75 | } 76 | 77 | if (e.ptr) { 78 | e = e.elements; 79 | } 80 | if (a.ptr) { 81 | a = a.elements; 82 | } 83 | if (e.length != a.length) 84 | assert.fail(e.length, a.length, "length mismatch"); 85 | 86 | 87 | for (let i = 0; i < e.length; i++) { 88 | if (isNaN(e[i]) !== isNaN(a[i])) { 89 | console.log('failed'); 90 | assert.fail(isNaN(e[i]), isNaN(a[i])); 91 | } 92 | if (Math.abs(e[i] - a[i]) >= EPSILON) { 93 | console.log('failed'); 94 | assert.fail(Math.abs(e[i] - a[i])); 95 | } 96 | } 97 | }, 98 | 99 | //Dual quaternions are very special & unique snowflakes 100 | toBeEqualishQuat2: function (a, epsilon?) { 101 | if (epsilon == undefined) epsilon = EPSILON; 102 | let allSignsFlipped = false; 103 | if (e.ptr) { 104 | e = e.elements; 105 | } 106 | if (a.ptr) { 107 | a = a.elements; 108 | } 109 | if (e.length != a.length) 110 | expected(e, "to have the same length as", a); 111 | 112 | if (e.ptr) { 113 | e = e.elements; 114 | } 115 | if (a.ptr) { 116 | a = a.elements; 117 | } 118 | 119 | for (let i = 0; i < e.length; i++) { 120 | if (isNaN(e[i]) !== isNaN(a[i])) 121 | expected(e, "to be equalish to", a); 122 | 123 | if (allSignsFlipped) { 124 | if (Math.abs(e[i] - (-a[i])) >= epsilon) 125 | expected(e, "to be equalish to", a); 126 | } else { 127 | if (Math.abs(e[i] - a[i]) >= epsilon) { 128 | allSignsFlipped = true; 129 | i = 0; 130 | } 131 | } 132 | } 133 | } 134 | }; 135 | }; 136 | -------------------------------------------------------------------------------- /convert/convert.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : converter.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 6/6/2019, 5:21:14 PM 6 | * @Description: 7 | */ 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | 11 | const glMatrixPath = path.resolve(__dirname, '../../gl-matrix/src'); 12 | 13 | const LUT = { 14 | 'vec2': 'Vector2', 15 | 'vec3': 'Vector3', 16 | 'vec4': 'Vector4', 17 | 'quat': 'Quaternion', 18 | 'quat2': 'Quaternion2', 19 | 'mat2': 'Matrix2', 20 | 'mat2d': 'Matrix2d', 21 | 'mat3': 'Matrix3', 22 | 'mat4': 'Matrix4', 23 | 'Number': 'f32' 24 | }; 25 | 26 | function walk(dir, callback) { 27 | fs.readdirSync(dir).forEach(file => { 28 | const filePath = path.join(dir, file); 29 | 30 | if (/index/.test(file) || /common/.test(file)) { 31 | return; 32 | } 33 | 34 | callback(filePath, file.replace(path.extname(file), '')); 35 | }); 36 | } 37 | 38 | function addHeader(content, name) { 39 | const isMat = /Matrix/.test(name); 40 | const isQuat = /Quat/.test(name); 41 | let size = 0; 42 | 43 | if (isQuat) { 44 | size = 4; 45 | } else { 46 | size = parseInt(/(\d+)/g.exec(name)[1], 10); 47 | if (isMat) { 48 | size *= size; 49 | } 50 | } 51 | const values = new Array(size).fill(0).map((_, i) => i); 52 | 53 | return `use wasm_bindgen::prelude::*; 54 | 55 | use super::common::*; 56 | 57 | #[wasm_bindgen] 58 | pub struct ${name}( 59 | ${values.map(i => 'pub f32').join(',\n')} 60 | ); 61 | 62 | #[wasm_bindgen] 63 | impl ${name} { 64 | #[wasm_bindgen(getter)] 65 | pub fn elements(&self) -> Box<[f32]> { 66 | Box::new([ 67 | ${values.map(i => 'self.' + i).join(',\n')} 68 | ]) 69 | } 70 | ${content.replace('import * as glMatrix from "./common.js";', '')} 71 | }`; 72 | } 73 | 74 | function replaceParameters(content, name) { 75 | content.match(/(\/\*\*[\s\S\n]+?export function [\s\S]+?{)/g).forEach(s => { 76 | const res = s.match(/\@param {(\S+)} (\S+) /g); 77 | 78 | if (!res || res.length === 0) { 79 | return; 80 | } 81 | 82 | let [fun, funName, funParams] = /function (\S+)\s*\(([\S\s]+)\)\s*{/.exec(s); 83 | funParams = funParams.split(','); 84 | 85 | let i = 0; 86 | res.forEach(p => { 87 | let [__, type, name] = /\@param {(\S+)} \[*(\S+?)\]* /.exec(p); 88 | 89 | type = LUT[type]; 90 | funParams[i] = `${name}: ${name === 'out' ? '&mut ' : type === 'f32' ? '' : '&'}${type}`; 91 | 92 | i += 1; 93 | }); 94 | 95 | const funRes = `function ${funName}(${funParams.join(', ')}) {` 96 | content = content.replace(s, s.replace(fun, funRes)); 97 | }); 98 | 99 | return content.replace(/export function/g, 'pub fn'); 100 | } 101 | 102 | function replaceBody(content, name) { 103 | return content.replace(/(\S+)\[(\d+)\]/g, '$1.$2') 104 | .replace(/var /g, 'let ') 105 | .replace(/pub fn [\s\S]+? {[\s\S\n]+?}/g, body => { 106 | return body.replace(/let \S+ = [\S\s]+?,([\n\s\S])+?;/g, s => { 107 | if (/return/.test(s)) { 108 | return s; 109 | } 110 | return s.replace(/[\s\n]|let|var|;/g, '').split(',').map(item => `let ${item};`).join('\n'); 111 | }); 112 | }); 113 | } 114 | 115 | function replaceReturn(content, name) { 116 | return content.replace(/(pub fn (create|clone|fromValues)[\s\S]+? ){[\s\n]+?let out = [\s\S\n]+?return out;[\s\n]+?}/g, (fun, f) => { 117 | let [_, count] = /glMatrix\.ARRAY_TYPE\((\d+)\)/.exec(fun); 118 | 119 | let values = new Array(count); 120 | for (let i = 0; i < count; i += 1) { 121 | let [__, value] = (new RegExp(`out\.${i} = (\\S+);`)).exec(fun); 122 | if (/^\d+$/.test(value)) { 123 | value += '.'; 124 | } 125 | 126 | values[i] = value; 127 | } 128 | 129 | return `${f} -> ${name} { 130 | ${name}(${values.join(', ')}) 131 | }`; 132 | }) 133 | .replace(/return out;\n/g, ''); 134 | } 135 | 136 | function replaceConstants(content, name) { 137 | return content.replace(/glMatrix\./g, ''); 138 | } 139 | 140 | function replaceAssignments(content, name) { 141 | return content.replace(/===/g, '==') 142 | .replace(/'/g, '"'); 143 | } 144 | 145 | function replaceMath(content, name) { 146 | return content.replace(/Math\./g, 'f32::'); 147 | } 148 | 149 | function replaceAlias(content, name) { 150 | return content.replace(/export const (\S+) = (\S+);/g, (s, alias, orig) => { 151 | return `pub fn ${alias}(out: &mut ${name}, a: &${name}, b: &${name}) { 152 | ${name}::${orig}(out, a, b); 153 | }` 154 | }); 155 | } 156 | 157 | walk(glMatrixPath, (fp, filename) => { 158 | const name = LUT[filename]; 159 | console.log(fp, filename, name); 160 | let content = fs.readFileSync(fp, {encoding: 'utf8'}); 161 | content = addHeader(content, name); 162 | content = replaceParameters(content, name); 163 | content = replaceBody(content, name); 164 | content = replaceReturn(content, name); 165 | content = replaceConstants(content, name); 166 | content = replaceAssignments(content, name); 167 | content = replaceMath(content, name); 168 | content = replaceAlias(content, name); 169 | 170 | fs.writeFileSync(path.resolve(__dirname, `./rust/${name.toLowerCase()}.rs`), content); 171 | }); 172 | -------------------------------------------------------------------------------- /wasm-opt.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @File : wasm-opt.js 4 | * @Author : dtysky (dtysky@outlook.com) 5 | * @Date : 2018/12/11 下午11:55:43 6 | * @Description: 7 | */ 8 | const binaryen = require("binaryen"); 9 | const path = require('path'); 10 | const fs = require('fs'); 11 | const typescript = require('typescript'); 12 | 13 | const package = require('./package.json'); 14 | const header = `/** 15 | * @license gl-matrix-wasm v${package.version} 16 | * Copyright (c) 2018-present Tianyu Dai (dtysky). 17 | * 18 | * This source code is licensed under the MIT license found in the 19 | * LICENSE file in the root directory of this source tree. 20 | */ 21 | `; 22 | 23 | let fp = path.resolve(__dirname, './pkg/gl_matrix_wasm_bg.wasm'); 24 | const originBuffer = fs.readFileSync(fp); 25 | 26 | const wasm = binaryen.readBinary(originBuffer); 27 | binaryen.setOptimizeLevel(2); 28 | binaryen.setShrinkLevel(2); 29 | wasm.optimize(); 30 | 31 | const wast = wasm.emitText() 32 | // remove borrow checking 33 | .replace(/\(br_if \$label\$\d\n\s+\(i32\.eq\n\s+\(tee_local \$\d+?\n\s+\(i32\.load\n\s+\(get_local \$\d\)\n\s+\)\n\s+\)\n\s+\(i32\.const -1\)\n\s+\)\n\s+\)/g, '') 34 | // remove null checking 35 | // .replace(/\(br_if \$label\$\d\n\s+\(i32\.eqz[\s\S\n]+?get_local \$\d+?\)\n\s+\)\n\s+\)/g, '') 36 | // remove getters 37 | .replace(/\(export "__wbg_(get|set)_\S+\d+"[\S\s]+?\n/g, '') 38 | .replace(/\(func \$__wbg_(set|get)_\S+?_\d+?[ \S]+type \$(6|7)\) \(param \$0 i32\) \((result|param \$1) f32\)\n[\s\S\n]+?\(unreachable\)[\s\S\n]+?\(unreachable\)\n\s+\)/g, '') 39 | .replace(/\(func \$\S+?_elements.+?\(type \$1\) \(param \$0 i32\) \(param \$1 i32\)[\s\S\n]+?\n \(unreachable\)\n\s*\)/g, '') 40 | .replace(/\(export "\S+_elements" \(func \S+\)\)/g, '') 41 | // fs.writeFileSync(fp.replace('.wasm', '.wast'), wast); 42 | 43 | const distBuffer = binaryen.parseText(wast).emitBinary(); 44 | fs.writeFileSync(fp, distBuffer); 45 | 46 | fp = path.resolve(__dirname, './pkg/gl_matrix_wasm.d.ts'); 47 | fs.writeFileSync(fp, header + fs.readFileSync(fp, {encoding: 'utf8'}) 48 | .replace(/get elements\(\)/g, 'readonly elements') 49 | .replace(/\d+?: number;\n/g, '') 50 | .replace( 51 | /export default function init (.+?)\n/, 52 | 'export function init (): Promise;' 53 | ) 54 | .replace(/(@param {\S+?} out[\s\S\n]+?@returns {void} \n\*\/)\n\s+static (\S+?out[\s\S]+?): void;/g, (_, comm, funh) => { 55 | const type = /@param {(\S+)} out/.exec(comm)[1]; 56 | 57 | return `${comm.replace('void', type)} 58 | static ${funh}: ${type}; 59 | ` 60 | }) 61 | ); 62 | fp = path.resolve(__dirname, './pkg/gl_matrix_wasm.js'); 63 | 64 | const offsets = { 65 | Matrix2: 4, 66 | Matrix2d: 6, 67 | Matrix3: 9, 68 | Matrix4: 16, 69 | Vector2: 2, 70 | Vector3: 3, 71 | Vector4: 4, 72 | Quaternion: 4, 73 | Quaternion2: 8 74 | }; 75 | const content = fs.readFileSync(fp, {encoding: 'utf8'}) 76 | .replace(/static __wrap\(ptr\) {\n[\s\S]+?\.create\((\S+?)\.prototype\);\n\s+obj\.ptr = ptr;\n\n\s+return obj;\n(\s+?)}\n/g, (_, type, indent) => `static __wrap(ptr) { 77 | ${indent} const obj = Object.create(${type}.prototype); 78 | ${indent} obj.ptr = ptr; 79 | ${indent} const realPtr = ptr / 4 + 1; 80 | ${indent} this._elements = new Float32Array(wasm.memory.buffer).subarray(realPtr, realPtr + ${offsets[type]}); 81 | 82 | ${indent} return obj; 83 | ${indent}}`) 84 | .replace(/get elements\(\) {\n[\s\S]+?\n[\s\S]+?\n(\s+)}/g, `get elements() { 85 | $1 return this._elements; 86 | $1}`) 87 | .replace(/\/\*\*\n\s+?\* @returns {number}\n\s+?\*\/\n\s+?get \d+?\(\) {\n[\s\S]+?\n\s+}\n\s+set \d+?\(arg0\) {\n[\s\S]+?\n\s+?}\n/g, '') 88 | .replace(/\s+if \(typeof input === 'undefined'\) \{\n\s+input = import\.meta\.url\.replace\(\/\\\.js\$\/, '_bg\.wasm'\);\n\s+\}\n/, '') 89 | .replace(/function init\((\S+?)\) {/, 'function initModule($1) {') 90 | .replace('export default init;', '') 91 | .replace(/(@param {\S+?} out[\s\S\n]+?@returns {void}\n\s+\*\/)\n\s+static (\S+?\(out[\s\S]+?){\n\s+([\s\S\n]+?)}/g, (_, comm, funh, funb) => { 92 | const type = /@param {(\S+)} out/.exec(comm)[1]; 93 | 94 | return `${comm.replace('void', type)} 95 | static ${funh} { 96 | ${funb.replace('return ', '')} return out; 97 | } 98 | ` 99 | }); 100 | 101 | const tsconfig = { 102 | compilerOptions: { 103 | allowJs: true, 104 | sourceMap: true, 105 | noImplicitAny: false, 106 | module: "umd", 107 | target: "es5", 108 | lib: ["es2017", "dom"], 109 | skipLibCheck: true 110 | }, 111 | include: [ 112 | 'pkg/**/*.ts' 113 | ] 114 | }; 115 | 116 | fs.writeFileSync(fp, typescript.transpileModule( 117 | header + content + ` 118 | export async function init() { 119 | return initModule(new Uint8Array([${distBuffer.join(',')}])); 120 | } 121 | `, 122 | tsconfig 123 | ).outputText); 124 | 125 | fs.writeFileSync( 126 | fp.replace('.js', '.split.js'), 127 | header + `import * as wasm from './gl_matrix_wasm_bg';` + content.replace('let wasm;', '') + ` 128 | export async function init() { 129 | return initModule(wasm); 130 | } 131 | ` 132 | ); 133 | 134 | fp = path.resolve(__dirname, './pkg/gl_matrix_wasm_bg.d.ts'); 135 | fs.writeFileSync(fp, header + fs.readFileSync(fp, {encoding: 'utf8'})); 136 | 137 | fs.unlinkSync(path.resolve(__dirname, './pkg/package.json')); 138 | fs.unlinkSync(path.resolve(__dirname, './pkg/.gitignore')); 139 | fs.unlinkSync(path.resolve(__dirname, './pkg/README.md')); 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gl-matrix-wasm 2 | 3 | Port [gl-matrix](https://github.com/toji/gl-matrix) to WebAssembly by rust, wasm-bindgen and wasm-pack. 4 | 5 | ## Goals 6 | 7 | 1. **Complete**: Support all functions in gl-matrix. 8 | 2. **Pure**: Write by pure rust without any third-part dependencies. 9 | 3. **Small**: 25K(gzip, separate files) / 32K (gzip, wasm buffer will be embedded js file). 10 | 4. **Reliable**: Full unit tests as same as gl-matrix. 11 | 5. **Fast**: Some tricks to speed up the performance in production version. 12 | 6. **LowCost**: Lower CPU and memory cost than js version. 13 | 7. **Controllable**: Predictable memory cost and linear performance(AOT). 14 | 15 | ## Difference 16 | 17 | Although this library supports all functions in gl-matrix, but there are a little difference: 18 | 19 | 1. Namespace: this library use `Vector2`, `Matrix4`... as namespace, it is not as same as gl-matrix's `vec2`, `mat4`. 20 | 2. Async: You must initialize this library asynchronous, it is painful, but was wasm required. 21 | 3. Data storage: When you use some ways such as `const vec2 = Vector2.create();` to create vectors and matrixes, you will get a **Object contains pointer** but not **TypedArray**. This is the largest difference between wasm and js version. In wasm version, all data are stored in wasm memory, and in js only store those pointers. If you want to get the real value of wasm object, please use `object.elements`, it will return a `Float32Array` that could be pass to GPU, or you can use `object.elements[0]`, `object.elements[1]`... to get each element by index. 22 | 23 | 24 | ## Usage 25 | 26 | First, install it: 27 | 28 | ```shell 29 | npm i gl-matrix-wasm --save 30 | 31 | // or yarn add gl-matrix-wasm 32 | ``` 33 | 34 | Then you can use two ways to import and use it: 35 | 36 | ### One JS file 37 | 38 | In current time, this way is suggested. It combine wasm file and js wrapper file to one js file(as umd module) which embed a wasm buffer. That means you don't need any external tools, you can use it just as you use **gl-matrix** before. 39 | 40 | ```ts 41 | import * as math from 'gl-matrix-wasm'; 42 | 43 | async function test() { 44 | // initialize 45 | await math.init(); 46 | 47 | const vec3 = math.Vector3.create(); 48 | console.log(vec3.elements); 49 | 50 | // don't want to free 51 | v1.free(); 52 | } 53 | ``` 54 | 55 | ### Separate files 56 | 57 | This way faces the future, it provides a wasm file and a js(es6) file, in js file, it simply import the wasm file. It means you need some tools to compile it to es5 and split wasm file to your final results. 58 | 59 | A common toolchain that support wasm is webpack(4.0+), your can put these config to your webpack.config.js file: 60 | 61 | ```js 62 | module: { 63 | rules: [ 64 | ...... 65 | { 66 | test: /\.wasm$/, 67 | type: 'webassembly/experimental' 68 | } 69 | ...... 70 | ] 71 | } 72 | ``` 73 | 74 | And you must not exclude **node_modules/gl-matrix** in your js loader. 75 | 76 | Now, you can use this library is separate-files mode: 77 | 78 | ```ts 79 | async function test() { 80 | const math = await import('gl-matrix-wasm/pkg/gl_matrix_wasm.split'); 81 | 82 | const v1 = math.Vector4.fromValues(1, 0, 0, 0); 83 | console.log(vec3.elements); 84 | 85 | // don't want to free 86 | v1.free(); 87 | } 88 | ``` 89 | 90 | ## Performance 91 | 92 | I did many tests to show how wasm version faster than js. But unfortunately, wasm does not run faster for all scene. 93 | 94 | So, for evaluating performance reliably, I made two kinds of tests: benchmark and real-world. 95 | 96 | >PS: In current time, rust & wasm-bindgen version is slower than c++ & emscripten, see [#1585](https://github.com/rustwasm/wasm-bindgen/issues/1585). 97 | 98 | ### Benchmark 99 | 100 | See [Benchmark(Matrix4, 2015 RMBP, Chrome)](./Benchmark.md) 101 | 102 | ### Real World 103 | 104 | Real world is different from benchmark, I made some tests for each matrix4's method with 10000 calling, and a "really real world" test is also provided, it supposes we will execute operations "Mul x 4 -> fromRTS x 1 -> getElements" 1000 times per frame and run it in 60fps. 105 | 106 | You can run these tests yourself: 107 | 108 | [Matrix4 Performance Tests](http://gl-matrix-wasm.dtysky.moe) 109 | 110 | ### CPU & Memory 111 | 112 | Waiting... 113 | 114 | ## Development 115 | 116 | Welcome to contribute to this project, you can run this project in development environment follow this steps: 117 | 118 | ### Install RUST 119 | 120 | ```sh 121 | $ curl https://sh.rustup.rs -sSf | sh 122 | $ rustup default nightly 123 | $ rustup target add wasm32-unknown-unknown --toolchain nightly 124 | $ cargo +nightly install wasm-pack 125 | $ cargo update 126 | ``` 127 | 128 | ### Install node packages 129 | 130 | ```sh 131 | $ npm i 132 | ``` 133 | 134 | ### Run 135 | 136 | #### Develop rust 137 | 138 | Low performance, but could be used to debug rust sources. 139 | 140 | ```sh 141 | npm run dev 142 | ``` 143 | 144 | #### Develop demo 145 | 146 | High performance, could be used to test the production. 147 | 148 | ```sh 149 | npm run dev-build 150 | ``` 151 | 152 | ### Unit test 153 | 154 | I use karma for testing. 155 | 156 | ```sh 157 | npm run test 158 | ``` 159 | 160 | ## Next 161 | 162 | SIMD on WebAssembly. 163 | 164 | ## License 165 | 166 | Copyright © 2019, 戴天宇, Tianyu Dai (dtysky < [dtysky@outlook.com](mailto:dtysky@outlook.com) >). All Rights Reserved. This project is free software and released under the [MIT License](https://opensource.org/licenses/MIT). 167 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "bumpalo" 5 | version = "3.2.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "cfg-if" 10 | version = "0.1.10" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "gl-matrix-wasm" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 18 | ] 19 | 20 | [[package]] 21 | name = "lazy_static" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | 25 | [[package]] 26 | name = "log" 27 | version = "0.4.8" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | dependencies = [ 30 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 31 | ] 32 | 33 | [[package]] 34 | name = "proc-macro2" 35 | version = "1.0.9" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 39 | ] 40 | 41 | [[package]] 42 | name = "quote" 43 | version = "1.0.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | dependencies = [ 46 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 47 | ] 48 | 49 | [[package]] 50 | name = "syn" 51 | version = "1.0.17" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | dependencies = [ 54 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 57 | ] 58 | 59 | [[package]] 60 | name = "unicode-xid" 61 | version = "0.2.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | 64 | [[package]] 65 | name = "wasm-bindgen" 66 | version = "0.2.60" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | dependencies = [ 69 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 71 | ] 72 | 73 | [[package]] 74 | name = "wasm-bindgen-backend" 75 | version = "0.2.60" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | dependencies = [ 78 | "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "wasm-bindgen-macro" 89 | version = "0.2.60" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 94 | ] 95 | 96 | [[package]] 97 | name = "wasm-bindgen-macro-support" 98 | version = "0.2.60" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | dependencies = [ 101 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "wasm-bindgen-shared" 110 | version = "0.2.60" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | 113 | [metadata] 114 | "checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" 115 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 116 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 117 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 118 | "checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" 119 | "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 120 | "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 121 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 122 | "checksum wasm-bindgen 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" 123 | "checksum wasm-bindgen-backend 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" 124 | "checksum wasm-bindgen-macro 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" 125 | "checksum wasm-bindgen-macro-support 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" 126 | "checksum wasm-bindgen-shared 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" 127 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "insecure-random": true, 4 | "no-banned-terms": true, 5 | "no-cookies": true, 6 | "no-delete-expression": true, 7 | "no-disable-auto-sanitization": true, 8 | "no-document-domain": true, 9 | "no-document-write": true, 10 | "no-eval": true, 11 | "no-exec-script": true, 12 | "no-function-constructor-with-string-args": true, 13 | "no-http-string": [ 14 | false, 15 | "http://www.example.com/?.*", 16 | "http://www.examples.com/?.*" 17 | ], 18 | "no-inner-html": true, 19 | "no-octal-literal": true, 20 | "no-reserved-keywords": true, 21 | "no-string-based-set-immediate": true, 22 | "no-string-based-set-interval": true, 23 | "no-string-based-set-timeout": true, 24 | "non-literal-require": true, 25 | "possible-timing-attack": true, 26 | "react-anchor-blank-noopener": true, 27 | "react-iframe-missing-sandbox": true, 28 | "react-no-dangerous-html": true, 29 | "forin": true, 30 | "jquery-deferred-must-complete": true, 31 | "label-position": true, 32 | "mocha-avoid-only": true, 33 | "mocha-no-side-effect-code": true, 34 | "no-any": false, 35 | "no-arg": true, 36 | "no-backbone-get-set-outside-model": false, 37 | "no-conditional-assignment": true, 38 | "no-console": [ 39 | true, 40 | "debug", 41 | "info", 42 | "log", 43 | "time", 44 | "timeEnd", 45 | "trace" 46 | ], 47 | "no-constant-condition": true, 48 | "no-control-regex": true, 49 | "no-debugger": true, 50 | "no-duplicate-case": true, 51 | "no-duplicate-variable": true, 52 | "no-empty": false, 53 | "no-increment-decrement": true, 54 | "no-invalid-regexp": true, 55 | "no-invalid-this": true, 56 | "no-jquery-raw-elements": true, 57 | "no-regex-spaces": true, 58 | "no-sparse-arrays": true, 59 | "no-stateless-class": true, 60 | "no-string-literal": false, 61 | "no-unnecessary-bind": true, 62 | "no-unnecessary-override": true, 63 | "no-unsafe-finally": true, 64 | "no-unused-expression": true, 65 | "no-unused-new": true, 66 | "no-use-before-declare": true, 67 | "no-with-statement": true, 68 | "promise-must-complete": true, 69 | "radix": true, 70 | "react-this-binding-issue": true, 71 | "react-unused-props-and-state": true, 72 | "switch-default": true, 73 | "triple-equals": [ 74 | true, 75 | "allow-null-check" 76 | ], 77 | "use-isnan": true, 78 | "use-named-parameter": true, 79 | "valid-typeof": true, 80 | "arrow-parens": false, 81 | "chai-prefer-contains-to-index-of": true, 82 | "chai-vague-errors": true, 83 | "class-name": true, 84 | "comment-format": true, 85 | "function-name": true, 86 | "import-name": false, 87 | "interface-name": true, 88 | "jsdoc-format": true, 89 | "max-file-line-count": true, 90 | "max-func-body-length": [ 91 | true, 92 | 100, 93 | { 94 | "ignore-parameters-to-function-regex": "describe" 95 | } 96 | ], 97 | "max-line-length": [ 98 | true, 99 | 140 100 | ], 101 | "member-access": true, 102 | "member-ordering": true, 103 | "missing-jsdoc": true, 104 | "mocha-unneeded-done": true, 105 | "new-parens": true, 106 | "no-construct": true, 107 | "no-empty-interfaces": true, 108 | "no-for-in": false, 109 | "no-function-expression": true, 110 | "no-inferrable-types": false, 111 | "no-multiline-string": true, 112 | "no-null-keyword": false, 113 | "no-require-imports": true, 114 | "no-shadowed-variable": true, 115 | "no-suspicious-comment": true, 116 | "no-typeof-undefined": true, 117 | "no-unnecessary-field-initialization": true, 118 | "no-unnecessary-local-variable": false, 119 | "no-unsupported-browser-code": true, 120 | "no-var-keyword": true, 121 | "no-var-requires": true, 122 | "no-var-self": true, 123 | "object-literal-sort-keys": false, 124 | "one-variable-per-declaration": true, 125 | "only-arrow-functions": false, 126 | "prefer-array-literal": true, 127 | "prefer-const": true, 128 | "typedef": [ 129 | true, 130 | "callSignature", 131 | "indexSignature", 132 | "parameter", 133 | "propertySignature", 134 | "variableDeclarator", 135 | "memberVariableDeclarator" 136 | ], 137 | "underscore-consistent-invocation": true, 138 | "variable-name": [ 139 | false, 140 | "allow-leading-underscore" 141 | ], 142 | "react-a11y-anchors": true, 143 | "react-a11y-aria-unsupported-elements": true, 144 | "react-a11y-event-has-role": true, 145 | "react-a11y-image-button-has-alt": true, 146 | "react-a11y-img-has-alt": true, 147 | "react-a11y-lang": true, 148 | "react-a11y-meta": true, 149 | "react-a11y-props": true, 150 | "react-a11y-proptypes": true, 151 | "react-a11y-role": true, 152 | "react-a11y-role-has-required-aria-props": true, 153 | "react-a11y-role-supports-aria-props": true, 154 | "react-a11y-tabindex-no-positive": true, 155 | "react-a11y-titles": true, 156 | "align": [ 157 | true, 158 | "parameters", 159 | "arguments", 160 | "statements" 161 | ], 162 | "curly": true, 163 | "eofline": true, 164 | "indent": [ 165 | true, 166 | "spaces" 167 | ], 168 | "linebreak-style": true, 169 | "no-consecutive-blank-lines": true, 170 | "no-empty-line-after-opening-brace": false, 171 | "no-single-line-block-comment": true, 172 | "no-trailing-whitespace": true, 173 | "no-unnecessary-semicolons": true, 174 | "object-literal-key-quotes": [ 175 | true, 176 | "as-needed" 177 | ], 178 | "one-line": [ 179 | true, 180 | "check-open-brace", 181 | "check-catch", 182 | "check-else", 183 | "check-whitespace" 184 | ], 185 | "quotemark": [ 186 | true, 187 | "single" 188 | ], 189 | "react-tsx-curly-spacing": false, 190 | "semicolon": [ 191 | true, 192 | "always" 193 | ], 194 | "trailing-comma": [ 195 | true, 196 | { 197 | "singleline": "never", 198 | "multiline": "never" 199 | } 200 | ], 201 | "typedef-whitespace": false, 202 | "whitespace": [ 203 | true, 204 | "check-branch", 205 | "check-decl", 206 | "check-operator", 207 | "check-separator", 208 | "check-type" 209 | ], 210 | "ban": false, 211 | "no-angle-bracket-type-assertion": false, 212 | "no-internal-module": false, 213 | "no-mergeable-namespace": false, 214 | "no-namespace": false, 215 | "no-reference": true, 216 | "no-unexternalized-strings": false, 217 | "prefer-type-cast": false, 218 | "missing-optional-annotation": false, 219 | "no-duplicate-parameter-names": false, 220 | "no-missing-visibility-modifiers": false, 221 | "no-multiple-var-decl": false, 222 | "no-switch-case-fall-through": false, 223 | "no-unused-imports": true 224 | }, 225 | "rulesDirectory": "./node_modules/tslint-microsoft-contrib" 226 | } 227 | -------------------------------------------------------------------------------- /src/matrix2.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : matrix2.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::vector2::*; 11 | 12 | #[wasm_bindgen] 13 | pub struct Matrix2(pub f32, pub f32, pub f32, pub f32); 14 | 15 | #[wasm_bindgen] 16 | impl Matrix2 { 17 | #[wasm_bindgen(getter)] 18 | pub fn elements(&self) -> Box<[f32]> { 19 | Box::new([self.0, self.1, self.2, self.3]) 20 | } 21 | 22 | pub fn create() -> Self { 23 | Matrix2(1., 0., 0., 1.) 24 | } 25 | 26 | pub fn clone(a: &Matrix2) -> Self { 27 | Matrix2(a.0, a.1, a.2, a.3) 28 | } 29 | 30 | pub fn copy(out: &mut Matrix2, a: &Matrix2) { 31 | out.0 = a.0; 32 | out.1 = a.1; 33 | out.2 = a.2; 34 | out.3 = a.3; 35 | } 36 | 37 | pub fn identity(out: &mut Matrix2) { 38 | out.0 = 1.; 39 | out.1 = 0.; 40 | out.2 = 0.; 41 | out.3 = 1.; 42 | } 43 | 44 | pub fn fromValues(m00: f32, m01: f32, m10: f32, m11: f32) -> Self { 45 | Matrix2(m00, m01, m10, m11) 46 | } 47 | 48 | pub fn set(out: &mut Matrix2, m00: f32, m01: f32, m10: f32, m11: f32) { 49 | out.0 = m00; 50 | out.1 = m01; 51 | out.2 = m10; 52 | out.3 = m11; 53 | } 54 | 55 | pub fn transpose(out: &mut Matrix2, a: &Matrix2) { 56 | // If we are transposing ourselves we can skip a few steps but have to cache 57 | // some values 58 | if (out as *const Matrix2) == (a as *const Matrix2) { 59 | let a1 = a.1; 60 | out.1 = a.2; 61 | out.2 = a1; 62 | } else { 63 | out.0 = a.0; 64 | out.1 = a.2; 65 | out.2 = a.1; 66 | out.3 = a.3; 67 | } 68 | } 69 | 70 | pub fn invert(out: &mut Matrix2, a: &Matrix2) { 71 | let a0 = a.0; 72 | let a1 = a.1; 73 | let a2 = a.2; 74 | let a3 = a.3; 75 | 76 | // Calculate the determinant 77 | let det = a0 * a3 - a2 * a1; 78 | 79 | if det == 0. { 80 | return; 81 | } 82 | 83 | let det = 1.0 / det; 84 | 85 | out.0 = a3 * det; 86 | out.1 = -a1 * det; 87 | out.2 = -a2 * det; 88 | out.3 = a0 * det; 89 | } 90 | 91 | pub fn adjoint(out: &mut Matrix2, a: &Matrix2) { 92 | // Caching this value is nessecary if out == a 93 | if (out as *const Matrix2) == (a as *const Matrix2) { 94 | let a = Matrix2::clone(a); 95 | out.0 = a.3; 96 | out.1 = -a.1; 97 | out.2 = -a.2; 98 | out.3 = a.0; 99 | return; 100 | } 101 | 102 | out.0 = a.3; 103 | out.1 = -a.1; 104 | out.2 = -a.2; 105 | out.3 = a.0; 106 | 107 | // console_log!("{} {} {} {}", out.0, out.1, out.2, out.3); 108 | } 109 | 110 | pub fn determinant(a: &mut Matrix2) -> f32 { 111 | a.0 * a.3 - a.2 * a.1 112 | } 113 | 114 | pub fn multiply(out: &mut Matrix2, a: &Matrix2, b: &Matrix2) { 115 | let a0 = a.0; 116 | let a1 = a.1; 117 | let a2 = a.2; 118 | let a3 = a.3; 119 | let b0 = b.0; 120 | let b1 = b.1; 121 | let b2 = b.2; 122 | let b3 = b.3; 123 | out.0 = a0 * b0 + a2 * b1; 124 | out.1 = a1 * b0 + a3 * b1; 125 | out.2 = a0 * b2 + a2 * b3; 126 | out.3 = a1 * b2 + a3 * b3; 127 | } 128 | 129 | pub fn rotate(out: &mut Matrix2, a: &Matrix2, rad: f32) { 130 | let a0 = a.0; 131 | let a1 = a.1; 132 | let a2 = a.2; 133 | let a3 = a.3; 134 | let s = f32::sin(rad); 135 | let c = f32::cos(rad); 136 | out.0 = a0 * c + a2 * s; 137 | out.1 = a1 * c + a3 * s; 138 | out.2 = a0 * -s + a2 * c; 139 | out.3 = a1 * -s + a3 * c; 140 | } 141 | 142 | pub fn scale(out: &mut Matrix2, a: &Matrix2, v: &Vector2) { 143 | let a0 = a.0; 144 | let a1 = a.1; 145 | let a2 = a.2; 146 | let a3 = a.3; 147 | let v0 = v.0; 148 | let v1 = v.1; 149 | out.0 = a0 * v0; 150 | out.1 = a1 * v0; 151 | out.2 = a2 * v1; 152 | out.3 = a3 * v1; 153 | } 154 | 155 | pub fn fromRotation(out: &mut Matrix2, rad: f32) { 156 | let s = f32::sin(rad); 157 | let c = f32::cos(rad); 158 | out.0 = c; 159 | out.1 = s; 160 | out.2 = -s; 161 | out.3 = c; 162 | } 163 | 164 | pub fn fromScaling(out: &mut Matrix2, v: &Vector2) { 165 | out.0 = v.0; 166 | out.1 = 0.; 167 | out.2 = 0.; 168 | out.3 = v.1; 169 | } 170 | 171 | // pub fn str(a: &Matrix2) -> String { 172 | // "mat2(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + a.3 + ")" 173 | // } 174 | 175 | pub fn frob(a: &Matrix2) -> f32 { 176 | (a.0.powi(2) + a.1.powi(2) + a.2.powi(2) + a.3.powi(2)).sqrt() 177 | } 178 | 179 | pub fn LDU(L: &mut Matrix2, D: &mut Matrix2, U: &mut Matrix2, a: &Matrix2) { 180 | L.2 = a.2 / a.0; 181 | U.0 = a.0; 182 | U.1 = a.1; 183 | U.3 = a.3 - L.2 * U.1; 184 | } 185 | 186 | pub fn add(out: &mut Matrix2, a: &Matrix2, b: &Matrix2) { 187 | out.0 = a.0 + b.0; 188 | out.1 = a.1 + b.1; 189 | out.2 = a.2 + b.2; 190 | out.3 = a.3 + b.3; 191 | } 192 | 193 | pub fn subtract(out: &mut Matrix2, a: &Matrix2, b: &Matrix2) { 194 | out.0 = a.0 - b.0; 195 | out.1 = a.1 - b.1; 196 | out.2 = a.2 - b.2; 197 | out.3 = a.3 - b.3; 198 | } 199 | 200 | pub fn exactEquals(a: &Matrix2, b: &Matrix2) -> bool { 201 | return a.0 == b.0 && a.1 == b.1 && a.2 == b.2 && a.3 == b.3; 202 | } 203 | 204 | pub fn equals(a: &Matrix2, b: &Matrix2) -> bool { 205 | let a0 = a.0; 206 | let a1 = a.1; 207 | let a2 = a.2; 208 | let a3 = a.3; 209 | let b0 = b.0; 210 | let b1 = b.1; 211 | let b2 = b.2; 212 | let b3 = b.3; 213 | 214 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 215 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 216 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 217 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 218 | } 219 | 220 | pub fn multiplyScalar(out: &mut Matrix2, a: &Matrix2, b: f32) { 221 | out.0 = a.0 * b; 222 | out.1 = a.1 * b; 223 | out.2 = a.2 * b; 224 | out.3 = a.3 * b; 225 | } 226 | 227 | pub fn multiplyScalarAndAdd(out: &mut Matrix2, a: &Matrix2, b: &Matrix2, scale: f32) { 228 | out.0 = a.0 + (b.0 * scale); 229 | out.1 = a.1 + (b.1 * scale); 230 | out.2 = a.2 + (b.2 * scale); 231 | out.3 = a.3 + (b.3 * scale); 232 | } 233 | 234 | pub fn mul(out: &mut Matrix2, a: &Matrix2, b: &Matrix2) { 235 | Matrix2::multiply(out, a, b); 236 | } 237 | 238 | pub fn sub(out: &mut Matrix2, a: &Matrix2, b: &Matrix2) { 239 | Matrix2::subtract(out, a, b); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/matrix2d.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : matrix2d.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::vector2::*; 11 | 12 | #[wasm_bindgen] 13 | pub struct Matrix2d(pub f32, pub f32, pub f32, pub f32, pub f32, pub f32); 14 | 15 | #[wasm_bindgen] 16 | impl Matrix2d { 17 | #[wasm_bindgen(getter)] 18 | pub fn elements(&self) -> Box<[f32]> { 19 | Box::new([self.0, self.1, self.2, self.3, self.4, self.5]) 20 | } 21 | 22 | pub fn create() -> Matrix2d { 23 | Matrix2d(1., 0., 0., 1., 0., 0.) 24 | } 25 | 26 | pub fn clone(a: &Matrix2d) -> Matrix2d { 27 | Matrix2d(a.0, a.1, a.2, a.3, a.4, a.5) 28 | } 29 | 30 | pub fn copy(out: &mut Matrix2d, a: &Matrix2d) { 31 | out.0 = a.0; 32 | out.1 = a.1; 33 | out.2 = a.2; 34 | out.3 = a.3; 35 | out.4 = a.4; 36 | out.5 = a.5; 37 | } 38 | 39 | pub fn identity(out: &mut Matrix2d) { 40 | out.0 = 1.; 41 | out.1 = 0.; 42 | out.2 = 0.; 43 | out.3 = 1.; 44 | out.4 = 0.; 45 | out.5 = 0.; 46 | } 47 | 48 | pub fn fromValues(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) -> Matrix2d { 49 | Matrix2d(a, b, c, d, tx, ty) 50 | } 51 | 52 | pub fn set(out: &mut Matrix2d, a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) { 53 | out.0 = a; 54 | out.1 = b; 55 | out.2 = c; 56 | out.3 = d; 57 | out.4 = tx; 58 | out.5 = ty; 59 | } 60 | 61 | pub fn invert(out: &mut Matrix2d, a: &Matrix2d) { 62 | let aa = a.0; 63 | let ab = a.1; 64 | let ac = a.2; 65 | let ad = a.3; 66 | let atx = a.4; 67 | let aty = a.5; 68 | 69 | let mut det = aa * ad - ab * ac; 70 | 71 | if det.abs() < EPSILON { 72 | return; 73 | } 74 | det = 1.0 / det; 75 | 76 | out.0 = ad * det; 77 | out.1 = -ab * det; 78 | out.2 = -ac * det; 79 | out.3 = aa * det; 80 | out.4 = (ac * aty - ad * atx) * det; 81 | out.5 = (ab * atx - aa * aty) * det; 82 | } 83 | 84 | pub fn determinant(a: &Matrix2d) -> f32 { 85 | a.0 * a.3 - a.1 * a.2 86 | } 87 | 88 | pub fn multiply(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d) { 89 | let a0 = a.0; 90 | let a1 = a.1; 91 | let a2 = a.2; 92 | let a3 = a.3; 93 | let a4 = a.4; 94 | let a5 = a.5; 95 | let b0 = b.0; 96 | let b1 = b.1; 97 | let b2 = b.2; 98 | let b3 = b.3; 99 | let b4 = b.4; 100 | let b5 = b.5; 101 | out.0 = a0 * b0 + a2 * b1; 102 | out.1 = a1 * b0 + a3 * b1; 103 | out.2 = a0 * b2 + a2 * b3; 104 | out.3 = a1 * b2 + a3 * b3; 105 | out.4 = a0 * b4 + a2 * b5 + a4; 106 | out.5 = a1 * b4 + a3 * b5 + a5; 107 | } 108 | 109 | pub fn rotate(out: &mut Matrix2d, a: &Matrix2d, rad: f32) { 110 | let a0 = a.0; 111 | let a1 = a.1; 112 | let a2 = a.2; 113 | let a3 = a.3; 114 | let a4 = a.4; 115 | let a5 = a.5; 116 | let s = f32::sin(rad); 117 | let c = f32::cos(rad); 118 | out.0 = a0 * c + a2 * s; 119 | out.1 = a1 * c + a3 * s; 120 | out.2 = a0 * -s + a2 * c; 121 | out.3 = a1 * -s + a3 * c; 122 | out.4 = a4; 123 | out.5 = a5; 124 | } 125 | 126 | pub fn scale(out: &mut Matrix2d, a: &Matrix2d, v: &Vector2) { 127 | let a0 = a.0; 128 | let a1 = a.1; 129 | let a2 = a.2; 130 | let a3 = a.3; 131 | let a4 = a.4; 132 | let a5 = a.5; 133 | let v0 = v.0; 134 | let v1 = v.1; 135 | out.0 = a0 * v0; 136 | out.1 = a1 * v0; 137 | out.2 = a2 * v1; 138 | out.3 = a3 * v1; 139 | out.4 = a4; 140 | out.5 = a5; 141 | } 142 | 143 | pub fn translate(out: &mut Matrix2d, a: &Matrix2d, v: &Vector2) { 144 | let a0 = a.0; 145 | let a1 = a.1; 146 | let a2 = a.2; 147 | let a3 = a.3; 148 | let a4 = a.4; 149 | let a5 = a.5; 150 | let v0 = v.0; 151 | let v1 = v.1; 152 | out.0 = a0; 153 | out.1 = a1; 154 | out.2 = a2; 155 | out.3 = a3; 156 | out.4 = a0 * v0 + a2 * v1 + a4; 157 | out.5 = a1 * v0 + a3 * v1 + a5; 158 | } 159 | 160 | pub fn fromRotation(out: &mut Matrix2d, rad: f32) { 161 | let s = f32::sin(rad); 162 | let c = f32::cos(rad); 163 | out.0 = c; 164 | out.1 = s; 165 | out.2 = -s; 166 | out.3 = c; 167 | out.4 = 0.; 168 | out.5 = 0.; 169 | } 170 | 171 | pub fn fromScaling(out: &mut Matrix2d, v: &Vector2) { 172 | out.0 = v.0; 173 | out.1 = 0.; 174 | out.2 = 0.; 175 | out.3 = v.1; 176 | out.4 = 0.; 177 | out.5 = 0.; 178 | } 179 | 180 | pub fn fromTranslation(out: &mut Matrix2d, v: &Vector2) { 181 | out.0 = 1.; 182 | out.1 = 0.; 183 | out.2 = 0.; 184 | out.3 = 1.; 185 | out.4 = v.0; 186 | out.5 = v.1; 187 | } 188 | 189 | // pub fn str(a: &Matrix2d) { 190 | // return "mat2d(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + 191 | // a.3 + ", " + a.4 + ", " + a.5 + ")"; 192 | // } 193 | 194 | pub fn frob(a: &Matrix2d) -> f32 { 195 | (a.0.powi(2) + a.1.powi(2) + a.2.powi(2) + a.3.powi(2) + a.4.powi(2) + a.5.powi(2) + 1.) 196 | .sqrt() 197 | } 198 | 199 | pub fn add(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d) { 200 | out.0 = a.0 + b.0; 201 | out.1 = a.1 + b.1; 202 | out.2 = a.2 + b.2; 203 | out.3 = a.3 + b.3; 204 | out.4 = a.4 + b.4; 205 | out.5 = a.5 + b.5; 206 | } 207 | 208 | pub fn subtract(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d) { 209 | out.0 = a.0 - b.0; 210 | out.1 = a.1 - b.1; 211 | out.2 = a.2 - b.2; 212 | out.3 = a.3 - b.3; 213 | out.4 = a.4 - b.4; 214 | out.5 = a.5 - b.5; 215 | } 216 | 217 | pub fn multiplyScalar(out: &mut Matrix2d, a: &Matrix2d, b: f32) { 218 | out.0 = a.0 * b; 219 | out.1 = a.1 * b; 220 | out.2 = a.2 * b; 221 | out.3 = a.3 * b; 222 | out.4 = a.4 * b; 223 | out.5 = a.5 * b; 224 | } 225 | 226 | pub fn multiplyScalarAndAdd(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d, scale: f32) { 227 | out.0 = a.0 + (b.0 * scale); 228 | out.1 = a.1 + (b.1 * scale); 229 | out.2 = a.2 + (b.2 * scale); 230 | out.3 = a.3 + (b.3 * scale); 231 | out.4 = a.4 + (b.4 * scale); 232 | out.5 = a.5 + (b.5 * scale); 233 | } 234 | 235 | pub fn exactEquals(a: &Matrix2d, b: &Matrix2d) -> bool { 236 | a.0 == b.0 && a.1 == b.1 && a.2 == b.2 && a.3 == b.3 && a.4 == b.4 && a.5 == b.5 237 | } 238 | 239 | pub fn equals(a: &Matrix2d, b: &Matrix2d) -> bool { 240 | let a0 = a.0; 241 | let a1 = a.1; 242 | let a2 = a.2; 243 | let a3 = a.3; 244 | let a4 = a.4; 245 | let a5 = a.5; 246 | let b0 = b.0; 247 | let b1 = b.1; 248 | let b2 = b.2; 249 | let b3 = b.3; 250 | let b4 = b.4; 251 | let b5 = b.5; 252 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 253 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 254 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 255 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 256 | && f32::abs(a4 - b4) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a4), f32::abs(b4))) 257 | && f32::abs(a5 - b5) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a5), f32::abs(b5))) 258 | } 259 | 260 | pub fn mul(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d) { 261 | Matrix2d::multiply(out, a, b); 262 | } 263 | 264 | pub fn sub(out: &mut Matrix2d, a: &Matrix2d, b: &Matrix2d) { 265 | Matrix2d::subtract(out, a, b); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/vector2.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : vector2.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix2::*; 11 | use super::matrix2d::*; 12 | use super::matrix3::*; 13 | use super::matrix4::*; 14 | use super::vector3::*; 15 | 16 | #[wasm_bindgen] 17 | pub struct Vector2(pub f32, pub f32); 18 | 19 | #[wasm_bindgen] 20 | impl Vector2 { 21 | #[wasm_bindgen(getter)] 22 | pub fn elements(&self) -> Box<[f32]> { 23 | Box::new([self.0, self.1]) 24 | } 25 | 26 | pub fn create() -> Vector2 { 27 | Vector2(0., 0.) 28 | } 29 | 30 | pub fn clone(a: &Vector2) -> Vector2 { 31 | Vector2(a.0, a.1) 32 | } 33 | 34 | pub fn fromValues(x: f32, y: f32) -> Vector2 { 35 | Vector2(x, y) 36 | } 37 | 38 | pub fn copy(out: &mut Vector2, a: &Vector2) { 39 | out.0 = a.0; 40 | out.1 = a.1; 41 | } 42 | 43 | pub fn set(out: &mut Vector2, x: f32, y: f32) { 44 | out.0 = x; 45 | out.1 = y; 46 | } 47 | 48 | pub fn add(out: &mut Vector2, a: &Vector2, b: &Vector2) { 49 | out.0 = a.0 + b.0; 50 | out.1 = a.1 + b.1; 51 | } 52 | 53 | pub fn subtract(out: &mut Vector2, a: &Vector2, b: &Vector2) { 54 | out.0 = a.0 - b.0; 55 | out.1 = a.1 - b.1; 56 | } 57 | 58 | pub fn multiply(out: &mut Vector2, a: &Vector2, b: &Vector2) { 59 | out.0 = a.0 * b.0; 60 | out.1 = a.1 * b.1; 61 | } 62 | 63 | pub fn divide(out: &mut Vector2, a: &Vector2, b: &Vector2) { 64 | out.0 = a.0 / b.0; 65 | out.1 = a.1 / b.1; 66 | } 67 | 68 | pub fn ceil(out: &mut Vector2, a: &Vector2) { 69 | out.0 = f32::ceil(a.0); 70 | out.1 = f32::ceil(a.1); 71 | } 72 | 73 | pub fn floor(out: &mut Vector2, a: &Vector2) { 74 | out.0 = f32::floor(a.0); 75 | out.1 = f32::floor(a.1); 76 | } 77 | 78 | pub fn min(out: &mut Vector2, a: &Vector2, b: &Vector2) { 79 | out.0 = f32::min(a.0, b.0); 80 | out.1 = f32::min(a.1, b.1); 81 | } 82 | 83 | pub fn max(out: &mut Vector2, a: &Vector2, b: &Vector2) { 84 | out.0 = f32::max(a.0, b.0); 85 | out.1 = f32::max(a.1, b.1); 86 | } 87 | 88 | pub fn round(out: &mut Vector2, a: &Vector2) { 89 | out.0 = f32::round(a.0); 90 | out.1 = f32::round(a.1); 91 | } 92 | 93 | pub fn scale(out: &mut Vector2, a: &Vector2, b: f32) { 94 | out.0 = a.0 * b; 95 | out.1 = a.1 * b; 96 | } 97 | 98 | pub fn scaleAndAdd(out: &mut Vector2, a: &Vector2, b: &Vector2, scale: f32) { 99 | out.0 = a.0 + (b.0 * scale); 100 | out.1 = a.1 + (b.1 * scale); 101 | } 102 | 103 | pub fn distance(a: &Vector2, b: &Vector2) -> f32 { 104 | let x = b.0 - a.0; 105 | let y = b.1 - a.1; 106 | f32::hypot(x, y) 107 | } 108 | 109 | pub fn squaredDistance(a: &Vector2, b: &Vector2) -> f32 { 110 | let x = b.0 - a.0; 111 | let y = b.1 - a.1; 112 | x * x + y * y 113 | } 114 | 115 | pub fn len(a: &Vector2) -> f32 { 116 | let x = a.0; 117 | let y = a.1; 118 | f32::hypot(x, y) 119 | } 120 | 121 | pub fn squaredLength(a: &Vector2) -> f32 { 122 | let x = a.0; 123 | let y = a.1; 124 | x * x + y * y 125 | } 126 | 127 | pub fn negate(out: &mut Vector2, a: &Vector2) { 128 | out.0 = -a.0; 129 | out.1 = -a.1; 130 | } 131 | 132 | pub fn inverse(out: &mut Vector2, a: &Vector2) { 133 | out.0 = 1.0 / a.0; 134 | out.1 = 1.0 / a.1; 135 | } 136 | 137 | pub fn normalize(out: &mut Vector2, a: &Vector2) { 138 | let x = a.0; 139 | let y = a.1; 140 | let mut len = x * x + y * y; 141 | if (len > EPSILON) { 142 | //TODO: evaluate use of glm_invsqrt here? 143 | len = 1. / f32::sqrt(len); 144 | } 145 | out.0 = a.0 * len; 146 | out.1 = a.1 * len; 147 | } 148 | 149 | pub fn dot(a: &Vector2, b: &Vector2) -> f32 { 150 | a.0 * b.0 + a.1 * b.1 151 | } 152 | 153 | pub fn cross(out: &mut Vector3, a: &Vector2, b: &Vector2) { 154 | let z = a.0 * b.1 - a.1 * b.0; 155 | out.0 = 0.; 156 | out.1 = 0.; 157 | out.2 = z; 158 | } 159 | 160 | pub fn lerp(out: &mut Vector2, a: &Vector2, b: &Vector2, t: f32) { 161 | let ax = a.0; 162 | let ay = a.1; 163 | out.0 = ax + t * (b.0 - ax); 164 | out.1 = ay + t * (b.1 - ay); 165 | } 166 | 167 | pub fn random(out: &mut Vector2, scale: Option) { 168 | let mut s = 1.; 169 | match scale { 170 | Some(value) => s = value, 171 | None => {} 172 | }; 173 | let scale = s; 174 | let r = RANDOM() * 2.0 * PI; 175 | out.0 = f32::cos(r) * scale; 176 | out.1 = f32::sin(r) * scale; 177 | } 178 | 179 | pub fn transformMat2(out: &mut Vector2, a: &Vector2, m: &Matrix2) { 180 | let x = a.0; 181 | let y = a.1; 182 | out.0 = m.0 * x + m.2 * y; 183 | out.1 = m.1 * x + m.3 * y; 184 | } 185 | 186 | pub fn transformMat2d(out: &mut Vector2, a: &Vector2, m: &Matrix2d) { 187 | let x = a.0; 188 | let y = a.1; 189 | out.0 = m.0 * x + m.2 * y + m.4; 190 | out.1 = m.1 * x + m.3 * y + m.5; 191 | } 192 | 193 | pub fn transformMat3(out: &mut Vector2, a: &Vector2, m: &Matrix3) { 194 | let x = a.0; 195 | let y = a.1; 196 | out.0 = m.0 * x + m.3 * y + m.6; 197 | out.1 = m.1 * x + m.4 * y + m.7; 198 | } 199 | 200 | pub fn transformMat4(out: &mut Vector2, a: &Vector2, m: &Matrix4) { 201 | let x = a.0; 202 | let y = a.1; 203 | out.0 = m.0 * x + m.4 * y + m.12; 204 | out.1 = m.1 * x + m.5 * y + m.13; 205 | } 206 | 207 | pub fn rotate(out: &mut Vector2, a: &Vector2, b: &Vector2, c: f32) { 208 | //Translate point to the origin 209 | let p0 = a.0 - b.0; 210 | let p1 = a.1 - b.1; 211 | let sinC = f32::sin(c); 212 | let cosC = f32::cos(c); 213 | 214 | //perform rotation and translate to correct position 215 | out.0 = p0 * cosC - p1 * sinC + b.0; 216 | out.1 = p0 * sinC + p1 * cosC + b.1; 217 | } 218 | 219 | pub fn angle(a: &Vector2, b: &Vector2) -> f32 { 220 | let x1 = a.0; 221 | let y1 = a.1; 222 | let x2 = b.0; 223 | let y2 = b.1; 224 | 225 | let mut len1 = x1 * x1 + y1 * y1; 226 | if (len1 > EPSILON) { 227 | //TODO: evaluate use of glm_invsqrt here? 228 | len1 = 1. / f32::sqrt(len1); 229 | } 230 | 231 | let mut len2 = x2 * x2 + y2 * y2; 232 | if (len2 > EPSILON) { 233 | //TODO: evaluate use of glm_invsqrt here? 234 | len2 = 1. / f32::sqrt(len2); 235 | } 236 | 237 | let cosine = (x1 * x2 + y1 * y2) * len1 * len2; 238 | 239 | if (cosine > 1.0) { 240 | 0. 241 | } else if (cosine < -1.0) { 242 | PI 243 | } else { 244 | f32::acos(cosine) 245 | } 246 | } 247 | 248 | pub fn zero(out: &mut Vector2) { 249 | out.0 = 0.0; 250 | out.1 = 0.0; 251 | } 252 | 253 | // pub fn str(a: &Vector2) { 254 | // return "vec2(" + a.0 + ", " + a.1 + ")"; 255 | // } 256 | 257 | pub fn exactEquals(a: &Vector2, b: &Vector2) -> bool { 258 | a.0 == b.0 && a.1 == b.1 259 | } 260 | 261 | pub fn equals(a: &Vector2, b: &Vector2) -> bool { 262 | let a0 = a.0; 263 | let a1 = a.1; 264 | let b0 = b.0; 265 | let b1 = b.1; 266 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 267 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 268 | } 269 | 270 | pub fn sub(out: &mut Vector2, a: &Vector2, b: &Vector2) { 271 | Vector2::subtract(out, a, b); 272 | } 273 | 274 | pub fn mul(out: &mut Vector2, a: &Vector2, b: &Vector2) { 275 | Vector2::multiply(out, a, b); 276 | } 277 | 278 | pub fn div(out: &mut Vector2, a: &Vector2, b: &Vector2) { 279 | Vector2::divide(out, a, b); 280 | } 281 | 282 | pub fn dist(a: &Vector2, b: &Vector2) -> f32 { 283 | Vector2::distance(a, b) 284 | } 285 | 286 | pub fn sqrDist(a: &Vector2, b: &Vector2) -> f32 { 287 | Vector2::squaredDistance(a, b) 288 | } 289 | 290 | pub fn sqrLen(a: &Vector2) -> f32 { 291 | Vector2::squaredLength(a) 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/vector4.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : vector4.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix4::*; 11 | use super::quaternion::*; 12 | 13 | #[wasm_bindgen] 14 | pub struct Vector4(pub f32, pub f32, pub f32, pub f32); 15 | 16 | #[wasm_bindgen] 17 | impl Vector4 { 18 | #[wasm_bindgen(getter)] 19 | pub fn elements(&self) -> Box<[f32]> { 20 | Box::new([self.0, self.1, self.2, self.3]) 21 | } 22 | 23 | pub fn create() -> Vector4 { 24 | Vector4(0., 0., 0., 0.) 25 | } 26 | 27 | pub fn clone(a: &Vector4) -> Vector4 { 28 | Vector4(a.0, a.1, a.2, a.3) 29 | } 30 | 31 | pub fn fromValues(x: f32, y: f32, z: f32, w: f32) -> Vector4 { 32 | Vector4(x, y, z, w) 33 | } 34 | 35 | pub fn copy(out: &mut Vector4, a: &Vector4) { 36 | out.0 = a.0; 37 | out.1 = a.1; 38 | out.2 = a.2; 39 | out.3 = a.3; 40 | } 41 | 42 | pub fn set(out: &mut Vector4, x: f32, y: f32, z: f32, w: f32) { 43 | out.0 = x; 44 | out.1 = y; 45 | out.2 = z; 46 | out.3 = w; 47 | } 48 | 49 | pub fn add(out: &mut Vector4, a: &Vector4, b: &Vector4) { 50 | out.0 = a.0 + b.0; 51 | out.1 = a.1 + b.1; 52 | out.2 = a.2 + b.2; 53 | out.3 = a.3 + b.3; 54 | } 55 | 56 | pub fn subtract(out: &mut Vector4, a: &Vector4, b: &Vector4) { 57 | out.0 = a.0 - b.0; 58 | out.1 = a.1 - b.1; 59 | out.2 = a.2 - b.2; 60 | out.3 = a.3 - b.3; 61 | } 62 | 63 | pub fn multiply(out: &mut Vector4, a: &Vector4, b: &Vector4) { 64 | out.0 = a.0 * b.0; 65 | out.1 = a.1 * b.1; 66 | out.2 = a.2 * b.2; 67 | out.3 = a.3 * b.3; 68 | } 69 | 70 | pub fn divide(out: &mut Vector4, a: &Vector4, b: &Vector4) { 71 | out.0 = a.0 / b.0; 72 | out.1 = a.1 / b.1; 73 | out.2 = a.2 / b.2; 74 | out.3 = a.3 / b.3; 75 | } 76 | 77 | pub fn ceil(out: &mut Vector4, a: &Vector4) { 78 | out.0 = f32::ceil(a.0); 79 | out.1 = f32::ceil(a.1); 80 | out.2 = f32::ceil(a.2); 81 | out.3 = f32::ceil(a.3); 82 | } 83 | 84 | pub fn floor(out: &mut Vector4, a: &Vector4) { 85 | out.0 = f32::floor(a.0); 86 | out.1 = f32::floor(a.1); 87 | out.2 = f32::floor(a.2); 88 | out.3 = f32::floor(a.3); 89 | } 90 | 91 | pub fn min(out: &mut Vector4, a: &Vector4, b: &Vector4) { 92 | out.0 = f32::min(a.0, b.0); 93 | out.1 = f32::min(a.1, b.1); 94 | out.2 = f32::min(a.2, b.2); 95 | out.3 = f32::min(a.3, b.3); 96 | } 97 | 98 | pub fn max(out: &mut Vector4, a: &Vector4, b: &Vector4) { 99 | out.0 = f32::max(a.0, b.0); 100 | out.1 = f32::max(a.1, b.1); 101 | out.2 = f32::max(a.2, b.2); 102 | out.3 = f32::max(a.3, b.3); 103 | } 104 | 105 | pub fn round(out: &mut Vector4, a: &Vector4) { 106 | out.0 = f32::round(a.0); 107 | out.1 = f32::round(a.1); 108 | out.2 = f32::round(a.2); 109 | out.3 = f32::round(a.3); 110 | } 111 | 112 | pub fn scale(out: &mut Vector4, a: &Vector4, b: f32) { 113 | out.0 = a.0 * b; 114 | out.1 = a.1 * b; 115 | out.2 = a.2 * b; 116 | out.3 = a.3 * b; 117 | } 118 | 119 | pub fn scaleAndAdd(out: &mut Vector4, a: &Vector4, b: &Vector4, scale: f32) { 120 | out.0 = a.0 + (b.0 * scale); 121 | out.1 = a.1 + (b.1 * scale); 122 | out.2 = a.2 + (b.2 * scale); 123 | out.3 = a.3 + (b.3 * scale); 124 | } 125 | 126 | pub fn distance(a: &Vector4, b: &Vector4) -> f32 { 127 | let x = b.0 - a.0; 128 | let y = b.1 - a.1; 129 | let z = b.2 - a.2; 130 | let w = b.3 - a.3; 131 | (x.powi(2) + y.powi(2) + z.powi(2) + w.powi(2)).sqrt() 132 | } 133 | 134 | pub fn squaredDistance(a: &Vector4, b: &Vector4) -> f32 { 135 | let x = b.0 - a.0; 136 | let y = b.1 - a.1; 137 | let z = b.2 - a.2; 138 | let w = b.3 - a.3; 139 | x * x + y * y + z * z + w * w 140 | } 141 | 142 | pub fn len(a: &Vector4) -> f32 { 143 | let x = a.0; 144 | let y = a.1; 145 | let z = a.2; 146 | let w = a.3; 147 | (x.powi(2) + y.powi(2) + z.powi(2) + w.powi(2)).sqrt() 148 | } 149 | 150 | pub fn squaredLength(a: &Vector4) -> f32 { 151 | let x = a.0; 152 | let y = a.1; 153 | let z = a.2; 154 | let w = a.3; 155 | x * x + y * y + z * z + w * w 156 | } 157 | 158 | pub fn negate(out: &mut Vector4, a: &Vector4) { 159 | out.0 = -a.0; 160 | out.1 = -a.1; 161 | out.2 = -a.2; 162 | out.3 = -a.3; 163 | } 164 | 165 | pub fn inverse(out: &mut Vector4, a: &Vector4) { 166 | out.0 = 1.0 / a.0; 167 | out.1 = 1.0 / a.1; 168 | out.2 = 1.0 / a.2; 169 | out.3 = 1.0 / a.3; 170 | } 171 | 172 | pub fn normalize(out: &mut Vector4, a: &Vector4) { 173 | let x = a.0; 174 | let y = a.1; 175 | let z = a.2; 176 | let w = a.3; 177 | let mut len = x * x + y * y + z * z + w * w; 178 | if (len > EPSILON) { 179 | len = 1. / f32::sqrt(len); 180 | } 181 | out.0 = x * len; 182 | out.1 = y * len; 183 | out.2 = z * len; 184 | out.3 = w * len; 185 | } 186 | 187 | pub fn dot(a: &Vector4, b: &Vector4) -> f32 { 188 | a.0 * b.0 + a.1 * b.1 + a.2 * b.2 + a.3 * b.3 189 | } 190 | 191 | pub fn cross(out: &mut Vector4, u: &Vector4, v: &Vector4, w: &Vector4) { 192 | if (out as *const Vector4) == (u as *const Vector4) { 193 | let v0 = v.0; 194 | let v1 = v.1; 195 | let v2 = v.2; 196 | let v3 = v.3; 197 | let w0 = w.0; 198 | let w1 = w.1; 199 | let w2 = w.2; 200 | let w3 = w.3; 201 | let A = (v0 * w1) - (v1 * w0); 202 | let B = (v0 * w2) - (v2 * w0); 203 | let C = (v0 * w3) - (v3 * w0); 204 | let D = (v1 * w2) - (v2 * w1); 205 | let E = (v1 * w3) - (v3 * w1); 206 | let F = (v2 * w3) - (v3 * w2); 207 | let G = out.0; 208 | let H = out.1; 209 | let I = out.2; 210 | let J = out.3; 211 | 212 | out.0 = (H * F) - (I * E) + (J * D); 213 | out.1 = -(G * F) + (I * C) - (J * B); 214 | out.2 = (G * E) - (H * C) + (J * A); 215 | out.3 = -(G * D) + (H * B) - (I * A); 216 | return; 217 | } 218 | 219 | let v0 = v.0; 220 | let v1 = v.1; 221 | let v2 = v.2; 222 | let v3 = v.3; 223 | let w0 = w.0; 224 | let w1 = w.1; 225 | let w2 = w.2; 226 | let w3 = w.3; 227 | let A = (v0 * w1) - (v1 * w0); 228 | let B = (v0 * w2) - (v2 * w0); 229 | let C = (v0 * w3) - (v3 * w0); 230 | let D = (v1 * w2) - (v2 * w1); 231 | let E = (v1 * w3) - (v3 * w1); 232 | let F = (v2 * w3) - (v3 * w2); 233 | let G = u.0; 234 | let H = u.1; 235 | let I = u.2; 236 | let J = u.3; 237 | 238 | out.0 = (H * F) - (I * E) + (J * D); 239 | out.1 = -(G * F) + (I * C) - (J * B); 240 | out.2 = (G * E) - (H * C) + (J * A); 241 | out.3 = -(G * D) + (H * B) - (I * A); 242 | } 243 | 244 | pub fn lerp(out: &mut Vector4, a: &Vector4, b: &Vector4, t: f32) { 245 | let ax = a.0; 246 | let ay = a.1; 247 | let az = a.2; 248 | let aw = a.3; 249 | out.0 = ax + t * (b.0 - ax); 250 | out.1 = ay + t * (b.1 - ay); 251 | out.2 = az + t * (b.2 - az); 252 | out.3 = aw + t * (b.3 - aw); 253 | } 254 | 255 | pub fn random(out: &mut Vector4, scale: Option) { 256 | let mut s = 1.; 257 | match scale { 258 | Some(value) => s = value, 259 | None => {} 260 | }; 261 | let scale = s; 262 | 263 | // Marsaglia, George. Choosing a Point from the Surface of a 264 | // Sphere. Ann. f32:: Statist. 43 (1972), no. 2, 645--646. 265 | // http://projecteuclid.org/euclid.aoms/1177692644; 266 | let mut v1 = 0.; 267 | let mut v2 = 0.; 268 | let mut v3 = 0.; 269 | let mut v4 = 0.; 270 | let mut s1 = 2.; 271 | let mut s2 = 2.; 272 | 273 | while s1 > 1. { 274 | v1 = RANDOM() * 2. - 1.; 275 | v2 = RANDOM() * 2. - 1.; 276 | s1 = v1 * v1 + v2 * v2; 277 | } 278 | 279 | while s2 > 1. { 280 | v3 = RANDOM() * 2. - 1.; 281 | v4 = RANDOM() * 2. - 1.; 282 | s2 = v3 * v3 + v4 * v4; 283 | } 284 | 285 | let d = f32::sqrt((1. - s1) / s2); 286 | out.0 = scale * v1; 287 | out.1 = scale * v2; 288 | out.2 = scale * v3 * d; 289 | out.3 = scale * v4 * d; 290 | } 291 | 292 | pub fn transformMat4(out: &mut Vector4, a: &Vector4, m: &Matrix4) { 293 | let x = a.0; 294 | let y = a.1; 295 | let z = a.2; 296 | let w = a.3; 297 | out.0 = m.0 * x + m.4 * y + m.8 * z + m.12 * w; 298 | out.1 = m.1 * x + m.5 * y + m.9 * z + m.13 * w; 299 | out.2 = m.2 * x + m.6 * y + m.10 * z + m.14 * w; 300 | out.3 = m.3 * x + m.7 * y + m.11 * z + m.15 * w; 301 | } 302 | 303 | pub fn transformQuat(out: &mut Vector4, a: &Vector4, q: &Quaternion) { 304 | let x = a.0; 305 | let y = a.1; 306 | let z = a.2; 307 | let qx = q.0; 308 | let qy = q.1; 309 | let qz = q.2; 310 | let qw = q.3; 311 | 312 | // calculate quat * vec 313 | let ix = qw * x + qy * z - qz * y; 314 | let iy = qw * y + qz * x - qx * z; 315 | let iz = qw * z + qx * y - qy * x; 316 | let iw = -qx * x - qy * y - qz * z; 317 | 318 | // calculate result * inverse quat 319 | out.0 = ix * qw + iw * -qx + iy * -qz - iz * -qy; 320 | out.1 = iy * qw + iw * -qy + iz * -qx - ix * -qz; 321 | out.2 = iz * qw + iw * -qz + ix * -qy - iy * -qx; 322 | out.3 = a.3; 323 | } 324 | 325 | pub fn zero(out: &mut Vector4) { 326 | out.0 = 0.0; 327 | out.1 = 0.0; 328 | out.2 = 0.0; 329 | out.3 = 0.0; 330 | } 331 | 332 | // pub fn str(a: &Vector4) { 333 | // return "vec4(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + a.3 + ")"; 334 | // } 335 | 336 | pub fn exactEquals(a: &Vector4, b: &Vector4) -> bool { 337 | a.0 == b.0 && a.1 == b.1 && a.2 == b.2 && a.3 == b.3 338 | } 339 | 340 | pub fn equals(a: &Vector4, b: &Vector4) -> bool { 341 | let a0 = a.0; 342 | let a1 = a.1; 343 | let a2 = a.2; 344 | let a3 = a.3; 345 | let b0 = b.0; 346 | let b1 = b.1; 347 | let b2 = b.2; 348 | let b3 = b.3; 349 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 350 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 351 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 352 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 353 | } 354 | 355 | pub fn sub(out: &mut Vector4, a: &Vector4, b: &Vector4) { 356 | Vector4::subtract(out, a, b); 357 | } 358 | 359 | pub fn mul(out: &mut Vector4, a: &Vector4, b: &Vector4) { 360 | Vector4::multiply(out, a, b); 361 | } 362 | 363 | pub fn div(out: &mut Vector4, a: &Vector4, b: &Vector4) { 364 | Vector4::divide(out, a, b); 365 | } 366 | 367 | pub fn dist(a: &Vector4, b: &Vector4) -> f32 { 368 | Vector4::distance(a, b) 369 | } 370 | 371 | pub fn sqrDist(a: &Vector4, b: &Vector4) -> f32 { 372 | Vector4::squaredDistance(a, b) 373 | } 374 | 375 | pub fn sqrLen(a: &Vector4) -> f32 { 376 | Vector4::squaredLength(a) 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /src/vector3.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : vector3.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix3::*; 11 | use super::matrix4::*; 12 | use super::quaternion::*; 13 | 14 | #[wasm_bindgen] 15 | pub struct Vector3(pub f32, pub f32, pub f32); 16 | 17 | #[wasm_bindgen] 18 | impl Vector3 { 19 | #[wasm_bindgen(getter)] 20 | pub fn elements(&self) -> Box<[f32]> { 21 | Box::new([self.0, self.1, self.2]) 22 | } 23 | 24 | pub fn create() -> Vector3 { 25 | Vector3(0., 0., 0.) 26 | } 27 | 28 | pub fn clone(a: &Vector3) -> Vector3 { 29 | Vector3(a.0, a.1, a.2) 30 | } 31 | 32 | pub fn len(a: &Vector3) -> f32 { 33 | let x = a.0; 34 | let y = a.1; 35 | let z = a.2; 36 | (x.powi(2) + y.powi(2) + z.powi(2)).sqrt() 37 | } 38 | 39 | pub fn fromValues(x: f32, y: f32, z: f32) -> Vector3 { 40 | Vector3(x, y, z) 41 | } 42 | 43 | pub fn copy(out: &mut Vector3, a: &Vector3) { 44 | out.0 = a.0; 45 | out.1 = a.1; 46 | out.2 = a.2; 47 | } 48 | 49 | pub fn set(out: &mut Vector3, x: f32, y: f32, z: f32) { 50 | out.0 = x; 51 | out.1 = y; 52 | out.2 = z; 53 | } 54 | 55 | pub fn add(out: &mut Vector3, a: &Vector3, b: &Vector3) { 56 | out.0 = a.0 + b.0; 57 | out.1 = a.1 + b.1; 58 | out.2 = a.2 + b.2; 59 | } 60 | 61 | pub fn subtract(out: &mut Vector3, a: &Vector3, b: &Vector3) { 62 | out.0 = a.0 - b.0; 63 | out.1 = a.1 - b.1; 64 | out.2 = a.2 - b.2; 65 | } 66 | 67 | pub fn multiply(out: &mut Vector3, a: &Vector3, b: &Vector3) { 68 | out.0 = a.0 * b.0; 69 | out.1 = a.1 * b.1; 70 | out.2 = a.2 * b.2; 71 | } 72 | 73 | pub fn divide(out: &mut Vector3, a: &Vector3, b: &Vector3) { 74 | out.0 = a.0 / b.0; 75 | out.1 = a.1 / b.1; 76 | out.2 = a.2 / b.2; 77 | } 78 | 79 | pub fn ceil(out: &mut Vector3, a: &Vector3) { 80 | out.0 = f32::ceil(a.0); 81 | out.1 = f32::ceil(a.1); 82 | out.2 = f32::ceil(a.2); 83 | } 84 | 85 | pub fn floor(out: &mut Vector3, a: &Vector3) { 86 | out.0 = f32::floor(a.0); 87 | out.1 = f32::floor(a.1); 88 | out.2 = f32::floor(a.2); 89 | } 90 | 91 | pub fn min(out: &mut Vector3, a: &Vector3, b: &Vector3) { 92 | out.0 = f32::min(a.0, b.0); 93 | out.1 = f32::min(a.1, b.1); 94 | out.2 = f32::min(a.2, b.2); 95 | } 96 | 97 | pub fn max(out: &mut Vector3, a: &Vector3, b: &Vector3) { 98 | out.0 = f32::max(a.0, b.0); 99 | out.1 = f32::max(a.1, b.1); 100 | out.2 = f32::max(a.2, b.2); 101 | } 102 | 103 | pub fn round(out: &mut Vector3, a: &Vector3) { 104 | out.0 = f32::round(a.0); 105 | out.1 = f32::round(a.1); 106 | out.2 = f32::round(a.2); 107 | } 108 | 109 | pub fn scale(out: &mut Vector3, a: &Vector3, b: f32) { 110 | out.0 = a.0 * b; 111 | out.1 = a.1 * b; 112 | out.2 = a.2 * b; 113 | } 114 | 115 | pub fn scaleAndAdd(out: &mut Vector3, a: &Vector3, b: &Vector3, scale: f32) { 116 | out.0 = a.0 + (b.0 * scale); 117 | out.1 = a.1 + (b.1 * scale); 118 | out.2 = a.2 + (b.2 * scale); 119 | } 120 | 121 | pub fn distance(a: &Vector3, b: &Vector3) -> f32 { 122 | let x = b.0 - a.0; 123 | let y = b.1 - a.1; 124 | let z = b.2 - a.2; 125 | (x.powi(2) + y.powi(2) + z.powi(2)).sqrt() 126 | } 127 | 128 | pub fn squaredDistance(a: &Vector3, b: &Vector3) -> f32 { 129 | let x = b.0 - a.0; 130 | let y = b.1 - a.1; 131 | let z = b.2 - a.2; 132 | x * x + y * y + z * z 133 | } 134 | 135 | pub fn squaredLength(a: &Vector3) -> f32 { 136 | let x = a.0; 137 | let y = a.1; 138 | let z = a.2; 139 | x * x + y * y + z * z 140 | } 141 | 142 | pub fn negate(out: &mut Vector3, a: &Vector3) { 143 | out.0 = -a.0; 144 | out.1 = -a.1; 145 | out.2 = -a.2; 146 | } 147 | 148 | pub fn inverse(out: &mut Vector3, a: &Vector3) { 149 | out.0 = 1.0 / a.0; 150 | out.1 = 1.0 / a.1; 151 | out.2 = 1.0 / a.2; 152 | } 153 | 154 | pub fn normalize(out: &mut Vector3, a: &Vector3) { 155 | let x = a.0; 156 | let y = a.1; 157 | let z = a.2; 158 | let mut len = x * x + y * y + z * z; 159 | if len > EPSILON { 160 | //TODO: evaluate use of glm_invsqrt here? 161 | len = 1. / f32::sqrt(len); 162 | } 163 | out.0 = a.0 * len; 164 | out.1 = a.1 * len; 165 | out.2 = a.2 * len; 166 | } 167 | 168 | pub fn dot(a: &Vector3, b: &Vector3) -> f32 { 169 | return a.0 * b.0 + a.1 * b.1 + a.2 * b.2; 170 | } 171 | 172 | pub fn cross(out: &mut Vector3, a: &Vector3, b: &Vector3) { 173 | if (out as *const Vector3) == (a as *const Vector3) { 174 | let ax = out.0; 175 | let ay = out.1; 176 | let az = out.2; 177 | let bx = b.0; 178 | let by = b.1; 179 | let bz = b.2; 180 | 181 | out.0 = ay * bz - az * by; 182 | out.1 = az * bx - ax * bz; 183 | out.2 = ax * by - ay * bx; 184 | return; 185 | } 186 | 187 | if (out as *const Vector3) == (b as *const Vector3) { 188 | let ax = a.0; 189 | let ay = a.1; 190 | let az = a.2; 191 | let bx = out.0; 192 | let by = out.1; 193 | let bz = out.2; 194 | 195 | out.0 = ay * bz - az * by; 196 | out.1 = az * bx - ax * bz; 197 | out.2 = ax * by - ay * bx; 198 | return; 199 | } 200 | 201 | let ax = a.0; 202 | let ay = a.1; 203 | let az = a.2; 204 | let bx = b.0; 205 | let by = b.1; 206 | let bz = b.2; 207 | 208 | out.0 = ay * bz - az * by; 209 | out.1 = az * bx - ax * bz; 210 | out.2 = ax * by - ay * bx; 211 | } 212 | 213 | pub fn lerp(out: &mut Vector3, a: &Vector3, b: &Vector3, t: f32) { 214 | let ax = a.0; 215 | let ay = a.1; 216 | let az = a.2; 217 | out.0 = ax + t * (b.0 - ax); 218 | out.1 = ay + t * (b.1 - ay); 219 | out.2 = az + t * (b.2 - az); 220 | } 221 | 222 | pub fn hermite(out: &mut Vector3, a: &Vector3, b: &Vector3, c: &Vector3, d: &Vector3, t: f32) { 223 | let factorTimes2 = t * t; 224 | let factor1 = factorTimes2 * (2. * t - 3.) + 1.; 225 | let factor2 = factorTimes2 * (t - 2.) + t; 226 | let factor3 = factorTimes2 * (t - 1.); 227 | let factor4 = factorTimes2 * (3. - 2. * t); 228 | 229 | out.0 = a.0 * factor1 + b.0 * factor2 + c.0 * factor3 + d.0 * factor4; 230 | out.1 = a.1 * factor1 + b.1 * factor2 + c.1 * factor3 + d.1 * factor4; 231 | out.2 = a.2 * factor1 + b.2 * factor2 + c.2 * factor3 + d.2 * factor4; 232 | } 233 | 234 | pub fn bezier(out: &mut Vector3, a: &Vector3, b: &Vector3, c: &Vector3, d: &Vector3, t: f32) { 235 | let inverseFactor = 1. - t; 236 | let inverseFactorTimesTwo = inverseFactor * inverseFactor; 237 | let factorTimes2 = t * t; 238 | let factor1 = inverseFactorTimesTwo * inverseFactor; 239 | let factor2 = 3. * t * inverseFactorTimesTwo; 240 | let factor3 = 3. * factorTimes2 * inverseFactor; 241 | let factor4 = factorTimes2 * t; 242 | 243 | out.0 = a.0 * factor1 + b.0 * factor2 + c.0 * factor3 + d.0 * factor4; 244 | out.1 = a.1 * factor1 + b.1 * factor2 + c.1 * factor3 + d.1 * factor4; 245 | out.2 = a.2 * factor1 + b.2 * factor2 + c.2 * factor3 + d.2 * factor4; 246 | } 247 | 248 | pub fn random(out: &mut Vector3, scale: Option) { 249 | let mut s = 1.; 250 | match scale { 251 | Some(value) => s = value, 252 | None => {} 253 | }; 254 | let scale = s; 255 | 256 | let r = RANDOM() * 2.0 * PI; 257 | let z = (RANDOM() * 2.0) - 1.0; 258 | let zScale = f32::sqrt(1.0 - z * z) * scale; 259 | 260 | out.0 = f32::cos(r) * zScale; 261 | out.1 = f32::sin(r) * zScale; 262 | out.2 = z * scale; 263 | } 264 | 265 | pub fn transformMat4(out: &mut Vector3, a: &Vector3, m: &Matrix4) { 266 | let x = a.0; 267 | let y = a.1; 268 | let z = a.2; 269 | let mut w = m.3 * x + m.7 * y + m.11 * z + m.15; 270 | 271 | if w.abs() < EPSILON { 272 | w = 1.0; 273 | } 274 | out.0 = (m.0 * x + m.4 * y + m.8 * z + m.12) / w; 275 | out.1 = (m.1 * x + m.5 * y + m.9 * z + m.13) / w; 276 | out.2 = (m.2 * x + m.6 * y + m.10 * z + m.14) / w; 277 | } 278 | 279 | pub fn transformMat3(out: &mut Vector3, a: &Vector3, m: &Matrix3) { 280 | let x = a.0; 281 | let y = a.1; 282 | let z = a.2; 283 | out.0 = x * m.0 + y * m.3 + z * m.6; 284 | out.1 = x * m.1 + y * m.4 + z * m.7; 285 | out.2 = x * m.2 + y * m.5 + z * m.8; 286 | } 287 | 288 | pub fn transformQuat(out: &mut Vector3, a: &Vector3, q: &Quaternion) { 289 | // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed 290 | let qx = q.0; 291 | let qy = q.1; 292 | let qz = q.2; 293 | let qw = q.3; 294 | let x = a.0; 295 | let y = a.1; 296 | let z = a.2; 297 | let mut uvx = qy * z - qz * y; 298 | let mut uvy = qz * x - qx * z; 299 | let mut uvz = qx * y - qy * x; 300 | let mut uuvx = qy * uvz - qz * uvy; 301 | let mut uuvy = qz * uvx - qx * uvz; 302 | let mut uuvz = qx * uvy - qy * uvx; 303 | // vec3.scale(uv, uv, 2 * w); 304 | let w2 = qw * 2.; 305 | uvx *= w2; 306 | uvy *= w2; 307 | uvz *= w2; 308 | uuvx *= 2.; 309 | uuvy *= 2.; 310 | uuvz *= 2.; 311 | // return vec3.add(out, a, vec3.add(out, uv, uuv)); 312 | out.0 = x + uvx + uuvx; 313 | out.1 = y + uvy + uuvy; 314 | out.2 = z + uvz + uuvz; 315 | } 316 | 317 | pub fn rotateX(out: &mut Vector3, a: &Vector3, b: &Vector3, c: f32) { 318 | let p = &mut Vector3::create(); 319 | let r = &mut Vector3::create(); 320 | //Translate point to the origin 321 | p.0 = a.0 - b.0; 322 | p.1 = a.1 - b.1; 323 | p.2 = a.2 - b.2; 324 | 325 | //perform rotation 326 | r.0 = p.0; 327 | r.1 = p.1 * f32::cos(c) - p.2 * f32::sin(c); 328 | r.2 = p.1 * f32::sin(c) + p.2 * f32::cos(c); 329 | 330 | //translate to correct position 331 | out.0 = r.0 + b.0; 332 | out.1 = r.1 + b.1; 333 | out.2 = r.2 + b.2; 334 | } 335 | 336 | pub fn rotateY(out: &mut Vector3, a: &Vector3, b: &Vector3, c: f32) { 337 | let p = &mut Vector3::create(); 338 | let r = &mut Vector3::create(); 339 | //Translate point to the origin 340 | p.0 = a.0 - b.0; 341 | p.1 = a.1 - b.1; 342 | p.2 = a.2 - b.2; 343 | 344 | //perform rotation 345 | r.0 = p.2 * f32::sin(c) + p.0 * f32::cos(c); 346 | r.1 = p.1; 347 | r.2 = p.2 * f32::cos(c) - p.0 * f32::sin(c); 348 | 349 | //translate to correct position 350 | out.0 = r.0 + b.0; 351 | out.1 = r.1 + b.1; 352 | out.2 = r.2 + b.2; 353 | } 354 | 355 | pub fn rotateZ(out: &mut Vector3, a: &Vector3, b: &Vector3, c: f32) { 356 | let p = &mut Vector3::create(); 357 | let r = &mut Vector3::create(); 358 | //Translate point to the origin 359 | p.0 = a.0 - b.0; 360 | p.1 = a.1 - b.1; 361 | p.2 = a.2 - b.2; 362 | 363 | //perform rotation 364 | r.0 = p.0 * f32::cos(c) - p.1 * f32::sin(c); 365 | r.1 = p.0 * f32::sin(c) + p.1 * f32::cos(c); 366 | r.2 = p.2; 367 | 368 | //translate to correct position 369 | out.0 = r.0 + b.0; 370 | out.1 = r.1 + b.1; 371 | out.2 = r.2 + b.2; 372 | } 373 | 374 | pub fn angle(a: &Vector3, b: &Vector3) -> f32 { 375 | let tempA = &mut Vector3::fromValues(a.0, a.1, a.2); 376 | let tempB = &mut Vector3::fromValues(b.0, b.1, b.2); 377 | 378 | Vector3::normalize(tempA, &Vector3::clone(tempA)); 379 | Vector3::normalize(tempB, &Vector3::clone(tempB)); 380 | 381 | let cosine = Vector3::dot(tempA, tempB); 382 | 383 | if (cosine > 1.0) { 384 | 0. 385 | } else if (cosine < -1.0) { 386 | PI 387 | } else { 388 | f32::acos(cosine) 389 | } 390 | } 391 | 392 | pub fn zero(out: &mut Vector3) { 393 | out.0 = 0.0; 394 | out.1 = 0.0; 395 | out.2 = 0.0; 396 | } 397 | 398 | // pub fn str(a: &Vector3) { 399 | // return "vec3(" + a.0 + ", " + a.1 + ", " + a.2 + ")"; 400 | // } 401 | 402 | pub fn exactEquals(a: &Vector3, b: &Vector3) -> bool { 403 | a.0 == b.0 && a.1 == b.1 && a.2 == b.2 404 | } 405 | 406 | pub fn equals(a: &Vector3, b: &Vector3) -> bool { 407 | let a0 = a.0; 408 | let a1 = a.1; 409 | let a2 = a.2; 410 | let b0 = b.0; 411 | let b1 = b.1; 412 | let b2 = b.2; 413 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 414 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 415 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 416 | } 417 | 418 | pub fn sub(out: &mut Vector3, a: &Vector3, b: &Vector3) { 419 | Vector3::subtract(out, a, b); 420 | } 421 | 422 | pub fn mul(out: &mut Vector3, a: &Vector3, b: &Vector3) { 423 | Vector3::multiply(out, a, b); 424 | } 425 | 426 | pub fn div(out: &mut Vector3, a: &Vector3, b: &Vector3) { 427 | Vector3::divide(out, a, b); 428 | } 429 | 430 | pub fn dist(a: &Vector3, b: &Vector3) { 431 | Vector3::distance(a, b); 432 | } 433 | 434 | pub fn sqrDist(a: &Vector3, b: &Vector3) { 435 | Vector3::squaredDistance(a, b); 436 | } 437 | 438 | pub fn sqrLen(a: &Vector3) -> f32 { 439 | Vector3::squaredLength(a) 440 | } 441 | } 442 | -------------------------------------------------------------------------------- /spec/mat2d-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from './spec-helper'; 2 | import { 3 | init, 4 | Vector2 as vec2, 5 | Vector3 as vec3, 6 | Vector4 as vec4, 7 | Matrix2 as mat2, 8 | Matrix2d as mat2d, 9 | Matrix3 as mat3, 10 | Matrix4 as mat4, 11 | Quaternion as quat, 12 | Quaternion2 as quat2, 13 | } from '../pkg/gl_matrix_wasm'; 14 | 15 | describe("mat2d", function() { 16 | let out, matA, matB, oldA, oldB, identity, result; 17 | 18 | before(done => { 19 | init().then(() => done()); 20 | }); 21 | 22 | beforeEach(function() { 23 | matA = mat2d.fromValues(1, 2, 24 | 3, 4, 25 | 5, 6); 26 | 27 | oldA = mat2d.fromValues(1, 2, 28 | 3, 4, 29 | 5, 6); 30 | 31 | matB = mat2d.fromValues(7, 8, 32 | 9, 10, 33 | 11, 12); 34 | 35 | oldB = mat2d.fromValues(7, 8, 36 | 9, 10, 37 | 11, 12); 38 | 39 | out = mat2d.fromValues(0, 0, 40 | 0, 0, 41 | 0, 0); 42 | 43 | identity = mat2d.fromValues(1, 0, 44 | 0, 1, 45 | 0, 0); 46 | }); 47 | 48 | describe("create", function() { 49 | beforeEach(function() { result = mat2d.create(); }); 50 | it("should return a 6 element array initialized to a 2x3 identity matrix", function() { expect(result).toBeEqualish(identity); }); 51 | }); 52 | 53 | describe("clone", function() { 54 | beforeEach(function() { result = mat2d.clone(matA); }); 55 | it("should return a 6 element array initialized to the values in matA", function() { expect(result).toBeEqualish(matA); }); 56 | }); 57 | 58 | describe("copy", function() { 59 | beforeEach(function() { result = mat2d.copy(out, matA); }); 60 | it("should place values into out", function() { expect(out).toBeEqualish(matA); }); 61 | 62 | }); 63 | 64 | describe("identity", function() { 65 | beforeEach(function() { result = mat2d.identity(out); }); 66 | it("should place values into out", function() { expect(out).toBeEqualish(identity); }); 67 | 68 | }); 69 | 70 | describe("invert", function() { 71 | describe("with a separate output matrix", function() { 72 | beforeEach(function() { result = mat2d.invert(out, matA); }); 73 | 74 | it("should place values into out", function() { expect(out).toBeEqualish([ -2, 1, 1.5, -0.5, 1, -2 ]); }); 75 | 76 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 77 | }); 78 | 79 | describe("when matA is the output matrix", function() { 80 | beforeEach(function() { result = mat2d.invert(matA, matA); }); 81 | 82 | it("should place values into matA", function() { expect(matA).toBeEqualish([ -2, 1, 1.5, -0.5, 1, -2 ]); }); 83 | 84 | }); 85 | }); 86 | 87 | describe("determinant", function() { 88 | beforeEach(function() { result = mat2d.determinant(matA); }); 89 | 90 | it("should return the determinant", function() { expect(result).toEqual(-2); }); 91 | }); 92 | 93 | describe("multiply", function() { 94 | 95 | 96 | describe("with a separate output matrix", function() { 97 | beforeEach(function() { result = mat2d.multiply(out, matA, matB); }); 98 | 99 | it("should place values into out", function() { expect(out).toBeEqualish([31, 46, 39, 58, 52, 76]); }); 100 | 101 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 102 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 103 | }); 104 | 105 | describe("when matA is the output matrix", function() { 106 | beforeEach(function() { result = mat2d.multiply(matA, matA, matB); }); 107 | 108 | it("should place values into matA", function() { expect(matA).toBeEqualish([31, 46, 39, 58, 52, 76]); }); 109 | 110 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 111 | }); 112 | 113 | describe("when matB is the output matrix", function() { 114 | beforeEach(function() { result = mat2d.multiply(matB, matA, matB); }); 115 | 116 | it("should place values into matB", function() { expect(matB).toBeEqualish([31, 46, 39, 58, 52, 76]); }); 117 | 118 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 119 | }); 120 | }); 121 | 122 | describe("rotate", function() { 123 | describe("with a separate output matrix", function() { 124 | beforeEach(function() { result = mat2d.rotate(out, matA, Math.PI * 0.5); }); 125 | 126 | it("should place values into out", function() { expect(out).toBeEqualish([3, 4, -1, -2, 5, 6]); }); 127 | 128 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 129 | }); 130 | 131 | describe("when matA is the output matrix", function() { 132 | beforeEach(function() { result = mat2d.rotate(matA, matA, Math.PI * 0.5); }); 133 | 134 | it("should place values into matA", function() { expect(matA).toBeEqualish([3, 4, -1, -2, 5, 6]); }); 135 | 136 | }); 137 | }); 138 | 139 | describe("scale", function() { 140 | let vecA; 141 | beforeEach(function() { vecA = vec2.fromValues(2, 3); }); 142 | 143 | describe("with a separate output matrix", function() { 144 | beforeEach(function() { result = mat2d.scale(out, matA, vecA); }); 145 | 146 | it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 9, 12, 5, 6]); }); 147 | 148 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 149 | }); 150 | 151 | describe("when matA is the output matrix", function() { 152 | beforeEach(function() { result = mat2d.scale(matA, matA, vecA); }); 153 | 154 | it("should place values into matA", function() { expect(matA).toBeEqualish([2, 4, 9, 12, 5, 6]); }); 155 | 156 | }); 157 | }); 158 | 159 | describe("translate", function() { 160 | let vecA; 161 | beforeEach(function() { vecA = vec2.fromValues(2, 3); }); 162 | 163 | describe("with a separate output matrix", function() { 164 | beforeEach(function() { result = mat2d.translate(out, matA, vecA); }); 165 | 166 | it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4, 16, 22]); }); 167 | 168 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 169 | }); 170 | 171 | describe("when matA is the output matrix", function() { 172 | beforeEach(function() { result = mat2d.translate(matA, matA, vecA); }); 173 | 174 | it("should place values into matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 16, 22]); }); 175 | 176 | }); 177 | }); 178 | 179 | describe("frob", function() { 180 | beforeEach(function() { result = mat2d.frob(matA); }); 181 | it("should return the Frobenius Norm of the matrix", function() { expect(result).toEqual( 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)); }); 182 | }); 183 | 184 | describe("add", function() { 185 | describe("with a separate output matrix", function() { 186 | beforeEach(function() { result = mat2d.add(out, matA, matB); }); 187 | 188 | it("should place values into out", function() { expect(out).toBeEqualish([8, 10, 12, 14, 16, 18]); }); 189 | 190 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 191 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 192 | }); 193 | 194 | describe("when matA is the output matrix", function() { 195 | beforeEach(function() { result = mat2d.add(matA, matA, matB); }); 196 | 197 | it("should place values into matA", function() { expect(matA).toBeEqualish([8, 10, 12, 14, 16, 18]); }); 198 | 199 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 200 | }); 201 | 202 | describe("when matB is the output matrix", function() { 203 | beforeEach(function() { result = mat2d.add(matB, matA, matB); }); 204 | 205 | it("should place values into matB", function() { expect(matB).toBeEqualish([8, 10, 12, 14, 16, 18]); }); 206 | 207 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 208 | }); 209 | }); 210 | 211 | describe("subtract", function() { 212 | 213 | 214 | describe("with a separate output matrix", function() { 215 | beforeEach(function() { result = mat2d.subtract(out, matA, matB); }); 216 | 217 | it("should place values into out", function() { expect(out).toBeEqualish([-6, -6, -6, -6, -6, -6]); }); 218 | 219 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 220 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 221 | }); 222 | 223 | describe("when matA is the output matrix", function() { 224 | beforeEach(function() { result = mat2d.subtract(matA, matA, matB); }); 225 | 226 | it("should place values into matA", function() { expect(matA).toBeEqualish([-6, -6, -6, -6, -6, -6]); }); 227 | 228 | it("should not modify matB", function() { expect(matB).toBeEqualish(oldB); }); 229 | }); 230 | 231 | describe("when matB is the output matrix", function() { 232 | beforeEach(function() { result = mat2d.subtract(matB, matA, matB); }); 233 | 234 | it("should place values into matB", function() { expect(matB).toBeEqualish([-6, -6, -6, -6, -6, -6]); }); 235 | 236 | it("should not modify matA", function() { expect(matA).toBeEqualish(oldA); }); 237 | }); 238 | }); 239 | 240 | describe("fromValues", function() { 241 | beforeEach(function() { result = mat2d.fromValues(1, 2, 3, 4, 5, 6); }); 242 | it("should return a 6 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2, 3, 4, 5, 6]); }); 243 | }); 244 | 245 | describe("set", function() { 246 | beforeEach(function() { result = mat2d.set(out, 1, 2, 3, 4, 5, 6); }); 247 | it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4, 5, 6]); }); 248 | 249 | }); 250 | 251 | describe("multiplyScalar", function() { 252 | describe("with a separate output matrix", function() { 253 | beforeEach(function() { result = mat2d.multiplyScalar(out, matA, 2); }); 254 | 255 | it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 6, 8, 10, 12]); }); 256 | 257 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6]); }); 258 | }); 259 | 260 | describe("when matA is the output matrix", function() { 261 | beforeEach(function() { result = mat2d.multiplyScalar(matA, matA, 2); }); 262 | 263 | it("should place values into matA", function() { expect(matA).toBeEqualish([2, 4, 6, 8, 10, 12]); }); 264 | 265 | }); 266 | }); 267 | 268 | describe("multiplyScalarAndAdd", function() { 269 | describe("with a separate output matrix", function() { 270 | beforeEach(function() { result = mat2d.multiplyScalarAndAdd(out, matA, matB, 0.5); }); 271 | 272 | it("should place values into out", function() { expect(out).toBeEqualish([4.5, 6, 7.5, 9, 10.5, 12]); }); 273 | 274 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6]); }); 275 | it("should not modify matB", function() { expect(matB).toBeEqualish([7, 8, 9, 10, 11, 12]); }); 276 | }); 277 | 278 | describe("when matA is the output matrix", function() { 279 | beforeEach(function() { result = mat2d.multiplyScalarAndAdd(matA, matA, matB, 0.5); }); 280 | 281 | it("should place values into matA", function() { expect(matA).toBeEqualish([4.5, 6, 7.5, 9, 10.5, 12]); }); 282 | 283 | it("should not modify matB", function() { expect(matB).toBeEqualish([7, 8, 9, 10, 11, 12]); }); 284 | }); 285 | 286 | describe("when matB is the output matrix", function() { 287 | beforeEach(function() { result = mat2d.multiplyScalarAndAdd(matB, matA, matB, 0.5); }); 288 | 289 | it("should place values into matB", function() { expect(matB).toBeEqualish([4.5, 6, 7.5, 9, 10.5, 12]); }); 290 | 291 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6]); }); 292 | }); 293 | }); 294 | 295 | describe("exactEquals", function() { 296 | let matC, r0, r1; 297 | beforeEach(function() { 298 | matA = mat2d.fromValues(0, 1, 2, 3, 4, 5); 299 | matB = mat2d.fromValues(0, 1, 2, 3, 4, 5); 300 | matC = mat2d.fromValues(1, 2, 3, 4, 5, 6); 301 | r0 = mat2d.exactEquals(matA, matB); 302 | r1 = mat2d.exactEquals(matA, matC); 303 | }); 304 | 305 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 306 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 307 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3, 4, 5]); }); 308 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3, 4, 5]); }); 309 | }); 310 | 311 | describe("equals", function() { 312 | let matC, matD, r0, r1, r2; 313 | beforeEach(function() { 314 | matA = mat2d.fromValues(0, 1, 2, 3, 4, 5); 315 | matB = mat2d.fromValues(0, 1, 2, 3, 4, 5); 316 | matC = mat2d.fromValues(1, 2, 3, 4, 5, 6); 317 | matD = mat2d.fromValues(1e-16, 1, 2, 3, 4, 5); 318 | r0 = mat2d.equals(matA, matB); 319 | r1 = mat2d.equals(matA, matC); 320 | r2 = mat2d.equals(matA, matD); 321 | }); 322 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 323 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 324 | it("should return true for close but not identical matrices", function() { expect(r2).toBe(true); }); 325 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3, 4, 5]); }); 326 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3, 4, 5]); }); 327 | }); 328 | 329 | }); 330 | -------------------------------------------------------------------------------- /spec/mat2-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from './spec-helper'; 2 | import { 3 | init, 4 | Vector2 as vec2, 5 | Vector3 as vec3, 6 | Vector4 as vec4, 7 | Matrix2 as mat2, 8 | Matrix2d as mat2d, 9 | Matrix3 as mat3, 10 | Matrix4 as mat4, 11 | Quaternion as quat, 12 | Quaternion2 as quat2, 13 | } from '../pkg/gl_matrix_wasm'; 14 | 15 | describe("mat2", function() { 16 | let out, matA, matB, identity, result; 17 | 18 | before(done => { 19 | init().then(() => done()); 20 | }); 21 | 22 | beforeEach(function() { 23 | matA = mat2.fromValues(1, 2, 24 | 3, 4); 25 | 26 | matB = mat2.fromValues(5, 6, 27 | 7, 8); 28 | 29 | out = mat2.fromValues(0, 0, 30 | 0, 0); 31 | 32 | identity = mat2.fromValues( 33 | 1, 0, 34 | 0, 1 35 | ); 36 | }); 37 | 38 | describe("create", function() { 39 | beforeEach(function() { result = mat2.create(); }); 40 | it("should return a 4 element array initialized to a 2x2 identity matrix", function() { expect(result).toBeEqualish(identity); }); 41 | }); 42 | 43 | describe("clone", function() { 44 | beforeEach(function() { result = mat2.clone(matA); }); 45 | it("should return a 4 element array initialized to the values in matA", function() { expect(result).toBeEqualish(matA); }); 46 | }); 47 | 48 | describe("copy", function() { 49 | beforeEach(function() { result = mat2.copy(out, matA); }); 50 | it("should place values into out", function() {expect(out).toBeEqualish(matA); }); 51 | 52 | }); 53 | 54 | describe("identity", function() { 55 | beforeEach(function() { result = mat2.identity(out); }); 56 | it("should place values into out", function() { expect(out).toBeEqualish(identity); }); 57 | 58 | }); 59 | 60 | describe("transpose", function() { 61 | describe("with a separate output matrix", function() { 62 | beforeEach(function() { result = mat2.transpose(out, matA); }); 63 | 64 | it("should place values into out", function() { expect(out).toBeEqualish([1, 3, 2, 4]); }); 65 | 66 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 67 | }); 68 | 69 | describe("when matA is the output matrix", function() { 70 | beforeEach(function() { result = mat2.transpose(matA, matA); }); 71 | 72 | it("should place values into matA", function() { expect(matA).toBeEqualish([1, 3, 2, 4]); }); 73 | 74 | }); 75 | }); 76 | 77 | describe("invert", function() { 78 | describe("with a separate output matrix", function() { 79 | beforeEach(function() { result = mat2.invert(out, matA); }); 80 | 81 | it("should place values into out", function() { expect(out).toBeEqualish([-2, 1, 1.5, -0.5]); }); 82 | 83 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 84 | }); 85 | 86 | describe("when matA is the output matrix", function() { 87 | beforeEach(function() { result = mat2.invert(matA, matA); }); 88 | 89 | it("should place values into matA", function() { expect(matA).toBeEqualish([-2, 1, 1.5, -0.5]); }); 90 | 91 | }); 92 | }); 93 | 94 | describe("adjoint", function() { 95 | describe("with a separate output matrix", function() { 96 | beforeEach(function() { result = mat2.adjoint(out, matA); }); 97 | 98 | it("should place values into out", function() { expect(out).toBeEqualish([4, -2, -3, 1]); }); 99 | 100 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 101 | }); 102 | 103 | describe("when matA is the output matrix", function() { 104 | beforeEach(function() { result = mat2.adjoint(matA, matA); }); 105 | 106 | it("should place values into matA", function() { 107 | expect(matA).toBeEqualish([4, -2, -3, 1]); 108 | }); 109 | 110 | }); 111 | }); 112 | 113 | describe("determinant", function() { 114 | beforeEach(function() { result = mat2.determinant(matA); }); 115 | 116 | it("should return the determinant", function() { expect(result).toEqual(-2); }); 117 | }); 118 | 119 | describe("multiply", function() { 120 | 121 | 122 | describe("with a separate output matrix", function() { 123 | beforeEach(function() { result = mat2.multiply(out, matA, matB); }); 124 | 125 | it("should place values into out", function() { expect(out).toBeEqualish([23, 34, 31, 46]); }); 126 | 127 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 128 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 129 | }); 130 | 131 | describe("when matA is the output matrix", function() { 132 | beforeEach(function() { result = mat2.multiply(matA, matA, matB); }); 133 | 134 | it("should place values into matA", function() { expect(matA).toBeEqualish([23, 34, 31, 46]); }); 135 | 136 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 137 | }); 138 | 139 | describe("when matB is the output matrix", function() { 140 | beforeEach(function() { result = mat2.multiply(matB, matA, matB); }); 141 | 142 | it("should place values into matB", function() { expect(matB).toBeEqualish([23, 34, 31, 46]); }); 143 | 144 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 145 | }); 146 | }); 147 | 148 | describe("rotate", function() { 149 | describe("with a separate output matrix", function() { 150 | beforeEach(function() { result = mat2.rotate(out, matA, Math.PI * 0.5); }); 151 | 152 | it("should place values into out", function() { expect(out).toBeEqualish([3, 4, -1, -2]); }); 153 | 154 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 155 | }); 156 | 157 | describe("when matA is the output matrix", function() { 158 | beforeEach(function() { result = mat2.rotate(matA, matA, Math.PI * 0.5); }); 159 | 160 | it("should place values into matA", function() { expect(matA).toBeEqualish([3, 4, -1, -2]); }); 161 | 162 | }); 163 | }); 164 | 165 | describe("scale", function() { 166 | let vecA; 167 | beforeEach(function() { vecA = vec2.fromValues(2, 3); }); 168 | 169 | describe("with a separate output matrix", function() { 170 | beforeEach(function() { result = mat2.scale(out, matA, vecA); }); 171 | 172 | it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 9, 12]); }); 173 | 174 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 175 | }); 176 | 177 | describe("when matA is the output matrix", function() { 178 | beforeEach(function() { result = mat2.scale(matA, matA, vecA); }); 179 | 180 | it("should place values into matA", function() { expect(matA).toBeEqualish([2, 4, 9, 12]); }); 181 | 182 | }); 183 | }); 184 | 185 | describe("frob", function() { 186 | beforeEach(function() { 187 | result = mat2.frob(matA); 188 | }); 189 | it("should return the Frobenius Norm of the matrix", function() { expect(result).toEqual( Math.sqrt(Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(3, 2) + Math.pow(4, 2))); }); 190 | }); 191 | 192 | describe("LDU", function() { 193 | let L, D, U, L_result, D_result, U_result; 194 | beforeEach(function() { 195 | L = mat2.create(); 196 | D = mat2.create(); 197 | U = mat2.create(); 198 | mat2.LDU(L, D, U, mat2.fromValues(4,3,6,3)); 199 | L_result = mat2.create(); 200 | L_result[2] = 1.5; 201 | D_result = mat2.create(); 202 | U_result = mat2.create(); 203 | U_result[0] = 4; U_result[1] = 3; U_result[3] = -1.5; 204 | }); 205 | it("should return a lower triangular, a diagonal and an upper triangular matrix", function() { 206 | expect(L).toBeEqualish(L_result); 207 | expect(D).toBeEqualish(D_result); 208 | expect(U).toBeEqualish(U_result); 209 | }); 210 | }); 211 | 212 | describe("add", function() { 213 | describe("with a separate output matrix", function() { 214 | beforeEach(function() { result = mat2.add(out, matA, matB); }); 215 | 216 | it("should place values into out", function() { expect(out).toBeEqualish([6, 8, 10, 12]); }); 217 | 218 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 219 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 220 | }); 221 | 222 | describe("when matA is the output matrix", function() { 223 | beforeEach(function() { result = mat2.add(matA, matA, matB); }); 224 | 225 | it("should place values into matA", function() { expect(matA).toBeEqualish([6, 8, 10, 12]); }); 226 | 227 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 228 | }); 229 | 230 | describe("when matB is the output matrix", function() { 231 | beforeEach(function() { result = mat2.add(matB, matA, matB); }); 232 | 233 | it("should place values into matB", function() { expect(matB).toBeEqualish([6, 8, 10, 12]); }); 234 | 235 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 236 | }); 237 | }); 238 | 239 | describe("subtract", function() { 240 | 241 | 242 | describe("with a separate output matrix", function() { 243 | beforeEach(function() { result = mat2.subtract(out, matA, matB); }); 244 | 245 | it("should place values into out", function() { expect(out).toBeEqualish([-4, -4, -4, -4]); }); 246 | 247 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 248 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 249 | }); 250 | 251 | describe("when matA is the output matrix", function() { 252 | beforeEach(function() { result = mat2.subtract(matA, matA, matB); }); 253 | 254 | it("should place values into matA", function() { expect(matA).toBeEqualish([-4, -4, -4, -4]); }); 255 | 256 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 257 | }); 258 | 259 | describe("when matB is the output matrix", function() { 260 | beforeEach(function() { result = mat2.subtract(matB, matA, matB); }); 261 | 262 | it("should place values into matB", function() { expect(matB).toBeEqualish([-4, -4, -4, -4]); }); 263 | 264 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 265 | }); 266 | }); 267 | 268 | describe("fromValues", function() { 269 | beforeEach(function() { result = mat2.fromValues(1, 2, 3, 4); }); 270 | it("should return a 4 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2, 3, 4]); }); 271 | }); 272 | 273 | describe("set", function() { 274 | beforeEach(function() { result = mat2.set(out, 1, 2, 3, 4); }); 275 | it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4]); }); 276 | 277 | }); 278 | 279 | describe("multiplyScalar", function() { 280 | describe("with a separate output matrix", function() { 281 | beforeEach(function() { result = mat2.multiplyScalar(out, matA, 2); }); 282 | 283 | it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 6, 8]); }); 284 | 285 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 286 | }); 287 | 288 | describe("when matA is the output matrix", function() { 289 | beforeEach(function() { result = mat2.multiplyScalar(matA, matA, 2); }); 290 | 291 | it("should place values into matA", function() { expect(matA).toBeEqualish([2, 4, 6, 8]); }); 292 | 293 | }); 294 | }); 295 | 296 | describe("multiplyScalarAndAdd", function() { 297 | describe("with a separate output matrix", function() { 298 | beforeEach(function() { result = mat2.multiplyScalarAndAdd(out, matA, matB, 0.5); }); 299 | 300 | it("should place values into out", function() { expect(out).toBeEqualish([3.5, 5, 6.5, 8]); }); 301 | 302 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 303 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 304 | }); 305 | 306 | describe("when matA is the output matrix", function() { 307 | beforeEach(function() { result = mat2.multiplyScalarAndAdd(matA, matA, matB, 0.5); }); 308 | 309 | it("should place values into matA", function() { expect(matA).toBeEqualish([3.5, 5, 6.5, 8]); }); 310 | 311 | it("should not modify matB", function() { expect(matB).toBeEqualish([5, 6, 7, 8]); }); 312 | }); 313 | 314 | describe("when matB is the output matrix", function() { 315 | beforeEach(function() { result = mat2.multiplyScalarAndAdd(matB, matA, matB, 0.5); }); 316 | 317 | it("should place values into matB", function() { expect(matB).toBeEqualish([3.5, 5, 6.5, 8]); }); 318 | 319 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); }); 320 | }); 321 | }); 322 | 323 | describe("exactEquals", function() { 324 | let matC, r0, r1, r2; 325 | beforeEach(function() { 326 | matA = mat2.fromValues(0, 1, 2, 3); 327 | matB = mat2.fromValues(0, 1, 2, 3); 328 | matC = mat2.fromValues(1, 2, 3, 4); 329 | r0 = mat2.exactEquals(matA, matB); 330 | r1 = mat2.exactEquals(matA, matC); 331 | }); 332 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 333 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 334 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3]); }); 335 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3]); }); 336 | }); 337 | 338 | describe("equals", function() { 339 | let matC, matD, r0, r1, r2; 340 | beforeEach(function() { 341 | matA = mat2.fromValues(0, 1, 2, 3); 342 | matB = mat2.fromValues(0, 1, 2, 3); 343 | matC = mat2.fromValues(1, 2, 3, 4); 344 | matD = mat2.fromValues(1e-16, 1, 2, 3); 345 | r0 = mat2.equals(matA, matB); 346 | r1 = mat2.equals(matA, matC); 347 | r2 = mat2.equals(matA, matD); 348 | }); 349 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 350 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 351 | it("should return true for close but not identical matrices", function() { expect(r2).toBe(true); }); 352 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3]); }); 353 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3]); }); 354 | }); 355 | 356 | }); 357 | -------------------------------------------------------------------------------- /src/quaternion.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : quaternion.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix3::*; 11 | use super::vector3::*; 12 | 13 | #[wasm_bindgen] 14 | pub struct Quaternion(pub f32, pub f32, pub f32, pub f32); 15 | 16 | #[wasm_bindgen] 17 | impl Quaternion { 18 | #[wasm_bindgen(getter)] 19 | pub fn elements(&self) -> Box<[f32]> { 20 | Box::new([self.0, self.1, self.2, self.3]) 21 | } 22 | 23 | pub fn create() -> Quaternion { 24 | Quaternion(0., 0., 0., 1.) 25 | } 26 | 27 | pub fn identity(out: &mut Quaternion) { 28 | out.0 = 0.; 29 | out.1 = 0.; 30 | out.2 = 0.; 31 | out.3 = 1.; 32 | } 33 | 34 | pub fn setAxisAngle(out: &mut Quaternion, axis: &Vector3, rad: f32) { 35 | let rad = rad * 0.5; 36 | let s = f32::sin(rad); 37 | out.0 = s * axis.0; 38 | out.1 = s * axis.1; 39 | out.2 = s * axis.2; 40 | out.3 = f32::cos(rad); 41 | } 42 | 43 | pub fn getAxisAngle(out_axis: &mut Vector3, q: &Quaternion) -> f32 { 44 | let rad = f32::acos(q.3) * 2.0; 45 | let s = f32::sin(rad / 2.0); 46 | if (s > EPSILON) { 47 | out_axis.0 = q.0 / s; 48 | out_axis.1 = q.1 / s; 49 | out_axis.2 = q.2 / s; 50 | } else { 51 | // If s is zero, return any axis (no rotation - axis does not matter) 52 | out_axis.0 = 1.; 53 | out_axis.1 = 0.; 54 | out_axis.2 = 0.; 55 | } 56 | rad 57 | } 58 | 59 | pub fn multiply(out: &mut Quaternion, a: &Quaternion, b: &Quaternion) { 60 | let ax = a.0; 61 | let ay = a.1; 62 | let az = a.2; 63 | let aw = a.3; 64 | let bx = b.0; 65 | let by = b.1; 66 | let bz = b.2; 67 | let bw = b.3; 68 | 69 | out.0 = ax * bw + aw * bx + ay * bz - az * by; 70 | out.1 = ay * bw + aw * by + az * bx - ax * bz; 71 | out.2 = az * bw + aw * bz + ax * by - ay * bx; 72 | out.3 = aw * bw - ax * bx - ay * by - az * bz; 73 | } 74 | 75 | pub fn rotateX(out: &mut Quaternion, a: &Quaternion, rad: f32) { 76 | let rad = rad * 0.5; 77 | 78 | let ax = a.0; 79 | let ay = a.1; 80 | let az = a.2; 81 | let aw = a.3; 82 | let mut bx = f32::sin(rad); 83 | let mut bw = f32::cos(rad); 84 | 85 | if bx.abs() < EPSILON { 86 | bx = 0.; 87 | } 88 | 89 | if bw.abs() < EPSILON { 90 | bw = 0.; 91 | } 92 | 93 | out.0 = ax * bw + aw * bx; 94 | out.1 = ay * bw + az * bx; 95 | out.2 = az * bw - ay * bx; 96 | out.3 = aw * bw - ax * bx; 97 | } 98 | 99 | pub fn rotateY(out: &mut Quaternion, a: &Quaternion, rad: f32) { 100 | let rad = rad * 0.5; 101 | 102 | let ax = a.0; 103 | let ay = a.1; 104 | let az = a.2; 105 | let aw = a.3; 106 | let mut by = f32::sin(rad); 107 | let mut bw = f32::cos(rad); 108 | 109 | if by.abs() < EPSILON { 110 | by = 0.; 111 | } 112 | 113 | if bw.abs() < EPSILON { 114 | bw = 0.; 115 | } 116 | 117 | out.0 = ax * bw - az * by; 118 | out.1 = ay * bw + aw * by; 119 | out.2 = az * bw + ax * by; 120 | out.3 = aw * bw - ay * by; 121 | } 122 | 123 | pub fn rotateZ(out: &mut Quaternion, a: &Quaternion, rad: f32) { 124 | let rad = rad * 0.5; 125 | 126 | let ax = a.0; 127 | let ay = a.1; 128 | let az = a.2; 129 | let aw = a.3; 130 | let mut bz = f32::sin(rad); 131 | let mut bw = f32::cos(rad); 132 | 133 | if bz.abs() < EPSILON { 134 | bz = 0.; 135 | } 136 | 137 | if bw.abs() < EPSILON { 138 | bw = 0.; 139 | } 140 | 141 | out.0 = ax * bw + ay * bz; 142 | out.1 = ay * bw - ax * bz; 143 | out.2 = az * bw + aw * bz; 144 | out.3 = aw * bw - az * bz; 145 | } 146 | 147 | pub fn calculateW(out: &mut Quaternion, a: &Quaternion) { 148 | let x = a.0; 149 | let y = a.1; 150 | let z = a.2; 151 | 152 | out.0 = x; 153 | out.1 = y; 154 | out.2 = z; 155 | out.3 = f32::sqrt(f32::abs(1.0 - x * x - y * y - z * z)); 156 | } 157 | 158 | pub fn slerp(out: &mut Quaternion, a: &Quaternion, b: &Quaternion, t: f32) { 159 | // benchmarks: 160 | // http://jsperf.com/quaternion-slerp-implementations 161 | let ax = a.0; 162 | let ay = a.1; 163 | let az = a.2; 164 | let aw = a.3; 165 | let mut bx = b.0; 166 | let mut by = b.1; 167 | let mut bz = b.2; 168 | let mut bw = b.3; 169 | 170 | let mut omega = 0.; 171 | let mut sinom = 0.; 172 | let mut scale0 = 0.; 173 | let mut scale1 = 0.; 174 | // calc cosine 175 | let mut cosom = ax * bx + ay * by + az * bz + aw * bw; 176 | 177 | if cosom.abs() < EPSILON { 178 | cosom = 0.; 179 | } 180 | 181 | // adjust signs (if necessary) 182 | if cosom < 0. { 183 | cosom = -cosom; 184 | bx = -bx; 185 | by = -by; 186 | bz = -bz; 187 | bw = -bw; 188 | } 189 | 190 | // calculate coefficients 191 | if (1.0 - cosom) > EPSILON { 192 | // standard case (slerp) 193 | omega = f32::acos(cosom); 194 | sinom = f32::sin(omega); 195 | scale0 = f32::sin((1.0 - t) * omega) / sinom; 196 | scale1 = f32::sin(t * omega) / sinom; 197 | } else { 198 | // "from" and "to" quaternions are very close 199 | // ... so we can do a linear interpolation 200 | scale0 = 1.0 - t; 201 | scale1 = t; 202 | } 203 | 204 | // calculate final values 205 | out.0 = scale0 * ax + scale1 * bx; 206 | out.1 = scale0 * ay + scale1 * by; 207 | out.2 = scale0 * az + scale1 * bz; 208 | out.3 = scale0 * aw + scale1 * bw; 209 | } 210 | 211 | pub fn random(out: &mut Quaternion) { 212 | // Implementation of http://planning.cs.uiuc.edu/node198.html 213 | // TODO: Calling random 3 times is probably not the fastest solution 214 | let u1 = RANDOM(); 215 | let u2 = RANDOM(); 216 | let u3 = RANDOM(); 217 | 218 | let sqrt1MinusU1 = f32::sqrt(1. - u1); 219 | let sqrtU1 = f32::sqrt(u1); 220 | 221 | out.0 = sqrt1MinusU1 * f32::sin(2.0 * PI * u2); 222 | out.1 = sqrt1MinusU1 * f32::cos(2.0 * PI * u2); 223 | out.2 = sqrtU1 * f32::sin(2.0 * PI * u3); 224 | out.3 = sqrtU1 * f32::cos(2.0 * PI * u3); 225 | } 226 | 227 | pub fn invert(out: &mut Quaternion, a: &Quaternion) { 228 | let a0 = a.0; 229 | let a1 = a.1; 230 | let a2 = a.2; 231 | let a3 = a.3; 232 | let dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; 233 | let invDot = if dot < EPSILON { 0. } else { 1. / dot }; 234 | 235 | // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 236 | 237 | out.0 = -a0 * invDot; 238 | out.1 = -a1 * invDot; 239 | out.2 = -a2 * invDot; 240 | out.3 = a3 * invDot; 241 | } 242 | 243 | pub fn conjugate(out: &mut Quaternion, a: &Quaternion) { 244 | out.0 = -a.0; 245 | out.1 = -a.1; 246 | out.2 = -a.2; 247 | out.3 = a.3; 248 | } 249 | 250 | pub fn fromMat3(out: &mut Quaternion, m: &Matrix3) { 251 | // Algorithm in Ken Shoemake"s article in 1987 SIGGRAPH course notes 252 | // article "Quaternion Calculus and Fast Animation". 253 | let fTrace = m.0 + m.4 + m.8; 254 | 255 | if (fTrace > 0.0) { 256 | // |w| > 1/2, may as well choose w > 1/2 257 | let mut fRoot = f32::sqrt(fTrace + 1.0); // 2w 258 | out.3 = 0.5 * fRoot; 259 | fRoot = 0.5 / fRoot; // 1/(4w) 260 | out.0 = (m.5 - m.7) * fRoot; 261 | out.1 = (m.6 - m.2) * fRoot; 262 | out.2 = (m.1 - m.3) * fRoot; 263 | } else { 264 | // |w| <= 1/2 265 | let mut i = 0; 266 | if (m.4 > m.0) { 267 | i = 1; 268 | } 269 | let tmp = if i == 0 { m.0 } else { m.4 }; 270 | if (m.8 > tmp) { 271 | i = 2; 272 | } 273 | 274 | match i { 275 | 0 => { 276 | // i = 0, j = 1, k = 2 277 | let mut fRoot = f32::sqrt(m.0 - m.4 - m.8 + 1.0); 278 | out.0 = 0.5 * fRoot; 279 | fRoot = 0.5 / fRoot; 280 | out.3 = (m.5 - m.7) * fRoot; 281 | out.1 = (m.3 + m.1) * fRoot; 282 | out.2 = (m.6 + m.2) * fRoot; 283 | } 284 | 1 => { 285 | // i = 1, j = 2, k = 0 286 | let mut fRoot = f32::sqrt(m.4 - m.8 - m.0 + 1.0); 287 | out.1 = 0.5 * fRoot; 288 | fRoot = 0.5 / fRoot; 289 | out.3 = (m.6 - m.2) * fRoot; 290 | out.2 = (m.7 + m.5) * fRoot; 291 | out.0 = (m.1 + m.3) * fRoot; 292 | } 293 | 2 => { 294 | // i = 2, j = 0, k = 1 295 | let mut fRoot = f32::sqrt(m.8 - m.0 - m.4 + 1.0); 296 | out.2 = 0.5 * fRoot; 297 | fRoot = 0.5 / fRoot; 298 | out.3 = (m.1 - m.3) * fRoot; 299 | out.0 = (m.2 + m.6) * fRoot; 300 | out.1 = (m.5 + m.7) * fRoot; 301 | } 302 | _ => {} 303 | } 304 | } 305 | } 306 | 307 | pub fn fromEuler(out: &mut Quaternion, x: f32, y: f32, z: f32) { 308 | let halfToRad = 0.5 * PI / 180.0; 309 | let x = x * halfToRad; 310 | let y = y * halfToRad; 311 | let z = z * halfToRad; 312 | 313 | let sx = f32::sin(x); 314 | let cx = f32::cos(x); 315 | let sy = f32::sin(y); 316 | let cy = f32::cos(y); 317 | let sz = f32::sin(z); 318 | let cz = f32::cos(z); 319 | 320 | out.0 = sx * cy * cz - cx * sy * sz; 321 | out.1 = cx * sy * cz + sx * cy * sz; 322 | out.2 = cx * cy * sz - sx * sy * cz; 323 | out.3 = cx * cy * cz + sx * sy * sz; 324 | } 325 | 326 | // pub fn str(a: &Quaternion) { 327 | // return "quat(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + a.3 + ")"; 328 | // } 329 | 330 | pub fn clone(a: &Quaternion) -> Quaternion { 331 | Quaternion(a.0, a.1, a.2, a.3) 332 | } 333 | 334 | pub fn fromValues(x: f32, y: f32, z: f32, w: f32) -> Quaternion { 335 | Quaternion(x, y, z, w) 336 | } 337 | 338 | pub fn copy(out: &mut Quaternion, a: &Quaternion) { 339 | out.0 = a.0; 340 | out.1 = a.1; 341 | out.2 = a.2; 342 | out.3 = a.3; 343 | } 344 | 345 | pub fn set(out: &mut Quaternion, x: f32, y: f32, z: f32, w: f32) { 346 | out.0 = x; 347 | out.1 = y; 348 | out.2 = z; 349 | out.3 = w; 350 | } 351 | 352 | pub fn add(out: &mut Quaternion, a: &Quaternion, b: &Quaternion) { 353 | out.0 = a.0 + b.0; 354 | out.1 = a.1 + b.1; 355 | out.2 = a.2 + b.2; 356 | out.3 = a.3 + b.3; 357 | } 358 | 359 | pub fn mul(out: &mut Quaternion, a: &Quaternion, b: &Quaternion) { 360 | Quaternion::multiply(out, a, b); 361 | } 362 | 363 | pub fn scale(out: &mut Quaternion, a: &Quaternion, b: f32) { 364 | out.0 = a.0 * b; 365 | out.1 = a.1 * b; 366 | out.2 = a.2 * b; 367 | out.3 = a.3 * b; 368 | } 369 | 370 | pub fn dot(a: &Quaternion, b: &Quaternion) -> f32 { 371 | a.0 * b.0 + a.1 * b.1 + a.2 * b.2 + a.3 * b.3 372 | } 373 | 374 | pub fn lerp(out: &mut Quaternion, a: &Quaternion, b: &Quaternion, t: f32) { 375 | let ax = a.0; 376 | let ay = a.1; 377 | let az = a.2; 378 | let aw = a.3; 379 | out.0 = ax + t * (b.0 - ax); 380 | out.1 = ay + t * (b.1 - ay); 381 | out.2 = az + t * (b.2 - az); 382 | out.3 = aw + t * (b.3 - aw); 383 | } 384 | 385 | pub fn len(a: &Quaternion) -> f32 { 386 | let x = a.0; 387 | let y = a.1; 388 | let z = a.2; 389 | let w = a.3; 390 | (x.powi(2) + y.powi(2) + z.powi(2) + w.powi(2)).sqrt() 391 | } 392 | 393 | pub fn squaredLength(a: &Quaternion) -> f32 { 394 | let x = a.0; 395 | let y = a.1; 396 | let z = a.2; 397 | let w = a.3; 398 | x * x + y * y + z * z + w * w 399 | } 400 | 401 | pub fn sqrLen(a: &Quaternion) -> f32 { 402 | Quaternion::squaredLength(a) 403 | } 404 | 405 | pub fn normalize(out: &mut Quaternion, a: &Quaternion) { 406 | let x = a.0; 407 | let y = a.1; 408 | let z = a.2; 409 | let w = a.3; 410 | let mut len = x * x + y * y + z * z + w * w; 411 | if (len > EPSILON) { 412 | len = 1. / f32::sqrt(len); 413 | } 414 | out.0 = x * len; 415 | out.1 = y * len; 416 | out.2 = z * len; 417 | out.3 = w * len; 418 | } 419 | 420 | pub fn exactEquals(a: &Quaternion, b: &Quaternion) -> bool { 421 | a.0 == b.0 && a.1 == b.1 && a.2 == b.2 && a.3 == b.3 422 | } 423 | 424 | pub fn equals(a: &Quaternion, b: &Quaternion) -> bool { 425 | let a0 = a.0; 426 | let a1 = a.1; 427 | let a2 = a.2; 428 | let a3 = a.3; 429 | let b0 = b.0; 430 | let b1 = b.1; 431 | let b2 = b.2; 432 | let b3 = b.3; 433 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 434 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 435 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 436 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 437 | } 438 | 439 | pub fn rotationTo(out: &mut Quaternion, a: &Vector3, b: &Vector3) { 440 | let tmpvec3 = &mut Vector3::create(); 441 | let xUnitVec3 = &Vector3::fromValues(1., 0., 0.); 442 | let yUnitVec3 = &Vector3::fromValues(0., 1., 0.); 443 | 444 | let dot = Vector3::dot(a, b); 445 | if (dot < -0.999999) { 446 | Vector3::cross(tmpvec3, xUnitVec3, a); 447 | if (Vector3::len(tmpvec3) < EPSILON) { 448 | Vector3::cross(tmpvec3, yUnitVec3, a); 449 | } 450 | Vector3::normalize(tmpvec3, &Vector3::clone(tmpvec3)); 451 | Quaternion::setAxisAngle(out, tmpvec3, PI); 452 | } else if (dot > 0.999999) { 453 | out.0 = 0.; 454 | out.1 = 0.; 455 | out.2 = 0.; 456 | out.3 = 1.; 457 | } else { 458 | Vector3::cross(tmpvec3, a, b); 459 | out.0 = tmpvec3.0; 460 | out.1 = tmpvec3.1; 461 | out.2 = tmpvec3.2; 462 | out.3 = 1. + dot; 463 | Quaternion::normalize(out, &Quaternion::clone(out)); 464 | } 465 | } 466 | 467 | pub fn sqlerp( 468 | out: &mut Quaternion, 469 | a: &Quaternion, 470 | b: &Quaternion, 471 | c: &Quaternion, 472 | d: &Quaternion, 473 | t: f32, 474 | ) { 475 | let temp1 = &mut Quaternion::create(); 476 | let temp2 = &mut Quaternion::create(); 477 | 478 | Quaternion::slerp(temp1, a, d, t); 479 | Quaternion::slerp(temp2, b, c, t); 480 | Quaternion::slerp(out, temp1, temp2, 2. * t * (1. - t)); 481 | } 482 | 483 | pub fn setAxes(out: &mut Quaternion, view: &Vector3, right: &Vector3, up: &Vector3) { 484 | let matr = &mut Matrix3::create(); 485 | 486 | matr.0 = right.0; 487 | matr.3 = right.1; 488 | matr.6 = right.2; 489 | 490 | matr.1 = up.0; 491 | matr.4 = up.1; 492 | matr.7 = up.2; 493 | 494 | matr.2 = -view.0; 495 | matr.5 = -view.1; 496 | matr.8 = -view.2; 497 | 498 | Quaternion::fromMat3(out, matr); 499 | Quaternion::normalize(out, &Quaternion::clone(out)); 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /src/matrix3.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : matrix3.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix2d::*; 11 | use super::matrix4::*; 12 | use super::quaternion::*; 13 | use super::vector2::*; 14 | 15 | #[wasm_bindgen] 16 | pub struct Matrix3( 17 | pub f32, 18 | pub f32, 19 | pub f32, 20 | pub f32, 21 | pub f32, 22 | pub f32, 23 | pub f32, 24 | pub f32, 25 | pub f32, 26 | ); 27 | 28 | #[wasm_bindgen] 29 | impl Matrix3 { 30 | #[wasm_bindgen(getter)] 31 | pub fn elements(&self) -> Box<[f32]> { 32 | Box::new([ 33 | self.0, self.1, self.2, self.3, self.4, self.5, self.6, self.7, self.8, 34 | ]) 35 | } 36 | 37 | pub fn create() -> Matrix3 { 38 | Matrix3(1., 0., 0., 0., 1., 0., 0., 0., 1.) 39 | } 40 | 41 | pub fn fromMat4(out: &mut Matrix3, a: &Matrix4) { 42 | out.0 = a.0; 43 | out.1 = a.1; 44 | out.2 = a.2; 45 | out.3 = a.4; 46 | out.4 = a.5; 47 | out.5 = a.6; 48 | out.6 = a.8; 49 | out.7 = a.9; 50 | out.8 = a.10; 51 | } 52 | 53 | pub fn clone(a: &Matrix3) -> Matrix3 { 54 | Matrix3(a.0, a.1, a.2, a.3, a.4, a.5, a.6, a.7, a.8) 55 | } 56 | 57 | pub fn copy(out: &mut Matrix3, a: &Matrix3) { 58 | out.0 = a.0; 59 | out.1 = a.1; 60 | out.2 = a.2; 61 | out.3 = a.3; 62 | out.4 = a.4; 63 | out.5 = a.5; 64 | out.6 = a.6; 65 | out.7 = a.7; 66 | out.8 = a.8; 67 | } 68 | 69 | pub fn fromValues( 70 | m00: f32, 71 | m01: f32, 72 | m02: f32, 73 | m10: f32, 74 | m11: f32, 75 | m12: f32, 76 | m20: f32, 77 | m21: f32, 78 | m22: f32, 79 | ) -> Matrix3 { 80 | Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22) 81 | } 82 | 83 | pub fn set( 84 | out: &mut Matrix3, 85 | m00: f32, 86 | m01: f32, 87 | m02: f32, 88 | m10: f32, 89 | m11: f32, 90 | m12: f32, 91 | m20: f32, 92 | m21: f32, 93 | m22: f32, 94 | ) { 95 | out.0 = m00; 96 | out.1 = m01; 97 | out.2 = m02; 98 | out.3 = m10; 99 | out.4 = m11; 100 | out.5 = m12; 101 | out.6 = m20; 102 | out.7 = m21; 103 | out.8 = m22; 104 | } 105 | 106 | pub fn identity(out: &mut Matrix3) { 107 | out.0 = 1.; 108 | out.1 = 0.; 109 | out.2 = 0.; 110 | out.3 = 0.; 111 | out.4 = 1.; 112 | out.5 = 0.; 113 | out.6 = 0.; 114 | out.7 = 0.; 115 | out.8 = 1.; 116 | } 117 | 118 | pub fn transpose(out: &mut Matrix3, a: &Matrix3) { 119 | // If we are transposing ourselves we can skip a few steps but have to cache some values 120 | if (out as *const Matrix3) == (a as *const Matrix3) { 121 | let a01 = a.1; 122 | let a02 = a.2; 123 | let a12 = a.5; 124 | out.1 = a.3; 125 | out.2 = a.6; 126 | out.3 = a01; 127 | out.5 = a.7; 128 | out.6 = a02; 129 | out.7 = a12; 130 | } else { 131 | out.0 = a.0; 132 | out.1 = a.3; 133 | out.2 = a.6; 134 | out.3 = a.1; 135 | out.4 = a.4; 136 | out.5 = a.7; 137 | out.6 = a.2; 138 | out.7 = a.5; 139 | out.8 = a.8; 140 | } 141 | } 142 | 143 | pub fn invert(out: &mut Matrix3, a: &Matrix3) { 144 | let a00 = a.0; 145 | let a01 = a.1; 146 | let a02 = a.2; 147 | let a10 = a.3; 148 | let a11 = a.4; 149 | let a12 = a.5; 150 | let a20 = a.6; 151 | let a21 = a.7; 152 | let a22 = a.8; 153 | 154 | let b01 = a22 * a11 - a12 * a21; 155 | let b11 = -a22 * a10 + a12 * a20; 156 | let b21 = a21 * a10 - a11 * a20; 157 | 158 | // Calculate the determinant 159 | let mut det = a00 * b01 + a01 * b11 + a02 * b21; 160 | 161 | if det.abs() < EPSILON { 162 | return; 163 | } 164 | det = 1.0 / det; 165 | 166 | out.0 = b01 * det; 167 | out.1 = (-a22 * a01 + a02 * a21) * det; 168 | out.2 = (a12 * a01 - a02 * a11) * det; 169 | out.3 = b11 * det; 170 | out.4 = (a22 * a00 - a02 * a20) * det; 171 | out.5 = (-a12 * a00 + a02 * a10) * det; 172 | out.6 = b21 * det; 173 | out.7 = (-a21 * a00 + a01 * a20) * det; 174 | out.8 = (a11 * a00 - a01 * a10) * det; 175 | } 176 | 177 | pub fn adjoint(out: &mut Matrix3, a: &Matrix3) { 178 | let a00 = a.0; 179 | let a01 = a.1; 180 | let a02 = a.2; 181 | let a10 = a.3; 182 | let a11 = a.4; 183 | let a12 = a.5; 184 | let a20 = a.6; 185 | let a21 = a.7; 186 | let a22 = a.8; 187 | 188 | out.0 = (a11 * a22 - a12 * a21); 189 | out.1 = (a02 * a21 - a01 * a22); 190 | out.2 = (a01 * a12 - a02 * a11); 191 | out.3 = (a12 * a20 - a10 * a22); 192 | out.4 = (a00 * a22 - a02 * a20); 193 | out.5 = (a02 * a10 - a00 * a12); 194 | out.6 = (a10 * a21 - a11 * a20); 195 | out.7 = (a01 * a20 - a00 * a21); 196 | out.8 = (a00 * a11 - a01 * a10); 197 | } 198 | 199 | pub fn determinant(a: &Matrix3) -> f32 { 200 | let a00 = a.0; 201 | let a01 = a.1; 202 | let a02 = a.2; 203 | let a10 = a.3; 204 | let a11 = a.4; 205 | let a12 = a.5; 206 | let a20 = a.6; 207 | let a21 = a.7; 208 | let a22 = a.8; 209 | 210 | a00 * (a22 * a11 - a12 * a21) 211 | + a01 * (-a22 * a10 + a12 * a20) 212 | + a02 * (a21 * a10 - a11 * a20) 213 | } 214 | 215 | pub fn multiply(out: &mut Matrix3, a: &Matrix3, b: &Matrix3) { 216 | let a00 = a.0; 217 | let a01 = a.1; 218 | let a02 = a.2; 219 | let a10 = a.3; 220 | let a11 = a.4; 221 | let a12 = a.5; 222 | let a20 = a.6; 223 | let a21 = a.7; 224 | let a22 = a.8; 225 | 226 | let b00 = b.0; 227 | let b01 = b.1; 228 | let b02 = b.2; 229 | let b10 = b.3; 230 | let b11 = b.4; 231 | let b12 = b.5; 232 | let b20 = b.6; 233 | let b21 = b.7; 234 | let b22 = b.8; 235 | 236 | out.0 = b00 * a00 + b01 * a10 + b02 * a20; 237 | out.1 = b00 * a01 + b01 * a11 + b02 * a21; 238 | out.2 = b00 * a02 + b01 * a12 + b02 * a22; 239 | 240 | out.3 = b10 * a00 + b11 * a10 + b12 * a20; 241 | out.4 = b10 * a01 + b11 * a11 + b12 * a21; 242 | out.5 = b10 * a02 + b11 * a12 + b12 * a22; 243 | 244 | out.6 = b20 * a00 + b21 * a10 + b22 * a20; 245 | out.7 = b20 * a01 + b21 * a11 + b22 * a21; 246 | out.8 = b20 * a02 + b21 * a12 + b22 * a22; 247 | } 248 | 249 | pub fn translate(out: &mut Matrix3, a: &Matrix3, v: &Vector2) { 250 | let a00 = a.0; 251 | let a01 = a.1; 252 | let a02 = a.2; 253 | let a10 = a.3; 254 | let a11 = a.4; 255 | let a12 = a.5; 256 | let a20 = a.6; 257 | let a21 = a.7; 258 | let a22 = a.8; 259 | let x = v.0; 260 | let y = v.1; 261 | 262 | out.0 = a00; 263 | out.1 = a01; 264 | out.2 = a02; 265 | 266 | out.3 = a10; 267 | out.4 = a11; 268 | out.5 = a12; 269 | 270 | out.6 = x * a00 + y * a10 + a20; 271 | out.7 = x * a01 + y * a11 + a21; 272 | out.8 = x * a02 + y * a12 + a22; 273 | } 274 | 275 | pub fn rotate(out: &mut Matrix3, a: &Matrix3, rad: f32) { 276 | let a00 = a.0; 277 | let a01 = a.1; 278 | let a02 = a.2; 279 | let a10 = a.3; 280 | let a11 = a.4; 281 | let a12 = a.5; 282 | let a20 = a.6; 283 | let a21 = a.7; 284 | let a22 = a.8; 285 | let s = f32::sin(rad); 286 | let c = f32::cos(rad); 287 | 288 | out.0 = c * a00 + s * a10; 289 | out.1 = c * a01 + s * a11; 290 | out.2 = c * a02 + s * a12; 291 | 292 | out.3 = c * a10 - s * a00; 293 | out.4 = c * a11 - s * a01; 294 | out.5 = c * a12 - s * a02; 295 | 296 | out.6 = a20; 297 | out.7 = a21; 298 | out.8 = a22; 299 | } 300 | 301 | pub fn scale(out: &mut Matrix3, a: &Matrix3, v: &Vector2) { 302 | let x = v.0; 303 | let y = v.1; 304 | 305 | out.0 = x * a.0; 306 | out.1 = x * a.1; 307 | out.2 = x * a.2; 308 | 309 | out.3 = y * a.3; 310 | out.4 = y * a.4; 311 | out.5 = y * a.5; 312 | 313 | out.6 = a.6; 314 | out.7 = a.7; 315 | out.8 = a.8; 316 | } 317 | 318 | pub fn fromTranslation(out: &mut Matrix3, v: &Vector2) { 319 | out.0 = 1.; 320 | out.1 = 0.; 321 | out.2 = 0.; 322 | out.3 = 0.; 323 | out.4 = 1.; 324 | out.5 = 0.; 325 | out.6 = v.0; 326 | out.7 = v.1; 327 | out.8 = 1.; 328 | } 329 | 330 | pub fn fromRotation(out: &mut Matrix3, rad: f32) { 331 | let s = f32::sin(rad); 332 | let c = f32::cos(rad); 333 | 334 | out.0 = c; 335 | out.1 = s; 336 | out.2 = 0.; 337 | 338 | out.3 = -s; 339 | out.4 = c; 340 | out.5 = 0.; 341 | 342 | out.6 = 0.; 343 | out.7 = 0.; 344 | out.8 = 1.; 345 | } 346 | 347 | pub fn fromScaling(out: &mut Matrix3, v: &Vector2) { 348 | out.0 = v.0; 349 | out.1 = 0.; 350 | out.2 = 0.; 351 | 352 | out.3 = 0.; 353 | out.4 = v.1; 354 | out.5 = 0.; 355 | 356 | out.6 = 0.; 357 | out.7 = 0.; 358 | out.8 = 1.; 359 | } 360 | 361 | pub fn fromMat2d(out: &mut Matrix3, a: &Matrix2d) { 362 | out.0 = a.0; 363 | out.1 = a.1; 364 | out.2 = 0.; 365 | 366 | out.3 = a.2; 367 | out.4 = a.3; 368 | out.5 = 0.; 369 | 370 | out.6 = a.4; 371 | out.7 = a.5; 372 | out.8 = 1.; 373 | } 374 | 375 | pub fn fromQuat(out: &mut Matrix3, q: &Quaternion) { 376 | let x = q.0; 377 | let y = q.1; 378 | let z = q.2; 379 | let w = q.3; 380 | let x2 = x + x; 381 | let y2 = y + y; 382 | let z2 = z + z; 383 | 384 | let xx = x * x2; 385 | let yx = y * x2; 386 | let yy = y * y2; 387 | let zx = z * x2; 388 | let zy = z * y2; 389 | let zz = z * z2; 390 | let wx = w * x2; 391 | let wy = w * y2; 392 | let wz = w * z2; 393 | 394 | out.0 = 1. - yy - zz; 395 | out.3 = yx - wz; 396 | out.6 = zx + wy; 397 | 398 | out.1 = yx + wz; 399 | out.4 = 1. - xx - zz; 400 | out.7 = zy - wx; 401 | 402 | out.2 = zx - wy; 403 | out.5 = zy + wx; 404 | out.8 = 1. - xx - yy; 405 | } 406 | 407 | pub fn normalFromMat4(out: &mut Matrix3, a: &Matrix4) { 408 | let a00 = a.0; 409 | let a01 = a.1; 410 | let a02 = a.2; 411 | let a03 = a.3; 412 | let a10 = a.4; 413 | let a11 = a.5; 414 | let a12 = a.6; 415 | let a13 = a.7; 416 | let a20 = a.8; 417 | let a21 = a.9; 418 | let a22 = a.10; 419 | let a23 = a.11; 420 | let a30 = a.12; 421 | let a31 = a.13; 422 | let a32 = a.14; 423 | let a33 = a.15; 424 | 425 | let b00 = a00 * a11 - a01 * a10; 426 | let b01 = a00 * a12 - a02 * a10; 427 | let b02 = a00 * a13 - a03 * a10; 428 | let b03 = a01 * a12 - a02 * a11; 429 | let b04 = a01 * a13 - a03 * a11; 430 | let b05 = a02 * a13 - a03 * a12; 431 | let b06 = a20 * a31 - a21 * a30; 432 | let b07 = a20 * a32 - a22 * a30; 433 | let b08 = a20 * a33 - a23 * a30; 434 | let b09 = a21 * a32 - a22 * a31; 435 | let b10 = a21 * a33 - a23 * a31; 436 | let b11 = a22 * a33 - a23 * a32; 437 | 438 | // Calculate the determinant 439 | let mut det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; 440 | 441 | if det.abs() < EPSILON { 442 | return; 443 | } 444 | det = 1.0 / det; 445 | 446 | out.0 = (a11 * b11 - a12 * b10 + a13 * b09) * det; 447 | out.1 = (a12 * b08 - a10 * b11 - a13 * b07) * det; 448 | out.2 = (a10 * b10 - a11 * b08 + a13 * b06) * det; 449 | 450 | out.3 = (a02 * b10 - a01 * b11 - a03 * b09) * det; 451 | out.4 = (a00 * b11 - a02 * b08 + a03 * b07) * det; 452 | out.5 = (a01 * b08 - a00 * b10 - a03 * b06) * det; 453 | 454 | out.6 = (a31 * b05 - a32 * b04 + a33 * b03) * det; 455 | out.7 = (a32 * b02 - a30 * b05 - a33 * b01) * det; 456 | out.8 = (a30 * b04 - a31 * b02 + a33 * b00) * det; 457 | } 458 | 459 | pub fn projection(out: &mut Matrix3, width: f32, height: f32) { 460 | out.0 = 2. / width; 461 | out.1 = 0.; 462 | out.2 = 0.; 463 | out.3 = 0.; 464 | out.4 = -2. / height; 465 | out.5 = 0.; 466 | out.6 = -1.; 467 | out.7 = 1.; 468 | out.8 = 1.; 469 | } 470 | 471 | // pub fn str(a: &Matrix3) { 472 | // return "mat3(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + 473 | // a.3 + ", " + a.4 + ", " + a.5 + ", " + 474 | // a.6 + ", " + a.7 + ", " + a.8 + ")"; 475 | // } 476 | 477 | pub fn frob(a: &Matrix3) -> f32 { 478 | (a.0.powi(2) 479 | + a.1.powi(2) 480 | + a.2.powi(2) 481 | + a.3.powi(2) 482 | + a.4.powi(2) 483 | + a.5.powi(2) 484 | + a.6.powi(2) 485 | + a.7.powi(2) 486 | + a.8.powi(2)) 487 | .sqrt() 488 | } 489 | 490 | pub fn add(out: &mut Matrix3, a: &Matrix3, b: &Matrix3) { 491 | out.0 = a.0 + b.0; 492 | out.1 = a.1 + b.1; 493 | out.2 = a.2 + b.2; 494 | out.3 = a.3 + b.3; 495 | out.4 = a.4 + b.4; 496 | out.5 = a.5 + b.5; 497 | out.6 = a.6 + b.6; 498 | out.7 = a.7 + b.7; 499 | out.8 = a.8 + b.8; 500 | } 501 | 502 | pub fn subtract(out: &mut Matrix3, a: &Matrix3, b: &Matrix3) { 503 | out.0 = a.0 - b.0; 504 | out.1 = a.1 - b.1; 505 | out.2 = a.2 - b.2; 506 | out.3 = a.3 - b.3; 507 | out.4 = a.4 - b.4; 508 | out.5 = a.5 - b.5; 509 | out.6 = a.6 - b.6; 510 | out.7 = a.7 - b.7; 511 | out.8 = a.8 - b.8; 512 | } 513 | 514 | pub fn multiplyScalar(out: &mut Matrix3, a: &Matrix3, b: f32) { 515 | out.0 = a.0 * b; 516 | out.1 = a.1 * b; 517 | out.2 = a.2 * b; 518 | out.3 = a.3 * b; 519 | out.4 = a.4 * b; 520 | out.5 = a.5 * b; 521 | out.6 = a.6 * b; 522 | out.7 = a.7 * b; 523 | out.8 = a.8 * b; 524 | } 525 | 526 | pub fn multiplyScalarAndAdd(out: &mut Matrix3, a: &Matrix3, b: &Matrix3, scale: f32) { 527 | out.0 = a.0 + (b.0 * scale); 528 | out.1 = a.1 + (b.1 * scale); 529 | out.2 = a.2 + (b.2 * scale); 530 | out.3 = a.3 + (b.3 * scale); 531 | out.4 = a.4 + (b.4 * scale); 532 | out.5 = a.5 + (b.5 * scale); 533 | out.6 = a.6 + (b.6 * scale); 534 | out.7 = a.7 + (b.7 * scale); 535 | out.8 = a.8 + (b.8 * scale); 536 | } 537 | 538 | pub fn exactEquals(a: &Matrix3, b: &Matrix3) -> bool { 539 | a.0 == b.0 540 | && a.1 == b.1 541 | && a.2 == b.2 542 | && a.3 == b.3 543 | && a.4 == b.4 544 | && a.5 == b.5 545 | && a.6 == b.6 546 | && a.7 == b.7 547 | && a.8 == b.8 548 | } 549 | 550 | pub fn equals(a: &Matrix3, b: &Matrix3) -> bool { 551 | let a0 = a.0; 552 | let a1 = a.1; 553 | let a2 = a.2; 554 | let a3 = a.3; 555 | let a4 = a.4; 556 | let a5 = a.5; 557 | let a6 = a.6; 558 | let a7 = a.7; 559 | let a8 = a.8; 560 | let b0 = b.0; 561 | let b1 = b.1; 562 | let b2 = b.2; 563 | let b3 = b.3; 564 | let b4 = b.4; 565 | let b5 = b.5; 566 | let b6 = b.6; 567 | let b7 = b.7; 568 | let b8 = b.8; 569 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 570 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 571 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 572 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 573 | && f32::abs(a4 - b4) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a4), f32::abs(b4))) 574 | && f32::abs(a5 - b5) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a5), f32::abs(b5))) 575 | && f32::abs(a6 - b6) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a6), f32::abs(b6))) 576 | && f32::abs(a7 - b7) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a7), f32::abs(b7))) 577 | && f32::abs(a8 - b8) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a8), f32::abs(b8))) 578 | } 579 | 580 | pub fn mul(out: &mut Matrix3, a: &Matrix3, b: &Matrix3) { 581 | Matrix3::multiply(out, a, b); 582 | } 583 | 584 | pub fn sub(out: &mut Matrix3, a: &Matrix3, b: &Matrix3) { 585 | Matrix3::subtract(out, a, b); 586 | } 587 | } 588 | -------------------------------------------------------------------------------- /src/quaternion2.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * @File : quaternion2.rs 3 | * @Author : dtysky (dtysky@outlook.com) 4 | * @Link : http://dtysky.moe 5 | * @Date : 2019/2/7 下午9:53:03 6 | */ 7 | use wasm_bindgen::prelude::*; 8 | 9 | use super::common::*; 10 | use super::matrix4::*; 11 | use super::quaternion::*; 12 | use super::vector3::*; 13 | 14 | #[wasm_bindgen] 15 | pub struct Quaternion2( 16 | pub f32, 17 | pub f32, 18 | pub f32, 19 | pub f32, 20 | pub f32, 21 | pub f32, 22 | pub f32, 23 | pub f32, 24 | ); 25 | 26 | #[wasm_bindgen] 27 | impl Quaternion2 { 28 | #[wasm_bindgen(getter)] 29 | pub fn elements(&self) -> Box<[f32]> { 30 | Box::new([ 31 | self.0, self.1, self.2, self.3, self.4, self.5, self.6, self.7, 32 | ]) 33 | } 34 | 35 | pub fn create() -> Quaternion2 { 36 | Quaternion2(0., 0., 0., 1., 0., 0., 0., 0.) 37 | } 38 | 39 | pub fn clone(a: &Quaternion2) -> Quaternion2 { 40 | Quaternion2(a.0, a.1, a.2, a.3, a.4, a.5, a.6, a.7) 41 | } 42 | 43 | pub fn fromValues( 44 | x1: f32, 45 | y1: f32, 46 | z1: f32, 47 | w1: f32, 48 | x2: f32, 49 | y2: f32, 50 | z2: f32, 51 | w2: f32, 52 | ) -> Quaternion2 { 53 | Quaternion2(x1, y1, z1, w1, x2, y2, z2, w2) 54 | } 55 | 56 | pub fn fromRotationTranslationValues( 57 | x1: f32, 58 | y1: f32, 59 | z1: f32, 60 | w1: f32, 61 | x2: f32, 62 | y2: f32, 63 | z2: f32, 64 | ) -> Quaternion2 { 65 | let ax = x2 * 0.5; 66 | let ay = y2 * 0.5; 67 | let az = z2 * 0.5; 68 | 69 | Quaternion2( 70 | x1, 71 | y1, 72 | z1, 73 | w1, 74 | ax * w1 + ay * z1 - az * y1, 75 | ay * w1 + az * x1 - ax * z1, 76 | az * w1 + ax * y1 - ay * x1, 77 | -ax * x1 - ay * y1 - az * z1, 78 | ) 79 | } 80 | 81 | pub fn fromRotationTranslation(out: &mut Quaternion2, q: &Quaternion, t: &Vector3) { 82 | let ax = t.0 * 0.5; 83 | let ay = t.1 * 0.5; 84 | let az = t.2 * 0.5; 85 | let bx = q.0; 86 | let by = q.1; 87 | let bz = q.2; 88 | let bw = q.3; 89 | out.0 = bx; 90 | out.1 = by; 91 | out.2 = bz; 92 | out.3 = bw; 93 | out.4 = ax * bw + ay * bz - az * by; 94 | out.5 = ay * bw + az * bx - ax * bz; 95 | out.6 = az * bw + ax * by - ay * bx; 96 | out.7 = -ax * bx - ay * by - az * bz; 97 | } 98 | 99 | pub fn fromTranslation(out: &mut Quaternion2, t: &Vector3) { 100 | out.0 = 0.; 101 | out.1 = 0.; 102 | out.2 = 0.; 103 | out.3 = 1.; 104 | out.4 = t.0 * 0.5; 105 | out.5 = t.1 * 0.5; 106 | out.6 = t.2 * 0.5; 107 | out.7 = 0.; 108 | } 109 | 110 | pub fn fromRotation(out: &mut Quaternion2, q: &Quaternion) { 111 | out.0 = q.0; 112 | out.1 = q.1; 113 | out.2 = q.2; 114 | out.3 = q.3; 115 | out.4 = 0.; 116 | out.5 = 0.; 117 | out.6 = 0.; 118 | out.7 = 0.; 119 | } 120 | 121 | pub fn fromMat4(out: &mut Quaternion2, a: &Matrix4) { 122 | //TODO Optimize this 123 | let outer = &mut Quaternion::create(); 124 | Matrix4::getRotation(outer, a); 125 | let t = &mut Vector3::create(); 126 | Matrix4::getTranslation(t, a); 127 | Quaternion2::fromRotationTranslation(out, outer, t); 128 | } 129 | 130 | pub fn copy(out: &mut Quaternion2, a: &Quaternion2) { 131 | out.0 = a.0; 132 | out.1 = a.1; 133 | out.2 = a.2; 134 | out.3 = a.3; 135 | out.4 = a.4; 136 | out.5 = a.5; 137 | out.6 = a.6; 138 | out.7 = a.7; 139 | } 140 | 141 | pub fn identity(out: &mut Quaternion2) { 142 | out.0 = 0.; 143 | out.1 = 0.; 144 | out.2 = 0.; 145 | out.3 = 1.; 146 | out.4 = 0.; 147 | out.5 = 0.; 148 | out.6 = 0.; 149 | out.7 = 0.; 150 | } 151 | 152 | pub fn set( 153 | out: &mut Quaternion2, 154 | x1: f32, 155 | y1: f32, 156 | z1: f32, 157 | w1: f32, 158 | x2: f32, 159 | y2: f32, 160 | z2: f32, 161 | w2: f32, 162 | ) { 163 | out.0 = x1; 164 | out.1 = y1; 165 | out.2 = z1; 166 | out.3 = w1; 167 | 168 | out.4 = x2; 169 | out.5 = y2; 170 | out.6 = z2; 171 | out.7 = w2; 172 | } 173 | 174 | pub fn getReal(out: &mut Quaternion, a: &Quaternion2) { 175 | out.0 = a.0; 176 | out.1 = a.1; 177 | out.2 = a.2; 178 | out.3 = a.3; 179 | } 180 | 181 | pub fn getDual(out: &mut Quaternion, a: &Quaternion2) { 182 | out.0 = a.4; 183 | out.1 = a.5; 184 | out.2 = a.6; 185 | out.3 = a.7; 186 | } 187 | 188 | pub fn setReal(out: &mut Quaternion2, a: &Quaternion) { 189 | out.0 = a.0; 190 | out.1 = a.1; 191 | out.2 = a.2; 192 | out.3 = a.3; 193 | } 194 | 195 | pub fn setDual(out: &mut Quaternion2, q: &Quaternion) { 196 | out.4 = q.0; 197 | out.5 = q.1; 198 | out.6 = q.2; 199 | out.7 = q.3; 200 | } 201 | 202 | pub fn getTranslation(out: &mut Vector3, a: &Quaternion2) { 203 | let ax = a.4; 204 | let ay = a.5; 205 | let az = a.6; 206 | let aw = a.7; 207 | let bx = -a.0; 208 | let by = -a.1; 209 | let bz = -a.2; 210 | let bw = a.3; 211 | out.0 = (ax * bw + aw * bx + ay * bz - az * by) * 2.; 212 | out.1 = (ay * bw + aw * by + az * bx - ax * bz) * 2.; 213 | out.2 = (az * bw + aw * bz + ax * by - ay * bx) * 2.; 214 | } 215 | 216 | pub fn translate(out: &mut Quaternion2, a: &Quaternion2, v: &Vector3) { 217 | let ax1 = a.0; 218 | let ay1 = a.1; 219 | let az1 = a.2; 220 | let aw1 = a.3; 221 | let bx1 = v.0 * 0.5; 222 | let by1 = v.1 * 0.5; 223 | let bz1 = v.2 * 0.5; 224 | let ax2 = a.4; 225 | let ay2 = a.5; 226 | let az2 = a.6; 227 | let aw2 = a.7; 228 | out.0 = ax1; 229 | out.1 = ay1; 230 | out.2 = az1; 231 | out.3 = aw1; 232 | out.4 = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2; 233 | out.5 = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2; 234 | out.6 = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2; 235 | out.7 = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2; 236 | } 237 | 238 | pub fn rotateX(out: &mut Quaternion2, a: &Quaternion2, rad: f32) { 239 | let mut bx = -a.0; 240 | let mut by = -a.1; 241 | let mut bz = -a.2; 242 | let mut bw = a.3; 243 | let ax = a.4; 244 | let ay = a.5; 245 | let az = a.6; 246 | let aw = a.7; 247 | let ax1 = ax * bw + aw * bx + ay * bz - az * by; 248 | let ay1 = ay * bw + aw * by + az * bx - ax * bz; 249 | let az1 = az * bw + aw * bz + ax * by - ay * bx; 250 | let aw1 = aw * bw - ax * bx - ay * by - az * bz; 251 | let o = &mut Quaternion::fromValues(out.0, out.1, out.2, out.3); 252 | Quaternion::rotateX(o, &Quaternion::fromValues(a.0, a.1, a.2, a.3), rad); 253 | bx = o.0; 254 | by = o.1; 255 | bz = o.2; 256 | bw = o.3; 257 | out.0 = bx; 258 | out.1 = by; 259 | out.2 = bz; 260 | out.3 = bw; 261 | out.4 = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; 262 | out.5 = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; 263 | out.6 = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; 264 | out.7 = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; 265 | } 266 | 267 | pub fn rotateY(out: &mut Quaternion2, a: &Quaternion2, rad: f32) { 268 | let mut bx = -a.0; 269 | let mut by = -a.1; 270 | let mut bz = -a.2; 271 | let mut bw = a.3; 272 | let ax = a.4; 273 | let ay = a.5; 274 | let az = a.6; 275 | let aw = a.7; 276 | let ax1 = ax * bw + aw * bx + ay * bz - az * by; 277 | let ay1 = ay * bw + aw * by + az * bx - ax * bz; 278 | let az1 = az * bw + aw * bz + ax * by - ay * bx; 279 | let aw1 = aw * bw - ax * bx - ay * by - az * bz; 280 | let o = &mut Quaternion::fromValues(out.0, out.1, out.2, out.3); 281 | Quaternion::rotateY(o, &Quaternion::fromValues(a.0, a.1, a.2, a.3), rad); 282 | bx = o.0; 283 | by = o.1; 284 | bz = o.2; 285 | bw = o.3; 286 | out.0 = bx; 287 | out.1 = by; 288 | out.2 = bz; 289 | out.3 = bw; 290 | out.4 = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; 291 | out.5 = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; 292 | out.6 = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; 293 | out.7 = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; 294 | } 295 | 296 | pub fn rotateZ(out: &mut Quaternion2, a: &Quaternion2, rad: f32) { 297 | let mut bx = -a.0; 298 | let mut by = -a.1; 299 | let mut bz = -a.2; 300 | let mut bw = a.3; 301 | let ax = a.4; 302 | let ay = a.5; 303 | let az = a.6; 304 | let aw = a.7; 305 | let ax1 = ax * bw + aw * bx + ay * bz - az * by; 306 | let ay1 = ay * bw + aw * by + az * bx - ax * bz; 307 | let az1 = az * bw + aw * bz + ax * by - ay * bx; 308 | let aw1 = aw * bw - ax * bx - ay * by - az * bz; 309 | let o = &mut Quaternion::fromValues(out.0, out.1, out.2, out.3); 310 | Quaternion::rotateZ(o, &Quaternion::fromValues(a.0, a.1, a.2, a.3), rad); 311 | bx = o.0; 312 | by = o.1; 313 | bz = o.2; 314 | bw = o.3; 315 | out.0 = bx; 316 | out.1 = by; 317 | out.2 = bz; 318 | out.3 = bw; 319 | out.4 = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; 320 | out.5 = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; 321 | out.6 = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; 322 | out.7 = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; 323 | } 324 | 325 | pub fn rotateByQuatAppend(out: &mut Quaternion2, a: &Quaternion2, q: &Quaternion) { 326 | let qx = q.0; 327 | let qy = q.1; 328 | let qz = q.2; 329 | let qw = q.3; 330 | let mut ax = a.0; 331 | let mut ay = a.1; 332 | let mut az = a.2; 333 | let mut aw = a.3; 334 | 335 | out.0 = ax * qw + aw * qx + ay * qz - az * qy; 336 | out.1 = ay * qw + aw * qy + az * qx - ax * qz; 337 | out.2 = az * qw + aw * qz + ax * qy - ay * qx; 338 | out.3 = aw * qw - ax * qx - ay * qy - az * qz; 339 | ax = a.4; 340 | ay = a.5; 341 | az = a.6; 342 | aw = a.7; 343 | out.4 = ax * qw + aw * qx + ay * qz - az * qy; 344 | out.5 = ay * qw + aw * qy + az * qx - ax * qz; 345 | out.6 = az * qw + aw * qz + ax * qy - ay * qx; 346 | out.7 = aw * qw - ax * qx - ay * qy - az * qz; 347 | } 348 | 349 | pub fn rotateByQuatPrepend(out: &mut Quaternion2, q: &Quaternion, a: &Quaternion2) { 350 | let qx = q.0; 351 | let qy = q.1; 352 | let qz = q.2; 353 | let qw = q.3; 354 | let mut bx = a.0; 355 | let mut by = a.1; 356 | let mut bz = a.2; 357 | let mut bw = a.3; 358 | 359 | out.0 = qx * bw + qw * bx + qy * bz - qz * by; 360 | out.1 = qy * bw + qw * by + qz * bx - qx * bz; 361 | out.2 = qz * bw + qw * bz + qx * by - qy * bx; 362 | out.3 = qw * bw - qx * bx - qy * by - qz * bz; 363 | bx = a.4; 364 | by = a.5; 365 | bz = a.6; 366 | bw = a.7; 367 | out.4 = qx * bw + qw * bx + qy * bz - qz * by; 368 | out.5 = qy * bw + qw * by + qz * bx - qx * bz; 369 | out.6 = qz * bw + qw * bz + qx * by - qy * bx; 370 | out.7 = qw * bw - qx * bx - qy * by - qz * bz; 371 | } 372 | 373 | pub fn rotateAroundAxis(out: &mut Quaternion2, a: &Quaternion2, axis: &Vector3, rad: f32) { 374 | //Special case for rad = 0 375 | if (f32::abs(rad) < EPSILON) { 376 | Quaternion2::copy(out, a); 377 | return; 378 | } 379 | let axisLength = (axis.0.powi(2) + axis.1.powi(2) + axis.2.powi(2)).sqrt(); 380 | 381 | let rad = rad * 0.5; 382 | let s = f32::sin(rad); 383 | let bx = s * axis.0 / axisLength; 384 | let by = s * axis.1 / axisLength; 385 | let bz = s * axis.2 / axisLength; 386 | let bw = f32::cos(rad); 387 | 388 | let ax1 = a.0; 389 | let ay1 = a.1; 390 | let az1 = a.2; 391 | let aw1 = a.3; 392 | out.0 = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; 393 | out.1 = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; 394 | out.2 = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; 395 | out.3 = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; 396 | 397 | let ax = a.4; 398 | let ay = a.5; 399 | let az = a.6; 400 | let aw = a.7; 401 | out.4 = ax * bw + aw * bx + ay * bz - az * by; 402 | out.5 = ay * bw + aw * by + az * bx - ax * bz; 403 | out.6 = az * bw + aw * bz + ax * by - ay * bx; 404 | out.7 = aw * bw - ax * bx - ay * by - az * bz; 405 | } 406 | 407 | pub fn add(out: &mut Quaternion2, a: &Quaternion2, b: &Quaternion2) { 408 | out.0 = a.0 + b.0; 409 | out.1 = a.1 + b.1; 410 | out.2 = a.2 + b.2; 411 | out.3 = a.3 + b.3; 412 | out.4 = a.4 + b.4; 413 | out.5 = a.5 + b.5; 414 | out.6 = a.6 + b.6; 415 | out.7 = a.7 + b.7; 416 | } 417 | 418 | pub fn multiply(out: &mut Quaternion2, a: &Quaternion2, b: &Quaternion2) { 419 | let ax0 = a.0; 420 | let ay0 = a.1; 421 | let az0 = a.2; 422 | let aw0 = a.3; 423 | let bx1 = b.4; 424 | let by1 = b.5; 425 | let bz1 = b.6; 426 | let bw1 = b.7; 427 | let ax1 = a.4; 428 | let ay1 = a.5; 429 | let az1 = a.6; 430 | let aw1 = a.7; 431 | let bx0 = b.0; 432 | let by0 = b.1; 433 | let bz0 = b.2; 434 | let bw0 = b.3; 435 | out.0 = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0; 436 | out.1 = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0; 437 | out.2 = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0; 438 | out.3 = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0; 439 | out.4 = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 440 | - az1 * by0; 441 | out.5 = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 442 | - ax1 * bz0; 443 | out.6 = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 444 | - ay1 * bx0; 445 | out.7 = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 446 | - ax1 * bx0 447 | - ay1 * by0 448 | - az1 * bz0; 449 | } 450 | 451 | pub fn mul(out: &mut Quaternion2, a: &Quaternion2, b: &Quaternion2) { 452 | Quaternion2::multiply(out, a, b); 453 | } 454 | 455 | pub fn scale(out: &mut Quaternion2, a: &Quaternion2, b: f32) { 456 | out.0 = a.0 * b; 457 | out.1 = a.1 * b; 458 | out.2 = a.2 * b; 459 | out.3 = a.3 * b; 460 | out.4 = a.4 * b; 461 | out.5 = a.5 * b; 462 | out.6 = a.6 * b; 463 | out.7 = a.7 * b; 464 | } 465 | 466 | pub fn dot(a: &Quaternion2, b: &Quaternion2) -> f32 { 467 | a.0 * b.0 + a.1 * b.1 + a.2 * b.2 + a.3 * b.3 468 | } 469 | 470 | pub fn lerp(out: &mut Quaternion2, a: &Quaternion2, b: &Quaternion2, t: f32) { 471 | let mt = 1. - t; 472 | let mut t = t; 473 | if (Quaternion2::dot(a, b) < EPSILON) { 474 | t = -t; 475 | } 476 | 477 | out.0 = a.0 * mt + b.0 * t; 478 | out.1 = a.1 * mt + b.1 * t; 479 | out.2 = a.2 * mt + b.2 * t; 480 | out.3 = a.3 * mt + b.3 * t; 481 | out.4 = a.4 * mt + b.4 * t; 482 | out.5 = a.5 * mt + b.5 * t; 483 | out.6 = a.6 * mt + b.6 * t; 484 | out.7 = a.7 * mt + b.7 * t; 485 | } 486 | 487 | pub fn invert(out: &mut Quaternion2, a: &Quaternion2) { 488 | let sqlen = Quaternion2::squaredLength(a); 489 | out.0 = -a.0 / sqlen; 490 | out.1 = -a.1 / sqlen; 491 | out.2 = -a.2 / sqlen; 492 | out.3 = a.3 / sqlen; 493 | out.4 = -a.4 / sqlen; 494 | out.5 = -a.5 / sqlen; 495 | out.6 = -a.6 / sqlen; 496 | out.7 = a.7 / sqlen; 497 | } 498 | 499 | pub fn conjugate(out: &mut Quaternion2, a: &Quaternion2) { 500 | out.0 = -a.0; 501 | out.1 = -a.1; 502 | out.2 = -a.2; 503 | out.3 = a.3; 504 | out.4 = -a.4; 505 | out.5 = -a.5; 506 | out.6 = -a.6; 507 | out.7 = a.7; 508 | } 509 | 510 | pub fn len(a: &Quaternion2) -> f32 { 511 | let x = a.0; 512 | let y = a.1; 513 | let z = a.2; 514 | let w = a.3; 515 | (x.powi(2) + y.powi(2) + z.powi(2) + w.powi(2)).sqrt() 516 | } 517 | 518 | pub fn squaredLength(a: &Quaternion2) -> f32 { 519 | let x = a.0; 520 | let y = a.1; 521 | let z = a.2; 522 | let w = a.3; 523 | x * x + y * y + z * z + w * w 524 | } 525 | 526 | pub fn sqrLen(a: &Quaternion2) -> f32 { 527 | Quaternion2::squaredLength(a) 528 | } 529 | 530 | pub fn normalize(out: &mut Quaternion2, a: &Quaternion2) { 531 | let mut magnitude = Quaternion2::squaredLength(a); 532 | if (magnitude > EPSILON) { 533 | magnitude = f32::sqrt(magnitude); 534 | 535 | let a0 = a.0 / magnitude; 536 | let a1 = a.1 / magnitude; 537 | let a2 = a.2 / magnitude; 538 | let a3 = a.3 / magnitude; 539 | 540 | let b0 = a.4; 541 | let b1 = a.5; 542 | let b2 = a.6; 543 | let b3 = a.7; 544 | 545 | let a_dot_b = (a0 * b0) + (a1 * b1) + (a2 * b2) + (a3 * b3); 546 | 547 | out.0 = a0; 548 | out.1 = a1; 549 | out.2 = a2; 550 | out.3 = a3; 551 | 552 | out.4 = (b0 - (a0 * a_dot_b)) / magnitude; 553 | out.5 = (b1 - (a1 * a_dot_b)) / magnitude; 554 | out.6 = (b2 - (a2 * a_dot_b)) / magnitude; 555 | out.7 = (b3 - (a3 * a_dot_b)) / magnitude; 556 | } 557 | } 558 | 559 | // pub fn str(a: &Quaternion2) { 560 | // return "quat2(" + a.0 + ", " + a.1 + ", " + a.2 + ", " + a.3 + ", " + 561 | // a.4 + ", " + a.5 + ", " + a.6 + ", " + a.7 + ")"; 562 | // } 563 | 564 | pub fn exactEquals(a: &Quaternion2, b: &Quaternion2) -> bool { 565 | a.0 == b.0 566 | && a.1 == b.1 567 | && a.2 == b.2 568 | && a.3 == b.3 569 | && a.4 == b.4 570 | && a.5 == b.5 571 | && a.6 == b.6 572 | && a.7 == b.7 573 | } 574 | 575 | pub fn equals(a: &Quaternion2, b: &Quaternion2) -> bool { 576 | let a0 = a.0; 577 | let a1 = a.1; 578 | let a2 = a.2; 579 | let a3 = a.3; 580 | let a4 = a.4; 581 | let a5 = a.5; 582 | let a6 = a.6; 583 | let a7 = a.7; 584 | let b0 = b.0; 585 | let b1 = b.1; 586 | let b2 = b.2; 587 | let b3 = b.3; 588 | let b4 = b.4; 589 | let b5 = b.5; 590 | let b6 = b.6; 591 | let b7 = b.7; 592 | f32::abs(a0 - b0) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a0), f32::abs(b0))) 593 | && f32::abs(a1 - b1) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a1), f32::abs(b1))) 594 | && f32::abs(a2 - b2) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a2), f32::abs(b2))) 595 | && f32::abs(a3 - b3) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a3), f32::abs(b3))) 596 | && f32::abs(a4 - b4) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a4), f32::abs(b4))) 597 | && f32::abs(a5 - b5) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a5), f32::abs(b5))) 598 | && f32::abs(a6 - b6) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a6), f32::abs(b6))) 599 | && f32::abs(a7 - b7) <= EPSILON * f32::max(1.0, f32::max(f32::abs(a7), f32::abs(b7))) 600 | } 601 | } 602 | -------------------------------------------------------------------------------- /spec/mat3-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from './spec-helper'; 2 | import { 3 | init, 4 | Vector2 as vec2, 5 | Vector3 as vec3, 6 | Vector4 as vec4, 7 | Matrix2 as mat2, 8 | Matrix2d as mat2d, 9 | Matrix3 as mat3, 10 | Matrix4 as mat4, 11 | Quaternion as quat, 12 | Quaternion2 as quat2, 13 | } from '../pkg/gl_matrix_wasm'; 14 | 15 | describe("mat3", function() { 16 | let out, matA, matB, identity, result; 17 | 18 | before(done => { 19 | init().then(() => done()); 20 | }); 21 | 22 | beforeEach(function() { 23 | matA = mat3.fromValues(1, 0, 0, 24 | 0, 1, 0, 25 | 1, 2, 1); 26 | 27 | matB = mat3.fromValues(1, 0, 0, 28 | 0, 1, 0, 29 | 3, 4, 1); 30 | 31 | out = mat3.fromValues(0, 0, 0, 32 | 0, 0, 0, 33 | 0, 0, 0); 34 | 35 | identity = mat3.fromValues(1, 0, 0, 36 | 0, 1, 0, 37 | 0, 0, 1); 38 | }); 39 | 40 | describe("normalFromMat4", function() { 41 | beforeEach(function() { 42 | matA = mat4.fromValues(1, 0, 0, 0, 43 | 0, 1, 0, 0, 44 | 0, 0, 1, 0, 45 | 0, 0, 0, 1); 46 | result = mat3.normalFromMat4(out, matA); 47 | }); 48 | 49 | 50 | 51 | describe("with translation and rotation", function() { 52 | beforeEach(function() { 53 | mat4.translate(matA, matA, vec3.fromValues(2, 4, 6)); 54 | mat4.rotateX(matA, matA, Math.PI / 2); 55 | 56 | result = mat3.normalFromMat4(out, matA); 57 | }); 58 | 59 | it("should give rotated matrix", function() { 60 | expect(out).toBeEqualish([1, 0, 0, 61 | 0, 0, 1, 62 | 0,-1, 0]); 63 | }); 64 | 65 | describe("and scale", function() { 66 | beforeEach(function() { 67 | mat4.scale(matA, matA, vec3.fromValues(2, 3, 4)); 68 | 69 | result = mat3.normalFromMat4(out, matA); 70 | }); 71 | 72 | it("should give rotated matrix", function() { 73 | expect(out).toBeEqualish([0.5, 0, 0, 74 | 0, 0, 0.333333, 75 | 0, -0.25, 0]); 76 | }); 77 | }); 78 | }); 79 | }); 80 | 81 | describe("fromQuat", function() { 82 | let q; 83 | 84 | beforeEach(function() { 85 | q = quat.fromValues( 0, -0.7071067811865475, 0, 0.7071067811865475 ); 86 | result = mat3.fromQuat(out, q); 87 | }); 88 | 89 | 90 | 91 | it("should rotate a vector the same as the original quat", function() { 92 | let v = vec3.create(); 93 | vec3.transformMat3(v, vec3.fromValues(0,0,-1), out); 94 | let v2 = vec3.create(); 95 | vec3.transformQuat(v2, vec3.fromValues(0,0,-1), q) 96 | expect(v).toBeEqualish(v2); 97 | }); 98 | 99 | it("should rotate a vector by PI/2 radians", function() { 100 | let v = vec3.create(); 101 | vec3.transformMat3(v, vec3.fromValues(0,0,-1), out); 102 | expect(v).toBeEqualish([1,0,0]); 103 | }); 104 | }); 105 | 106 | describe("fromMat4", function() { 107 | beforeEach(function() { 108 | result = mat3.fromMat4(out, mat4.fromValues(1, 2, 3, 4, 109 | 5, 6, 7, 8, 110 | 9,10,11,12, 111 | 13,14,15,16)); }); 112 | 113 | 114 | 115 | it("should calculate proper mat3", function() { 116 | expect(out).toBeEqualish([ 1, 2, 3, 117 | 5, 6, 7, 118 | 9,10,11]); 119 | }); 120 | }); 121 | 122 | describe("scale", function() { 123 | beforeEach(function() { result = mat3.scale(out, matA, vec2.fromValues(2,2)); }); 124 | 125 | it('should place proper values in out', function() { 126 | expect(out).toBeEqualish([ 2, 0, 0, 127 | 0, 2, 0, 128 | 1, 2, 1 ]); 129 | }); 130 | }); 131 | 132 | describe("create", function() { 133 | beforeEach(function() { result = mat3.create(); }); 134 | it("should return a 9 element array initialized to a 3x3 identity matrix", function() { expect(result).toBeEqualish(identity); }); 135 | }); 136 | 137 | describe("clone", function() { 138 | beforeEach(function() { result = mat3.clone(matA); }); 139 | it("should return a 9 element array initialized to the values in matA", function() { expect(result).toBeEqualish(matA); }); 140 | }); 141 | 142 | describe("copy", function() { 143 | beforeEach(function() { result = mat3.copy(out, matA); }); 144 | it("should place values into out", function() { expect(out).toBeEqualish(matA); }); 145 | 146 | }); 147 | 148 | describe("identity", function() { 149 | beforeEach(function() { result = mat3.identity(out); }); 150 | it("should place values into out", function() { expect(out).toBeEqualish(identity); }); 151 | 152 | }); 153 | 154 | describe("transpose", function() { 155 | describe("with a separate output matrix", function() { 156 | beforeEach(function() { result = mat3.transpose(out, matA); }); 157 | 158 | it("should place values into out", function() { 159 | expect(out).toBeEqualish([ 160 | 1, 0, 1, 161 | 0, 1, 2, 162 | 0, 0, 1 163 | ]); 164 | }); 165 | 166 | it("should not modify matA", function() { 167 | expect(matA).toBeEqualish([ 168 | 1, 0, 0, 169 | 0, 1, 0, 170 | 1, 2, 1 171 | ]); 172 | }); 173 | }); 174 | 175 | describe("when matA is the output matrix", function() { 176 | beforeEach(function() { result = mat3.transpose(matA, matA); }); 177 | 178 | it("should place values into matA", function() { 179 | expect(matA).toBeEqualish([ 180 | 1, 0, 1, 181 | 0, 1, 2, 182 | 0, 0, 1 183 | ]); 184 | }); 185 | 186 | }); 187 | }); 188 | 189 | describe("invert", function() { 190 | describe("with a separate output matrix", function() { 191 | beforeEach(function() { result = mat3.invert(out, matA); }); 192 | 193 | it("should place values into out", function() { 194 | expect(out).toBeEqualish([ 195 | 1, 0, 0, 196 | 0, 1, 0, 197 | -1, -2, 1 198 | ]); 199 | }); 200 | 201 | it("should not modify matA", function() { 202 | expect(matA).toBeEqualish([ 203 | 1, 0, 0, 204 | 0, 1, 0, 205 | 1, 2, 1 206 | ]); 207 | }); 208 | }); 209 | 210 | describe("when matA is the output matrix", function() { 211 | beforeEach(function() { result = mat3.invert(matA, matA); }); 212 | 213 | it("should place values into matA", function() { 214 | expect(matA).toBeEqualish([ 215 | 1, 0, 0, 216 | 0, 1, 0, 217 | -1, -2, 1 218 | ]); 219 | }); 220 | 221 | }); 222 | }); 223 | 224 | describe("adjoint", function() { 225 | describe("with a separate output matrix", function() { 226 | beforeEach(function() { result = mat3.adjoint(out, matA); }); 227 | 228 | it("should place values into out", function() { 229 | expect(out).toBeEqualish([ 230 | 1, 0, 0, 231 | 0, 1, 0, 232 | -1, -2, 1 233 | ]); 234 | }); 235 | 236 | it("should not modify matA", function() { 237 | expect(matA).toBeEqualish([ 238 | 1, 0, 0, 239 | 0, 1, 0, 240 | 1, 2, 1 241 | ]); 242 | }); 243 | }); 244 | 245 | describe("when matA is the output matrix", function() { 246 | beforeEach(function() { result = mat3.adjoint(matA, matA); }); 247 | 248 | it("should place values into matA", function() { 249 | expect(matA).toBeEqualish([ 250 | 1, 0, 0, 251 | 0, 1, 0, 252 | -1, -2, 1 253 | ]); 254 | }); 255 | 256 | }); 257 | }); 258 | 259 | describe("determinant", function() { 260 | beforeEach(function() { result = mat3.determinant(matA); }); 261 | 262 | it("should return the determinant", function() { expect(result).toEqual(1); }); 263 | }); 264 | 265 | describe("multiply", function() { 266 | 267 | 268 | describe("with a separate output matrix", function() { 269 | beforeEach(function() { result = mat3.multiply(out, matA, matB); }); 270 | 271 | it("should place values into out", function() { 272 | expect(out).toBeEqualish([ 273 | 1, 0, 0, 274 | 0, 1, 0, 275 | 4, 6, 1 276 | ]); 277 | }); 278 | 279 | it("should not modify matA", function() { 280 | expect(matA).toBeEqualish([ 281 | 1, 0, 0, 282 | 0, 1, 0, 283 | 1, 2, 1 284 | ]); 285 | }); 286 | it("should not modify matB", function() { 287 | expect(matB).toBeEqualish([ 288 | 1, 0, 0, 289 | 0, 1, 0, 290 | 3, 4, 1 291 | ]); 292 | }); 293 | }); 294 | 295 | describe("when matA is the output matrix", function() { 296 | beforeEach(function() { result = mat3.multiply(matA, matA, matB); }); 297 | 298 | it("should place values into matA", function() { 299 | expect(matA).toBeEqualish([ 300 | 1, 0, 0, 301 | 0, 1, 0, 302 | 4, 6, 1 303 | ]); 304 | }); 305 | 306 | it("should not modify matB", function() { 307 | expect(matB).toBeEqualish([ 308 | 1, 0, 0, 309 | 0, 1, 0, 310 | 3, 4, 1 311 | ]); 312 | }); 313 | }); 314 | 315 | describe("when matB is the output matrix", function() { 316 | beforeEach(function() { result = mat3.multiply(matB, matA, matB); }); 317 | 318 | it("should place values into matB", function() { 319 | expect(matB).toBeEqualish([ 320 | 1, 0, 0, 321 | 0, 1, 0, 322 | 4, 6, 1 323 | ]); 324 | }); 325 | 326 | it("should not modify matA", function() { 327 | expect(matA).toBeEqualish([ 328 | 1, 0, 0, 329 | 0, 1, 0, 330 | 1, 2, 1 331 | ]); 332 | }); 333 | }); 334 | }); 335 | 336 | describe("frob", function() { 337 | beforeEach(function() { result = mat3.frob(matA); }); 338 | it("should return the Frobenius Norm of the matrix", function() { expect(result).toEqual( 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))); }); 339 | }); 340 | 341 | describe("add", function() { 342 | beforeEach(function() { 343 | matA = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 344 | matB = mat3.fromValues(10, 11, 12, 13, 14, 15, 16, 17, 18); 345 | }); 346 | describe("with a separate output matrix", function() { 347 | beforeEach(function() { 348 | result = mat3.add(out, matA, matB); 349 | }); 350 | 351 | it("should place values into out", function() { expect(out).toBeEqualish([11, 13, 15, 17, 19, 21, 23, 25, 27]); }); 352 | 353 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 354 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 355 | }); 356 | 357 | describe("when matA is the output matrix", function() { 358 | beforeEach(function() { result = mat3.add(matA, matA, matB); }); 359 | 360 | it("should place values into matA", function() { expect(matA).toBeEqualish([11, 13, 15, 17, 19, 21, 23, 25, 27]); }); 361 | 362 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 363 | }); 364 | 365 | describe("when matB is the output matrix", function() { 366 | beforeEach(function() { result = mat3.add(matB, matA, matB); }); 367 | 368 | it("should place values into matB", function() { expect(matB).toBeEqualish([11, 13, 15, 17, 19, 21, 23, 25, 27]); }); 369 | 370 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 371 | }); 372 | }); 373 | 374 | describe("subtract", function() { 375 | beforeEach(function() { 376 | matA = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 377 | matB = mat3.fromValues(10, 11, 12, 13, 14, 15, 16, 17, 18); 378 | }); 379 | 380 | 381 | describe("with a separate output matrix", function() { 382 | beforeEach(function() { result = mat3.subtract(out, matA, matB); }); 383 | 384 | it("should place values into out", function() { expect(out).toBeEqualish([-9, -9, -9, -9, -9, -9, -9, -9, -9]); }); 385 | 386 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 387 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 388 | }); 389 | 390 | describe("when matA is the output matrix", function() { 391 | beforeEach(function() { result = mat3.subtract(matA, matA, matB); }); 392 | 393 | it("should place values into matA", function() { expect(matA).toBeEqualish([-9, -9, -9, -9, -9, -9, -9, -9, -9]); }); 394 | 395 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 396 | }); 397 | 398 | describe("when matB is the output matrix", function() { 399 | beforeEach(function() { result = mat3.subtract(matB, matA, matB); }); 400 | 401 | it("should place values into matB", function() { expect(matB).toBeEqualish([-9, -9, -9, -9, -9, -9, -9, -9, -9]); }); 402 | 403 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 404 | }); 405 | }); 406 | 407 | describe("fromValues", function() { 408 | beforeEach(function() { result = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); }); 409 | it("should return a 9 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 410 | }); 411 | 412 | describe("set", function() { 413 | beforeEach(function() { result = mat3.set(out, 1, 2, 3, 4, 5, 6, 7, 8, 9); }); 414 | it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 415 | 416 | }); 417 | 418 | describe("multiplyScalar", function() { 419 | beforeEach(function() { 420 | matA = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 421 | }); 422 | describe("with a separate output matrix", function() { 423 | beforeEach(function() { result = mat3.multiplyScalar(out, matA, 2); }); 424 | 425 | it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 6, 8, 10, 12, 14, 16, 18]); }); 426 | 427 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 428 | }); 429 | 430 | describe("when matA is the output matrix", function() { 431 | beforeEach(function() { result = mat3.multiplyScalar(matA, matA, 2); }); 432 | 433 | it("should place values into matA", function() { expect(matA).toBeEqualish([2, 4, 6, 8, 10, 12, 14, 16, 18]); }); 434 | 435 | }); 436 | }); 437 | 438 | describe("multiplyScalarAndAdd", function() { 439 | beforeEach(function() { 440 | matA = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 441 | matB = mat3.fromValues(10, 11, 12, 13, 14, 15, 16, 17, 18); 442 | }); 443 | describe("with a separate output matrix", function() { 444 | beforeEach(function() { result = mat3.multiplyScalarAndAdd(out, matA, matB, 0.5); }); 445 | 446 | it("should place values into out", function() { expect(out).toBeEqualish([6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); }); 447 | 448 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 449 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 450 | }); 451 | 452 | describe("when matA is the output matrix", function() { 453 | beforeEach(function() { result = mat3.multiplyScalarAndAdd(matA, matA, matB, 0.5); }); 454 | 455 | it("should place values into matA", function() { expect(matA).toBeEqualish([6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); }); 456 | 457 | it("should not modify matB", function() { expect(matB).toBeEqualish([10, 11, 12, 13, 14, 15, 16, 17, 18]); }); 458 | }); 459 | 460 | describe("when matB is the output matrix", function() { 461 | beforeEach(function() { result = mat3.multiplyScalarAndAdd(matB, matA, matB, 0.5); }); 462 | 463 | it("should place values into matB", function() { expect(matB).toBeEqualish([6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18]); }); 464 | 465 | it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); 466 | }); 467 | }); 468 | 469 | describe("projection", function() { 470 | beforeEach(function() { 471 | result = mat3.projection(out, 100.0, 200.0); 472 | }); 473 | 474 | 475 | 476 | it("should give projection matrix", function() { 477 | expect(out).toBeEqualish([0.02, 0, 0, 478 | 0, -0.01, 0, 479 | -1, 1, 1]); 480 | }); 481 | }); 482 | 483 | describe("exactEquals", function() { 484 | let matC, r0, r1; 485 | beforeEach(function() { 486 | matA = mat3.fromValues(0, 1, 2, 3, 4, 5, 6, 7, 8); 487 | matB = mat3.fromValues(0, 1, 2, 3, 4, 5, 6, 7, 8); 488 | matC = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 489 | r0 = mat3.exactEquals(matA, matB); 490 | r1 = mat3.exactEquals(matA, matC); 491 | }); 492 | 493 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 494 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 495 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3, 4, 5, 6, 7, 8]); }); 496 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3, 4, 5, 6, 7, 8]); }); 497 | }); 498 | 499 | describe("equals", function() { 500 | let matC, matD, r0, r1, r2; 501 | beforeEach(function() { 502 | matA = mat3.fromValues(0, 1, 2, 3, 4, 5, 6, 7, 8); 503 | matB = mat3.fromValues(0, 1, 2, 3, 4, 5, 6, 7, 8); 504 | matC = mat3.fromValues(1, 2, 3, 4, 5, 6, 7, 8, 9); 505 | matD = mat3.fromValues(1e-16, 1, 2, 3, 4, 5, 6, 7, 8); 506 | r0 = mat3.equals(matA, matB); 507 | r1 = mat3.equals(matA, matC); 508 | r2 = mat3.equals(matA, matD); 509 | }); 510 | it("should return true for identical matrices", function() { expect(r0).toBe(true); }); 511 | it("should return false for different matrices", function() { expect(r1).toBe(false); }); 512 | it("should return true for close but not identical matrices", function() { expect(r2).toBe(true); }); 513 | it("should not modify matA", function() { expect(matA).toBeEqualish([0, 1, 2, 3, 4, 5, 6, 7, 8]); }); 514 | it("should not modify matB", function() { expect(matB).toBeEqualish([0, 1, 2, 3, 4, 5, 6, 7, 8]); }); 515 | }); 516 | 517 | }); 518 | --------------------------------------------------------------------------------
Wasm: {this.state[name].wasm}ms
JS: {this.state[name].js}ms