mat3.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /**
  2. * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. /*
  8. * This code has been modified from https://github.com/toji/gl-matrix/,
  9. * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. */
  18. import { NumberArray } from '../../../mol-util/type-helpers';
  19. import { EPSILON } from './common';
  20. import { Mat4 } from './mat4';
  21. import { Vec3 } from './vec3';
  22. interface Mat3 extends Array<number> { [d: number]: number, '@type': 'mat3', length: 9 }
  23. interface ReadonlyMat3 extends Array<number> { readonly [d: number]: number, '@type': 'mat3', length: 9 }
  24. function Mat3() {
  25. return Mat3.zero();
  26. }
  27. namespace Mat3 {
  28. export function zero(): Mat3 {
  29. // force double backing array by 0.1.
  30. const ret = [0.1, 0, 0, 0, 0, 0, 0, 0, 0];
  31. ret[0] = 0.0;
  32. return ret as any;
  33. }
  34. export function identity(): Mat3 {
  35. const out = zero();
  36. out[0] = 1;
  37. out[1] = 0;
  38. out[2] = 0;
  39. out[3] = 0;
  40. out[4] = 1;
  41. out[5] = 0;
  42. out[6] = 0;
  43. out[7] = 0;
  44. out[8] = 1;
  45. return out;
  46. }
  47. export function setIdentity(mat: Mat3): Mat3 {
  48. mat[0] = 1;
  49. mat[1] = 0;
  50. mat[2] = 0;
  51. mat[3] = 0;
  52. mat[4] = 1;
  53. mat[5] = 0;
  54. mat[6] = 0;
  55. mat[7] = 0;
  56. mat[8] = 1;
  57. return mat;
  58. }
  59. export function toArray<T extends NumberArray>(a: Mat3, out: T, offset: number) {
  60. out[offset + 0] = a[0];
  61. out[offset + 1] = a[1];
  62. out[offset + 2] = a[2];
  63. out[offset + 3] = a[3];
  64. out[offset + 4] = a[4];
  65. out[offset + 5] = a[5];
  66. out[offset + 6] = a[6];
  67. out[offset + 7] = a[7];
  68. out[offset + 8] = a[8];
  69. return out;
  70. }
  71. export function fromArray(a: Mat3, array: NumberArray, offset: number) {
  72. a[0] = array[offset + 0];
  73. a[1] = array[offset + 1];
  74. a[2] = array[offset + 2];
  75. a[3] = array[offset + 3];
  76. a[4] = array[offset + 4];
  77. a[5] = array[offset + 5];
  78. a[6] = array[offset + 6];
  79. a[7] = array[offset + 7];
  80. a[8] = array[offset + 8];
  81. return a;
  82. }
  83. export function fromColumns(out: Mat3, left: Vec3, middle: Vec3, right: Vec3) {
  84. out[0] = left[0];
  85. out[1] = left[1];
  86. out[2] = left[2];
  87. out[3] = middle[0];
  88. out[4] = middle[1];
  89. out[5] = middle[2];
  90. out[6] = right[0];
  91. out[7] = right[1];
  92. out[8] = right[2];
  93. return out;
  94. }
  95. /**
  96. * Copies the upper-left 3x3 values into the given mat3.
  97. */
  98. export function fromMat4(out: Mat3, a: Mat4) {
  99. out[0] = a[0];
  100. out[1] = a[1];
  101. out[2] = a[2];
  102. out[3] = a[4];
  103. out[4] = a[5];
  104. out[5] = a[6];
  105. out[6] = a[8];
  106. out[7] = a[9];
  107. out[8] = a[10];
  108. return out;
  109. }
  110. export function create(a00: number, a01: number, a02: number, a10: number, a11: number, a12: number, a20: number, a21: number, a22: number): Mat3 {
  111. const out = zero();
  112. out[0] = a00;
  113. out[1] = a01;
  114. out[2] = a02;
  115. out[3] = a10;
  116. out[4] = a11;
  117. out[5] = a12;
  118. out[6] = a20;
  119. out[7] = a21;
  120. out[8] = a22;
  121. return out;
  122. }
  123. const _id = identity();
  124. export function isIdentity(m: Mat3, eps?: number) {
  125. return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);
  126. }
  127. export function hasNaN(m: Mat3) {
  128. for (let i = 0; i < 9; i++) if (isNaN(m[i])) return true;
  129. return false;
  130. }
  131. /**
  132. * Creates a new Mat3 initialized with values from an existing matrix
  133. */
  134. export function clone(a: Mat3) {
  135. return copy(zero(), a);
  136. }
  137. export function areEqual(a: Mat3, b: Mat3, eps: number) {
  138. for (let i = 0; i < 9; i++) {
  139. if (Math.abs(a[i] - b[i]) > eps) return false;
  140. }
  141. return true;
  142. }
  143. export function setValue(a: Mat3, i: number, j: number, value: number) {
  144. a[3 * j + i] = value;
  145. }
  146. export function getValue(a: Mat3, i: number, j: number) {
  147. return a[3 * j + i];
  148. }
  149. /**
  150. * Copy the values from one Mat3 to another
  151. */
  152. export function copy(out: Mat3, a: Mat3) {
  153. out[0] = a[0];
  154. out[1] = a[1];
  155. out[2] = a[2];
  156. out[3] = a[3];
  157. out[4] = a[4];
  158. out[5] = a[5];
  159. out[6] = a[6];
  160. out[7] = a[7];
  161. out[8] = a[8];
  162. return out;
  163. }
  164. /**
  165. * Transpose the values of a Mat3
  166. */
  167. export function transpose(out: Mat3, a: Mat3) {
  168. // If we are transposing ourselves we can skip a few steps but have to cache some values
  169. if (out === a) {
  170. const a01 = a[1], a02 = a[2], a12 = a[5];
  171. out[1] = a[3];
  172. out[2] = a[6];
  173. out[3] = a01;
  174. out[5] = a[7];
  175. out[6] = a02;
  176. out[7] = a12;
  177. } else {
  178. out[0] = a[0];
  179. out[1] = a[3];
  180. out[2] = a[6];
  181. out[3] = a[1];
  182. out[4] = a[4];
  183. out[5] = a[7];
  184. out[6] = a[2];
  185. out[7] = a[5];
  186. out[8] = a[8];
  187. }
  188. return out;
  189. }
  190. /**
  191. * Inverts a Mat3
  192. */
  193. export function invert(out: Mat3, a: Mat3): Mat3 {
  194. const a00 = a[0], a01 = a[1], a02 = a[2];
  195. const a10 = a[3], a11 = a[4], a12 = a[5];
  196. const a20 = a[6], a21 = a[7], a22 = a[8];
  197. const b01 = a22 * a11 - a12 * a21;
  198. const b11 = -a22 * a10 + a12 * a20;
  199. const b21 = a21 * a10 - a11 * a20;
  200. // Calculate the determinant
  201. let det = a00 * b01 + a01 * b11 + a02 * b21;
  202. if (!det) {
  203. console.warn('non-invertible matrix.', a);
  204. return out;
  205. }
  206. det = 1.0 / det;
  207. out[0] = b01 * det;
  208. out[1] = (-a22 * a01 + a02 * a21) * det;
  209. out[2] = (a12 * a01 - a02 * a11) * det;
  210. out[3] = b11 * det;
  211. out[4] = (a22 * a00 - a02 * a20) * det;
  212. out[5] = (-a12 * a00 + a02 * a10) * det;
  213. out[6] = b21 * det;
  214. out[7] = (-a21 * a00 + a01 * a20) * det;
  215. out[8] = (a11 * a00 - a01 * a10) * det;
  216. return out;
  217. }
  218. export function symmtricFromUpper(out: Mat3, a: Mat3) {
  219. if (out === a) {
  220. out[3] = a[1];
  221. out[6] = a[2];
  222. out[7] = a[5];
  223. } else {
  224. out[0] = a[0];
  225. out[1] = a[1];
  226. out[2] = a[2];
  227. out[3] = a[1];
  228. out[4] = a[4];
  229. out[5] = a[5];
  230. out[6] = a[2];
  231. out[7] = a[5];
  232. out[8] = a[8];
  233. }
  234. return out;
  235. }
  236. export function symmtricFromLower(out: Mat3, a: Mat3) {
  237. if (out === a) {
  238. out[1] = a[3];
  239. out[2] = a[6];
  240. out[5] = a[7];
  241. } else {
  242. out[0] = a[0];
  243. out[1] = a[3];
  244. out[2] = a[6];
  245. out[3] = a[3];
  246. out[4] = a[4];
  247. out[5] = a[7];
  248. out[6] = a[6];
  249. out[7] = a[7];
  250. out[8] = a[8];
  251. }
  252. return out;
  253. }
  254. export function determinant(a: Mat3) {
  255. const a00 = a[0], a01 = a[1], a02 = a[2];
  256. const a10 = a[3], a11 = a[4], a12 = a[5];
  257. const a20 = a[6], a21 = a[7], a22 = a[8];
  258. const b01 = a22 * a11 - a12 * a21;
  259. const b11 = -a22 * a10 + a12 * a20;
  260. const b21 = a21 * a10 - a11 * a20;
  261. // Calculate the determinant
  262. return a00 * b01 + a01 * b11 + a02 * b21;
  263. }
  264. export function trace(a: Mat3) {
  265. return a[0] + a[4] + a[8];
  266. }
  267. export function sub(out: Mat3, a: Mat3, b: Mat3) {
  268. out[0] = a[0] - b[0];
  269. out[1] = a[1] - b[1];
  270. out[2] = a[2] - b[2];
  271. out[3] = a[3] - b[3];
  272. out[4] = a[4] - b[4];
  273. out[5] = a[5] - b[5];
  274. out[6] = a[6] - b[6];
  275. out[7] = a[7] - b[7];
  276. out[8] = a[8] - b[8];
  277. return out;
  278. }
  279. export function add(out: Mat3, a: Mat3, b: Mat3) {
  280. out[0] = a[0] + b[0];
  281. out[1] = a[1] + b[1];
  282. out[2] = a[2] + b[2];
  283. out[3] = a[3] + b[3];
  284. out[4] = a[4] + b[4];
  285. out[5] = a[5] + b[5];
  286. out[6] = a[6] + b[6];
  287. out[7] = a[7] + b[7];
  288. out[8] = a[8] + b[8];
  289. return out;
  290. }
  291. export function mul(out: Mat3, a: Mat3, b: Mat3) {
  292. const a00 = a[0], a01 = a[1], a02 = a[2],
  293. a10 = a[3], a11 = a[4], a12 = a[5],
  294. a20 = a[6], a21 = a[7], a22 = a[8];
  295. const b00 = b[0], b01 = b[1], b02 = b[2],
  296. b10 = b[3], b11 = b[4], b12 = b[5],
  297. b20 = b[6], b21 = b[7], b22 = b[8];
  298. out[0] = b00 * a00 + b01 * a10 + b02 * a20;
  299. out[1] = b00 * a01 + b01 * a11 + b02 * a21;
  300. out[2] = b00 * a02 + b01 * a12 + b02 * a22;
  301. out[3] = b10 * a00 + b11 * a10 + b12 * a20;
  302. out[4] = b10 * a01 + b11 * a11 + b12 * a21;
  303. out[5] = b10 * a02 + b11 * a12 + b12 * a22;
  304. out[6] = b20 * a00 + b21 * a10 + b22 * a20;
  305. out[7] = b20 * a01 + b21 * a11 + b22 * a21;
  306. out[8] = b20 * a02 + b21 * a12 + b22 * a22;
  307. return out;
  308. }
  309. export function subScalar(out: Mat3, a: Mat3, s: number) {
  310. out[0] = a[0] - s;
  311. out[1] = a[1] - s;
  312. out[2] = a[2] - s;
  313. out[3] = a[3] - s;
  314. out[4] = a[4] - s;
  315. out[5] = a[5] - s;
  316. out[6] = a[6] - s;
  317. out[7] = a[7] - s;
  318. out[8] = a[8] - s;
  319. return out;
  320. }
  321. export function addScalar(out: Mat3, a: Mat3, s: number) {
  322. out[0] = a[0] + s;
  323. out[1] = a[1] + s;
  324. out[2] = a[2] + s;
  325. out[3] = a[3] + s;
  326. out[4] = a[4] + s;
  327. out[5] = a[5] + s;
  328. out[6] = a[6] + s;
  329. out[7] = a[7] + s;
  330. out[8] = a[8] + s;
  331. return out;
  332. }
  333. export function mulScalar(out: Mat3, a: Mat3, s: number) {
  334. out[0] = a[0] * s;
  335. out[1] = a[1] * s;
  336. out[2] = a[2] * s;
  337. out[3] = a[3] * s;
  338. out[4] = a[4] * s;
  339. out[5] = a[5] * s;
  340. out[6] = a[6] * s;
  341. out[7] = a[7] * s;
  342. out[8] = a[8] * s;
  343. return out;
  344. }
  345. const piThird = Math.PI / 3;
  346. const tmpB = zero();
  347. /**
  348. * Given a real symmetric 3x3 matrix A, compute the eigenvalues
  349. *
  350. * From https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
  351. */
  352. export function symmetricEigenvalues(out: Vec3, a: Mat3) {
  353. const p1 = a[1] * a[1] + a[2] * a[2] + a[5] * a[5];
  354. if (p1 === 0) {
  355. out[0] = a[0];
  356. out[1] = a[4];
  357. out[2] = a[8];
  358. } else {
  359. const q = trace(a) / 3;
  360. const a1 = a[0] - q;
  361. const a2 = a[4] - q;
  362. const a3 = a[8] - q;
  363. const p2 = a1 * a1 + a2 * a2 + a3 * a3 + 2 * p1;
  364. const p = Math.sqrt(p2 / 6);
  365. mulScalar(tmpB, Identity, q);
  366. sub(tmpB, a, tmpB);
  367. mulScalar(tmpB, tmpB, (1 / p));
  368. const r = determinant(tmpB) / 2;
  369. // In exact arithmetic for a symmetric matrix -1 <= r <= 1
  370. // but computation error can leave it slightly outside this range.
  371. const phi = r <= -1 ? piThird : r >= 1 ?
  372. 0 : Math.acos(r) / 3;
  373. // the eigenvalues satisfy eig3 <= eig2 <= eig1
  374. out[0] = q + 2 * p * Math.cos(phi);
  375. out[2] = q + 2 * p * Math.cos(phi + (2 * piThird));
  376. out[1] = 3 * q - out[0] - out[2]; // since trace(A) = eig1 + eig2 + eig3
  377. }
  378. return out;
  379. }
  380. const tmpR0 = [0.1, 0.0, 0.0] as unknown as Vec3;
  381. const tmpR1 = [0.1, 0.0, 0.0] as unknown as Vec3;
  382. const tmpR2 = [0.1, 0.0, 0.0] as unknown as Vec3;
  383. const tmpR0xR1 = [0.1, 0.0, 0.0] as unknown as Vec3;
  384. const tmpR0xR2 = [0.1, 0.0, 0.0] as unknown as Vec3;
  385. const tmpR1xR2 = [0.1, 0.0, 0.0] as unknown as Vec3;
  386. /**
  387. * Calculates the eigenvector for the given eigenvalue `e` of matrix `a`
  388. */
  389. export function eigenvector(out: Vec3, a: Mat3, e: number) {
  390. Vec3.set(tmpR0, a[0] - e, a[1], a[2]);
  391. Vec3.set(tmpR1, a[1], a[4] - e, a[5]);
  392. Vec3.set(tmpR2, a[2], a[5], a[8] - e);
  393. Vec3.cross(tmpR0xR1, tmpR0, tmpR1);
  394. Vec3.cross(tmpR0xR2, tmpR0, tmpR2);
  395. Vec3.cross(tmpR1xR2, tmpR1, tmpR2);
  396. const d0 = Vec3.dot(tmpR0xR1, tmpR0xR1);
  397. const d1 = Vec3.dot(tmpR0xR2, tmpR0xR2);
  398. const d2 = Vec3.dot(tmpR1xR2, tmpR1xR2);
  399. let dmax = d0;
  400. let imax = 0;
  401. if (d1 > dmax) {
  402. dmax = d1;
  403. imax = 1;
  404. }
  405. if (d2 > dmax) imax = 2;
  406. if (imax === 0) {
  407. Vec3.scale(out, tmpR0xR1, 1 / Math.sqrt(d0));
  408. } else if (imax === 1) {
  409. Vec3.scale(out, tmpR0xR2, 1 / Math.sqrt(d1));
  410. } else {
  411. Vec3.scale(out, tmpR1xR2, 1 / Math.sqrt(d2));
  412. }
  413. return out;
  414. }
  415. /**
  416. * Get matrix to transform directions, e.g. normals
  417. */
  418. export function directionTransform(out: Mat3, t: Mat4) {
  419. fromMat4(out, t);
  420. invert(out, out);
  421. transpose(out, out);
  422. return out;
  423. }
  424. export const Identity: ReadonlyMat3 = identity();
  425. /** Return the Frobenius inner product of two matrices (= dot product of the flattened matrices).
  426. * Can be used as a measure of similarity between two rotation matrices. */
  427. export function innerProduct(a: Mat3, b: Mat3) {
  428. return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
  429. + a[3] * b[3] + a[4] * b[4] + a[5] * b[5]
  430. + a[6] * b[6] + a[7] * b[7] + a[8] * b[8];
  431. }
  432. }
  433. export { Mat3 };