@ccp-nc/crystvis-js 0.7.0 → 0.7.1

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 (41) hide show
  1. package/package.json +3 -2
  2. package/docs/data/search.json +0 -1
  3. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  4. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  5. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  6. package/docs/index.html +0 -59
  7. package/docs/lib_model.module_js-AtomImage.html +0 -3
  8. package/docs/lib_model.module_js-BondImage.html +0 -3
  9. package/docs/lib_model.module_js-Model.html +0 -3
  10. package/docs/lib_model.module_js.html +0 -3
  11. package/docs/lib_modelview.module_js-ModelView.html +0 -12
  12. package/docs/lib_modelview.module_js.html +0 -3
  13. package/docs/lib_visualizer.module_js-CrystVis.html +0 -3
  14. package/docs/lib_visualizer.module_js.html +0 -3
  15. package/docs/model.js.html +0 -2191
  16. package/docs/modelview.js.html +0 -473
  17. package/docs/scripts/core.js +0 -726
  18. package/docs/scripts/core.min.js +0 -23
  19. package/docs/scripts/resize.js +0 -90
  20. package/docs/scripts/search.js +0 -265
  21. package/docs/scripts/search.min.js +0 -6
  22. package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
  23. package/docs/scripts/third-party/fuse.js +0 -9
  24. package/docs/scripts/third-party/hljs-line-num-original.js +0 -369
  25. package/docs/scripts/third-party/hljs-line-num.js +0 -1
  26. package/docs/scripts/third-party/hljs-original.js +0 -5171
  27. package/docs/scripts/third-party/hljs.js +0 -1
  28. package/docs/scripts/third-party/popper.js +0 -5
  29. package/docs/scripts/third-party/tippy.js +0 -1
  30. package/docs/scripts/third-party/tocbot.js +0 -672
  31. package/docs/scripts/third-party/tocbot.min.js +0 -1
  32. package/docs/styles/clean-jsdoc-theme-base.css +0 -1159
  33. package/docs/styles/clean-jsdoc-theme-dark.css +0 -412
  34. package/docs/styles/clean-jsdoc-theme-light.css +0 -482
  35. package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -30
  36. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -1
  37. package/docs/styles/clean-jsdoc-theme.min.css +0 -1
  38. package/docs/tutorial-Events.html +0 -13
  39. package/docs/tutorial-Queries.html +0 -16
  40. package/docs/tutorial-ThreejsMigration.html +0 -25
  41. package/docs/visualizer.js.html +0 -810
@@ -1,810 +0,0 @@
1
- <!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: visualizer.js</title><!--[if lt IE 9]>
2
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
3
- <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="lib_model.module_js.html">lib/model.js</a></div><div class="sidebar-section-children"><a href="lib_modelview.module_js.html">lib/modelview.js</a></div><div class="sidebar-section-children"><a href="lib_visualizer.module_js.html">lib/visualizer.js</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="lib_model.module_js-AtomImage.html">AtomImage</a></div><div class="sidebar-section-children"><a href="lib_model.module_js-BondImage.html">BondImage</a></div><div class="sidebar-section-children"><a href="lib_model.module_js-Model.html">Model</a></div><div class="sidebar-section-children"><a href="lib_modelview.module_js-ModelView.html">ModelView</a></div><div class="sidebar-section-children"><a href="lib_visualizer.module_js-CrystVis.html">CrystVis</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-Events.html">Events</a></div><div class="sidebar-section-children"><a href="tutorial-Queries.html">Queries</a></div><div class="sidebar-section-children"><a href="tutorial-ThreejsMigration.html">ThreejsMigration</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">visualizer.js</h1></header><article><pre class="prettyprint source lang-js"><code>'use strict';
4
-
5
- /**
6
- * @fileoverview Class constituting the main object that plots crystals in the webpage
7
- * @module
8
- */
9
-
10
- import * as _ from 'lodash';
11
-
12
- import {
13
- Renderer as Renderer
14
- } from './render.js';
15
- // themes
16
- import {themes} from './render.js';
17
- import {
18
- Loader as Loader
19
- } from './loader.js';
20
- import {
21
- Model as Model
22
- } from './model.js';
23
- import {
24
- ModelView as ModelView
25
- } from './modelview.js';
26
- import {
27
- AtomMesh
28
- } from './primitives/index.js';
29
- import {
30
- addStaticVar
31
- } from './utils.js';
32
-
33
-
34
- const model_parameter_defaults = {
35
- supercell: [1, 1, 1],
36
- molecularCrystal: false
37
- };
38
-
39
- /** An object providing a full interface to a renderer for crystallographic models */
40
- class CrystVis {
41
-
42
- /**
43
- * An object providing a full interface to a renderer for crystallographic
44
- * models
45
- * @class
46
- * @param {string} element CSS-style identifier for the HTML element to
47
- * put the renderer in
48
- * @param {int} width Window width
49
- * @param {int} height Window height. If both this and width are
50
- * set to 0, the window fits its context and
51
- * automatically resizes with it
52
- * @param {Object} rendererOptions Options for the renderer
53
- */
54
- constructor(element, width = 0, height = 0, rendererOptions = {}) {
55
-
56
- // Create a renderer
57
- this._renderer = new Renderer(element, width, height, rendererOptions);
58
- this._loader = new Loader();
59
-
60
- this._models = {};
61
-
62
- this._current_model = null;
63
- this._current_mname = null;
64
- this._displayed = null;
65
- this._selected = null;
66
- this._notifications = [];
67
-
68
- // Handling events
69
- this._atom_click_events = {};
70
- this._atom_click_events[CrystVis.LEFT_CLICK] = this._defaultAtomLeftClick.bind(this);
71
- this._atom_click_events[CrystVis.LEFT_CLICK + CrystVis.SHIFT_BUTTON] = this._defaultAtomShiftLeftClick.bind(this);
72
- this._atom_click_events[CrystVis.LEFT_CLICK + CrystVis.CTRL_BUTTON] = this._defaultAtomCtrlLeftClick.bind(this);
73
-
74
- this._atom_click_defaults = _.cloneDeep(this._atom_click_events);
75
-
76
- this._atom_box_event = this._defaultAtomBox.bind(this);
77
-
78
- this._renderer.addClickListener(this._handleAtomClick.bind(this),
79
- this._renderer._groups.model, AtomMesh);
80
- this._renderer.addSelBoxListener(this._handleAtomBox.bind(this),
81
- this._renderer._groups.model, AtomMesh);
82
-
83
- // Additional options
84
- // Hidden (need dedicated setters)
85
- this._hsel = false; // If true, highlight the selected atoms
86
-
87
- // Vanilla (no get/set needed)
88
- this.cifsymtol = 1e-2; // Parameter controlling the tolerance to symmetry when loading CIF files
89
-
90
- // Disposal state
91
- this._isDisposed = false;
92
-
93
- // Model source / parameter / metadata stores (keyed by model name)
94
- this._model_sources = {}; // { text, extension }
95
- this._model_parameters = {}; // parameters passed to loadModels / reloadModel
96
- this._model_meta = {}; // { prefix, originalName }
97
-
98
- // Lifecycle / camera-change callbacks
99
- this._model_list_change_cbs = [];
100
- this._display_change_cbs = [];
101
- this._camera_change_cbs = [];
102
-
103
- // Subscribe to camera changes from the renderer
104
- this._camera_unsub = this._renderer.onCameraChange((state) => {
105
- this._camera_change_cbs.forEach(cb => cb(state));
106
- });
107
-
108
- }
109
-
110
- /**
111
- * Whether this instance has been disposed.
112
- * Once true, most methods will throw rather than silently fail.
113
- * @readonly
114
- * @type {boolean}
115
- */
116
- get isDisposed() {
117
- return this._isDisposed;
118
- }
119
-
120
- /**
121
- * List of loaded models
122
- * @readonly
123
- * @type {Array}
124
- */
125
- get modelList() {
126
- return Object.keys(this._models);
127
- }
128
-
129
- /**
130
- * Currently loaded model
131
- * @readonly
132
- * @type {Model}
133
- */
134
- get model() {
135
- return this._current_model;
136
- }
137
-
138
- /**
139
- * Name of the currently loaded model
140
- * @readonly
141
- * @type {String}
142
- */
143
- get modelName() {
144
- return this._current_mname;
145
- }
146
-
147
- /**
148
- * Displayed atoms
149
- * @type {ModelView}
150
- */
151
- get displayed() {
152
- return this._displayed;
153
- }
154
-
155
- set displayed(d) {
156
- if (!(d instanceof ModelView)) {
157
- throw new Error('.displayed must be set with a ModelView');
158
- }
159
- this._displayed.hide();
160
- this._displayed = d;
161
- this._displayed.show();
162
- }
163
-
164
- /**
165
- * Selected atoms
166
- * @type {ModelView}
167
- */
168
- get selected() {
169
- return this._selected;
170
- }
171
-
172
- set selected(s) {
173
- if (!(s instanceof ModelView)) {
174
- throw new Error('.selected must be set with a ModelView');
175
- }
176
- this._selected.setProperty('highlighted', false);
177
- this._selected = s;
178
- this._selected.setProperty('highlighted', this._hsel);
179
- }
180
-
181
- /** Whether the selected atoms should be highlighted with auras
182
- * @type {bool}
183
- */
184
- get highlightSelected() {
185
- return this._hsel;
186
- }
187
-
188
- set highlightSelected(hs) {
189
- this._hsel = hs;
190
- if (this._selected) {
191
- this._selected.setProperty('highlighted', this._hsel);
192
- }
193
- }
194
-
195
- get notifications() {
196
- return this._notifications;
197
- }
198
-
199
- set notifications(n) {
200
- this._notifications = n;
201
- }
202
-
203
-
204
- /** Theme
205
- * @type {object}
206
- */
207
- get theme() {
208
- return this._renderer.theme;
209
- }
210
-
211
- set theme(t) {
212
- // if t is a string, try to find the corresponding theme
213
- // from the list of themes
214
- if (typeof t === 'string') {
215
- if (themes[t]) {
216
- t = themes[t];
217
- } else {
218
- throw new Error('Theme ' + t + ' not found');
219
- }
220
- }
221
- this._renderer.theme = t;
222
- }
223
-
224
-
225
- /**
226
- * Set a callback function for an event where a user clicks on an atom. The
227
- * function should take as arguments the atom image for the clicked atom and
228
- * the event object:
229
- *
230
- * function callback(atom, event) {
231
- * ...
232
- * }
233
- *
234
- * @param {Function} callback Callback function for the event. Passing "null" restores default behaviour
235
- * @param {int} modifiers Click event. Use the following flags to define it:
236
- *
237
- * * CrystVis.LEFT_CLICK
238
- * * CrystVis.RIGHT_CLICK
239
- * * CrystVis.MIDDLE_CLICK
240
- * * CrystVis.CTRL_BUTTON
241
- * * CrystVis.ALT_BUTTON
242
- * * CrystVis.SHIFT_BUTTON
243
- * * CrystVis.CMD_BUTTON
244
- *
245
- * For example, CrystVis.LEFT_CLICK + CrystVis.SHIFT_BUTTON
246
- * defines the event for a click while the Shift key is pressed.
247
- *
248
- */
249
- onAtomClick(callback = null, modifiers = CrystVis.LEFT_CLICK) {
250
-
251
- // Check that event makes sense
252
- var lc = modifiers &amp; CrystVis.LEFT_CLICK;
253
- var mc = modifiers &amp; CrystVis.MIDDLE_CLICK;
254
- var rc = modifiers &amp; CrystVis.RIGHT_CLICK;
255
-
256
- if (lc + mc + rc == 0) {
257
- throw 'Can not set event without any click type';
258
- }
259
- if ((lc &amp;&amp; mc) || (lc &amp;&amp; rc) || (mc &amp;&amp; rc)) {
260
- throw 'Can not set event with two or more click types';
261
- }
262
-
263
- if (callback)
264
- this._atom_click_events[modifiers] = callback.bind(this);
265
- else
266
- this._atom_click_events[modifiers] = this._atom_click_defaults[modifiers];
267
- }
268
-
269
- /**
270
- * Set a callback function for an event where a user drags a box around multiple atoms.
271
- * The function should take as arguments a ModelView including the atoms in the box:
272
- *
273
- * function callback(view) {
274
- * ...
275
- * }
276
- *
277
- * @param {Function} callback Callback function for the event. Passing "null" restores default behaviour
278
- */
279
- onAtomBox(callback = null) {
280
- if (callback)
281
- this._atom_box_event = callback;
282
- else
283
- this._atom_box_event = this._defaultAtomBox.bind(this);
284
- }
285
-
286
- // ─── Private event emitters ─────────────────────────────────────────────────
287
-
288
- _emitModelListChange() {
289
- const names = Object.keys(this._models);
290
- this._model_list_change_cbs.forEach(cb => cb(names));
291
- }
292
-
293
- _emitDisplayChange() {
294
- this._display_change_cbs.forEach(cb => cb(this._current_mname));
295
- }
296
-
297
- // ─── Camera state API ────────────────────────────────────────────────────────
298
-
299
- /**
300
- * Return a plain serialisable snapshot of the current camera state.
301
- *
302
- * @return {{ position: {x,y,z}, target: {x,y,z}, zoom: number }}
303
- */
304
- getCameraState() {
305
- return this._renderer.getCameraState();
306
- }
307
-
308
- /**
309
- * Restore a camera snapshot produced by {@link CrystVis#getCameraState}.
310
- * Safe to call after `displayModel()`.
311
- *
312
- * @param {{ position?: {x,y,z}, target?: {x,y,z}, zoom?: number }} state
313
- */
314
- setCameraState(state) {
315
- this._renderer.setCameraState(state);
316
- }
317
-
318
- /**
319
- * Subscribe to camera-change events (rotate, pan, zoom).
320
- * The callback receives a snapshot identical to {@link CrystVis#getCameraState}.
321
- *
322
- * @param {Function} callback `callback(cameraState)`
323
- * @return {Function} Unsubscribe function
324
- */
325
- onCameraChange(callback) {
326
- this._camera_change_cbs.push(callback);
327
- return () => {
328
- this._camera_change_cbs = this._camera_change_cbs.filter(cb => cb !== callback);
329
- };
330
- }
331
-
332
- // ─── Lifecycle event APIs ────────────────────────────────────────────────────
333
-
334
- /**
335
- * Subscribe to model-list change events (model added or deleted).
336
- * The callback receives the new list of model names.
337
- *
338
- * @param {Function} callback `callback(modelNames: string[])`
339
- * @return {Function} Unsubscribe function
340
- */
341
- onModelListChange(callback) {
342
- this._model_list_change_cbs.push(callback);
343
- return () => {
344
- this._model_list_change_cbs = this._model_list_change_cbs.filter(cb => cb !== callback);
345
- };
346
- }
347
-
348
- /**
349
- * Subscribe to display-change events fired whenever `displayModel()` completes.
350
- * The callback receives the name of the newly displayed model (or `null` when cleared).
351
- *
352
- * @param {Function} callback `callback(modelName: string|null)`
353
- * @return {Function} Unsubscribe function
354
- */
355
- onDisplayChange(callback) {
356
- this._display_change_cbs.push(callback);
357
- return () => {
358
- this._display_change_cbs = this._display_change_cbs.filter(cb => cb !== callback);
359
- };
360
- }
361
-
362
- // ─── Model source / parameter / metadata APIs ────────────────────────────────
363
-
364
- /**
365
- * Return the raw file text and format extension originally passed to
366
- * `loadModels()` for the named model.
367
- *
368
- * @param {String} name Model name
369
- * @return {{ text: string, extension: string }|null}
370
- */
371
- getModelSource(name) {
372
- const src = this._model_sources[name];
373
- return src ? { ...src } : null;
374
- }
375
-
376
- /**
377
- * Return the loading parameters that were used when the named model was
378
- * last loaded / reloaded (a clone of the merged parameter object).
379
- *
380
- * @param {String} name Model name
381
- * @return {Object|null}
382
- */
383
- getModelParameters(name) {
384
- const p = this._model_parameters[name];
385
- return p ? JSON.parse(JSON.stringify(p)) : null;
386
- }
387
-
388
- /**
389
- * Return metadata stored alongside the named model:
390
- * `{ prefix, originalName }`.
391
- *
392
- * @param {String} name Model name
393
- * @return {{ prefix: string, originalName: string }|null}
394
- */
395
- getModelMeta(name) {
396
- const m = this._model_meta[name];
397
- return m ? { ...m } : null;
398
- }
399
-
400
- // ─── Atomic unload ───────────────────────────────────────────────────────────
401
-
402
- /**
403
- * Remove *all* loaded models and reset the view in a single atomic operation
404
- * (only one render pass after everything is cleared, unlike calling
405
- * `deleteModel()` in a loop).
406
- */
407
- unloadAll() {
408
- if (this._isDisposed) {
409
- throw new Error('CrystVis: cannot call unloadAll() on a disposed instance');
410
- }
411
-
412
- // Clear the displayed model (handles renderer.clear(), selection reset, etc.)
413
- // displayModel() with no args also emits _emitDisplayChange()
414
- this.displayModel();
415
-
416
- this._models = {};
417
- this._model_sources = {};
418
- this._model_parameters = {};
419
- this._model_meta = {};
420
-
421
- this._emitModelListChange();
422
- }
423
-
424
- // ─── Internal atom-click/box defaults ────────────────────────────────────────
425
-
426
- _defaultAtomLeftClick(atom, event) {
427
- var i = atom.imgIndex;
428
- this.selected = new ModelView(this._current_model, [i]);
429
- }
430
- _defaultAtomShiftLeftClick(atom, event) {
431
- var i = atom.imgIndex;
432
- this.selected = this.selected.or(new ModelView(this._current_model, [i]));
433
- }
434
- _defaultAtomCtrlLeftClick(atom, event) {
435
- var i = atom.imgIndex;
436
- this.selected = this.selected.xor(new ModelView(this._current_model, [i]));
437
- }
438
-
439
- _defaultAtomBox(view) {
440
- this.selected = this.selected.xor(view);
441
- console.log(view);
442
- }
443
-
444
- // Callback for when atoms are clicked
445
- _handleAtomClick(alist, event) {
446
-
447
- if (alist.length == 0) {
448
- return;
449
- }
450
-
451
- let clicked = alist[0].image;
452
-
453
- let modifiers = [CrystVis.LEFT_CLICK, CrystVis.MIDDLE_CLICK, CrystVis.RIGHT_CLICK][event.button];
454
-
455
- modifiers += event.shiftKey * CrystVis.SHIFT_BUTTON;
456
- modifiers += (event.ctrlKey || event.metaKey) * CrystVis.CTRL_BUTTON;
457
- modifiers += event.altKey * CrystVis.ALT_BUTTON;
458
-
459
- var callback = this._atom_click_events[modifiers];
460
-
461
- if (callback)
462
- callback(clicked, event);
463
-
464
- }
465
-
466
- // Callback for a whole box dragged over atoms
467
- _handleAtomBox(alist) {
468
-
469
- var indices = alist.map(function(a) {
470
- return a.image.imgIndex;
471
- });
472
-
473
- var callback = this._atom_box_event;
474
-
475
- if (callback)
476
- callback(new ModelView(this._current_model, indices));
477
- }
478
-
479
- /**
480
- * Center the camera on a given point
481
- *
482
- * @param {float[]} center Point in model space that the orbiting camera
483
- * should be centred on and look at
484
- * @param {float[]} shift Shift (in units of width/height of the canvas) with
485
- * which the center of the camera should be rendered with
486
- * respect to the center of the canvas
487
- */
488
- /**
489
- * Release all resources held by this instance: cancels the animation loop,
490
- * removes all canvas event listeners, disposes OrbitControls and the
491
- * THREE.WebGLRenderer, and nulls internal references. After calling this
492
- * method the instance must not be used again.
493
- */
494
- dispose() {
495
- if (this._isDisposed) {
496
- return;
497
- }
498
- this._isDisposed = true;
499
-
500
- // Unsubscribe camera-change listener before tearing down the renderer
501
- if (this._camera_unsub) {
502
- this._camera_unsub();
503
- this._camera_unsub = null;
504
- }
505
-
506
- // Tear down the renderer (animation loop, orbit controls, WebGL context)
507
- if (this._renderer) {
508
- this._renderer.dispose();
509
- this._renderer = null;
510
- }
511
-
512
- // Drop model and view references
513
- this._current_model = null;
514
- this._current_mname = null;
515
- this._displayed = null;
516
- this._selected = null;
517
- this._models = {};
518
- this._model_sources = {};
519
- this._model_parameters = {};
520
- this._model_meta = {};
521
-
522
- // Drop event callbacks
523
- this._atom_click_events = {};
524
- this._atom_click_defaults = {};
525
- this._atom_box_event = null;
526
- this._notifications = [];
527
- this._model_list_change_cbs = [];
528
- this._display_change_cbs = [];
529
- this._camera_change_cbs = [];
530
- }
531
-
532
- centerCamera(center = [0, 0, 0], shift = [0, 0]) {
533
- const renderer = this._renderer;
534
-
535
- renderer.resetOrbitCenter(center[0], center[1], center[2]);
536
- renderer.resetCameraCenter(shift[0], shift[1]);
537
- }
538
-
539
- /**
540
- * Load one or more atomic models from a file's contents
541
- *
542
- * @param {String} contents The contents of the structure file
543
- * @param {String} format The file's format (cif, xyz, etc.). Default is cif.
544
- * @param {String} prefix Prefix to use when naming the models. Default is empty.
545
- * @param {Object} parameters Loading parameters:
546
- *
547
- * - `supercell`: supercell size (only used if the structure is periodic)
548
- * - `molecularCrystal`: if true, try to make the model load completing molecules across periodic boundaries
549
- * - `useNMRActiveIsotopes`: if true, all isotopes are set by default to the most common one with non-zero spin
550
- * - `vdwScaling`: scale van der Waals radii by a constant factor
551
- * - `vdwElementScaling`: table of per-element factors to scale VdW radii by
552
- *
553
- * @return {Object} Status map of the models we tried to load. Keys are the model names (strings that can be
554
- * passed directly to `displayModel()`). Values are `0` for a successful load, or an error
555
- * message string if loading failed. Example: to display the first loaded model, use
556
- * `visualizer.displayModel(Object.keys(loaded)[0])` and check
557
- * `loaded[modelName] !== 0` to detect errors.
558
- */
559
- loadModels(contents, format = 'cif', prefix = null, parameters = {}) {
560
- if (this._isDisposed) {
561
- throw new Error('CrystVis: cannot call loadModels() on a disposed instance');
562
- }
563
- // clear existing notifications
564
- this.clearNotifications();
565
-
566
- parameters = _.merge(model_parameter_defaults, parameters);
567
-
568
- // By default, it's cif
569
- format = format.toLowerCase();
570
-
571
- // By default, same as the format
572
- prefix = prefix || format;
573
-
574
- var structs = this._loader.load(contents, format, prefix);
575
-
576
- var status = {};
577
-
578
- if (this._loader.status == Loader.STATUS_ERROR) {
579
- status[prefix] = this._loader.error_message;
580
- // display error notification to user
581
- this.addNotification('Error loading model: '+ prefix);
582
- this.addNotification(this._loader.error_message);
583
- return status;
584
- }
585
-
586
- // Now make unique names
587
- for (var n in structs) {
588
- var iter = 0;
589
- var coll = true;
590
- var nn = n;
591
- while (coll) {
592
- nn = n + (iter > 0 ? '_' + iter : '');
593
- coll = nn in this._models;
594
- iter++;
595
- }
596
- var s = structs[n];
597
- if (!s) {
598
- status[nn] = 'Model could not load properly';
599
- this.addNotification('Model '+ nn + ' could not load properly');
600
- continue;
601
- }
602
- this._models[nn] = new Model(s, parameters);
603
- this._model_sources[nn] = { text: contents, extension: format };
604
- this._model_parameters[nn] = JSON.parse(JSON.stringify(parameters));
605
- this._model_meta[nn] = { prefix: prefix, originalName: n };
606
- status[nn] = 0; // Success
607
- }
608
-
609
- this._emitModelListChange();
610
- return status;
611
- }
612
-
613
- /**
614
- * Reload a model, possibly with new parameters
615
- *
616
- * @param {String} name Name of the model to reload.
617
- * @param {Object} parameters Loading parameters as in .loadModels()
618
- */
619
- reloadModel(name, parameters = {}) {
620
- if (this._isDisposed) {
621
- throw new Error('CrystVis: cannot call reloadModel() on a disposed instance');
622
- }
623
- // clear existing notifications from scene
624
- this.clearNotifications();
625
-
626
- if (!(name in this._models)) {
627
- throw 'The requested model does not exist';
628
- }
629
-
630
- var current = (this._current_mname == name);
631
- if (current) {
632
- // Hide the model to reload it later
633
- this.displayModel();
634
- }
635
-
636
- var s = this._models[name]._atoms_base;
637
- parameters = _.merge(model_parameter_defaults, parameters);
638
-
639
- this._models[name] = new Model(s, parameters);
640
- this._model_parameters[name] = JSON.parse(JSON.stringify(parameters));
641
-
642
- if (current) {
643
- this.displayModel(name);
644
- }
645
- }
646
-
647
- /**
648
- * Render a model
649
- *
650
- * @param {String} name Name of the model to display. If empty, just
651
- * clear the renderer window.
652
- */
653
- displayModel(name = null) {
654
- if (this._isDisposed) {
655
- throw new Error('CrystVis: cannot call displayModel() on a disposed instance');
656
- }
657
-
658
- if (this._current_model) {
659
- // clear notifications from previous model
660
- this.clearNotifications();
661
- this.selected = this._current_model.view([]);
662
- this._current_model.renderer = null;
663
- this._current_model = null;
664
- this._current_mname = null;
665
- }
666
- this._renderer.clear();
667
-
668
- if (!name) {
669
- // If called with nothing, just quit here
670
- this._emitDisplayChange();
671
- return;
672
- }
673
-
674
- // if the model isn't in this._models
675
- if (!(name in this._models) &amp;&amp; Object.keys(this._models).length > 0) {
676
- // in case the model does not exist, reset the orbit
677
- this._renderer.resetOrbitCenter(5,5,5);
678
- this.addNotification('The requested model does not exist.')
679
- throw 'The requested model does not exist.';
680
- }
681
-
682
- var m = this._models[name];
683
- m.renderer = this._renderer;
684
-
685
- this._current_model = m;
686
- this._current_mname = name;
687
-
688
- this._displayed = m.find({
689
- 'cell': [
690
- [0, 0, 0]
691
- ]
692
- });
693
- this._selected = new ModelView(m, []); // Empty
694
-
695
- // Set the camera in a way that will center the model
696
- var c = m.fracToAbs([0.5, 0.5, 0.5]);
697
- this._renderer.resetOrbitCenter(c[0], c[1], c[2]);
698
-
699
- this._displayed.show();
700
- this._emitDisplayChange();
701
- }
702
-
703
- /**
704
- * Erase a model from the recorded ones
705
- *
706
- * @param {String} name Name of the model to delete
707
- */
708
- deleteModel(name) {
709
-
710
- if (!(name in this._models)) {
711
- throw 'The requested model does not exist!';
712
- }
713
-
714
- if (this._current_mname == name) {
715
- this.displayModel();
716
- }
717
-
718
- delete this._models[name];
719
- delete this._model_sources[name];
720
- delete this._model_parameters[name];
721
- delete this._model_meta[name];
722
- this._emitModelListChange();
723
- }
724
-
725
- /**
726
- * Add a primitive shape to the drawing
727
- *
728
- * @param {THREE.Object3D} p Primitive to add
729
- */
730
- addPrimitive(p) {
731
- this._renderer.add(p);
732
- }
733
-
734
- /**
735
- * Remove a primitive shape from the drawing
736
- *
737
- * @param {THREE.Object3D} p Primitive to remove
738
- */
739
- removePrimitive(p) {
740
- this._renderer.remove(p);
741
- }
742
-
743
- /**
744
- * Add a notification to the list of notifications to be displayed
745
- */
746
- addNotification(n) {
747
- this._notifications.push(n);
748
- this.addNotifications();
749
- }
750
-
751
- /**
752
- * Adds all notifications to the drawing
753
- *
754
- */
755
- addNotifications() {
756
- // remove displayed notifications
757
- // (doesn't remove them from this._notifications)
758
- this._renderer.clearNotifications();
759
- // add full list of notifications
760
- this._renderer.addNotifications(this._notifications);
761
- }
762
-
763
- /**
764
- * Removes notifications from the drawing
765
- */
766
- clearNotifications() {
767
- this._notifications = [];
768
- this._renderer.clearNotifications();
769
- }
770
-
771
- /**
772
- * Recover a data URL of a PNG screenshot of the current scene
773
- *
774
- * @return {String} A data URL of the PNG screenshot
775
- */
776
- getScreenshotData(transparent = true, scale_pixels = 3) {
777
-
778
- var renderer = this._renderer;
779
- // save current alpha and antialias settings
780
- var old_alpha = renderer._r.getClearAlpha();
781
- var old_PixelRatio = renderer._r.getPixelRatio();
782
-
783
- // set new alpha and antialias settings
784
- renderer._r.setClearAlpha(transparent ? 0 : 1);
785
- renderer._r.setPixelRatio(scale_pixels);
786
-
787
- // Force a render
788
- this._renderer._render();
789
- // Grab the data from the canvas
790
- var data = renderer._r.domElement.toDataURL();
791
-
792
- // restore old alpha and antialias settings
793
- renderer._r.setClearAlpha(old_alpha);
794
- renderer._r.setPixelRatio(old_PixelRatio);
795
-
796
- return data;
797
- }
798
- }
799
-
800
- addStaticVar(CrystVis, 'LEFT_CLICK', 1);
801
- addStaticVar(CrystVis, 'MIDDLE_CLICK', 2);
802
- addStaticVar(CrystVis, 'RIGHT_CLICK', 4);
803
- addStaticVar(CrystVis, 'ALT_BUTTON', 8);
804
- addStaticVar(CrystVis, 'CTRL_BUTTON', 16);
805
- addStaticVar(CrystVis, 'CMD_BUTTON', 16); // Alias for Mac users
806
- addStaticVar(CrystVis, 'SHIFT_BUTTON', 32);
807
-
808
- export {
809
- CrystVis
810
- }</code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><div class="mobile-nav-links"></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="lib_model.module_js.html">lib/model.js</a></div><div class="sidebar-section-children"><a href="lib_modelview.module_js.html">lib/modelview.js</a></div><div class="sidebar-section-children"><a href="lib_visualizer.module_js.html">lib/visualizer.js</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="lib_model.module_js-AtomImage.html">AtomImage</a></div><div class="sidebar-section-children"><a href="lib_model.module_js-BondImage.html">BondImage</a></div><div class="sidebar-section-children"><a href="lib_model.module_js-Model.html">Model</a></div><div class="sidebar-section-children"><a href="lib_modelview.module_js-ModelView.html">ModelView</a></div><div class="sidebar-section-children"><a href="lib_visualizer.module_js-CrystVis.html">CrystVis</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-Events.html">Events</a></div><div class="sidebar-section-children"><a href="tutorial-Queries.html">Queries</a></div><div class="sidebar-section-children"><a href="tutorial-ThreejsMigration.html">ThreejsMigration</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>