UNPKG

@ccp-nc/crystvis-js

Version:

A Three.js based crystallographic visualisation tool

449 lines (385 loc) 25.5 kB
<!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: modelview.js</title><!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![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">modelview.js</h1></header><article><pre class="prettyprint source lang-js"><code>'use strict'; /** * @fileoverview Class holding "model views", subsets of atoms in a Model used * for selection or to perform operations in block * @module */ import _ from 'lodash'; /** A 'view' representing a subset of atom images of a model, used for selection and further manipulations */ class ModelView { /** * @param {Model} model Model to use for the view * @param {int[]} indices Indices of the atom images to include in the view */ constructor(model, indices) { this._model = model; this._indices = indices; this._images = _.map(indices, function(i) { return model._atom_images[i]; }); this._bonds = {}; for (var i = 0; i &lt; this._images.length; ++i) { var img = this._images[i]; var bonds = img.bonds; for (var j = 0; j &lt; bonds.length; ++j) { var b = bonds[j]; this._bonds[b.key] = b; } } this._bonds = Object.values(this._bonds); } /** * Model used by this view * @readonly * @type {Model} */ get model() { return this._model; } /** * Indices of the atom images in this view * @readonly * @type {int[]} */ get indices() { return Array.from(this._indices); } /** * Atom images in this view * @readonly * @type {AtomImage[]} */ get atoms() { return Array.from(this._images); } /** * Number of atom images in this view * @readonly * @type {int} */ get length() { return this._indices.length; } /** * Multiplicity of the atoms in the view * @readonly * Returns a dictionary with the multiplicities of the atoms in the view * with the keys being the crystallographic labels of the atoms * @type {Object.&lt;string, int>} */ get unique_labels_multiplicity() { // all labels: let labels = this._images.map(function(a) { return a.crystLabel; }); let allIndices = this._indices; // unique labels: let ulabels = _.uniq(labels); // multiplicity of each label: let mult = {}; for (let i = 0; i &lt; ulabels.length; ++i) { let l = ulabels[i]; let indices = []; for (let j = 0; j &lt; labels.length; ++j) { if (labels[j] === l) { indices.push(allIndices[j]); } } mult[l] = indices.length; } return mult; } // Operations on the selected atoms // Visibility /** * Make all atoms in the view visible. Can be chained * @return {ModelView} */ show() { this._model._setAtomsProperty(this._images, 'visible', true); return this; } /** * Make all atoms in the view invisible. Can be chained * @return {ModelView} */ hide() { this._model._setAtomsProperty(this._images, 'visible', false); return this; } /** * Run a function on each AtomImage, returning an Array of the results. * * @param {Function} func Function to run, should take AtomImage and * index as arguments * * @return {Array} Return values */ map(func) { var returns = []; for (var i = 0; i &lt; this.length; ++i) { returns.push(func(this._images[i], i)); } return returns; } /** * Perform a further search within the atoms included in this ModelView. * * @param {Array} query Query for the search, formatted as for * the Model.find function. * * @return {ModelView} Result of the query */ find(query) { var found = this._model._qparse.parse(query); return new ModelView(this._model, _.intersectionWith(this._indices, found)); } // Logical operations with another ModelView /** * Intersection with another ModelView * @param {ModelView} mview Other view * @return {ModelView} Result */ and(mview) { if (this._model != mview._model) throw 'The two ModelViews do not refer to the same Model'; return new ModelView(this._model, _.intersectionWith(this._indices, mview._indices)); } /** * Union with another ModelView * @param {ModelView} mview Other view * @return {ModelView} Result */ or(mview) { if (this._model != mview._model) throw 'The two ModelViews do not refer to the same Model'; return new ModelView(this._model, _.unionWith(this._indices, mview._indices)); } /** * Exclusive OR with another ModelView * @param {ModelView} mview Other view * @return {ModelView} Result */ xor(mview) { if (this._model != mview._model) throw 'The two ModelViews do not refer to the same Model'; return new ModelView(this._model, _.xorWith(this._indices, mview._indices)); } /** * Complement to this ModelView * @return {ModelView} Result */ not() { var indices = _.xorWith(this._indices, _.range(this._model._atom_images.length)); return new ModelView(this._model, indices); } /** * Remove all atoms in mview from the current view */ remove(mview) { if (this._model != mview._model) throw 'The two ModelViews do not refer to the same Model'; return new ModelView(this._model, _.differenceWith(this._indices, mview._indices)); } /** * Unique atoms in the current view (based on site labels) */ uniqueSites() { // all labels: var labels = this._images.map(function(a) { return a.crystLabel; }); var allIndices = this._indices; // unique labels: var ulabels = _.uniq(labels); // indices of unique labels: var uindices = ulabels.map(function(l) { return allIndices[labels.indexOf(l)]; }); // sort the uindices: uindices.sort(function(a, b) { return a - b; }); // return the unique atoms: return new ModelView(this._model, uindices); } /** * Internal function used to turn a single value, array of values, or * function into an array of values * @private */ _standardValueArray(value) { if (_.isFunction(value)) { value = this.map(value); } else if (!(value instanceof Array)) { value = Array(this.length).fill(value); } return Array.from(value); } /** * Set some property of the atoms within the ModelView. * * @param {String} name Name of the property to set * @param {int|Array|function} value Value to set for the atoms. It can * be either: * * 1. a single value for all of them * 2. an Array of values as long as * the ModelView * 3. a function that accepts an * AtomImage and an index and returns * a value * * If left empty, the property is * restored to its default value. */ setProperty(name, value=null) { if (name[0] == '_') { // Assignment of hidden properties not supported! throw 'Can not assign a value to hidden properties'; } value = this._standardValueArray(value); for (var i = 0; i &lt; this.length; ++i) { var v = value[i]; var aimg = this._images[i]; aimg[name] = v; } return this; } /** * Get sorted set of unique elements in the ModelView */ get elements() { return _.uniq(this.map(a => a.element).sort()); } /** * Add labels to the atom images in this ModelView * * @param {String | String[] | Function} text Text of the labels, * as single value, array, * or function returning a * string for each atom image. * @param {String | String[] | Function} name Name of the label * @param {Object | Object[] | Function} args Arguments for creating the label */ addLabels(text, name = 'label', args = {}) { // Defaults if (!text) { text = function(a) { return a.element; } } text = this._standardValueArray(text); name = this._standardValueArray(name); args = this._standardValueArray(args); for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; aimg.addLabel(String(text[i]), name[i], args[i]); } return this; } /** * Remove labels from the atom images in this ModelView * * @param {String | String[] | Function} name Name of the labels to remove */ removeLabels(name = 'label') { name = this._standardValueArray(name); for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; aimg.removeLabel(name[i]); } return this; } /** * Get or set labels' properties for the atom images in this ModelView * * @param {String | String[] | Function} name Name of the labels * @param {String | String[] | Function} property Property to get or set * @param {Any | Any[] | Function} value If not provided, get. If provided, * set this value */ labelProperties(name = 'label', property = 'color', value = null) { name = this._standardValueArray(name); property = this._standardValueArray(property); var ans = null; if (value !== null) { value = this._standardValueArray(value); ans = []; } for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; if (value !== null) { aimg.labelProperty(name[i], property[i], value[i]); } else { ans.push(aimg.labelProperty(name[i], property[i])); } } if (value === null) return ans; else return this; } /** * Add ellipsoids to the atom images in this ModelView * * @param {Object | Object[] | Function} data Data to use for the ellipsoid * (see AtomImage.addEllipsoid for details) * @param {String | String[] | Function} name Name of the ellipsoids * @param {Object | Object[] | Function} args Arguments for creating the ellipsoids */ addEllipsoids(data, name = 'ellipsoid', args = {}) { data = this._standardValueArray(data); name = this._standardValueArray(name); args = this._standardValueArray(args); for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; aimg.addEllipsoid(data[i], name[i], args[i]); } return this; } /** * Remove ellipsoids from the atom images in this ModelView * * @param {String | String[] | Function} name Name of the ellipsoids to remove */ removeEllipsoids(name = 'ellipsoid') { name = this._standardValueArray(name); for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; aimg.removeEllipsoid(name[i]); } return this; } /** * Get or set ellipsoids' properties for the atom images in this ModelView * * @param {String | String[] | Function} name Name of the ellipsoids * @param {String | String[] | Function} property Property to get or set * @param {Any | Any[] | Function} value If not provided, get. If provided, * set this value */ ellipsoidProperties(name = 'ellipsoid', property = 'color', value = null) { name = this._standardValueArray(name); property = this._standardValueArray(property); var ans = null; if (value !== null) { value = this._standardValueArray(value); ans = []; } for (var i = 0; i &lt; this.length; ++i) { var aimg = this._images[i]; if (value !== null) { aimg.ellipsoidProperty(name[i], property[i], value[i]); } else { ans.push(aimg.ellipsoidProperty(name[i], property[i])); } } if (value === null) return ans; else return this; } } export { ModelView }</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>