mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
944d370c14 | ||
|
|
74f9aa6af6 | ||
|
|
c20c9c9917 | ||
|
|
4801435d72 | ||
|
|
33fd105ef7 | ||
|
|
3ea3fb8984 | ||
|
|
b4bbc544ca | ||
|
|
5f880e920b | ||
|
|
bcce801dd7 | ||
|
|
00f9dcee4a | ||
|
|
505af2bc96 | ||
|
|
c217aab5fc | ||
|
|
5d5fd0028f | ||
|
|
c88693dfdd | ||
|
|
0a16ec1bd2 | ||
|
|
6f36a3c724 | ||
|
|
71496bd1ef | ||
|
|
c5a99a7c12 | ||
|
|
cfaf33d696 | ||
|
|
e24c76d2bf | ||
|
|
b6273205a2 | ||
|
|
b38193aa19 | ||
|
|
f9cfacae23 | ||
|
|
ac46317dc6 | ||
|
|
2b492a5a61 | ||
|
|
a2133657f0 | ||
|
|
e8de45789f | ||
|
|
3d2bd167ca | ||
|
|
504c8626dc | ||
|
|
f4b29dc7e0 | ||
|
|
b34c5c743b | ||
|
|
c57311d6c0 | ||
|
|
4d786dc697 | ||
|
|
062e3e055a | ||
|
|
1465174a45 | ||
|
|
cc00ada5a3 | ||
|
|
cbf312b62d | ||
|
|
2be3144086 | ||
|
|
da3acd9d19 | ||
|
|
34b048479b | ||
|
|
ca92931bf2 | ||
|
|
0d3daeb823 | ||
|
|
58b1d7e0eb | ||
|
|
c5997ed056 | ||
|
|
da1deee7f3 | ||
|
|
a4eaff3175 | ||
|
|
211cfc0bd3 | ||
|
|
c0f85b691d | ||
|
|
6b93d58ea6 | ||
|
|
5bce423b49 | ||
|
|
01b0dde503 | ||
|
|
ed1bc8cb7d | ||
|
|
abe559261b | ||
|
|
b0cdf22cb8 | ||
|
|
a61ba71f1e | ||
|
|
7061d57559 | ||
|
|
83a1e6c87c | ||
|
|
c18888b8de | ||
|
|
2d80935e00 | ||
|
|
4287d158b6 | ||
|
|
4e9b569178 | ||
|
|
5d626d291b | ||
|
|
afa4a01c44 | ||
|
|
3c4a23c5a3 | ||
|
|
8d92c976d9 | ||
|
|
1a9adfad29 | ||
|
|
a9e9a5974d | ||
|
|
27160aa8fe | ||
|
|
6a71af00cf | ||
|
|
6c0938db50 | ||
|
|
b76173c82f | ||
|
|
0bcf2b1ff4 | ||
|
|
d074415a26 | ||
|
|
42f3e38026 | ||
|
|
74c16ee7ba | ||
|
|
f6ef22b917 | ||
|
|
c3f3d7efda | ||
|
|
61d44efdc4 | ||
|
|
ad200c86ec | ||
|
|
4aecf4e0b4 | ||
|
|
d81f37c78b | ||
|
|
c2979ce5ab | ||
|
|
09c7edce88 | ||
|
|
beefb79258 | ||
|
|
529c6ac81c | ||
|
|
84b988ea96 | ||
|
|
35e978efc9 | ||
|
|
19559d01f7 | ||
|
|
56cec343e2 | ||
|
|
20c9bd0130 | ||
|
|
1336997c58 | ||
|
|
b178fdefdc | ||
|
|
453d60060a | ||
|
|
901fac97a0 | ||
|
|
29b6a88343 | ||
|
|
a2217c7fc6 | ||
|
|
3eec30aa42 | ||
|
|
f352e19e90 | ||
|
|
be65ef89bc | ||
|
|
cfc46073c0 | ||
|
|
1819a2bbda | ||
|
|
aa3a42f94e | ||
|
|
4bff55b612 | ||
|
|
dbc4e09909 | ||
|
|
0a074c8a66 | ||
|
|
8b314ebb75 | ||
|
|
4dc0791aeb | ||
|
|
e3483f11b1 | ||
|
|
244a678ab7 | ||
|
|
8e1d44fabc | ||
|
|
954a5b58e0 | ||
|
|
53223bc72b | ||
|
|
a11a1fa07e | ||
|
|
9ab4001544 | ||
|
|
3e3b71d230 | ||
|
|
186929269b | ||
|
|
afa7a04af0 | ||
|
|
2861d12f04 | ||
|
|
65e1212b2f | ||
|
|
47136c8b71 | ||
|
|
375b829562 | ||
|
|
2718d42b01 | ||
|
|
93d4118c0a | ||
|
|
3a2a47af12 | ||
|
|
0eacdfca85 | ||
|
|
a7388be25f | ||
|
|
5fcdcb1275 | ||
|
|
4635bdffb0 | ||
|
|
72b1c36111 | ||
|
|
fc5ff601a8 | ||
|
|
8dd142fc9f | ||
|
|
97f24293e2 | ||
|
|
f4beba5215 | ||
|
|
e2abe0f52a | ||
|
|
5d18643374 | ||
|
|
769f2a30c2 | ||
|
|
0e040d7744 | ||
|
|
7600d0a44e | ||
|
|
9113d6d189 | ||
|
|
fd9dac86b9 | ||
|
|
fe1f3bd4bb | ||
|
|
d950718110 | ||
|
|
4be903b9d5 | ||
|
|
8050644869 | ||
|
|
347c1986df | ||
|
|
eac4a8988b | ||
|
|
25137f29d2 | ||
|
|
4601fe74bb | ||
|
|
f0b54e9cbf | ||
|
|
fdb45c3624 | ||
|
|
f3b1ec0ed8 | ||
|
|
79510614b9 | ||
|
|
f8c9bc6812 | ||
|
|
85d35aab90 | ||
|
|
f5b09dbd10 | ||
|
|
57ea322fd6 | ||
|
|
c31aab5594 | ||
|
|
a5556e8c41 | ||
|
|
0ce2966c47 | ||
|
|
f2c04a13af | ||
|
|
60ba0de219 | ||
|
|
99e515604e | ||
|
|
84dfa60fc2 | ||
|
|
a753095f92 | ||
|
|
ce0ff2fed8 |
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -4,7 +4,9 @@
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"firsttris.vscode-jest-runner",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
"wayou.vscode-todo-highlight"
|
||||
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Mol* Viewer",
|
||||
"url": "http://localhost:1338/build/viewer/index.html",
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -143,7 +143,7 @@ To get syntax highlighting for shader and graphql files add the following to Vis
|
||||
|
||||
## Deploy
|
||||
npm run test
|
||||
NODE_ENV=production npm run build
|
||||
npm run build
|
||||
node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
|
||||
|
||||
## Contributing
|
||||
|
||||
330
package-lock.json
generated
330
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.7",
|
||||
"version": "0.3.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1363,14 +1363,14 @@
|
||||
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz",
|
||||
"integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg=="
|
||||
"version": "12.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz",
|
||||
"integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz",
|
||||
"integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.2.tgz",
|
||||
"integrity": "sha512-djYYKmdNRSBtL1x4CiE9UJb9yZhwtI1VC+UxZD0psNznrUj80ywsxKlEGAE+QL1qvLjPbfb24VosjkYM6W4RSQ==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -1382,9 +1382,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz",
|
||||
"integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg=="
|
||||
"version": "15.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
@@ -1392,18 +1392,18 @@
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.2.tgz",
|
||||
"integrity": "sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==",
|
||||
"version": "16.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.5.tgz",
|
||||
"integrity": "sha512-jQ12VMiFOWYlp+j66dghOWcmDDwhca0bnlcTxS4Qz/fh5gi6wpaZDthPEu/Gc/YlAuO87vbiUXL8qKstFvuOaA==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.0.tgz",
|
||||
"integrity": "sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==",
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.1.tgz",
|
||||
"integrity": "sha512-1S/akvkKr63qIUWVu5IKYou2P9fHLb/P2VAwyxVV85JGaGZTcUniMiTuIqM3lXFB25ej6h+CYEQ27ERVwi6eGA==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -2365,7 +2365,7 @@
|
||||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -2410,7 +2410,7 @@
|
||||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -2462,7 +2462,7 @@
|
||||
},
|
||||
"buffer": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -3051,13 +3051,13 @@
|
||||
}
|
||||
},
|
||||
"concurrently": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.2.tgz",
|
||||
"integrity": "sha512-Kim9SFrNr2jd8/0yNYqDTFALzUX1tvimmwFWxmp/D4mRI+kbqIIwE2RkBDrxS2ic25O1UgQMI5AtBqdtX3ynYg==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.0.0.tgz",
|
||||
"integrity": "sha512-1yDvK8mduTIdxIxV9C60KoiOySUl/lfekpdbI+U5GXaPrgdffEavFa9QZB3vh68oWOpbCC+TuvxXV9YRPMvUrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"date-fns": "^1.30.1",
|
||||
"date-fns": "^2.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"read-pkg": "^4.0.1",
|
||||
"rxjs": "^6.5.2",
|
||||
@@ -3099,9 +3099,9 @@
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.4.1.tgz",
|
||||
"integrity": "sha512-2RhmH/sjDSCYW2F3ZQxOUx/I7PvzXpi89aQL2d3OAxSTwLx6NilATeUbe0menFE3Lu5lFkOFci36ivimwYHHxw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
@@ -3262,7 +3262,7 @@
|
||||
},
|
||||
"create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -3275,7 +3275,7 @@
|
||||
},
|
||||
"create-hmac": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -3520,9 +3520,9 @@
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.6.tgz",
|
||||
"integrity": "sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg=="
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
|
||||
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ=="
|
||||
},
|
||||
"currently-unhandled": {
|
||||
"version": "0.4.1",
|
||||
@@ -3534,9 +3534,9 @@
|
||||
}
|
||||
},
|
||||
"cyclist": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
|
||||
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
||||
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
|
||||
"dev": true
|
||||
},
|
||||
"d": {
|
||||
@@ -3749,9 +3749,9 @@
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
||||
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"diff-sequences": {
|
||||
@@ -3762,7 +3762,7 @@
|
||||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -3855,9 +3855,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz",
|
||||
"integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==",
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
|
||||
"integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@@ -3883,7 +3883,7 @@
|
||||
},
|
||||
"enabled": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
|
||||
"integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -4368,7 +4368,7 @@
|
||||
},
|
||||
"fecha": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
|
||||
"integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
|
||||
"dev": true
|
||||
},
|
||||
@@ -4996,7 +4996,8 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -5020,13 +5021,15 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -5043,19 +5046,22 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -5186,7 +5192,8 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -5200,6 +5207,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -5216,6 +5224,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -5224,13 +5233,15 @@
|
||||
"version": "0.0.8",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -5251,6 +5262,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -5339,7 +5351,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -5353,6 +5366,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -5448,7 +5462,8 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -5490,6 +5505,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -5511,6 +5527,7 @@
|
||||
"resolved": false,
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -5559,13 +5576,15 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5794,9 +5813,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"graphql": {
|
||||
"version": "14.4.2",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-14.4.2.tgz",
|
||||
"integrity": "sha512-6uQadiRgnpnSS56hdZUSvFrVcQ6OF9y6wkxJfKquFtHlnl7+KSuWwSJsdwiK1vybm1HgcdbpGkCpvhvsVQ0UZQ==",
|
||||
"version": "14.5.8",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-14.5.8.tgz",
|
||||
"integrity": "sha512-MMwmi0zlVLQKLdGiMfWkgQD7dY/TUKt4L+zgJ/aR0Howebod3aNgP5JkgvAULiR2HPVZaP2VEElqtdidHweLkg==",
|
||||
"requires": {
|
||||
"iterall": "^1.2.2"
|
||||
}
|
||||
@@ -6567,9 +6586,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
|
||||
"integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.3.0.tgz",
|
||||
"integrity": "sha512-7XlnO8yBXOdi7AzowjZssQr47Ctidqm7GbgARapOaqSN9HQhlClnOkR9HieGauIT3A8MBC6u9wPCXs97PCYpWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"neo-async": "^2.6.0",
|
||||
@@ -9421,6 +9440,12 @@
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
@@ -10610,12 +10635,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"parallel-transform": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
|
||||
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
|
||||
"integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cyclist": "~0.2.2",
|
||||
"cyclist": "^1.0.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^2.1.5"
|
||||
}
|
||||
@@ -10630,9 +10655,9 @@
|
||||
}
|
||||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
|
||||
"integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
|
||||
"integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asn1.js": "^4.0.0",
|
||||
@@ -11316,9 +11341,9 @@
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.9.0.tgz",
|
||||
"integrity": "sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==",
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz",
|
||||
"integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -11326,14 +11351,14 @@
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz",
|
||||
"integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==",
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz",
|
||||
"integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.15.0"
|
||||
"scheduler": "^0.16.2"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
@@ -11821,9 +11846,9 @@
|
||||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
|
||||
"integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
|
||||
"integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
@@ -12151,7 +12176,7 @@
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
}
|
||||
@@ -12366,24 +12391,78 @@
|
||||
}
|
||||
},
|
||||
"sass-loader": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz",
|
||||
"integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.0.tgz",
|
||||
"integrity": "sha512-+qeMu563PN7rPdit2+n5uuYVR0SSVwm0JsOUsaJXzgYcClWSlmX0iHDnmeOobPkf5kUglVot3QS6SyLyaQoJ4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone-deep": "^4.0.1",
|
||||
"loader-utils": "^1.0.1",
|
||||
"neo-async": "^2.5.0",
|
||||
"pify": "^4.0.1",
|
||||
"loader-utils": "^1.2.3",
|
||||
"neo-async": "^2.6.1",
|
||||
"schema-utils": "^2.1.0",
|
||||
"semver": "^6.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
|
||||
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
|
||||
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^2.0.0",
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.2.0.tgz",
|
||||
"integrity": "sha512-5EwsCNhfFTZvUreQhx/4vVQpJ/lnCAkgoIHLhSpp4ZirE+4hzFvdJi0FMub6hxbFVBJYSpeVVmon+2e7uEGRrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.10.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@@ -12399,9 +12478,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
|
||||
"integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==",
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz",
|
||||
"integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@@ -12482,9 +12561,9 @@
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.8.0.tgz",
|
||||
"integrity": "sha512-3tHgtF4OzDmeKYj6V9nSyceRS0UJ3C7VqyD2Yj28vC/z2j6jG5FmFGahOKMD9CrglxTm3tETr87jEypaYV8DUg==",
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
|
||||
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
|
||||
"dev": true
|
||||
},
|
||||
"serve-static": {
|
||||
@@ -12546,7 +12625,7 @@
|
||||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -12611,9 +12690,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"simple-git": {
|
||||
"version": "1.124.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.124.0.tgz",
|
||||
"integrity": "sha512-ks9mBoO4ODQy/xGLC8Cc+YDvj/hho/IKgPhi6h5LI/sA+YUdHc3v0DEoHzM29VmulubpGCxMJUSFmyXNsjNMEA==",
|
||||
"version": "1.126.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.126.0.tgz",
|
||||
"integrity": "sha512-47mqHxgZnN8XRa9HbpWprzUv3Ooqz9RY/LSZgvA7jCkW8jcwLahMz7LKugY91KZehfG0sCVPtgXiU72hd6b1Bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.0.1"
|
||||
@@ -13017,7 +13096,7 @@
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -13201,9 +13280,9 @@
|
||||
}
|
||||
},
|
||||
"swagger-ui-dist": {
|
||||
"version": "3.23.5",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.23.5.tgz",
|
||||
"integrity": "sha512-fsCF3wR0kBF5G7yF8uD5kALSbo2TjpfdheXWnrmxD2/d/8bgKDlUVoqO4gMNrZRQOEbyXCexZiU9geMNspWWyA=="
|
||||
"version": "3.23.11",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.23.11.tgz",
|
||||
"integrity": "sha512-ipENHHH/sqpngTpHXUwg55eAOZ7b2UVayUwwuWPA6nQSPhjBVXX4zOPpNKUwQIFOl3oIwVvZF7mqoxH7pMgVzA=="
|
||||
},
|
||||
"swap-case": {
|
||||
"version": "1.1.2",
|
||||
@@ -13245,9 +13324,9 @@
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.2.0.tgz",
|
||||
"integrity": "sha512-6lPt7lZdZ/13icQJp8XasFOwZjFJkxFFIb/N1fhYEQNoNI3Ilo3KABZ9OocZvZoB39r6SiIk/0+v/bt8nZoSeA==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.2.tgz",
|
||||
"integrity": "sha512-obxk4x19Zlzj9zY4QeXj9iPCb5W8YGn4v3pn4/fHj0Nw8+R7N02Kvwvz9VpOItCZZD8RC+vnYCDL0gP6FAJ7Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
@@ -13584,15 +13663,16 @@
|
||||
}
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "24.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz",
|
||||
"integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==",
|
||||
"version": "24.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.1.0.tgz",
|
||||
"integrity": "sha512-HEGfrIEAZKfu1pkaxB9au17b1d9b56YZSqz5eCVE8mX68+5reOvlM93xGOzzCREIov9mdH7JBG+s0UyNAqr0tQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bs-logger": "0.x",
|
||||
"buffer-from": "1.x",
|
||||
"fast-json-stable-stringify": "2.x",
|
||||
"json5": "2.x",
|
||||
"lodash.memoize": "4.x",
|
||||
"make-error": "1.x",
|
||||
"mkdirp": "0.x",
|
||||
"resolve": "1.x",
|
||||
@@ -13622,9 +13702,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs-parser": {
|
||||
@@ -13650,16 +13730,16 @@
|
||||
"integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.19.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.19.0.tgz",
|
||||
"integrity": "sha512-1LwwtBxfRJZnUvoS9c0uj8XQtAnyhWr9KlNvDIdB+oXyT+VpsOAaEhEgKi1HrZ8rq0ki/AAnbGSv4KM6/AfVZw==",
|
||||
"version": "5.20.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz",
|
||||
"integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"diff": "^4.0.1",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"minimatch": "^3.0.4",
|
||||
@@ -13752,9 +13832,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
|
||||
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
@@ -14446,9 +14526,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.39.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.39.2.tgz",
|
||||
"integrity": "sha512-AKgTfz3xPSsEibH00JfZ9sHXGUwIQ6eZ9tLN8+VLzachk1Cw2LVmy+4R7ZiwTa9cZZ15tzySjeMui/UnSCAZhA==",
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.0.tgz",
|
||||
"integrity": "sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.8.5",
|
||||
@@ -14836,9 +14916,9 @@
|
||||
}
|
||||
},
|
||||
"webpack-cli": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.7.tgz",
|
||||
"integrity": "sha512-OhTUCttAsr+IZSMVwGROGRHvT+QAs8H6/mHIl4SvhAwYywjiylYjpwybGx7WQ9Hkb45FhjtsymkwiRRbGJ1SZQ==",
|
||||
"version": "3.3.9",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz",
|
||||
"integrity": "sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "2.4.2",
|
||||
@@ -15160,7 +15240,7 @@
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
||||
41
package.json
41
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.7",
|
||||
"version": "0.3.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -15,15 +15,16 @@
|
||||
"test": "npm run lint && jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"build-tsc": "tsc",
|
||||
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/",
|
||||
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
|
||||
"build-webpack": "webpack --mode production",
|
||||
"watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
"watch-tsc": "tsc -watch",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/ --watch",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --display minimal",
|
||||
"serve": "http-server -p 1338",
|
||||
"model-server": "node lib/servers/model/server.js",
|
||||
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
|
||||
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
@@ -64,7 +65,7 @@
|
||||
"devDependencies": {
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^4.1.2",
|
||||
"concurrently": "^5.0.0",
|
||||
"cpx": "^1.5.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
@@ -80,14 +81,14 @@
|
||||
"node-sass": "^4.12.0",
|
||||
"raw-loader": "^3.1.0",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"simple-git": "^1.124.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"simple-git": "^1.126.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint": "^5.19.0",
|
||||
"typescript": "^3.5.3",
|
||||
"webpack": "^4.39.2",
|
||||
"webpack-cli": "^3.3.7"
|
||||
"ts-jest": "^24.1.0",
|
||||
"tslint": "^5.20.0",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.0",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^1.0.36",
|
||||
@@ -95,22 +96,22 @@
|
||||
"@types/compression": "1.0.1",
|
||||
"@types/express": "^4.17.1",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.2",
|
||||
"@types/node-fetch": "^2.5.0",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/node": "^12.7.12",
|
||||
"@types/node-fetch": "^2.5.2",
|
||||
"@types/react": "^16.9.5",
|
||||
"@types/react-dom": "^16.9.1",
|
||||
"@types/swagger-ui-dist": "3.0.3",
|
||||
"@types/webgl2": "0.0.5",
|
||||
"argparse": "^1.0.10",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql": "^14.5.8",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"rxjs": "^6.5.2",
|
||||
"swagger-ui-dist": "^3.23.5",
|
||||
"react": "^16.10.2",
|
||||
"react-dom": "^16.10.2",
|
||||
"rxjs": "^6.5.3",
|
||||
"swagger-ui-dist": "^3.23.11",
|
||||
"util.promisify": "^1.0.0",
|
||||
"xhr2": "^0.2.0"
|
||||
}
|
||||
|
||||
@@ -19,4 +19,12 @@ export class BasicWrapperControls extends PluginUIComponent {
|
||||
<TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomToastMessage extends PluginUIComponent {
|
||||
render() {
|
||||
return <>
|
||||
Custom <i>Toast</i> content. No timeout.
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,10 @@
|
||||
|
||||
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
|
||||
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
|
||||
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
|
||||
|
||||
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
|
||||
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import { StateBuilder, StateTransform } from '../../mol-state';
|
||||
import { StripedResidues } from './coloring';
|
||||
// import { BasicWrapperControls } from './controls';
|
||||
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
|
||||
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
|
||||
import { CustomToastMessage } from './controls';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
@@ -152,6 +154,28 @@ class BasicWrapper {
|
||||
dynamicSuperposition: async () => {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
|
||||
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
|
||||
},
|
||||
toggleValidationTooltip: async () => {
|
||||
const state = this.plugin.state.behaviorState;
|
||||
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
},
|
||||
showToasts: () => {
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
title: 'Toast 1',
|
||||
message: 'This is an example text, timeout 3s',
|
||||
key: 'toast-1',
|
||||
timeoutMs: 3000
|
||||
});
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
title: 'Toast 2',
|
||||
message: CustomToastMessage,
|
||||
key: 'toast-2'
|
||||
});
|
||||
},
|
||||
hideToasts: () => {
|
||||
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-1' });
|
||||
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-2' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { OrderedSet } from '../../mol-data/int';
|
||||
import { openCif, downloadCif } from './helpers';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
|
||||
import { Sequence } from '../../mol-model/sequence';
|
||||
|
||||
|
||||
async function downloadFromPdb(pdb: string) {
|
||||
@@ -110,9 +111,10 @@ export function printSequence(model: Model) {
|
||||
console.log('\nSequence\n=============');
|
||||
const { byEntityKey } = model.sequence;
|
||||
for (const key of Object.keys(byEntityKey)) {
|
||||
const seq = byEntityKey[+key];
|
||||
console.log(`${seq.entityId} (${seq.sequence.kind} ${seq.num.value(0)} (offset ${seq.sequence.offset}), ${seq.num.value(seq.num.rowCount - 1)}) (${seq.compId.value(0)}, ${seq.compId.value(seq.compId.rowCount - 1)})`);
|
||||
console.log(`${seq.sequence.sequence}`);
|
||||
const { sequence, entityId } = byEntityKey[+key];
|
||||
const { seqId, compId } = sequence
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${Sequence.getSequenceString(sequence)}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
@@ -159,14 +161,14 @@ export function printUnits(structure: Structure) {
|
||||
console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`);
|
||||
|
||||
const props = StructureProperties.coarse;
|
||||
const seq = l.unit.model.sequence;
|
||||
const modelSeq = l.unit.model.sequence;
|
||||
|
||||
for (let j = 0, _j = Math.min(size, 3); j < _j; j++) {
|
||||
l.element = OrderedSet.getAt(elements, j);
|
||||
|
||||
const residues: string[] = [];
|
||||
const start = props.seq_id_begin(l), end = props.seq_id_end(l);
|
||||
const compId = seq.byEntityKey[props.entityKey(l)].compId.value;
|
||||
const compId = modelSeq.byEntityKey[props.entityKey(l)].sequence.compId.value;
|
||||
for (let e = start; e <= end; e++) residues.push(compId(e));
|
||||
console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { StateAction } from '../../../../mol-state';
|
||||
import { PluginContext } from '../../../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { Ingredient, CellPacking } from './data';
|
||||
import { Ingredient, CellPacking, Cell } from './data';
|
||||
import { getFromPdb, getFromCellPackDB } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext } from '../../../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
|
||||
@@ -28,6 +28,11 @@ import { compile } from '../../../../mol-script/runtime/query/compiler';
|
||||
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
|
||||
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
|
||||
import { ColorTheme } from '../../../../mol-theme/color';
|
||||
import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
|
||||
import { ModelFormat } from '../../../../mol-model-formats/structure/format';
|
||||
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../../../mol-data/db';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`
|
||||
@@ -118,6 +123,109 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
return builder.getStructure();
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const d = model.sourceData.data.atom_site
|
||||
const n = d._rowCount
|
||||
const rowCount = n * transforms.length
|
||||
|
||||
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
|
||||
|
||||
const x = d.Cartn_x.toArray()
|
||||
const y = d.Cartn_y.toArray()
|
||||
const z = d.Cartn_z.toArray()
|
||||
|
||||
const Cartn_x = new Float32Array(rowCount)
|
||||
const Cartn_y = new Float32Array(rowCount)
|
||||
const Cartn_z = new Float32Array(rowCount)
|
||||
const map = new Uint32Array(rowCount)
|
||||
const seq = new Int32Array(rowCount)
|
||||
let offset = 0
|
||||
for (let c = 0; c < count; ++c) {
|
||||
const cStart = offsets[c]
|
||||
const cEnd = offsets[c + 1]
|
||||
const cLength = cEnd - cStart
|
||||
for (let t = 0, tl = transforms.length; t < tl; ++t) {
|
||||
const m = transforms[t]
|
||||
for (let j = cStart; j < cEnd; ++j) {
|
||||
const i = offset + j - cStart
|
||||
const xj = x[j], yj = y[j], zj = z[j]
|
||||
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
|
||||
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
|
||||
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
|
||||
map[i] = j
|
||||
seq[i] = t + 1
|
||||
}
|
||||
offset += cLength
|
||||
}
|
||||
}
|
||||
|
||||
function multColumn<T>(column: Column<T>) {
|
||||
const array = column.toArray()
|
||||
return Column.ofLambda({
|
||||
value: row => array[map[row]],
|
||||
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
|
||||
rowCount, schema: column.schema
|
||||
})
|
||||
}
|
||||
|
||||
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
|
||||
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
|
||||
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
|
||||
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
|
||||
auth_seq_id: CifField.ofNumbers(seq),
|
||||
|
||||
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
|
||||
Cartn_x: CifField.ofNumbers(Cartn_x),
|
||||
Cartn_y: CifField.ofNumbers(Cartn_y),
|
||||
Cartn_z: CifField.ofNumbers(Cartn_z),
|
||||
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
|
||||
id: CifField.ofColumn(Column.ofLambda({
|
||||
value: row => row,
|
||||
areValuesEqual: (rowA, rowB) => rowA === rowB,
|
||||
rowCount, schema: d.id.schema,
|
||||
})),
|
||||
|
||||
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
|
||||
|
||||
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
|
||||
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
|
||||
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
|
||||
label_seq_id: CifField.ofNumbers(seq),
|
||||
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
|
||||
|
||||
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
|
||||
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
|
||||
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
|
||||
}
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
}
|
||||
|
||||
return {
|
||||
header: name,
|
||||
categoryNames: Object.keys(categories),
|
||||
categories
|
||||
};
|
||||
}
|
||||
|
||||
async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model)
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = ModelFormat.mmCIF(cif)
|
||||
const models = await _parse_mmCif(format, ctx)
|
||||
return models[0]
|
||||
})
|
||||
|
||||
const curveModel = await curveModelTask.run()
|
||||
return getStructure(curveModel)
|
||||
}
|
||||
|
||||
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const { name, source, results, nbCurve } = ingredient
|
||||
|
||||
@@ -133,10 +241,12 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const model = await getModel(source.pdb || name, baseUrl)
|
||||
if (!model) return
|
||||
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
const transforms = nbCurve ? getCurveTransforms(ingredient) : getResultTransforms(results)
|
||||
const assembly = getAssembly(transforms, structure)
|
||||
return assembly
|
||||
if (nbCurve) {
|
||||
return getCurve(name, getCurveTransforms(ingredient), model)
|
||||
} else {
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
return getAssembly(getResultTransforms(results), structure)
|
||||
}
|
||||
}
|
||||
|
||||
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
|
||||
@@ -144,14 +254,16 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
const { ingredients, name } = packing
|
||||
const structures: Structure[] = []
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) ctx.update(iName)
|
||||
if (ctx.shouldUpdate) await ctx.update(iName)
|
||||
const s = await getIngredientStructure(ingredients[iName], baseUrl)
|
||||
if (s) structures.push(s)
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
|
||||
const builder = Structure.Builder({ label: name })
|
||||
let offsetInvariantId = 0
|
||||
for (const s of structures) {
|
||||
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
|
||||
let maxInvariantId = 0
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId
|
||||
@@ -161,6 +273,7 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
offsetInvariantId += maxInvariantId
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
|
||||
const s = builder.getStructure()
|
||||
return s
|
||||
})
|
||||
@@ -175,6 +288,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
['curveTest', 'Curve Test'],
|
||||
]),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
preset: PD.Group({
|
||||
@@ -183,7 +297,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['spacefill', 'Spacefill'],
|
||||
['gaussian-surface', 'Gaussian Surface'],
|
||||
['point', 'Point'],
|
||||
])
|
||||
] as ['spacefill' | 'gaussian-surface' | 'point', string][])
|
||||
}, { isExpanded: true })
|
||||
},
|
||||
from: PSO.Root
|
||||
@@ -192,14 +306,50 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
const root = state.build().toRoot();
|
||||
|
||||
const cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
let cellPackBuilder: any
|
||||
|
||||
if (params.id === 'curveTest') {
|
||||
const url = `${params.baseUrl}/extras/rna_allpoints.json`
|
||||
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
|
||||
const { points } = await (new Response(data)).json() as { points: number[] }
|
||||
const curve0: Vec3[] = []
|
||||
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j))
|
||||
}
|
||||
const cell: Cell = {
|
||||
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
|
||||
compartments: {
|
||||
'CurveCompartment': {
|
||||
interior: {
|
||||
ingredients: {
|
||||
'CurveIngredient': {
|
||||
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
|
||||
results: [],
|
||||
name: 'RNA',
|
||||
nbCurve: 1,
|
||||
curve0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
} else {
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
|
||||
const { packings } = cellPackObject.data
|
||||
let tree = state.build().to(cellPackBuilder.ref);
|
||||
const tree = state.build().to(cellPackBuilder.ref);
|
||||
|
||||
const isHiv = (
|
||||
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
|
||||
@@ -256,7 +406,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
if (isHiv) {
|
||||
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
|
||||
tree.apply(StateTransforms.Data.Download, { url, isBinary: true }, { state: { isGhost: true } })
|
||||
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
@@ -270,7 +420,9 @@ export const LoadCellPackModel = StateAction.build({
|
||||
)
|
||||
}
|
||||
|
||||
console.time('cellpack')
|
||||
await state.updateTree(tree).runInContext(taskCtx);
|
||||
console.timeEnd('cellpack')
|
||||
}));
|
||||
|
||||
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
|
||||
|
||||
@@ -14,8 +14,8 @@ async function parseCif(data: string|Uint8Array) {
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function parsePDBfile(data: string) {
|
||||
const comp = parsePDB(data);
|
||||
async function parsePDBfile(data: string, id: string) {
|
||||
const comp = parsePDB(data, id);
|
||||
const parsed = await comp.run();
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
@@ -26,9 +26,9 @@ async function downloadCif(url: string, isBinary: boolean) {
|
||||
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
|
||||
}
|
||||
|
||||
async function downloadPDB(url: string) {
|
||||
async function downloadPDB(url: string, id: string) {
|
||||
const data = await fetch(url);
|
||||
return parsePDBfile(await data.text());
|
||||
return parsePDBfile(await data.text(), id);
|
||||
}
|
||||
|
||||
export async function getFromPdb(id: string) {
|
||||
@@ -42,6 +42,7 @@ function getCellPackDataUrl(id: string, baseUrl: string) {
|
||||
}
|
||||
|
||||
export async function getFromCellPackDB(id: string, baseUrl: string) {
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl));
|
||||
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
|
||||
return parsed;
|
||||
}
|
||||
@@ -138,23 +138,19 @@ function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, struc
|
||||
}
|
||||
|
||||
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
const direction = Vec3.sub(Vec3.zero(), e.pos, e.in);
|
||||
const direction = Vec3.sub(Vec3(), e.pos, e.in);
|
||||
Vec3.normalize(direction, direction);
|
||||
const up = Vec3.sub(Vec3.zero(), e.pos, e.up);
|
||||
const up = Vec3.sub(Vec3(), e.pos, e.up);
|
||||
Vec3.normalize(up, up);
|
||||
|
||||
const s: Camera.Snapshot = {
|
||||
mode: 'perspective',
|
||||
position: Vec3.scaleAndAdd(Vec3.zero(), e.pos, direction, e.slab.zoom),
|
||||
target: e.pos,
|
||||
direction,
|
||||
up,
|
||||
near: e.slab.zoom + e.slab.z_front,
|
||||
far: e.slab.zoom + e.slab.z_back,
|
||||
fogNear: e.slab.zoom + e.slab.z_front,
|
||||
fogFar: e.slab.zoom + e.slab.z_back,
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1
|
||||
position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
target: e.pos,
|
||||
radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
fog: 50,
|
||||
up,
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
||||
BIN
src/apps/viewer/favicon.ico
Executable file
BIN
src/apps/viewer/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>Mol* Viewer</title>
|
||||
<style>
|
||||
* {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import './favicon.ico'
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
|
||||
@@ -27,7 +27,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
name: 'proteopedia-wrapper-evolutionary-conservation',
|
||||
display: 'Evolutionary Conservation',
|
||||
async getData(model: Model) {
|
||||
const id = model.label.toLowerCase();
|
||||
const id = model.entryId.toLowerCase();
|
||||
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await req.json();
|
||||
const annotations = (json && json.residueAnnotations) || [];
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
== v3.4 ==
|
||||
|
||||
* Fixed HET group reset.
|
||||
* Updated core.
|
||||
* Removed Camera Cliping.
|
||||
|
||||
== v3.3 ==
|
||||
|
||||
* Camera Clipping.
|
||||
|
||||
@@ -18,9 +18,9 @@ export interface ModelInfo {
|
||||
|
||||
export namespace ModelInfo {
|
||||
async function getPreferredAssembly(ctx: PluginContext, model: Model) {
|
||||
if (model.label.length <= 3) return void 0;
|
||||
if (model.entryId.length <= 3) return void 0;
|
||||
try {
|
||||
const id = model.label.toLowerCase();
|
||||
const id = model.entryId.toLowerCase();
|
||||
const src = await ctx.runTask(ctx.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${id}` })) as string;
|
||||
const json = JSON.parse(src);
|
||||
const data = json && json[id];
|
||||
|
||||
@@ -133,8 +133,8 @@
|
||||
addControl('Reset Position', () => PluginWrapper.camera.resetPosition());
|
||||
addControl('Toggle Spin', () => PluginWrapper.camera.toggleSpin());
|
||||
// Same as "wheel icon" and Viewport options
|
||||
addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ require('../../mol-plugin/skin/light.scss')
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
static VERSION_MAJOR = 3;
|
||||
static VERSION_MINOR = 3;
|
||||
static VERSION_MINOR = 4;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -335,7 +335,7 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
hetGroups = {
|
||||
reset: () => {
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocus);
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
|
||||
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
},
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export interface ColorThemeComponentProps {
|
||||
colorTheme: ColorTheme<any>
|
||||
}
|
||||
|
||||
export interface ColorThemeComponentState {
|
||||
|
||||
}
|
||||
|
||||
export class ColorThemeComponent extends React.Component<ColorThemeComponentProps, ColorThemeComponentState> {
|
||||
state = {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const ct = this.props.colorTheme
|
||||
return <div>
|
||||
<span>Color Theme Info </span>
|
||||
|
||||
{ct.description ? <div><i>{ct.description}</i></div> : ''}
|
||||
{
|
||||
ct.legend && ct.legend.kind === 'scale-legend'
|
||||
? <div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '30px',
|
||||
background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`
|
||||
}}
|
||||
>
|
||||
<span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span>
|
||||
<span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span>
|
||||
</div>
|
||||
: ct.legend && ct.legend.kind === 'table-legend'
|
||||
? <div>
|
||||
{ct.legend.table.map((value, i) => {
|
||||
const [name, color] = value
|
||||
return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}>
|
||||
<div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div>
|
||||
{name}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,11 @@
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
|
||||
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { Object3D } from '../mol-gl/object3d';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
|
||||
export { Camera }
|
||||
|
||||
// TODO: slab controls that modify near/far planes?
|
||||
|
||||
class Camera implements Object3D {
|
||||
readonly updatedViewProjection = new BehaviorSubject<Camera>(this);
|
||||
|
||||
class Camera {
|
||||
readonly view: Mat4 = Mat4.identity();
|
||||
readonly projection: Mat4 = Mat4.identity();
|
||||
readonly projectionView: Mat4 = Mat4.identity();
|
||||
@@ -32,14 +26,17 @@ class Camera implements Object3D {
|
||||
width: 1, height: 1
|
||||
}
|
||||
|
||||
near = 1
|
||||
far = 10000
|
||||
fogNear = 5000
|
||||
fogFar = 10000
|
||||
zoom = 1
|
||||
|
||||
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
|
||||
|
||||
get position() { return this.state.position; }
|
||||
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
|
||||
|
||||
get direction() { return this.state.direction; }
|
||||
set direction(v: Vec3) { Vec3.copy(this.state.direction, v); }
|
||||
|
||||
get up() { return this.state.up; }
|
||||
set up(v: Vec3) { Vec3.copy(this.state.up, v); }
|
||||
|
||||
@@ -51,10 +48,12 @@ class Camera implements Object3D {
|
||||
private deltaDirection = Vec3.zero();
|
||||
private newPosition = Vec3.zero();
|
||||
|
||||
updateMatrices() {
|
||||
update() {
|
||||
const snapshot = this.state as Camera.Snapshot;
|
||||
const height = 2 * Math.tan(snapshot.fov / 2) * Vec3.distance(snapshot.position, snapshot.target);
|
||||
snapshot.zoom = this.viewport.height / height;
|
||||
this.zoom = this.viewport.height / height;
|
||||
|
||||
updateClip(this);
|
||||
|
||||
switch (this.state.mode) {
|
||||
case 'orthographic': updateOrtho(this); break;
|
||||
@@ -62,11 +61,7 @@ class Camera implements Object3D {
|
||||
default: throw new Error('unknown camera mode');
|
||||
}
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON.Value) || !Mat4.areEqual(this.view, this.prevView, EPSILON.Value);
|
||||
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
Mat4.invert(this.inverseProjectionView, this.projectionView)
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
|
||||
|
||||
if (changed) {
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
@@ -74,14 +69,13 @@ class Camera implements Object3D {
|
||||
|
||||
Mat4.copy(this.prevView, this.view);
|
||||
Mat4.copy(this.prevProjection, this.projection);
|
||||
this.updatedViewProjection.next(this);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
setState(snapshot: Partial<Camera.Snapshot>) {
|
||||
this.transition.apply(snapshot);
|
||||
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
|
||||
this.transition.apply(snapshot, durationMs);
|
||||
}
|
||||
|
||||
getSnapshot() {
|
||||
@@ -90,41 +84,29 @@ class Camera implements Object3D {
|
||||
return ret;
|
||||
}
|
||||
|
||||
getFocus(target: Vec3, radius: number, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
getFocus(target: Vec3, radius: number): Partial<Camera.Snapshot> {
|
||||
const fov = this.state.fov
|
||||
const { width, height } = this.viewport
|
||||
const aspect = width / height
|
||||
const aspectFactor = (height < width ? 1 : aspect)
|
||||
const currentDistance = Vec3.distance(this.state.position, target)
|
||||
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
|
||||
|
||||
const deltaDistance = Math.abs(currentDistance - targetDistance)
|
||||
Vec3.sub(this.deltaDirection, this.target, this.position)
|
||||
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance)
|
||||
Vec3.sub(this.newPosition, target, this.deltaDirection)
|
||||
|
||||
if (dir) {
|
||||
Vec3.setMagnitude(this.deltaDirection, dir, targetDistance)
|
||||
Vec3.add(this.newPosition, target, this.deltaDirection)
|
||||
} else {
|
||||
Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
|
||||
if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
|
||||
Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
|
||||
}
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
|
||||
state.target = Vec3.clone(target)
|
||||
state.radius = radius
|
||||
state.position = Vec3.clone(this.newPosition)
|
||||
|
||||
return { target, position: Vec3.clone(this.newPosition) };
|
||||
return state
|
||||
}
|
||||
|
||||
focus(target: Vec3, radius: number, dir?: Vec3) {
|
||||
if (radius > 0) this.setState(this.getFocus(target, radius, dir));
|
||||
focus(target: Vec3, radius: number, durationMs?: number) {
|
||||
if (radius > 0) this.setState(this.getFocus(target, radius), durationMs);
|
||||
}
|
||||
|
||||
// lookAt(target: Vec3) {
|
||||
// cameraLookAt(this.position, this.up, this.direction, target);
|
||||
// }
|
||||
|
||||
// translate(v: Vec3) {
|
||||
// Vec3.add(this.position, this.position, v);
|
||||
// cameraLookAt(this.position, this.up, this.direction, this.target);
|
||||
// }
|
||||
|
||||
project(out: Vec4, point: Vec3) {
|
||||
return cameraProject(out, point, this.viewport, this.projectionView)
|
||||
}
|
||||
@@ -133,10 +115,6 @@ class Camera implements Object3D {
|
||||
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.updatedViewProjection.complete();
|
||||
}
|
||||
|
||||
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) {
|
||||
this.viewport = viewport;
|
||||
Camera.copySnapshot(this.state, state);
|
||||
@@ -147,13 +125,6 @@ class Camera implements Object3D {
|
||||
namespace Camera {
|
||||
export type Mode = 'perspective' | 'orthographic'
|
||||
|
||||
export interface ClippingInfo {
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offseted view in a larger frustum. This is useful for
|
||||
* - multi-window or multi-monitor/multi-machine setups
|
||||
@@ -181,64 +152,48 @@ namespace Camera {
|
||||
export function createDefaultSnapshot(): Snapshot {
|
||||
return {
|
||||
mode: 'perspective',
|
||||
fov: Math.PI / 4,
|
||||
|
||||
position: Vec3.create(0, 0, 100),
|
||||
direction: Vec3.create(0, 0, 1),
|
||||
up: Vec3.create(0, 1, 0),
|
||||
|
||||
target: Vec3.create(0, 0, 0),
|
||||
|
||||
near: 1,
|
||||
far: 10000,
|
||||
fogNear: 1,
|
||||
fogFar: 10000,
|
||||
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1,
|
||||
radius: 10,
|
||||
fog: 50,
|
||||
};
|
||||
}
|
||||
|
||||
export interface Snapshot {
|
||||
mode: Mode,
|
||||
mode: Mode
|
||||
fov: number
|
||||
|
||||
position: Vec3,
|
||||
// Normalized camera direction
|
||||
direction: Vec3,
|
||||
up: Vec3,
|
||||
target: Vec3,
|
||||
position: Vec3
|
||||
up: Vec3
|
||||
target: Vec3
|
||||
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number,
|
||||
|
||||
fov: number,
|
||||
zoom: number,
|
||||
radius: number
|
||||
fog: number
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
if (!source) return;
|
||||
if (!source) return out;
|
||||
|
||||
if (typeof source.mode !== 'undefined') out.mode = source.mode;
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
|
||||
if (typeof source.position !== 'undefined') Vec3.copy(out.position, source.position);
|
||||
if (typeof source.direction !== 'undefined') Vec3.copy(out.direction, source.direction);
|
||||
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
|
||||
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
|
||||
|
||||
if (typeof source.near !== 'undefined') out.near = source.near;
|
||||
if (typeof source.far !== 'undefined') out.far = source.far;
|
||||
if (typeof source.fogNear !== 'undefined') out.fogNear = source.fogNear;
|
||||
if (typeof source.fogFar !== 'undefined') out.fogFar = source.fogFar;
|
||||
if (typeof source.radius !== 'undefined') out.radius = source.radius;
|
||||
if (typeof source.fog !== 'undefined') out.fog = source.fog;
|
||||
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
if (typeof source.zoom !== 'undefined') out.zoom = source.zoom;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
const _center = Vec3.zero();
|
||||
function updateOrtho(camera: Camera) {
|
||||
const { viewport, state: { zoom, near, far }, viewOffset } = camera
|
||||
const { viewport, zoom, near, far, viewOffset } = camera
|
||||
|
||||
const fullLeft = -(viewport.width - viewport.x) / 2
|
||||
const fullRight = (viewport.width - viewport.x) / 2
|
||||
@@ -270,16 +225,15 @@ function updateOrtho(camera: Camera) {
|
||||
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updatePers(camera: Camera) {
|
||||
const aspect = camera.viewport.width / camera.viewport.height
|
||||
|
||||
const { state: { fov, near, far }, viewOffset } = camera
|
||||
const { near, far, viewOffset } = camera
|
||||
|
||||
let top = near * Math.tan(0.5 * fov)
|
||||
let top = near * Math.tan(0.5 * camera.state.fov)
|
||||
let height = 2 * top
|
||||
let width = aspect * height
|
||||
let left = -0.5 * width
|
||||
@@ -295,6 +249,33 @@ function updatePers(camera: Camera) {
|
||||
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
const { radius, mode, fog } = camera.state
|
||||
|
||||
const cDist = Vec3.distance(camera.position, camera.target)
|
||||
const bRadius = Math.max(1, radius)
|
||||
|
||||
let near = cDist - bRadius
|
||||
let far = cDist + bRadius
|
||||
|
||||
const fogNearFactor = -(50 - fog) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + bRadius
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(5, near)
|
||||
far = Math.max(5, far)
|
||||
} else {
|
||||
near = Math.max(0, near)
|
||||
far = Math.max(0, far)
|
||||
}
|
||||
|
||||
camera.near = near;
|
||||
camera.far = far;
|
||||
camera.fogNear = fogNear;
|
||||
camera.fogFar = fogFar;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -22,7 +22,7 @@ class CameraTransitionManager {
|
||||
private current = Camera.createDefaultSnapshot();
|
||||
|
||||
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
|
||||
if (durationMs <= 0 || to.mode !== this.camera.state.mode) {
|
||||
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
|
||||
this.finish(to);
|
||||
return;
|
||||
}
|
||||
@@ -68,33 +68,21 @@ class CameraTransitionManager {
|
||||
namespace CameraTransitionManager {
|
||||
export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void
|
||||
|
||||
const _rot = Quat.identity();
|
||||
export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void {
|
||||
Camera.copySnapshot(out, target);
|
||||
|
||||
// Rotate direction
|
||||
const rot = Quat.identity();
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.direction, target.direction), t);
|
||||
Vec3.transformQuat(out.direction, source.direction, rot);
|
||||
|
||||
// Rotate up
|
||||
Quat.setIdentity(rot);
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, rot);
|
||||
Quat.slerp(_rot, Quat.Identity, Quat.rotationTo(_rot, source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, _rot);
|
||||
|
||||
// Lerp target
|
||||
// Lerp target, position & radius
|
||||
Vec3.lerp(out.target, source.target, target.target, t);
|
||||
Vec3.lerp(out.position, source.position, target.position, t);
|
||||
out.radius = lerp(source.radius, target.radius, t);
|
||||
|
||||
// Update position
|
||||
const dist = -lerp(Vec3.distance(source.position, source.target), Vec3.distance(target.position, target.target), t);
|
||||
Vec3.scale(out.position, out.direction, dist);
|
||||
Vec3.add(out.position, out.position, out.target);
|
||||
|
||||
// Lerp other props
|
||||
out.zoom = lerp(source.zoom, target.zoom, t);
|
||||
// Lerp fov & fog
|
||||
out.fov = lerp(source.fov, target.fov, t);
|
||||
out.near = lerp(source.near, target.near, t);
|
||||
out.far = lerp(source.far, target.far, t);
|
||||
out.fogNear = lerp(source.fogNear, target.fogNear, t);
|
||||
out.fogFar = lerp(source.fogFar, target.fogFar, t);
|
||||
out.fog = lerp(source.fog, target.fog, t);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
|
||||
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra'
|
||||
|
||||
export { Viewport }
|
||||
|
||||
@@ -55,31 +55,6 @@ namespace Viewport {
|
||||
|
||||
//
|
||||
|
||||
const tmpVec3 = Vec3()
|
||||
|
||||
/** Modifies the direction & up vectors in place, both are normalized */
|
||||
export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
|
||||
Vec3.sub(tmpVec3, target, position)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
|
||||
if (!Vec3.isZero(tmpVec3)) {
|
||||
// change direction vector to look at target
|
||||
const d = Vec3.dot(tmpVec3, up)
|
||||
if (Math.abs(d - 1) < EPSILON.Value) { // parallel
|
||||
Vec3.scale(up, direction, -1)
|
||||
} else if (Math.abs(d + 1) < EPSILON.Value) { // anti parallel
|
||||
Vec3.copy(up, direction)
|
||||
}
|
||||
Vec3.copy(direction, tmpVec3)
|
||||
|
||||
// normalize up vector
|
||||
Vec3.cross(tmpVec3, direction, up)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
Vec3.cross(up, tmpVec3, direction)
|
||||
Vec3.normalize(up, up)
|
||||
}
|
||||
}
|
||||
|
||||
const NEAR_RANGE = 0
|
||||
const FAR_RANGE = 1
|
||||
|
||||
|
||||
@@ -32,14 +32,12 @@ import { readTexture } from '../mol-gl/compute/util';
|
||||
import { DrawPass } from './passes/draw';
|
||||
import { PickPass } from './passes/pick';
|
||||
import { Task } from '../mol-task';
|
||||
import { ImagePass, ImageProps } from './passes/image';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
// TODO: FPS cap?
|
||||
// maxFps: PD.Numeric(30),
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
|
||||
cameraClipDistance: PD.Numeric(0, { min: 0.0, max: 50.0, step: 0.1 }, { description: 'The distance between camera and scene at which to clip regardless of near clipping plane.' }),
|
||||
clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
|
||||
fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
|
||||
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
|
||||
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
|
||||
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
@@ -77,6 +75,7 @@ interface Canvas3D {
|
||||
downloadScreenshot: () => void
|
||||
getPixelData: (variant: GraphicsRenderVariant) => PixelData
|
||||
setProps: (props: Partial<Canvas3DProps>) => void
|
||||
getImagePass: () => ImagePass
|
||||
|
||||
/** Returns a copy of the current Canvas3D instance props */
|
||||
readonly props: Readonly<Canvas3DProps>
|
||||
@@ -91,15 +90,16 @@ const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnim
|
||||
const DefaultRunTask = (task: Task<unknown>) => task.run()
|
||||
|
||||
namespace Canvas3D {
|
||||
export interface HighlightEvent { current: Representation.Loci, modifiers?: ModifiersKeys }
|
||||
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
|
||||
const gl = getGLContext(canvas, {
|
||||
alpha: false,
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
depth: true,
|
||||
preserveDrawingBuffer: true
|
||||
preserveDrawingBuffer: true,
|
||||
premultipliedAlpha: false,
|
||||
})
|
||||
if (gl === null) throw new Error('Could not create a WebGL rendering context')
|
||||
const input = InputObserver.fromElement(canvas)
|
||||
@@ -116,31 +116,31 @@ namespace Canvas3D {
|
||||
const startTime = now()
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
|
||||
|
||||
const camera = new Camera({
|
||||
near: 0.1,
|
||||
far: 10000,
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode
|
||||
})
|
||||
|
||||
const webgl = createContext(gl)
|
||||
|
||||
let width = gl.drawingBufferWidth
|
||||
let height = gl.drawingBufferHeight
|
||||
|
||||
const scene = Scene.create(webgl)
|
||||
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode,
|
||||
fog: p.cameraFog
|
||||
})
|
||||
|
||||
const controls = TrackballControls.create(input, camera, p.trackball)
|
||||
const renderer = Renderer.create(webgl, camera, p.renderer)
|
||||
const renderer = Renderer.create(webgl, p.renderer)
|
||||
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
|
||||
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, 0.5)
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
|
||||
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
|
||||
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
|
||||
|
||||
let drawPending = false
|
||||
let cameraResetRequested: boolean | Vec3 = false
|
||||
let cameraResetRequested = false
|
||||
|
||||
function getLoci(pickingId: PickingId) {
|
||||
let loci: Loci = EmptyLoci
|
||||
@@ -148,7 +148,9 @@ namespace Canvas3D {
|
||||
reprRenderObjects.forEach((_, _repr) => {
|
||||
const _loci = _repr.getLoci(pickingId)
|
||||
if (!isEmptyLoci(_loci)) {
|
||||
if (!isEmptyLoci(loci)) console.warn('found another loci')
|
||||
if (!isEmptyLoci(loci)) {
|
||||
console.warn('found another loci, this should not happen')
|
||||
}
|
||||
loci = _loci
|
||||
repr = _repr
|
||||
}
|
||||
@@ -172,47 +174,13 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
let currentNear = -1, currentFar = -1, currentFogNear = -1, currentFogFar = -1
|
||||
function setClipping() {
|
||||
const cDist = Vec3.distance(camera.state.position, camera.state.target)
|
||||
const bRadius = Math.max(10, scene.boundingSphere.radius)
|
||||
|
||||
const nearFactor = (50 - p.clip[0]) / 50
|
||||
const farFactor = -(50 - p.clip[1]) / 50
|
||||
let near = cDist - (bRadius * nearFactor)
|
||||
let far = cDist + (bRadius * farFactor)
|
||||
|
||||
const fogNearFactor = (50 - p.fog[0]) / 50
|
||||
const fogFarFactor = -(50 - p.fog[1]) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + (bRadius * fogFarFactor)
|
||||
|
||||
if (camera.state.mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(5, p.cameraClipDistance, near)
|
||||
far = Math.max(5, far)
|
||||
fogNear = Math.max(5, fogNear)
|
||||
fogFar = Math.max(5, fogFar)
|
||||
} else if (camera.state.mode === 'orthographic') {
|
||||
if (p.cameraClipDistance > 0) {
|
||||
near = Math.max(p.cameraClipDistance, near)
|
||||
}
|
||||
}
|
||||
|
||||
if (near !== currentNear || far !== currentFar || fogNear !== currentFogNear || fogFar !== currentFogFar) {
|
||||
camera.setState({ near, far, fogNear, fogFar })
|
||||
currentNear = near, currentFar = far, currentFogNear = fogNear, currentFogFar = fogFar
|
||||
}
|
||||
}
|
||||
|
||||
function render(variant: 'pick' | 'draw', force: boolean) {
|
||||
if (scene.isCommiting) return false
|
||||
|
||||
let didRender = false
|
||||
controls.update(currentTime);
|
||||
// TODO: is this a good fix? Also, setClipping does not work if the user has manually set a clipping plane.
|
||||
if (!camera.transition.inTransition) setClipping();
|
||||
const cameraChanged = camera.updateMatrices();
|
||||
controls.update(currentTime)
|
||||
Viewport.set(camera.viewport, 0, 0, width, height)
|
||||
const cameraChanged = camera.update()
|
||||
multiSample.update(force || cameraChanged, currentTime)
|
||||
|
||||
if (force || cameraChanged || multiSample.enabled) {
|
||||
@@ -221,9 +189,9 @@ namespace Canvas3D {
|
||||
pickPass.render()
|
||||
break;
|
||||
case 'draw':
|
||||
renderer.setViewport(0, 0, width, height);
|
||||
renderer.setViewport(0, 0, width, height)
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render()
|
||||
multiSample.render(true)
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
@@ -271,8 +239,7 @@ namespace Canvas3D {
|
||||
|
||||
runTask(scene.commit()).then(() => {
|
||||
if (cameraResetRequested && !scene.isCommiting) {
|
||||
const dir = typeof cameraResetRequested === 'boolean' ? undefined : cameraResetRequested
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, dir)
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
|
||||
cameraResetRequested = false
|
||||
}
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
@@ -333,6 +300,8 @@ namespace Canvas3D {
|
||||
reprRenderObjects.clear()
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
},
|
||||
|
||||
// draw,
|
||||
@@ -343,11 +312,11 @@ namespace Canvas3D {
|
||||
getLoci,
|
||||
|
||||
handleResize,
|
||||
resetCamera: (dir?: Vec3) => {
|
||||
resetCamera: () => {
|
||||
if (scene.isCommiting) {
|
||||
cameraResetRequested = dir || true
|
||||
cameraResetRequested = true
|
||||
} else {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, dir)
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
|
||||
requestDraw(true);
|
||||
}
|
||||
},
|
||||
@@ -370,9 +339,10 @@ namespace Canvas3D {
|
||||
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
|
||||
camera.setState({ mode: props.cameraMode })
|
||||
}
|
||||
if (props.cameraClipDistance !== undefined) p.cameraClipDistance = props.cameraClipDistance
|
||||
if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]]
|
||||
if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]]
|
||||
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
|
||||
camera.setState({ fog: props.cameraFog })
|
||||
}
|
||||
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
|
||||
|
||||
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) multiSample.setProps(props.multiSample)
|
||||
@@ -381,13 +351,15 @@ namespace Canvas3D {
|
||||
if (props.debug) debugHelper.setProps(props.debug)
|
||||
requestDraw(true)
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
|
||||
},
|
||||
|
||||
get props() {
|
||||
return {
|
||||
cameraMode: camera.state.mode,
|
||||
cameraClipDistance: p.cameraClipDistance,
|
||||
clip: p.clip,
|
||||
fog: p.fog,
|
||||
cameraFog: camera.state.fog,
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
|
||||
postprocessing: { ...postprocessing.props },
|
||||
multiSample: { ...multiSample.props },
|
||||
@@ -411,7 +383,6 @@ namespace Canvas3D {
|
||||
input.dispose()
|
||||
controls.dispose()
|
||||
renderer.dispose()
|
||||
camera.dispose()
|
||||
interactionHelper.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,29 @@
|
||||
*/
|
||||
|
||||
import { Quat, Vec2, Vec3, EPSILON } from '../../mol-math/linear-algebra';
|
||||
import { cameraLookAt, Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, ButtonsType, PinchInput } from '../../mol-util/input/input-observer';
|
||||
import { Object3D } from '../../mol-gl/object3d';
|
||||
import { Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Camera } from '../camera';
|
||||
import { absMax } from '../../mol-math/misc';
|
||||
import { Binding } from '../../mol-util/binding';
|
||||
|
||||
const B = ButtonsType
|
||||
const M = ModifiersKeys
|
||||
const Trigger = Binding.Trigger
|
||||
|
||||
export const DefaultTrackballBindings = {
|
||||
dragRotate: Binding(Trigger(B.Flag.Primary, M.create()), 'Rotate the 3D scene by dragging using ${trigger}'),
|
||||
dragRotateZ: Binding(Trigger(B.Flag.Primary, M.create({ shift: true })), 'Rotate the 3D scene around the z-axis by dragging using ${trigger}'),
|
||||
dragPan: Binding(Trigger(B.Flag.Secondary, M.create()), 'Pan the 3D scene by dragging using ${trigger}'),
|
||||
dragZoom: Binding.Empty,
|
||||
dragFocus: Binding(Trigger(B.Flag.Forth, M.create()), 'Focus the 3D scene by dragging using ${trigger}'),
|
||||
dragFocusZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Focus and zoom the 3D scene by dragging using ${trigger}'),
|
||||
|
||||
scrollZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Zoom the 3D scene by scrolling using ${trigger}'),
|
||||
scrollFocus: Binding(Trigger(B.Flag.Auxilary, M.create({ shift: true })), 'Focus the 3D scene by scrolling using ${trigger}'),
|
||||
scrollFocusZoom: Binding.Empty,
|
||||
}
|
||||
|
||||
export const TrackballControlsParams = {
|
||||
noScroll: PD.Boolean(true, { isHidden: true }),
|
||||
@@ -21,14 +40,16 @@ export const TrackballControlsParams = {
|
||||
zoomSpeed: PD.Numeric(6.0, { min: 0.1, max: 10, step: 0.1 }),
|
||||
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
|
||||
|
||||
spin: PD.Boolean(false),
|
||||
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
|
||||
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
|
||||
|
||||
staticMoving: PD.Boolean(true, { isHidden: true }),
|
||||
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
|
||||
|
||||
minDistance: PD.Numeric(0.01, {}, { isHidden: true }),
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true })
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
|
||||
|
||||
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true })
|
||||
}
|
||||
export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
|
||||
|
||||
@@ -44,11 +65,10 @@ interface TrackballControls {
|
||||
dispose: () => void
|
||||
}
|
||||
namespace TrackballControls {
|
||||
export function create(input: InputObserver, object: Object3D & { target: Vec3 }, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
export function create(input: InputObserver, camera: Camera, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props }
|
||||
|
||||
const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
|
||||
const target: Vec3 = object.target
|
||||
const viewport = Viewport()
|
||||
|
||||
let disposed = false
|
||||
|
||||
@@ -60,91 +80,116 @@ namespace TrackballControls {
|
||||
let _isInteracting = false;
|
||||
|
||||
// For internal use
|
||||
const lastPosition = Vec3.zero()
|
||||
const lastPosition = Vec3()
|
||||
|
||||
const _eye = Vec3.zero()
|
||||
const _eye = Vec3()
|
||||
|
||||
const _movePrev = Vec2.zero()
|
||||
const _moveCurr = Vec2.zero()
|
||||
const _rotPrev = Vec2()
|
||||
const _rotCurr = Vec2()
|
||||
const _rotLastAxis = Vec3()
|
||||
let _rotLastAngle = 0
|
||||
|
||||
const _lastAxis = Vec3.zero()
|
||||
let _lastAngle = 0
|
||||
const _zRotPrev = Vec2()
|
||||
const _zRotCurr = Vec2()
|
||||
let _zRotLastAngle = 0
|
||||
|
||||
const _zoomStart = Vec2.zero()
|
||||
const _zoomEnd = Vec2.zero()
|
||||
const _zoomStart = Vec2()
|
||||
const _zoomEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2.zero()
|
||||
const _panEnd = Vec2.zero()
|
||||
const _focusStart = Vec2()
|
||||
const _focusEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2()
|
||||
const _panEnd = Vec2()
|
||||
|
||||
// Initial values for reseting
|
||||
const target0 = Vec3.clone(target)
|
||||
const position0 = Vec3.clone(object.position)
|
||||
const up0 = Vec3.clone(object.up)
|
||||
const target0 = Vec3.clone(camera.target)
|
||||
const position0 = Vec3.clone(camera.position)
|
||||
const up0 = Vec3.clone(camera.up)
|
||||
|
||||
const mouseOnScreenVec2 = Vec2.zero()
|
||||
const mouseOnScreenVec2 = Vec2()
|
||||
function getMouseOnScreen(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnScreenVec2,
|
||||
(pageX - viewport.x) / viewport.width,
|
||||
(pageY - viewport.y) / viewport.height
|
||||
);
|
||||
return mouseOnScreenVec2;
|
||||
}
|
||||
|
||||
const mouseOnCircleVec2 = Vec2.zero()
|
||||
const mouseOnCircleVec2 = Vec2()
|
||||
function getMouseOnCircle(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnCircleVec2,
|
||||
((pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5)),
|
||||
((viewport.height + 2 * (viewport.y - pageY)) / viewport.width) // screen.width intentional
|
||||
(pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5),
|
||||
(viewport.height + 2 * (viewport.y - pageY)) / viewport.width // screen.width intentional
|
||||
);
|
||||
return mouseOnCircleVec2;
|
||||
}
|
||||
|
||||
const rotAxis = Vec3.zero()
|
||||
const rotQuat = Quat.zero()
|
||||
const rotEyeDir = Vec3.zero()
|
||||
const rotObjUpDir = Vec3.zero()
|
||||
const rotObjSideDir = Vec3.zero()
|
||||
const rotMoveDir = Vec3.zero()
|
||||
const rotAxis = Vec3()
|
||||
const rotQuat = Quat()
|
||||
const rotEyeDir = Vec3()
|
||||
const rotObjUpDir = Vec3()
|
||||
const rotObjSideDir = Vec3()
|
||||
const rotMoveDir = Vec3()
|
||||
|
||||
function rotateCamera() {
|
||||
Vec3.set(rotMoveDir, _moveCurr[0] - _movePrev[0], _moveCurr[1] - _movePrev[1], 0);
|
||||
let angle = Vec3.magnitude(rotMoveDir);
|
||||
const dx = _rotCurr[0] - _rotPrev[0]
|
||||
const dy = _rotCurr[1] - _rotPrev[1]
|
||||
Vec3.set(rotMoveDir, dx, dy, 0);
|
||||
|
||||
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed;
|
||||
|
||||
if (angle) {
|
||||
Vec3.copy(_eye, object.position)
|
||||
Vec3.sub(_eye, _eye, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
Vec3.normalize(rotEyeDir, Vec3.copy(rotEyeDir, _eye))
|
||||
Vec3.normalize(rotObjUpDir, Vec3.copy(rotObjUpDir, object.up))
|
||||
Vec3.normalize(rotEyeDir, _eye)
|
||||
Vec3.normalize(rotObjUpDir, camera.up)
|
||||
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir))
|
||||
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, _moveCurr[1] - _movePrev[1])
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, _moveCurr[0] - _movePrev[0])
|
||||
|
||||
Vec3.add(rotMoveDir, Vec3.copy(rotMoveDir, rotObjUpDir), rotObjSideDir)
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy)
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx)
|
||||
|
||||
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir)
|
||||
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye))
|
||||
|
||||
angle *= p.rotateSpeed;
|
||||
Quat.setAxisAngle(rotQuat, rotAxis, angle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
|
||||
Vec3.copy(_lastAxis, rotAxis)
|
||||
_lastAngle = angle;
|
||||
} else if (!p.staticMoving && _lastAngle) {
|
||||
_lastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, Vec3.copy(_eye, object.position), target)
|
||||
Quat.setAxisAngle(rotQuat, _lastAxis, _lastAngle)
|
||||
Vec3.copy(_rotLastAxis, rotAxis)
|
||||
_rotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _rotLastAngle) {
|
||||
_rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
|
||||
const zRotQuat = Quat()
|
||||
|
||||
function zRotateCamera() {
|
||||
const dx = _zRotCurr[0] - _zRotPrev[0]
|
||||
const dy = _zRotCurr[1] - _zRotPrev[1]
|
||||
const angle = p.rotateSpeed * (-dx + dy) * -0.05
|
||||
|
||||
if (angle) {
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, angle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
_zRotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _zRotLastAngle) {
|
||||
_zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
|
||||
function zoomCamera() {
|
||||
@@ -160,9 +205,23 @@ namespace TrackballControls {
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2.zero()
|
||||
const panObjUp = Vec3.zero()
|
||||
const panOffset = Vec3.zero()
|
||||
function focusCamera() {
|
||||
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
|
||||
if (factor !== 0.0) {
|
||||
const radius = Math.max(1, camera.state.radius + 10 * factor)
|
||||
camera.setState({ radius })
|
||||
}
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
} else {
|
||||
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2()
|
||||
const panObjUp = Vec3()
|
||||
const panOffset = Vec3()
|
||||
|
||||
function panCamera() {
|
||||
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart)
|
||||
@@ -170,14 +229,14 @@ namespace TrackballControls {
|
||||
if (Vec2.squaredMagnitude(panMouseChange)) {
|
||||
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed)
|
||||
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), object.up)
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up)
|
||||
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0])
|
||||
|
||||
Vec3.setMagnitude(panObjUp, object.up, panMouseChange[1])
|
||||
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1])
|
||||
Vec3.add(panOffset, panOffset, panObjUp)
|
||||
|
||||
Vec3.add(object.position, object.position, panOffset)
|
||||
Vec3.add(target, target, panOffset)
|
||||
Vec3.add(camera.position, camera.position, panOffset)
|
||||
Vec3.add(camera.target, camera.target, panOffset)
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_panStart, _panEnd)
|
||||
@@ -193,14 +252,16 @@ namespace TrackballControls {
|
||||
function checkDistances() {
|
||||
if (Vec3.squaredMagnitude(_eye) > p.maxDistance * p.maxDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.maxDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
|
||||
if (Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.minDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,18 +271,19 @@ namespace TrackballControls {
|
||||
if (lastUpdated === t) return;
|
||||
if (p.spin) spin(t - lastUpdated);
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
rotateCamera()
|
||||
zRotateCamera()
|
||||
zoomCamera()
|
||||
focusCamera()
|
||||
panCamera()
|
||||
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
checkDistances()
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
|
||||
if (Vec3.squaredDistance(lastPosition, object.position) > EPSILON.Value) {
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
if (Vec3.squaredDistance(lastPosition, camera.position) > EPSILON) {
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
lastUpdated = t;
|
||||
@@ -229,53 +291,82 @@ namespace TrackballControls {
|
||||
|
||||
/** Reset object's vectors and the target vector to their initial values */
|
||||
function reset() {
|
||||
Vec3.copy(target, target0)
|
||||
Vec3.copy(object.position, position0)
|
||||
Vec3.copy(object.up, up0)
|
||||
Vec3.copy(camera.target, target0)
|
||||
Vec3.copy(camera.position, position0)
|
||||
Vec3.copy(camera.up, up0)
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
// listeners
|
||||
|
||||
function onDrag({ pageX, pageY, buttons, isStart }: DragInput) {
|
||||
function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
|
||||
_isInteracting = true;
|
||||
|
||||
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers)
|
||||
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers)
|
||||
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers)
|
||||
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers)
|
||||
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers)
|
||||
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers)
|
||||
|
||||
getMouseOnCircle(pageX, pageY)
|
||||
getMouseOnScreen(pageX, pageY)
|
||||
|
||||
if (isStart) {
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) {
|
||||
Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
if (dragRotateZ) {
|
||||
Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
if (dragZoom || dragFocusZoom) {
|
||||
Vec2.copy(_zoomStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_zoomEnd, _zoomStart)
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY))
|
||||
}
|
||||
if (dragFocus) {
|
||||
Vec2.copy(_focusStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_focusEnd, _focusStart)
|
||||
}
|
||||
if (dragPan) {
|
||||
Vec2.copy(_panStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_panEnd, _panStart)
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2)
|
||||
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
|
||||
if (dragFocusZoom) {
|
||||
const dist = Vec3.distance(camera.state.position, camera.state.target);
|
||||
camera.setState({ radius: dist / 5 })
|
||||
}
|
||||
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
|
||||
}
|
||||
|
||||
function onInteractionEnd() {
|
||||
_isInteracting = false;
|
||||
}
|
||||
|
||||
function onWheel({ dy }: WheelInput) {
|
||||
_zoomEnd[1] += dy * 0.0001
|
||||
function onWheel({ dx, dy, dz, buttons, modifiers }: WheelInput) {
|
||||
const delta = absMax(dx, dy, dz)
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_zoomEnd[1] += delta * 0.0001
|
||||
}
|
||||
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
|
||||
_focusEnd[1] += delta * 0.0001
|
||||
}
|
||||
}
|
||||
|
||||
function onPinch({ fraction }: PinchInput) {
|
||||
_isInteracting = true;
|
||||
_zoomEnd[1] += (fraction - 1) * 0.1
|
||||
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_isInteracting = true;
|
||||
_zoomEnd[1] += (fraction - 1) * 0.1
|
||||
}
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
@@ -292,7 +383,7 @@ namespace TrackballControls {
|
||||
function spin(deltaT: number) {
|
||||
const frameSpeed = (p.spinSpeed || 0) / 1000;
|
||||
_spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
|
||||
if (!_isInteracting) Vec2.add(_moveCurr, _movePrev, _spinSpeed);
|
||||
if (!_isInteracting) Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
|
||||
}
|
||||
|
||||
// force an update at start
|
||||
|
||||
@@ -11,13 +11,15 @@ import InputObserver, { ModifiersKeys, ButtonsType } from '../../mol-util/input/
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
|
||||
type Canvas3D = import('../canvas3d').Canvas3D
|
||||
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
|
||||
type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
|
||||
|
||||
export class Canvas3dInteractionHelper {
|
||||
private ev = RxEventHelper.create();
|
||||
|
||||
readonly events = {
|
||||
highlight: this.ev<import('../canvas3d').Canvas3D.HighlightEvent>(),
|
||||
click: this.ev<import('../canvas3d').Canvas3D.ClickEvent>(),
|
||||
hover: this.ev<HoverEvent>(),
|
||||
click: this.ev<ClickEvent>(),
|
||||
};
|
||||
|
||||
private cX = -1;
|
||||
@@ -52,14 +54,14 @@ export class Canvas3dInteractionHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
// only highlight the latest
|
||||
if (!this.inside || this.currentIdentifyT !== t) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loci = this.getLoci(this.id);
|
||||
// only broadcast the latest hover
|
||||
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
|
||||
this.events.highlight.next({ current: loci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
}
|
||||
@@ -76,12 +78,13 @@ export class Canvas3dInteractionHelper {
|
||||
this.inside = false;
|
||||
if (this.prevLoci.loci !== EmptyLoci) {
|
||||
this.prevLoci = Representation.Loci.Empty;
|
||||
this.events.highlight.next({ current: this.prevLoci });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
}
|
||||
}
|
||||
|
||||
move(x: number, y: number, modifiers: ModifiersKeys) {
|
||||
move(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
|
||||
this.inside = true;
|
||||
this.buttons = buttons;
|
||||
this.modifiers = modifiers;
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
@@ -98,7 +101,7 @@ export class Canvas3dInteractionHelper {
|
||||
modify(modifiers: ModifiersKeys) {
|
||||
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
|
||||
this.modifiers = modifiers;
|
||||
this.events.highlight.next({ current: this.prevLoci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -107,8 +110,8 @@ export class Canvas3dInteractionHelper {
|
||||
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
|
||||
input.move.subscribe(({x, y, inside, buttons, modifiers }) => {
|
||||
if (!inside || buttons) { return; }
|
||||
this.move(x, y, modifiers);
|
||||
if (!inside) return;
|
||||
this.move(x, y, buttons, modifiers);
|
||||
});
|
||||
|
||||
input.leave.subscribe(() => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { createTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class DrawPass {
|
||||
colorTarget: RenderTarget
|
||||
@@ -18,11 +19,11 @@ export class DrawPass {
|
||||
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private debugHelper: BoundingSphereHelper) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = createRenderTarget(webgl, width, height)
|
||||
this.packedDepth = !extensions.depthTexture
|
||||
this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
|
||||
@@ -42,28 +43,28 @@ export class DrawPass {
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
const { webgl, renderer, scene, debugHelper, colorTarget, depthTarget } = this
|
||||
const { gl } = webgl
|
||||
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
colorTarget.bind()
|
||||
}
|
||||
renderer.setViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
renderer.render(scene, 'color', true)
|
||||
|
||||
renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
|
||||
renderer.render(scene, camera, 'color', true)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'color', false)
|
||||
renderer.render(debugHelper.scene, camera, 'color', false)
|
||||
}
|
||||
|
||||
// do a depth pass if not rendering to drawing buffer and
|
||||
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (!toDrawingBuffer && depthTarget) {
|
||||
depthTarget.bind()
|
||||
renderer.render(scene, 'depth', true)
|
||||
renderer.render(scene, camera, 'depth', true)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'depth', false)
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
91
src/mol-canvas3d/passes/image.ts
Normal file
91
src/mol-canvas3d/passes/image.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { DrawPass } from './draw'
|
||||
import { PostprocessingPass, PostprocessingParams } from './postprocessing'
|
||||
import { MultiSamplePass, MultiSampleParams } from './multi-sample'
|
||||
import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
export const ImageParams = {
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
}
|
||||
export type ImageProps = PD.Values<typeof ImageParams>
|
||||
|
||||
export class ImagePass {
|
||||
private _width = 1024
|
||||
private _height = 768
|
||||
private _camera = new Camera()
|
||||
|
||||
private _colorTarget: RenderTarget
|
||||
get colorTarget() { return this._colorTarget }
|
||||
|
||||
readonly drawPass: DrawPass
|
||||
private readonly postprocessing: PostprocessingPass
|
||||
private readonly multiSample: MultiSamplePass
|
||||
|
||||
get width() { return this._width }
|
||||
get height() { return this._height }
|
||||
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
|
||||
const p = { ...PD.getDefaultValues(ImageParams), ...props }
|
||||
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
|
||||
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
|
||||
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
|
||||
|
||||
this.setSize(this._width, this._height)
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this._width = width
|
||||
this._height = height
|
||||
|
||||
this.drawPass.setSize(width, height)
|
||||
this.postprocessing.setSize(width, height)
|
||||
this.multiSample.setSize(width, height)
|
||||
}
|
||||
|
||||
setProps(props: Partial<ImageProps> = {}) {
|
||||
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) this.multiSample.setProps(props.multiSample)
|
||||
}
|
||||
|
||||
render() {
|
||||
Camera.copySnapshot(this._camera.state, this.camera.state)
|
||||
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
|
||||
this._camera.update()
|
||||
|
||||
this.renderer.setViewport(0, 0, this._width, this._height);
|
||||
|
||||
if (this.multiSample.enabled) {
|
||||
this.multiSample.render(false)
|
||||
this._colorTarget = this.multiSample.colorTarget
|
||||
} else {
|
||||
this.drawPass.render(false)
|
||||
if (this.postprocessing.enabled) {
|
||||
this.postprocessing.render(false)
|
||||
this._colorTarget = this.postprocessing.target
|
||||
} else {
|
||||
this._colorTarget = this.drawPass.colorTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getImageData(width: number, height: number) {
|
||||
this.setSize(width, height)
|
||||
this.render()
|
||||
const pd = this.colorTarget.getPixelData()
|
||||
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
|
||||
|
||||
export class MultiSamplePass {
|
||||
props: MultiSampleProps
|
||||
colorTarget: RenderTarget
|
||||
|
||||
private composeTarget: RenderTarget
|
||||
private holdTarget: RenderTarget
|
||||
@@ -65,6 +66,7 @@ export class MultiSamplePass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
|
||||
const { gl } = webgl
|
||||
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
|
||||
@@ -92,6 +94,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this.colorTarget.setSize(width, height)
|
||||
this.composeTarget.setSize(width, height)
|
||||
this.holdTarget.setSize(width, height)
|
||||
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
|
||||
@@ -102,15 +105,15 @@ export class MultiSamplePass {
|
||||
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
|
||||
}
|
||||
|
||||
render() {
|
||||
render(toDrawingBuffer: boolean) {
|
||||
if (this.props.mode === 'temporal') {
|
||||
this.renderTemporalMultiSample()
|
||||
this.renderTemporalMultiSample(toDrawingBuffer)
|
||||
} else {
|
||||
this.renderMultiSample()
|
||||
this.renderMultiSample(toDrawingBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
private renderMultiSample() {
|
||||
private renderMultiSample(toDrawingBuffer: boolean) {
|
||||
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -135,7 +138,7 @@ export class MultiSamplePass {
|
||||
for (let i = 0; i < offsetList.length; ++i) {
|
||||
const offset = offsetList[i]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// the theory is that equal weights for each sample lead to an accumulation of rounding
|
||||
// errors. The following equation varies the sampleWeight per sample so that it is uniformly
|
||||
@@ -168,16 +171,20 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample() {
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean) {
|
||||
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -223,7 +230,7 @@ export class MultiSamplePass {
|
||||
for (let i = 0; i < numSamplesPerFrame; ++i) {
|
||||
const offset = offsetList[this.sampleIndex]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
@@ -252,7 +259,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
@@ -261,7 +272,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
|
||||
ValueCell.update(compose.values.tColor, holdTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
if (accumulationWeight === 0) state.disable(gl.BLEND)
|
||||
else state.enable(gl.BLEND)
|
||||
@@ -269,7 +284,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class PickPass {
|
||||
pickDirty = true
|
||||
@@ -26,7 +27,7 @@ export class PickPass {
|
||||
private pickWidth: number
|
||||
private pickHeight: number
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
|
||||
const { gl } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
@@ -64,14 +65,14 @@ export class PickPass {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { renderer, scene } = this
|
||||
const { renderer, scene, camera } = this
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, 'pickObject', true);
|
||||
renderer.render(scene, camera, 'pickObject', true);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, 'pickInstance', true);
|
||||
renderer.render(scene, camera, 'pickInstance', true);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, 'pickGroup', true);
|
||||
renderer.render(scene, camera, 'pickGroup', true);
|
||||
|
||||
this.pickDirty = false
|
||||
}
|
||||
|
||||
@@ -160,10 +160,10 @@ export class PostprocessingPass {
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.state.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.state.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.state.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.state.fogNear)
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
|
||||
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
|
||||
|
||||
const { gl, state } = this.webgl
|
||||
|
||||
@@ -4,16 +4,57 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
/** resize canvas to container element */
|
||||
/** Set canvas size taking `devicePixelRatio` into account */
|
||||
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
|
||||
canvas.width = window.devicePixelRatio * width
|
||||
canvas.height = window.devicePixelRatio * height
|
||||
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
|
||||
}
|
||||
|
||||
/** Resize canvas to container element taking `devicePixelRatio` into account */
|
||||
export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
|
||||
let w = window.innerWidth
|
||||
let h = window.innerHeight
|
||||
let width = window.innerWidth
|
||||
let height = window.innerHeight
|
||||
if (container !== document.body) {
|
||||
let bounds = container.getBoundingClientRect()
|
||||
w = bounds.right - bounds.left
|
||||
h = bounds.bottom - bounds.top
|
||||
width = bounds.right - bounds.left
|
||||
height = bounds.bottom - bounds.top
|
||||
}
|
||||
canvas.width = window.devicePixelRatio * w
|
||||
canvas.height = window.devicePixelRatio * h
|
||||
Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
|
||||
setCanvasSize(canvas, width, height)
|
||||
}
|
||||
|
||||
function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?: string, quality?: any) {
|
||||
const bin = atob(canvas.toDataURL(type, quality).split(',')[1])
|
||||
const len = bin.length
|
||||
const len32 = len >> 2
|
||||
const a8 = new Uint8Array(len)
|
||||
const a32 = new Uint32Array( a8.buffer, 0, len32 )
|
||||
|
||||
let j = 0
|
||||
for (let i = 0; i < len32; ++i) {
|
||||
a32[i] = bin.charCodeAt(j++) |
|
||||
bin.charCodeAt(j++) << 8 |
|
||||
bin.charCodeAt(j++) << 16 |
|
||||
bin.charCodeAt(j++) << 24
|
||||
}
|
||||
|
||||
let tailLength = len & 3;
|
||||
while (tailLength--) a8[j] = bin.charCodeAt(j++)
|
||||
|
||||
callback(new Blob([a8], { type: type || 'image/png' }));
|
||||
}
|
||||
|
||||
export async function canvasToBlob(canvas: HTMLCanvasElement, type?: string, quality?: any): Promise<Blob> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (blob: Blob | null) => {
|
||||
if (blob) resolve(blob)
|
||||
else reject('no blob returned')
|
||||
}
|
||||
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
_canvasToBlob(canvas, callback, type, quality)
|
||||
} else {
|
||||
canvas.toBlob(callback, type, quality)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -73,6 +73,7 @@ namespace Column {
|
||||
rowCount: number,
|
||||
schema: T,
|
||||
valueKind?: (row: number) => ValueKind,
|
||||
areValuesEqual?: (rowA: number, rowB: number) => boolean
|
||||
}
|
||||
|
||||
export interface ArraySpec<T extends Schema> {
|
||||
@@ -247,7 +248,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
|
||||
}
|
||||
}
|
||||
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqual, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
return {
|
||||
schema: schema,
|
||||
__array: void 0,
|
||||
@@ -260,7 +261,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, sch
|
||||
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
|
||||
return array;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
|
||||
areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +302,7 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
}
|
||||
}
|
||||
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number) {
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number): Column<T> {
|
||||
if (!column.isDefined) return Column.Undefined(end - start, column.schema);
|
||||
if (start === 0 && end === column.rowCount) return column;
|
||||
if (!!column.__array && ColumnHelpers.isTypedArray(column.__array)) return windowTyped(column, start, end);
|
||||
|
||||
@@ -218,6 +218,16 @@ namespace Table {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function toArrays<S extends Schema>(table: Table<S>) {
|
||||
const arrays: { [k: string]: ArrayLike<any> } = {}
|
||||
const { _columns } = table;
|
||||
for (let i = 0; i < _columns.length; i++) {
|
||||
const c = _columns[i]
|
||||
arrays[c] = table[c].toArray();
|
||||
}
|
||||
return arrays as { [k in keyof S]: ArrayLike<S[k]['T']> }
|
||||
}
|
||||
|
||||
export function formatToString<S extends Schema>(table: Table<S>) {
|
||||
const sb = StringBuilder.create();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import Tuple from '../tuple'
|
||||
export const Empty = Tuple.Zero;
|
||||
export function ofRange(min: number, max: number) { return max < min ? Tuple.create(min, min) : Tuple.create(min, max + 1); }
|
||||
export function ofBounds(start: number, end: number) { return end <= start ? Tuple.create(start, start) : Tuple.create(start, end); }
|
||||
export function ofLength(length: number) { return length < 0 ? Tuple.create(0, 0) : Tuple.create(0, length); }
|
||||
export const is = Tuple.is;
|
||||
|
||||
export const start = Tuple.fst;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Interval {
|
||||
export const ofRange: <T extends number = number>(min: T, max: T) => Interval<T> = Impl.ofRange as any;
|
||||
/** Create interval from bounds [start, end), i.e. [start, end - 1] */
|
||||
export const ofBounds: <T extends number = number>(start: T, end: T) => Interval<T> = Impl.ofBounds as any;
|
||||
/** Create interval from length [0, length), i.e. [0, length - 1] */
|
||||
export const ofLength: <T extends number = number>(length: T) => Interval<T> = Impl.ofLength as any;
|
||||
export const is: <T extends number = number>(v: any) => v is Interval<T> = Impl.is as any;
|
||||
|
||||
/** Test if a value is within the bounds of the interval */
|
||||
|
||||
@@ -177,8 +177,8 @@ export namespace Lines {
|
||||
}
|
||||
|
||||
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount)
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount, 0, 4)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount, 0, 4)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
|
||||
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { getSphere } from './sphere';
|
||||
|
||||
const tmpEllipsoidMat = Mat4.identity()
|
||||
const tmpVec = Vec3()
|
||||
|
||||
function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
|
||||
Vec3.add(tmpVec, center, dirMajor)
|
||||
Mat4.targetTo(m, center, tmpVec, dirMinor)
|
||||
Mat4.setTranslation(m, center)
|
||||
return Mat4.scale(m, m, radiusScale)
|
||||
}
|
||||
|
||||
export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
|
||||
MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
|
||||
}
|
||||
@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) {
|
||||
return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
|
||||
}
|
||||
|
||||
function getSphere(detail: number) {
|
||||
export function getSphere(detail: number) {
|
||||
let sphere = sphereMap.get(detail)
|
||||
if (sphere === undefined) {
|
||||
sphere = Sphere(detail)
|
||||
|
||||
@@ -101,9 +101,9 @@ export namespace MeshBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number) {
|
||||
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number, radialSegments: number) {
|
||||
const { vertices: va, edges: ea } = cage
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius }
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments }
|
||||
for (let i = 0, il = ea.length; i < il; i += 2) {
|
||||
Vec3.fromArray(tmpVecA, va, ea[i] * 3)
|
||||
Vec3.fromArray(tmpVecB, va, ea[i + 1] * 3)
|
||||
|
||||
@@ -89,7 +89,7 @@ export namespace Spheres {
|
||||
const padding = getMaxSize(size)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding
|
||||
transform.aTransform.ref.value, instanceCount, padding, 4
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -130,7 +130,7 @@ export namespace Spheres {
|
||||
const padding = getMaxSize(values)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, spheres.sphereCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding, 4
|
||||
)
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3 } from '../../mol-math/linear-algebra'
|
||||
import { NumberArray } from '../../mol-util/type-helpers'
|
||||
|
||||
export interface Cage {
|
||||
readonly vertices: ArrayLike<number>
|
||||
readonly edges: ArrayLike<number>
|
||||
@@ -11,4 +14,24 @@ export interface Cage {
|
||||
|
||||
export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>): Cage {
|
||||
return { vertices, edges }
|
||||
}
|
||||
|
||||
export function copyCage(cage: Cage): Cage {
|
||||
return {
|
||||
vertices: new Float32Array(cage.vertices),
|
||||
edges: new Uint32Array(cage.edges)
|
||||
}
|
||||
}
|
||||
|
||||
const tmpV = Vec3.zero()
|
||||
|
||||
/** Transform primitive in-place */
|
||||
export function transformCage(cage: Cage, t: Mat4) {
|
||||
const { vertices } = cage
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
|
||||
Vec3.toArray(tmpV, vertices as NumberArray, i)
|
||||
}
|
||||
return cage
|
||||
}
|
||||
@@ -30,6 +30,14 @@ export function createPrimitive(vertices: ArrayLike<number>, indices: ArrayLike<
|
||||
return builder.getPrimitive()
|
||||
}
|
||||
|
||||
export function copyPrimitive(primitive: Primitive): Primitive {
|
||||
return {
|
||||
vertices: new Float32Array(primitive.vertices),
|
||||
normals: new Float32Array(primitive.normals),
|
||||
indices: new Uint32Array(primitive.indices)
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrimitiveBuilder {
|
||||
add(a: Vec3, b: Vec3, c: Vec3): void
|
||||
getPrimitive(): Primitive
|
||||
|
||||
@@ -78,10 +78,22 @@ export function PentagonalPrism() {
|
||||
|
||||
let hexagonalPrism: Primitive
|
||||
export function HexagonalPrism() {
|
||||
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, true))
|
||||
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, false))
|
||||
return hexagonalPrism
|
||||
}
|
||||
|
||||
let shiftedHexagonalPrism: Primitive
|
||||
export function ShiftedHexagonalPrism() {
|
||||
if (!shiftedHexagonalPrism) shiftedHexagonalPrism = Prism(polygon(6, true))
|
||||
return shiftedHexagonalPrism
|
||||
}
|
||||
|
||||
let heptagonalPrism: Primitive
|
||||
export function HeptagonalPrism() {
|
||||
if (!heptagonalPrism) heptagonalPrism = Prism(polygon(7, false))
|
||||
return heptagonalPrism
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
@@ -133,6 +145,6 @@ export function PentagonalPrismCage() {
|
||||
|
||||
let hexagonalPrismCage: Cage
|
||||
export function HexagonalPrismCage() {
|
||||
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, true))
|
||||
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, false))
|
||||
return hexagonalPrismCage
|
||||
}
|
||||
@@ -28,11 +28,9 @@ import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-dat
|
||||
function createRenderer(gl: WebGLRenderingContext) {
|
||||
const ctx = createContext(gl)
|
||||
const camera = new Camera({
|
||||
near: 0.01,
|
||||
far: 10000,
|
||||
position: Vec3.create(0, 0, 50)
|
||||
})
|
||||
const renderer = Renderer.create(ctx, camera)
|
||||
const renderer = Renderer.create(ctx)
|
||||
return { ctx, camera, renderer }
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,7 @@ export const GlobalUniformSchema = {
|
||||
uFogFar: UniformSpec('f'),
|
||||
uFogColor: UniformSpec('v3'),
|
||||
|
||||
uTransparentBackground: UniformSpec('i'),
|
||||
uPickingAlphaThreshold: UniformSpec('f'),
|
||||
uInteriorDarkening: UniformSpec('f'),
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -80,14 +80,15 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
|
||||
const v = Vec3.zero()
|
||||
const boundaryHelper = new BoundaryHelper()
|
||||
|
||||
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number): Sphere3D {
|
||||
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
|
||||
const step = stepFactor * 3
|
||||
boundaryHelper.reset(0)
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i)
|
||||
boundaryHelper.boundaryStep(v, 0)
|
||||
}
|
||||
boundaryHelper.finishBoundaryStep()
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i)
|
||||
boundaryHelper.extendStep(v, 0)
|
||||
}
|
||||
@@ -109,8 +110,8 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
|
||||
return boundaryHelper.getSphere()
|
||||
}
|
||||
|
||||
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
|
||||
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount)
|
||||
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
|
||||
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount)
|
||||
Sphere3D.expand(boundingSphere, boundingSphere, padding)
|
||||
Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding)
|
||||
|
||||
@@ -38,14 +38,15 @@ interface Renderer {
|
||||
readonly props: Readonly<RendererProps>
|
||||
|
||||
clear: () => void
|
||||
render: (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => void
|
||||
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
|
||||
setProps: (props: Partial<RendererProps>) => void
|
||||
setViewport: (x: number, y: number, width: number, height: number) => void
|
||||
dispose: () => void
|
||||
}
|
||||
|
||||
export const RendererParams = {
|
||||
backgroundColor: PD.Color(Color(0x000000)),
|
||||
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
|
||||
transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
|
||||
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
|
||||
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
|
||||
@@ -59,39 +60,40 @@ export const RendererParams = {
|
||||
export type RendererProps = PD.Values<typeof RendererParams>
|
||||
|
||||
namespace Renderer {
|
||||
export function create(ctx: WebGLContext, camera: Camera, props: Partial<RendererProps> = {}): Renderer {
|
||||
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
|
||||
const { gl, state, stats } = ctx
|
||||
const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
|
||||
|
||||
const viewport = Viewport()
|
||||
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
|
||||
|
||||
const view = Mat4.clone(camera.view)
|
||||
const invView = Mat4.invert(Mat4.identity(), view)
|
||||
const modelView = Mat4.clone(camera.view)
|
||||
const invModelView = Mat4.invert(Mat4.identity(), modelView)
|
||||
const invProjection = Mat4.invert(Mat4.identity(), camera.projection)
|
||||
const modelViewProjection = Mat4.mul(Mat4.identity(), modelView, camera.projection)
|
||||
const invModelViewProjection = Mat4.invert(Mat4.identity(), modelViewProjection)
|
||||
const view = Mat4()
|
||||
const invView = Mat4()
|
||||
const modelView = Mat4()
|
||||
const invModelView = Mat4()
|
||||
const invProjection = Mat4()
|
||||
const modelViewProjection = Mat4()
|
||||
const invModelViewProjection = Mat4()
|
||||
|
||||
const viewOffset = camera.viewOffset.enabled ? Vec2.create(camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2()
|
||||
const viewOffset = Vec2()
|
||||
|
||||
const globalUniforms: GlobalUniformValues = {
|
||||
uModel: ValueCell.create(Mat4.identity()),
|
||||
uView: ValueCell.create(camera.view),
|
||||
uView: ValueCell.create(view),
|
||||
uInvView: ValueCell.create(invView),
|
||||
uModelView: ValueCell.create(modelView),
|
||||
uInvModelView: ValueCell.create(invModelView),
|
||||
uInvProjection: ValueCell.create(invProjection),
|
||||
uProjection: ValueCell.create(Mat4.clone(camera.projection)),
|
||||
uProjection: ValueCell.create(Mat4()),
|
||||
uModelViewProjection: ValueCell.create(modelViewProjection),
|
||||
uInvModelViewProjection: ValueCell.create(invModelViewProjection),
|
||||
|
||||
uIsOrtho: ValueCell.create(camera.state.mode === 'orthographic' ? 1 : 0),
|
||||
uIsOrtho: ValueCell.create(1),
|
||||
uViewOffset: ValueCell.create(viewOffset),
|
||||
|
||||
uPixelRatio: ValueCell.create(ctx.pixelRatio),
|
||||
uViewportHeight: ValueCell.create(viewport.height),
|
||||
uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
|
||||
uViewOffset: ValueCell.create(viewOffset),
|
||||
|
||||
uLightIntensity: ValueCell.create(p.lightIntensity),
|
||||
uAmbientIntensity: ValueCell.create(p.ambientIntensity),
|
||||
@@ -100,13 +102,14 @@ namespace Renderer {
|
||||
uRoughness: ValueCell.create(p.roughness),
|
||||
uReflectivity: ValueCell.create(p.reflectivity),
|
||||
|
||||
uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)),
|
||||
uNear: ValueCell.create(camera.state.near),
|
||||
uFar: ValueCell.create(camera.state.far),
|
||||
uFogNear: ValueCell.create(camera.state.fogNear),
|
||||
uFogFar: ValueCell.create(camera.state.fogFar),
|
||||
uCameraPosition: ValueCell.create(Vec3()),
|
||||
uNear: ValueCell.create(1),
|
||||
uFar: ValueCell.create(10000),
|
||||
uFogNear: ValueCell.create(1),
|
||||
uFogFar: ValueCell.create(10000),
|
||||
uFogColor: ValueCell.create(bgColor),
|
||||
|
||||
uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
|
||||
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
|
||||
uInteriorDarkening: ValueCell.create(p.interiorDarkening),
|
||||
}
|
||||
@@ -158,7 +161,7 @@ namespace Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
const render = (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => {
|
||||
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
|
||||
ValueCell.update(globalUniforms.uModel, scene.view)
|
||||
ValueCell.update(globalUniforms.uView, camera.view)
|
||||
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
|
||||
@@ -173,10 +176,10 @@ namespace Renderer {
|
||||
ValueCell.update(globalUniforms.uViewOffset, camera.viewOffset.enabled ? Vec2.set(viewOffset, camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2.set(viewOffset, 0, 0))
|
||||
|
||||
ValueCell.update(globalUniforms.uCameraPosition, camera.state.position)
|
||||
ValueCell.update(globalUniforms.uFar, camera.state.far)
|
||||
ValueCell.update(globalUniforms.uNear, camera.state.near)
|
||||
ValueCell.update(globalUniforms.uFogFar, camera.state.fogFar)
|
||||
ValueCell.update(globalUniforms.uFogNear, camera.state.fogNear)
|
||||
ValueCell.update(globalUniforms.uFar, camera.far)
|
||||
ValueCell.update(globalUniforms.uNear, camera.near)
|
||||
ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
|
||||
ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
|
||||
|
||||
globalUniformsNeedUpdate = true
|
||||
state.currentRenderItemId = -1
|
||||
@@ -191,7 +194,7 @@ namespace Renderer {
|
||||
|
||||
if (clear) {
|
||||
if (variant === 'color') {
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
|
||||
} else {
|
||||
state.clearColor(1, 1, 1, 1)
|
||||
}
|
||||
@@ -204,7 +207,7 @@ namespace Renderer {
|
||||
if (r.state.opaque) renderObject(r, variant)
|
||||
}
|
||||
|
||||
state.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE)
|
||||
state.enable(gl.BLEND)
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i]
|
||||
@@ -224,7 +227,7 @@ namespace Renderer {
|
||||
clear: () => {
|
||||
state.depthMask(true)
|
||||
state.colorMask(true, true, true, true)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
},
|
||||
render,
|
||||
@@ -243,6 +246,10 @@ namespace Renderer {
|
||||
Color.toVec3Normalized(bgColor, p.backgroundColor)
|
||||
ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
|
||||
}
|
||||
if (props.transparentBackground !== undefined && props.transparentBackground !== p.transparentBackground) {
|
||||
p.transparentBackground = props.transparentBackground
|
||||
ValueCell.update(globalUniforms.uTransparentBackground, p.transparentBackground ? 1 : 0)
|
||||
}
|
||||
if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
|
||||
p.lightIntensity = props.lightIntensity
|
||||
ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
|
||||
|
||||
@@ -2,10 +2,11 @@ export default `
|
||||
#ifdef dUseFog
|
||||
float depth = length(vViewPosition);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
if (fogAlpha < 0.01)
|
||||
discard;
|
||||
gl_FragColor = vec4(gl_FragColor.rgb, fogAlpha);
|
||||
if (uTransparentBackground == 0) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
} else {
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
}
|
||||
#endif
|
||||
`
|
||||
@@ -36,7 +36,7 @@ geometry.normal = normal;
|
||||
geometry.viewDir = normalize(vViewPosition);
|
||||
|
||||
IncidentLight directLight;
|
||||
directLight.direction = geometry.viewDir;
|
||||
directLight.direction = vec3(0.0, 0.0, -1.0);
|
||||
directLight.color = vec3(uLightIntensity);
|
||||
|
||||
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
|
||||
|
||||
@@ -5,7 +5,11 @@ uniform int uGroupCount;
|
||||
|
||||
uniform vec3 uHighlightColor;
|
||||
uniform vec3 uSelectColor;
|
||||
varying float vMarker;
|
||||
#if __VERSION__ != 300
|
||||
varying float vMarker;
|
||||
#else
|
||||
flat in float vMarker;
|
||||
#endif
|
||||
|
||||
varying vec3 vViewPosition;
|
||||
|
||||
@@ -18,6 +22,7 @@ uniform vec3 uFogColor;
|
||||
uniform float uAlpha;
|
||||
uniform float uPickingAlphaThreshold;
|
||||
uniform int uPickable;
|
||||
uniform int uTransparentBackground;
|
||||
|
||||
uniform float uInteriorDarkening;
|
||||
`
|
||||
@@ -8,7 +8,11 @@ uniform int uGroupCount;
|
||||
|
||||
uniform vec2 uMarkerTexDim;
|
||||
uniform sampler2D tMarker;
|
||||
varying float vMarker;
|
||||
#if __VERSION__ != 300
|
||||
varying float vMarker;
|
||||
#else
|
||||
flat out float vMarker;
|
||||
#endif
|
||||
|
||||
varying vec3 vViewPosition;
|
||||
`
|
||||
@@ -124,8 +124,8 @@ void main(void){
|
||||
#elif defined(dColorType_depth)
|
||||
gl_FragColor = material;
|
||||
#else
|
||||
vec3 normal = cameraNormal;
|
||||
vec3 vViewPosition = -cameraPos;
|
||||
vec3 normal = -cameraNormal;
|
||||
vec3 vViewPosition = cameraPos;
|
||||
#include apply_light_color
|
||||
|
||||
if (interior) {
|
||||
|
||||
@@ -190,6 +190,7 @@ export interface WebGLContext {
|
||||
readonly framebufferCache: FramebufferCache
|
||||
|
||||
readonly maxTextureSize: number
|
||||
readonly maxRenderbufferSize: number
|
||||
readonly maxDrawBuffers: number
|
||||
|
||||
unbindFramebuffer: () => void
|
||||
@@ -212,6 +213,7 @@ export function createContext(gl: GLRenderingContext): WebGLContext {
|
||||
|
||||
const parameters = {
|
||||
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,
|
||||
maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) as number,
|
||||
maxDrawBuffers: isWebGL2(gl) ? gl.getParameter(gl.MAX_DRAW_BUFFERS) as number : 0,
|
||||
maxVertexTextureImageUnits: gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) as number,
|
||||
}
|
||||
@@ -274,6 +276,7 @@ export function createContext(gl: GLRenderingContext): WebGLContext {
|
||||
framebufferCache,
|
||||
|
||||
get maxTextureSize () { return parameters.maxTextureSize },
|
||||
get maxRenderbufferSize () { return parameters.maxRenderbufferSize },
|
||||
get maxDrawBuffers () { return parameters.maxDrawBuffers },
|
||||
|
||||
unbindFramebuffer: () => unbindFramebuffer(gl),
|
||||
|
||||
57
src/mol-io/reader/3dg/parser.ts
Normal file
57
src/mol-io/reader/3dg/parser.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ReaderResult as Result } from '../result'
|
||||
import { Task } from '../../../mol-task'
|
||||
import { parseCsv } from '../csv/parser';
|
||||
import { Column, Table } from '../../../mol-data/db';
|
||||
import { toTable } from '../cif/schema';
|
||||
|
||||
import Schema = Column.Schema
|
||||
import { CsvTable } from '../csv/data-model';
|
||||
|
||||
|
||||
export const Schema3DG = {
|
||||
/** Chromosome name */
|
||||
chromosome: Schema.str,
|
||||
/** Base position */
|
||||
position: Schema.int,
|
||||
/** X coordinate */
|
||||
x: Schema.float,
|
||||
/** Y coordinate */
|
||||
y: Schema.float,
|
||||
/** Z coordinate */
|
||||
z: Schema.float,
|
||||
}
|
||||
export type Schema3DG = typeof Schema3DG
|
||||
|
||||
export interface File3DG {
|
||||
table: Table<Schema3DG>
|
||||
}
|
||||
|
||||
const FieldNames = [ 'chromosome', 'position', 'x', 'y', 'z' ]
|
||||
|
||||
function categoryFromTable(name: string, table: CsvTable) {
|
||||
return {
|
||||
name,
|
||||
rowCount: table.rowCount,
|
||||
fieldNames: FieldNames,
|
||||
getField: (name: string) => {
|
||||
return table.getColumn(FieldNames.indexOf(name).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function parse3DG(data: string) {
|
||||
return Task.create<Result<File3DG>>('Parse 3DG', async ctx => {
|
||||
const opts = { quote: '', comment: '#', delimiter: '\t', noColumnNames: true }
|
||||
const csvFile = await parseCsv(data, opts).runInContext(ctx)
|
||||
if (csvFile.isError) return Result.error(csvFile.message, csvFile.line)
|
||||
const category = categoryFromTable('3dg', csvFile.result.table)
|
||||
const table = toTable(Schema3DG, category)
|
||||
return Result.success({ table })
|
||||
});
|
||||
}
|
||||
33
src/mol-io/reader/_spec/3dg.spec.ts
Normal file
33
src/mol-io/reader/_spec/3dg.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { parse3DG } from '../3dg/parser';
|
||||
|
||||
const basic3dgString = `1(mat) 1420000 0.791377837067 10.9947291355 -13.1882897693
|
||||
1(mat) 1440000 -0.268241283699 10.5200875887 -13.0896257278
|
||||
1(mat) 1460000 -1.3853075236 10.5513787498 -13.1440142173
|
||||
1(mat) 1480000 -1.55984101733 11.4340829129 -13.6026301209
|
||||
1(mat) 1500000 -0.770991778399 11.4758488546 -14.5881137222
|
||||
1(mat) 1520000 -0.0848245107875 12.2624690808 -14.354289628
|
||||
1(mat) 1540000 -0.458643807046 12.5985791771 -13.4701149287
|
||||
1(mat) 1560000 -0.810322906201 12.2461643989 -12.3172933413
|
||||
1(mat) 1580000 -2.08211172035 12.8886838656 -12.8742007778
|
||||
1(mat) 1600000 -3.52093948201 13.1850935438 -12.4118684428`
|
||||
|
||||
describe('3dg reader', () => {
|
||||
it('basic', async () => {
|
||||
const parsed = await parse3DG(basic3dgString).run();
|
||||
expect(parsed.isError).toBe(false)
|
||||
|
||||
if (parsed.isError) return;
|
||||
const { chromosome, position, x, y, z } = parsed.result.table;
|
||||
expect(chromosome.value(0)).toBe('1(mat)')
|
||||
expect(position.value(1)).toBe(1440000)
|
||||
expect(x.value(5)).toBe(-0.0848245107875)
|
||||
expect(y.value(5)).toBe(12.2624690808)
|
||||
expect(z.value(5)).toBe(-14.354289628)
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column, ColumnHelpers } from '../../../mol-data/db'
|
||||
import { Column, ColumnHelpers, Table } from '../../../mol-data/db'
|
||||
import { Tensor } from '../../../mol-math/linear-algebra'
|
||||
import { getNumberType, NumberType, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser';
|
||||
import { Encoding } from '../../common/binary-cif';
|
||||
@@ -70,6 +70,14 @@ export namespace CifCategory {
|
||||
getField(name) { return fields[name]; }
|
||||
};
|
||||
}
|
||||
|
||||
export function ofTable(name: string, table: Table<any>) {
|
||||
const fields: { [name: string]: CifField | undefined } = {}
|
||||
for (const name of table._columns) {
|
||||
fields[name] = CifField.ofColumn(table[name])
|
||||
}
|
||||
return ofFields(name, fields)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +134,7 @@ export namespace CifField {
|
||||
float,
|
||||
valueKind,
|
||||
areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],
|
||||
toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),
|
||||
toStringArray: params => params ? ColumnHelpers.createAndFillArray(rowCount, str, params) : values as string[],
|
||||
toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params),
|
||||
toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
}
|
||||
@@ -138,6 +146,14 @@ export namespace CifField {
|
||||
const float: CifField['float'] = row => values[row];
|
||||
const valueKind: CifField['valueKind'] = row => Column.ValueKind.Present;
|
||||
|
||||
const toFloatArray = (params: Column.ToArrayParams<number>) => {
|
||||
if (!params || params.array && values instanceof params.array) {
|
||||
return values as number[]
|
||||
} else {
|
||||
return ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
__array: void 0,
|
||||
binaryEncoding: void 0,
|
||||
@@ -149,8 +165,8 @@ export namespace CifField {
|
||||
valueKind,
|
||||
areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],
|
||||
toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),
|
||||
toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params),
|
||||
toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
toIntArray: toFloatArray,
|
||||
toFloatArray
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,16 +224,22 @@ export namespace CifField {
|
||||
case 'float':
|
||||
case 'int':
|
||||
str = row => { return '' + column.value(row); };
|
||||
int = row => column.value(row);
|
||||
float = row => column.value(row);
|
||||
int = column.value;
|
||||
float = column.value;
|
||||
break
|
||||
case 'str':
|
||||
str = row => column.value(row);
|
||||
str = column.value;
|
||||
int = row => { const v = column.value(row); return fastParseInt(v, 0, v.length) || 0; };
|
||||
float = row => { const v = column.value(row); return fastParseFloat(v, 0, v.length) || 0; };
|
||||
break
|
||||
case 'list':
|
||||
const { separator } = column.schema;
|
||||
str = row => column.value(row).join(separator);
|
||||
int = row => NaN;
|
||||
float = row => NaN;
|
||||
break
|
||||
default:
|
||||
throw new Error('unsupported')
|
||||
throw new Error(`unsupported valueType '${column.schema.valueType}'`)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.313, IHM 1.01, CARB draft.
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.314, IHM 1.01, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.313, IHM 1.01, CARB draft.
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.314, IHM 1.01, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.313, IHM 1.01, CARB draft.
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.314, IHM 1.01, CARB draft.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
@@ -213,6 +213,89 @@ export const mmCIF_Schema = {
|
||||
*/
|
||||
ihm_model_id: int,
|
||||
},
|
||||
/**
|
||||
* Data items in the ATOM_SITE_ANISOTROP category record details
|
||||
* about anisotropic displacement parameters.
|
||||
* If the ATOM_SITE_ANISOTROP category is used for storing these
|
||||
* data, the corresponding ATOM_SITE data items are not used.
|
||||
*/
|
||||
atom_site_anisotrop: {
|
||||
/**
|
||||
* This data item is a pointer to _atom_site.id in the ATOM_SITE
|
||||
* category.
|
||||
*/
|
||||
id: int,
|
||||
/**
|
||||
* This data item is a pointer to _atom_type.symbol in the
|
||||
* ATOM_TYPE category.
|
||||
*/
|
||||
type_symbol: str,
|
||||
/**
|
||||
* The elements of the standard anisotropic atomic
|
||||
* displacement matrix U, which appears in the structure-factor
|
||||
* term as:
|
||||
*
|
||||
* T = exp{-2 pi^2^ sum~i~[sum~j~(U^ij^ h~i~ h~j~ a*~i~ a*~j~)]}
|
||||
*
|
||||
* h = the Miller indices
|
||||
* a* = the reciprocal space cell lengths
|
||||
*
|
||||
* These matrix elements may appear with atomic coordinates
|
||||
* in the ATOM_SITE category, or they may appear in the separate
|
||||
* ATOM_SITE_ANISOTROP category, but they may not appear in both
|
||||
* places. Similarly, anisotropic displacements may appear as
|
||||
* either B's or U's, but not as both.
|
||||
*
|
||||
* The unique elements of the real symmetric matrix are
|
||||
* entered by row.
|
||||
*/
|
||||
U: Matrix(3, 3),
|
||||
/**
|
||||
* The standard uncertainty (estimated standard deviation)
|
||||
* of _atom_site_anisotrop.U.
|
||||
*/
|
||||
U_esd: Matrix(3, 3),
|
||||
/**
|
||||
* Pointer to _atom_site.auth_seq_id
|
||||
*/
|
||||
pdbx_auth_seq_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.auth_asym_id
|
||||
*/
|
||||
pdbx_auth_asym_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.auth_atom_id
|
||||
*/
|
||||
pdbx_auth_atom_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.auth_comp_id
|
||||
*/
|
||||
pdbx_auth_comp_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.label_seq_id
|
||||
*/
|
||||
pdbx_label_seq_id: int,
|
||||
/**
|
||||
* Pointer to _atom_site.label_alt_id.
|
||||
*/
|
||||
pdbx_label_alt_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.label_asym_id
|
||||
*/
|
||||
pdbx_label_asym_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.label_atom_id
|
||||
*/
|
||||
pdbx_label_atom_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.label_comp_id
|
||||
*/
|
||||
pdbx_label_comp_id: str,
|
||||
/**
|
||||
* Pointer to _atom_site.pdbx_PDB_ins_code
|
||||
*/
|
||||
pdbx_PDB_ins_code: str,
|
||||
},
|
||||
/**
|
||||
* Data items in the ATOM_SITES category record details about
|
||||
* the crystallographic cell and cell transformations, which are
|
||||
|
||||
@@ -174,7 +174,7 @@ function moveNextInternal(state: State) {
|
||||
|
||||
if (tokenizer.position >= tokenizer.length) {
|
||||
state.tokenType = CsvTokenType.End;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
tokenizer.tokenStart = tokenizer.position;
|
||||
@@ -209,22 +209,26 @@ function moveNext(state: State) {
|
||||
function readRecordsChunk(chunkSize: number, state: State) {
|
||||
if (state.tokenType === CsvTokenType.End) return 0
|
||||
|
||||
let newRecord = moveNext(state);
|
||||
if (newRecord) ++state.recordCount
|
||||
let counter = 0;
|
||||
let newRecord: boolean | undefined
|
||||
|
||||
const { tokens, tokenizer } = state;
|
||||
let counter = 0;
|
||||
|
||||
while (state.tokenType === CsvTokenType.Value && counter < chunkSize) {
|
||||
TokenBuilder.add(tokens[state.fieldCount % state.columnCount], tokenizer.tokenStart, tokenizer.tokenEnd);
|
||||
++state.fieldCount
|
||||
newRecord = moveNext(state);
|
||||
if (newRecord) ++state.recordCount
|
||||
++counter;
|
||||
if (newRecord) {
|
||||
++state.recordCount
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
function readRecordsChunks(state: State) {
|
||||
let newRecord = moveNext(state);
|
||||
if (newRecord) ++state.recordCount
|
||||
return chunkedSubtask(state.runtimeCtx, 100000, state, readRecordsChunk,
|
||||
(ctx, state) => ctx.update({ message: 'Parsing...', current: state.tokenizer.position, max: state.data.length }));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d';
|
||||
import { Sphere3D } from './primitives/sphere3d';
|
||||
|
||||
export { CentroidHelper }
|
||||
|
||||
@@ -35,6 +36,10 @@ class CentroidHelper {
|
||||
if (d > this.radiusSq) this.radiusSq = d;
|
||||
}
|
||||
|
||||
getSphere(): Sphere3D {
|
||||
return { center: Vec3.clone(this.center), radius: Math.sqrt(this.radiusSq) };
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Sphere3D {
|
||||
export function equals(a: Sphere3D, b: Sphere3D) {
|
||||
const ar = a.radius;
|
||||
const br = b.radius;
|
||||
return (Math.abs(ar - br) <= EPSILON.Value * Math.max(1.0, Math.abs(ar), Math.abs(br)) &&
|
||||
return (Math.abs(ar - br) <= EPSILON * Math.max(1.0, Math.abs(ar), Math.abs(br)) &&
|
||||
Vec3.equals(a.center, b.center));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../linear-algebra'
|
||||
@@ -9,9 +10,10 @@ import { SpacegroupName, TransformData, GroupData, getSpacegroupIndex, OperatorD
|
||||
import { SymmetryOperator } from '../../geometry';
|
||||
|
||||
interface SpacegroupCell {
|
||||
// zero based spacegroup number
|
||||
/** Zero based spacegroup number */
|
||||
readonly index: number,
|
||||
readonly size: Vec3,
|
||||
readonly volume: number,
|
||||
readonly anglesInRadians: Vec3,
|
||||
/** Transfrom cartesian -> fractional coordinates within the cell */
|
||||
readonly toFractional: Mat4,
|
||||
@@ -34,7 +36,7 @@ namespace SpacegroupCell {
|
||||
return cell.index === 0 && cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1;
|
||||
}
|
||||
|
||||
// returns Zero cell if the spacegroup does not exist
|
||||
/** Returns Zero cell if the spacegroup does not exist */
|
||||
export function create(nameOrNumber: number | string | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell {
|
||||
const index = getSpacegroupIndex(nameOrNumber);
|
||||
if (index < 0) {
|
||||
@@ -42,6 +44,8 @@ namespace SpacegroupCell {
|
||||
return Zero;
|
||||
}
|
||||
|
||||
const volume = size[0] * size[1] * size[2]
|
||||
|
||||
const alpha = anglesInRadians[0];
|
||||
const beta = anglesInRadians[1];
|
||||
const gamma = anglesInRadians[2];
|
||||
@@ -64,13 +68,13 @@ namespace SpacegroupCell {
|
||||
]);
|
||||
const toFractional = Mat4.invert(Mat4.zero(), fromFractional)!;
|
||||
|
||||
return { index, size, anglesInRadians, toFractional, fromFractional };
|
||||
return { index, size, volume, anglesInRadians, toFractional, fromFractional };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Spacegroup {
|
||||
// P1 with [1, 1, 1] cell.
|
||||
/** P1 with [1, 1, 1] cell */
|
||||
export const ZeroP1 = create(SpacegroupCell.Zero);
|
||||
|
||||
export function create(cell: SpacegroupCell): Spacegroup {
|
||||
@@ -78,18 +82,56 @@ namespace Spacegroup {
|
||||
return { name: SpacegroupNames[cell.index], cell, operators };
|
||||
}
|
||||
|
||||
const _tempVec = Vec3.zero(), _tempMat = Mat4.zero();
|
||||
export function updateOperatorMatrix(spacegroup: Spacegroup, index: number, i: number, j: number, k: number, target: Mat4) {
|
||||
_tempVec[0] = i;
|
||||
_tempVec[1] = j;
|
||||
_tempVec[2] = k;
|
||||
const _ijkVec = Vec3();
|
||||
const _tempMat = Mat4();
|
||||
export function setOperatorMatrix(spacegroup: Spacegroup, index: number, i: number, j: number, k: number, target: Mat4) {
|
||||
Vec3.set(_ijkVec, i, j, k);
|
||||
|
||||
Mat4.fromTranslation(_tempMat, _tempVec);
|
||||
return Mat4.mul(target, Mat4.mul(target, Mat4.mul(target, spacegroup.cell.fromFractional, _tempMat), spacegroup.operators[index]), spacegroup.cell.toFractional);
|
||||
Mat4.fromTranslation(_tempMat, _ijkVec);
|
||||
return Mat4.mul(
|
||||
target,
|
||||
Mat4.mul(
|
||||
target,
|
||||
Mat4.mul(target, spacegroup.cell.fromFractional, _tempMat),
|
||||
spacegroup.operators[index]
|
||||
),
|
||||
spacegroup.cell.toFractional
|
||||
);
|
||||
}
|
||||
|
||||
export function getSymmetryOperator(spacegroup: Spacegroup, index: number, i: number, j: number, k: number): SymmetryOperator {
|
||||
const operator = updateOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero());
|
||||
const operator = setOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero());
|
||||
return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, { id: '', operList: [] }, '', Vec3.create(i, j, k), index);
|
||||
}
|
||||
|
||||
const _translationRef = Vec3()
|
||||
const _translationRefSymop = Vec3()
|
||||
const _translationSymop = Vec3()
|
||||
export function setOperatorMatrixRef(spacegroup: Spacegroup, index: number, i: number, j: number, k: number, ref: Vec3, target: Mat4) {
|
||||
Vec3.set(_ijkVec, i, j, k);
|
||||
Vec3.floor(_translationRef, ref)
|
||||
|
||||
Mat4.copy(target, spacegroup.operators[index])
|
||||
|
||||
Vec3.floor(_translationRefSymop, Vec3.transformMat4(_translationRefSymop, ref, target))
|
||||
|
||||
Mat4.getTranslation(_translationSymop, target)
|
||||
Vec3.sub(_translationSymop, _translationSymop, _translationRefSymop)
|
||||
Vec3.add(_translationSymop, _translationSymop, _translationRef)
|
||||
Vec3.add(_translationSymop, _translationSymop, _ijkVec)
|
||||
|
||||
Mat4.setTranslation(target, _translationSymop)
|
||||
Mat4.mul(target, spacegroup.cell.fromFractional, target)
|
||||
Mat4.mul(target, target, spacegroup.cell.toFractional)
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Symmetry operator for transformation around the given
|
||||
* reference point `ref` in fractional coordinates
|
||||
*/
|
||||
export function getSymmetryOperatorRef(spacegroup: Spacegroup, index: number, i: number, j: number, k: number, ref: Vec3) {
|
||||
const operator = setOperatorMatrixRef(spacegroup, index, i, j, k, ref, Mat4.zero());
|
||||
return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, { id: '', operList: [] }, '', Vec3.create(i, j, k), index);
|
||||
}
|
||||
|
||||
|
||||
@@ -1345,7 +1345,17 @@ export const SpacegroupNames: { [num: number]: SpacegroupName } = (function () {
|
||||
|
||||
// return -1 if the spacegroup does not exist.
|
||||
export function getSpacegroupIndex(nameOrNumber: number | string | SpacegroupName): number {
|
||||
const index = typeof nameOrNumber === 'number' ? nameOrNumber - 1 : ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName];
|
||||
let index: number
|
||||
if (typeof nameOrNumber === 'number') {
|
||||
// used by CCP4, see http://www.ccp4.ac.uk/html/pointless.html#setting
|
||||
if (nameOrNumber === 1017) index = 254
|
||||
else if (nameOrNumber === 2017) index = 255
|
||||
else if (nameOrNumber === 2018) index = 257
|
||||
else if (nameOrNumber === 3018) index = 258
|
||||
else index = nameOrNumber - 1
|
||||
} else {
|
||||
index = ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName];
|
||||
}
|
||||
if (typeof index === 'undefined' || typeof SpacegroupNames[index] === 'undefined') return -1;
|
||||
return index;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -17,7 +17,7 @@
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*/
|
||||
|
||||
export const enum EPSILON { Value = 0.000001 }
|
||||
export const EPSILON = 0.000001;
|
||||
|
||||
export function equalEps(a: number, b: number, eps: number) {
|
||||
return Math.abs(a - b) <= eps;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -17,10 +17,11 @@
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*/
|
||||
|
||||
import { Mat4 } from '../3d'
|
||||
import { Mat4, Vec3, EPSILON } from '../3d'
|
||||
import { NumberArray } from '../../../mol-util/type-helpers';
|
||||
|
||||
interface Mat3 extends Array<number> { [d: number]: number, '@type': 'mat3', length: 9 }
|
||||
interface ReadonlyMat3 extends Array<number> { readonly [d: number]: number, '@type': 'mat3', length: 9 }
|
||||
|
||||
function Mat3() {
|
||||
return Mat3.zero();
|
||||
@@ -71,6 +72,7 @@ namespace Mat3 {
|
||||
out[offset + 6] = a[6];
|
||||
out[offset + 7] = a[7];
|
||||
out[offset + 8] = a[8];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromArray(a: Mat3, array: NumberArray, offset: number) {
|
||||
@@ -102,6 +104,25 @@ namespace Mat3 {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function create(a00: number, a01: number, a02: number, a10: number, a11: number, a12: number, a20: number, a21: number, a22: number): Mat3 {
|
||||
const out = zero();
|
||||
out[0] = a00
|
||||
out[1] = a01
|
||||
out[2] = a02
|
||||
out[3] = a10
|
||||
out[4] = a11
|
||||
out[5] = a12
|
||||
out[6] = a20
|
||||
out[7] = a21
|
||||
out[8] = a22
|
||||
return out;
|
||||
}
|
||||
|
||||
const _id = identity();
|
||||
export function isIdentity(m: Mat3, eps?: number) {
|
||||
return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);
|
||||
}
|
||||
|
||||
export function hasNaN(m: Mat3) {
|
||||
for (let i = 0; i < 9; i++) if (isNaN(m[i])) return true
|
||||
return false
|
||||
@@ -114,6 +135,13 @@ namespace Mat3 {
|
||||
return Mat3.copy(Mat3.zero(), a);
|
||||
}
|
||||
|
||||
export function areEqual(a: Mat3, b: Mat3, eps: number) {
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (Math.abs(a[i] - b[i]) > eps) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function setValue(a: Mat3, i: number, j: number, value: number) {
|
||||
a[3 * j + i] = value;
|
||||
}
|
||||
@@ -198,6 +226,44 @@ namespace Mat3 {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function symmtricFromUpper(out: Mat3, a: Mat3) {
|
||||
if (out === a) {
|
||||
out[3] = a[1];
|
||||
out[6] = a[2];
|
||||
out[7] = a[5];
|
||||
} else {
|
||||
out[0] = a[0];
|
||||
out[1] = a[1];
|
||||
out[2] = a[2];
|
||||
out[3] = a[1];
|
||||
out[4] = a[4];
|
||||
out[5] = a[5];
|
||||
out[6] = a[2];
|
||||
out[7] = a[5];
|
||||
out[8] = a[8];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function symmtricFromLower(out: Mat3, a: Mat3) {
|
||||
if (out === a) {
|
||||
out[1] = a[3];
|
||||
out[2] = a[6];
|
||||
out[5] = a[7];
|
||||
} else {
|
||||
out[0] = a[0];
|
||||
out[1] = a[3];
|
||||
out[2] = a[6];
|
||||
out[3] = a[3];
|
||||
out[4] = a[4];
|
||||
out[5] = a[7];
|
||||
out[6] = a[6];
|
||||
out[7] = a[7];
|
||||
out[8] = a[8];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function determinant(a: Mat3) {
|
||||
const a00 = a[0], a01 = a[1], a02 = a[2];
|
||||
const a10 = a[3], a11 = a[4], a12 = a[5];
|
||||
@@ -210,6 +276,172 @@ namespace Mat3 {
|
||||
// Calculate the determinant
|
||||
return a00 * b01 + a01 * b11 + a02 * b21;
|
||||
}
|
||||
|
||||
export function trace(a: Mat3) {
|
||||
return a[0] + a[4] + a[8]
|
||||
}
|
||||
|
||||
export function sub(out: Mat3, a: Mat3, b: Mat3) {
|
||||
out[0] = a[0] - b[0]
|
||||
out[1] = a[1] - b[1]
|
||||
out[2] = a[2] - b[2]
|
||||
out[3] = a[3] - b[3]
|
||||
out[4] = a[4] - b[4]
|
||||
out[5] = a[5] - b[5]
|
||||
out[6] = a[6] - b[6]
|
||||
out[7] = a[7] - b[7]
|
||||
out[8] = a[8] - b[8]
|
||||
return out
|
||||
}
|
||||
|
||||
export function add(out: Mat3, a: Mat3, b: Mat3) {
|
||||
out[0] = a[0] + b[0]
|
||||
out[1] = a[1] + b[1]
|
||||
out[2] = a[2] + b[2]
|
||||
out[3] = a[3] + b[3]
|
||||
out[4] = a[4] + b[4]
|
||||
out[5] = a[5] + b[5]
|
||||
out[6] = a[6] + b[6]
|
||||
out[7] = a[7] + b[7]
|
||||
out[8] = a[8] + b[8]
|
||||
return out
|
||||
}
|
||||
|
||||
export function mul(out: Mat3, a: Mat3, b: Mat3) {
|
||||
const a00 = a[0], a01 = a[1], a02 = a[2],
|
||||
a10 = a[3], a11 = a[4], a12 = a[5],
|
||||
a20 = a[6], a21 = a[7], a22 = a[8];
|
||||
|
||||
const b00 = b[0], b01 = b[1], b02 = b[2],
|
||||
b10 = b[3], b11 = b[4], b12 = b[5],
|
||||
b20 = b[6], b21 = b[7], b22 = b[8];
|
||||
|
||||
out[0] = b00 * a00 + b01 * a10 + b02 * a20;
|
||||
out[1] = b00 * a01 + b01 * a11 + b02 * a21;
|
||||
out[2] = b00 * a02 + b01 * a12 + b02 * a22;
|
||||
|
||||
out[3] = b10 * a00 + b11 * a10 + b12 * a20;
|
||||
out[4] = b10 * a01 + b11 * a11 + b12 * a21;
|
||||
out[5] = b10 * a02 + b11 * a12 + b12 * a22;
|
||||
|
||||
out[6] = b20 * a00 + b21 * a10 + b22 * a20;
|
||||
out[7] = b20 * a01 + b21 * a11 + b22 * a21;
|
||||
out[8] = b20 * a02 + b21 * a12 + b22 * a22;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function subScalar(out: Mat3, a: Mat3, s: number) {
|
||||
out[0] = a[0] - s
|
||||
out[1] = a[1] - s
|
||||
out[2] = a[2] - s
|
||||
out[3] = a[3] - s
|
||||
out[4] = a[4] - s
|
||||
out[5] = a[5] - s
|
||||
out[6] = a[6] - s
|
||||
out[7] = a[7] - s
|
||||
out[8] = a[8] - s
|
||||
return out
|
||||
}
|
||||
|
||||
export function addScalar(out: Mat3, a: Mat3, s: number) {
|
||||
out[0] = a[0] + s
|
||||
out[1] = a[1] + s
|
||||
out[2] = a[2] + s
|
||||
out[3] = a[3] + s
|
||||
out[4] = a[4] + s
|
||||
out[5] = a[5] + s
|
||||
out[6] = a[6] + s
|
||||
out[7] = a[7] + s
|
||||
out[8] = a[8] + s
|
||||
return out
|
||||
}
|
||||
|
||||
export function mulScalar(out: Mat3, a: Mat3, s: number) {
|
||||
out[0] = a[0] * s
|
||||
out[1] = a[1] * s
|
||||
out[2] = a[2] * s
|
||||
out[3] = a[3] * s
|
||||
out[4] = a[4] * s
|
||||
out[5] = a[5] * s
|
||||
out[6] = a[6] * s
|
||||
out[7] = a[7] * s
|
||||
out[8] = a[8] * s
|
||||
return out
|
||||
}
|
||||
|
||||
const piThird = Math.PI / 3
|
||||
const tmpB = Mat3()
|
||||
/**
|
||||
* Given a real symmetric 3x3 matrix A, compute the eigenvalues
|
||||
*
|
||||
* From https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
|
||||
*/
|
||||
export function symmetricEigenvalues(out: Vec3, a: Mat3) {
|
||||
const p1 = a[1] * a[1] + a[2] * a[2] + a[5] * a[5]
|
||||
if (p1 === 0) {
|
||||
out[0] = a[0]
|
||||
out[1] = a[4]
|
||||
out[2] = a[8]
|
||||
} else {
|
||||
const q = trace(a) / 3
|
||||
const a1 = a[0] - q
|
||||
const a2 = a[4] - q
|
||||
const a3 = a[8] - q
|
||||
const p2 = a1 * a1 + a2 * a2 + a3 * a3 + 2 * p1
|
||||
const p = Math.sqrt(p2 / 6)
|
||||
mulScalar(tmpB, Identity, q)
|
||||
sub(tmpB, a, tmpB)
|
||||
mulScalar(tmpB, tmpB, (1 / p))
|
||||
const r = determinant(tmpB) / 2
|
||||
// In exact arithmetic for a symmetric matrix -1 <= r <= 1
|
||||
// but computation error can leave it slightly outside this range.
|
||||
const phi = r <= -1 ? piThird : r >= 1 ?
|
||||
0 : Math.acos(r) / 3
|
||||
// the eigenvalues satisfy eig3 <= eig2 <= eig1
|
||||
out[0] = q + 2 * p * Math.cos(phi)
|
||||
out[2] = q + 2 * p * Math.cos(phi + (2 * piThird))
|
||||
out[1] = 3 * q - out[0] - out[2] // since trace(A) = eig1 + eig2 + eig3
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
const tmpR0 = [0.1, 0.0, 0.0] as Vec3
|
||||
const tmpR1 = [0.1, 0.0, 0.0] as Vec3
|
||||
const tmpR2 = [0.1, 0.0, 0.0] as Vec3
|
||||
const tmpR0xR1 = [0.1, 0.0, 0.0] as Vec3
|
||||
const tmpR0xR2 = [0.1, 0.0, 0.0] as Vec3
|
||||
const tmpR1xR2 = [0.1, 0.0, 0.0] as Vec3
|
||||
/**
|
||||
* Calculates the eigenvector for the given eigenvalue `e` of matrix `a`
|
||||
*/
|
||||
export function eigenvector(out: Vec3, a: Mat3, e: number) {
|
||||
Vec3.set(tmpR0, a[0] - e, a[1], a[2])
|
||||
Vec3.set(tmpR1, a[1], a[4] - e, a[5])
|
||||
Vec3.set(tmpR2, a[2], a[5], a[8] - e)
|
||||
Vec3.cross(tmpR0xR1, tmpR0, tmpR1)
|
||||
Vec3.cross(tmpR0xR2, tmpR0, tmpR2)
|
||||
Vec3.cross(tmpR1xR2, tmpR1, tmpR2)
|
||||
const d0 = Vec3.dot(tmpR0xR1, tmpR0xR1)
|
||||
const d1 = Vec3.dot(tmpR0xR2, tmpR0xR2)
|
||||
const d2 = Vec3.dot(tmpR1xR2, tmpR1xR2)
|
||||
let dmax = d0
|
||||
let imax = 0
|
||||
if (d1 > dmax) {
|
||||
dmax = d1
|
||||
imax = 1
|
||||
}
|
||||
if (d2 > dmax) imax = 2
|
||||
if (imax === 0) {
|
||||
Vec3.scale(out, tmpR0xR1, 1 / Math.sqrt(d0))
|
||||
} else if (imax === 1) {
|
||||
Vec3.scale(out, tmpR0xR2, 1 / Math.sqrt(d1))
|
||||
} else {
|
||||
Vec3.scale(out, tmpR1xR2, 1 / Math.sqrt(d2))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
export const Identity: ReadonlyMat3 = identity()
|
||||
}
|
||||
|
||||
export default Mat3
|
||||
@@ -101,7 +101,7 @@ namespace Mat4 {
|
||||
|
||||
const _id = identity();
|
||||
export function isIdentity(m: Mat4, eps?: number) {
|
||||
return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON.Value : eps);
|
||||
return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);
|
||||
}
|
||||
|
||||
export function hasNaN(m: Mat4) {
|
||||
@@ -141,6 +141,7 @@ namespace Mat4 {
|
||||
out[offset + 13] = a[13];
|
||||
out[offset + 14] = a[14];
|
||||
out[offset + 15] = a[15];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromArray(a: Mat4, array: NumberArray, offset: number) {
|
||||
@@ -499,7 +500,7 @@ namespace Mat4 {
|
||||
b10, b11, b12,
|
||||
b20, b21, b22;
|
||||
|
||||
if (Math.abs(len) < EPSILON.Value) {
|
||||
if (Math.abs(len) < EPSILON) {
|
||||
return Mat4.identity();
|
||||
}
|
||||
|
||||
@@ -549,7 +550,7 @@ namespace Mat4 {
|
||||
len = Math.sqrt(x * x + y * y + z * z),
|
||||
s, c, t;
|
||||
|
||||
if (Math.abs(len) < EPSILON.Value) { return setIdentity(out); }
|
||||
if (Math.abs(len) < EPSILON) { return setIdentity(out); }
|
||||
|
||||
len = 1 / len;
|
||||
x *= len;
|
||||
@@ -719,7 +720,7 @@ namespace Mat4 {
|
||||
* [ 0 1 ]
|
||||
*/
|
||||
export function isRotationAndTranslation(a: Mat4, eps?: number) {
|
||||
return _isRotationAndTranslation(a, typeof eps !== 'undefined' ? eps : EPSILON.Value)
|
||||
return _isRotationAndTranslation(a, typeof eps !== 'undefined' ? eps : EPSILON)
|
||||
}
|
||||
|
||||
function _isRotationAndTranslation(a: Mat4, eps: number) {
|
||||
@@ -854,9 +855,9 @@ namespace Mat4 {
|
||||
const centery = center[1];
|
||||
const centerz = center[2];
|
||||
|
||||
if (Math.abs(eyex - centerx) < EPSILON.Value &&
|
||||
Math.abs(eyey - centery) < EPSILON.Value &&
|
||||
Math.abs(eyez - centerz) < EPSILON.Value
|
||||
if (Math.abs(eyex - centerx) < EPSILON &&
|
||||
Math.abs(eyey - centery) < EPSILON &&
|
||||
Math.abs(eyez - centerz) < EPSILON
|
||||
) {
|
||||
return setIdentity(out);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { EPSILON } from './common';
|
||||
import { NumberArray } from '../../../mol-util/type-helpers';
|
||||
|
||||
interface Quat extends Array<number> { [d: number]: number, '@type': 'quat', length: 4 }
|
||||
interface ReadonlyQuat extends Array<number> { readonly [d: number]: number, '@type': 'quat', length: 4 }
|
||||
|
||||
function Quat() {
|
||||
return Quat.zero();
|
||||
@@ -281,7 +282,7 @@ namespace Quat {
|
||||
export function fromUnitVec3 (out: Quat, a: Vec3, b: Vec3) {
|
||||
// assumes a and b are normalized
|
||||
let r = Vec3.dot(a, b) + 1
|
||||
if (r < EPSILON.Value) {
|
||||
if (r < EPSILON) {
|
||||
// If u and v are exactly opposite, rotate 180 degrees
|
||||
// around an arbitrary orthogonal axis. Axis normalisation
|
||||
// can happen later, when we normalise the quaternion.
|
||||
@@ -318,6 +319,7 @@ namespace Quat {
|
||||
out[offset + 1] = a[1];
|
||||
out[offset + 2] = a[2];
|
||||
out[offset + 3] = a[3];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromArray(a: Quat, array: NumberArray, offset: number) {
|
||||
@@ -439,6 +441,8 @@ namespace Quat {
|
||||
export function toString(a: Quat, precision?: number) {
|
||||
return `[${a[0].toPrecision(precision)} ${a[1].toPrecision(precision)} ${a[2].toPrecision(precision)} ${a[3].toPrecision(precision)}]`;
|
||||
}
|
||||
|
||||
export const Identity: ReadonlyQuat = identity()
|
||||
}
|
||||
|
||||
export default Quat
|
||||
@@ -53,6 +53,7 @@ namespace Vec2 {
|
||||
export function toArray(a: Vec2, out: NumberArray, offset: number) {
|
||||
out[offset + 0] = a[0];
|
||||
out[offset + 1] = a[1];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromArray(a: Vec2, array: NumberArray, offset: number) {
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace Vec3 {
|
||||
out[offset + 0] = v[0]
|
||||
out[offset + 1] = v[1]
|
||||
out[offset + 2] = v[2]
|
||||
return out
|
||||
}
|
||||
|
||||
export function create(x: number, y: number, z: number): Vec3 {
|
||||
@@ -490,9 +491,9 @@ namespace Vec3 {
|
||||
export function equals(a: Vec3, b: Vec3) {
|
||||
const a0 = a[0], a1 = a[1], a2 = a[2];
|
||||
const b0 = b[0], b1 = b[1], b2 = b[2];
|
||||
return (Math.abs(a0 - b0) <= EPSILON.Value * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
|
||||
Math.abs(a1 - b1) <= EPSILON.Value * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON.Value * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
|
||||
return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
|
||||
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
|
||||
}
|
||||
|
||||
const rotTemp = zero();
|
||||
@@ -500,7 +501,7 @@ namespace Vec3 {
|
||||
export function makeRotation(mat: Mat4, a: Vec3, b: Vec3): Mat4 {
|
||||
const by = angle(a, b);
|
||||
if (Math.abs(by) < 0.0001) return Mat4.setIdentity(mat);
|
||||
if (Math.abs(by - Math.PI) < EPSILON.Value) {
|
||||
if (Math.abs(by - Math.PI) < EPSILON) {
|
||||
// here, axis can be [0,0,0] but the rotation is a simple flip
|
||||
return Mat4.fromScaling(mat, flipScaling);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Vec4 {
|
||||
out[offset + 1] = a[1];
|
||||
out[offset + 2] = a[2];
|
||||
out[offset + 3] = a[3];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function fromArray(a: Vec4, array: NumberArray, offset: number) {
|
||||
@@ -220,10 +221,10 @@ namespace Vec4 {
|
||||
export function equals(a: Vec4, b: Vec4) {
|
||||
const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
|
||||
const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
|
||||
return (Math.abs(a0 - b0) <= EPSILON.Value * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
|
||||
Math.abs(a1 - b1) <= EPSILON.Value * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON.Value * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
|
||||
Math.abs(a3 - b3) <= EPSILON.Value * Math.max(1.0, Math.abs(a3), Math.abs(b3)));
|
||||
return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
|
||||
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
|
||||
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
|
||||
Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)));
|
||||
}
|
||||
|
||||
export function toString(a: Vec4, precision?: number) {
|
||||
|
||||
35
src/mol-math/linear-algebra/_spec/mat3.spec.ts
Normal file
35
src/mol-math/linear-algebra/_spec/mat3.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat3, Vec3 } from '../3d'
|
||||
|
||||
describe('Mat3', () => {
|
||||
it('symmetricEigenvalues', () => {
|
||||
const m = Mat3.create(
|
||||
0.1945, -0.0219, -0.0416,
|
||||
-0.0219, 0.1995, -0.0119,
|
||||
-0.0416, -0.0119, 0.3673
|
||||
)
|
||||
const e = Vec3.create(0.377052701425898, 0.21713981522725134, 0.1671074833468507)
|
||||
expect(Vec3.equals(e, Mat3.symmetricEigenvalues(Vec3(), m))).toBe(true);
|
||||
});
|
||||
|
||||
it('eigenvectors', () => {
|
||||
const m = Mat3.create(
|
||||
0.1945, -0.0219, -0.0416,
|
||||
-0.0219, 0.1995, -0.0119,
|
||||
-0.0416, -0.0119, 0.3673
|
||||
)
|
||||
const e = Vec3.create(0.377052701425898, 0.21713981522725134, 0.1671074833468507)
|
||||
const v0 = Vec3.create(-0.2176231019882068, -0.038522620041966125, 0.9752723687391808)
|
||||
const v1 = Vec3.create(-0.5905636938047126, 0.8007524989198634, -0.10014968314142503)
|
||||
const v2 = Vec3.create(0.7770937582036648, 0.5977553372576602, 0.19701230352667118)
|
||||
|
||||
expect(Vec3.equals(v0, Mat3.eigenvector(Vec3(), m, e[0]))).toBe(true);
|
||||
expect(Vec3.equals(v1, Mat3.eigenvector(Vec3(), m, e[1]))).toBe(true);
|
||||
expect(Vec3.equals(v2, Mat3.eigenvector(Vec3(), m, e[2]))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -56,40 +56,36 @@ export namespace Tensor {
|
||||
export function ColumnMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [1, 0], ctor); }
|
||||
export function RowMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [0, 1], ctor); }
|
||||
|
||||
export function toMat4(space: Space, data: Tensor.Data): Mat4 {
|
||||
export function toMat4(out: Mat4, space: Space, data: Tensor.Data): Mat4 {
|
||||
if (space.rank !== 2) throw new Error('Invalid tensor rank');
|
||||
const mat = Mat4.zero();
|
||||
const d0 = Math.min(4, space.dimensions[0]), d1 = Math.min(4, space.dimensions[1]);
|
||||
for (let i = 0; i < d0; i++) {
|
||||
for (let j = 0; j < d1; j++) Mat4.setValue(mat, i, j, space.get(data, i, j));
|
||||
for (let j = 0; j < d1; j++) Mat4.setValue(out, i, j, space.get(data, i, j));
|
||||
}
|
||||
return mat;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function toMat3(space: Space, data: Tensor.Data): Mat3 {
|
||||
export function toMat3(out: Mat3, space: Space, data: Tensor.Data): Mat3 {
|
||||
if (space.rank !== 2) throw new Error('Invalid tensor rank');
|
||||
const mat = Mat3.zero();
|
||||
const d0 = Math.min(3, space.dimensions[0]), d1 = Math.min(3, space.dimensions[1]);
|
||||
for (let i = 0; i < d0; i++) {
|
||||
for (let j = 0; j < d1; j++) Mat3.setValue(mat, i, j, space.get(data, i, j));
|
||||
for (let j = 0; j < d1; j++) Mat3.setValue(out, i, j, space.get(data, i, j));
|
||||
}
|
||||
return mat;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function toVec3(space: Space, data: Tensor.Data): Vec3 {
|
||||
export function toVec3(out: Vec3, space: Space, data: Tensor.Data): Vec3 {
|
||||
if (space.rank !== 1) throw new Error('Invalid tensor rank');
|
||||
const vec = Vec3.zero();
|
||||
const d0 = Math.min(3, space.dimensions[0]);
|
||||
for (let i = 0; i < d0; i++) vec[i] = data[i];
|
||||
return vec;
|
||||
for (let i = 0; i < d0; i++) out[i] = data[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function toVec4(space: Space, data: Tensor.Data): Vec4 {
|
||||
export function toVec4(out: Vec4, space: Space, data: Tensor.Data): Vec4 {
|
||||
if (space.rank !== 1) throw new Error('Invalid tensor rank');
|
||||
const vec = Vec4.zero();
|
||||
const d0 = Math.min(4, space.dimensions[0]);
|
||||
for (let i = 0; i < d0; i++) vec[i] = data[i];
|
||||
return vec;
|
||||
for (let i = 0; i < d0; i++) out[i] = data[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function areEqualExact(a: Tensor.Data, b: Tensor.Data) {
|
||||
|
||||
@@ -14,4 +14,19 @@ export function radToDeg (rad: number) {
|
||||
|
||||
export function isPowerOfTwo (x: number) {
|
||||
return (x !== 0) && (x & (x - 1)) === 0
|
||||
}
|
||||
|
||||
/** return the value that has the largest absolute value */
|
||||
export function absMax(...values: number[]) {
|
||||
let max = 0
|
||||
let absMax = 0
|
||||
for (let i = 0, il = values.length; i < il; ++i) {
|
||||
const value = values[i]
|
||||
const abs = Math.abs(value)
|
||||
if (abs > absMax) {
|
||||
max = value
|
||||
absMax = abs
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
79
src/mol-model-formats/structure/3dg.ts
Normal file
79
src/mol-model-formats/structure/3dg.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Model } from '../../mol-model/structure/model';
|
||||
import { Task } from '../../mol-task';
|
||||
import { ModelFormat } from './format';
|
||||
import { _parse_mmCif } from './mmcif/parser';
|
||||
import { CifCategory, CifField } from '../../mol-io/reader/cif';
|
||||
import { Column } from '../../mol-data/db';
|
||||
import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
|
||||
import { EntityBuilder } from './common/entity';
|
||||
import { File3DG } from '../../mol-io/reader/3dg/parser';
|
||||
import { fillSerial } from '../../mol-util/array';
|
||||
import { MoleculeType } from '../../mol-model/structure/model/types';
|
||||
|
||||
function getCategories(table: File3DG['table']) {
|
||||
const entityIds = new Array<string>(table._rowCount)
|
||||
const entityBuilder = new EntityBuilder()
|
||||
|
||||
const seqIdStarts = table.position.toArray({ array: Uint32Array })
|
||||
const seqIdEnds = new Uint32Array(table._rowCount)
|
||||
const stride = seqIdStarts[1] - seqIdStarts[0]
|
||||
|
||||
const objectRadius = stride / 3500
|
||||
|
||||
for (let i = 0, il = table._rowCount; i < il; ++i) {
|
||||
const chr = table.chromosome.value(i)
|
||||
const entityId = entityBuilder.getEntityId(chr, MoleculeType.DNA, chr)
|
||||
entityIds[i] = entityId
|
||||
seqIdEnds[i] = seqIdStarts[i] + stride - 1
|
||||
}
|
||||
|
||||
const ihm_sphere_obj_site: CifCategory.SomeFields<mmCIF_Schema['ihm_sphere_obj_site']> = {
|
||||
id: CifField.ofNumbers(fillSerial(new Uint32Array(table._rowCount))),
|
||||
entity_id: CifField.ofStrings(entityIds),
|
||||
seq_id_begin: CifField.ofNumbers(seqIdStarts),
|
||||
seq_id_end: CifField.ofNumbers(seqIdEnds),
|
||||
asym_id: CifField.ofColumn(table.chromosome),
|
||||
|
||||
Cartn_x: CifField.ofNumbers(Column.mapToArray(table.x, x => x * 10, Float32Array)),
|
||||
Cartn_y: CifField.ofNumbers(Column.mapToArray(table.y, y => y * 10, Float32Array)),
|
||||
Cartn_z: CifField.ofNumbers(Column.mapToArray(table.z, z => z * 10, Float32Array)),
|
||||
|
||||
object_radius: CifField.ofColumn(Column.ofConst(objectRadius, table._rowCount, Column.Schema.float)),
|
||||
rmsf: CifField.ofColumn(Column.ofConst(0, table._rowCount, Column.Schema.float)),
|
||||
model_id: CifField.ofColumn(Column.ofConst(1, table._rowCount, Column.Schema.int)),
|
||||
}
|
||||
|
||||
return {
|
||||
entity: entityBuilder.getEntityCategory(),
|
||||
ihm_model_list: CifCategory.ofFields('ihm_model_list', {
|
||||
model_id: CifField.ofNumbers([1]),
|
||||
model_name: CifField.ofStrings(['3DG Model']),
|
||||
}),
|
||||
ihm_sphere_obj_site: CifCategory.ofFields('ihm_sphere_obj_site', ihm_sphere_obj_site)
|
||||
}
|
||||
}
|
||||
|
||||
async function mmCifFrom3dg(file3dg: File3DG) {
|
||||
const categories = getCategories(file3dg.table)
|
||||
|
||||
return {
|
||||
header: '3DG',
|
||||
categoryNames: Object.keys(categories),
|
||||
categories
|
||||
};
|
||||
}
|
||||
|
||||
export function trajectoryFrom3DG(file3dg: File3DG): Task<Model.Trajectory> {
|
||||
return Task.create('Parse 3DG', async ctx => {
|
||||
await ctx.update('Converting to mmCIF');
|
||||
const cif = await mmCifFrom3dg(file3dg);
|
||||
const format = ModelFormat.mmCIF(cif);
|
||||
return _parse_mmCif(format, ctx);
|
||||
})
|
||||
}
|
||||
89
src/mol-model-formats/structure/mmcif/anisotropic.ts
Normal file
89
src/mol-model-formats/structure/mmcif/anisotropic.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Table } from '../../../mol-data/db';
|
||||
import { Model, CustomPropertyDescriptor } from '../../../mol-model/structure';
|
||||
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { CifWriter } from '../../../mol-io/writer/cif';
|
||||
|
||||
export interface AtomSiteAnisotrop {
|
||||
data: Table<AtomSiteAnisotrop.Schema['atom_site_anisotrop']>
|
||||
/** maps atom_site-index to atom_site_anisotrop-index */
|
||||
elementToAnsiotrop: Int32Array
|
||||
}
|
||||
|
||||
export namespace AtomSiteAnisotrop {
|
||||
export function getAtomSiteAnisotrop(model: Model) {
|
||||
if (model.sourceData.kind !== 'mmCIF') return void 0;
|
||||
const { atom_site_anisotrop } = model.sourceData.data
|
||||
return Table.ofColumns(Schema.atom_site_anisotrop, atom_site_anisotrop);
|
||||
}
|
||||
|
||||
export const PropName = '__AtomSiteAnisotrop__';
|
||||
export function get(model: Model): AtomSiteAnisotrop | undefined {
|
||||
if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]
|
||||
if (!model.customProperties.has(Descriptor)) return void 0;
|
||||
|
||||
const data = getAtomSiteAnisotrop(model);
|
||||
if (!data) return void 0;
|
||||
|
||||
const prop = { data, elementToAnsiotrop: getElementToAnsiotrop(model, data) }
|
||||
set(model, prop)
|
||||
|
||||
return prop;
|
||||
}
|
||||
function set(model: Model, prop: AtomSiteAnisotrop) {
|
||||
(model._staticPropertyData[PropName] as AtomSiteAnisotrop) = prop;
|
||||
}
|
||||
|
||||
export const Schema = { atom_site_anisotrop: mmCIF_Schema['atom_site_anisotrop'] };
|
||||
export type Schema = typeof Schema
|
||||
|
||||
export const Descriptor: CustomPropertyDescriptor = {
|
||||
isStatic: true,
|
||||
name: 'atom_site_anisotrop',
|
||||
cifExport: {
|
||||
prefix: '',
|
||||
categories: [{
|
||||
name: 'atom_site_anisotrop',
|
||||
instance(ctx) {
|
||||
const atom_site_anisotrop = getAtomSiteAnisotrop(ctx.firstModel);
|
||||
if (!atom_site_anisotrop) return CifWriter.Category.Empty;
|
||||
return CifWriter.Category.ofTable(atom_site_anisotrop);
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
function getElementToAnsiotrop(model: Model, data: Table<Schema['atom_site_anisotrop']>) {
|
||||
const { atomId } = model.atomicConformation
|
||||
const atomIdToElement = new Int32Array(atomId.rowCount)
|
||||
atomIdToElement.fill(-1)
|
||||
for (let i = 0, il = atomId.rowCount; i < il; i++) {
|
||||
atomIdToElement[atomId.value(i)] = i
|
||||
}
|
||||
|
||||
const { id } = data
|
||||
const elementToAnsiotrop = new Int32Array(atomId.rowCount)
|
||||
elementToAnsiotrop.fill(-1)
|
||||
for (let i = 0, il = id.rowCount; i < il; ++i) {
|
||||
const ei = atomIdToElement[id.value(i)]
|
||||
if (ei !== -1) elementToAnsiotrop[ei] = i
|
||||
}
|
||||
|
||||
return elementToAnsiotrop
|
||||
}
|
||||
|
||||
export async function attachFromMmCif(model: Model) {
|
||||
if (model.customProperties.has(Descriptor)) return true;
|
||||
if (model.sourceData.kind !== 'mmCIF') return false;
|
||||
const atomSiteAnisotrop = getAtomSiteAnisotrop(model);
|
||||
if (!atomSiteAnisotrop || atomSiteAnisotrop._rowCount === 0) return false;
|
||||
|
||||
model.customProperties.add(Descriptor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Mat4, Tensor } from '../../../mol-math/linear-algebra'
|
||||
import { Mat4, Tensor, Vec3 } from '../../../mol-math/linear-algebra'
|
||||
import { SymmetryOperator } from '../../../mol-math/geometry/symmetry-operator'
|
||||
import { Assembly, OperatorGroup, OperatorGroups } from '../../../mol-model/structure/model/properties/symmetry'
|
||||
import { Queries as Q } from '../../../mol-model/structure'
|
||||
@@ -76,8 +76,8 @@ function getMatrices({ data }: mmCIF_Format): Matrices {
|
||||
const matrices = new Map<string, Mat4>();
|
||||
|
||||
for (let i = 0, _i = pdbx_struct_oper_list._rowCount; i < _i; i++) {
|
||||
const m = Tensor.toMat4(_schema.matrix.space, matrix.value(i));
|
||||
const t = Tensor.toVec3(_schema.vector.space, vector.value(i));
|
||||
const m = Tensor.toMat4(Mat4(), _schema.matrix.space, matrix.value(i));
|
||||
const t = Tensor.toVec3(Vec3(), _schema.vector.space, vector.value(i));
|
||||
Mat4.setTranslation(m, t);
|
||||
Mat4.setValue(m, 3, 3, 1);
|
||||
matrices.set(id.value(i), m);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column, Table } from '../../../mol-data/db';
|
||||
@@ -26,15 +27,15 @@ function findHierarchyOffsets(atom_site: AtomSite) {
|
||||
const start = 0, end = atom_site._rowCount;
|
||||
const residues = [start as ElementIndex], chains = [start as ElementIndex];
|
||||
|
||||
const { label_entity_id, label_asym_id, label_seq_id, auth_seq_id, pdbx_PDB_ins_code, label_comp_id } = atom_site;
|
||||
const { label_entity_id, label_asym_id, label_seq_id, auth_seq_id, pdbx_PDB_ins_code } = atom_site;
|
||||
|
||||
for (let i = start + 1 as ElementIndex; i < end; i++) {
|
||||
const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !label_asym_id.areValuesEqual(i - 1, i);
|
||||
const newResidue = newChain
|
||||
|| !label_seq_id.areValuesEqual(i - 1, i)
|
||||
|| !auth_seq_id.areValuesEqual(i - 1, i)
|
||||
|| !pdbx_PDB_ins_code.areValuesEqual(i - 1, i)
|
||||
|| !label_comp_id.areValuesEqual(i - 1, i);
|
||||
|| !pdbx_PDB_ins_code.areValuesEqual(i - 1, i);
|
||||
// not checking label_comp_id to allow for MICROHETEROGENEITY
|
||||
|
||||
if (newResidue) residues[residues.length] = i as ElementIndex;
|
||||
if (newChain) chains[chains.length] = i as ElementIndex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -8,10 +8,9 @@
|
||||
import { Model } from '../../../../mol-model/structure/model/model'
|
||||
import { LinkType } from '../../../../mol-model/structure/model/types'
|
||||
import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
|
||||
import { mmCIF_Database } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Structure, Unit, StructureProperties, StructureElement } from '../../../../mol-model/structure';
|
||||
import { Segmentation } from '../../../../mol-data/int';
|
||||
import { mmCIF_Database, mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { CifWriter } from '../../../../mol-io/writer/cif'
|
||||
import { Table } from '../../../../mol-data/db';
|
||||
|
||||
export interface ComponentBond {
|
||||
entries: Map<string, ComponentBond.Entry>
|
||||
@@ -26,10 +25,10 @@ export namespace ComponentBond {
|
||||
categories: [{
|
||||
name: 'chem_comp_bond',
|
||||
instance(ctx) {
|
||||
const chem_comp_bond = getChemCompBond(ctx.structures[0].model);
|
||||
const chem_comp_bond = getChemCompBond(ctx.firstModel);
|
||||
if (!chem_comp_bond) return CifWriter.Category.Empty;
|
||||
|
||||
const comp_names = getUniqueResidueNames(ctx.structures[0]);
|
||||
const comp_names = ctx.structures[0].uniqueResidueNames;
|
||||
const { comp_id, _rowCount } = chem_comp_bond;
|
||||
const indices: number[] = [];
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
@@ -53,14 +52,23 @@ export namespace ComponentBond {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function attachFromExternalData(model: Model, bonds: ComponentBond, force = false) {
|
||||
export function attachFromExternalData(model: Model, table: mmCIF_Database['chem_comp_bond'], force = false) {
|
||||
if (!force && model.customProperties.has(Descriptor)) return true;
|
||||
if (model._staticPropertyData.__ComponentBondData__) delete model._staticPropertyData.__ComponentBondData__;
|
||||
const chem_comp_bond = chemCompBondFromTable(model, table);
|
||||
if (chem_comp_bond._rowCount === 0) return false;
|
||||
|
||||
model.customProperties.add(Descriptor);
|
||||
model._staticPropertyData[PropName] = bonds;
|
||||
model._staticPropertyData.__ComponentBondData__ = chem_comp_bond;
|
||||
return true;
|
||||
}
|
||||
|
||||
function chemCompBondFromTable(model: Model, table: mmCIF_Database['chem_comp_bond']): mmCIF_Database['chem_comp_bond'] {
|
||||
return Table.pick(table, mmCIF_Schema.chem_comp_bond, (i: number) => {
|
||||
return model.properties.chemicalComponentMap.has(table.comp_id.value(i))
|
||||
})
|
||||
}
|
||||
|
||||
export class ComponentBondImpl implements ComponentBond {
|
||||
entries: Map<string, ComponentBond.Entry> = new Map();
|
||||
|
||||
@@ -144,21 +152,4 @@ export namespace ComponentBond {
|
||||
model._staticPropertyData[PropName] = chemComp;
|
||||
return chemComp;
|
||||
}
|
||||
|
||||
function getUniqueResidueNames(s: Structure) {
|
||||
const prop = StructureProperties.residue.label_comp_id;
|
||||
const names = new Set<string>();
|
||||
const loc = StructureElement.Location.create();
|
||||
for (const unit of s.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
|
||||
loc.unit = unit;
|
||||
while (residues.hasNext) {
|
||||
const seg = residues.move();
|
||||
loc.element = unit.elements[seg.start];
|
||||
names.add(prop(loc));
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ function getGaussianConformation(data: mmCIF['ihm_gaussian_obj_site']): CoarseGa
|
||||
const { covariance_matrix: cm } = data;
|
||||
|
||||
for (let i = 0, _i = cm.rowCount; i < _i; i++) {
|
||||
covariance_matrix[i] = Tensor.toMat3(matrix_space, cm.value(i));
|
||||
covariance_matrix[i] = Tensor.toMat3(Mat3(), matrix_space, cm.value(i));
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Column, Table } from '../../../mol-data/db';
|
||||
import { mmCIF_Database, mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/geometry';
|
||||
import { Tensor, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Tensor, Vec3, Mat3 } from '../../../mol-math/linear-algebra';
|
||||
import { RuntimeContext } from '../../../mol-task';
|
||||
import UUID from '../../../mol-util/uuid';
|
||||
import { Model } from '../../../mol-model/structure/model/model';
|
||||
@@ -23,12 +23,13 @@ import { getSecondaryStructure } from './secondary-structure';
|
||||
import { getSequence } from './sequence';
|
||||
import { sortAtomSite } from './sort';
|
||||
import { StructConn } from './bonds/struct_conn';
|
||||
import { getMoleculeType, MoleculeType, getEntityType, getEntitySubtype } from '../../../mol-model/structure/model/types';
|
||||
import { getMoleculeType, MoleculeType, getEntityType, getEntitySubtype, getDefaultChemicalComponent } from '../../../mol-model/structure/model/types';
|
||||
import { ModelFormat } from '../format';
|
||||
import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
|
||||
import mmCIF_Format = ModelFormat.mmCIF
|
||||
import { memoize1 } from '../../../mol-util/memoize';
|
||||
import { ElementIndex, EntityIndex } from '../../../mol-model/structure/model';
|
||||
import { AtomSiteAnisotrop } from './anisotropic';
|
||||
|
||||
export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) {
|
||||
const formatData = getFormatData(format)
|
||||
@@ -72,9 +73,11 @@ function getNcsOperators(format: mmCIF_Format) {
|
||||
|
||||
const opers: SymmetryOperator[] = [];
|
||||
for (let i = 0; i < struct_ncs_oper._rowCount; i++) {
|
||||
const m = Tensor.toMat3(matrixSpace, matrix.value(i));
|
||||
const v = Tensor.toVec3(vectorSpace, vector.value(i));
|
||||
const m = Tensor.toMat3(Mat3(), matrixSpace, matrix.value(i));
|
||||
const v = Tensor.toVec3(Vec3(), vectorSpace, vector.value(i));
|
||||
if (!SymmetryOperator.checkIfRotationAndTranslation(m, v)) continue;
|
||||
// ignore non-identity 'given' NCS operators
|
||||
if (struct_ncs_oper.code.value(i) === 'given' && !Mat3.isIdentity(m) && !Vec3.isZero(v)) continue;
|
||||
const ncsId = id.value(i)
|
||||
opers[opers.length] = SymmetryOperator.ofRotationAndOffset(`ncs_${ncsId}`, m, v, ncsId);
|
||||
}
|
||||
@@ -124,19 +127,30 @@ function getMissingResidues(format: mmCIF_Format): Model['properties']['missingR
|
||||
function getChemicalComponentMap(format: mmCIF_Format): Model['properties']['chemicalComponentMap'] {
|
||||
const map = new Map<string, ChemicalComponent>();
|
||||
const { chem_comp } = format.data
|
||||
const { id } = chem_comp
|
||||
for (let i = 0, il = id.rowCount; i < il; ++i) {
|
||||
map.set(id.value(i), Table.getRow(chem_comp, i))
|
||||
|
||||
if (chem_comp._rowCount > 0) {
|
||||
const { id } = chem_comp
|
||||
for (let i = 0, il = id.rowCount; i < il; ++i) {
|
||||
map.set(id.value(i), Table.getRow(chem_comp, i))
|
||||
}
|
||||
} else {
|
||||
const uniqueNames = getUniqueComponentNames(format);
|
||||
uniqueNames.forEach(n => {
|
||||
map.set(n, getDefaultChemicalComponent(n));
|
||||
});
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
function getSaccharideComponentMap(format: mmCIF_Format): SaccharideComponentMap {
|
||||
const map = new Map<string, SaccharideComponent>();
|
||||
const { pdbx_chem_comp_identifier } = format.data
|
||||
if (pdbx_chem_comp_identifier._rowCount > 0) {
|
||||
const { comp_id, type, identifier } = pdbx_chem_comp_identifier
|
||||
for (let i = 0, il = pdbx_chem_comp_identifier._rowCount; i < il; ++i) {
|
||||
|
||||
if (format.data.pdbx_chem_comp_identifier._rowCount > 0) {
|
||||
// note that `pdbx_chem_comp_identifier` does not contain
|
||||
// a 'SNFG CARB SYMBOL' entry for 'Unknown' saccharide components
|
||||
// so we always need to check `chem_comp` for those
|
||||
const { comp_id, type, identifier } = format.data.pdbx_chem_comp_identifier
|
||||
for (let i = 0, il = comp_id.rowCount; i < il; ++i) {
|
||||
if (type.value(i) === 'SNFG CARB SYMBOL') {
|
||||
const snfgName = identifier.value(i)
|
||||
const saccharideComp = SaccharidesSnfgMap.get(snfgName)
|
||||
@@ -147,21 +161,24 @@ function getSaccharideComponentMap(format: mmCIF_Format): SaccharideComponentMap
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (format.data.chem_comp._rowCount > 0) {
|
||||
}
|
||||
|
||||
if (format.data.chem_comp._rowCount > 0) {
|
||||
const { id, type } = format.data.chem_comp
|
||||
for (let i = 0, il = id.rowCount; i < il; ++i) {
|
||||
const _id = id.value(i)
|
||||
if (map.has(_id)) continue
|
||||
const _type = type.value(i)
|
||||
if (SaccharideCompIdMap.has(_id)) {
|
||||
map.set(_id, SaccharideCompIdMap.get(_id)!)
|
||||
} else if (!map.has(_id) && getMoleculeType(_type, _id) === MoleculeType.saccharide) {
|
||||
} else if (getMoleculeType(_type, _id) === MoleculeType.saccharide) {
|
||||
map.set(_id, UnknownSaccharideComponent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const uniqueNames = getUniqueComponentNames(format)
|
||||
SaccharideCompIdMap.forEach((v, k) => {
|
||||
if (uniqueNames.has(k)) map.set(k, v)
|
||||
if (!map.has(k) && uniqueNames.has(k)) map.set(k, v)
|
||||
})
|
||||
}
|
||||
return map
|
||||
@@ -217,13 +234,14 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIn
|
||||
|
||||
return {
|
||||
id: UUID.create22(),
|
||||
entryId: entry,
|
||||
label: label.join(' | '),
|
||||
entry,
|
||||
sourceData: format,
|
||||
modelNum,
|
||||
entities,
|
||||
symmetry: getSymmetry(format),
|
||||
sequence: getSequence(format.data, entities, atomic.hierarchy, formatData.modifiedResidues.parentId),
|
||||
sequence: getSequence(format.data, entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId),
|
||||
atomicHierarchy: atomic.hierarchy,
|
||||
atomicConformation: atomic.conformation,
|
||||
coarseHierarchy: coarse.hierarchy,
|
||||
@@ -253,13 +271,14 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD
|
||||
|
||||
return {
|
||||
id: UUID.create22(),
|
||||
entryId: entry,
|
||||
label: label.join(' | '),
|
||||
entry,
|
||||
sourceData: format,
|
||||
modelNum: data.model_id,
|
||||
entities: data.entities,
|
||||
symmetry: getSymmetry(format),
|
||||
sequence: getSequence(format.data, data.entities, atomic.hierarchy, formatData.modifiedResidues.parentId),
|
||||
sequence: getSequence(format.data, data.entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId),
|
||||
atomicHierarchy: atomic.hierarchy,
|
||||
atomicConformation: atomic.conformation,
|
||||
coarseHierarchy: coarse.hierarchy,
|
||||
@@ -277,6 +296,7 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD
|
||||
function attachProps(model: Model) {
|
||||
ComponentBond.attachFromMmCif(model);
|
||||
StructConn.attachFromMmCif(model);
|
||||
AtomSiteAnisotrop.attachFromMmCif(model);
|
||||
}
|
||||
|
||||
function findModelEnd(num: Column<number>, startIndex: number) {
|
||||
@@ -368,11 +388,19 @@ function getEntities(format: mmCIF_Format): Entities {
|
||||
}
|
||||
|
||||
if (assignSubtype) {
|
||||
const chemCompType = new Map<string, string>()
|
||||
const { id, type } = format.data.chem_comp;
|
||||
for (let i = 0, il = format.data.chem_comp._rowCount; i < il; i++) {
|
||||
chemCompType.set(id.value(i), type.value(i))
|
||||
}
|
||||
|
||||
const { label_entity_id, label_comp_id } = format.data.atom_site;
|
||||
for (let i = 0 as ElementIndex, il = format.data.atom_site._rowCount; i < il; i++) {
|
||||
const entityId = label_entity_id.value(i);
|
||||
if (!entityIds.has(entityId)) {
|
||||
subtypes[getEntityIndex(entityId)] = getEntitySubtype(label_comp_id.value(i))
|
||||
const compId = label_comp_id.value(i)
|
||||
const compType = chemCompType.get(compId) || ''
|
||||
subtypes[getEntityIndex(entityId)] = getEntitySubtype(compId, compType)
|
||||
entityIds.add(entityId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { mmCIF_Database as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif'
|
||||
@@ -10,19 +11,12 @@ import { Column } from '../../../mol-data/db';
|
||||
import { AtomicHierarchy } from '../../../mol-model/structure/model/properties/atomic';
|
||||
import { Entities } from '../../../mol-model/structure/model/properties/common';
|
||||
import { Sequence } from '../../../mol-model/sequence';
|
||||
import { CoarseHierarchy } from '../../../mol-model/structure/model/properties/coarse';
|
||||
|
||||
// TODO how to handle microheterogeneity
|
||||
// see http://mmcif.wwpdb.org/dictionaries/mmcif_pdbx_v50.dic/Categories/entity_poly_seq.html
|
||||
//
|
||||
// Data items in the ENTITY_POLY_SEQ category specify the sequence
|
||||
// of monomers in a polymer. Allowance is made for the possibility
|
||||
// of microheterogeneity in a sample by allowing a given sequence
|
||||
// number to be correlated with more than one monomer ID. The
|
||||
// corresponding ATOM_SITE entries should reflect this
|
||||
// heterogeneity.
|
||||
|
||||
export function getSequence(cif: mmCIF, entities: Entities, hierarchy: AtomicHierarchy, modResMap: ReadonlyMap<string, string>): StructureSequence {
|
||||
if (!cif.entity_poly_seq._rowCount) return StructureSequence.fromAtomicHierarchy(entities, hierarchy, modResMap);
|
||||
export function getSequence(cif: mmCIF, entities: Entities, atomicHierarchy: AtomicHierarchy, coarseHierarchy: CoarseHierarchy, modResMap: ReadonlyMap<string, string>): StructureSequence {
|
||||
if (!cif.entity_poly_seq._rowCount) {
|
||||
return StructureSequence.fromHierarchy(entities, atomicHierarchy, coarseHierarchy, modResMap);
|
||||
}
|
||||
|
||||
const { entity_id, num, mon_id } = cif.entity_poly_seq;
|
||||
|
||||
@@ -37,15 +31,13 @@ export function getSequence(cif: mmCIF, entities: Entities, hierarchy: AtomicHie
|
||||
i++;
|
||||
|
||||
const id = entity_id.value(start);
|
||||
const _compId = Column.window(mon_id, start, i);
|
||||
const _num = Column.window(num, start, i);
|
||||
const compId = Column.window(mon_id, start, i);
|
||||
const seqId = Column.window(num, start, i);
|
||||
const entityKey = entities.getEntityIndex(id);
|
||||
|
||||
byEntityKey[entityKey] = {
|
||||
entityId: id,
|
||||
compId: _compId,
|
||||
num: _num,
|
||||
sequence: Sequence.ofResidueNames(_compId, _num, modResMap)
|
||||
sequence: Sequence.ofResidueNames(compId, seqId, modResMap)
|
||||
};
|
||||
|
||||
sequences.push(byEntityKey[entityKey]);
|
||||
|
||||
146
src/mol-model-formats/structure/pdb/anisotropic.ts
Normal file
146
src/mol-model-formats/structure/pdb/anisotropic.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CifField } from '../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { TokenBuilder, Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
|
||||
import { guessElementSymbolTokens } from '../util';
|
||||
import { parseIntSkipLeadingWhitespace as fastParseInt } from '../../../mol-io/reader/common/text/number-parser';
|
||||
|
||||
type AnisotropicTemplate = typeof getAnisotropicTemplate extends (...args: any) => infer T ? T : never
|
||||
export function getAnisotropicTemplate(data: string, count: number) {
|
||||
const str = () => [] as string[];
|
||||
const float = () => new Float32Array(count);
|
||||
const ts = () => TokenBuilder.create(data, 2 * count);
|
||||
return {
|
||||
index: 0,
|
||||
count,
|
||||
id: str(),
|
||||
type_symbol: ts(),
|
||||
pdbx_label_atom_id: ts(),
|
||||
pdbx_label_alt_id: ts(),
|
||||
pdbx_label_comp_id: ts(),
|
||||
pdbx_label_asym_id: ts(),
|
||||
pdbx_label_seq_id: ts(),
|
||||
pdbx_PDB_ins_code: ts(),
|
||||
'U[1][1]': float(),
|
||||
'U[2][2]': float(),
|
||||
'U[3][3]': float(),
|
||||
'U[1][2]': float(),
|
||||
'U[1][3]': float(),
|
||||
'U[2][3]': float(),
|
||||
pdbx_auth_seq_id: ts(),
|
||||
pdbx_auth_comp_id: ts(),
|
||||
pdbx_auth_asym_id: ts(),
|
||||
pdbx_auth_atom_id: ts(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getAnisotropic(sites: AnisotropicTemplate): { [K in keyof mmCIF_Schema['atom_site_anisotrop']]?: CifField } {
|
||||
const pdbx_auth_seq_id = CifField.ofTokens(sites.pdbx_auth_seq_id);
|
||||
const pdbx_auth_comp_id = CifField.ofTokens(sites.pdbx_auth_comp_id);
|
||||
const pdbx_auth_asym_id = CifField.ofTokens(sites.pdbx_auth_asym_id);
|
||||
const pdbx_auth_atom_id = CifField.ofTokens(sites.pdbx_auth_atom_id);
|
||||
|
||||
const fields: { [K in keyof mmCIF_Schema['atom_site_anisotrop']]?: CifField } = {
|
||||
id: CifField.ofStrings(sites.id),
|
||||
type_symbol: CifField.ofTokens(sites.type_symbol),
|
||||
pdbx_label_atom_id: pdbx_auth_atom_id,
|
||||
pdbx_label_alt_id: CifField.ofTokens(sites.pdbx_label_alt_id),
|
||||
pdbx_label_comp_id: pdbx_auth_comp_id,
|
||||
pdbx_label_asym_id: pdbx_auth_asym_id,
|
||||
pdbx_label_seq_id: pdbx_auth_seq_id,
|
||||
pdbx_PDB_ins_code: CifField.ofTokens(sites.pdbx_PDB_ins_code),
|
||||
|
||||
pdbx_auth_seq_id,
|
||||
pdbx_auth_comp_id,
|
||||
pdbx_auth_asym_id,
|
||||
pdbx_auth_atom_id,
|
||||
};
|
||||
|
||||
(fields as any)['U[1][1]'] = CifField.ofNumbers(sites['U[1][1]']);
|
||||
(fields as any)['U[2][2]'] = CifField.ofNumbers(sites['U[2][2]']);
|
||||
(fields as any)['U[3][3]'] = CifField.ofNumbers(sites['U[3][3]']);
|
||||
(fields as any)['U[1][2]'] = CifField.ofNumbers(sites['U[1][2]']);
|
||||
(fields as any)['U[1][3]'] = CifField.ofNumbers(sites['U[1][3]']);
|
||||
(fields as any)['U[2][3]'] = CifField.ofNumbers(sites['U[2][3]']);
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
export function addAnisotropic(sites: AnisotropicTemplate, model: string, data: Tokenizer, s: number, e: number) {
|
||||
const { data: str } = data;
|
||||
const length = e - s;
|
||||
|
||||
// COLUMNS DATA TYPE FIELD DEFINITION
|
||||
// -----------------------------------------------------------------
|
||||
// 1 - 6 Record name "ANISOU"
|
||||
// 7 - 11 Integer serial Atom serial number.
|
||||
Tokenizer.trim(data, s + 6, s + 11);
|
||||
sites.id[sites.index] = str.substring(data.tokenStart, data.tokenEnd);
|
||||
|
||||
// 13 - 16 Atom name Atom name.
|
||||
TokenBuilder.addToken(sites.pdbx_auth_atom_id, Tokenizer.trim(data, s + 12, s + 16));
|
||||
|
||||
// 17 Character altLoc Alternate location indicator
|
||||
if (str.charCodeAt(s + 16) === 32) { // ' '
|
||||
TokenBuilder.add(sites.pdbx_label_alt_id, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.pdbx_label_alt_id, s + 16, s + 17);
|
||||
}
|
||||
|
||||
// 18 - 20 Residue name resName Residue name.
|
||||
TokenBuilder.addToken(sites.pdbx_auth_comp_id, Tokenizer.trim(data, s + 17, s + 20));
|
||||
|
||||
// 22 Character chainID Chain identifier.
|
||||
TokenBuilder.add(sites.pdbx_auth_asym_id, s + 21, s + 22);
|
||||
|
||||
// 23 - 26 Integer resSeq Residue sequence number.
|
||||
TokenBuilder.addToken(sites.pdbx_auth_seq_id, Tokenizer.trim(data, s + 22, s + 26));
|
||||
|
||||
// 27 AChar iCode Insertion code.
|
||||
if (str.charCodeAt(s + 26) === 32) { // ' '
|
||||
TokenBuilder.add(sites.pdbx_PDB_ins_code, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.pdbx_PDB_ins_code, s + 26, s + 27);
|
||||
}
|
||||
|
||||
// 29 - 35 Integer u[0][0] U(1,1)
|
||||
sites['U[1][1]'][sites.index] = fastParseInt(str, s + 28, s + 35) / 10000
|
||||
|
||||
// 36 - 42 Integer u[1][1] U(2,2)
|
||||
sites['U[2][2]'][sites.index] = fastParseInt(str, s + 35, s + 42) / 10000
|
||||
|
||||
// 43 - 49 Integer u[2][2] U(3,3)
|
||||
sites['U[3][3]'][sites.index] = fastParseInt(str, s + 42, s + 49) / 10000
|
||||
|
||||
// 50 - 56 Integer u[0][1] U(1,2)
|
||||
sites['U[1][2]'][sites.index] = fastParseInt(str, s + 49, s + 56) / 10000
|
||||
|
||||
// 57 - 63 Integer u[0][2] U(1,3)
|
||||
sites['U[1][3]'][sites.index] = fastParseInt(str, s + 56, s + 63) / 10000
|
||||
|
||||
// 64 - 70 Integer u[1][2] U(2,3)
|
||||
sites['U[2][3]'][sites.index] = fastParseInt(str, s + 63, s + 70) / 10000
|
||||
|
||||
// 77 - 78 LString(2) element Element symbol, right-justified.
|
||||
if (length >= 78) {
|
||||
Tokenizer.trim(data, s + 76, s + 78);
|
||||
|
||||
if (data.tokenStart < data.tokenEnd) {
|
||||
TokenBuilder.addToken(sites.type_symbol, data);
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
|
||||
// 79 - 80 LString(2) charge Charge on the atom.
|
||||
// TODO
|
||||
|
||||
sites.index++;
|
||||
}
|
||||
157
src/mol-model-formats/structure/pdb/atom-site.ts
Normal file
157
src/mol-model-formats/structure/pdb/atom-site.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CifField } from '../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { TokenBuilder, Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
|
||||
import { guessElementSymbolTokens } from '../util';
|
||||
|
||||
type AtomSiteTemplate = typeof getAtomSiteTemplate extends (...args: any) => infer T ? T : never
|
||||
export function getAtomSiteTemplate(data: string, count: number) {
|
||||
const str = () => [] as string[];
|
||||
const ts = () => TokenBuilder.create(data, 2 * count);
|
||||
return {
|
||||
index: 0,
|
||||
count,
|
||||
group_PDB: ts(),
|
||||
id: str(),
|
||||
auth_atom_id: ts(),
|
||||
label_alt_id: ts(),
|
||||
auth_comp_id: ts(),
|
||||
auth_asym_id: ts(),
|
||||
auth_seq_id: ts(),
|
||||
pdbx_PDB_ins_code: ts(),
|
||||
Cartn_x: ts(),
|
||||
Cartn_y: ts(),
|
||||
Cartn_z: ts(),
|
||||
occupancy: ts(),
|
||||
B_iso_or_equiv: ts(),
|
||||
type_symbol: ts(),
|
||||
pdbx_PDB_model_num: str(),
|
||||
label_entity_id: str()
|
||||
};
|
||||
}
|
||||
|
||||
export function getAtomSite(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema['atom_site']]?: CifField } {
|
||||
const auth_asym_id = CifField.ofTokens(sites.auth_asym_id);
|
||||
const auth_atom_id = CifField.ofTokens(sites.auth_atom_id);
|
||||
const auth_comp_id = CifField.ofTokens(sites.auth_comp_id);
|
||||
const auth_seq_id = CifField.ofTokens(sites.auth_seq_id);
|
||||
|
||||
return {
|
||||
auth_asym_id,
|
||||
auth_atom_id,
|
||||
auth_comp_id,
|
||||
auth_seq_id,
|
||||
B_iso_or_equiv: CifField.ofTokens(sites.B_iso_or_equiv),
|
||||
Cartn_x: CifField.ofTokens(sites.Cartn_x),
|
||||
Cartn_y: CifField.ofTokens(sites.Cartn_y),
|
||||
Cartn_z: CifField.ofTokens(sites.Cartn_z),
|
||||
group_PDB: CifField.ofTokens(sites.group_PDB),
|
||||
id: CifField.ofStrings(sites.id),
|
||||
|
||||
label_alt_id: CifField.ofTokens(sites.label_alt_id),
|
||||
|
||||
label_asym_id: auth_asym_id,
|
||||
label_atom_id: auth_atom_id,
|
||||
label_comp_id: auth_comp_id,
|
||||
label_seq_id: auth_seq_id,
|
||||
label_entity_id: CifField.ofStrings(sites.label_entity_id),
|
||||
|
||||
occupancy: CifField.ofTokens(sites.occupancy),
|
||||
type_symbol: CifField.ofTokens(sites.type_symbol),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofTokens(sites.pdbx_PDB_ins_code),
|
||||
pdbx_PDB_model_num: CifField.ofStrings(sites.pdbx_PDB_model_num)
|
||||
};
|
||||
}
|
||||
|
||||
export function addAtom(sites: AtomSiteTemplate, model: string, data: Tokenizer, s: number, e: number) {
|
||||
const { data: str } = data;
|
||||
const length = e - s;
|
||||
|
||||
// TODO: filter invalid atoms
|
||||
|
||||
// COLUMNS DATA TYPE CONTENTS
|
||||
// --------------------------------------------------------------------------------
|
||||
// 1 - 6 Record name "ATOM "
|
||||
TokenBuilder.addToken(sites.group_PDB, Tokenizer.trim(data, s, s + 6));
|
||||
|
||||
// 7 - 11 Integer Atom serial number.
|
||||
// TODO: support HEX
|
||||
Tokenizer.trim(data, s + 6, s + 11);
|
||||
sites.id[sites.index] = data.data.substring(data.tokenStart, data.tokenEnd);
|
||||
|
||||
// 13 - 16 Atom Atom name.
|
||||
TokenBuilder.addToken(sites.auth_atom_id, Tokenizer.trim(data, s + 12, s + 16));
|
||||
|
||||
// 17 Character Alternate location indicator.
|
||||
if (str.charCodeAt(s + 16) === 32) { // ' '
|
||||
TokenBuilder.add(sites.label_alt_id, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.label_alt_id, s + 16, s + 17);
|
||||
}
|
||||
|
||||
// 18 - 20 Residue name Residue name.
|
||||
TokenBuilder.addToken(sites.auth_comp_id, Tokenizer.trim(data, s + 17, s + 20));
|
||||
|
||||
// 22 Character Chain identifier.
|
||||
TokenBuilder.add(sites.auth_asym_id, s + 21, s + 22);
|
||||
|
||||
// 23 - 26 Integer Residue sequence number.
|
||||
// TODO: support HEX
|
||||
TokenBuilder.addToken(sites.auth_seq_id, Tokenizer.trim(data, s + 22, s + 26));
|
||||
|
||||
// 27 AChar Code for insertion of residues.
|
||||
if (str.charCodeAt(s + 26) === 32) { // ' '
|
||||
TokenBuilder.add(sites.pdbx_PDB_ins_code, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.pdbx_PDB_ins_code, s + 26, s + 27);
|
||||
}
|
||||
|
||||
// 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_x, Tokenizer.trim(data, s + 30, s + 38));
|
||||
|
||||
// 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_y, Tokenizer.trim(data, s + 38, s + 46));
|
||||
|
||||
// 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_z, Tokenizer.trim(data, s + 46, s + 54));
|
||||
|
||||
// 55 - 60 Real(6.2) Occupancy.
|
||||
TokenBuilder.addToken(sites.occupancy, Tokenizer.trim(data, s + 54, s + 60));
|
||||
|
||||
// 61 - 66 Real(6.2) Temperature factor (Default = 0.0).
|
||||
if (length >= 66) {
|
||||
TokenBuilder.addToken(sites.B_iso_or_equiv, Tokenizer.trim(data, s + 60, s + 66));
|
||||
} else {
|
||||
TokenBuilder.add(sites.B_iso_or_equiv, 0, 0);
|
||||
}
|
||||
|
||||
// 73 - 76 LString(4) Segment identifier, left-justified.
|
||||
// ignored
|
||||
|
||||
// 77 - 78 LString(2) Element symbol, right-justified.
|
||||
if (length >= 78) {
|
||||
Tokenizer.trim(data, s + 76, s + 78);
|
||||
|
||||
if (data.tokenStart < data.tokenEnd) {
|
||||
TokenBuilder.addToken(sites.type_symbol, data);
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
|
||||
// 79 - 80 LString(2) charge Charge on the atom.
|
||||
// TODO
|
||||
|
||||
sites.pdbx_PDB_model_num[sites.index] = model;
|
||||
|
||||
sites.index++;
|
||||
}
|
||||
@@ -6,161 +6,18 @@
|
||||
*/
|
||||
|
||||
import { substringStartsWith } from '../../../mol-util/string';
|
||||
import { CifField, CifCategory, CifFrame } from '../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { TokenBuilder, Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
|
||||
import { CifCategory, CifFrame } from '../../../mol-io/reader/cif';
|
||||
import { Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
|
||||
import { PdbFile } from '../../../mol-io/reader/pdb/schema';
|
||||
import { parseCryst1, parseRemark350, parseMtrix } from './assembly';
|
||||
import { parseHelix, parseSheet } from './secondary-structure';
|
||||
import { guessElementSymbolTokens } from '../util';
|
||||
import { parseCmpnd, parseHetnam } from './entity';
|
||||
import { ComponentBuilder } from '../common/component';
|
||||
import { EntityBuilder } from '../common/entity';
|
||||
import { Column } from '../../../mol-data/db';
|
||||
import { getMoleculeType } from '../../../mol-model/structure/model/types';
|
||||
|
||||
type AtomSiteTemplate = typeof getAtomSiteTemplate extends (...args: any) => infer T ? T : never
|
||||
function getAtomSiteTemplate(data: string, count: number) {
|
||||
const str = () => [] as string[];
|
||||
const ts = () => TokenBuilder.create(data, 2 * count);
|
||||
return {
|
||||
index: 0,
|
||||
count,
|
||||
group_PDB: ts(),
|
||||
id: str(),
|
||||
auth_atom_id: ts(),
|
||||
label_alt_id: ts(),
|
||||
auth_comp_id: ts(),
|
||||
auth_asym_id: ts(),
|
||||
auth_seq_id: ts(),
|
||||
pdbx_PDB_ins_code: ts(),
|
||||
Cartn_x: ts(),
|
||||
Cartn_y: ts(),
|
||||
Cartn_z: ts(),
|
||||
occupancy: ts(),
|
||||
B_iso_or_equiv: ts(),
|
||||
type_symbol: ts(),
|
||||
pdbx_PDB_model_num: str(),
|
||||
label_entity_id: str()
|
||||
};
|
||||
}
|
||||
|
||||
function getAomSite(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema['atom_site']]?: CifField } {
|
||||
const auth_asym_id = CifField.ofTokens(sites.auth_asym_id);
|
||||
const auth_atom_id = CifField.ofTokens(sites.auth_atom_id);
|
||||
const auth_comp_id = CifField.ofTokens(sites.auth_comp_id);
|
||||
const auth_seq_id = CifField.ofTokens(sites.auth_seq_id);
|
||||
|
||||
return {
|
||||
auth_asym_id,
|
||||
auth_atom_id,
|
||||
auth_comp_id,
|
||||
auth_seq_id,
|
||||
B_iso_or_equiv: CifField.ofTokens(sites.B_iso_or_equiv),
|
||||
Cartn_x: CifField.ofTokens(sites.Cartn_x),
|
||||
Cartn_y: CifField.ofTokens(sites.Cartn_y),
|
||||
Cartn_z: CifField.ofTokens(sites.Cartn_z),
|
||||
group_PDB: CifField.ofTokens(sites.group_PDB),
|
||||
id: CifField.ofStrings(sites.id),
|
||||
|
||||
label_alt_id: CifField.ofTokens(sites.label_alt_id),
|
||||
|
||||
label_asym_id: auth_asym_id,
|
||||
label_atom_id: auth_atom_id,
|
||||
label_comp_id: auth_comp_id,
|
||||
label_seq_id: auth_seq_id,
|
||||
label_entity_id: CifField.ofStrings(sites.label_entity_id),
|
||||
|
||||
occupancy: CifField.ofTokens(sites.occupancy),
|
||||
type_symbol: CifField.ofTokens(sites.type_symbol),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofTokens(sites.pdbx_PDB_ins_code),
|
||||
pdbx_PDB_model_num: CifField.ofStrings(sites.pdbx_PDB_model_num)
|
||||
};
|
||||
}
|
||||
|
||||
function addAtom(sites: AtomSiteTemplate, model: string, data: Tokenizer, s: number, e: number) {
|
||||
const { data: str } = data;
|
||||
const length = e - s;
|
||||
|
||||
// TODO: filter invalid atoms
|
||||
|
||||
// COLUMNS DATA TYPE CONTENTS
|
||||
// --------------------------------------------------------------------------------
|
||||
// 1 - 6 Record name "ATOM "
|
||||
TokenBuilder.addToken(sites.group_PDB, Tokenizer.trim(data, s, s + 6));
|
||||
|
||||
// 7 - 11 Integer Atom serial number.
|
||||
// TODO: support HEX
|
||||
Tokenizer.trim(data, s + 6, s + 11);
|
||||
sites.id[sites.index] = data.data.substring(data.tokenStart, data.tokenEnd);
|
||||
|
||||
// 13 - 16 Atom Atom name.
|
||||
TokenBuilder.addToken(sites.auth_atom_id, Tokenizer.trim(data, s + 12, s + 16));
|
||||
|
||||
// 17 Character Alternate location indicator.
|
||||
if (str.charCodeAt(s + 16) === 32) { // ' '
|
||||
TokenBuilder.add(sites.label_alt_id, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.label_alt_id, s + 16, s + 17);
|
||||
}
|
||||
|
||||
// 18 - 20 Residue name Residue name.
|
||||
TokenBuilder.addToken(sites.auth_comp_id, Tokenizer.trim(data, s + 17, s + 20));
|
||||
|
||||
// 22 Character Chain identifier.
|
||||
TokenBuilder.add(sites.auth_asym_id, s + 21, s + 22);
|
||||
|
||||
// 23 - 26 Integer Residue sequence number.
|
||||
// TODO: support HEX
|
||||
TokenBuilder.addToken(sites.auth_seq_id, Tokenizer.trim(data, s + 22, s + 26));
|
||||
|
||||
// 27 AChar Code for insertion of residues.
|
||||
if (str.charCodeAt(s + 26) === 32) { // ' '
|
||||
TokenBuilder.add(sites.label_alt_id, 0, 0);
|
||||
} else {
|
||||
TokenBuilder.add(sites.label_alt_id, s + 26, s + 27);
|
||||
}
|
||||
|
||||
// 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_x, Tokenizer.trim(data, s + 30, s + 38));
|
||||
|
||||
// 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_y, Tokenizer.trim(data, s + 38, s + 46));
|
||||
|
||||
// 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms.
|
||||
TokenBuilder.addToken(sites.Cartn_z, Tokenizer.trim(data, s + 46, s + 54));
|
||||
|
||||
// 55 - 60 Real(6.2) Occupancy.
|
||||
TokenBuilder.addToken(sites.occupancy, Tokenizer.trim(data, s + 54, s + 60));
|
||||
|
||||
// 61 - 66 Real(6.2) Temperature factor (Default = 0.0).
|
||||
if (length >= 66) {
|
||||
TokenBuilder.addToken(sites.B_iso_or_equiv, Tokenizer.trim(data, s + 60, s + 66));
|
||||
} else {
|
||||
TokenBuilder.add(sites.label_alt_id, 0, 0);
|
||||
}
|
||||
|
||||
// 73 - 76 LString(4) Segment identifier, left-justified.
|
||||
// ignored
|
||||
|
||||
// 77 - 78 LString(2) Element symbol, right-justified.
|
||||
if (length >= 78) {
|
||||
Tokenizer.trim(data, s + 76, s + 78);
|
||||
|
||||
if (data.tokenStart < data.tokenEnd) {
|
||||
TokenBuilder.addToken(sites.type_symbol, data);
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
} else {
|
||||
guessElementSymbolTokens(sites.type_symbol, str, s + 12, s + 16)
|
||||
}
|
||||
|
||||
sites.pdbx_PDB_model_num[sites.index] = model;
|
||||
|
||||
sites.index++;
|
||||
}
|
||||
import { getAtomSiteTemplate, addAtom, getAtomSite } from './atom-site';
|
||||
import { addAnisotropic, getAnisotropicTemplate, getAnisotropic } from './anisotropic';
|
||||
|
||||
export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
const { lines } = pdb;
|
||||
@@ -169,11 +26,13 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
|
||||
// Count the atoms
|
||||
let atomCount = 0;
|
||||
let anisotropicCount = 0;
|
||||
for (let i = 0, _i = lines.count; i < _i; i++) {
|
||||
const s = indices[2 * i], e = indices[2 * i + 1];
|
||||
switch (data[s]) {
|
||||
case 'A':
|
||||
if (substringStartsWith(data, s, e, 'ATOM ')) atomCount++;
|
||||
else if (substringStartsWith(data, s, e, 'ANISOU')) anisotropicCount++;
|
||||
break;
|
||||
case 'H':
|
||||
if (substringStartsWith(data, s, e, 'HETATM')) atomCount++;
|
||||
@@ -182,6 +41,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
}
|
||||
|
||||
const atomSite = getAtomSiteTemplate(data, atomCount);
|
||||
const anisotropic = getAnisotropicTemplate(data, anisotropicCount);
|
||||
const entityBuilder = new EntityBuilder();
|
||||
const helperCategories: CifCategory[] = [];
|
||||
const heteroNames: [string, string][] = [];
|
||||
@@ -192,9 +52,12 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
let s = indices[2 * i], e = indices[2 * i + 1];
|
||||
switch (data[s]) {
|
||||
case 'A':
|
||||
if (!substringStartsWith(data, s, e, 'ATOM ')) continue;
|
||||
if (!modelNum) { modelNum++; modelStr = '' + modelNum; }
|
||||
addAtom(atomSite, modelStr, tokenizer, s, e);
|
||||
if (substringStartsWith(data, s, e, 'ATOM ')) {
|
||||
if (!modelNum) { modelNum++; modelStr = '' + modelNum; }
|
||||
addAtom(atomSite, modelStr, tokenizer, s, e);
|
||||
} else if (substringStartsWith(data, s, e, 'ANISOU')) {
|
||||
addAnisotropic(anisotropic, modelStr, tokenizer, s, e)
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
if (substringStartsWith(data, s, e, 'CRYST1')) {
|
||||
@@ -301,7 +164,8 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
const categories = {
|
||||
entity: entityBuilder.getEntityCategory(),
|
||||
chem_comp: componentBuilder.getChemCompCategory(),
|
||||
atom_site: CifCategory.ofFields('atom_site', getAomSite(atomSite))
|
||||
atom_site: CifCategory.ofFields('atom_site', getAtomSite(atomSite)),
|
||||
atom_site_anisotrop: CifCategory.ofFields('atom_site_anisotrop', getAnisotropic(anisotropic))
|
||||
} as any;
|
||||
|
||||
for (const c of helperCategories) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Task } from '../../mol-task';
|
||||
import { ThemeDataContext, ThemeProvider } from '../../mol-theme/theme';
|
||||
import { ColorTheme, LocationColor } from '../../mol-theme/color';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { TableLegend } from '../../mol-util/color/lists';
|
||||
import { TableLegend } from '../../mol-util/legend';
|
||||
import { Loci } from '../../mol-model/loci';
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ export namespace StructureQualityReport {
|
||||
{
|
||||
const url = mapUrl(model);
|
||||
const dataStr = await fetch({ url }).runInContext(ctx) as string;
|
||||
const data = JSON.parse(dataStr)[model.label.toLowerCase()];
|
||||
const data = JSON.parse(dataStr)[model.entryId.toLowerCase()];
|
||||
if (!data) return false;
|
||||
info = PropertyWrapper.createInfo();
|
||||
issueMap = createIssueMapFromJson(model, data);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { StructureElement } from '../../../mol-model/structure';
|
||||
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { TableLegend } from '../../../mol-util/color/lists';
|
||||
import { TableLegend } from '../../../mol-util/legend';
|
||||
|
||||
const ValidationColors = [
|
||||
Color.fromRgb(170, 170, 170), // not applicable
|
||||
|
||||
@@ -264,8 +264,8 @@ export namespace AssemblySymmetry {
|
||||
db = createDatabaseFromCif(model)
|
||||
} else {
|
||||
let result: AssemblySymmetryGraphQL.Query
|
||||
console.log('model.label.toLowerCase()', model.label.toLowerCase())
|
||||
const variables: AssemblySymmetryGraphQL.Variables = { pdbId: model.label.toLowerCase() };
|
||||
console.log('model.entryId.toLowerCase()', model.entryId.toLowerCase())
|
||||
const variables: AssemblySymmetryGraphQL.Variables = { pdbId: model.entryId.toLowerCase() };
|
||||
try {
|
||||
console.log('foo', client)
|
||||
result = await client.request<AssemblySymmetryGraphQL.Query>(ctx || RuntimeContext.Synchronous, query, variables);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { StructureRepresentationProvider, StructureRepresentation, ComplexRepres
|
||||
import { AssemblySymmetry } from '../assembly-symmetry';
|
||||
import { Table } from '../../../mol-data/db';
|
||||
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { Tensor } from '../../../mol-math/linear-algebra';
|
||||
import { Tensor, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere';
|
||||
import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
|
||||
import { VisualUpdateState } from '../../../mol-repr/util';
|
||||
@@ -127,8 +127,8 @@ export function createAssemblySymmetryAxesMesh(ctx: VisualContext, structure: St
|
||||
for (let i = 0, il = axes._rowCount; i < il; ++i) {
|
||||
if (axes.symmetry_id.value(i) !== symmetryId) continue
|
||||
|
||||
const start = Tensor.toVec3(vectorSpace, axes.start.value(i))
|
||||
const end = Tensor.toVec3(vectorSpace, axes.end.value(i))
|
||||
const start = Tensor.toVec3(Vec3(), vectorSpace, axes.start.value(i))
|
||||
const end = Tensor.toVec3(Vec3(), vectorSpace, axes.end.value(i))
|
||||
builderState.currentGroup = i
|
||||
addSphere(builderState, start, radius, 2)
|
||||
addSphere(builderState, end, radius, 2)
|
||||
|
||||
@@ -12,10 +12,9 @@ import { AssemblySymmetry } from '../assembly-symmetry';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Unit, StructureElement, StructureProperties } from '../../../mol-model/structure';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { ScaleLegend } from '../../../mol-util/color/scale';
|
||||
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
|
||||
import { getSymmetrySelectParam } from '../util';
|
||||
import { getPalette, getPaletteParams } from '../../../mol-util/color/palette';
|
||||
import { TableLegend } from '../../../mol-util/color/lists';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC)
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column, Table } from '../../mol-data/db';
|
||||
import { toTable } from '../../mol-io/reader/cif/schema';
|
||||
import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
|
||||
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
|
||||
import { CifWriter } from '../../mol-io/writer/cif';
|
||||
|
||||
export namespace ChemCompBond {
|
||||
export type Property = Table<Schema['chem_comp_bond']>
|
||||
|
||||
export function getFromModel(model: Model): Property {
|
||||
if (model.sourceData.kind !== 'mmCIF') return Table.ofUndefinedColumns(Schema.chem_comp_bond, 0);
|
||||
const { chem_comp_bond } = model.sourceData.data
|
||||
return Table.ofColumns(Schema.chem_comp_bond, {
|
||||
...chem_comp_bond,
|
||||
molstar_protonation_variant: Column.Undefined(chem_comp_bond._rowCount, Column.Schema.Str())
|
||||
});
|
||||
}
|
||||
|
||||
export function get(model: Model): Property {
|
||||
return model._staticPropertyData.__ChemCompBond__ || getFromModel(model);
|
||||
}
|
||||
function set(model: Model, prop: Property) {
|
||||
(model._staticPropertyData.__ChemCompBond__ as Property) = prop;
|
||||
}
|
||||
|
||||
export const Schema = { chem_comp_bond: mmCIF_chemCompBond_schema };
|
||||
export type Schema = typeof Schema
|
||||
|
||||
export const Descriptor = CustomPropertyDescriptor({
|
||||
isStatic: true,
|
||||
name: 'chem_comp_bond',
|
||||
cifExport: {
|
||||
prefix: '',
|
||||
context(ctx): Property { return get(ctx.firstModel); },
|
||||
categories: [{
|
||||
name: 'chem_comp_bond',
|
||||
instance(ctx: Property) {
|
||||
return CifWriter.Category.ofTable(ctx);
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
function fromCifData(model: Model): Table<Schema['chem_comp_bond']> | undefined {
|
||||
if (model.sourceData.kind !== 'mmCIF') return void 0;
|
||||
const cat = model.sourceData.frame.categories.chem_comp_bond;
|
||||
if (!cat) return void 0;
|
||||
return toTable(Schema.chem_comp_bond, cat);
|
||||
}
|
||||
|
||||
export async function attachFromCifOrTable(model: Model, params: {
|
||||
// optional Table source
|
||||
wwPDB_apiSourceTable?: (model: Model) => Promise<Table<Schema['chem_comp_bond']>>
|
||||
}) {
|
||||
if (model.customProperties.has(Descriptor)) return true;
|
||||
|
||||
let chemCompBond: Table<Schema['chem_comp_bond']> | undefined = fromCifData(model);
|
||||
if (chemCompBond === void 0 && params.wwPDB_apiSourceTable) {
|
||||
const data = await params.wwPDB_apiSourceTable(model);
|
||||
if (!data) return false;
|
||||
chemCompBond = chemCompBondFromTable(model, data);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!chemCompBond) return false;
|
||||
|
||||
model.customProperties.add(Descriptor);
|
||||
set(model, chemCompBond);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function chemCompBondFromTable(model: Model, table: Table<ChemCompBond.Schema['chem_comp_bond']>): Table<ChemCompBond.Schema['chem_comp_bond']> {
|
||||
return Table.pick(table, ChemCompBond.Schema.chem_comp_bond, (i: number) => {
|
||||
return model.properties.chemicalComponentMap.has(table.comp_id.value(i))
|
||||
})
|
||||
}
|
||||
@@ -104,7 +104,7 @@ namespace Loci {
|
||||
export function getBoundingSphere(loci: Loci, boundingSphere?: Sphere3D): Sphere3D | undefined {
|
||||
if (loci.kind === 'every-loci' || loci.kind === 'empty-loci') return void 0;
|
||||
|
||||
if (!boundingSphere) boundingSphere = Sphere3D.zero()
|
||||
if (!boundingSphere) boundingSphere = Sphere3D()
|
||||
sphereHelper.reset();
|
||||
|
||||
if (loci.kind === 'structure-loci') {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { AminoAlphabet, NuclecicAlphabet, getProteinOneLetterCode, getRnaOneLetterCode, getDnaOneLetterCode } from './constants';
|
||||
@@ -22,8 +23,17 @@ namespace Sequence {
|
||||
|
||||
export interface Base<K extends Kind, Alphabet extends string> {
|
||||
readonly kind: K,
|
||||
readonly length: number,
|
||||
readonly offset: number,
|
||||
readonly sequence: ArrayLike<Alphabet>
|
||||
|
||||
readonly code: Column<Alphabet>
|
||||
readonly label: Column<string>
|
||||
|
||||
readonly seqId: Column<number>
|
||||
readonly compId: Column<string>
|
||||
|
||||
/** maps seqId to list of compIds */
|
||||
readonly microHet: ReadonlyMap<number, string[]>
|
||||
}
|
||||
|
||||
export interface Protein extends Base<Kind.Protein, AminoAlphabet> { }
|
||||
@@ -31,55 +41,89 @@ namespace Sequence {
|
||||
export interface DNA extends Base<Kind.DNA, NuclecicAlphabet> { }
|
||||
export interface Generic extends Base<Kind.Generic, 'X' | '-'> { }
|
||||
|
||||
export function create(kind: Kind, sequence: string, offset: number = 0): Sequence {
|
||||
return { kind: kind as any, sequence: sequence as any, offset };
|
||||
export function create<K extends Kind, Alphabet extends string>(kind: K, code: Column<Alphabet>, label: Column<string>, seqId: Column<number>, compId: Column<string>, microHet: Map<number, string[]>, offset: number = 0): Base<K, Alphabet> {
|
||||
const length = code.rowCount
|
||||
return { kind, code, label, seqId, compId, microHet, offset, length };
|
||||
}
|
||||
|
||||
export function getSequenceString(seq: Sequence) {
|
||||
return seq.sequence as string;
|
||||
const array = seq.code.toArray()
|
||||
return (array instanceof Array ? array : Array.from(array)).join('')
|
||||
}
|
||||
|
||||
function determineKind(names: Column<string>) {
|
||||
for (let i = 0, _i = Math.min(names.rowCount, 10); i < _i; i++) {
|
||||
const name = names.value(i) || '';
|
||||
if (getProteinOneLetterCode(name) !== 'X') return { kind: Kind.Protein, code: getProteinOneLetterCode };
|
||||
if (getRnaOneLetterCode(name) !== 'X') return { kind: Kind.RNA, code: getRnaOneLetterCode };
|
||||
if (getDnaOneLetterCode(name) !== 'X') return { kind: Kind.DNA, code: getDnaOneLetterCode };
|
||||
if (getProteinOneLetterCode(name) !== 'X') return Kind.Protein;
|
||||
if (getRnaOneLetterCode(name) !== 'X') return Kind.RNA;
|
||||
if (getDnaOneLetterCode(name) !== 'X') return Kind.DNA;
|
||||
}
|
||||
return { kind: Kind.Generic, code: (v: string) => 'X' };
|
||||
return Kind.Generic;
|
||||
}
|
||||
|
||||
function modCode(code: (name: string) => string, map: ReadonlyMap<string, string>): (name: string) => string {
|
||||
return n => {
|
||||
const ret = code(n);
|
||||
if (ret !== 'X' || !map.has(n)) return ret;
|
||||
return code(map.get(n)!);
|
||||
function codeProvider(kind: Kind, map?: ReadonlyMap<string, string>) {
|
||||
let code: (name: string) => string
|
||||
switch (kind) {
|
||||
case Kind.Protein: code = getProteinOneLetterCode; break;
|
||||
case Kind.DNA: code = getDnaOneLetterCode; break;
|
||||
case Kind.RNA: code = getRnaOneLetterCode; break;
|
||||
case Kind.Generic: code = () => 'X'; break;
|
||||
default: throw new Error(`unknown kind '${kind}'`)
|
||||
}
|
||||
if (map && map.size > 0) {
|
||||
return (name: string) => {
|
||||
const ret = code(name);
|
||||
if (ret !== 'X' || !map.has(name)) return ret;
|
||||
return code(map.get(name)!);
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
export function ofResidueNames(residueName: Column<string>, seqId: Column<number>, modifiedMap?: ReadonlyMap<string, string>): Sequence {
|
||||
export function ofResidueNames(compId: Column<string>, seqId: Column<number>, modifiedMap?: ReadonlyMap<string, string>): Sequence {
|
||||
if (seqId.rowCount === 0) throw new Error('cannot be empty');
|
||||
|
||||
const { kind, code } = determineKind(residueName);
|
||||
|
||||
if (!modifiedMap || modifiedMap.size === 0) return new Impl(kind, residueName, seqId, code) as Sequence;
|
||||
return new Impl(kind, residueName, seqId, modCode(code, modifiedMap)) as Sequence;
|
||||
const kind = determineKind(compId);
|
||||
return new ResidueNamesImpl(kind, compId, seqId, modifiedMap) as Sequence;
|
||||
}
|
||||
|
||||
class Impl implements Base<any, any> {
|
||||
class ResidueNamesImpl<K extends Kind, Alphabet extends string> implements Base<K, Alphabet> {
|
||||
private _offset = 0;
|
||||
private _seq: string | undefined = void 0;
|
||||
private _length = 0;
|
||||
private _microHet: ReadonlyMap<number, string[]> | undefined = void 0;
|
||||
private _code: Column<Alphabet> | undefined = undefined
|
||||
private _label: Column<string> | undefined = undefined
|
||||
|
||||
private codeFromName: (name: string) => string
|
||||
|
||||
get code(): Column<Alphabet> {
|
||||
if (this._code !== void 0) return this._code;
|
||||
this.create();
|
||||
return this._code!;
|
||||
}
|
||||
|
||||
get label(): Column<string> {
|
||||
if (this._label !== void 0) return this._label;
|
||||
this.create();
|
||||
return this._label!;
|
||||
}
|
||||
|
||||
get offset() {
|
||||
if (this._seq !== void 0) return this._offset;
|
||||
if (this._code !== void 0) return this._offset;
|
||||
this.create();
|
||||
return this._offset;
|
||||
}
|
||||
|
||||
get sequence(): any {
|
||||
if (this._seq !== void 0) return this._seq;
|
||||
get length() {
|
||||
if (this._code !== void 0) return this._length;
|
||||
this.create();
|
||||
return this._seq;
|
||||
return this._length;
|
||||
}
|
||||
|
||||
get microHet(): ReadonlyMap<number, string[]> {
|
||||
if (this._microHet !== void 0) return this._microHet;
|
||||
this.create();
|
||||
return this._microHet!;
|
||||
}
|
||||
|
||||
private create() {
|
||||
@@ -91,21 +135,97 @@ namespace Sequence {
|
||||
}
|
||||
|
||||
const count = maxSeqId - minSeqId + 1;
|
||||
const sequenceArray = new Array(maxSeqId + 1);
|
||||
const sequenceArray = new Array<string>(maxSeqId + 1);
|
||||
const labels = new Array<string[]>(maxSeqId + 1);
|
||||
for (let i = 0; i < count; i++) {
|
||||
sequenceArray[i] = '-';
|
||||
labels[i] = [];
|
||||
}
|
||||
|
||||
const compIds = new Array<string[]>(maxSeqId + 1);
|
||||
for (let i = minSeqId; i <= maxSeqId; ++i) {
|
||||
compIds[i] = [];
|
||||
}
|
||||
|
||||
for (let i = 0, _i = this.seqId.rowCount; i < _i; i++) {
|
||||
sequenceArray[this.seqId.value(i) - minSeqId] = this.code(this.residueName.value(i) || '');
|
||||
const seqId = this.seqId.value(i)
|
||||
const idx = seqId - minSeqId;
|
||||
const name = this.compId.value(i);
|
||||
const code = this.codeFromName(name);
|
||||
// in case of MICROHETEROGENEITY `sequenceArray[idx]` may already be set
|
||||
if (!sequenceArray[idx] || sequenceArray[idx] === '-') {
|
||||
if (code === 'X' && this.modifiedMap && this.modifiedMap.has(name)) {
|
||||
sequenceArray[idx] = this.modifiedMap.get(name)!
|
||||
} else {
|
||||
sequenceArray[idx] = code;
|
||||
}
|
||||
}
|
||||
labels[idx].push(code === 'X' ? name : code);
|
||||
compIds[seqId].push(name);
|
||||
}
|
||||
|
||||
this._seq = sequenceArray.join('');
|
||||
const microHet = new Map()
|
||||
for (let i = minSeqId; i <= maxSeqId; ++i) {
|
||||
if (compIds[i].length > 1) microHet.set(i, compIds[i])
|
||||
}
|
||||
|
||||
this._code = Column.ofStringArray(sequenceArray) as Column<Alphabet>
|
||||
this._label = Column.ofLambda({
|
||||
value: i => {
|
||||
const l = labels[i]
|
||||
return l.length > 1 ? `(${l.join('|')})` : l.join('')
|
||||
},
|
||||
rowCount: labels.length,
|
||||
schema: Column.Schema.str
|
||||
})
|
||||
this._microHet = microHet
|
||||
this._offset = minSeqId - 1;
|
||||
this._length = count
|
||||
}
|
||||
|
||||
constructor(public kind: Kind, private residueName: Column<string>, private seqId: Column<number>, private code: (name: string) => string) {
|
||||
constructor(public kind: K, public compId: Column<string>, public seqId: Column<number>, private modifiedMap?: ReadonlyMap<string, string>) {
|
||||
|
||||
this.codeFromName = codeProvider(kind)
|
||||
}
|
||||
}
|
||||
|
||||
export function ofSequenceRanges(seqIdBegin: Column<number>, seqIdEnd: Column<number>): Sequence {
|
||||
const kind = Kind.Generic
|
||||
|
||||
return new SequenceRangesImpl(kind, seqIdBegin, seqIdEnd) as Sequence;
|
||||
}
|
||||
|
||||
class SequenceRangesImpl<K extends Kind, Alphabet extends string> implements Base<K, Alphabet> {
|
||||
public offset: number
|
||||
public length: number
|
||||
public code: Column<Alphabet>
|
||||
public label: Column<string>
|
||||
public seqId: Column<number>
|
||||
public compId: Column<string>
|
||||
public microHet: ReadonlyMap<number, string[]>
|
||||
|
||||
constructor(public kind: K, private seqIdStart: Column<number>, private seqIdEnd: Column<number>) {
|
||||
let maxSeqId = 0, minSeqId = Number.MAX_SAFE_INTEGER;
|
||||
for (let i = 0, _i = this.seqIdStart.rowCount; i < _i; i++) {
|
||||
const idStart = this.seqIdStart.value(i);
|
||||
const idEnd = this.seqIdEnd.value(i);
|
||||
if (idStart < minSeqId) minSeqId = idStart;
|
||||
if (maxSeqId < idEnd) maxSeqId = idEnd;
|
||||
}
|
||||
|
||||
const count = maxSeqId - minSeqId + 1;
|
||||
|
||||
this.code = Column.ofConst('X', count, Column.Schema.str) as Column<Alphabet>
|
||||
this.label = Column.ofConst('', count, Column.Schema.str)
|
||||
this.seqId = Column.ofLambda({
|
||||
value: row => row + minSeqId + 1,
|
||||
rowCount: count,
|
||||
schema: Column.Schema.int
|
||||
})
|
||||
this.compId = Column.ofConst('', count, Column.Schema.str)
|
||||
|
||||
this.offset = minSeqId - 1;
|
||||
this.length = count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { CustomProperties } from '../common/custom-property';
|
||||
import { SecondaryStructure } from './properties/seconday-structure';
|
||||
import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
|
||||
import { ModelFormat } from '../../../mol-model-formats/structure/format';
|
||||
import { calcModelCenter } from './util';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
|
||||
/**
|
||||
* Interface to the "source data" of the molecule.
|
||||
@@ -23,6 +25,7 @@ import { ModelFormat } from '../../../mol-model-formats/structure/format';
|
||||
*/
|
||||
export interface Model extends Readonly<{
|
||||
id: UUID,
|
||||
entryId: string,
|
||||
label: string,
|
||||
|
||||
/** the name of the entry/file/collection the model is part of */
|
||||
@@ -74,4 +77,12 @@ export interface Model extends Readonly<{
|
||||
export namespace Model {
|
||||
// TODO: is this enough?
|
||||
export type Trajectory = ReadonlyArray<Model>
|
||||
|
||||
const CenterProp = '__Center__'
|
||||
export function getCenter(model: Model): Vec3 {
|
||||
if (model._dynamicPropertyData[CenterProp]) return model._dynamicPropertyData[CenterProp]
|
||||
const center = calcModelCenter(model.atomicConformation, model.coarseConformation)
|
||||
model._dynamicPropertyData[CenterProp] = center
|
||||
return center
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,17 @@ import { ElementIndex, ChainIndex, EntityIndex } from '../../indexing';
|
||||
import SortedRanges from '../../../../../mol-data/int/sorted-ranges';
|
||||
|
||||
export interface CoarsedElementKeys {
|
||||
// assign a key to each element
|
||||
/** Assign a key to each element */
|
||||
chainKey: ArrayLike<ChainIndex>,
|
||||
// assign a key to each element, index to the Model.entities.data table
|
||||
/** Assign a key to each element, index to the Model.entities.data table */
|
||||
entityKey: ArrayLike<EntityIndex>,
|
||||
|
||||
/** find index of the residue/feature element where seq_id is included */
|
||||
/** Find index of the residue/feature element where seq_id is included */
|
||||
findSequenceKey(entityId: string, asym_id: string, seq_id: number): ElementIndex
|
||||
findChainKey(entityId: string, asym_id: string): ChainIndex
|
||||
|
||||
/** Returns index or -1 if not present. */
|
||||
getEntityFromChain(cI: ChainIndex): EntityIndex
|
||||
}
|
||||
|
||||
export interface CoarseElementData {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column } from '../../../../mol-data/db'
|
||||
@@ -9,6 +10,8 @@ import { AtomicHierarchy } from './atomic/hierarchy';
|
||||
import { Entities } from './common';
|
||||
import { Sequence } from '../../../sequence';
|
||||
import { ChainIndex } from '../indexing';
|
||||
import { CoarseHierarchy } from './coarse';
|
||||
import { CoarseElements } from './coarse/hierarchy';
|
||||
|
||||
interface StructureSequence {
|
||||
readonly sequences: ReadonlyArray<StructureSequence.Entity>,
|
||||
@@ -18,19 +21,41 @@ interface StructureSequence {
|
||||
namespace StructureSequence {
|
||||
export interface Entity {
|
||||
readonly entityId: string,
|
||||
readonly num: Column<number>,
|
||||
// Corresponds to _entity_poly_seq.mon_id
|
||||
readonly compId: Column<string>,
|
||||
readonly sequence: Sequence
|
||||
}
|
||||
|
||||
const Empty: StructureSequence = { byEntityKey: {}, sequences: [] }
|
||||
|
||||
function merge(...entitySeqs: StructureSequence[]): StructureSequence {
|
||||
const sequences: StructureSequence.Entity[] = []
|
||||
const byEntityKey: { [key: number]: StructureSequence.Entity } = {}
|
||||
|
||||
for (let i = 0, il = entitySeqs.length; i < il; ++i) {
|
||||
sequences.push(...entitySeqs[i].sequences)
|
||||
Object.assign(byEntityKey, entitySeqs[i].byEntityKey)
|
||||
}
|
||||
return { sequences, byEntityKey }
|
||||
}
|
||||
|
||||
export function fromHierarchy(entities: Entities, atomicHierarchy: AtomicHierarchy, coarseHierarchy: CoarseHierarchy, modResMap?: ReadonlyMap<string, string>): StructureSequence {
|
||||
const atomic = fromAtomicHierarchy(entities, atomicHierarchy, modResMap)
|
||||
const coarse = coarseHierarchy.isDefined ? fromCoarseHierarchy(entities, coarseHierarchy) : Empty
|
||||
return merge(atomic, coarse)
|
||||
}
|
||||
|
||||
export function fromAtomicHierarchy(entities: Entities, hierarchy: AtomicHierarchy, modResMap?: ReadonlyMap<string, string>): StructureSequence {
|
||||
const { label_comp_id, label_seq_id } = hierarchy.residues
|
||||
const { chainAtomSegments, residueAtomSegments } = hierarchy
|
||||
const { count, offsets } = chainAtomSegments
|
||||
|
||||
const byEntityKey: StructureSequence['byEntityKey'] = { };
|
||||
const sequences: StructureSequence.Entity[] = [];
|
||||
|
||||
// check if chain segments are empty
|
||||
if (count === 1 && offsets[0] === 0 && offsets[1] === 0) {
|
||||
return { byEntityKey, sequences };
|
||||
}
|
||||
|
||||
for (let cI = 0 as ChainIndex, _cI = hierarchy.chains._rowCount; cI < _cI; cI++) {
|
||||
const entityKey = hierarchy.index.getEntityFromChain(cI);
|
||||
// Only for polymers, trying to mirror _entity_poly_seq
|
||||
@@ -43,16 +68,14 @@ namespace StructureSequence {
|
||||
}
|
||||
cI--;
|
||||
|
||||
const rStart = residueAtomSegments.index[chainAtomSegments.offsets[start]];
|
||||
const rEnd = residueAtomSegments.index[chainAtomSegments.offsets[cI + 1]];
|
||||
const rStart = residueAtomSegments.index[offsets[start]];
|
||||
const rEnd = residueAtomSegments.index[offsets[cI + 1]];
|
||||
|
||||
const compId = Column.window(label_comp_id, rStart, rEnd);
|
||||
const num = Column.window(label_seq_id, rStart, rEnd);
|
||||
|
||||
byEntityKey[entityKey] = {
|
||||
entityId: entities.data.id.value(entityKey),
|
||||
compId,
|
||||
num,
|
||||
sequence: Sequence.ofResidueNames(compId, num, modResMap)
|
||||
};
|
||||
|
||||
@@ -61,6 +84,52 @@ namespace StructureSequence {
|
||||
|
||||
return { byEntityKey, sequences };
|
||||
}
|
||||
|
||||
export function fromCoarseHierarchy(entities: Entities, hierarchy: CoarseHierarchy): StructureSequence {
|
||||
const spheres = fromCoarseElements(entities, hierarchy.spheres)
|
||||
const gaussians = fromCoarseElements(entities, hierarchy.gaussians)
|
||||
return merge(spheres, gaussians)
|
||||
}
|
||||
|
||||
export function fromCoarseElements(entities: Entities, elements: CoarseElements): StructureSequence {
|
||||
const { chainElementSegments, seq_id_begin, seq_id_end } = elements
|
||||
const { count, offsets } = chainElementSegments
|
||||
|
||||
const byEntityKey: StructureSequence['byEntityKey'] = { };
|
||||
const sequences: StructureSequence.Entity[] = [];
|
||||
|
||||
// check if chain segments are empty
|
||||
if (count === 1 && offsets[0] === 0 && offsets[1] === 0) {
|
||||
return { byEntityKey, sequences };
|
||||
}
|
||||
|
||||
for (let cI = 0 as ChainIndex, _cI = count; cI < _cI; cI++) {
|
||||
const eK = elements.getEntityFromChain(cI);
|
||||
if (byEntityKey[eK] !== void 0) continue;
|
||||
|
||||
let start = cI;
|
||||
cI++;
|
||||
while (cI < _cI && eK === elements.getEntityFromChain(cI)) {
|
||||
cI++;
|
||||
}
|
||||
cI--;
|
||||
|
||||
const eStart = offsets[start];
|
||||
const eEnd = offsets[cI + 1];
|
||||
|
||||
const seqIdBegin = Column.window(seq_id_begin, eStart, eEnd);
|
||||
const seqIdEnd = Column.window(seq_id_end, eStart, eEnd);
|
||||
|
||||
byEntityKey[eK] = {
|
||||
entityId: entities.data.id.value(eK),
|
||||
sequence: Sequence.ofSequenceRanges(seqIdBegin, seqIdEnd)
|
||||
};
|
||||
|
||||
sequences.push(byEntityKey[eK]);
|
||||
}
|
||||
|
||||
return { byEntityKey, sequences };
|
||||
}
|
||||
}
|
||||
|
||||
export default StructureSequence
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -9,6 +9,7 @@ import { arrayFind } from '../../../../mol-data/util'
|
||||
import { StructureQuery } from '../../query'
|
||||
import { Model } from '../../model'
|
||||
import { Spacegroup } from '../../../../mol-math/geometry';
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
|
||||
/** Determine an atom set and a list of operators that should be applied to that set */
|
||||
export interface OperatorGroup {
|
||||
@@ -47,8 +48,14 @@ interface ModelSymmetry {
|
||||
readonly isNonStandardCrytalFrame: boolean,
|
||||
readonly ncsOperators?: ReadonlyArray<SymmetryOperator>,
|
||||
|
||||
// optionally cached operators from [-3, -3, -3] to [3, 3, 3]
|
||||
_operators_333?: SymmetryOperator[]
|
||||
/**
|
||||
* optionally cached operators from [-3, -3, -3] to [3, 3, 3]
|
||||
* around reference point `ref` in fractional coordinates
|
||||
*/
|
||||
_operators_333?: {
|
||||
ref: Vec3,
|
||||
operators: SymmetryOperator[]
|
||||
}
|
||||
}
|
||||
|
||||
namespace ModelSymmetry {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user