├── .eslintignore ├── test ├── simple.js ├── foo.js ├── foo.test.js └── index.test.js ├── .gitignore ├── babel.config.cjs ├── .codeclimate.yml ├── .eslintrc.cjs ├── .github └── workflows │ └── main.yaml ├── package.json ├── README-CN.md ├── README.md └── src ├── retmap.js ├── utils.js └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | test/out.cjs -------------------------------------------------------------------------------- /test/simple.js: -------------------------------------------------------------------------------- 1 | let m = mat2(1, 0, 0, 1); 2 | m *= 3; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | test/out.js 4 | coverage -------------------------------------------------------------------------------- /test/foo.js: -------------------------------------------------------------------------------- 1 | export default function foo(a, b) { 2 | if(a > 0 && b > 0) { 3 | return 10; 4 | } else if(b > 0) { 5 | return 0; 6 | } else { 7 | return -10; 8 | } 9 | } -------------------------------------------------------------------------------- /test/foo.test.js: -------------------------------------------------------------------------------- 1 | import foo from './foo.js' 2 | 3 | test('foo', () => { 4 | expect(foo(1, 2)).toBe(10); 5 | expect(foo(-1, -2)).toBe(-10); 6 | expect(foo(-1, 2)).toBe(0); 7 | }); -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', 4 | { 5 | targets: { 6 | node: 'current', 7 | }, 8 | }], 9 | ], 10 | plugins: [ 11 | './src/index.js', 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | method-complexity: 3 | enabled: true 4 | config: 5 | threshold: 50 6 | method-lines: 7 | enabled: true 8 | config: 9 | threshold: 200 10 | file-lines: 11 | enabled: true 12 | config: 13 | threshold: 800 14 | exclude_patterns: 15 | - "test/" 16 | - "**/.eslintrc.cjs" 17 | - "**/babel.config.cjs" -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | jest: true, 3 | extends: 'airbnb-base', 4 | rules: { 5 | 'no-console': 'off', 6 | 'no-param-reassign': 'off', 7 | 'import/prefer-default-export': 'off', 8 | 'no-underscore-dangle': 'off', 9 | 'import/extensions': 'off', 10 | 'no-mixed-operators': 'off', 11 | 'no-plusplus': 'off', 12 | 'import/no-named-as-default-member': 'off', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | run-tests: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: [16] 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | registry-url: https://registry.npmjs.org 20 | - name: Install dependencies 📦️ 21 | run: npm install 22 | # - name: test 23 | # run: npm run test:coverage 24 | - name: Code Climate Coverage Action 25 | uses: paambaati/codeclimate-action@v3.2.0 26 | env: 27 | CC_TEST_REPORTER_ID: 0cad7f41ea82b1b3ae2b1d64cc05c040ed59a17c16ca0a125ff711031b5905be 28 | with: 29 | coverageCommand: npm run test:coverage 30 | debug: true 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-gl-matrix", 3 | "version": "1.0.1", 4 | "description": "A babel plugin for gl-matrix", 5 | "main": "src/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "compile": "babel test/index.js", 9 | "compile:case": "babel test/simple.js", 10 | "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", 11 | "test:coverage": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage", 12 | "lint": "eslint --fix --ext .js src test" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@babel/cli": "^7.19.3", 19 | "@babel/core": "^7.20.5", 20 | "@babel/preset-env": "^7.20.2", 21 | "cross-env": "^7.0.3", 22 | "eslint": "^8.29.0", 23 | "eslint-config-airbnb-base": "^15.0.0", 24 | "eslint-plugin-import": "^2.26.0", 25 | "gl-matrix": "^3.4.3", 26 | "jest": "^29.3.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transfrom-gl-matrix 2 | 3 | 这是 [gl-matrix](https://github.com/toji/gl-matrix) 的 babel 插件。 4 | 5 | gl-matrix是个高性能的JavaScript矩阵运算库,但是它的API不方便使用。 6 | 7 | 例如,要定义一个3维向量,原生的API长这样: 8 | 9 | ```js 10 | const v = vec3.fromValues(1, 0, 0); 11 | ``` 12 | 13 | 有了这个插件以后,可以这么写: 14 | 15 | ```js 16 | const v = vec3(1, 0, 0); 17 | ``` 18 | 19 | 要将两个向量相加,原生的API长这样: 20 | 21 | ```js 22 | const v1 = vec3.fromValues(1, 0, 0); 23 | const v2 = vec3.fromValues(0, 1, 0); 24 | const v = vec3.add(vec3.create(), v1, v2); 25 | ``` 26 | 27 | 用这个插件可以这么写: 28 | 29 | ```js 30 | const v1 = vec3(1, 0, 0); 31 | const v2 = vec3(0, 1, 0); 32 | const v = v1 + v2; 33 | ``` 34 | 35 | 同样,求两个向量的乘积: 36 | 37 | ```js 38 | const v = vec3.multiply(vec3.create(), v1, v2); // 原生写法 39 | ``` 40 | 41 | ```js 42 | const v = v1 * v2; // 插件写法 43 | ``` 44 | 45 | 标量与向量相乘: 46 | 47 | ```js 48 | const v = vec3.scale(vec3.create(), v1, n); // 原生写法 49 | ``` 50 | 51 | ```js 52 | const v = vec3(v1) * n; // 插件写法 53 | ``` 54 | 55 | 向量的矩阵变换: 56 | 57 | ```js 58 | const v1 = vec3.fromValues(1, 2, 3); 59 | const m1 = mat3.fromValues(2, 0, 0, 0, 2, 0, 0, 0, 2); 60 | 61 | const v = vec3.transformMat3(vec3.create(), v1, m1); // 原生写法 62 | ``` 63 | 64 | ```js 65 | const v1 = vec3(1, 2, 3); 66 | const m1 = mat3(2, 0, 0, 0, 2, 0, 0, 0, 2); 67 | 68 | const v = mat3(m1) * vec3(v1); // 插件写法 69 | ``` 70 | 71 | 比较 (0.4之后的版本) 72 | 73 | ```js 74 | const v1 = vec3.fromValues(1, 2, 3); 75 | const v2 = vec3.fromValues(1, 2, 3); 76 | const v3 = vec3.fromValues(1, 1, 1); 77 | 78 | // original 79 | console.log(vec3.equals(v1, v2), vec3.equals(v1, v3)); // true false 80 | ``` 81 | 82 | ```js 83 | const v1 = vec3(1, 2, 3); 84 | const v2 = vec3(1, 2, 3); 85 | const v3 = vec3(1, 1, 1); 86 | 87 | console.log(v1 == v2, v1 != v3); // true, true 88 | ``` 89 | 90 | 向量分量 (version > 1.0),支持xyzw, rgba, stpq, 和glsl一样。 91 | 92 | ```js 93 | const v1 = vec3.fromValues(1, 2, 3); 94 | const v2 = vec2.fromValues(v1[0], v1[1]); 95 | ``` 96 | 97 | ```js 98 | const v1 = vec3(1, 2, 3); 99 | const v2 = v1.xy; // with plugin 100 | ``` 101 | 102 | **注意:所有第一个参数为out的方法,使用插件都不应写out而是直接赋值。** 103 | 104 | ## 使用方式 105 | 106 | 配置 .babelrc 107 | 108 | ```json 109 | { 110 | "presets": [ 111 | ["@babel/preset-env", 112 | { 113 | "targets": { 114 | "node": "current" 115 | } 116 | }] 117 | ], 118 | "plugins": [ 119 | "./src/index.js" 120 | ] 121 | } 122 | ``` 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-transfrom-gl-matrix 2 | 3 | [中文文档](README-CN.md) 4 | 5 | A babel plugin for [gl-matrix](https://github.com/toji/gl-matrix). 6 | 7 | GlMatrix is a high-performance JavaScript matrix library, but its API is not easy to use. 8 | 9 | For example, to define a three-dimensional vector, the native API looks like this: 10 | 11 | ```js 12 | const v = vec3.fromValues(1, 0, 0); 13 | ``` 14 | 15 | With this plug-in, you can write as follows: 16 | 17 | ```js 18 | const v = vec3(1, 0, 0); 19 | ``` 20 | 21 | To add the two vectors together, the native API looks like this: 22 | 23 | ```js 24 | const v1 = vec3.fromValues(1, 0, 0); 25 | const v2 = vec3.fromValues(0, 1, 0); 26 | const v = vec3.add(vec3.create(), v1, v2); 27 | ``` 28 | 29 | With this plug-in, you can write as follows: 30 | 31 | ```js 32 | const v1 = vec3(1, 0, 0); 33 | const v2 = vec3(0, 1, 0); 34 | const v = v1 + v2; 35 | ``` 36 | 37 | Similarly, calculate the multiplication of two vectors: 38 | 39 | ```js 40 | const v = vec3.multiply(vec3.create(), v1, v2); // original 41 | ``` 42 | 43 | ```js 44 | const v = v1 * v2; // with plugin 45 | ``` 46 | 47 | Multiplication of scalars and vectors: 48 | 49 | ```js 50 | const v = vec3.scale(vec3.create(), v1, n); // original 51 | ``` 52 | 53 | ```js 54 | const v = v1 * n; // with plugin 55 | ``` 56 | 57 | Matrix transformation of vectors: 58 | 59 | ```js 60 | const v1 = vec3.fromValues(1, 2, 3); 61 | const m1 = mat3.fromValues(2, 0, 0, 0, 2, 0, 0, 0, 2); 62 | 63 | const v = vec3.transformMat3(vec3.create(), v1, m1); // original 64 | ``` 65 | 66 | ```js 67 | const v1 = vec3(1, 2, 3); 68 | const m1 = mat3(2, 0, 0, 0, 2, 0, 0, 0, 2); 69 | 70 | const v = m1 * v1; // with plugin 71 | ``` 72 | 73 | Comparative (version > 0.4) 74 | 75 | ```js 76 | const v1 = vec3.fromValues(1, 2, 3); 77 | const v2 = vec3.fromValues(1, 2, 3); 78 | const v3 = vec3.fromValues(1, 1, 1); 79 | 80 | // original 81 | console.log(vec3.equals(v1, v2), vec3.equals(v1, v3)); // true false 82 | ``` 83 | 84 | ```js 85 | const v1 = vec3(1, 2, 3); 86 | const v2 = vec3(1, 2, 3); 87 | const v3 = vec3(1, 1, 1); 88 | 89 | console.log(v1 == v2, v1 != v3); // true, true 90 | ``` 91 | 92 | Vector Components (version > 1.0) 93 | 94 | Supports xyzw, rgba, stpq, the same as glsl. 95 | 96 | ```js 97 | const v1 = vec3.fromValues(1, 2, 3); 98 | const v2 = vec2.fromValues(v1[0], v1[1]); 99 | ``` 100 | 101 | ```js 102 | const v1 = vec3(1, 2, 3); 103 | const v2 = v1.xy; // with plugin 104 | ``` 105 | 106 | **Note: For all methods whose first parameter is out, when using this plug-in, you should ignore the `out` and directly get the return value.** 107 | 108 | ## Usage 109 | 110 | .babelrc 111 | 112 | ```json 113 | { 114 | "presets": [ 115 | ["@babel/preset-env", 116 | { 117 | "targets": { 118 | "node": "current" 119 | } 120 | }] 121 | ], 122 | "plugins": [ 123 | "transform-gl-matrix" 124 | ] 125 | } 126 | ``` 127 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | vec2, 3 | vec3, 4 | mat2, 5 | mat2d, 6 | mat3, 7 | mat4, 8 | quat, 9 | quat2, 10 | glMatrix, 11 | } from 'gl-matrix'; 12 | 13 | glMatrix.setMatrixArrayType(Array); 14 | 15 | test('mat2d expand', () => { 16 | const arr = [1, 2, 3, 4, 5, 6]; 17 | const m1 = mat2d(...arr); 18 | const m2 = mat2d.fromValues(...arr); 19 | expect(m1).toEqual(m2); 20 | }); 21 | 22 | test('create vec2', () => { 23 | const v1 = vec2(1.0, 1.0); 24 | const v2 = vec2.fromValues(1.0, 1.0); 25 | 26 | expect(v1).toEqual(v2); 27 | }); 28 | 29 | test('create vec3', () => { 30 | const v1 = vec3(1.0, 1.0, 1.0); 31 | const v2 = vec3.fromValues(1.0, 1.0, 1.0); 32 | 33 | expect(v1).toEqual(v2); 34 | 35 | const v3 = vec2(1.0, 1.0); 36 | const v4 = vec3(vec2(v3), 1.0); 37 | 38 | expect(v1).toEqual(v4); 39 | }); 40 | 41 | test('create mat3', () => { 42 | const m1 = 2 * mat3(1, 0, 0, 0, 1, 0, 0, 0, 1); 43 | const m2 = mat3(1, 0, 0, 0, 1, 0, 0, 0, 1) * 2; 44 | 45 | expect(m1).toEqual(mat3(2, 0, 0, 0, 2, 0, 0, 0, 2)); 46 | expect(m1).toEqual(m2); 47 | }); 48 | 49 | test('eq', () => { 50 | const v1 = vec2(1, 0); 51 | const v2 = vec2(0, 1); 52 | 53 | expect(v1).toBe(vec2(v1)); 54 | expect(v2).toBe(vec2(v2)); 55 | }); 56 | 57 | test('equals', () => { 58 | const v1 = vec2(1, 0); 59 | const v2 = vec2(1, 0); 60 | 61 | expect(vec2(v1) == vec2(v2)).toBe(true); // eslint-disable-line eqeqeq 62 | expect(vec2(v1) == vec2(1, 0)).toBe(true); // eslint-disable-line eqeqeq 63 | expect(vec2(1, 0) == vec2(v2)).toBe(true); // eslint-disable-line eqeqeq 64 | expect([1, 0] == vec2(v2)).toBe(true); // eslint-disable-line eqeqeq 65 | expect(vec2(v1) == [1, 0]).toBe(true); // eslint-disable-line eqeqeq 66 | expect(vec2.equals(v1, v2)).toBe(true); 67 | }); 68 | 69 | test('not equals', () => { 70 | const v1 = vec2(1, 0); 71 | const v2 = vec2(1, 0); 72 | 73 | expect(vec2(v1) != vec2(v2)).toBe(false); // eslint-disable-line eqeqeq 74 | expect(vec2(v1) != [2, 0]).toBe(true); // eslint-disable-line eqeqeq 75 | expect([2, 0] != vec2(v2)).toBe(true); // eslint-disable-line eqeqeq 76 | expect(vec2(v1) != vec2(2, 0)).toBe(true); // eslint-disable-line eqeqeq 77 | expect(vec2(2, 0) != vec2(v2)).toBe(true); // eslint-disable-line eqeqeq 78 | }); 79 | 80 | test('eq ne 2', () => { 81 | const v1 = vec3.fromValues(1, 2, 3); 82 | const v2 = vec3.fromValues(1, 2, 3); 83 | const v3 = vec3.fromValues(1, 1, 1); 84 | 85 | expect([vec3(v1) == v2, vec3(v1) != v3]).toEqual([true, true]); // eslint-disable-line eqeqeq 86 | }); 87 | 88 | test('add1', () => { 89 | const v1 = vec2(1.0, 1.0); 90 | 91 | expect(vec2(v1) + 1).toEqual(vec2(2.0, 2.0)); 92 | expect(vec2(v1) + [1, 1]).toEqual(vec2(2.0, 2.0)); 93 | }); 94 | 95 | test('add2', () => { 96 | const v = vec2(1, 0) + vec2(0, 1) + 1; 97 | 98 | expect(v).toEqual(vec2(2, 2)); 99 | }); 100 | 101 | test('sub1', () => { 102 | const v = vec2(1, 0) - vec2(0, 1) + vec2(1, 1); 103 | 104 | expect(v).toEqual(vec2(2, 0)); 105 | }); 106 | 107 | test('sub2', () => { 108 | const v = vec2(1, 0) - (vec2(0, 1) + vec2(1, 1)); 109 | 110 | expect(v).toEqual(vec2(0, -2)); 111 | }); 112 | 113 | test('sub3', () => { 114 | const v = vec2(1, 0); 115 | 116 | expect(1 - vec2(v)).toEqual(vec2(0, 1)); 117 | }); 118 | 119 | test('vec multiply', () => { 120 | const v1 = vec2(1, 2); 121 | const v2 = vec2(3, 4); 122 | 123 | const v = vec2(v1) * vec2(v2); 124 | 125 | expect(v).toEqual(vec2.multiply(v1, v2)); 126 | 127 | expect(v).toEqual(vec2(3, 8)); 128 | 129 | expect(vec2(v1) * vec2(v2) + 1).toEqual(vec2(4, 9)); 130 | }); 131 | 132 | test('vec cross', () => { 133 | const v1 = vec2(1, 2); 134 | const v2 = vec2(3, 4); 135 | 136 | const v = vec2.cross(v1, v2); 137 | 138 | expect(v).toEqual(vec3(0, 0, -2)); 139 | }); 140 | 141 | test('dot', () => { 142 | const v1 = vec2(1, 2); 143 | const v2 = vec2(3, 4); 144 | 145 | expect(vec2.dot(v1, v2)).toBe(11); 146 | 147 | expect(vec2.dot(v1, v2) + 1).toBe(12); 148 | }); 149 | 150 | test('multiply', () => { 151 | const m1 = mat2d(1, 0, 0, 1, 0, 0); 152 | const m2 = mat2d(1, 2, 3, 4, 5, 6); 153 | 154 | expect(mat2d(m1) * mat2d(m2)).toEqual(mat2d.multiply(m1, m2)); 155 | 156 | const q1 = quat(1, 2, 3, 4); 157 | const q2 = quat(5, 6, 7, 8); 158 | 159 | expect(quat(q1) * quat(q2)).toEqual(quat.multiply(q1, q2)); 160 | 161 | const q3 = quat2(1, 2, 3, 4, 5, 6, 7, 8); 162 | const q4 = quat2(1, 0, 3, 5, 9, 8, 6, 4); 163 | 164 | expect(quat2(q3) * quat2(q4)).toEqual(quat2.multiply(q3, q4)); 165 | }); 166 | 167 | test('scale', () => { 168 | const v1 = 2 * vec2(1, 1); 169 | 170 | expect(v1).toEqual(vec2(2, 2)); 171 | 172 | const v2 = -vec2(1, 1); 173 | 174 | expect(v2).toEqual(vec2(-1, -1)); 175 | 176 | const v3 = vec2(1, 2) * 0.5; 177 | 178 | expect(v3).toEqual(vec2(0.5, 1)); 179 | }); 180 | 181 | test('transform', () => { 182 | const v1 = vec2(1, 1); 183 | const m1 = mat2(1, 0, 0, 1); 184 | expect(v1).toEqual(mat2(m1) * vec2(v1)); 185 | 186 | const m2 = mat2(2, 0, 0, 2); 187 | expect(vec2(v1) * 2).toEqual(mat2(m2) * vec2(v1)); 188 | 189 | const m3 = mat2d(1, 0, 0, 1, 0, 0); 190 | expect(v1).toEqual(mat2d(m3) * vec2(v1)); 191 | 192 | const m4 = mat2d(2, 0, 0, 2, 0, 0); 193 | expect(vec2(v1) * 2).toEqual(mat2d(m4) * vec2(v1)); 194 | 195 | const m5 = mat3(1, 0, 0, 0, 1, 0, 0, 0, 1); 196 | expect(v1).toEqual(mat3(m5) * vec2(v1)); 197 | 198 | const m6 = mat3(1, 2, 3, 4, 5, 6, 7, 8, 9); 199 | expect(mat3(m6) * vec2(v1)).toEqual(vec2.transformMat3(v1, m6)); 200 | 201 | const m7 = mat4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); 202 | expect(mat4(m7) * vec2(v1)).toEqual(vec2.transformMat4(v1, m7)); 203 | 204 | expect(mat4(m7) * [1, 1, 1]).toEqual(vec3.transformMat4(vec3(...v1, 1), m7)); 205 | 206 | expect(vec2(v1) * mat4(m7)).toEqual( 207 | vec2.transformMat4(v1, mat4.transpose(m7)), 208 | ); 209 | }); 210 | 211 | test('+=, -=, *=', () => { 212 | let v1 = vec2(1, 1); 213 | const v2 = v1; 214 | 215 | v1 += vec2(1, 1); 216 | 217 | expect(v1).toBe(v2); 218 | expect(v1).toEqual(vec2(2, 2)); 219 | 220 | let v3 = vec2(3, 4); 221 | const v4 = v3; 222 | 223 | v3 *= vec2(2, 2); 224 | 225 | expect(v3).toBe(v4); 226 | expect(v3).toEqual(vec2.multiply(vec2.fromValues(3, 4), vec2.fromValues(2, 2))); 227 | }); 228 | 229 | test('scope', () => { 230 | const mat2d = { 231 | add(a, b) { 232 | return a + b; 233 | }, 234 | }; 235 | expect(mat2d.add(1, 2)).toBe(3); 236 | }); 237 | -------------------------------------------------------------------------------- /src/retmap.js: -------------------------------------------------------------------------------- 1 | export default { 2 | vec2: 'vec2', 3 | vec3: 'vec3', 4 | vec4: 'vec4', 5 | mat2: 'mat2', 6 | mat2d: 'mat2d', 7 | mat3: 'mat3', 8 | mat4: 'mat4', 9 | quat: 'quat', 10 | quat2: 'quat2', 11 | 12 | 'vec2.create': 'vec2', 13 | 'vec2.clone': 'vec2', 14 | 'vec2.fromValues': 'vec2', 15 | 'vec2.copy': 'vec2', 16 | 'vec2.set': 'vec2', 17 | 'vec2.add': 'vec2', 18 | 'vec2.subtract': 'vec2', 19 | 'vec2.multiply': 'vec2', 20 | 'vec2.divide': 'vec2', 21 | 'vec2.ceil': 'vec2', 22 | 'vec2.floor': 'vec2', 23 | 'vec2.min': 'vec2', 24 | 'vec2.max': 'vec2', 25 | 'vec2.round': 'vec2', 26 | 'vec2.scale': 'vec2', 27 | 'vec2.scaleAndAdd': 'vec2', 28 | // 'vec2.distance': 'number', 29 | // 'vec2.squaredDistance': 'number', 30 | // 'vec2.length': 'number', 31 | // 'vec2.squaredLength': 'number', 32 | 'vec2.negate': 'vec2', 33 | 'vec2.inverse': 'vec2', 34 | 'vec2.normalize': 'vec2', 35 | // 'vec2.dot': 'number', 36 | 'vec2.cross': 'vec3', 37 | 'vec2.lerp': 'vec2', 38 | 'vec2.random': 'vec2', 39 | 'vec2.transformMat2': 'vec2', 40 | 'vec2.transformMat2d': 'vec2', 41 | 'vec2.transformMat3': 'vec2', 42 | 'vec2.transformMat4': 'vec2', 43 | 'vec2.rotate': 'vec2', 44 | // 'vec2.angle': 'number', 45 | 'vec2.zero': 'vec2', 46 | // 'vec2.str': 'string', 47 | // 'vec2.exactEquals': 'boolean', 48 | // 'vec2.equals': 'boolean', 49 | // 'vec2.len': 'number', 50 | 'vec2.sub': 'vec2', 51 | 'vec2.mul': 'vec2', 52 | 'vec2.div': 'vec2', 53 | // 'vec2.dist': 'number', 54 | // 'vec2.sqrDist': 'number', 55 | // 'vec2.sqrLen': 'number', 56 | // 'vec2.forEach': 'array', 57 | 58 | 'vec3.create': 'vec3', 59 | 'vec3.clone': 'vec3', 60 | 'vec3.fromValues': 'vec3', 61 | 'vec3.copy': 'vec3', 62 | 'vec3.set': 'vec3', 63 | 'vec3.add': 'vec3', 64 | 'vec3.subtract': 'vec3', 65 | 'vec3.multiply': 'vec3', 66 | 'vec3.divide': 'vec3', 67 | 'vec3.ceil': 'vec3', 68 | 'vec3.floor': 'vec3', 69 | 'vec3.min': 'vec3', 70 | 'vec3.max': 'vec3', 71 | 'vec3.round': 'vec3', 72 | 'vec3.scale': 'vec3', 73 | 'vec3.scaleAndAdd': 'vec3', 74 | // 'vec3.distance': 'number', 75 | // 'vec3.squaredDistance': 'number', 76 | // 'vec3.length': 'number', 77 | // 'vec3.squaredLength': 'number', 78 | 'vec3.negate': 'vec3', 79 | 'vec3.inverse': 'vec3', 80 | 'vec3.normalize': 'vec3', 81 | // 'vec3.dot': 'number', 82 | 'vec3.cross': 'vec3', 83 | 'vec3.lerp': 'vec3', 84 | 'vec3.hermite': 'vec3', 85 | 'vec3.bezier': 'vec3', 86 | 'vec3.random': 'vec3', 87 | 'vec3.transformMat3': 'vec3', 88 | 'vec3.transformMat4': 'vec3', 89 | 'vec3.transformQuat': 'vec3', 90 | 'vec3.rotateX': 'vec3', 91 | 'vec3.rotateY': 'vec3', 92 | 'vec3.rotateZ': 'vec3', 93 | // 'vec3.angle': 'number', 94 | 'vec3.zero': 'vec3', 95 | // 'vec3.str': 'string', 96 | // 'vec3.exactEquals': 'boolean', 97 | // 'vec3.equals': 'boolean', 98 | // 'vec3.len': 'number', 99 | 'vec3.sub': 'vec3', 100 | 'vec3.mul': 'vec3', 101 | 'vec3.div': 'vec3', 102 | // 'vec3.dist': 'number', 103 | // 'vec3.sqrDist': 'number', 104 | // 'vec3.sqrLen': 'number', 105 | // 'vec3.forEach': 'array', 106 | 107 | 'vec4.create': 'vec4', 108 | 'vec4.clone': 'vec4', 109 | 'vec4.fromValues': 'vec4', 110 | 'vec4.copy': 'vec4', 111 | 'vec4.set': 'vec4', 112 | 'vec4.add': 'vec4', 113 | 'vec4.subtract': 'vec4', 114 | 'vec4.multiply': 'vec4', 115 | 'vec4.divide': 'vec4', 116 | 'vec4.ceil': 'vec4', 117 | 'vec4.floor': 'vec4', 118 | 'vec4.min': 'vec4', 119 | 'vec4.max': 'vec4', 120 | 'vec4.round': 'vec4', 121 | 'vec4.scale': 'vec4', 122 | 'vec4.scaleAndAdd': 'vec4', 123 | // 'vec4.distance': 'number', 124 | // 'vec4.squaredDistance': 'number', 125 | // 'vec4.length': 'number', 126 | // 'vec4.squaredLength': 'number', 127 | 'vec4.negate': 'vec4', 128 | 'vec4.inverse': 'vec4', 129 | 'vec4.normalize': 'vec4', 130 | // 'vec4.dot': 'number', 131 | 'vec4.cross': 'vec4', 132 | 'vec4.lerp': 'vec4', 133 | 'vec4.random': 'vec4', 134 | 'vec4.transformMat4': 'vec4', 135 | 'vec4.transformQuat': 'vec4', 136 | 'vec4.zero': 'vec4', 137 | // 'vec4.str': 'string', 138 | // 'vec4.exactEquals': 'boolean', 139 | // 'vec4.equals': 'boolean', 140 | // 'vec4.len': 'number', 141 | 'vec4.sub': 'vec4', 142 | 'vec4.mul': 'vec4', 143 | 'vec4.div': 'vec4', 144 | // 'vec4.dist': 'number', 145 | // 'vec4.sqrDist': 'number', 146 | // 'vec4.sqrLen': 'number', 147 | // 'vec4.forEach': 'array', 148 | 149 | 'mat2.create': 'mat2', 150 | 'mat2.clone': 'mat2', 151 | 'mat2.copy': 'mat2', 152 | 'mat2.identity': 'mat2', 153 | 'mat2.fromValues': 'mat2', 154 | 'mat2.set': 'mat2', 155 | 'mat2.transpose': 'mat2', 156 | 'mat2.invert': 'mat2', 157 | 'mat2.adjoint': 'mat2', 158 | // 'mat2.determinant': 'number', 159 | 'mat2.multiply': 'mat2', 160 | 'mat2.rotate': 'mat2', 161 | 'mat2.scale': 'mat2', 162 | 'mat2.fromRotation': 'mat2', 163 | 'mat2.fromScaling': 'mat2', 164 | // 'mat2.str': 'string', 165 | // 'mat2.frob': 'number', 166 | 'mat2.add': 'mat2', 167 | 'mat2.subtract': 'mat2', 168 | // 'mat2.exactEquals': 'boolean', 169 | // 'mat2.equals': 'boolean', 170 | 'mat2.multiplyScalar': 'mat2', 171 | 'mat2.multiplyScalarAndAdd': 'mat2', 172 | 'mat2.mul': 'mat2', 173 | 'mat2.sub': 'mat2', 174 | 175 | 'mat2d.create': 'mat2d', 176 | 'mat2d.clone': 'mat2d', 177 | 'mat2d.copy': 'mat2d', 178 | 'mat2d.identity': 'mat2d', 179 | 'mat2d.fromValues': 'mat2d', 180 | 'mat2d.set': 'mat2d', 181 | 'mat2d.invert': 'mat2d', 182 | // 'mat2d.determinant': 'number', 183 | 'mat2d.multiply': 'mat2d', 184 | 'mat2d.rotate': 'mat2d', 185 | 'mat2d.scale': 'mat2d', 186 | 'mat2d.translate': 'mat2d', 187 | 'mat2d.fromRotation': 'mat2d', 188 | 'mat2d.fromScaling': 'mat2d', 189 | 'mat2d.fromTranslation': 'mat2d', 190 | // 'mat2d.str': 'string', 191 | // 'mat2d.frob': 'number', 192 | 'mat2d.add': 'mat2d', 193 | 'mat2d.subtract': 'mat2d', 194 | 'mat2d.multiplyScalar': 'mat2d', 195 | 'mat2d.multiplyScalarAndAdd': 'mat2d', 196 | // 'mat2d.exactEquals': 'boolean', 197 | // 'mat2d.equals': 'boolean', 198 | 'mat2d.mul': 'mat2d', 199 | 'mat2d.sub': 'mat2d', 200 | 201 | 'mat3.create': 'mat3', 202 | 'mat3.fromMat4': 'mat3', 203 | 'mat3.clone': 'mat3', 204 | 'mat3.copy': 'mat3', 205 | 'mat3.fromValues': 'mat3', 206 | 'mat3.set': 'mat3', 207 | 'mat3.identity': 'mat3', 208 | 'mat3.transpose': 'mat3', 209 | 'mat3.invert': 'mat3', 210 | 'mat3.adjoint': 'mat3', 211 | // 'mat3.determinant': 'number', 212 | 'mat3.multiply': 'mat3', 213 | 'mat3.translate': 'mat3', 214 | 'mat3.rotate': 'mat3', 215 | 'mat3.scale': 'mat3', 216 | 'mat3.fromTranslation': 'mat3', 217 | 'mat3.fromRotation': 'mat3', 218 | 'mat3.fromScaling': 'mat3', 219 | 'mat3.fromMat2d': 'mat3', 220 | 'mat3.fromQuat': 'mat3', 221 | 'mat3.normalFromMat4': 'mat3', 222 | 'mat3.projection': 'mat3', 223 | // 'mat3.str': 'string', 224 | // 'mat3.frob': 'number', 225 | 'mat3.add': 'mat3', 226 | 'mat3.subtract': 'mat3', 227 | 'mat3.multiplyScalar': 'mat3', 228 | 'mat3.multiplyScalarAndAdd': 'mat3', 229 | // 'mat3.exactEquals': 'boolean', 230 | // 'mat3.equals': 'boolean', 231 | 'mat3.mul': 'mat3', 232 | 'mat3.sub': 'mat3', 233 | 234 | 'mat4.create': 'mat4', 235 | 'mat4.clone': 'mat4', 236 | 'mat4.copy': 'mat4', 237 | 'mat4.fromValues': 'mat4', 238 | 'mat4.set': 'mat4', 239 | 'mat4.identity': 'mat4', 240 | 'mat4.transpose': 'mat4', 241 | 'mat4.invert': 'mat4', 242 | 'mat4.adjoint': 'mat4', 243 | // 'mat4.determinant': 'number', 244 | 'mat4.multiply': 'mat4', 245 | 'mat4.translate': 'mat4', 246 | 'mat4.scale': 'mat4', 247 | 'mat4.rotate': 'mat4', 248 | 'mat4.rotateX': 'mat4', 249 | 'mat4.rotateY': 'mat4', 250 | 'mat4.rotateZ': 'mat4', 251 | 'mat4.fromTranslation': 'mat4', 252 | 'mat4.fromScaling': 'mat4', 253 | 'mat4.fromRotation': 'mat4', 254 | 'mat4.fromXRotation': 'mat4', 255 | 'mat4.fromYRotation': 'mat4', 256 | 'mat4.fromZRotation': 'mat4', 257 | 'mat4.fromRotationTranslation': 'mat4', 258 | 'mat4.fromQuat2': 'mat4', 259 | 'mat4.getTranslation': 'vec3', 260 | 'mat4.getScaling': 'vec3', 261 | 'mat4.getRotation': 'quat', 262 | 'mat4.decompose': 'quat', 263 | 'mat4.fromRotationTranslationScale': 'mat4', 264 | 'mat4.fromRotationTranslationScaleOrigin': 'mat4', 265 | 'mat4.fromQuat': 'mat4', 266 | 'mat4.frustum': 'mat4', 267 | 'mat4.perspectiveN0': 'mat4', 268 | 'mat4.perspectiveZ0': 'mat4', 269 | 'mat4.perspectiveFromFieldOfView': 'mat4', 270 | 'mat4.orthoNO': 'mat4', 271 | 'mat4.orthoZO': 'mat4', 272 | 'mat4.lookAt': 'mat4', 273 | 'mat4.targetTo': 'mat4', 274 | // 'mat4.str': 'string', 275 | // 'mat4.frob': 'number', 276 | 'mat4.add': 'mat4', 277 | 'mat4.subtract': 'mat4', 278 | 'mat4.multiplyScalar': 'mat4', 279 | 'mat4.multiplyScalarAndAdd': 'mat4', 280 | // 'mat4.exactEquals': 'boolean', 281 | // 'mat4.equals': 'boolean', 282 | 'mat4.mul': 'mat4', 283 | 'mat4.sub': 'mat4', 284 | 285 | 'quat.create': 'quat', 286 | 'quat.identity': 'quat', 287 | 'quat.setAxisAngle': 'quat', 288 | 'quat.getAxisAngle': 'vec3', 289 | // 'quat.getAngle': 'number', 290 | 'quat.multiply': 'quat', 291 | 'quat.rotateX': 'quat', 292 | 'quat.rotateY': 'quat', 293 | 'quat.rotateZ': 'quat', 294 | 'quat.calculateW': 'quat', 295 | 'quat.exp': 'quat', 296 | 'quat.ln': 'quat', 297 | 'quat.pow': 'quat', 298 | 'quat.slerp': 'quat', 299 | 'quat.random': 'quat', 300 | 'quat.invert': 'quat', 301 | 'quat.conjugate': 'quat', 302 | 'quat.fromMat3': 'quat', 303 | 'quat.fromEuler': 'quat', 304 | // 'quat.str': 'string', 305 | 'quat.clone': 'quat', 306 | 'quat.fromValues': 'quat', 307 | 'quat.copy': 'quat', 308 | 'quat.set': 'quat', 309 | 'quat.add': 'quat', 310 | 'quat.mul': 'quat', 311 | 'quat.scale': 'quat', 312 | 'quat.dot': 'quat', 313 | 'quat.lerp': 'quat', 314 | 'quat.length': 'quat', 315 | 'quat.squaredLength': 'quat', 316 | 'quat.sqrLen': 'quat', 317 | 'quat.normalize': 'quat', 318 | // 'quat.exactEquals': 'boolean', 319 | // 'quat.equals': 'boolean', 320 | 'quat.rotationTo': 'quat', 321 | 'quat.sqlerp': 'quat', 322 | 'quat.setAxes': 'quat', 323 | 324 | 'quat2.create': 'quat2', 325 | 'quat2.clone': 'quat2', 326 | 'quat2.fromValues': 'quat2', 327 | 'quat2.fromRotationTranslationValues': 'quat2', 328 | 'quat2.fromRotationTranslation': 'quat2', 329 | 'quat2.fromTranslation': 'quat2', 330 | 'quat2.fromRotation': 'quat2', 331 | 'quat2.fromMat4': 'quat2', 332 | 'quat2.copy': 'quat2', 333 | 'quat2.identity': 'quat2', 334 | 'quat2.set': 'quat2', 335 | 'quat2.getDual': 'quat', 336 | 'quat2.setReal': 'quat2', 337 | 'quat2.setDual': 'quat2', 338 | 'quat2.getTranslation': 'vec3', 339 | 'quat2.translate': 'quat2', 340 | 'quat2.rotateX': 'quat2', 341 | 'quat2.rotateY': 'quat2', 342 | 'quat2.rotateZ': 'quat2', 343 | 'quat2.rotateByQuatAppend': 'quat2', 344 | 'quat2.rotateByQuatPrepend': 'quat2', 345 | 'quat2.rotateAroundAxis': 'quat2', 346 | 'quat2.add': 'quat2', 347 | 'quat2.multiply': 'quat2', 348 | 'quat2.mul': 'quat2', 349 | 'quat2.scale': 'quat2', 350 | // 'quat2.dot': 'number', 351 | 'quat2.lerp': 'quat2', 352 | 'quat2.invert': 'quat2', 353 | 'quat2.conjugate': 'quat2', 354 | // 'quat2.length': 'number', 355 | // 'quat2.len': 'number', 356 | // 'quat2.squaredLength': 'number', 357 | // 'quat2.sqrLen': 'number', 358 | 'quat2.normalize': 'quat2', 359 | 'quat2.str': 'quat2', 360 | // 'quat2.exactEquals': 'boolean', 361 | // 'quat2.equals': 'boolean', 362 | }; 363 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import retmap from './retmap.js'; 2 | 3 | export function getOperandType(t, path, node = path.node) { 4 | if (t.isIdentifier(node)) { 5 | // 变量标识符,在赋值语句中记录类型 6 | const binding = path.scope.getBinding(node.name); 7 | if (binding) { 8 | return binding._operandType; 9 | } 10 | } else if (t.isCallExpression(node)) { 11 | // 函数调用,记录返回值类型 12 | const { callee } = node; 13 | if (t.isIdentifier(callee)) { 14 | const funcName = callee.name; 15 | const retType = retmap[funcName] || null; 16 | const _binding = path.scope.getBinding(retType); 17 | if (!_binding || _binding.kind === 'module') { 18 | return retType; 19 | } 20 | } 21 | if (t.isMemberExpression(callee)) { 22 | const { object, property } = callee; 23 | if (t.isIdentifier(object) && t.isIdentifier(property)) { 24 | const funcName = `${object.name}.${property.name}`; 25 | const retType = retmap[funcName] || null; 26 | const _binding = path.scope.getBinding(retType); 27 | if (!_binding || _binding.kind === 'module') { 28 | return retType; 29 | } 30 | } 31 | } 32 | } else if (t.isUnaryExpression(node)) { 33 | // 一元运算符,记录操作数类型 34 | const { operator, argument } = node; 35 | if (operator === '-' || operator === '~') { 36 | return getOperandType(t, path, argument); 37 | } 38 | } else if (t.isBinaryExpression(node)) { 39 | // 二元运算符,记录操作数类型 40 | const { left, right } = node; 41 | const leftType = getOperandType(t, path, left); 42 | const rightType = getOperandType(t, path, right); 43 | if (leftType) { 44 | return leftType; 45 | } 46 | return rightType; 47 | } else if (t.isMemberExpression(node)) { 48 | const { object, property } = node; 49 | const type = getOperandType(t, path, object); 50 | if (type === 'vec2' && /^[xy]{2}|[st]{2}$/.test(property.name)) { 51 | return 'vec2'; 52 | } 53 | if (type === 'vec3' && /^[xyz]{2,3}|[rgb]{2,3}|[stp]{2,3}$/.test(property.name)) { 54 | return `vec${property.name.length}`; 55 | } 56 | if (type === 'vec4' && /^[xyzw]{2,4}|[rgba]{2,4}|[stpq]{2,4}$/.test(property.name)) { 57 | return `vec${property.name.length}`; 58 | } 59 | } else if (t.isArrayExpression(node)) { 60 | const { elements } = node; 61 | if (elements.length >= 2 && elements.length <= 4) { 62 | return `vec${elements.length}`; 63 | } 64 | } 65 | return null; 66 | } 67 | 68 | export function makeCreate(t, type) { 69 | return t.callExpression( 70 | t.memberExpression( 71 | t.identifier(type), 72 | t.identifier('create'), 73 | false, 74 | ), 75 | [], 76 | ); 77 | } 78 | 79 | export function spreadArgs(t, path, args) { 80 | const ret = []; 81 | for (let i = 0; i < args.length; i++) { 82 | const arg = args[i]; 83 | if (getOperandType(t, path, arg)) { 84 | ret.push(t.spreadElement(arg)); 85 | } else { 86 | ret.push(arg); 87 | } 88 | } 89 | return ret; 90 | } 91 | 92 | export function fromScalarValues(t, type, node) { 93 | if (type === 'vec2' || type === 'vec3' || type === 'vec4') { 94 | return t.callExpression( 95 | t.memberExpression( 96 | t.identifier(type), 97 | t.identifier('fromValues'), 98 | false, 99 | ), 100 | [node, node, node, node].slice(0, Number(type.slice(-1))), 101 | ); 102 | } 103 | const o = t.numericLiteral(0); 104 | if (type === 'mat2') { 105 | return t.callExpression( 106 | t.memberExpression( 107 | t.identifier(type), 108 | t.identifier('fromValues'), 109 | false, 110 | ), 111 | [node, o, o, node], 112 | ); 113 | } 114 | if (type === 'mat2d') { 115 | return t.callExpression( 116 | t.memberExpression( 117 | t.identifier(type), 118 | t.identifier('fromValues'), 119 | false, 120 | ), 121 | [node, o, o, node, o, o], 122 | ); 123 | } 124 | if (type === 'mat3') { 125 | return t.callExpression( 126 | t.memberExpression( 127 | t.identifier(type), 128 | t.identifier('fromValues'), 129 | false, 130 | ), 131 | [node, o, o, 132 | o, node, o, 133 | o, o, node], 134 | ); 135 | } 136 | if (type === 'mat4') { 137 | return t.callExpression( 138 | t.memberExpression( 139 | t.identifier(type), 140 | t.identifier('fromValues'), 141 | false, 142 | ), 143 | [node, o, o, o, 144 | o, node, o, o, 145 | o, o, node, o, 146 | o, o, o, node], 147 | ); 148 | } 149 | throw new TypeError(`Cannot transform scalar to ${type}`); 150 | } 151 | 152 | export function makeBinaryExpression(t, path, type, left, right, operator) { 153 | let node = null; 154 | if (type) { 155 | const leftType = getOperandType(t, path, left); 156 | const rightType = getOperandType(t, path, right); 157 | if (!leftType && !rightType) return; 158 | if (operator === '+' || operator === '-') { 159 | const op = operator === '+' ? 'add' : 'sub'; 160 | if (leftType === rightType) { 161 | if (t.isCallExpression(left) && left.callee.property.name === 'scale') { 162 | // scale and add 163 | left.callee.property.name = 'scaleAndAdd'; 164 | if (operator === '-') { 165 | left.arguments.splice(1, 0, t.unaryExpression('-', right)); 166 | } else { 167 | left.arguments.splice(1, 0, right); 168 | } 169 | node = left; 170 | } else if (t.isCallExpression(right) && right.callee.property.name === 'scale') { 171 | // scale and add 172 | right.callee.property.name = 'scaleAndAdd'; 173 | if (operator === '-') { 174 | right.arguments.splice(1, 0, t.unaryExpression('-', left)); 175 | } else { 176 | right.arguments.splice(1, 0, left); 177 | } 178 | node = right; 179 | } else { 180 | node = t.callExpression( 181 | t.memberExpression( 182 | t.identifier(type), 183 | t.identifier(op), 184 | false, 185 | ), 186 | [ 187 | left, 188 | right, 189 | ], 190 | ); 191 | } 192 | } else if (!rightType) { 193 | node = t.callExpression( 194 | t.memberExpression( 195 | t.identifier(type), 196 | t.identifier(op), 197 | false, 198 | ), 199 | [ 200 | left, 201 | fromScalarValues(t, type, right), 202 | ], 203 | ); 204 | } else if (!leftType) { 205 | node = t.callExpression( 206 | t.memberExpression( 207 | t.identifier(type), 208 | t.identifier(op), 209 | false, 210 | ), 211 | [ 212 | fromScalarValues(t, type, left), 213 | right, 214 | ], 215 | ); 216 | } else { 217 | throw new TypeError(`type mismatch: ${leftType} ${operator} ${rightType}`); 218 | } 219 | } else if (operator === '*') { 220 | if (leftType === rightType) { 221 | node = t.callExpression( 222 | t.memberExpression( 223 | t.identifier(type), 224 | t.identifier('multiply'), 225 | false, 226 | ), 227 | [ 228 | left, 229 | right, 230 | ], 231 | ); 232 | } else if (!rightType) { 233 | const fun = retmap[`${type}.multiplyScalar`] ? 'multiplyScalar' : 'scale'; 234 | node = t.callExpression( 235 | t.memberExpression( 236 | t.identifier(type), 237 | t.identifier(fun), 238 | false, 239 | ), 240 | [ 241 | left, 242 | right, 243 | ], 244 | ); 245 | } else if (!leftType) { 246 | const fun = retmap[`${type}.multiplyScalar`] ? 'multiplyScalar' : 'scale'; 247 | node = t.callExpression( 248 | t.memberExpression( 249 | t.identifier(type), 250 | t.identifier(fun), 251 | false, 252 | ), 253 | [ 254 | right, 255 | left, 256 | ], 257 | ); 258 | } else if (rightType.startsWith('vec') && leftType.startsWith('mat')) { 259 | // M x V 260 | node = t.callExpression( 261 | t.memberExpression( 262 | t.identifier(rightType), 263 | t.identifier(`transform${leftType.slice(0, 1).toUpperCase()}${leftType.slice(1)}`), 264 | false, 265 | ), 266 | [ 267 | right, 268 | left, 269 | ], 270 | ); 271 | } else if (rightType.startsWith('mat') && leftType.startsWith('vec')) { 272 | // V x M 273 | node = t.callExpression( 274 | t.memberExpression( 275 | t.identifier(leftType), 276 | t.identifier(`transform${rightType.slice(0, 1).toUpperCase()}${rightType.slice(1)}`), 277 | false, 278 | ), 279 | [ 280 | left, 281 | t.callExpression( 282 | t.memberExpression( 283 | t.identifier(rightType), 284 | t.identifier('transpose'), 285 | false, 286 | ), 287 | [ 288 | right, 289 | ], 290 | ), 291 | ], 292 | ); 293 | } else { 294 | throw new TypeError(`type mismatch: ${leftType} ${operator} ${rightType}`); 295 | } 296 | } else if (operator === '/') { 297 | if (!rightType) { 298 | const fun = retmap[`${type}.multiplyScalar`] ? 'multiplyScalar' : 'scale'; 299 | node = t.callExpression( 300 | t.memberExpression( 301 | t.identifier(type), 302 | t.identifier(fun), 303 | false, 304 | ), 305 | [ 306 | left, 307 | t.binaryExpression( 308 | '/', 309 | t.numericLiteral(1), 310 | right, 311 | ), 312 | ], 313 | ); 314 | } else if (leftType === rightType && retmap[`${type}.divide`]) { 315 | node = t.callExpression( 316 | t.memberExpression( 317 | t.identifier(type), 318 | t.identifier('divide'), 319 | false, 320 | ), 321 | [ 322 | left, 323 | right, 324 | ], 325 | ); 326 | } 327 | } else if (operator === '==') { 328 | node = t.callExpression( 329 | t.memberExpression( 330 | t.identifier(type), 331 | t.identifier('equals'), 332 | false, 333 | ), 334 | [ 335 | left, 336 | right, 337 | ], 338 | ); 339 | } else if (operator === '!=') { 340 | node = t.unaryExpression( 341 | '!', 342 | t.callExpression( 343 | t.memberExpression( 344 | t.identifier(type), 345 | t.identifier('equals'), 346 | false, 347 | ), 348 | [ 349 | left, 350 | right, 351 | ], 352 | ), 353 | ); 354 | } else { 355 | throw new TypeError(`type mismatch: ${leftType} ${operator} ${rightType}`); 356 | } 357 | } 358 | // eslint-disable-next-line consistent-return 359 | return node; 360 | } 361 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | getOperandType, makeCreate, fromScalarValues, spreadArgs, 3 | makeBinaryExpression, 4 | } from './utils.js'; 5 | import retmap from './retmap.js'; 6 | 7 | // eslint-disable-next-line func-names 8 | export default function ({ types: t }) { 9 | return { 10 | visitor: { 11 | VariableDeclarator: { 12 | exit(path) { 13 | const { id, init } = path.node; 14 | if (t.isIdentifier(id)) { 15 | const binding = path.scope.getBinding(id.name); 16 | if (binding) { 17 | binding._operandType = getOperandType(t, path, init); 18 | } 19 | } 20 | }, 21 | }, 22 | AssignmentExpression: { 23 | // TODO: += -= *= /= etc. 24 | exit(path) { 25 | const { left, right, operator } = path.node; 26 | if (t.isIdentifier(left)) { 27 | const binding = path.scope.getBinding(left.name); 28 | if (binding) { 29 | const type = getOperandType(t, path, left) || getOperandType(t, path, right); 30 | if (type) { 31 | binding._operandType = type; 32 | if (binding.kind !== 'const') { 33 | if (operator === '+=' || operator === '-=' || operator === '*=' || operator === '/=') { 34 | const node = makeBinaryExpression( 35 | t, 36 | path, 37 | type, 38 | left, 39 | right, 40 | operator.slice(0, 1), 41 | ); 42 | node._makeCreated = true; 43 | node.arguments.unshift(left); 44 | path.replaceWith(node); 45 | } 46 | if (operator === '=') { 47 | if (t.isCallExpression(right) && t.isMemberExpression(right.callee)) { 48 | const { object, property } = right.callee; 49 | if (t.isIdentifier(object) && t.isIdentifier(property)) { 50 | if (property.name !== 'create' && property.name !== 'fromValues') { 51 | right.arguments[0] = left; 52 | path.replaceWith(right); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | }, 62 | }, 63 | CallExpression: { 64 | exit(path) { 65 | const type = getOperandType(t, path); 66 | if (type) { 67 | if (t.isIdentifier(path.node.callee)) { 68 | const args = path.node.arguments; 69 | const _type = getOperandType(t, path, args[0]); 70 | if (args.length === 1 && type === _type) { 71 | path.replaceWith(args[0]); 72 | } else if (args.length === 1 && !_type && !t.isSpreadElement(args[0])) { 73 | // 用一个参数来构造向量和矩阵 74 | const node = fromScalarValues(t, type, args[0]); 75 | path.replaceWith(node); 76 | } else { 77 | const hasType = args.some((arg) => getOperandType(t, path, arg)); 78 | let node; 79 | if (hasType) { 80 | // 用多个参数来构造向量和矩阵 81 | node = t.callExpression( 82 | t.memberExpression( 83 | t.identifier(type), 84 | t.identifier('fromValues'), 85 | false, 86 | ), 87 | spreadArgs(t, path, args), 88 | ); 89 | path.replaceWith(node); 90 | } else { 91 | node = t.callExpression( 92 | t.memberExpression( 93 | t.identifier(type), 94 | t.identifier('fromValues'), 95 | false, 96 | ), 97 | args, 98 | ); 99 | } 100 | path.replaceWith(node); 101 | } 102 | } 103 | if (t.isMemberExpression(path.node.callee) && !path.node._makeCreated) { 104 | const { object, property } = path.node.callee; 105 | if (t.isIdentifier(object) && t.isIdentifier(property)) { 106 | // const funcName = `${object.name}.${property.name}`; 107 | if (property.name !== 'create' && property.name !== 'fromValues') { 108 | path.node.arguments.unshift( 109 | makeCreate(t, type), 110 | ); 111 | path.node._makeCreated = true; 112 | } 113 | } 114 | } 115 | } 116 | }, 117 | }, 118 | UnaryExpression: { 119 | exit(path) { 120 | const type = getOperandType(t, path); 121 | if (type) { 122 | const { operator, argument } = path.node; 123 | if (operator === '-') { 124 | if (retmap[`${type}.negate`]) { 125 | const node = t.callExpression( 126 | t.memberExpression( 127 | t.identifier(type), 128 | t.identifier('negate'), 129 | false, 130 | ), 131 | [ 132 | argument], 133 | ); 134 | path.replaceWith(node); 135 | } else { 136 | const node = t.callExpression( 137 | t.memberExpression( 138 | t.identifier(type), 139 | t.identifier('multiplyScalar'), 140 | false, 141 | ), 142 | [ 143 | argument, 144 | t.numericLiteral(-1), 145 | ], 146 | ); 147 | path.replaceWith(node); 148 | } 149 | } 150 | if (operator === '~' && retmap[`${type}.transpose`]) { 151 | const node = t.callExpression( 152 | t.memberExpression( 153 | t.identifier(type), 154 | t.identifier('transpose'), 155 | false, 156 | ), 157 | [ 158 | argument], 159 | ); 160 | path.replaceWith(node); 161 | } 162 | } 163 | }, 164 | }, 165 | BinaryExpression: { 166 | exit(path) { 167 | const type = getOperandType(t, path); 168 | if (type) { 169 | const { left, right, operator } = path.node; 170 | const node = makeBinaryExpression(t, path, type, left, right, operator); 171 | if (node) path.replaceWith(node); 172 | } 173 | }, 174 | }, 175 | MemberExpression: { 176 | exit(path) { 177 | const { object, property } = path.node; 178 | const type = getOperandType(t, path, object); 179 | if (type) { 180 | if (t.isIdentifier(property)) { 181 | if (type.startsWith('vec') && (property.name === 'x' || (type !== 'vec2' && property.name === 'r') || property.name === 's')) { 182 | path.replaceWith(t.memberExpression(path.node.object, t.identifier('0'), true)); 183 | } else if (type.startsWith('vec') && (property.name === 'y' || (type !== 'vec2' && property.name === 'g') || property.name === 't')) { 184 | path.replaceWith(t.memberExpression(path.node.object, t.identifier('1'), true)); 185 | } else if ((type === 'vec3' || type === 'vec4') && (property.name === 'z' || property.name === 'b' || property.name === 'p')) { 186 | path.replaceWith(t.memberExpression(path.node.object, t.identifier('2'), true)); 187 | } else if (type === 'vec4' && (property.name === 'w' || property.name === 'a' || property.name === 'q')) { 188 | path.replaceWith(t.memberExpression(path.node.object, t.identifier('3'), true)); 189 | } else if (type === 'vec2') { 190 | if (/[xy]{2}/.test(property.name)) { 191 | if (property.name === 'xy') { 192 | path.replaceWith(object); 193 | } else { 194 | path.replaceWith( 195 | t.callExpression( 196 | t.memberExpression( 197 | t.identifier(type), 198 | t.identifier('fromValues'), 199 | false, 200 | ), 201 | [ 202 | t.memberExpression(path.node.object, t.identifier('xy'.indexOf(property.name[0]).toString()), true), 203 | t.memberExpression(path.node.object, t.identifier('xy'.indexOf(property.name[1]).toString()), true), 204 | ], 205 | ), 206 | ); 207 | } 208 | } else if (/[st]{2}/.test(property.name)) { 209 | if (property.name === 'st') { 210 | path.replaceWith(object); 211 | } else { 212 | path.replaceWith( 213 | t.callExpression( 214 | t.memberExpression( 215 | t.identifier(type), 216 | t.identifier('fromValues'), 217 | false, 218 | ), 219 | [ 220 | t.memberExpression(path.node.object, t.identifier('st'.indexOf(property.name[0]).toString()), true), 221 | t.memberExpression(path.node.object, t.identifier('st'.indexOf(property.name[1]).toString()), true), 222 | ], 223 | ), 224 | ); 225 | } 226 | } 227 | } else if (type === 'vec3') { 228 | if (/[xyz]{2,3}/.test(property.name)) { 229 | if (property.name === 'xyz') { 230 | path.replaceWith(object); 231 | } else { 232 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('xyz'.indexOf(c).toString()), true)); 233 | path.replaceWith( 234 | t.callExpression( 235 | t.memberExpression( 236 | t.identifier(`vec${property.name.length}`), 237 | t.identifier('fromValues'), 238 | false, 239 | ), 240 | args, 241 | ), 242 | ); 243 | } 244 | } else if (/[rgb]{2,3}/.test(property.name)) { 245 | if (property.name === 'rgb') { 246 | path.replaceWith(object); 247 | } else { 248 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('rgb'.indexOf(c).toString()), true)); 249 | path.replaceWith( 250 | t.callExpression( 251 | t.memberExpression( 252 | t.identifier(`vec${property.name.length}`), 253 | t.identifier('fromValues'), 254 | false, 255 | ), 256 | args, 257 | ), 258 | ); 259 | } 260 | } else if (/[stq]{2,3}/.test(property.name)) { 261 | if (property.name === 'stq') { 262 | path.replaceWith(object); 263 | } else { 264 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('stq'.indexOf(c).toString()), true)); 265 | path.replaceWith( 266 | t.callExpression( 267 | t.memberExpression( 268 | t.identifier(`vec${property.name.length}`), 269 | t.identifier('fromValues'), 270 | false, 271 | ), 272 | args, 273 | ), 274 | ); 275 | } 276 | } 277 | } else if (type === 'vec4') { 278 | if (/[xyzw]{2,4}/.test(property.name)) { 279 | if (property.name === 'xyzw') { 280 | path.replaceWith(object); 281 | } else { 282 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('xyzw'.indexOf(c).toString()), true)); 283 | path.replaceWith( 284 | t.callExpression( 285 | t.memberExpression( 286 | t.identifier(`vec${property.name.length}`), 287 | t.identifier('fromValues'), 288 | false, 289 | ), 290 | args, 291 | ), 292 | ); 293 | } 294 | } else if (/[rgba]{2,4}/.test(property.name)) { 295 | if (property.name === 'rgba') { 296 | path.replaceWith(object); 297 | } else { 298 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('rgba'.indexOf(c).toString()), true)); 299 | path.replaceWith( 300 | t.callExpression( 301 | t.memberExpression( 302 | t.identifier(`vec${property.name.length}`), 303 | t.identifier('fromValues'), 304 | false, 305 | ), 306 | args, 307 | ), 308 | ); 309 | } 310 | } else if (/[stpq]{2,4}/.test(property.name)) { 311 | if (property.name === 'stpq') { 312 | path.replaceWith(object); 313 | } else { 314 | const args = [...property.name].map((c) => t.memberExpression(path.node.object, t.identifier('stpq'.indexOf(c).toString()), true)); 315 | path.replaceWith( 316 | t.callExpression( 317 | t.memberExpression( 318 | t.identifier(`vec${property.name.length}`), 319 | t.identifier('fromValues'), 320 | false, 321 | ), 322 | args, 323 | ), 324 | ); 325 | } 326 | } 327 | } 328 | } 329 | } 330 | }, 331 | }, 332 | }, 333 | }; 334 | } 335 | --------------------------------------------------------------------------------