diff --git a/package-lock.json b/package-lock.json index bf7faf7..076e611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "mc-react-structure-visualizer", "version": "0.5.0", "dependencies": { - "3dmol": "^2.1.0" + "3dmol": "^2.1.0", + "mathjs": "^12.4.1" }, "devDependencies": { "@types/react": "^18.2.55", @@ -333,7 +334,6 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", - "peer": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1687,6 +1687,18 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/complex.js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1786,6 +1798,11 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2068,6 +2085,11 @@ "node": ">=6" } }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2465,6 +2487,18 @@ "is-callable": "^1.1.3" } }, + "node_modules/fraction.js": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.4.tgz", + "integrity": "sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3161,6 +3195,11 @@ "set-function-name": "^2.0.1" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3310,6 +3349,28 @@ "node": ">=12" } }, + "node_modules/mathjs": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-12.4.1.tgz", + "integrity": "sha512-welnW3khgwYjPYvECFHO+xkCxAx9IKIIPDDWPi8B5rKAvmgoEHnQX9slEmHKZTNaJiE+OS4qrJJcB4sfDn/4sw==", + "dependencies": { + "@babel/runtime": "^7.24.0", + "complex.js": "^2.1.1", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "4.3.4", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.1.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3811,8 +3872,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "peer": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", @@ -3982,6 +4042,11 @@ "loose-envify": "^1.1.0" } }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -4191,6 +4256,11 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4303,6 +4373,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-function": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.1.tgz", + "integrity": "sha512-Pq1DVubcvibmm8bYcMowjVnnMwPVMeh0DIdA8ad8NZY2sJgapANJmiigSUwlt+EgXxpfIv8MWrQXTIzkfYZLYQ==", + "engines": { + "node": ">= 14" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index fe54313..f257dec 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "preview": "vite preview" }, "dependencies": { - "3dmol": "^2.1.0" + "3dmol": "^2.1.0", + "mathjs": "^12.4.1" }, "peerDependencies": { "bootstrap": "^5.2.3", diff --git a/src/StructureVisualizer/ControlBox/index.jsx b/src/StructureVisualizer/ControlBox/index.jsx index 2103398..e25c23d 100644 --- a/src/StructureVisualizer/ControlBox/index.jsx +++ b/src/StructureVisualizer/ControlBox/index.jsx @@ -73,17 +73,16 @@ class ControlBox extends React.Component { this.handleOptionChange("atomLabels")} - label="Atom labels" + checked={this.props.viewerParams.packedCell} + onChange={() => this.handleOptionChange("packedCell")} + label="Packed cell" /> this.handleOptionChange("packedCell")} - disabled={true} - label="Packed cell" + checked={this.props.viewerParams.atomLabels} + onChange={() => this.handleOptionChange("atomLabels")} + label="Atom labels" /> { + let cart = new $3Dmol.Vector3(atom.x, atom.y, atom.z); + if (this.props.viewerParams.packedCell) { + let frac = cart.clone().applyMatrix3(fracConversionMatrix); + let folded_frac = new $3Dmol.Vector3( + mod(frac.x, 1), + mod(frac.y, 1), + mod(frac.z, 1), + ); + // convert back to cartesian + cart = folded_frac.applyMatrix3(cellMatrix); + } + atoms.push({ + elem: atom.elem, + x: cart.x, + y: cart.y, + z: cart.z, + }); + }); + + // Build the supercell + + let sc = this.props.viewerParams.supercell; + for (let i = -1; i < sc[0] + 1; i++) { + for (let j = -1; j < sc[1] + 1; j++) { + for (let k = -1; k < sc[2] + 1; k++) { + let offset = new $3Dmol.Vector3(i, j, k); + offset.applyMatrix3(cellMatrix); + + // prettier-ignore + if ( + i == -1 || i == sc[0] || + j == -1 || j == sc[1] || + k == -1 || k == sc[2] + ) { + // we are outside the specified supercell. + // in case of packed cell, add all atoms from the + // neighboring cells that are exactly on edges + if (this.props.viewerParams.packedCell) { + atoms.forEach((atom) => { + let cart = new $3Dmol.Vector3(atom.x, atom.y, atom.z); + cart.add(offset); + let frac = cart.clone().applyMatrix3(fracConversionMatrix); + + // prettier-ignore + if ( + frac.x > -0.0001 && frac.x < sc[0] + 0.0001 && + frac.y > -0.0001 && frac.y < sc[1] + 0.0001 && + frac.z > -0.0001 && frac.z < sc[2] + 0.0001 + ) { + final_atoms.push({ + elem: atom.elem, + x: cart.x, + y: cart.y, + z: cart.z, + }); + } + }); + } else { + // in "non-packed" case, skip these edge cells + continue + } + } else { + atoms.forEach((atom) => { + final_atoms.push({ + elem: atom.elem, + x: atom.x + offset.x, + y: atom.y + offset.y, + z: atom.z + offset.z, + }); + }); + } + } + } + } + + this.model.addAtoms(final_atoms); + } + } + updateView() { this.viewer.removeAllModels(); - this.model = this.viewer.addModel(this.props.cifText, "cif"); + // this.model = this.viewer.addModel(this.props.cifText, "cif"); + this.custom3dmolSetup(); let style = { sphere: { scale: 0.3, colorscheme: "Jmol" }, @@ -81,8 +204,8 @@ class Visualizer3dmol extends React.Component { this.viewer.setStyle(style); this.viewer.addUnitCell(this.model); - let sc = this.props.viewerParams.supercell; - this.viewer.replicateUnitCell(sc[0], sc[1], sc[2], this.model); + //let sc = this.props.viewerParams.supercell; + //this.viewer.replicateUnitCell(sc[0], sc[1], sc[2], this.model); this.model.assignBonds(); diff --git a/src/StructureVisualizer/index.jsx b/src/StructureVisualizer/index.jsx index bae7696..40263ac 100644 --- a/src/StructureVisualizer/index.jsx +++ b/src/StructureVisualizer/index.jsx @@ -7,8 +7,8 @@ const StructureVisualizer = (props) => { const [viewerParams, setViewerParams] = useState({ supercell: props.initSupercell || [2, 2, 2], bonds: true, + packedCell: true, atomLabels: false, - packedCell: false, vdwRadius: false, }); const [mouseEnabled, setMouseEnabled] = useState(false);