@ccp-nc/crystvis-js 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/workflows/test-mocha.yml +30 -0
  3. package/.vscode/settings.json +4 -0
  4. package/LICENSE +21 -0
  5. package/README.html +1127 -0
  6. package/README.md +76 -0
  7. package/demo/demo.css +30 -0
  8. package/demo/index.html +76 -0
  9. package/demo/main.js +143 -0
  10. package/docs/.nojekyll +0 -0
  11. package/docs-tutorials/Events.md +57 -0
  12. package/docs-tutorials/Queries.md +50 -0
  13. package/fonts/Rubik/OFL.txt +93 -0
  14. package/fonts/Rubik/README.txt +77 -0
  15. package/fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf +0 -0
  16. package/fonts/Rubik/Rubik-VariableFont_wght.ttf +0 -0
  17. package/fonts/Rubik/static/Rubik-Black.ttf +0 -0
  18. package/fonts/Rubik/static/Rubik-BlackItalic.ttf +0 -0
  19. package/fonts/Rubik/static/Rubik-Bold.ttf +0 -0
  20. package/fonts/Rubik/static/Rubik-BoldItalic.ttf +0 -0
  21. package/fonts/Rubik/static/Rubik-ExtraBold.ttf +0 -0
  22. package/fonts/Rubik/static/Rubik-ExtraBoldItalic.ttf +0 -0
  23. package/fonts/Rubik/static/Rubik-Italic.ttf +0 -0
  24. package/fonts/Rubik/static/Rubik-Light.ttf +0 -0
  25. package/fonts/Rubik/static/Rubik-LightItalic.ttf +0 -0
  26. package/fonts/Rubik/static/Rubik-Medium.ttf +0 -0
  27. package/fonts/Rubik/static/Rubik-MediumItalic.ttf +0 -0
  28. package/fonts/Rubik/static/Rubik-Regular.ttf +0 -0
  29. package/fonts/Rubik/static/Rubik-SemiBold.ttf +0 -0
  30. package/fonts/Rubik/static/Rubik-SemiBoldItalic.ttf +0 -0
  31. package/index.html +25 -0
  32. package/index.js +11 -0
  33. package/jsconf.json +14 -0
  34. package/lib/assets/fonts/Rubik-Medium.fnt +297 -0
  35. package/lib/assets/fonts/Rubik-Medium.png +0 -0
  36. package/lib/assets/fonts/bmpfonts.in.js +16 -0
  37. package/lib/assets/fonts/bmpfonts.js +9 -0
  38. package/lib/assets/fonts/font.js +82 -0
  39. package/lib/assets/fonts/index.js +14 -0
  40. package/lib/assets/fonts/threebmfont.js +28 -0
  41. package/lib/data.js +125 -0
  42. package/lib/formats/cell.js +114 -0
  43. package/lib/formats/cif.js +22 -0
  44. package/lib/formats/magres.js +337 -0
  45. package/lib/formats/xyz.js +124 -0
  46. package/lib/loader.js +87 -0
  47. package/lib/model.js +2076 -0
  48. package/lib/modelview.js +382 -0
  49. package/lib/nmrdata.js +2898 -0
  50. package/lib/orbit.js +1233 -0
  51. package/lib/primitives/atoms.js +261 -0
  52. package/lib/primitives/cell.js +160 -0
  53. package/lib/primitives/dither.js +156 -0
  54. package/lib/primitives/ellipsoid.js +183 -0
  55. package/lib/primitives/geometries.js +20 -0
  56. package/lib/primitives/index.js +48 -0
  57. package/lib/primitives/isosurface.js +171 -0
  58. package/lib/primitives/shapes.js +100 -0
  59. package/lib/primitives/sprites.js +172 -0
  60. package/lib/query.js +158 -0
  61. package/lib/render.js +440 -0
  62. package/lib/selbox.js +361 -0
  63. package/lib/shaders/aura.frag +26 -0
  64. package/lib/shaders/aura.vert +37 -0
  65. package/lib/shaders/dither.frag +42 -0
  66. package/lib/shaders/dither.vert +8 -0
  67. package/lib/shaders/index.in.js +17 -0
  68. package/lib/shaders/index.js +25 -0
  69. package/lib/shaders/msdf300.frag +25 -0
  70. package/lib/shaders/msdf300.vert +45 -0
  71. package/lib/tensor.js +227 -0
  72. package/lib/utils.js +168 -0
  73. package/lib/visualizer.js +480 -0
  74. package/package.json +106 -0
  75. package/scripts/build-bundle.js +17 -0
  76. package/scripts/build-fonts.js +43 -0
  77. package/scripts/build-resources.js +46 -0
  78. package/scripts/plugins-shim.js +10 -0
  79. package/test/chemdata.js +69 -0
  80. package/test/data/CHA.cif +74 -0
  81. package/test/data/H2O.xyz +8 -0
  82. package/test/data/H2_bound.xyz +4 -0
  83. package/test/data/ethanol.cell +25 -0
  84. package/test/data/ethanol.magres +238 -0
  85. package/test/data/example_single.cif +789 -0
  86. package/test/data/frac.cell +8 -0
  87. package/test/data/org.cif +427 -0
  88. package/test/data/pyridine.xyz +13 -0
  89. package/test/data/si8.xyz +10 -0
  90. package/test/loader.js +107 -0
  91. package/test/model.js +368 -0
  92. package/test/query.js +135 -0
  93. package/test/tensor.js +133 -0
  94. package/test/test-html/examples.js +1485 -0
  95. package/test/test-html/index.html +33 -0
  96. package/test/test-html/index.js +279 -0
  97. package/tools/compile_colors.py +120 -0
  98. package/tools/compile_periodic.py +96 -0
  99. package/tools/ptable.json +497 -0
  100. package/tools/test +5844 -0
package/lib/tensor.js ADDED
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview TensorData class to store tensors like NMR data and such.
5
+ * @module
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import * as mjs from 'mathjs';
10
+ import * as THREE from 'three';
11
+
12
+ const efg2hz = 234964.77815245767;
13
+ const isc2hz = 1.6784031762379067e-16; // hbar/(2*pi)*1e19
14
+
15
+ class TensorData {
16
+
17
+ /**
18
+ * Create a TensorData object, to store whole tensors,
19
+ * diagonalise their symmetric part and return derived quantities
20
+ *
21
+ * @param {Array | mathjs.Matrix | TensorData} M Tensor in 3x3 matrix form
22
+ *
23
+ */
24
+ constructor(M) {
25
+
26
+ if (M instanceof TensorData)
27
+ M = M._M;
28
+ else if (M instanceof Array)
29
+ M = mjs.matrix(M);
30
+
31
+ this._M = M;
32
+
33
+ var MT = mjs.transpose(M);
34
+ this._Msymm = mjs.divide(mjs.add(M, MT), 2.0);
35
+ this._Masymm = mjs.subtract(M, this._Msymm);
36
+
37
+ // Diagonalize
38
+ var eigs = mjs.eigs(this._Msymm);
39
+ // Sort by eigenvalue magnitude
40
+ var evecs = mjs.transpose(eigs.vectors._data);
41
+ eigs = _.zip(eigs.values._data, evecs);
42
+ eigs = _.sortBy(eigs, function(x) {
43
+ return x[0];
44
+ });
45
+ eigs = _.unzip(eigs);
46
+
47
+ this._evals = eigs[0];
48
+
49
+ // Make it right-handed
50
+ evecs = eigs[1];
51
+ evecs[2] = mjs.cross(evecs[0], evecs[1]);
52
+ this._evecs = mjs.transpose(evecs);
53
+
54
+ // Isotropy
55
+ this._iso = mjs.mean(this._evals);
56
+
57
+ // Haeberlen order
58
+ var iso = this._iso;
59
+ var haeb = _.zip(_.range(3), this._evals);
60
+ haeb = _.sortBy(haeb, function(x) {
61
+ return Math.abs(x[1] - iso);
62
+ });
63
+
64
+ this._haeb_evals = [
65
+ this._evals[haeb[1][0]],
66
+ this._evals[haeb[0][0]],
67
+ this._evals[haeb[2][0]]
68
+ ];
69
+
70
+ this._haeb_evecs = mjs.transpose([
71
+ evecs[haeb[1][0]],
72
+ evecs[haeb[0][0]],
73
+ mjs.cross(evecs[haeb[1][0]], evecs[haeb[0][0]])
74
+ ]);
75
+
76
+ }
77
+
78
+ get data() {
79
+ return JSON.parse(JSON.stringify(this._M._data));
80
+ }
81
+
82
+ get symmetric() {
83
+ return JSON.parse(JSON.stringify(this._Msymm._data));
84
+ }
85
+
86
+ get asymmetric() {
87
+ return JSON.parse(JSON.stringify(this._Masymm._data));
88
+ }
89
+
90
+ get eigenvalues() {
91
+ return Array.from(this._evals);
92
+ }
93
+
94
+ get eigenvectors() {
95
+ return JSON.parse(JSON.stringify(this._evecs));
96
+ }
97
+
98
+ get haeberlen_eigenvalues() {
99
+ return Array.from(this._haeb_evals);
100
+ }
101
+
102
+ get haeberlen_eigenvectors() {
103
+ return JSON.parse(JSON.stringify(this._haeb_evecs));
104
+ }
105
+
106
+ get isotropy() {
107
+ return this._iso;
108
+ }
109
+
110
+ get anisotropy() {
111
+ return this._haeb_evals[2] - (this._haeb_evals[0] + this._haeb_evals[1]) / 2.0;
112
+ }
113
+
114
+ get reduced_anisotropy() {
115
+ return this._haeb_evals[2] - this._iso;
116
+ }
117
+
118
+ get asymmetry() {
119
+ var ra = this.reduced_anisotropy;
120
+ return (this._haeb_evals[1] - this._haeb_evals[0]) / ra;
121
+ }
122
+
123
+ get span() {
124
+ return this._evals[2] - this._evals[0];
125
+ }
126
+
127
+ get skew() {
128
+ var s = this.span;
129
+ return 3 * (this._evals[1] - this._iso) / s;
130
+ }
131
+
132
+ /**
133
+ * Rotate the TensorData by a given basis, either as passive or active
134
+ * transformation. Returns the rotated TensorData (does not modify this in
135
+ * place). Default is passive. The convention is such that for a symmetric
136
+ * tensor,
137
+ *
138
+ * T.rotate(T.eigenvectors)
139
+ *
140
+ * returns the diagonalised tensor.
141
+ *
142
+ * @param {Array | mathjs.Matrix | TensorData} basis Basis to rotate into
143
+ * @param {Boolean} active If true, make it an active transformation (default is false)
144
+ *
145
+ * @return {TensorData} Rotated tensor
146
+ */
147
+ rotate(basis, active = false) {
148
+ // Rotate the tensor by the given basis of vectors
149
+ if (basis instanceof mjs.Matrix)
150
+ basis = basis._data;
151
+ if (basis instanceof TensorData)
152
+ basis = basis._M._data;
153
+
154
+ var bR = basis;
155
+ var bL = mjs.transpose(basis);
156
+
157
+ if (active) {
158
+ bR = bL;
159
+ bL = basis;
160
+ }
161
+ var rdata = mjs.multiply(bL, this._M._data, bR);
162
+
163
+ return new TensorData(rdata);
164
+ }
165
+
166
+
167
+ /**
168
+ * Convert this TensorData to return a clone that has been converted from
169
+ * atomic units to Hertz, assuming it's an Electric Field Gradient tensor.
170
+ *
171
+ * @param {Number} Q Quadrupolar moment of the given nucleus (barn)
172
+ *
173
+ * @return {TensorData} Converted tensor
174
+ */
175
+ efgAtomicToHz(Q) {
176
+
177
+ // Clone self, then multiply all the necessary quantities
178
+ var clone = _.clone(this);
179
+
180
+ var k = efg2hz*Q;
181
+
182
+ clone._M = mjs.multiply(this._M, k);
183
+ clone._Msymm = mjs.multiply(this._Msymm, k);
184
+ clone._Masymm = mjs.multiply(this._Masymm, k);
185
+
186
+ clone._iso = this._iso*k;
187
+
188
+ clone._evals = mjs.multiply(this._evals, k);
189
+ clone._haeb_evals = mjs.multiply(this._haeb_evals, k);
190
+
191
+ return clone;
192
+ }
193
+
194
+ /**
195
+ * Convert this TensorData to return a clone that has been converted from
196
+ * atomic units to Hertz, assuming it's an Indirect Spin-spin Coupling
197
+ * tensor.
198
+ *
199
+ * @param {Number} g1 Gyromagnetic ratio of the first atom
200
+ * @param {Number} g2 Gyromagnetic ratio of the second atom
201
+ *
202
+ * @return {TensorData} Converted tensor
203
+ */
204
+ iscAtomicToHz(g1, g2) {
205
+
206
+ // Clone self, then multiply all the necessary quantities
207
+ var clone = _.clone(this);
208
+
209
+ var k = isc2hz*g1*g2;
210
+
211
+ clone._M = mjs.multiply(this._M, k);
212
+ clone._Msymm = mjs.multiply(this._Msymm, k);
213
+ clone._Masymm = mjs.multiply(this._Masymm, k);
214
+
215
+ clone._iso = this._iso*k;
216
+
217
+ clone._evals = mjs.multiply(this._evals, k);
218
+ clone._haeb_evals = mjs.multiply(this._haeb_evals, k);
219
+
220
+ return clone;
221
+ }
222
+
223
+ }
224
+
225
+ export {
226
+ TensorData
227
+ }
package/lib/utils.js ADDED
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Utility functions
5
+ * @module
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import * as THREE from 'three';
10
+
11
+ /**
12
+ * Compute a full list of indices of all cells for
13
+ * a supercell of given size
14
+ * @param {Array} scell Size of the requested supercell
15
+ */
16
+ function supercellGrid(scell) {
17
+ var bounds = _.map(scell, function(x) {
18
+ var lb = Math.ceil(-x / 2)+(1-x%2); // This makes it so that for even supercells we skew on the positive side
19
+ if (Object.is(lb, -0)) {
20
+ lb = 0; // Avoids -0
21
+ }
22
+ var ub = Math.ceil(x / 2)+(1-x%2);
23
+ return [lb, ub];
24
+ });
25
+ var grid = [];
26
+
27
+ for (var i = bounds[0][0]; i < bounds[0][1]; ++i) {
28
+ for (var j = bounds[1][0]; j < bounds[1][1]; ++j) {
29
+ for (var k = bounds[2][0]; k < bounds[2][1]; ++k) {
30
+ grid.push([i, j, k]);
31
+ }
32
+ }
33
+ }
34
+
35
+ return grid;
36
+ }
37
+
38
+ /**
39
+ * Reduce a tuple of atomic index + i,j,k cell indices to a single integer
40
+ * @param {int} i Atomic index
41
+ * @param {Array} ijk Cell indices
42
+ * @param {Array} scell Supercell size
43
+ * @param {int} n Number of atoms in the model
44
+ *
45
+ * @return {int} Overall index
46
+ */
47
+ function supercellIndex(i, ijk, scell, n) {
48
+
49
+ ijk = _.map(ijk, function(x, i) {
50
+ return x + Math.floor((scell[i]-1)/2); // Important (depends on the convention in supercellGrid)
51
+ });
52
+
53
+ // If any of these is smaller than 0, we're sure to be out
54
+ if (ijk[0] < 0 || ijk[1] < 0 || ijk[2] < 0) {
55
+ return -1;
56
+ }
57
+ if (ijk[0] >= scell[0] || ijk[1] >= scell[1] || ijk[2] >= scell[2]) {
58
+ return -1;
59
+ }
60
+
61
+ var itot = ijk[2] + ijk[1] * scell[2] + ijk[0] * scell[2] * scell[1];
62
+ itot = i + itot*n;
63
+
64
+ return itot;
65
+ }
66
+
67
+ /**
68
+ * Turn a unit cell expressed as Array of Arrays into a THREE.Matrix3 object
69
+ * @param {Array} cell Cell in Array form
70
+ *
71
+ * @return {THREE.Matrix3} Cell in THREE.Matrix3 form
72
+ */
73
+ function cellMatrix3(cell) {
74
+ var lc = cell;
75
+ cell = new THREE.Matrix3();
76
+ cell.set(lc[0][0], lc[1][0], lc[2][0],
77
+ lc[0][1], lc[1][1], lc[2][1],
78
+ lc[0][2], lc[1][2], lc[2][2]);
79
+
80
+ return cell;
81
+ }
82
+
83
+ /** Add a static variable to a class definition, old style (will become
84
+ * obsolete once the static keyword is widely accepted in ES)
85
+ * @param {Object} cls Class
86
+ * @param {String} name Name of the variable to define
87
+ * @param {any} value Value to assign to it
88
+ */
89
+ function addStaticVar(cls, name, value) {
90
+ Object.defineProperty(cls, name, {
91
+ value: value,
92
+ writable: false
93
+ });
94
+ }
95
+
96
+ /** Shift a CPK Color to be more distinct (used for isotopes)
97
+ *
98
+ * @param {int} basec Base color to modify. Can be anything accepted
99
+ * by the THREE.Color constructor, but by default
100
+ * we assume it's an hex integer.
101
+ * @param {float} shift Shift to apply. Should range from -1 to 1.
102
+ *
103
+ * @return {int} Shifted color, returned as hex code.
104
+ *
105
+ * */
106
+ function shiftCpkColor(basec, shift=0) {
107
+ let c = new THREE.Color(basec);
108
+ let hsl = {};
109
+ c.getHSL(hsl);
110
+ // Here the goal is to choose a color that's still similar enough
111
+ // to the original, but also contrasts to it.
112
+ // We shift it more in hue if it's not very saturated/bright, and
113
+ // also shift its lightness towards 0.5 and saturation towards 1,
114
+ // so the hue becomes more evident
115
+
116
+ /*
117
+ let f = ((1.0-hsl.s)/2 + Math.abs(hsl.l-0.5))*0.7 + 0.3;
118
+ hsl.h = (hsl.h+shift*f*0.5)%1;
119
+
120
+ f *= 0.4;
121
+ hsl.s = f+(1-f)*hsl.s;
122
+ f *= 0.5;
123
+ hsl.l = f/2.0+(1-f)*hsl.l;
124
+ */
125
+
126
+ // How close to white/black is the color?
127
+ let bw = Math.abs(hsl.l-0.5)/0.5;
128
+
129
+ if (Math.abs(bw-1) < 1e-2) {
130
+ // By convention we set the hue as blue
131
+ hsl.h = 0.6666;
132
+ }
133
+
134
+ // Reduce/increase luminance most for blacks and whites
135
+ hsl.l = (hsl.l-0.5)*(1-0.05*bw)+0.5;
136
+ // Increase saturation most for blacks and whites
137
+ hsl.s = hsl.s + 0.8*bw;
138
+ // Rotate hue least for vivid colors
139
+ hsl.h = (hsl.h+0.1*(0.5+bw)*shift);
140
+
141
+ c.setHSL(hsl.h, hsl.s, hsl.l);
142
+
143
+ return c.getHex();
144
+ }
145
+
146
+ /** Produce a low-collision hash code from a string argument. The algorithm
147
+ * is inspired by Java's .hashCode() method.
148
+ *
149
+ * @param {String} arg The string to hash
150
+ *
151
+ * @return {int} Hash code
152
+ */
153
+ function hashCode(arg) {
154
+ arg = arg.toString(); // For sanity
155
+ let hash = 0;
156
+
157
+ for (let i = 0; i < arg.length; ++i) {
158
+ let char = arg.charCodeAt(i);
159
+ hash = ((hash<<5)-hash)+char;
160
+ hash = hash & hash; // Convert to 32bit integer
161
+ }
162
+
163
+ return hash;
164
+ }
165
+
166
+ export {
167
+ supercellGrid, supercellIndex, cellMatrix3, addStaticVar, shiftCpkColor, hashCode
168
+ }