├── .eslint.json ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── elm.json ├── release.sh ├── src ├── Elm │ └── Kernel │ │ └── MJS.js └── Math │ ├── Matrix4.elm │ ├── Vector2.elm │ ├── Vector3.elm │ └── Vector4.elm └── tests └── Tests.elm /.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "no-unused-vars": ["error", { "varsIgnorePattern": "^_MJS_(?!.*Local$)" }], 8 | "indent": [ "error", 4, { "SwitchCase": 1 } ], 9 | "space-infix-ops": [ "error" ], 10 | "linebreak-style": [ "error", "unix" ], 11 | "comma-style": [ "error", "last" ], 12 | "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], 13 | "curly": "error", 14 | "quotes": [ "error", "single" ], 15 | "semi": [ "error", "always" ], 16 | "space-before-function-paren": [ "error", "never" ], 17 | "keyword-spacing": [ "error", { "before": true, "after": true } ], 18 | "space-before-blocks": [ "error", "always" ], 19 | "key-spacing": [ "error", { "beforeColon": false, "afterColon": true, "mode": "strict" } ], 20 | "semi-spacing": [ "error", {"before": false, "after": true} ], 21 | "comma-spacing": [ "error", { "before": false, "after": true } ], 22 | "comma-dangle": ["error", "never"], 23 | "eqeqeq": "error" 24 | }, 25 | "globals" : { 26 | "__Maybe_Just": false, 27 | "__Maybe_Nothing": false, 28 | "F2": false, 29 | "F3": false, 30 | "F4": false, 31 | "F5": false, 32 | "F6": false, 33 | "Float64Array": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | install: 5 | - npm install -g elm 6 | - npm install -g elm-test 7 | - elm-package install -y 8 | - pushd tests && elm-package install -y && popd 9 | script: 10 | - elm-test 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to elm-explorations/linear-algebra 2 | 3 | This repository is kept for maintenance and does not accept most feature requests. In particular, 4 | [this](https://github.com/elm/package.elm-lang.org/issues/149#issuecomment-171068020) requires that all 5 | changes be PATCH-level, meaning that the API cannot be modified. 6 | 7 | Any contribution should: 8 | * Compile using `elm make` 9 | * Be recognized as a PATCH change using `elm package diff` 10 | * Not introduce mutable variables 11 | * Justify why the change is needed and why it won't break anything already here 12 | 13 | Documentation improvements are welcome. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, John P Mayer, Jr 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Linear Algebra for Elm 2 | 3 | A library for fast vector and matrix math. See the full docs [here][docs]. 4 | 5 | [docs]: https://package.elm-lang.org/packages/elm-explorations/linear-algebra/latest/ 6 | 7 | This is needed for 3D rendering with [WebGL in Elm][webgl], but is useful for 8 | anything where fast linear algebra is needed. 9 | 10 | [webgl]: https://github.com/elm-explorations/webgl 11 | 12 | It is based on [the MJS library](https://code.google.com/p/webgl-mjs/) for JavaScript. 13 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "elm-explorations/linear-algebra", 4 | "summary": "A linear algebra library for fast vector and matrix math", 5 | "license": "BSD-3-Clause", 6 | "version": "1.0.3", 7 | "exposed-modules": [ 8 | "Math.Vector2", 9 | "Math.Vector3", 10 | "Math.Vector4", 11 | "Math.Matrix4" 12 | ], 13 | "elm-version": "0.19.0 <= v < 0.20.0", 14 | "dependencies": { 15 | "elm/core": "1.0.0 <= v < 2.0.0" 16 | }, 17 | "test-dependencies": { 18 | "elm-explorations/test": "1.0.0 <= v < 2.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | rm -rf release || exit 0; 5 | 6 | elm bump 7 | 8 | version=$(grep -m1 version elm.json | awk -F: '{ print $2 }' | sed 's/[", ]//g') 9 | 10 | git commit -a -m "Bump to $version" 11 | git push 12 | 13 | cleanup="tests CONTRIBUTING.md .eslintrc.json release.sh" 14 | last_commit=$(git rev-parse HEAD) 15 | 16 | git clone --reference . git@github.com:elm-explorations/linear-algebra.git release 17 | ( 18 | cd release 19 | git checkout $last_commit 20 | git rm -rf --ignore-unmatch $cleanup 21 | git commit -m "Cleanup and release $version" 22 | git tag -a $version -m "Release $version" 23 | git push origin $version 24 | elm publish 25 | ) 26 | -------------------------------------------------------------------------------- /src/Elm/Kernel/MJS.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | import Maybe exposing (Just, Nothing) 4 | 5 | */ 6 | 7 | /* 8 | * Copyright (c) 2010 Mozilla Corporation 9 | * Copyright (c) 2010 Vladimir Vukicevic 10 | * Copyright (c) 2013 John Mayer 11 | * Copyright (c) 2018 Andrey Kuzmin 12 | * 13 | * Permission is hereby granted, free of charge, to any person 14 | * obtaining a copy of this software and associated documentation 15 | * files (the "Software"), to deal in the Software without 16 | * restriction, including without limitation the rights to use, 17 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the 19 | * Software is furnished to do so, subject to the following 20 | * conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be 23 | * included in all copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | * OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | 35 | // Vector2 36 | 37 | var _MJS_v2 = F2(function(x, y) { 38 | return new Float64Array([x, y]); 39 | }); 40 | 41 | var _MJS_v2getX = function(a) { 42 | return a[0]; 43 | }; 44 | 45 | var _MJS_v2getY = function(a) { 46 | return a[1]; 47 | }; 48 | 49 | var _MJS_v2setX = F2(function(x, a) { 50 | return new Float64Array([x, a[1]]); 51 | }); 52 | 53 | var _MJS_v2setY = F2(function(y, a) { 54 | return new Float64Array([a[0], y]); 55 | }); 56 | 57 | var _MJS_v2toRecord = function(a) { 58 | return { __$x: a[0], __$y: a[1] }; 59 | }; 60 | 61 | var _MJS_v2fromRecord = function(r) { 62 | return new Float64Array([r.__$x, r.__$y]); 63 | }; 64 | 65 | var _MJS_v2add = F2(function(a, b) { 66 | var r = new Float64Array(2); 67 | r[0] = a[0] + b[0]; 68 | r[1] = a[1] + b[1]; 69 | return r; 70 | }); 71 | 72 | var _MJS_v2sub = F2(function(a, b) { 73 | var r = new Float64Array(2); 74 | r[0] = a[0] - b[0]; 75 | r[1] = a[1] - b[1]; 76 | return r; 77 | }); 78 | 79 | var _MJS_v2negate = function(a) { 80 | var r = new Float64Array(2); 81 | r[0] = -a[0]; 82 | r[1] = -a[1]; 83 | return r; 84 | }; 85 | 86 | var _MJS_v2direction = F2(function(a, b) { 87 | var r = new Float64Array(2); 88 | r[0] = a[0] - b[0]; 89 | r[1] = a[1] - b[1]; 90 | var im = 1.0 / _MJS_v2lengthLocal(r); 91 | r[0] = r[0] * im; 92 | r[1] = r[1] * im; 93 | return r; 94 | }); 95 | 96 | function _MJS_v2lengthLocal(a) { 97 | return Math.sqrt(a[0] * a[0] + a[1] * a[1]); 98 | } 99 | var _MJS_v2length = _MJS_v2lengthLocal; 100 | 101 | var _MJS_v2lengthSquared = function(a) { 102 | return a[0] * a[0] + a[1] * a[1]; 103 | }; 104 | 105 | var _MJS_v2distance = F2(function(a, b) { 106 | var dx = a[0] - b[0]; 107 | var dy = a[1] - b[1]; 108 | return Math.sqrt(dx * dx + dy * dy); 109 | }); 110 | 111 | var _MJS_v2distanceSquared = F2(function(a, b) { 112 | var dx = a[0] - b[0]; 113 | var dy = a[1] - b[1]; 114 | return dx * dx + dy * dy; 115 | }); 116 | 117 | var _MJS_v2normalize = function(a) { 118 | var r = new Float64Array(2); 119 | var im = 1.0 / _MJS_v2lengthLocal(a); 120 | r[0] = a[0] * im; 121 | r[1] = a[1] * im; 122 | return r; 123 | }; 124 | 125 | var _MJS_v2scale = F2(function(k, a) { 126 | var r = new Float64Array(2); 127 | r[0] = a[0] * k; 128 | r[1] = a[1] * k; 129 | return r; 130 | }); 131 | 132 | var _MJS_v2dot = F2(function(a, b) { 133 | return a[0] * b[0] + a[1] * b[1]; 134 | }); 135 | 136 | // Vector3 137 | 138 | var _MJS_v3temp1Local = new Float64Array(3); 139 | var _MJS_v3temp2Local = new Float64Array(3); 140 | var _MJS_v3temp3Local = new Float64Array(3); 141 | 142 | var _MJS_v3 = F3(function(x, y, z) { 143 | return new Float64Array([x, y, z]); 144 | }); 145 | 146 | var _MJS_v3getX = function(a) { 147 | return a[0]; 148 | }; 149 | 150 | var _MJS_v3getY = function(a) { 151 | return a[1]; 152 | }; 153 | 154 | var _MJS_v3getZ = function(a) { 155 | return a[2]; 156 | }; 157 | 158 | var _MJS_v3setX = F2(function(x, a) { 159 | return new Float64Array([x, a[1], a[2]]); 160 | }); 161 | 162 | var _MJS_v3setY = F2(function(y, a) { 163 | return new Float64Array([a[0], y, a[2]]); 164 | }); 165 | 166 | var _MJS_v3setZ = F2(function(z, a) { 167 | return new Float64Array([a[0], a[1], z]); 168 | }); 169 | 170 | var _MJS_v3toRecord = function(a) { 171 | return { __$x: a[0], __$y: a[1], __$z: a[2] }; 172 | }; 173 | 174 | var _MJS_v3fromRecord = function(r) { 175 | return new Float64Array([r.__$x, r.__$y, r.__$z]); 176 | }; 177 | 178 | var _MJS_v3add = F2(function(a, b) { 179 | var r = new Float64Array(3); 180 | r[0] = a[0] + b[0]; 181 | r[1] = a[1] + b[1]; 182 | r[2] = a[2] + b[2]; 183 | return r; 184 | }); 185 | 186 | function _MJS_v3subLocal(a, b, r) { 187 | if (r === undefined) { 188 | r = new Float64Array(3); 189 | } 190 | r[0] = a[0] - b[0]; 191 | r[1] = a[1] - b[1]; 192 | r[2] = a[2] - b[2]; 193 | return r; 194 | } 195 | var _MJS_v3sub = F2(_MJS_v3subLocal); 196 | 197 | var _MJS_v3negate = function(a) { 198 | var r = new Float64Array(3); 199 | r[0] = -a[0]; 200 | r[1] = -a[1]; 201 | r[2] = -a[2]; 202 | return r; 203 | }; 204 | 205 | function _MJS_v3directionLocal(a, b, r) { 206 | if (r === undefined) { 207 | r = new Float64Array(3); 208 | } 209 | return _MJS_v3normalizeLocal(_MJS_v3subLocal(a, b, r), r); 210 | } 211 | var _MJS_v3direction = F2(_MJS_v3directionLocal); 212 | 213 | function _MJS_v3lengthLocal(a) { 214 | return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); 215 | } 216 | var _MJS_v3length = _MJS_v3lengthLocal; 217 | 218 | var _MJS_v3lengthSquared = function(a) { 219 | return a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; 220 | }; 221 | 222 | var _MJS_v3distance = F2(function(a, b) { 223 | var dx = a[0] - b[0]; 224 | var dy = a[1] - b[1]; 225 | var dz = a[2] - b[2]; 226 | return Math.sqrt(dx * dx + dy * dy + dz * dz); 227 | }); 228 | 229 | var _MJS_v3distanceSquared = F2(function(a, b) { 230 | var dx = a[0] - b[0]; 231 | var dy = a[1] - b[1]; 232 | var dz = a[2] - b[2]; 233 | return dx * dx + dy * dy + dz * dz; 234 | }); 235 | 236 | function _MJS_v3normalizeLocal(a, r) { 237 | if (r === undefined) { 238 | r = new Float64Array(3); 239 | } 240 | var im = 1.0 / _MJS_v3lengthLocal(a); 241 | r[0] = a[0] * im; 242 | r[1] = a[1] * im; 243 | r[2] = a[2] * im; 244 | return r; 245 | } 246 | var _MJS_v3normalize = _MJS_v3normalizeLocal; 247 | 248 | var _MJS_v3scale = F2(function(k, a) { 249 | return new Float64Array([a[0] * k, a[1] * k, a[2] * k]); 250 | }); 251 | 252 | var _MJS_v3dotLocal = function(a, b) { 253 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 254 | }; 255 | var _MJS_v3dot = F2(_MJS_v3dotLocal); 256 | 257 | function _MJS_v3crossLocal(a, b, r) { 258 | if (r === undefined) { 259 | r = new Float64Array(3); 260 | } 261 | r[0] = a[1] * b[2] - a[2] * b[1]; 262 | r[1] = a[2] * b[0] - a[0] * b[2]; 263 | r[2] = a[0] * b[1] - a[1] * b[0]; 264 | return r; 265 | } 266 | var _MJS_v3cross = F2(_MJS_v3crossLocal); 267 | 268 | var _MJS_v3mul4x4 = F2(function(m, v) { 269 | var w; 270 | var tmp = _MJS_v3temp1Local; 271 | var r = new Float64Array(3); 272 | 273 | tmp[0] = m[3]; 274 | tmp[1] = m[7]; 275 | tmp[2] = m[11]; 276 | w = _MJS_v3dotLocal(v, tmp) + m[15]; 277 | tmp[0] = m[0]; 278 | tmp[1] = m[4]; 279 | tmp[2] = m[8]; 280 | r[0] = (_MJS_v3dotLocal(v, tmp) + m[12]) / w; 281 | tmp[0] = m[1]; 282 | tmp[1] = m[5]; 283 | tmp[2] = m[9]; 284 | r[1] = (_MJS_v3dotLocal(v, tmp) + m[13]) / w; 285 | tmp[0] = m[2]; 286 | tmp[1] = m[6]; 287 | tmp[2] = m[10]; 288 | r[2] = (_MJS_v3dotLocal(v, tmp) + m[14]) / w; 289 | return r; 290 | }); 291 | 292 | // Vector4 293 | 294 | var _MJS_v4 = F4(function(x, y, z, w) { 295 | return new Float64Array([x, y, z, w]); 296 | }); 297 | 298 | var _MJS_v4getX = function(a) { 299 | return a[0]; 300 | }; 301 | 302 | var _MJS_v4getY = function(a) { 303 | return a[1]; 304 | }; 305 | 306 | var _MJS_v4getZ = function(a) { 307 | return a[2]; 308 | }; 309 | 310 | var _MJS_v4getW = function(a) { 311 | return a[3]; 312 | }; 313 | 314 | var _MJS_v4setX = F2(function(x, a) { 315 | return new Float64Array([x, a[1], a[2], a[3]]); 316 | }); 317 | 318 | var _MJS_v4setY = F2(function(y, a) { 319 | return new Float64Array([a[0], y, a[2], a[3]]); 320 | }); 321 | 322 | var _MJS_v4setZ = F2(function(z, a) { 323 | return new Float64Array([a[0], a[1], z, a[3]]); 324 | }); 325 | 326 | var _MJS_v4setW = F2(function(w, a) { 327 | return new Float64Array([a[0], a[1], a[2], w]); 328 | }); 329 | 330 | var _MJS_v4toRecord = function(a) { 331 | return { __$x: a[0], __$y: a[1], __$z: a[2], __$w: a[3] }; 332 | }; 333 | 334 | var _MJS_v4fromRecord = function(r) { 335 | return new Float64Array([r.__$x, r.__$y, r.__$z, r.__$w]); 336 | }; 337 | 338 | var _MJS_v4add = F2(function(a, b) { 339 | var r = new Float64Array(4); 340 | r[0] = a[0] + b[0]; 341 | r[1] = a[1] + b[1]; 342 | r[2] = a[2] + b[2]; 343 | r[3] = a[3] + b[3]; 344 | return r; 345 | }); 346 | 347 | var _MJS_v4sub = F2(function(a, b) { 348 | var r = new Float64Array(4); 349 | r[0] = a[0] - b[0]; 350 | r[1] = a[1] - b[1]; 351 | r[2] = a[2] - b[2]; 352 | r[3] = a[3] - b[3]; 353 | return r; 354 | }); 355 | 356 | var _MJS_v4negate = function(a) { 357 | var r = new Float64Array(4); 358 | r[0] = -a[0]; 359 | r[1] = -a[1]; 360 | r[2] = -a[2]; 361 | r[3] = -a[3]; 362 | return r; 363 | }; 364 | 365 | var _MJS_v4direction = F2(function(a, b) { 366 | var r = new Float64Array(4); 367 | r[0] = a[0] - b[0]; 368 | r[1] = a[1] - b[1]; 369 | r[2] = a[2] - b[2]; 370 | r[3] = a[3] - b[3]; 371 | var im = 1.0 / _MJS_v4lengthLocal(r); 372 | r[0] = r[0] * im; 373 | r[1] = r[1] * im; 374 | r[2] = r[2] * im; 375 | r[3] = r[3] * im; 376 | return r; 377 | }); 378 | 379 | function _MJS_v4lengthLocal(a) { 380 | return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]); 381 | } 382 | var _MJS_v4length = _MJS_v4lengthLocal; 383 | 384 | var _MJS_v4lengthSquared = function(a) { 385 | return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]; 386 | }; 387 | 388 | var _MJS_v4distance = F2(function(a, b) { 389 | var dx = a[0] - b[0]; 390 | var dy = a[1] - b[1]; 391 | var dz = a[2] - b[2]; 392 | var dw = a[3] - b[3]; 393 | return Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw); 394 | }); 395 | 396 | var _MJS_v4distanceSquared = F2(function(a, b) { 397 | var dx = a[0] - b[0]; 398 | var dy = a[1] - b[1]; 399 | var dz = a[2] - b[2]; 400 | var dw = a[3] - b[3]; 401 | return dx * dx + dy * dy + dz * dz + dw * dw; 402 | }); 403 | 404 | var _MJS_v4normalize = function(a) { 405 | var r = new Float64Array(4); 406 | var im = 1.0 / _MJS_v4lengthLocal(a); 407 | r[0] = a[0] * im; 408 | r[1] = a[1] * im; 409 | r[2] = a[2] * im; 410 | r[3] = a[3] * im; 411 | return r; 412 | }; 413 | 414 | var _MJS_v4scale = F2(function(k, a) { 415 | var r = new Float64Array(4); 416 | r[0] = a[0] * k; 417 | r[1] = a[1] * k; 418 | r[2] = a[2] * k; 419 | r[3] = a[3] * k; 420 | return r; 421 | }); 422 | 423 | var _MJS_v4dot = F2(function(a, b) { 424 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; 425 | }); 426 | 427 | // Matrix4 428 | 429 | var _MJS_m4x4temp1Local = new Float64Array(16); 430 | var _MJS_m4x4temp2Local = new Float64Array(16); 431 | 432 | var _MJS_m4x4identity = new Float64Array([ 433 | 1.0, 0.0, 0.0, 0.0, 434 | 0.0, 1.0, 0.0, 0.0, 435 | 0.0, 0.0, 1.0, 0.0, 436 | 0.0, 0.0, 0.0, 1.0 437 | ]); 438 | 439 | var _MJS_m4x4fromRecord = function(r) { 440 | var m = new Float64Array(16); 441 | m[0] = r.__$m11; 442 | m[1] = r.__$m21; 443 | m[2] = r.__$m31; 444 | m[3] = r.__$m41; 445 | m[4] = r.__$m12; 446 | m[5] = r.__$m22; 447 | m[6] = r.__$m32; 448 | m[7] = r.__$m42; 449 | m[8] = r.__$m13; 450 | m[9] = r.__$m23; 451 | m[10] = r.__$m33; 452 | m[11] = r.__$m43; 453 | m[12] = r.__$m14; 454 | m[13] = r.__$m24; 455 | m[14] = r.__$m34; 456 | m[15] = r.__$m44; 457 | return m; 458 | }; 459 | 460 | var _MJS_m4x4toRecord = function(m) { 461 | return { 462 | __$m11: m[0], __$m21: m[1], __$m31: m[2], __$m41: m[3], 463 | __$m12: m[4], __$m22: m[5], __$m32: m[6], __$m42: m[7], 464 | __$m13: m[8], __$m23: m[9], __$m33: m[10], __$m43: m[11], 465 | __$m14: m[12], __$m24: m[13], __$m34: m[14], __$m44: m[15] 466 | }; 467 | }; 468 | 469 | var _MJS_m4x4inverse = function(m) { 470 | var r = new Float64Array(16); 471 | 472 | r[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + 473 | m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; 474 | r[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - 475 | m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; 476 | r[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + 477 | m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; 478 | r[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - 479 | m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; 480 | r[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - 481 | m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; 482 | r[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + 483 | m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; 484 | r[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - 485 | m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; 486 | r[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + 487 | m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; 488 | r[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + 489 | m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; 490 | r[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - 491 | m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; 492 | r[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + 493 | m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; 494 | r[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - 495 | m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; 496 | r[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - 497 | m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; 498 | r[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + 499 | m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; 500 | r[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - 501 | m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; 502 | r[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + 503 | m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; 504 | 505 | var det = m[0] * r[0] + m[1] * r[4] + m[2] * r[8] + m[3] * r[12]; 506 | 507 | if (det === 0) { 508 | return __Maybe_Nothing; 509 | } 510 | 511 | det = 1.0 / det; 512 | 513 | for (var i = 0; i < 16; i = i + 1) { 514 | r[i] = r[i] * det; 515 | } 516 | 517 | return __Maybe_Just(r); 518 | }; 519 | 520 | var _MJS_m4x4inverseOrthonormal = function(m) { 521 | var r = _MJS_m4x4transposeLocal(m); 522 | var t = [m[12], m[13], m[14]]; 523 | r[3] = r[7] = r[11] = 0; 524 | r[12] = -_MJS_v3dotLocal([r[0], r[4], r[8]], t); 525 | r[13] = -_MJS_v3dotLocal([r[1], r[5], r[9]], t); 526 | r[14] = -_MJS_v3dotLocal([r[2], r[6], r[10]], t); 527 | return r; 528 | }; 529 | 530 | function _MJS_m4x4makeFrustumLocal(left, right, bottom, top, znear, zfar) { 531 | var r = new Float64Array(16); 532 | 533 | r[0] = 2 * znear / (right - left); 534 | r[1] = 0; 535 | r[2] = 0; 536 | r[3] = 0; 537 | r[4] = 0; 538 | r[5] = 2 * znear / (top - bottom); 539 | r[6] = 0; 540 | r[7] = 0; 541 | r[8] = (right + left) / (right - left); 542 | r[9] = (top + bottom) / (top - bottom); 543 | r[10] = -(zfar + znear) / (zfar - znear); 544 | r[11] = -1; 545 | r[12] = 0; 546 | r[13] = 0; 547 | r[14] = -2 * zfar * znear / (zfar - znear); 548 | r[15] = 0; 549 | 550 | return r; 551 | } 552 | var _MJS_m4x4makeFrustum = F6(_MJS_m4x4makeFrustumLocal); 553 | 554 | var _MJS_m4x4makePerspective = F4(function(fovy, aspect, znear, zfar) { 555 | var ymax = znear * Math.tan(fovy * Math.PI / 360.0); 556 | var ymin = -ymax; 557 | var xmin = ymin * aspect; 558 | var xmax = ymax * aspect; 559 | 560 | return _MJS_m4x4makeFrustumLocal(xmin, xmax, ymin, ymax, znear, zfar); 561 | }); 562 | 563 | function _MJS_m4x4makeOrthoLocal(left, right, bottom, top, znear, zfar) { 564 | var r = new Float64Array(16); 565 | 566 | r[0] = 2 / (right - left); 567 | r[1] = 0; 568 | r[2] = 0; 569 | r[3] = 0; 570 | r[4] = 0; 571 | r[5] = 2 / (top - bottom); 572 | r[6] = 0; 573 | r[7] = 0; 574 | r[8] = 0; 575 | r[9] = 0; 576 | r[10] = -2 / (zfar - znear); 577 | r[11] = 0; 578 | r[12] = -(right + left) / (right - left); 579 | r[13] = -(top + bottom) / (top - bottom); 580 | r[14] = -(zfar + znear) / (zfar - znear); 581 | r[15] = 1; 582 | 583 | return r; 584 | } 585 | var _MJS_m4x4makeOrtho = F6(_MJS_m4x4makeOrthoLocal); 586 | 587 | var _MJS_m4x4makeOrtho2D = F4(function(left, right, bottom, top) { 588 | return _MJS_m4x4makeOrthoLocal(left, right, bottom, top, -1, 1); 589 | }); 590 | 591 | function _MJS_m4x4mulLocal(a, b) { 592 | var r = new Float64Array(16); 593 | var a11 = a[0]; 594 | var a21 = a[1]; 595 | var a31 = a[2]; 596 | var a41 = a[3]; 597 | var a12 = a[4]; 598 | var a22 = a[5]; 599 | var a32 = a[6]; 600 | var a42 = a[7]; 601 | var a13 = a[8]; 602 | var a23 = a[9]; 603 | var a33 = a[10]; 604 | var a43 = a[11]; 605 | var a14 = a[12]; 606 | var a24 = a[13]; 607 | var a34 = a[14]; 608 | var a44 = a[15]; 609 | var b11 = b[0]; 610 | var b21 = b[1]; 611 | var b31 = b[2]; 612 | var b41 = b[3]; 613 | var b12 = b[4]; 614 | var b22 = b[5]; 615 | var b32 = b[6]; 616 | var b42 = b[7]; 617 | var b13 = b[8]; 618 | var b23 = b[9]; 619 | var b33 = b[10]; 620 | var b43 = b[11]; 621 | var b14 = b[12]; 622 | var b24 = b[13]; 623 | var b34 = b[14]; 624 | var b44 = b[15]; 625 | 626 | r[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 627 | r[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 628 | r[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 629 | r[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 630 | r[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 631 | r[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 632 | r[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 633 | r[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 634 | r[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 635 | r[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 636 | r[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 637 | r[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 638 | r[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 639 | r[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 640 | r[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 641 | r[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 642 | 643 | return r; 644 | } 645 | var _MJS_m4x4mul = F2(_MJS_m4x4mulLocal); 646 | 647 | var _MJS_m4x4mulAffine = F2(function(a, b) { 648 | var r = new Float64Array(16); 649 | var a11 = a[0]; 650 | var a21 = a[1]; 651 | var a31 = a[2]; 652 | var a12 = a[4]; 653 | var a22 = a[5]; 654 | var a32 = a[6]; 655 | var a13 = a[8]; 656 | var a23 = a[9]; 657 | var a33 = a[10]; 658 | var a14 = a[12]; 659 | var a24 = a[13]; 660 | var a34 = a[14]; 661 | 662 | var b11 = b[0]; 663 | var b21 = b[1]; 664 | var b31 = b[2]; 665 | var b12 = b[4]; 666 | var b22 = b[5]; 667 | var b32 = b[6]; 668 | var b13 = b[8]; 669 | var b23 = b[9]; 670 | var b33 = b[10]; 671 | var b14 = b[12]; 672 | var b24 = b[13]; 673 | var b34 = b[14]; 674 | 675 | r[0] = a11 * b11 + a12 * b21 + a13 * b31; 676 | r[1] = a21 * b11 + a22 * b21 + a23 * b31; 677 | r[2] = a31 * b11 + a32 * b21 + a33 * b31; 678 | r[3] = 0; 679 | r[4] = a11 * b12 + a12 * b22 + a13 * b32; 680 | r[5] = a21 * b12 + a22 * b22 + a23 * b32; 681 | r[6] = a31 * b12 + a32 * b22 + a33 * b32; 682 | r[7] = 0; 683 | r[8] = a11 * b13 + a12 * b23 + a13 * b33; 684 | r[9] = a21 * b13 + a22 * b23 + a23 * b33; 685 | r[10] = a31 * b13 + a32 * b23 + a33 * b33; 686 | r[11] = 0; 687 | r[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14; 688 | r[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24; 689 | r[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34; 690 | r[15] = 1; 691 | 692 | return r; 693 | }); 694 | 695 | var _MJS_m4x4makeRotate = F2(function(angle, axis) { 696 | var r = new Float64Array(16); 697 | axis = _MJS_v3normalizeLocal(axis, _MJS_v3temp1Local); 698 | var x = axis[0]; 699 | var y = axis[1]; 700 | var z = axis[2]; 701 | var c = Math.cos(angle); 702 | var c1 = 1 - c; 703 | var s = Math.sin(angle); 704 | 705 | r[0] = x * x * c1 + c; 706 | r[1] = y * x * c1 + z * s; 707 | r[2] = z * x * c1 - y * s; 708 | r[3] = 0; 709 | r[4] = x * y * c1 - z * s; 710 | r[5] = y * y * c1 + c; 711 | r[6] = y * z * c1 + x * s; 712 | r[7] = 0; 713 | r[8] = x * z * c1 + y * s; 714 | r[9] = y * z * c1 - x * s; 715 | r[10] = z * z * c1 + c; 716 | r[11] = 0; 717 | r[12] = 0; 718 | r[13] = 0; 719 | r[14] = 0; 720 | r[15] = 1; 721 | 722 | return r; 723 | }); 724 | 725 | var _MJS_m4x4rotate = F3(function(angle, axis, m) { 726 | var r = new Float64Array(16); 727 | var im = 1.0 / _MJS_v3lengthLocal(axis); 728 | var x = axis[0] * im; 729 | var y = axis[1] * im; 730 | var z = axis[2] * im; 731 | var c = Math.cos(angle); 732 | var c1 = 1 - c; 733 | var s = Math.sin(angle); 734 | var xs = x * s; 735 | var ys = y * s; 736 | var zs = z * s; 737 | var xyc1 = x * y * c1; 738 | var xzc1 = x * z * c1; 739 | var yzc1 = y * z * c1; 740 | var t11 = x * x * c1 + c; 741 | var t21 = xyc1 + zs; 742 | var t31 = xzc1 - ys; 743 | var t12 = xyc1 - zs; 744 | var t22 = y * y * c1 + c; 745 | var t32 = yzc1 + xs; 746 | var t13 = xzc1 + ys; 747 | var t23 = yzc1 - xs; 748 | var t33 = z * z * c1 + c; 749 | var m11 = m[0], m21 = m[1], m31 = m[2], m41 = m[3]; 750 | var m12 = m[4], m22 = m[5], m32 = m[6], m42 = m[7]; 751 | var m13 = m[8], m23 = m[9], m33 = m[10], m43 = m[11]; 752 | var m14 = m[12], m24 = m[13], m34 = m[14], m44 = m[15]; 753 | 754 | r[0] = m11 * t11 + m12 * t21 + m13 * t31; 755 | r[1] = m21 * t11 + m22 * t21 + m23 * t31; 756 | r[2] = m31 * t11 + m32 * t21 + m33 * t31; 757 | r[3] = m41 * t11 + m42 * t21 + m43 * t31; 758 | r[4] = m11 * t12 + m12 * t22 + m13 * t32; 759 | r[5] = m21 * t12 + m22 * t22 + m23 * t32; 760 | r[6] = m31 * t12 + m32 * t22 + m33 * t32; 761 | r[7] = m41 * t12 + m42 * t22 + m43 * t32; 762 | r[8] = m11 * t13 + m12 * t23 + m13 * t33; 763 | r[9] = m21 * t13 + m22 * t23 + m23 * t33; 764 | r[10] = m31 * t13 + m32 * t23 + m33 * t33; 765 | r[11] = m41 * t13 + m42 * t23 + m43 * t33; 766 | r[12] = m14, 767 | r[13] = m24; 768 | r[14] = m34; 769 | r[15] = m44; 770 | 771 | return r; 772 | }); 773 | 774 | function _MJS_m4x4makeScale3Local(x, y, z) { 775 | var r = new Float64Array(16); 776 | 777 | r[0] = x; 778 | r[1] = 0; 779 | r[2] = 0; 780 | r[3] = 0; 781 | r[4] = 0; 782 | r[5] = y; 783 | r[6] = 0; 784 | r[7] = 0; 785 | r[8] = 0; 786 | r[9] = 0; 787 | r[10] = z; 788 | r[11] = 0; 789 | r[12] = 0; 790 | r[13] = 0; 791 | r[14] = 0; 792 | r[15] = 1; 793 | 794 | return r; 795 | } 796 | var _MJS_m4x4makeScale3 = F3(_MJS_m4x4makeScale3Local); 797 | 798 | var _MJS_m4x4makeScale = function(v) { 799 | return _MJS_m4x4makeScale3Local(v[0], v[1], v[2]); 800 | }; 801 | 802 | var _MJS_m4x4scale3 = F4(function(x, y, z, m) { 803 | var r = new Float64Array(16); 804 | 805 | r[0] = m[0] * x; 806 | r[1] = m[1] * x; 807 | r[2] = m[2] * x; 808 | r[3] = m[3] * x; 809 | r[4] = m[4] * y; 810 | r[5] = m[5] * y; 811 | r[6] = m[6] * y; 812 | r[7] = m[7] * y; 813 | r[8] = m[8] * z; 814 | r[9] = m[9] * z; 815 | r[10] = m[10] * z; 816 | r[11] = m[11] * z; 817 | r[12] = m[12]; 818 | r[13] = m[13]; 819 | r[14] = m[14]; 820 | r[15] = m[15]; 821 | 822 | return r; 823 | }); 824 | 825 | var _MJS_m4x4scale = F2(function(v, m) { 826 | var r = new Float64Array(16); 827 | var x = v[0]; 828 | var y = v[1]; 829 | var z = v[2]; 830 | 831 | r[0] = m[0] * x; 832 | r[1] = m[1] * x; 833 | r[2] = m[2] * x; 834 | r[3] = m[3] * x; 835 | r[4] = m[4] * y; 836 | r[5] = m[5] * y; 837 | r[6] = m[6] * y; 838 | r[7] = m[7] * y; 839 | r[8] = m[8] * z; 840 | r[9] = m[9] * z; 841 | r[10] = m[10] * z; 842 | r[11] = m[11] * z; 843 | r[12] = m[12]; 844 | r[13] = m[13]; 845 | r[14] = m[14]; 846 | r[15] = m[15]; 847 | 848 | return r; 849 | }); 850 | 851 | function _MJS_m4x4makeTranslate3Local(x, y, z) { 852 | var r = new Float64Array(16); 853 | 854 | r[0] = 1; 855 | r[1] = 0; 856 | r[2] = 0; 857 | r[3] = 0; 858 | r[4] = 0; 859 | r[5] = 1; 860 | r[6] = 0; 861 | r[7] = 0; 862 | r[8] = 0; 863 | r[9] = 0; 864 | r[10] = 1; 865 | r[11] = 0; 866 | r[12] = x; 867 | r[13] = y; 868 | r[14] = z; 869 | r[15] = 1; 870 | 871 | return r; 872 | } 873 | var _MJS_m4x4makeTranslate3 = F3(_MJS_m4x4makeTranslate3Local); 874 | 875 | var _MJS_m4x4makeTranslate = function(v) { 876 | return _MJS_m4x4makeTranslate3Local(v[0], v[1], v[2]); 877 | }; 878 | 879 | var _MJS_m4x4translate3 = F4(function(x, y, z, m) { 880 | var r = new Float64Array(16); 881 | var m11 = m[0]; 882 | var m21 = m[1]; 883 | var m31 = m[2]; 884 | var m41 = m[3]; 885 | var m12 = m[4]; 886 | var m22 = m[5]; 887 | var m32 = m[6]; 888 | var m42 = m[7]; 889 | var m13 = m[8]; 890 | var m23 = m[9]; 891 | var m33 = m[10]; 892 | var m43 = m[11]; 893 | 894 | r[0] = m11; 895 | r[1] = m21; 896 | r[2] = m31; 897 | r[3] = m41; 898 | r[4] = m12; 899 | r[5] = m22; 900 | r[6] = m32; 901 | r[7] = m42; 902 | r[8] = m13; 903 | r[9] = m23; 904 | r[10] = m33; 905 | r[11] = m43; 906 | r[12] = m11 * x + m12 * y + m13 * z + m[12]; 907 | r[13] = m21 * x + m22 * y + m23 * z + m[13]; 908 | r[14] = m31 * x + m32 * y + m33 * z + m[14]; 909 | r[15] = m41 * x + m42 * y + m43 * z + m[15]; 910 | 911 | return r; 912 | }); 913 | 914 | var _MJS_m4x4translate = F2(function(v, m) { 915 | var r = new Float64Array(16); 916 | var x = v[0]; 917 | var y = v[1]; 918 | var z = v[2]; 919 | var m11 = m[0]; 920 | var m21 = m[1]; 921 | var m31 = m[2]; 922 | var m41 = m[3]; 923 | var m12 = m[4]; 924 | var m22 = m[5]; 925 | var m32 = m[6]; 926 | var m42 = m[7]; 927 | var m13 = m[8]; 928 | var m23 = m[9]; 929 | var m33 = m[10]; 930 | var m43 = m[11]; 931 | 932 | r[0] = m11; 933 | r[1] = m21; 934 | r[2] = m31; 935 | r[3] = m41; 936 | r[4] = m12; 937 | r[5] = m22; 938 | r[6] = m32; 939 | r[7] = m42; 940 | r[8] = m13; 941 | r[9] = m23; 942 | r[10] = m33; 943 | r[11] = m43; 944 | r[12] = m11 * x + m12 * y + m13 * z + m[12]; 945 | r[13] = m21 * x + m22 * y + m23 * z + m[13]; 946 | r[14] = m31 * x + m32 * y + m33 * z + m[14]; 947 | r[15] = m41 * x + m42 * y + m43 * z + m[15]; 948 | 949 | return r; 950 | }); 951 | 952 | var _MJS_m4x4makeLookAt = F3(function(eye, center, up) { 953 | var z = _MJS_v3directionLocal(eye, center, _MJS_v3temp1Local); 954 | var x = _MJS_v3normalizeLocal(_MJS_v3crossLocal(up, z, _MJS_v3temp2Local), _MJS_v3temp2Local); 955 | var y = _MJS_v3normalizeLocal(_MJS_v3crossLocal(z, x, _MJS_v3temp3Local), _MJS_v3temp3Local); 956 | var tm1 = _MJS_m4x4temp1Local; 957 | var tm2 = _MJS_m4x4temp2Local; 958 | 959 | tm1[0] = x[0]; 960 | tm1[1] = y[0]; 961 | tm1[2] = z[0]; 962 | tm1[3] = 0; 963 | tm1[4] = x[1]; 964 | tm1[5] = y[1]; 965 | tm1[6] = z[1]; 966 | tm1[7] = 0; 967 | tm1[8] = x[2]; 968 | tm1[9] = y[2]; 969 | tm1[10] = z[2]; 970 | tm1[11] = 0; 971 | tm1[12] = 0; 972 | tm1[13] = 0; 973 | tm1[14] = 0; 974 | tm1[15] = 1; 975 | 976 | tm2[0] = 1; tm2[1] = 0; tm2[2] = 0; tm2[3] = 0; 977 | tm2[4] = 0; tm2[5] = 1; tm2[6] = 0; tm2[7] = 0; 978 | tm2[8] = 0; tm2[9] = 0; tm2[10] = 1; tm2[11] = 0; 979 | tm2[12] = -eye[0]; tm2[13] = -eye[1]; tm2[14] = -eye[2]; tm2[15] = 1; 980 | 981 | return _MJS_m4x4mulLocal(tm1, tm2); 982 | }); 983 | 984 | 985 | function _MJS_m4x4transposeLocal(m) { 986 | var r = new Float64Array(16); 987 | 988 | r[0] = m[0]; r[1] = m[4]; r[2] = m[8]; r[3] = m[12]; 989 | r[4] = m[1]; r[5] = m[5]; r[6] = m[9]; r[7] = m[13]; 990 | r[8] = m[2]; r[9] = m[6]; r[10] = m[10]; r[11] = m[14]; 991 | r[12] = m[3]; r[13] = m[7]; r[14] = m[11]; r[15] = m[15]; 992 | 993 | return r; 994 | } 995 | var _MJS_m4x4transpose = _MJS_m4x4transposeLocal; 996 | 997 | var _MJS_m4x4makeBasis = F3(function(vx, vy, vz) { 998 | var r = new Float64Array(16); 999 | 1000 | r[0] = vx[0]; 1001 | r[1] = vx[1]; 1002 | r[2] = vx[2]; 1003 | r[3] = 0; 1004 | r[4] = vy[0]; 1005 | r[5] = vy[1]; 1006 | r[6] = vy[2]; 1007 | r[7] = 0; 1008 | r[8] = vz[0]; 1009 | r[9] = vz[1]; 1010 | r[10] = vz[2]; 1011 | r[11] = 0; 1012 | r[12] = 0; 1013 | r[13] = 0; 1014 | r[14] = 0; 1015 | r[15] = 1; 1016 | 1017 | return r; 1018 | }); 1019 | -------------------------------------------------------------------------------- /src/Math/Matrix4.elm: -------------------------------------------------------------------------------- 1 | module Math.Matrix4 2 | exposing 3 | ( Mat4 4 | , fromRecord 5 | , identity 6 | , inverse 7 | , inverseOrthonormal 8 | , makeBasis 9 | , makeFrustum 10 | , makeLookAt 11 | , makeOrtho 12 | , makeOrtho2D 13 | , makePerspective 14 | , makeRotate 15 | , makeScale 16 | , makeScale3 17 | , makeTranslate 18 | , makeTranslate3 19 | , mul 20 | , mulAffine 21 | , rotate 22 | , scale 23 | , scale3 24 | , toRecord 25 | , transform 26 | , translate 27 | , translate3 28 | , transpose 29 | ) 30 | 31 | {-| A high performance linear algebra library using native JS arrays. Geared 32 | towards 3D graphics and use with `Graphics.WebGL`. All matrices are immutable. 33 | 34 | This library uses the convention that the prefix `make` is creating a new 35 | array,as without the prefix, you are applying some transform to an 36 | existing matrix. 37 | 38 | 39 | # Create 40 | 41 | @docs Mat4, identity 42 | 43 | 44 | # Operations 45 | 46 | @docs inverse, inverseOrthonormal, mul, mulAffine, transpose, makeBasis, transform 47 | 48 | 49 | # Projections 50 | 51 | @docs makeFrustum, makePerspective, makeOrtho, makeOrtho2D, makeLookAt 52 | 53 | 54 | # Apply Transformations 55 | 56 | @docs rotate, scale, scale3, translate, translate3 57 | 58 | 59 | # Create Transformations 60 | 61 | @docs makeRotate, makeScale, makeScale3, makeTranslate, makeTranslate3 62 | 63 | 64 | # Conversions 65 | 66 | @docs toRecord, fromRecord 67 | 68 | -} 69 | 70 | import Elm.Kernel.MJS 71 | import Math.Vector3 exposing (Vec3) 72 | 73 | 74 | {-| 4x4 matrix type 75 | -} 76 | type Mat4 77 | = Mat4 78 | 79 | 80 | {-| Multiply a vector by a 4x4 matrix: m * v 81 | -} 82 | transform : Mat4 -> Vec3 -> Vec3 83 | transform = 84 | Elm.Kernel.MJS.v3mul4x4 85 | 86 | 87 | {-| A matrix with all 0s, except 1s on the diagonal. 88 | -} 89 | identity : Mat4 90 | identity = 91 | Elm.Kernel.MJS.m4x4identity 92 | 93 | 94 | {-| Computes the inverse of any matrix. This is somewhat computationally 95 | intensive. If the matrix is not invertible, `Nothing` is returned. 96 | -} 97 | inverse : Mat4 -> Maybe Mat4 98 | inverse = 99 | Elm.Kernel.MJS.m4x4inverse 100 | 101 | 102 | {-| Computes the inverse of the given matrix, assuming that the matrix is 103 | orthonormal. This algorithm is more efficient than general matrix inversion, and 104 | has no possibility of failing. 105 | -} 106 | inverseOrthonormal : Mat4 -> Mat4 107 | inverseOrthonormal = 108 | Elm.Kernel.MJS.m4x4inverseOrthonormal 109 | 110 | 111 | {-| Creates a matrix for a projection frustum with the given parameters. 112 | 113 | Parameters: 114 | 115 | - left - the left coordinate of the frustum 116 | - right- the right coordinate of the frustum 117 | - bottom - the bottom coordinate of the frustum 118 | - top - the top coordinate of the frustum 119 | - znear - the near z distance of the frustum 120 | - zfar - the far z distance of the frustum 121 | 122 | -} 123 | makeFrustum : Float -> Float -> Float -> Float -> Float -> Float -> Mat4 124 | makeFrustum = 125 | Elm.Kernel.MJS.m4x4makeFrustum 126 | 127 | 128 | {-| Creates a matrix for a perspective projection with the given parameters. 129 | 130 | Parameters: 131 | 132 | - fovy - field of view in the y axis, in degrees 133 | - aspect - aspect ratio 134 | - znear - the near z distance of the projection 135 | - zfar - the far z distance of the projection 136 | 137 | -} 138 | makePerspective : Float -> Float -> Float -> Float -> Mat4 139 | makePerspective = 140 | Elm.Kernel.MJS.m4x4makePerspective 141 | 142 | 143 | {-| Creates a matrix for an orthogonal frustum projection with the given parameters. 144 | 145 | Parameters: 146 | 147 | - left - the left coordinate of the frustum 148 | - right- the right coordinate of the frustum 149 | - bottom - the bottom coordinate of the frustum 150 | - top - the top coordinate of the frustum 151 | - znear - the near z distance of the frustum 152 | - zfar - the far z distance of the frustum 153 | 154 | -} 155 | makeOrtho : Float -> Float -> Float -> Float -> Float -> Float -> Mat4 156 | makeOrtho = 157 | Elm.Kernel.MJS.m4x4makeOrtho 158 | 159 | 160 | {-| Creates a matrix for a 2D orthogonal frustum projection with the given 161 | parameters. `znear` and `zfar` are assumed to be -1 and 1, respectively. 162 | 163 | Parameters: 164 | 165 | - left - the left coordinate of the frustum 166 | - right- the right coordinate of the frustum 167 | - bottom - the bottom coordinate of the frustum 168 | - top - the top coordinate of the frustum 169 | 170 | -} 171 | makeOrtho2D : Float -> Float -> Float -> Float -> Mat4 172 | makeOrtho2D = 173 | Elm.Kernel.MJS.m4x4makeOrtho2D 174 | 175 | 176 | {-| Matrix multiplcation: a * b 177 | -} 178 | mul : Mat4 -> Mat4 -> Mat4 179 | mul = 180 | Elm.Kernel.MJS.m4x4mul 181 | 182 | 183 | {-| Matrix multiplication, assuming a and b are affine: a * b 184 | -} 185 | mulAffine : Mat4 -> Mat4 -> Mat4 186 | mulAffine = 187 | Elm.Kernel.MJS.m4x4mulAffine 188 | 189 | 190 | {-| Creates a transformation matrix for rotation in radians about the 191 | 3-element vector axis. 192 | -} 193 | makeRotate : Float -> Vec3 -> Mat4 194 | makeRotate = 195 | Elm.Kernel.MJS.m4x4makeRotate 196 | 197 | 198 | {-| Concatenates a rotation in radians about an axis to the given matrix. 199 | -} 200 | rotate : Float -> Vec3 -> Mat4 -> Mat4 201 | rotate = 202 | Elm.Kernel.MJS.m4x4rotate 203 | 204 | 205 | {-| Creates a transformation matrix for scaling by 3 scalar values, one for 206 | each of the x, y, and z directions. 207 | -} 208 | makeScale3 : Float -> Float -> Float -> Mat4 209 | makeScale3 = 210 | Elm.Kernel.MJS.m4x4makeScale3 211 | 212 | 213 | {-| Creates a transformation matrix for scaling each of the x, y, and z axes by 214 | the amount given in the corresponding element of the 3-element vector. 215 | -} 216 | makeScale : Vec3 -> Mat4 217 | makeScale = 218 | Elm.Kernel.MJS.m4x4makeScale 219 | 220 | 221 | {-| Concatenates a scaling to the given matrix. 222 | -} 223 | scale3 : Float -> Float -> Float -> Mat4 -> Mat4 224 | scale3 = 225 | Elm.Kernel.MJS.m4x4scale3 226 | 227 | 228 | {-| Concatenates a scaling to the given matrix. 229 | -} 230 | scale : Vec3 -> Mat4 -> Mat4 231 | scale = 232 | Elm.Kernel.MJS.m4x4scale 233 | 234 | 235 | {-| Creates a transformation matrix for translating by 3 scalar values, one for 236 | each of the x, y, and z directions. 237 | -} 238 | makeTranslate3 : Float -> Float -> Float -> Mat4 239 | makeTranslate3 = 240 | Elm.Kernel.MJS.m4x4makeTranslate3 241 | 242 | 243 | {-| Creates a transformation matrix for translating each of the x, y, and z 244 | axes by the amount given in the corresponding element of the 3-element vector. 245 | -} 246 | makeTranslate : Vec3 -> Mat4 247 | makeTranslate = 248 | Elm.Kernel.MJS.m4x4makeTranslate 249 | 250 | 251 | {-| Concatenates a translation to the given matrix. 252 | -} 253 | translate3 : Float -> Float -> Float -> Mat4 -> Mat4 254 | translate3 = 255 | Elm.Kernel.MJS.m4x4translate3 256 | 257 | 258 | {-| Concatenates a translation to the given matrix. 259 | -} 260 | translate : Vec3 -> Mat4 -> Mat4 261 | translate = 262 | Elm.Kernel.MJS.m4x4translate 263 | 264 | 265 | {-| Creates a transformation matrix for a camera. 266 | 267 | Parameters: 268 | 269 | - eye - The location of the camera 270 | - center - The location of the focused object 271 | - up - The "up" direction according to the camera 272 | 273 | -} 274 | makeLookAt : Vec3 -> Vec3 -> Vec3 -> Mat4 275 | makeLookAt = 276 | Elm.Kernel.MJS.m4x4makeLookAt 277 | 278 | 279 | {-| "Flip" the matrix across the diagonal by swapping row index and column 280 | index. 281 | -} 282 | transpose : Mat4 -> Mat4 283 | transpose = 284 | Elm.Kernel.MJS.m4x4transpose 285 | 286 | 287 | {-| Creates a transform from a basis consisting of 3 linearly independent vectors. 288 | -} 289 | makeBasis : Vec3 -> Vec3 -> Vec3 -> Mat4 290 | makeBasis = 291 | Elm.Kernel.MJS.m4x4makeBasis 292 | 293 | 294 | {-| Convert a matrix to a record. Elements are given by their row and column indices, starting at 1, so `m23` means the element in the second row, third column. 295 | -} 296 | toRecord : Mat4 -> { m11 : Float, m21 : Float, m31 : Float, m41 : Float, m12 : Float, m22 : Float, m32 : Float, m42 : Float, m13 : Float, m23 : Float, m33 : Float, m43 : Float, m14 : Float, m24 : Float, m34 : Float, m44 : Float } 297 | toRecord = 298 | Elm.Kernel.MJS.m4x4toRecord 299 | 300 | 301 | {-| Convert a record to a matrix. 302 | -} 303 | fromRecord : { m11 : Float, m21 : Float, m31 : Float, m41 : Float, m12 : Float, m22 : Float, m32 : Float, m42 : Float, m13 : Float, m23 : Float, m33 : Float, m43 : Float, m14 : Float, m24 : Float, m34 : Float, m44 : Float } -> Mat4 304 | fromRecord = 305 | Elm.Kernel.MJS.m4x4fromRecord 306 | -------------------------------------------------------------------------------- /src/Math/Vector2.elm: -------------------------------------------------------------------------------- 1 | module Math.Vector2 2 | exposing 3 | ( Vec2 4 | , add 5 | , direction 6 | , distance 7 | , distanceSquared 8 | , dot 9 | , fromRecord 10 | , getX 11 | , getY 12 | , length 13 | , lengthSquared 14 | , negate 15 | , normalize 16 | , scale 17 | , setX 18 | , setY 19 | , sub 20 | , toRecord 21 | , vec2 22 | ) 23 | 24 | {-| A high performance linear algebra library using native JS arrays. Geared 25 | towards 3D graphics and use with `Graphics.WebGL`. All vectors are immutable. 26 | 27 | 28 | # Create 29 | 30 | @docs Vec2, vec2 31 | 32 | 33 | # Get and Set 34 | 35 | The set functions create a new copy of the vector, updating a single field. 36 | 37 | @docs getX, getY, setX, setY 38 | 39 | 40 | # Operations 41 | 42 | @docs add, sub, negate, scale, dot, normalize, direction 43 | @docs length, lengthSquared, distance, distanceSquared 44 | 45 | 46 | # Conversions 47 | 48 | @docs toRecord, fromRecord 49 | 50 | -} 51 | 52 | import Elm.Kernel.MJS 53 | 54 | 55 | {-| Two dimensional vector type 56 | -} 57 | type Vec2 58 | = Vec2 59 | 60 | 61 | {-| Creates a new 2-element vector with the given values. 62 | -} 63 | vec2 : Float -> Float -> Vec2 64 | vec2 = 65 | Elm.Kernel.MJS.v2 66 | 67 | 68 | {-| Extract the x component of a vector. 69 | -} 70 | getX : Vec2 -> Float 71 | getX = 72 | Elm.Kernel.MJS.v2getX 73 | 74 | 75 | {-| Extract the y component of a vector. 76 | -} 77 | getY : Vec2 -> Float 78 | getY = 79 | Elm.Kernel.MJS.v2getY 80 | 81 | 82 | {-| Update the x component of a vector, returning a new vector. 83 | -} 84 | setX : Float -> Vec2 -> Vec2 85 | setX = 86 | Elm.Kernel.MJS.v2setX 87 | 88 | 89 | {-| Update the y component of a vector, returning a new vector. 90 | -} 91 | setY : Float -> Vec2 -> Vec2 92 | setY = 93 | Elm.Kernel.MJS.v2setY 94 | 95 | 96 | {-| Convert a vector to a record. 97 | -} 98 | toRecord : Vec2 -> { x : Float, y : Float } 99 | toRecord = 100 | Elm.Kernel.MJS.v2toRecord 101 | 102 | 103 | {-| Convert a record to a vector. 104 | -} 105 | fromRecord : { x : Float, y : Float } -> Vec2 106 | fromRecord = 107 | Elm.Kernel.MJS.v2fromRecord 108 | 109 | 110 | {-| Vector addition: a + b 111 | -} 112 | add : Vec2 -> Vec2 -> Vec2 113 | add = 114 | Elm.Kernel.MJS.v2add 115 | 116 | 117 | {-| Vector subtraction: a - b 118 | -} 119 | sub : Vec2 -> Vec2 -> Vec2 120 | sub = 121 | Elm.Kernel.MJS.v2sub 122 | 123 | 124 | {-| Vector negation: -a 125 | -} 126 | negate : Vec2 -> Vec2 127 | negate = 128 | Elm.Kernel.MJS.v2negate 129 | 130 | 131 | {-| The normalized direction from b to a: (a - b) / |a - b| 132 | -} 133 | direction : Vec2 -> Vec2 -> Vec2 134 | direction = 135 | Elm.Kernel.MJS.v2direction 136 | 137 | 138 | {-| The length of the given vector: |a| 139 | -} 140 | length : Vec2 -> Float 141 | length = 142 | Elm.Kernel.MJS.v2length 143 | 144 | 145 | {-| The square of the length of the given vector: |a| * |a| 146 | -} 147 | lengthSquared : Vec2 -> Float 148 | lengthSquared = 149 | Elm.Kernel.MJS.v2lengthSquared 150 | 151 | 152 | {-| The distance between two vectors. 153 | -} 154 | distance : Vec2 -> Vec2 -> Float 155 | distance = 156 | Elm.Kernel.MJS.v2distance 157 | 158 | 159 | {-| The square of the distance between two vectors. 160 | -} 161 | distanceSquared : Vec2 -> Vec2 -> Float 162 | distanceSquared = 163 | Elm.Kernel.MJS.v2distanceSquared 164 | 165 | 166 | {-| A unit vector with the same direction as the given vector: a / |a| 167 | -} 168 | normalize : Vec2 -> Vec2 169 | normalize = 170 | Elm.Kernel.MJS.v2normalize 171 | 172 | 173 | {-| Multiply the vector by a scalar: s * v 174 | -} 175 | scale : Float -> Vec2 -> Vec2 176 | scale = 177 | Elm.Kernel.MJS.v2scale 178 | 179 | 180 | {-| The dot product of a and b 181 | -} 182 | dot : Vec2 -> Vec2 -> Float 183 | dot = 184 | Elm.Kernel.MJS.v2dot 185 | -------------------------------------------------------------------------------- /src/Math/Vector3.elm: -------------------------------------------------------------------------------- 1 | module Math.Vector3 2 | exposing 3 | ( Vec3 4 | , add 5 | , cross 6 | , direction 7 | , distance 8 | , distanceSquared 9 | , dot 10 | , fromRecord 11 | , getX 12 | , getY 13 | , getZ 14 | , i 15 | , j 16 | , k 17 | , length 18 | , lengthSquared 19 | , negate 20 | , normalize 21 | , scale 22 | , setX 23 | , setY 24 | , setZ 25 | , sub 26 | , toRecord 27 | , vec3 28 | ) 29 | 30 | {-| A high performance linear algebra library using native JS arrays. Geared 31 | towards 3D graphics and use with `Graphics.WebGL`. All vectors are immutable. 32 | 33 | 34 | # Create 35 | 36 | @docs Vec3, vec3, i, j, k 37 | 38 | 39 | # Get and Set 40 | 41 | The set functions create a new copy of the vector, updating a single field. 42 | 43 | @docs getX, getY, getZ, setX, setY, setZ 44 | 45 | 46 | # Operations 47 | 48 | @docs add, sub, negate, scale, dot, cross, normalize, direction 49 | @docs length, lengthSquared, distance, distanceSquared 50 | 51 | 52 | # Conversions 53 | 54 | @docs toRecord, fromRecord 55 | 56 | -} 57 | 58 | import Elm.Kernel.MJS 59 | 60 | 61 | {-| Three dimensional vector type 62 | -} 63 | type Vec3 64 | = Vec3 65 | 66 | 67 | {-| Creates a new 3-element vector with the given values. 68 | -} 69 | vec3 : Float -> Float -> Float -> Vec3 70 | vec3 = 71 | Elm.Kernel.MJS.v3 72 | 73 | 74 | {-| The unit vector î which points in the x direction: `vec3 1 0 0` 75 | -} 76 | i : Vec3 77 | i = 78 | Elm.Kernel.MJS.v3 1 0 0 79 | 80 | 81 | {-| The unit vector ĵ which points in the y direction: `vec3 0 1 0` 82 | -} 83 | j : Vec3 84 | j = 85 | Elm.Kernel.MJS.v3 0 1 0 86 | 87 | 88 | {-| The unit vector k̂ which points in the z direction: `vec3 0 0 1` 89 | -} 90 | k : Vec3 91 | k = 92 | Elm.Kernel.MJS.v3 0 0 1 93 | 94 | 95 | {-| Extract the x component of a vector. 96 | -} 97 | getX : Vec3 -> Float 98 | getX = 99 | Elm.Kernel.MJS.v3getX 100 | 101 | 102 | {-| Extract the y component of a vector. 103 | -} 104 | getY : Vec3 -> Float 105 | getY = 106 | Elm.Kernel.MJS.v3getY 107 | 108 | 109 | {-| Extract the z component of a vector. 110 | -} 111 | getZ : Vec3 -> Float 112 | getZ = 113 | Elm.Kernel.MJS.v3getZ 114 | 115 | 116 | {-| Update the x component of a vector, returning a new vector. 117 | -} 118 | setX : Float -> Vec3 -> Vec3 119 | setX = 120 | Elm.Kernel.MJS.v3setX 121 | 122 | 123 | {-| Update the y component of a vector, returning a new vector. 124 | -} 125 | setY : Float -> Vec3 -> Vec3 126 | setY = 127 | Elm.Kernel.MJS.v3setY 128 | 129 | 130 | {-| Update the z component of a vector, returning a new vector. 131 | -} 132 | setZ : Float -> Vec3 -> Vec3 133 | setZ = 134 | Elm.Kernel.MJS.v3setZ 135 | 136 | 137 | {-| Convert a vector to a record. 138 | -} 139 | toRecord : Vec3 -> { x : Float, y : Float, z : Float } 140 | toRecord = 141 | Elm.Kernel.MJS.v3toRecord 142 | 143 | 144 | {-| Convert a record to a vector. 145 | -} 146 | fromRecord : { x : Float, y : Float, z : Float } -> Vec3 147 | fromRecord = 148 | Elm.Kernel.MJS.v3fromRecord 149 | 150 | 151 | {-| Vector addition: a + b 152 | -} 153 | add : Vec3 -> Vec3 -> Vec3 154 | add = 155 | Elm.Kernel.MJS.v3add 156 | 157 | 158 | {-| Vector subtraction: a - b 159 | -} 160 | sub : Vec3 -> Vec3 -> Vec3 161 | sub = 162 | Elm.Kernel.MJS.v3sub 163 | 164 | 165 | {-| Vector negation: -a 166 | -} 167 | negate : Vec3 -> Vec3 168 | negate = 169 | Elm.Kernel.MJS.v3negate 170 | 171 | 172 | {-| The normalized direction from b to a: (a - b) / |a - b| 173 | -} 174 | direction : Vec3 -> Vec3 -> Vec3 175 | direction = 176 | Elm.Kernel.MJS.v3direction 177 | 178 | 179 | {-| The length of the given vector: |a| 180 | -} 181 | length : Vec3 -> Float 182 | length = 183 | Elm.Kernel.MJS.v3length 184 | 185 | 186 | {-| The square of the length of the given vector: |a| * |a| 187 | -} 188 | lengthSquared : Vec3 -> Float 189 | lengthSquared = 190 | Elm.Kernel.MJS.v3lengthSquared 191 | 192 | 193 | {-| The distance between two vectors. 194 | -} 195 | distance : Vec3 -> Vec3 -> Float 196 | distance = 197 | Elm.Kernel.MJS.v3distance 198 | 199 | 200 | {-| The square of the distance between two vectors. 201 | -} 202 | distanceSquared : Vec3 -> Vec3 -> Float 203 | distanceSquared = 204 | Elm.Kernel.MJS.v3distanceSquared 205 | 206 | 207 | {-| A unit vector with the same direction as the given vector: a / |a| 208 | -} 209 | normalize : Vec3 -> Vec3 210 | normalize = 211 | Elm.Kernel.MJS.v3normalize 212 | 213 | 214 | {-| Multiply the vector by a scalar: s * v 215 | -} 216 | scale : Float -> Vec3 -> Vec3 217 | scale = 218 | Elm.Kernel.MJS.v3scale 219 | 220 | 221 | {-| The dot product of a and b 222 | -} 223 | dot : Vec3 -> Vec3 -> Float 224 | dot = 225 | Elm.Kernel.MJS.v3dot 226 | 227 | 228 | {-| The cross product of a and b 229 | -} 230 | cross : Vec3 -> Vec3 -> Vec3 231 | cross = 232 | Elm.Kernel.MJS.v3cross 233 | -------------------------------------------------------------------------------- /src/Math/Vector4.elm: -------------------------------------------------------------------------------- 1 | module Math.Vector4 2 | exposing 3 | ( Vec4 4 | , add 5 | , direction 6 | , distance 7 | , distanceSquared 8 | , dot 9 | , fromRecord 10 | , getW 11 | , getX 12 | , getY 13 | , getZ 14 | , length 15 | , lengthSquared 16 | , negate 17 | , normalize 18 | , scale 19 | , setW 20 | , setX 21 | , setY 22 | , setZ 23 | , sub 24 | , toRecord 25 | , vec4 26 | ) 27 | 28 | {-| A high performance linear algebra library using native JS arrays. Geared 29 | towards 3D graphics and use with `Graphics.WebGL`. All vectors are immutable. 30 | 31 | 32 | # Create 33 | 34 | @docs Vec4, vec4 35 | 36 | 37 | # Get and Set 38 | 39 | The set functions create a new copy of the vector, updating a single field. 40 | 41 | @docs getX, getY, getZ, getW, setX, setY, setZ, setW 42 | 43 | 44 | # Operations 45 | 46 | @docs add, sub, negate, scale, dot, normalize, direction 47 | @docs length, lengthSquared, distance, distanceSquared 48 | 49 | 50 | # Conversions 51 | 52 | @docs toRecord, fromRecord 53 | 54 | -} 55 | 56 | import Elm.Kernel.MJS 57 | 58 | 59 | {-| Four dimensional vector type 60 | -} 61 | type Vec4 62 | = Vec4 63 | 64 | 65 | {-| Creates a new 4-element vector with the given x, y, z, and w values. 66 | -} 67 | vec4 : Float -> Float -> Float -> Float -> Vec4 68 | vec4 = 69 | Elm.Kernel.MJS.v4 70 | 71 | 72 | {-| Extract the x component of a vector. 73 | -} 74 | getX : Vec4 -> Float 75 | getX = 76 | Elm.Kernel.MJS.v4getX 77 | 78 | 79 | {-| Extract the y component of a vector. 80 | -} 81 | getY : Vec4 -> Float 82 | getY = 83 | Elm.Kernel.MJS.v4getY 84 | 85 | 86 | {-| Extract the z component of a vector. 87 | -} 88 | getZ : Vec4 -> Float 89 | getZ = 90 | Elm.Kernel.MJS.v4getZ 91 | 92 | 93 | {-| Extract the w component of a vector. 94 | -} 95 | getW : Vec4 -> Float 96 | getW = 97 | Elm.Kernel.MJS.v4getW 98 | 99 | 100 | {-| Update the x component of a vector, returning a new vector. 101 | -} 102 | setX : Float -> Vec4 -> Vec4 103 | setX = 104 | Elm.Kernel.MJS.v4setX 105 | 106 | 107 | {-| Update the y component of a vector, returning a new vector. 108 | -} 109 | setY : Float -> Vec4 -> Vec4 110 | setY = 111 | Elm.Kernel.MJS.v4setY 112 | 113 | 114 | {-| Update the z component of a vector, returning a new vector. 115 | -} 116 | setZ : Float -> Vec4 -> Vec4 117 | setZ = 118 | Elm.Kernel.MJS.v4setZ 119 | 120 | 121 | {-| Update the w component of a vector, returning a new vector. 122 | -} 123 | setW : Float -> Vec4 -> Vec4 124 | setW = 125 | Elm.Kernel.MJS.v4setW 126 | 127 | 128 | {-| Convert a vector to a record. 129 | -} 130 | toRecord : Vec4 -> { x : Float, y : Float, z : Float, w : Float } 131 | toRecord = 132 | Elm.Kernel.MJS.v4toRecord 133 | 134 | 135 | {-| Convert a record to a vector. 136 | -} 137 | fromRecord : { x : Float, y : Float, z : Float, w : Float } -> Vec4 138 | fromRecord = 139 | Elm.Kernel.MJS.v4fromRecord 140 | 141 | 142 | {-| Vector addition: a + b 143 | -} 144 | add : Vec4 -> Vec4 -> Vec4 145 | add = 146 | Elm.Kernel.MJS.v4add 147 | 148 | 149 | {-| Vector subtraction: a - b 150 | -} 151 | sub : Vec4 -> Vec4 -> Vec4 152 | sub = 153 | Elm.Kernel.MJS.v4sub 154 | 155 | 156 | {-| Vector negation: -a 157 | -} 158 | negate : Vec4 -> Vec4 159 | negate = 160 | Elm.Kernel.MJS.v4negate 161 | 162 | 163 | {-| The normalized direction from b to a: (a - b) / |a - b| 164 | -} 165 | direction : Vec4 -> Vec4 -> Vec4 166 | direction = 167 | Elm.Kernel.MJS.v4direction 168 | 169 | 170 | {-| The length of the given vector: |a| 171 | -} 172 | length : Vec4 -> Float 173 | length = 174 | Elm.Kernel.MJS.v4length 175 | 176 | 177 | {-| The square of the length of the given vector: |a| * |a| 178 | -} 179 | lengthSquared : Vec4 -> Float 180 | lengthSquared = 181 | Elm.Kernel.MJS.v4lengthSquared 182 | 183 | 184 | {-| The distance between two vectors. 185 | -} 186 | distance : Vec4 -> Vec4 -> Float 187 | distance = 188 | Elm.Kernel.MJS.v4distance 189 | 190 | 191 | {-| The square of the distance between two vectors. 192 | -} 193 | distanceSquared : Vec4 -> Vec4 -> Float 194 | distanceSquared = 195 | Elm.Kernel.MJS.v4distanceSquared 196 | 197 | 198 | {-| A unit vector with the same direction as the given vector: a / |a| 199 | -} 200 | normalize : Vec4 -> Vec4 201 | normalize = 202 | Elm.Kernel.MJS.v4normalize 203 | 204 | 205 | {-| Multiply the vector by a scalar: s * v 206 | -} 207 | scale : Float -> Vec4 -> Vec4 208 | scale = 209 | Elm.Kernel.MJS.v4scale 210 | 211 | 212 | {-| The dot product of a and b 213 | -} 214 | dot : Vec4 -> Vec4 -> Float 215 | dot = 216 | Elm.Kernel.MJS.v4dot 217 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (suite) 2 | 3 | import Expect 4 | import Fuzz 5 | import Math.Matrix4 as M4 6 | import Math.Vector2 as V2 7 | import Math.Vector3 as V3 8 | import Math.Vector4 as V4 9 | import Test exposing (Test, describe, fuzz, fuzz3, test) 10 | 11 | 12 | suite : Test 13 | suite = 14 | describe "elm-linear-algebra" 15 | [ describe "Vector2 module" 16 | [ test "vec2" <| 17 | \() -> 18 | Expect.equal 19 | (V2.vec2 3 4) 20 | (V2.vec2 3 4) 21 | , test "setX" <| 22 | \() -> 23 | Expect.equal 24 | (V2.vec2 5 4) 25 | (V2.vec2 3 4 |> V2.setX 5) 26 | , test "setY" <| 27 | \() -> 28 | Expect.equal 29 | (V2.vec2 3 6) 30 | (V2.vec2 3 4 |> V2.setY 6) 31 | ] 32 | , describe "Vector4 module" 33 | [ test "vec4" <| 34 | \() -> 35 | Expect.equal 36 | (V4.vec4 1 2 3 4) 37 | (V4.vec4 1 2 3 4) 38 | , test "setX" <| 39 | \() -> 40 | Expect.equal 41 | (V4.vec4 5 2 3 4) 42 | (V4.vec4 1 2 3 4 |> V4.setX 5) 43 | , test "setY" <| 44 | \() -> 45 | Expect.equal 46 | (V4.vec4 1 6 3 4) 47 | (V4.vec4 1 2 3 4 |> V4.setY 6) 48 | , test "setZ" <| 49 | \() -> 50 | Expect.equal 51 | (V4.vec4 1 2 7 4) 52 | (V4.vec4 1 2 3 4 |> V4.setZ 7) 53 | , test "setW" <| 54 | \() -> 55 | Expect.equal 56 | (V4.vec4 1 2 3 8) 57 | (V4.vec4 1 2 3 4 |> V4.setW 8) 58 | ] 59 | , describe "Matrix4 module" 60 | [ describe "inverse" 61 | [ test "Identity is its own inverse" <| 62 | \_ -> 63 | M4.inverse M4.identity |> Expect.equal (Just M4.identity) 64 | , test "Scaling in 3D inverts to shrinking" <| 65 | \_ -> 66 | M4.makeScale3 2 3 (1 / 8) 67 | |> M4.inverse 68 | |> Expect.equal (Just (M4.makeScale3 0.5 (1 / 3) 8)) 69 | , fuzz3 Fuzz.float Fuzz.float Fuzz.float "Translating in 3D inverts to translating the other way" <| 70 | \x y z -> 71 | M4.makeTranslate3 x y z 72 | |> M4.inverse 73 | |> Expect.equal (Just (M4.makeTranslate3 -x -y -z)) 74 | , Test.concat <| 75 | -- using constants instead of fuzz because of floating point errors 76 | flip List.map 77 | [ pi, 1.5 * pi, pi / 12, pi / 50 ] 78 | (\theta -> 79 | test ("Rotation by " ++ toString theta) <| 80 | \_ -> 81 | let 82 | m = 83 | M4.makeRotate theta V3.i 84 | in 85 | m 86 | |> M4.inverse 87 | |> Maybe.map (M4.mul m) 88 | |> Expect.equal (Just M4.identity) 89 | ) 90 | , test "cannot invert a singular matrix" <| 91 | \_ -> 92 | M4.makeBasis V3.i V3.j (V3.fromTuple ( 0, 0, 0 )) 93 | |> M4.inverse 94 | |> Expect.equal Nothing 95 | , test "inverts a known matrix" <| 96 | \_ -> 97 | M4.makeBasis 98 | (V3.fromTuple ( 2, 9, -3 )) 99 | (V3.fromTuple ( -4, 3, 14 )) 100 | (V3.fromTuple ( 1, 12, 6 )) 101 | |> M4.inverse 102 | |> Expect.equal 103 | (M4.makeBasis 104 | (V3.fromTuple ( -150, -90, 135 )) 105 | (V3.fromTuple ( 38, 15, -16 )) 106 | (V3.fromTuple ( -51, -15, 42 )) 107 | |> M4.scale3 (1 / 195) (1 / 195) (1 / 195) 108 | |> Just 109 | ) 110 | , test "fromRecord should be able to create the identity matrix" <| 111 | \_ -> 112 | M4.fromRecord { m11 = 1, m21 = 0, m31 = 0, m41 = 0, m12 = 0, m22 = 1, m32 = 0, m42 = 0, m13 = 0, m23 = 0, m33 = 1, m43 = 0, m14 = 0, m24 = 0, m34 = 0, m44 = 1 } |> Expect.equal M4.identity 113 | , test "toRecord should convert the identity matrix" <| 114 | \_ -> 115 | { m11 = 1, m21 = 0, m31 = 0, m41 = 0, m12 = 0, m22 = 1, m32 = 0, m42 = 0, m13 = 0, m23 = 0, m33 = 1, m43 = 0, m14 = 0, m24 = 0, m34 = 0, m44 = 1 } |> Expect.equal (M4.toRecord M4.identity) 116 | , fuzz fuzz4x4 "fromRecord should be the opposite of toRecord" <| 117 | \record4x4 -> 118 | M4.toRecord (M4.fromRecord record4x4) |> Expect.equal record4x4 119 | , test "matrix make basis should not lose precision" <| 120 | \_ -> 121 | let 122 | matrix = 123 | M4.makeBasis 124 | (V3.fromTuple ( 0.0007645259938837921, 0, 0 )) 125 | (V3.fromTuple ( 0, 0, 0 )) 126 | (V3.fromTuple ( 0, 0, -178844.39532879056 )) 127 | in 128 | matrix 129 | |> toString 130 | |> Expect.equal "{ 0 = 0.0007645259938837921, 1 = 0, 2 = 0, 3 = 0, 4 = 0, 5 = 0, 6 = 0, 7 = 0, 8 = 0, 9 = 0, 10 = -178844.39532879056, 11 = 0, 12 = 0, 13 = 0, 14 = 0, 15 = 1 }" 131 | ] 132 | ] 133 | ] 134 | 135 | 136 | fuzz4x4 : Fuzz.Fuzzer { m11 : Float, m21 : Float, m31 : Float, m41 : Float, m12 : Float, m22 : Float, m32 : Float, m42 : Float, m13 : Float, m23 : Float, m33 : Float, m43 : Float, m14 : Float, m24 : Float, m34 : Float, m44 : Float } 137 | fuzz4x4 = 138 | Fuzz.map 139 | (\m11 m21 m31 m41 m12 m22 m32 m42 m13 m23 m33 m43 m14 m24 m34 m44 -> 140 | { m11 = m11 141 | , m21 = m21 142 | , m31 = m31 143 | , m41 = m41 144 | , m12 = m12 145 | , m22 = m22 146 | , m32 = m32 147 | , m42 = m42 148 | , m13 = m13 149 | , m23 = m23 150 | , m33 = m33 151 | , m43 = m43 152 | , m14 = m14 153 | , m24 = m24 154 | , m34 = m34 155 | , m44 = m44 156 | } 157 | ) 158 | Fuzz.float 159 | |> Fuzz.andMap Fuzz.float 160 | |> Fuzz.andMap Fuzz.float 161 | |> Fuzz.andMap Fuzz.float 162 | |> Fuzz.andMap Fuzz.float 163 | |> Fuzz.andMap Fuzz.float 164 | |> Fuzz.andMap Fuzz.float 165 | |> Fuzz.andMap Fuzz.float 166 | |> Fuzz.andMap Fuzz.float 167 | |> Fuzz.andMap Fuzz.float 168 | |> Fuzz.andMap Fuzz.float 169 | |> Fuzz.andMap Fuzz.float 170 | |> Fuzz.andMap Fuzz.float 171 | |> Fuzz.andMap Fuzz.float 172 | |> Fuzz.andMap Fuzz.float 173 | |> Fuzz.andMap Fuzz.float 174 | --------------------------------------------------------------------------------