@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.
- package/.eslintrc.json +16 -0
- package/.github/workflows/test-mocha.yml +30 -0
- package/.vscode/settings.json +4 -0
- package/LICENSE +21 -0
- package/README.html +1127 -0
- package/README.md +76 -0
- package/demo/demo.css +30 -0
- package/demo/index.html +76 -0
- package/demo/main.js +143 -0
- package/docs/.nojekyll +0 -0
- package/docs-tutorials/Events.md +57 -0
- package/docs-tutorials/Queries.md +50 -0
- package/fonts/Rubik/OFL.txt +93 -0
- package/fonts/Rubik/README.txt +77 -0
- package/fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf +0 -0
- package/fonts/Rubik/Rubik-VariableFont_wght.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Black.ttf +0 -0
- package/fonts/Rubik/static/Rubik-BlackItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Bold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-BoldItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-ExtraBold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-ExtraBoldItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Italic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Light.ttf +0 -0
- package/fonts/Rubik/static/Rubik-LightItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Medium.ttf +0 -0
- package/fonts/Rubik/static/Rubik-MediumItalic.ttf +0 -0
- package/fonts/Rubik/static/Rubik-Regular.ttf +0 -0
- package/fonts/Rubik/static/Rubik-SemiBold.ttf +0 -0
- package/fonts/Rubik/static/Rubik-SemiBoldItalic.ttf +0 -0
- package/index.html +25 -0
- package/index.js +11 -0
- package/jsconf.json +14 -0
- package/lib/assets/fonts/Rubik-Medium.fnt +297 -0
- package/lib/assets/fonts/Rubik-Medium.png +0 -0
- package/lib/assets/fonts/bmpfonts.in.js +16 -0
- package/lib/assets/fonts/bmpfonts.js +9 -0
- package/lib/assets/fonts/font.js +82 -0
- package/lib/assets/fonts/index.js +14 -0
- package/lib/assets/fonts/threebmfont.js +28 -0
- package/lib/data.js +125 -0
- package/lib/formats/cell.js +114 -0
- package/lib/formats/cif.js +22 -0
- package/lib/formats/magres.js +337 -0
- package/lib/formats/xyz.js +124 -0
- package/lib/loader.js +87 -0
- package/lib/model.js +2076 -0
- package/lib/modelview.js +382 -0
- package/lib/nmrdata.js +2898 -0
- package/lib/orbit.js +1233 -0
- package/lib/primitives/atoms.js +261 -0
- package/lib/primitives/cell.js +160 -0
- package/lib/primitives/dither.js +156 -0
- package/lib/primitives/ellipsoid.js +183 -0
- package/lib/primitives/geometries.js +20 -0
- package/lib/primitives/index.js +48 -0
- package/lib/primitives/isosurface.js +171 -0
- package/lib/primitives/shapes.js +100 -0
- package/lib/primitives/sprites.js +172 -0
- package/lib/query.js +158 -0
- package/lib/render.js +440 -0
- package/lib/selbox.js +361 -0
- package/lib/shaders/aura.frag +26 -0
- package/lib/shaders/aura.vert +37 -0
- package/lib/shaders/dither.frag +42 -0
- package/lib/shaders/dither.vert +8 -0
- package/lib/shaders/index.in.js +17 -0
- package/lib/shaders/index.js +25 -0
- package/lib/shaders/msdf300.frag +25 -0
- package/lib/shaders/msdf300.vert +45 -0
- package/lib/tensor.js +227 -0
- package/lib/utils.js +168 -0
- package/lib/visualizer.js +480 -0
- package/package.json +106 -0
- package/scripts/build-bundle.js +17 -0
- package/scripts/build-fonts.js +43 -0
- package/scripts/build-resources.js +46 -0
- package/scripts/plugins-shim.js +10 -0
- package/test/chemdata.js +69 -0
- package/test/data/CHA.cif +74 -0
- package/test/data/H2O.xyz +8 -0
- package/test/data/H2_bound.xyz +4 -0
- package/test/data/ethanol.cell +25 -0
- package/test/data/ethanol.magres +238 -0
- package/test/data/example_single.cif +789 -0
- package/test/data/frac.cell +8 -0
- package/test/data/org.cif +427 -0
- package/test/data/pyridine.xyz +13 -0
- package/test/data/si8.xyz +10 -0
- package/test/loader.js +107 -0
- package/test/model.js +368 -0
- package/test/query.js +135 -0
- package/test/tensor.js +133 -0
- package/test/test-html/examples.js +1485 -0
- package/test/test-html/index.html +33 -0
- package/test/test-html/index.js +279 -0
- package/tools/compile_colors.py +120 -0
- package/tools/compile_periodic.py +96 -0
- package/tools/ptable.json +497 -0
- package/tools/test +5844 -0
package/lib/model.js
ADDED
|
@@ -0,0 +1,2076 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Class holding the atomic models to be plotted
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import _ from 'lodash';
|
|
9
|
+
import * as mjs from 'mathjs';
|
|
10
|
+
import {
|
|
11
|
+
PeriodicTable as PeriodicTable
|
|
12
|
+
} from 'mendeleev';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Atoms as Atoms
|
|
16
|
+
} from 'crystcif-parse';
|
|
17
|
+
|
|
18
|
+
import * as utils from './utils.js';
|
|
19
|
+
import * as data from './data.js';
|
|
20
|
+
import {
|
|
21
|
+
QueryParser as QueryParser
|
|
22
|
+
} from './query.js';
|
|
23
|
+
import {
|
|
24
|
+
ModelView as ModelView
|
|
25
|
+
} from './modelview.js';
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const LABEL_HEIGHT = 0.04; // For now fixed, just a value that works
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** An 'image' of a single atom from a model. This represents a specific periodic copy of that atom (if applicable). */
|
|
32
|
+
class AtomImage {
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @class
|
|
36
|
+
* @param {Model} model The model from which the image is from
|
|
37
|
+
* @param {int} index Index of the atom in the model
|
|
38
|
+
* @param {Array} ijk Indices of the cell in which the image is located
|
|
39
|
+
*/
|
|
40
|
+
constructor(model, index, ijk) {
|
|
41
|
+
|
|
42
|
+
this._model = model;
|
|
43
|
+
this._index = index;
|
|
44
|
+
this._ijk = ijk || [0, 0, 0];
|
|
45
|
+
|
|
46
|
+
// String ID
|
|
47
|
+
this._id = this._index + '_' + _.join(this._ijk, '_');
|
|
48
|
+
// Integer index
|
|
49
|
+
this._img_index = utils.supercellIndex(index, this._ijk,
|
|
50
|
+
model.supercell, model.length);
|
|
51
|
+
|
|
52
|
+
this._xyz0 = model._positions[index];
|
|
53
|
+
|
|
54
|
+
this._bondsFrom = []; // BondImages of bonds for which this is atom1
|
|
55
|
+
this._bondsTo = []; // BondImages of bonds for which this is atom2
|
|
56
|
+
|
|
57
|
+
if (!model.periodic) {
|
|
58
|
+
this._fxyz0 = null;
|
|
59
|
+
this._fxyz = null;
|
|
60
|
+
this._xyz = this._xyz0;
|
|
61
|
+
} else {
|
|
62
|
+
this._fxyz0 = model._scaled_positions[index];
|
|
63
|
+
this._fxyz = [this._fxyz0[0] + ijk[0],
|
|
64
|
+
this._fxyz0[1] + ijk[1],
|
|
65
|
+
this._fxyz0[2] + ijk[2]
|
|
66
|
+
];
|
|
67
|
+
this._xyz = mjs.multiply(this._fxyz, model._cell);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this._isotope = null; // By default look up the model
|
|
71
|
+
|
|
72
|
+
// Visual properties
|
|
73
|
+
this._visible = false;
|
|
74
|
+
this._color = this.cpkColor;
|
|
75
|
+
this._uses_cpk = true;
|
|
76
|
+
this._base_radius = this.vdwRadius / 4.0;
|
|
77
|
+
this._scale = 1.0;
|
|
78
|
+
this._opacity = 1.0;
|
|
79
|
+
this._highlighted = false;
|
|
80
|
+
|
|
81
|
+
this._mesh = null; // Will be created when first requested
|
|
82
|
+
this._aura = null;
|
|
83
|
+
|
|
84
|
+
this._labels = {};
|
|
85
|
+
this._ellipsoids = {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Model this atom belongs to
|
|
90
|
+
* @readonly
|
|
91
|
+
* @type {Model}
|
|
92
|
+
*/
|
|
93
|
+
get model() {
|
|
94
|
+
return this._model;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Renderer used by this atom
|
|
99
|
+
* @readonly
|
|
100
|
+
* @type {Renderer}
|
|
101
|
+
*/
|
|
102
|
+
get renderer() {
|
|
103
|
+
var m = this.model;
|
|
104
|
+
|
|
105
|
+
if (m) {
|
|
106
|
+
return m._renderer;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Index of the atom
|
|
114
|
+
* @readonly
|
|
115
|
+
* @type {int}
|
|
116
|
+
*/
|
|
117
|
+
get index() {
|
|
118
|
+
return this._index;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* String ID of the image
|
|
123
|
+
* @readonly
|
|
124
|
+
* @type {String}
|
|
125
|
+
*/
|
|
126
|
+
get id() {
|
|
127
|
+
return this._id;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Index of this image
|
|
132
|
+
* @readonly
|
|
133
|
+
* @type {int}
|
|
134
|
+
*/
|
|
135
|
+
get imgIndex() {
|
|
136
|
+
return this._img_index;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Index of the species of this atom
|
|
141
|
+
* @readonly
|
|
142
|
+
* @type {int}
|
|
143
|
+
*/
|
|
144
|
+
get speciesIndex() {
|
|
145
|
+
return this._model._species_indices[this._index];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Symbol of this atom's element
|
|
150
|
+
* @readonly
|
|
151
|
+
* @type {String}
|
|
152
|
+
*/
|
|
153
|
+
get element() {
|
|
154
|
+
return this._model._elems[this._index];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Crystal site label of this atom
|
|
159
|
+
* @readonly
|
|
160
|
+
* @type {String}
|
|
161
|
+
*/
|
|
162
|
+
get crystLabel() {
|
|
163
|
+
return this._model._labels[this._index];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Periodic table information for this atom's element
|
|
168
|
+
* @readonly
|
|
169
|
+
* @type {Object}
|
|
170
|
+
*/
|
|
171
|
+
get elementData() {
|
|
172
|
+
return data.getElementData(this.element);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Information for this atom's isotope
|
|
177
|
+
* @readonly
|
|
178
|
+
* @type {Object}
|
|
179
|
+
*/
|
|
180
|
+
get isotopeData() {
|
|
181
|
+
let idata = this._isotope;
|
|
182
|
+
if (idata === null)
|
|
183
|
+
idata = this._model._isotopes[this._index];
|
|
184
|
+
return idata;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Atomic mass of this atom's isotope
|
|
189
|
+
* @type {int}
|
|
190
|
+
*/
|
|
191
|
+
get isotope() {
|
|
192
|
+
return this.isotopeData.A;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
set isotope(A) {
|
|
196
|
+
this._isotope = data.getIsotopeData(this.element, A);
|
|
197
|
+
if (this._isotope === null)
|
|
198
|
+
throw Error('Isotope does not exist for this element');
|
|
199
|
+
// Reset color
|
|
200
|
+
if (this._uses_cpk) {
|
|
201
|
+
this.color = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Atomic mass of the global isotope set as default for this atom's species
|
|
207
|
+
* @type {int}
|
|
208
|
+
*/
|
|
209
|
+
set isotopeGlobal(A) {
|
|
210
|
+
// Set the isotope for this atom in the model
|
|
211
|
+
let iso = data.getIsotopeData(this.element, A);
|
|
212
|
+
if (iso === null)
|
|
213
|
+
throw Error('Isotope does not exist for this element');
|
|
214
|
+
this._model._isotopes[this._index] = iso;
|
|
215
|
+
// Reset color
|
|
216
|
+
if (this._uses_cpk) {
|
|
217
|
+
this.color = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Atomic number of element
|
|
223
|
+
* @readonly
|
|
224
|
+
* @type {int}
|
|
225
|
+
*/
|
|
226
|
+
get number() {
|
|
227
|
+
var el = PeriodicTable.getElement(this.element);
|
|
228
|
+
return (el ? el.number : 0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Hex integer code of the conventional CPK color used for this element
|
|
233
|
+
* (altered in case of non-standard isotopes)
|
|
234
|
+
* @readonly
|
|
235
|
+
* @type {int}
|
|
236
|
+
*/
|
|
237
|
+
get cpkColor() {
|
|
238
|
+
return data.getCpkColor(this.element, this.isotope);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Van dew Waals radius for this element
|
|
243
|
+
* @readonly
|
|
244
|
+
* @type {float}
|
|
245
|
+
*/
|
|
246
|
+
get vdwRadius() {
|
|
247
|
+
return data.getVdwRadius(this.element);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Bonds from this atom
|
|
252
|
+
* @readonly
|
|
253
|
+
* @type {BondImage[]}
|
|
254
|
+
*/
|
|
255
|
+
get bondsFrom() {
|
|
256
|
+
return Array.from(this._bondsFrom);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Bonds to this atom
|
|
261
|
+
* @readonly
|
|
262
|
+
* @type {BondImage[]}
|
|
263
|
+
*/
|
|
264
|
+
get bondsTo() {
|
|
265
|
+
return Array.from(this._bondsTo);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* All bonds connected to this atom
|
|
270
|
+
* @readonly
|
|
271
|
+
* @type {BondImage[]}
|
|
272
|
+
*/
|
|
273
|
+
get bonds() {
|
|
274
|
+
return _.concat(this._bondsFrom, this._bondsTo);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* All atoms bonded to this atom
|
|
279
|
+
* @readonly
|
|
280
|
+
* @type {AtomImage[]}
|
|
281
|
+
*/
|
|
282
|
+
get bondedAtoms() {
|
|
283
|
+
return _.concat(_.map(this._bondsFrom, function(b) {
|
|
284
|
+
return b.atom2;
|
|
285
|
+
}),
|
|
286
|
+
_.map(this._bondsTo, function(b) {
|
|
287
|
+
return b.atom1;
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Cell indices of this atom image
|
|
293
|
+
* @readonly
|
|
294
|
+
* @type {int[]}
|
|
295
|
+
*/
|
|
296
|
+
get ijk() {
|
|
297
|
+
return Array.from(this._ijk);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Position of this atom's original
|
|
302
|
+
* @readonly
|
|
303
|
+
* @type {float[]}
|
|
304
|
+
*/
|
|
305
|
+
get xyz0() {
|
|
306
|
+
return Array.from(this._xyz0);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Position of this atom image
|
|
311
|
+
* @readonly
|
|
312
|
+
* @type {float[]}
|
|
313
|
+
*/
|
|
314
|
+
get xyz() {
|
|
315
|
+
return Array.from(this._xyz);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Fractional coordinates of this atom's original
|
|
320
|
+
* @readonly
|
|
321
|
+
* @type {float[]}
|
|
322
|
+
*/
|
|
323
|
+
get fxyz0() {
|
|
324
|
+
return Array.from(this._fxyz0);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Fractional coordinates of this atom image
|
|
329
|
+
* @readonly
|
|
330
|
+
* @type {float[]}
|
|
331
|
+
*/
|
|
332
|
+
get fxyz() {
|
|
333
|
+
return Array.from(this._fxyz);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Index of the molecule this atom belongs to
|
|
338
|
+
* @readonly
|
|
339
|
+
* @type {int}
|
|
340
|
+
*/
|
|
341
|
+
get moleculeIndex() {
|
|
342
|
+
return this._model._molinds[this._index];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Mesh corresponding to this atom image
|
|
347
|
+
* @readonly
|
|
348
|
+
* @type {AtomMesh}
|
|
349
|
+
*/
|
|
350
|
+
get mesh() {
|
|
351
|
+
var r = this.renderer;
|
|
352
|
+
if (!this._mesh && r) {
|
|
353
|
+
this._mesh = new r.Primitives.AtomMesh(this._xyz, this.radius, this._color);
|
|
354
|
+
this._mesh.image = this;
|
|
355
|
+
}
|
|
356
|
+
return this._mesh;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Aura used to highlight this atom image
|
|
361
|
+
* @readonly
|
|
362
|
+
* @type {AuraMesh}
|
|
363
|
+
*/
|
|
364
|
+
get aura() {
|
|
365
|
+
var r = this.renderer;
|
|
366
|
+
if (!this._aura && r) {
|
|
367
|
+
this._aura = new r.Primitives.AuraMesh({
|
|
368
|
+
radius: this.radius,
|
|
369
|
+
scale: 0.02
|
|
370
|
+
});
|
|
371
|
+
this.mesh.add(this._aura);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return this._aura;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Get and set graphical properties
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Whether the atom is visible
|
|
381
|
+
* @type {bool}
|
|
382
|
+
*/
|
|
383
|
+
get visible() {
|
|
384
|
+
return this._visible;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
set visible(v) {
|
|
388
|
+
|
|
389
|
+
this._visible = v;
|
|
390
|
+
|
|
391
|
+
var mesh = this.mesh;
|
|
392
|
+
|
|
393
|
+
if (v) {
|
|
394
|
+
this.renderer.add(mesh, 'model');
|
|
395
|
+
} else {
|
|
396
|
+
this.renderer.remove(mesh, 'model');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Update aura visibility
|
|
400
|
+
this.highlighted = this._highlighted;
|
|
401
|
+
|
|
402
|
+
// Update connected bonds' visibility
|
|
403
|
+
for (let i = 0; i < this._bondsFrom.length; ++i) {
|
|
404
|
+
let b = this._bondsFrom[i];
|
|
405
|
+
b.visible = b._visible;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
for (let i = 0; i < this._bondsTo.length; ++i) {
|
|
409
|
+
let b = this._bondsTo[i];
|
|
410
|
+
b.visible = b._visible;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Starting radius of the atom
|
|
416
|
+
* @type {float}
|
|
417
|
+
*/
|
|
418
|
+
get baseRadius() {
|
|
419
|
+
return this._base_radius;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
set baseRadius(r) {
|
|
423
|
+
if (r == null) { // Default value
|
|
424
|
+
r = this.vdwRadius / 4.0;
|
|
425
|
+
}
|
|
426
|
+
this._base_radius = r;
|
|
427
|
+
var mesh = this.mesh;
|
|
428
|
+
mesh.atom_radius = this.radius;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Scale of the atom
|
|
433
|
+
* @type {float}
|
|
434
|
+
*/
|
|
435
|
+
get scale() {
|
|
436
|
+
return this._scale;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
set scale(s) {
|
|
440
|
+
if (s == null) {
|
|
441
|
+
s = 1;
|
|
442
|
+
}
|
|
443
|
+
this._scale = s;
|
|
444
|
+
var mesh = this.mesh;
|
|
445
|
+
mesh.atom_radius = this.radius;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Final radius of the atom (starting radius * scale)
|
|
450
|
+
* @type {float}
|
|
451
|
+
*/
|
|
452
|
+
get radius() {
|
|
453
|
+
return this._scale * this._base_radius;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
set radius(r) {
|
|
457
|
+
if (r == null) {
|
|
458
|
+
r == this.baseRadius;
|
|
459
|
+
}
|
|
460
|
+
this.scale = r / this._base_radius;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Color of the atom
|
|
465
|
+
* @type {int}
|
|
466
|
+
*/
|
|
467
|
+
get color() {
|
|
468
|
+
return this._color;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
set color(c) {
|
|
472
|
+
if (c === null) {
|
|
473
|
+
c = this.cpkColor;
|
|
474
|
+
this._uses_cpk = true;
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
this._uses_cpk = false;
|
|
478
|
+
}
|
|
479
|
+
this._color = c;
|
|
480
|
+
var mesh = this.mesh;
|
|
481
|
+
if (mesh) {
|
|
482
|
+
mesh.atom_color = c;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
_.map(this._bondsFrom, function(b) {
|
|
486
|
+
b.color1 = c;
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
_.map(this._bondsTo, function(b) {
|
|
490
|
+
b.color2 = c;
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Opacity of the atom
|
|
497
|
+
* @type {float}
|
|
498
|
+
*/
|
|
499
|
+
get opacity() {
|
|
500
|
+
return this._opacity;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
set opacity(o) {
|
|
504
|
+
if (o == null) {
|
|
505
|
+
o = 1;
|
|
506
|
+
}
|
|
507
|
+
this._opacity = o;
|
|
508
|
+
var mesh = this.mesh;
|
|
509
|
+
mesh.atom_opacity = o;
|
|
510
|
+
|
|
511
|
+
_.map(this._bondsFrom, function(b) {
|
|
512
|
+
b.opacity1 = o;
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
_.map(this._bondsTo, function(b) {
|
|
516
|
+
b.opacity2 = o;
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Whether the atom is highlighted
|
|
522
|
+
* @type {bool}
|
|
523
|
+
*/
|
|
524
|
+
get highlighted() {
|
|
525
|
+
return this._highlighted;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
set highlighted(h) {
|
|
529
|
+
if (h == null) {
|
|
530
|
+
h = false;
|
|
531
|
+
}
|
|
532
|
+
this._highlighted = h;
|
|
533
|
+
var aura = this.aura;
|
|
534
|
+
if (h && this._visible) {
|
|
535
|
+
aura.visible = true;
|
|
536
|
+
} else {
|
|
537
|
+
aura.visible = false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Add a text label to the atom.
|
|
543
|
+
*
|
|
544
|
+
* @param {String} text Content of the label
|
|
545
|
+
* @param {String} name Name to use to refer to the label (necessary to overwrite/erase later)
|
|
546
|
+
* @param {Object} parameters Dictionary of other options (e.g. font family, text color, etc. See TextSprite)
|
|
547
|
+
*/
|
|
548
|
+
addLabel(text, name, parameters = {}) {
|
|
549
|
+
this.removeLabel(name); // Precautionary
|
|
550
|
+
|
|
551
|
+
var defaults = {
|
|
552
|
+
faceCamera: true,
|
|
553
|
+
fixScale: true,
|
|
554
|
+
shift: [1.0*this.radius, 0, 0], // This just works well
|
|
555
|
+
height: LABEL_HEIGHT,
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
parameters = _.merge(defaults, parameters);
|
|
559
|
+
parameters.position = [0, 0, 0]; // This is not customizable
|
|
560
|
+
|
|
561
|
+
var r = this.renderer;
|
|
562
|
+
if (r) {
|
|
563
|
+
var label = new r.Primitives.TextSprite(text, parameters);
|
|
564
|
+
this._labels[name] = label;
|
|
565
|
+
this.mesh.add(label);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Remove the label of a given name
|
|
571
|
+
*
|
|
572
|
+
* @param {String} name Name of the label
|
|
573
|
+
*/
|
|
574
|
+
removeLabel(name) {
|
|
575
|
+
|
|
576
|
+
let l = this._labels[name];
|
|
577
|
+
if (l)
|
|
578
|
+
this._mesh.remove(l);
|
|
579
|
+
delete this._labels[name];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Retrieve or set a label's properties
|
|
584
|
+
*
|
|
585
|
+
* @param {String} name Name of the label
|
|
586
|
+
* @param {String} property Property to set
|
|
587
|
+
* @param {?} value Value to set. If omitted, returns the current
|
|
588
|
+
* value instead.
|
|
589
|
+
*/
|
|
590
|
+
labelProperty(name, property, value = null) {
|
|
591
|
+
if (value) {
|
|
592
|
+
this._labels[name][property] = value;
|
|
593
|
+
} else {
|
|
594
|
+
return this._labels[name][property];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Add an ellipsoid to the atom.
|
|
600
|
+
*
|
|
601
|
+
* @param {TensorData | Object | Array} data The data to base the
|
|
602
|
+
* ellipsoid on. Can be:
|
|
603
|
+
* - a TensorData object;
|
|
604
|
+
* - an Object with 'eigenvalues'
|
|
605
|
+
* and 'eigenvectors' members
|
|
606
|
+
* - an Array of the form
|
|
607
|
+
* [eigenvalues, eigenvectors]
|
|
608
|
+
* @param {String} name Name of the ellipsoid
|
|
609
|
+
* @param {Object} parameters Additional options to
|
|
610
|
+
* pass (see EllipsoidMesh)
|
|
611
|
+
*/
|
|
612
|
+
addEllipsoid(data, name, parameters = {}) {
|
|
613
|
+
this.removeEllipsoid(name);
|
|
614
|
+
|
|
615
|
+
parameters = _.clone(parameters); // Avoid editing the reference object
|
|
616
|
+
|
|
617
|
+
if (data instanceof Array) {
|
|
618
|
+
parameters.eigenvalues = data[0];
|
|
619
|
+
parameters.eigenvectors = data[1];
|
|
620
|
+
} else {
|
|
621
|
+
parameters.eigenvalues = data.eigenvalues;
|
|
622
|
+
parameters.eigenvectors = data.eigenvectors;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (parameters.ditherSeed == null) {
|
|
626
|
+
// As long as it's consistent for a given atom, the actual value is irrelevant
|
|
627
|
+
let seed = utils.hashCode(this._fxyz + name);
|
|
628
|
+
parameters.ditherSeed = seed/4294967295.0; // Reduce to ]0.5,-0.5]
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
var r = this.renderer;
|
|
635
|
+
if (r) {
|
|
636
|
+
var ellips = new r.Primitives.EllipsoidMesh(parameters);
|
|
637
|
+
this._ellipsoids[name] = ellips;
|
|
638
|
+
this.mesh.add(ellips);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Remove the ellipsoid with a given name
|
|
644
|
+
*
|
|
645
|
+
* @param {String} name Name of the ellipsoid
|
|
646
|
+
*/
|
|
647
|
+
removeEllipsoid(name) {
|
|
648
|
+
let l = this._ellipsoids[name];
|
|
649
|
+
if (l)
|
|
650
|
+
this._mesh.remove(l);
|
|
651
|
+
delete this._ellipsoids[name];
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Retrieve or set an ellipsoid's properties
|
|
656
|
+
*
|
|
657
|
+
* @param {String} name Name of the ellipsoid
|
|
658
|
+
* @param {String} property Property to set
|
|
659
|
+
* @param {?} value Value to set. If omitted, returns the current
|
|
660
|
+
* value instead.
|
|
661
|
+
*/
|
|
662
|
+
ellipsoidProperty(name, property, value = null) {
|
|
663
|
+
if (value) {
|
|
664
|
+
this._ellipsoids[name][property] = value;
|
|
665
|
+
} else {
|
|
666
|
+
return this._ellipsoids[name][property];
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Get the value for one array for this image
|
|
672
|
+
* @param {String} name Name of the array
|
|
673
|
+
*
|
|
674
|
+
* @return {*} Value of the array for this atom
|
|
675
|
+
*/
|
|
676
|
+
getArrayValue(name) {
|
|
677
|
+
return this._model.getArray(name)[this._index];
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Check equality with another image
|
|
681
|
+
equals(ai) {
|
|
682
|
+
return (this._model == ai._model &&
|
|
683
|
+
this._index == ai._index &&
|
|
684
|
+
_.isEqual(this._ijk, ai._ijk));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Return a copy, possibly shifted to a different cell
|
|
688
|
+
copy(shift = [0, 0, 0]) {
|
|
689
|
+
return new AtomImage(this._model,
|
|
690
|
+
this._index,
|
|
691
|
+
mjs.add(this._ijk, shift));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** An 'image' of a single bond in the model. This represents the connection
|
|
696
|
+
between two specific AtomImages */
|
|
697
|
+
class BondImage {
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* @class
|
|
702
|
+
* @param {Model} model The model from which the image is from
|
|
703
|
+
* @param {AtomImage} im1 AtomImage from which the bond starts
|
|
704
|
+
* @param {AtomImage} im2 AtomImage to which the bond ends
|
|
705
|
+
*/
|
|
706
|
+
constructor(model, im1, im2) {
|
|
707
|
+
|
|
708
|
+
this._model = model;
|
|
709
|
+
this._im1 = im1;
|
|
710
|
+
this._im2 = im2;
|
|
711
|
+
|
|
712
|
+
this._im1._bondsFrom.push(this);
|
|
713
|
+
this._im2._bondsTo.push(this);
|
|
714
|
+
|
|
715
|
+
this._length = mjs.distance(this._im1.xyz, this._im2.xyz);
|
|
716
|
+
|
|
717
|
+
this._key = this._im1.imgIndex + '_' + this._im2.imgIndex;
|
|
718
|
+
|
|
719
|
+
// Visual properties
|
|
720
|
+
this._visible = true;
|
|
721
|
+
this._radius = 0.2;
|
|
722
|
+
this._opacity = 1.0;
|
|
723
|
+
|
|
724
|
+
this._mesh = null; // Created on first request
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Model this bond belongs to
|
|
730
|
+
* @readonly
|
|
731
|
+
* @type {Model}
|
|
732
|
+
*/
|
|
733
|
+
get model() {
|
|
734
|
+
return this._model;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Renderer used by this bond
|
|
739
|
+
* @readonly
|
|
740
|
+
* @type {Renderer}
|
|
741
|
+
*/
|
|
742
|
+
get renderer() {
|
|
743
|
+
var m = this.model;
|
|
744
|
+
if (m) {
|
|
745
|
+
return m._renderer;
|
|
746
|
+
}
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* First atom connected to this bond
|
|
752
|
+
* @readonly
|
|
753
|
+
* @type {AtomImage}
|
|
754
|
+
*/
|
|
755
|
+
get atom1() {
|
|
756
|
+
return this._im1;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Second atom connected to this bond
|
|
761
|
+
* @readonly
|
|
762
|
+
* @type {AtomImage}
|
|
763
|
+
*/
|
|
764
|
+
get atom2() {
|
|
765
|
+
return this._im2;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* A unique string key used to quickly reference the bond
|
|
770
|
+
* @readonly
|
|
771
|
+
* @type {String}
|
|
772
|
+
*/
|
|
773
|
+
get key() {
|
|
774
|
+
// Used in dictionary for quick reference
|
|
775
|
+
return this._key;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Bond length in Angstroms
|
|
780
|
+
* @readonly
|
|
781
|
+
* @type {float}
|
|
782
|
+
*/
|
|
783
|
+
get length() {
|
|
784
|
+
return this._length;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Mesh corresponding to this bond image
|
|
789
|
+
* @readonly
|
|
790
|
+
* @type {AtomMesh}
|
|
791
|
+
*/
|
|
792
|
+
get mesh() {
|
|
793
|
+
var r = this.renderer;
|
|
794
|
+
if (!this._mesh && r) {
|
|
795
|
+
this._mesh = new r.Primitives.BondMesh(this.atom1.xyz, this.atom2.xyz,
|
|
796
|
+
this._radius,
|
|
797
|
+
this.atom1.color, this.atom2.color);
|
|
798
|
+
}
|
|
799
|
+
return this._mesh;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Radius of the bond
|
|
804
|
+
* @type {float}
|
|
805
|
+
*/
|
|
806
|
+
get radius() {
|
|
807
|
+
return this._radius;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
set radius(r) {
|
|
811
|
+
if (r == null) {
|
|
812
|
+
r = 0.2;
|
|
813
|
+
}
|
|
814
|
+
this._radius = r;
|
|
815
|
+
var mesh = this.mesh;
|
|
816
|
+
if (mesh) {
|
|
817
|
+
mesh.bond_radius = r;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* First color of the bond
|
|
823
|
+
* @type {int}
|
|
824
|
+
*/
|
|
825
|
+
set color1(c) {
|
|
826
|
+
if (c == null) {
|
|
827
|
+
c = this._im1.color;
|
|
828
|
+
}
|
|
829
|
+
var mesh = this.mesh;
|
|
830
|
+
if (mesh) {
|
|
831
|
+
mesh.bond_color_1 = c;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Second color of the bond
|
|
837
|
+
* @type {int}
|
|
838
|
+
*/
|
|
839
|
+
set color2(c) {
|
|
840
|
+
if (c == null) {
|
|
841
|
+
c = this._im2.color;
|
|
842
|
+
}
|
|
843
|
+
var mesh = this.mesh;
|
|
844
|
+
if (mesh) {
|
|
845
|
+
mesh.bond_color_2 = c;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* First opacity of the bond
|
|
851
|
+
* @type {float}
|
|
852
|
+
*/
|
|
853
|
+
set opacity1(o) {
|
|
854
|
+
if (o == null) {
|
|
855
|
+
o = this._im1.opacity;
|
|
856
|
+
}
|
|
857
|
+
var mesh = this.mesh;
|
|
858
|
+
if (mesh) {
|
|
859
|
+
mesh.bond_opacity_1 = o;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Second opacity of the bond
|
|
865
|
+
* @type {float}
|
|
866
|
+
*/
|
|
867
|
+
set opacity2(o) {
|
|
868
|
+
if (o == null) {
|
|
869
|
+
o = this._im2.opacity;
|
|
870
|
+
}
|
|
871
|
+
var mesh = this.mesh;
|
|
872
|
+
if (mesh) {
|
|
873
|
+
mesh.bond_opacity_2 = o;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Whether the bond is visible
|
|
879
|
+
* @type {bool}
|
|
880
|
+
*/
|
|
881
|
+
get visible() {
|
|
882
|
+
return this._visible;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
set visible(v) {
|
|
886
|
+
|
|
887
|
+
this._visible = v;
|
|
888
|
+
v = v && this.atom1.visible && this.atom2.visible;
|
|
889
|
+
|
|
890
|
+
var mesh = this.mesh;
|
|
891
|
+
if (v) {
|
|
892
|
+
this.renderer.add(mesh, 'model');
|
|
893
|
+
} else {
|
|
894
|
+
this.renderer.remove(mesh, 'model');
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
class Model {
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* An object containing an Atomic structure and taking care of its periodic
|
|
905
|
+
* nature, allowing querying and selection, and so on.
|
|
906
|
+
* @class
|
|
907
|
+
* @param {crystcif.Atoms} atoms Atomic structure, in crystcif's Atoms format
|
|
908
|
+
* @param {Object} parameters Additional options:
|
|
909
|
+
*
|
|
910
|
+
* - `supercell`
|
|
911
|
+
* - `molecularCrystal` (if true, load full molecules in central unit cell)
|
|
912
|
+
* - `useNMRActiveIsotopes` (if true, all isotopes are set by default to the most common
|
|
913
|
+
* one with non-zero spin)
|
|
914
|
+
* - `vdwScaling` (scale van der Waals radii by a constant factor)
|
|
915
|
+
* - `vdwElementScaling` (table of per-element factors to scale VdW radii by)
|
|
916
|
+
*/
|
|
917
|
+
constructor(atoms, parameters = {}) {
|
|
918
|
+
|
|
919
|
+
var defaults = {
|
|
920
|
+
supercell: [1, 1, 1],
|
|
921
|
+
molecularCrystal: false,
|
|
922
|
+
useNMRActiveIsotopes: false,
|
|
923
|
+
vdwScaling: 1.0,
|
|
924
|
+
vdwElementScaling: {}
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
parameters = _.merge(defaults, parameters);
|
|
928
|
+
|
|
929
|
+
this._vdwScaling = parameters.vdwScaling;
|
|
930
|
+
this._vdwElementScaling = parameters.vdwElementScaling;
|
|
931
|
+
|
|
932
|
+
const initMolecules = ((atoms, supercell) => {
|
|
933
|
+
|
|
934
|
+
if (!(atoms instanceof Atoms)) {
|
|
935
|
+
throw new Error('Model must be initialised with a loaded Atoms object');
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
this._atoms_base = atoms;
|
|
939
|
+
this._data = {};
|
|
940
|
+
|
|
941
|
+
/* Load the positions, cell, and other key data
|
|
942
|
+
Important: to save memory, we're simply storing references.
|
|
943
|
+
These are NOT to be changed!
|
|
944
|
+
*/
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
this._elems = this._atoms_base._arrays['symbols'];
|
|
948
|
+
this._isotopes = this._elems.map((el) => {
|
|
949
|
+
const iso = parameters.useNMRActiveIsotopes? 'nmr' : null;
|
|
950
|
+
let isodata = data.getIsotopeData(el, iso);
|
|
951
|
+
if (isodata === null) {
|
|
952
|
+
// No NMR active isotope?
|
|
953
|
+
isodata = data.getIsotopeData(el);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return isodata;
|
|
957
|
+
});
|
|
958
|
+
this._nums = this._atoms_base._arrays['numbers'];
|
|
959
|
+
this._positions = this._atoms_base._arrays['positions'];
|
|
960
|
+
this._cell = this._atoms_base._cell;
|
|
961
|
+
this._pbc = this._atoms_base._pbc;
|
|
962
|
+
this._periodic = !this._pbc.includes(false);
|
|
963
|
+
this._inv_cell = this._atoms_base._inv_cell;
|
|
964
|
+
this._supercell = [1, 1, 1];
|
|
965
|
+
this._supercell_grid = [
|
|
966
|
+
[0, 0, 0]
|
|
967
|
+
];
|
|
968
|
+
|
|
969
|
+
// Species indices (used for labels)
|
|
970
|
+
let sp_count = {};
|
|
971
|
+
this._species_indices = [];
|
|
972
|
+
this._species_indices = this._elems.map((s, i) => {
|
|
973
|
+
let c = sp_count[s];
|
|
974
|
+
c = c? c : 0;
|
|
975
|
+
sp_count[s] = c+1;
|
|
976
|
+
return c;
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
// Crystallographic labels
|
|
980
|
+
if ('labels' in this._atoms_base._arrays) {
|
|
981
|
+
this._labels = this._atoms_base._arrays['labels'];
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
// Build them
|
|
985
|
+
this._labels = [];
|
|
986
|
+
for (let i = 0; i < this._elems.length; ++i) {
|
|
987
|
+
this._labels.push(this._elems[i] + '_' + (this._species_indices[i]+1));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
if (this._periodic) {
|
|
993
|
+
// R matrix: indispensable for calculations of periodic distances
|
|
994
|
+
this._r_matrix = mjs.multiply(this._cell, mjs.transpose(this._cell));
|
|
995
|
+
var ediag = mjs.eigs(this._r_matrix);
|
|
996
|
+
// Sort by eigenvalue
|
|
997
|
+
ediag = _.zip(ediag.values, ediag.vectors);
|
|
998
|
+
ediag = _.sortBy(ediag, function(x) {
|
|
999
|
+
return x[0];
|
|
1000
|
+
});
|
|
1001
|
+
ediag = _.unzip(ediag);
|
|
1002
|
+
|
|
1003
|
+
this._r_diag = {
|
|
1004
|
+
values: ediag[0],
|
|
1005
|
+
vectors: ediag[1],
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
this._supercell = supercell; // Default
|
|
1009
|
+
this._supercell_grid = utils.supercellGrid(supercell);
|
|
1010
|
+
this._scaled_positions = this._atoms_base.get_scaled_positions();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Compile all images for this supercell
|
|
1014
|
+
this._atom_images = this._atomImages();
|
|
1015
|
+
|
|
1016
|
+
this._computeBonds();
|
|
1017
|
+
this._computeMolecules();
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
}).bind(this);
|
|
1021
|
+
|
|
1022
|
+
initMolecules(atoms, parameters.supercell);
|
|
1023
|
+
|
|
1024
|
+
if (parameters.molecularCrystal) {
|
|
1025
|
+
atoms = _.cloneDeep(atoms);
|
|
1026
|
+
var pos = this.positions;
|
|
1027
|
+
for (let i = 0; i < this.length; ++i) {
|
|
1028
|
+
let mol_i = this._molinds[i];
|
|
1029
|
+
let mol = this._molecules[mol_i];
|
|
1030
|
+
for (let j = 0; j < mol.length; ++j) {
|
|
1031
|
+
var a = mol[j];
|
|
1032
|
+
if (a.index == i) {
|
|
1033
|
+
pos[i] = mjs.add(pos[i], this.fracToAbs(a.cell));
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
atoms.set_array('positions', pos);
|
|
1039
|
+
initMolecules(atoms, parameters.supercell);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
this._primitives = {}; // Any additional primitives drawn on this model
|
|
1043
|
+
|
|
1044
|
+
this._bond_images = this._bondImages();
|
|
1045
|
+
|
|
1046
|
+
// A special ModelView for convenience
|
|
1047
|
+
this._all = new ModelView(this, _.range(this._atom_images.length));
|
|
1048
|
+
|
|
1049
|
+
// Parser for queries
|
|
1050
|
+
this._qparse = new QueryParser({
|
|
1051
|
+
'all': this._queryAll,
|
|
1052
|
+
'indices': this._queryIndices,
|
|
1053
|
+
'elements': this._queryElements,
|
|
1054
|
+
'cell': this._queryCell,
|
|
1055
|
+
'box': this._queryBox,
|
|
1056
|
+
'sphere': this._querySphere,
|
|
1057
|
+
'bonded': this._queryBonded,
|
|
1058
|
+
'molecule': this._queryMolecule,
|
|
1059
|
+
}, this);
|
|
1060
|
+
|
|
1061
|
+
// By default no rendering
|
|
1062
|
+
this.renderer = null;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Using the .get_ methods of _atoms guarantees these are copies,
|
|
1066
|
+
// not pointers to the real thing
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Number of atoms in this model's original cell
|
|
1070
|
+
* @readonly
|
|
1071
|
+
* @type {int}
|
|
1072
|
+
*/
|
|
1073
|
+
get length() {
|
|
1074
|
+
return this._atoms_base.length();
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Chemical symbols in this model's original cell
|
|
1079
|
+
* @readonly
|
|
1080
|
+
* @type {String[]}
|
|
1081
|
+
*/
|
|
1082
|
+
get symbols() {
|
|
1083
|
+
return this._atoms_base.get_chemical_symbols();
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Atomic numbers in this model's original cell
|
|
1088
|
+
* @readonly
|
|
1089
|
+
* @type {int[]}
|
|
1090
|
+
*/
|
|
1091
|
+
get numbers() {
|
|
1092
|
+
return this._atoms_base.get_atomic_numbers();
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Coordinates of the atoms in this model's original cell
|
|
1097
|
+
* @readonly
|
|
1098
|
+
* @type {Array[]}
|
|
1099
|
+
*/
|
|
1100
|
+
get positions() {
|
|
1101
|
+
return this._atoms_base.get_positions();
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Fractional coordinates of the atoms in this model's original cell
|
|
1106
|
+
* @readonly
|
|
1107
|
+
* @type {Array[]}
|
|
1108
|
+
*/
|
|
1109
|
+
get scaledPositions() {
|
|
1110
|
+
return this._atoms_base.get_scaled_positions();
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Unit cell of the model's original cell
|
|
1115
|
+
* @readonly
|
|
1116
|
+
* @type {Array[]}
|
|
1117
|
+
*/
|
|
1118
|
+
get cell() {
|
|
1119
|
+
return this._atoms_base.get_cell();
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Periodic boundary conditions
|
|
1124
|
+
* @readonly
|
|
1125
|
+
* @type {bool[]}
|
|
1126
|
+
*/
|
|
1127
|
+
get pbc() {
|
|
1128
|
+
return this._atoms_base.get_pbc();
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Additional information from the model's original cell
|
|
1133
|
+
* @readonly
|
|
1134
|
+
* @type {Object}
|
|
1135
|
+
*/
|
|
1136
|
+
get info() {
|
|
1137
|
+
return this._atoms_base.info;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Whether this model is periodic in all three directions of space
|
|
1142
|
+
* @readonly
|
|
1143
|
+
* @type {bool}
|
|
1144
|
+
*/
|
|
1145
|
+
get periodic() {
|
|
1146
|
+
return this._periodic;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* Indices of each atom by their species (e.g. C1, C2, H1, C3, H2, etc.)
|
|
1151
|
+
* @readonly
|
|
1152
|
+
* @type {int[]}
|
|
1153
|
+
*/
|
|
1154
|
+
get speciesIndices() {
|
|
1155
|
+
return Array.from(this._species_indices);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Crystallographic labels of each atom
|
|
1160
|
+
* @readonly
|
|
1161
|
+
* @type {String[]}
|
|
1162
|
+
*/
|
|
1163
|
+
get crystalLabels() {
|
|
1164
|
+
return Array.from(this._labels);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Shape of the supercell for this model
|
|
1169
|
+
* @readonly
|
|
1170
|
+
* @type {int[]}
|
|
1171
|
+
*/
|
|
1172
|
+
get supercell() {
|
|
1173
|
+
return Array.from(this._supercell);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* Full grid of origin coordinates of the cells making up the supercell
|
|
1178
|
+
* @readonly
|
|
1179
|
+
* @type {Array[]}
|
|
1180
|
+
*/
|
|
1181
|
+
get supercellGrid() {
|
|
1182
|
+
return JSON.parse(JSON.stringify(this._supercell_grid));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Atom images in this model
|
|
1187
|
+
* @readonly
|
|
1188
|
+
* @type {AtomImage[]}
|
|
1189
|
+
*/
|
|
1190
|
+
get atoms() {
|
|
1191
|
+
return Array.from(this._atom_images);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* ModelView containing all the atoms of the image
|
|
1196
|
+
* @readonly
|
|
1197
|
+
* @type {ModelView}
|
|
1198
|
+
*/
|
|
1199
|
+
get all() {
|
|
1200
|
+
return this._all;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Graphical object representing the unit cell's axes
|
|
1205
|
+
* @readonly
|
|
1206
|
+
* @type {AxesMesh}
|
|
1207
|
+
*/
|
|
1208
|
+
get axes() {
|
|
1209
|
+
return this._cartesian_axes;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Graphical object representing the unit cell's box
|
|
1214
|
+
* @readonly
|
|
1215
|
+
* @type {BoxMesh}
|
|
1216
|
+
*/
|
|
1217
|
+
get box() {
|
|
1218
|
+
return this._cartesian_box;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Global scaling factor for Van der Waals radii
|
|
1223
|
+
* @readonly
|
|
1224
|
+
* @type {float}
|
|
1225
|
+
*/
|
|
1226
|
+
get vdwScaling() {
|
|
1227
|
+
return this._vdwScaling;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* Table of scaling factors by element for Van der Waals radii
|
|
1232
|
+
* @readonly
|
|
1233
|
+
* @type {Object}
|
|
1234
|
+
*/
|
|
1235
|
+
get vdwElementScaling() {
|
|
1236
|
+
return JSON.parse(JSON.stringify(this._vdwElementScaling));
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Renderer used for this model's graphics
|
|
1241
|
+
* @type {Renderer}
|
|
1242
|
+
*/
|
|
1243
|
+
set renderer(r) {
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
if (r) {
|
|
1247
|
+
this._renderer = r;
|
|
1248
|
+
if (this.periodic) {
|
|
1249
|
+
// Create axes and box
|
|
1250
|
+
if (!this._cartesian_box) {
|
|
1251
|
+
this._cartesian_box = new r.Primitives.BoxMesh(this.cell);
|
|
1252
|
+
}
|
|
1253
|
+
if (!this._cartesian_axes) {
|
|
1254
|
+
this._cartesian_axes = new r.Primitives.AxesMesh(this.cell, {
|
|
1255
|
+
linewidth: 1.5
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
r.add(this._cartesian_box);
|
|
1259
|
+
r.add(this._cartesian_axes);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// And the primitives
|
|
1263
|
+
for (var name in this._primitives) {
|
|
1264
|
+
var p = this._primitives[name];
|
|
1265
|
+
r.add(p);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
} else {
|
|
1269
|
+
|
|
1270
|
+
if (this._renderer)
|
|
1271
|
+
this._renderer.clear();
|
|
1272
|
+
|
|
1273
|
+
this._renderer = null;
|
|
1274
|
+
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Set and get arrays on the underlying Atoms object
|
|
1279
|
+
/**
|
|
1280
|
+
* Set an array for the underlying Atoms object
|
|
1281
|
+
* @param {String} name Name of the array to use
|
|
1282
|
+
* @param {Array} arr Array to store
|
|
1283
|
+
*/
|
|
1284
|
+
setArray(name, arr) {
|
|
1285
|
+
this._atoms_base.set_array(name, arr);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Retrieve an array from the underlying Atoms object
|
|
1290
|
+
* @param {String} name Name of the array to retrieve
|
|
1291
|
+
* @return {Array} Retrieved array
|
|
1292
|
+
*/
|
|
1293
|
+
getArray(name) {
|
|
1294
|
+
return this._atoms_base.get_array(name);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* Check if an array exists in the underlying Atoms object
|
|
1299
|
+
* @param {String} name Name of the array to check
|
|
1300
|
+
* @return {bool} Whether the array exists
|
|
1301
|
+
*/
|
|
1302
|
+
hasArray(name) {
|
|
1303
|
+
return (name in this._atoms_base._arrays);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Delete an array from the underlying Atoms object
|
|
1308
|
+
* @param {String} name Name of the array to delete
|
|
1309
|
+
*/
|
|
1310
|
+
deleteArray(name) {
|
|
1311
|
+
delete this._atoms_base._arrays[name];
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// These functions are for adding and removing graphical representations
|
|
1315
|
+
// that are meant to be drawn on to of the existing 3D model
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Add link drawn on model
|
|
1319
|
+
*
|
|
1320
|
+
* @param {Atom | Array} from Starting point
|
|
1321
|
+
* @param {Atom | Array} to End point
|
|
1322
|
+
* @param {String} name Name to use for the link object
|
|
1323
|
+
* @param {String} label Text label to add to the link
|
|
1324
|
+
* @param {Object} parameters Additional parameters (see LineMesh)
|
|
1325
|
+
*/
|
|
1326
|
+
addLink(from, to, name = 'link', label = null, parameters = {}) {
|
|
1327
|
+
|
|
1328
|
+
this.removeGraphics(name);
|
|
1329
|
+
|
|
1330
|
+
parameters = _.clone(parameters); // Avoid editing the reference object
|
|
1331
|
+
|
|
1332
|
+
var r = this._renderer;
|
|
1333
|
+
if (r) {
|
|
1334
|
+
var link = new r.Primitives.LineMesh(from, to, parameters);
|
|
1335
|
+
|
|
1336
|
+
this._primitives[name] = link;
|
|
1337
|
+
r.add(link);
|
|
1338
|
+
|
|
1339
|
+
if (label) {
|
|
1340
|
+
var text = new r.Primitives.TextSprite(label, {
|
|
1341
|
+
color: parameters.color,
|
|
1342
|
+
fixScale: true,
|
|
1343
|
+
faceCamera: true,
|
|
1344
|
+
height: LABEL_HEIGHT,
|
|
1345
|
+
shift: [LABEL_HEIGHT, 0, 0],
|
|
1346
|
+
onOverlay: parameters.onOverlay
|
|
1347
|
+
});
|
|
1348
|
+
link.add(text);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
/**
|
|
1354
|
+
* Add a sphere drawn on model
|
|
1355
|
+
*
|
|
1356
|
+
* @param {Atom | Array} center Center of the sphere
|
|
1357
|
+
* @param {float} radius Radius of the sphere
|
|
1358
|
+
* @param {String} name Name to use for the sphere object
|
|
1359
|
+
* @param {Object} parameters Additional parameters (see EllipsoidMesh)
|
|
1360
|
+
*/
|
|
1361
|
+
addSphere(center, radius, name='sphere', parameters = {}) {
|
|
1362
|
+
|
|
1363
|
+
this.removeGraphics(name);
|
|
1364
|
+
|
|
1365
|
+
var r = this._renderer;
|
|
1366
|
+
if (r) {
|
|
1367
|
+
|
|
1368
|
+
parameters = _.merge({
|
|
1369
|
+
color: 0xffffff,
|
|
1370
|
+
opacity: 0.5,
|
|
1371
|
+
opacityMode: r.Primitives.EllipsoidMesh.DITHER,
|
|
1372
|
+
showCircles: true,
|
|
1373
|
+
showAxes: true
|
|
1374
|
+
}, parameters); // Avoid editing the reference object
|
|
1375
|
+
|
|
1376
|
+
var sph = new r.Primitives.EllipsoidMesh({
|
|
1377
|
+
color: parameters.color,
|
|
1378
|
+
opacity: parameters.opacity,
|
|
1379
|
+
opacityMode: parameters.opacityMode,
|
|
1380
|
+
showCircles: parameters.showCircles,
|
|
1381
|
+
showAxes: parameters.showAxes,
|
|
1382
|
+
scalingFactor: radius,
|
|
1383
|
+
center: center
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
this._primitives[name] = sph;
|
|
1387
|
+
r.add(sph);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Remove the graphical object with a given name
|
|
1393
|
+
*
|
|
1394
|
+
* @param {String} name Name of the graphical object to remove
|
|
1395
|
+
*/
|
|
1396
|
+
removeGraphics(name) {
|
|
1397
|
+
var g = this._primitives[name];
|
|
1398
|
+
var r = this._renderer;
|
|
1399
|
+
if (g && r)
|
|
1400
|
+
r.remove(g);
|
|
1401
|
+
delete this._primitives[name];
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Remove all graphical objects
|
|
1406
|
+
*/
|
|
1407
|
+
clearGraphics() {
|
|
1408
|
+
var r = this._renderer;
|
|
1409
|
+
|
|
1410
|
+
if (r) {
|
|
1411
|
+
_.map(this._primitives, function(g) {
|
|
1412
|
+
r.remove(g);
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
this._primitives = {};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* Compute the bonds within the model. For internal use
|
|
1421
|
+
* @private
|
|
1422
|
+
*/
|
|
1423
|
+
_computeBonds() {
|
|
1424
|
+
|
|
1425
|
+
var N = this.length;
|
|
1426
|
+
this._bondmat = Array(N); // Bond matrix
|
|
1427
|
+
this._bondmat = _.map(this._bondmat, function() {
|
|
1428
|
+
return _.map(Array(N), function() {
|
|
1429
|
+
return [];
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
// Van der Waals radii by element
|
|
1434
|
+
var vdwf = this._vdwScaling;
|
|
1435
|
+
var vdwf_table = this._vdwElementScaling;
|
|
1436
|
+
|
|
1437
|
+
var vdwr = _.map(this.symbols, function(s) {
|
|
1438
|
+
var f = vdwf;
|
|
1439
|
+
|
|
1440
|
+
if (s in vdwf_table) {
|
|
1441
|
+
f = vdwf_table[s];
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
return data.getVdwRadius(s)*f;
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
var maxr = _.max(vdwr);
|
|
1448
|
+
|
|
1449
|
+
var cell = this.cell;
|
|
1450
|
+
var sgrid = [
|
|
1451
|
+
[0, 0, 0]
|
|
1452
|
+
];
|
|
1453
|
+
var p = this._positions;
|
|
1454
|
+
|
|
1455
|
+
if (this._periodic) {
|
|
1456
|
+
var scell = this.minimumSupercell(maxr);
|
|
1457
|
+
sgrid = utils.supercellGrid(scell);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// Now iterate over all atom pairs
|
|
1461
|
+
for (let i = 0; i < this.length; ++i) {
|
|
1462
|
+
|
|
1463
|
+
var p1 = p[i];
|
|
1464
|
+
|
|
1465
|
+
for (let j = i; j < this.length; ++j) {
|
|
1466
|
+
|
|
1467
|
+
var p2 = p[j];
|
|
1468
|
+
|
|
1469
|
+
for (let k = 0; k < sgrid.length; ++k) {
|
|
1470
|
+
var c = sgrid[k];
|
|
1471
|
+
if ((i == j) && (c[0] == 0 && c[1] == 0 && c[2] == 0)) {
|
|
1472
|
+
// Just the same atom, skip
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
var r = [0, 0, 0];
|
|
1476
|
+
// Here we write the algebra explicitly
|
|
1477
|
+
// for efficiency reasons
|
|
1478
|
+
if (this._periodic) {
|
|
1479
|
+
r[0] = c[0] * cell[0][0] + c[1] * cell[1][0] + c[2] * cell[2][0];
|
|
1480
|
+
r[1] = c[0] * cell[0][1] + c[1] * cell[1][1] + c[2] * cell[2][1];
|
|
1481
|
+
r[2] = c[0] * cell[0][2] + c[1] * cell[1][2] + c[2] * cell[2][2];
|
|
1482
|
+
}
|
|
1483
|
+
r = [p2[0] - p1[0] + r[0], p2[1] - p1[1] + r[1], p2[2] - p1[2] + r[2]];
|
|
1484
|
+
r = Math.sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
|
|
1485
|
+
if (r < (vdwr[i] + vdwr[j]) / 2.0) {
|
|
1486
|
+
// Bond!
|
|
1487
|
+
this._bondmat[i][j].push([c[0], c[1], c[2]]);
|
|
1488
|
+
this._bondmat[j][i].push([-c[0], -c[1], -c[2]]);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Compute the molecules within the model. For internal use
|
|
1497
|
+
* @private
|
|
1498
|
+
*/
|
|
1499
|
+
_computeMolecules() {
|
|
1500
|
+
|
|
1501
|
+
this._molecules = [];
|
|
1502
|
+
this._molinds = [];
|
|
1503
|
+
|
|
1504
|
+
if (this.length < 2) {
|
|
1505
|
+
// No molecules can be computed
|
|
1506
|
+
this._molecules = null;
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
var mol_sets = [];
|
|
1511
|
+
var unsorted_atoms = _.range(this.length);
|
|
1512
|
+
|
|
1513
|
+
while (unsorted_atoms.length > 0) {
|
|
1514
|
+
var mol_queue = [
|
|
1515
|
+
[unsorted_atoms.shift(), [0, 0, 0]]
|
|
1516
|
+
];
|
|
1517
|
+
var current_mol = [];
|
|
1518
|
+
var current_mol_cells = [];
|
|
1519
|
+
while (mol_queue.length > 0) {
|
|
1520
|
+
var ac1 = mol_queue.shift();
|
|
1521
|
+
var a1 = ac1[0];
|
|
1522
|
+
var c1 = ac1[1];
|
|
1523
|
+
|
|
1524
|
+
current_mol.push(a1);
|
|
1525
|
+
current_mol_cells.push(c1);
|
|
1526
|
+
// Find linked atoms
|
|
1527
|
+
var link1 = this._bondmat[a1];
|
|
1528
|
+
for (let i in link1) {
|
|
1529
|
+
var a2 = parseInt(i);
|
|
1530
|
+
var link12 = link1[i];
|
|
1531
|
+
// Is a2 still unsorted?
|
|
1532
|
+
if (!unsorted_atoms.includes(a2) || link12.length == 0)
|
|
1533
|
+
continue;
|
|
1534
|
+
|
|
1535
|
+
for (let j = 0; j < link12.length; ++j) {
|
|
1536
|
+
var c2 = link12[j];
|
|
1537
|
+
mol_queue.push([a2, mjs.add(c1, c2)]);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
unsorted_atoms.splice(unsorted_atoms.indexOf(a2), 1);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
mol_sets.push([
|
|
1544
|
+
current_mol,
|
|
1545
|
+
current_mol_cells
|
|
1546
|
+
]);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
for (let i = 0; i < mol_sets.length; ++i) {
|
|
1550
|
+
|
|
1551
|
+
var mol = [];
|
|
1552
|
+
for (let j = 0; j < mol_sets[i][0].length; ++j) {
|
|
1553
|
+
mol.push({
|
|
1554
|
+
'index': mol_sets[i][0][j],
|
|
1555
|
+
'cell': mol_sets[i][1][j]
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
this._molecules.push(mol);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Assign the molecule's index for each atom
|
|
1563
|
+
this._molinds = _.range(this.length);
|
|
1564
|
+
|
|
1565
|
+
for (let i = 0; i < this._molecules.length; ++i) {
|
|
1566
|
+
var m = this._molecules[i];
|
|
1567
|
+
for (let j = 0; j < m.length; ++j) {
|
|
1568
|
+
var a = m[j];
|
|
1569
|
+
this._molinds[a.index] = i;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* Return a list of all AtomImages within the given supercell.
|
|
1576
|
+
*
|
|
1577
|
+
* @private
|
|
1578
|
+
* @return {AtomImage[]} List of AtomImage objects
|
|
1579
|
+
*/
|
|
1580
|
+
_atomImages() {
|
|
1581
|
+
var sgrid = this._supercell_grid;
|
|
1582
|
+
var imgs = [];
|
|
1583
|
+
var indices = _.range(this.length);
|
|
1584
|
+
var model = this;
|
|
1585
|
+
for (let i = 0; i < sgrid.length; ++i) {
|
|
1586
|
+
var cell = sgrid[i];
|
|
1587
|
+
imgs = imgs.concat(_.map(indices, function(a) {
|
|
1588
|
+
return new AtomImage(model, a, cell);
|
|
1589
|
+
}));
|
|
1590
|
+
}
|
|
1591
|
+
return imgs;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Return a list of all BondImages within the given supercell.
|
|
1596
|
+
*
|
|
1597
|
+
* @private
|
|
1598
|
+
* @return {BondImage[]} List of BondImage objects
|
|
1599
|
+
*/
|
|
1600
|
+
_bondImages() {
|
|
1601
|
+
var bondimgs = [];
|
|
1602
|
+
|
|
1603
|
+
for (let ii = 0; ii < this._atom_images.length; ++ii) {
|
|
1604
|
+
var im1 = this._atom_images[ii];
|
|
1605
|
+
var i = im1.index;
|
|
1606
|
+
var bonds = this._bondmat[i];
|
|
1607
|
+
var c1 = im1.ijk;
|
|
1608
|
+
for (let j = i; j < this.length; ++j) {
|
|
1609
|
+
var blist = bonds[j];
|
|
1610
|
+
for (let k = 0; k < blist.length; ++k) {
|
|
1611
|
+
var r = blist[k];
|
|
1612
|
+
var c2 = [c1[0] + r[0], c1[1] + r[1], c1[2] + r[2]];
|
|
1613
|
+
var jj = utils.supercellIndex(j, c2, this._supercell, this.length);
|
|
1614
|
+
if (jj >= 0 && jj < this._atom_images.length) {
|
|
1615
|
+
var im2 = this._atom_images[jj];
|
|
1616
|
+
var bimg = new BondImage(this, im1, im2);
|
|
1617
|
+
bondimgs.push(bimg);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
return bondimgs;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* Convert fractional coordinates to absolute
|
|
1628
|
+
*
|
|
1629
|
+
* @param {float[]} fx Fractional coordinates
|
|
1630
|
+
* @return {float[]} Absolute coordinates
|
|
1631
|
+
*/
|
|
1632
|
+
fracToAbs(fx) {
|
|
1633
|
+
if (!this.periodic) {
|
|
1634
|
+
return null
|
|
1635
|
+
}
|
|
1636
|
+
var c = this._atoms_base._cell;
|
|
1637
|
+
return [fx[0] * c[0][0] + fx[1] * c[1][0] + fx[2] * c[2][0],
|
|
1638
|
+
fx[0] * c[0][1] + fx[1] * c[1][1] + fx[2] * c[2][1],
|
|
1639
|
+
fx[0] * c[0][2] + fx[1] * c[1][2] + fx[2] * c[2][2]
|
|
1640
|
+
];
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Convert absolute coordinates to fractional
|
|
1645
|
+
*
|
|
1646
|
+
* @param {float[]} x Absolute coordinates
|
|
1647
|
+
* @return {float[]} Fractional coordinates
|
|
1648
|
+
*/
|
|
1649
|
+
absToFrac(x) {
|
|
1650
|
+
if (!this.periodic) {
|
|
1651
|
+
return null
|
|
1652
|
+
}
|
|
1653
|
+
var ic = this._atoms_base._inv_cell;
|
|
1654
|
+
return [x[0] * ic[0][0] + x[1] * ic[1][0] + x[2] * ic[2][0],
|
|
1655
|
+
x[0] * ic[0][1] + x[1] * ic[1][1] + x[2] * ic[2][1],
|
|
1656
|
+
x[0] * ic[0][2] + x[1] * ic[1][2] + x[2] * ic[2][2]
|
|
1657
|
+
];
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* Compute and return the minimum supercell that guarantees
|
|
1662
|
+
* containing all atoms at a maximum distance r from those in the
|
|
1663
|
+
* [0,0,0] cell.
|
|
1664
|
+
*
|
|
1665
|
+
* @param {float} r Maximum distance that must be contained within the supercell
|
|
1666
|
+
*/
|
|
1667
|
+
minimumSupercell(r) {
|
|
1668
|
+
|
|
1669
|
+
var diag = _.map(this._r_diag.values, function(x) {
|
|
1670
|
+
return mjs.pow(x, -0.5)
|
|
1671
|
+
});
|
|
1672
|
+
var utransf_mat = mjs.multiply(this._r_diag.vectors, mjs.diag(diag));
|
|
1673
|
+
var utransf_norm = mjs.transpose(utransf_mat);
|
|
1674
|
+
for (let i = 0; i < 3; ++i) {
|
|
1675
|
+
var norm = mjs.norm(utransf_mat[i]);
|
|
1676
|
+
for (let j = 0; j < 3; ++j) {
|
|
1677
|
+
utransf_norm[j][i] *= r / norm;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
var qmatrix = mjs.multiply(utransf_mat, utransf_norm);
|
|
1681
|
+
var scell = [];
|
|
1682
|
+
for (let i = 0; i < 3; ++i) {
|
|
1683
|
+
var b = 0;
|
|
1684
|
+
for (let j = 0; j < 3; ++j) {
|
|
1685
|
+
b = Math.max(Math.ceil(Math.abs(qmatrix[i][j])), b);
|
|
1686
|
+
}
|
|
1687
|
+
scell.push(2 * b + 1);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
return scell;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Find a group of atoms based on a given query and return as AtomImages
|
|
1695
|
+
* @param {Array} query A search query for atoms. Must use nested lists
|
|
1696
|
+
* of types and arguments, and can use logic
|
|
1697
|
+
* operators $and, $or and $xor.
|
|
1698
|
+
* @return {ModelView} ModelView object for found atoms
|
|
1699
|
+
*/
|
|
1700
|
+
find(query) {
|
|
1701
|
+
var found = this._qparse.parse(query);
|
|
1702
|
+
return this.view(found);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/** Create a new ModelView for this model, using a given list of indices
|
|
1706
|
+
* @param {Array} indices Indices of atoms to include in the ModelView
|
|
1707
|
+
*
|
|
1708
|
+
* @return {ModelView} ModelView object for specified indices
|
|
1709
|
+
*/
|
|
1710
|
+
view(indices) {
|
|
1711
|
+
return new ModelView(this, indices);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/**
|
|
1715
|
+
* Set a property on a series of atom images
|
|
1716
|
+
*
|
|
1717
|
+
* @private
|
|
1718
|
+
* @param {AtomImage[]} aimages List of AtomImages, or their indices
|
|
1719
|
+
* @param {String} name Name of the property to set
|
|
1720
|
+
* @param {String} value Value to set to the property
|
|
1721
|
+
*/
|
|
1722
|
+
_setAtomsProperty(aimages, name, value) {
|
|
1723
|
+
|
|
1724
|
+
// Value can be a single value or an Array
|
|
1725
|
+
var isarr = (value instanceof Array);
|
|
1726
|
+
|
|
1727
|
+
for (let i = 0; i < aimages.length; ++i) {
|
|
1728
|
+
var id = aimages[i];
|
|
1729
|
+
if (id instanceof AtomImage)
|
|
1730
|
+
id = id.imgIndex;
|
|
1731
|
+
this._atom_images[id][name] = isarr ? value[i] : value;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* Set a property on a series of bond images
|
|
1738
|
+
*
|
|
1739
|
+
* @private
|
|
1740
|
+
* @param {BondImage[]} aimages List of BondImages
|
|
1741
|
+
* @param {String} name Name of the property to set
|
|
1742
|
+
* @param {String} value Value to set to the property
|
|
1743
|
+
*/
|
|
1744
|
+
_setBondsProperty(bimages, name, value) {
|
|
1745
|
+
|
|
1746
|
+
// Value can be a single value or an Array
|
|
1747
|
+
var isarr = (value instanceof Array);
|
|
1748
|
+
|
|
1749
|
+
for (let i = 0; i < bimages.length; ++i) {
|
|
1750
|
+
var bimg = bimages[i];
|
|
1751
|
+
bimg[name] = isarr ? value[i] : value;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// Query functions. These are for internal use. They return the indices of
|
|
1757
|
+
// AtomImages in the _atom_images array.
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* @private
|
|
1761
|
+
*/
|
|
1762
|
+
_queryAll() {
|
|
1763
|
+
// All atoms
|
|
1764
|
+
return _.range(this._atom_images.length);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
/**
|
|
1768
|
+
* @private
|
|
1769
|
+
*/
|
|
1770
|
+
_queryIndices(indices) {
|
|
1771
|
+
|
|
1772
|
+
if (typeof(indices) == 'number') {
|
|
1773
|
+
indices = [indices]; // A single index
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
var scell = this.supercell;
|
|
1777
|
+
var n = this.length;
|
|
1778
|
+
var scgrid = this._supercell_grid;
|
|
1779
|
+
|
|
1780
|
+
var found = _.map(indices, function(i) {
|
|
1781
|
+
return _.map(scgrid, function(ijk) {
|
|
1782
|
+
return utils.supercellIndex(i, ijk, scell, n);
|
|
1783
|
+
});
|
|
1784
|
+
});
|
|
1785
|
+
|
|
1786
|
+
return _.flatten(found);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
* @private
|
|
1791
|
+
*/
|
|
1792
|
+
_queryElements(elems) {
|
|
1793
|
+
if (_.isString(elems)) {
|
|
1794
|
+
elems = [elems]; // A single symbol
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
var indices = _.reduce(this._elems, function(inds, s, i) {
|
|
1798
|
+
if (elems.indexOf(s) > -1) {
|
|
1799
|
+
inds.push(i);
|
|
1800
|
+
}
|
|
1801
|
+
return inds;
|
|
1802
|
+
}, []);
|
|
1803
|
+
|
|
1804
|
+
return this._queryIndices(indices);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* @private
|
|
1809
|
+
*/
|
|
1810
|
+
_queryCell(ijk) {
|
|
1811
|
+
|
|
1812
|
+
// Check if ijk is contained in the supercell's limits
|
|
1813
|
+
var ind = _.findIndex(this._supercell_grid, function(x) {
|
|
1814
|
+
return _.isEqual(x, ijk);
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1817
|
+
if (ind < 0) {
|
|
1818
|
+
return [];
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
var scell = this.supercell;
|
|
1822
|
+
var n = this.length;
|
|
1823
|
+
var found = _.map(_.range(n), function(x) {
|
|
1824
|
+
return utils.supercellIndex(x, ijk, scell, n);
|
|
1825
|
+
});
|
|
1826
|
+
|
|
1827
|
+
return found;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
/**
|
|
1831
|
+
* @private
|
|
1832
|
+
*/
|
|
1833
|
+
_queryBox(x0, x1) {
|
|
1834
|
+
|
|
1835
|
+
if (x0 instanceof AtomImage) {
|
|
1836
|
+
x0 = x0.xyz;
|
|
1837
|
+
}
|
|
1838
|
+
if (x1 instanceof AtomImage) {
|
|
1839
|
+
x1 = x1.xyz;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// Box sides?
|
|
1843
|
+
var box = _.zip(x0, x1);
|
|
1844
|
+
var xmin = _.map(box, _.min);
|
|
1845
|
+
var xmax = _.map(box, _.max);
|
|
1846
|
+
|
|
1847
|
+
var fxmin;
|
|
1848
|
+
var fxmax;
|
|
1849
|
+
if (this.periodic) {
|
|
1850
|
+
var fx0 = this.absToFrac(x0);
|
|
1851
|
+
var fx1 = this.absToFrac(x1);
|
|
1852
|
+
var fbox = _.zip(fx0, fx1);
|
|
1853
|
+
fxmin = _.map(fbox, _.min);
|
|
1854
|
+
fxmax = _.map(fbox, _.max);
|
|
1855
|
+
fxmin = _.map(fxmin, Math.floor);
|
|
1856
|
+
fxmax = _.map(fxmax, Math.ceil);
|
|
1857
|
+
|
|
1858
|
+
// Now add supercell limits
|
|
1859
|
+
var scmin = this._supercell_grid[0];
|
|
1860
|
+
var scmax = this._supercell_grid[this._supercell_grid.length - 1];
|
|
1861
|
+
fxmin = _.zipWith(fxmin, scmin, function(f, s) {
|
|
1862
|
+
return Math.max(f, s);
|
|
1863
|
+
});
|
|
1864
|
+
fxmax = _.zipWith(fxmax, scmax, function(f, s) {
|
|
1865
|
+
return Math.min(f, s + 1);
|
|
1866
|
+
});
|
|
1867
|
+
} else {
|
|
1868
|
+
fxmin = [0, 0, 0];
|
|
1869
|
+
fxmax = [1, 1, 1];
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
var found = []
|
|
1873
|
+
|
|
1874
|
+
// Now iterate over the cells, and atoms
|
|
1875
|
+
for (let i = fxmin[0]; i < fxmax[0]; ++i) {
|
|
1876
|
+
for (let j = fxmin[1]; j < fxmax[1]; ++j) {
|
|
1877
|
+
for (let k = fxmin[2]; k < fxmax[2]; ++k) {
|
|
1878
|
+
// var p0 = this.fracToAbs([i, j, k]);
|
|
1879
|
+
for (let a = 0; a < this.length; ++a) {
|
|
1880
|
+
|
|
1881
|
+
var ind = utils.supercellIndex(a, [i, j, k], this._supercell,
|
|
1882
|
+
this.length);
|
|
1883
|
+
var aimg = this._atom_images[ind];
|
|
1884
|
+
|
|
1885
|
+
// Is it in the box?
|
|
1886
|
+
var isin = _.reduce(aimg.xyz, function(r, x, e) {
|
|
1887
|
+
return (r && (xmin[e] <= x) && (x <= xmax[e]));
|
|
1888
|
+
}, true);
|
|
1889
|
+
|
|
1890
|
+
if (isin)
|
|
1891
|
+
found.push(ind);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
return found;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* @private
|
|
1902
|
+
*/
|
|
1903
|
+
_querySphere(x0, r) {
|
|
1904
|
+
|
|
1905
|
+
if (x0 instanceof AtomImage) {
|
|
1906
|
+
x0 = x0.xyz; // Can use an atom as centre
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
var scell = [1, 1, 1];
|
|
1910
|
+
var cell0 = [0, 0, 0];
|
|
1911
|
+
var fx0 = this.absToFrac(x0);
|
|
1912
|
+
|
|
1913
|
+
if (this.periodic) {
|
|
1914
|
+
// Supercell necessary for the search?
|
|
1915
|
+
scell = this.minimumSupercell(r);
|
|
1916
|
+
cell0 = _.map(fx0, Math.floor);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
var fxmin;
|
|
1920
|
+
var fxmax;
|
|
1921
|
+
if (this.periodic) {
|
|
1922
|
+
fxmin = _.zipWith(cell0, scell, function(c0, s) {
|
|
1923
|
+
return c0 - (s - 1) / 2;
|
|
1924
|
+
});
|
|
1925
|
+
fxmax = _.zipWith(cell0, scell, function(c0, s) {
|
|
1926
|
+
return c0 + (s + 1) / 2;
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
// Now add supercell limits
|
|
1930
|
+
var scmin = this._supercell_grid[0];
|
|
1931
|
+
var scmax = this._supercell_grid[this._supercell_grid.length - 1];
|
|
1932
|
+
fxmin = _.zipWith(fxmin, scmin, function(f, s) {
|
|
1933
|
+
return Math.max(f, s);
|
|
1934
|
+
});
|
|
1935
|
+
fxmax = _.zipWith(fxmax, scmax, function(f, s) {
|
|
1936
|
+
return Math.min(f, s + 1);
|
|
1937
|
+
});
|
|
1938
|
+
} else {
|
|
1939
|
+
fxmin = [0, 0, 0];
|
|
1940
|
+
fxmax = [1, 1, 1];
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
var found = [];
|
|
1944
|
+
|
|
1945
|
+
for (let i = fxmin[0]; i < fxmax[0]; ++i) {
|
|
1946
|
+
for (let j = fxmin[1]; j < fxmax[1]; ++j) {
|
|
1947
|
+
for (let k = fxmin[2]; k < fxmax[2]; ++k) {
|
|
1948
|
+
for (let a = 0; a < this.length; ++a) {
|
|
1949
|
+
|
|
1950
|
+
var ind = utils.supercellIndex(a, [i, j, k], this._supercell,
|
|
1951
|
+
this.length);
|
|
1952
|
+
var aimg = this._atom_images[ind];
|
|
1953
|
+
|
|
1954
|
+
// Is it in the sphere?
|
|
1955
|
+
var isin = mjs.distance(aimg.xyz, x0) <= r;
|
|
1956
|
+
|
|
1957
|
+
if (isin)
|
|
1958
|
+
found.push(ind);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
return found;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* @private
|
|
1969
|
+
*/
|
|
1970
|
+
_queryBonded(atoms, distance = 1, exact = false) {
|
|
1971
|
+
|
|
1972
|
+
if (atoms instanceof AtomImage || typeof(atoms) == 'number') {
|
|
1973
|
+
atoms = [atoms];
|
|
1974
|
+
}
|
|
1975
|
+
if (atoms instanceof ModelView) {
|
|
1976
|
+
atoms = atoms._images;
|
|
1977
|
+
}
|
|
1978
|
+
if (atoms instanceof Array && typeof(atoms[0]) == 'number') {
|
|
1979
|
+
var imgs = this._atom_images;
|
|
1980
|
+
atoms = _.map(atoms, function(i) {
|
|
1981
|
+
return imgs[i];
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
if (distance < 1) {
|
|
1986
|
+
return [];
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
function a2ii(a) {
|
|
1990
|
+
return a.imgIndex;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Find all atoms that are at most [distance] bonds away from the ones
|
|
1994
|
+
// passed as argument
|
|
1995
|
+
|
|
1996
|
+
var bonded_tree = [atoms]; // We start with distance zero and build up
|
|
1997
|
+
var found = [];
|
|
1998
|
+
|
|
1999
|
+
for (let d = 1; d <= distance; ++d) {
|
|
2000
|
+
var previous = bonded_tree[d - 1];
|
|
2001
|
+
var next = [];
|
|
2002
|
+
for (let i = 0; i < previous.length; ++i) {
|
|
2003
|
+
next = next.concat(previous[i].bondedAtoms);
|
|
2004
|
+
}
|
|
2005
|
+
bonded_tree.push(next);
|
|
2006
|
+
if (!exact) {
|
|
2007
|
+
found = found.concat(_.map(next, a2ii));
|
|
2008
|
+
} else if (d == distance) {
|
|
2009
|
+
found = _.map(next, a2ii);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
found = _.uniq(found); // Remove duplicate values
|
|
2014
|
+
found = _.difference(found, _.map(bonded_tree[0], function(a) {
|
|
2015
|
+
return a.imgIndex;
|
|
2016
|
+
})); // Remove the starting atoms
|
|
2017
|
+
|
|
2018
|
+
return found;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* @private
|
|
2023
|
+
*/
|
|
2024
|
+
_queryMolecule(atoms) {
|
|
2025
|
+
|
|
2026
|
+
if (atoms instanceof AtomImage || typeof(atoms) == 'number') {
|
|
2027
|
+
atoms = [atoms];
|
|
2028
|
+
}
|
|
2029
|
+
if (atoms instanceof ModelView) {
|
|
2030
|
+
atoms = atoms._images;
|
|
2031
|
+
}
|
|
2032
|
+
if (atoms instanceof Array && typeof(atoms[0]) == 'number') {
|
|
2033
|
+
var imgs = this._atom_images;
|
|
2034
|
+
atoms = _.map(atoms, function(i) {
|
|
2035
|
+
return imgs[i];
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
var found = [];
|
|
2040
|
+
|
|
2041
|
+
// For each atom, select the whole molecule
|
|
2042
|
+
for (let i = 0; i < atoms.length; ++atoms) {
|
|
2043
|
+
var a = atoms[i];
|
|
2044
|
+
|
|
2045
|
+
var ind = a.index;
|
|
2046
|
+
var mol_ind = this._molinds[ind];
|
|
2047
|
+
var mol = this._molecules[mol_ind];
|
|
2048
|
+
|
|
2049
|
+
// Identify the atom
|
|
2050
|
+
var c0 = _.find(mol, function(am) {
|
|
2051
|
+
return am.index == ind;
|
|
2052
|
+
}).cell;
|
|
2053
|
+
|
|
2054
|
+
// Supercell indices?
|
|
2055
|
+
for (let j = 0; j < mol.length; ++j) {
|
|
2056
|
+
var am = mol[j];
|
|
2057
|
+
var cm = mjs.subtract(am.cell, c0);
|
|
2058
|
+
var im = am.index;
|
|
2059
|
+
var iim = utils.supercellIndex(im, cm, this._supercell, this.length);
|
|
2060
|
+
if (iim >= 0 && iim < this._atom_images.length) {
|
|
2061
|
+
found.push(iim);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
return found;
|
|
2067
|
+
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
export {
|
|
2073
|
+
AtomImage,
|
|
2074
|
+
BondImage,
|
|
2075
|
+
Model
|
|
2076
|
+
}
|