mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
346 Commits
safari-deb
...
clamp-cube
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3667092327 | ||
|
|
842824057b | ||
|
|
6a32f85e60 | ||
|
|
f29c62ec33 | ||
|
|
d77981230b | ||
|
|
e2b92c15f0 | ||
|
|
14614f4803 | ||
|
|
37d3489b07 | ||
|
|
f81225cc0d | ||
|
|
eb47f43940 | ||
|
|
7618a5e2c9 | ||
|
|
ab3ff842b2 | ||
|
|
82f0f92c15 | ||
|
|
545d9434d8 | ||
|
|
bbc43d5113 | ||
|
|
a6709acf65 | ||
|
|
509a027742 | ||
|
|
7244023233 | ||
|
|
c5f987d8b2 | ||
|
|
793696d4c0 | ||
|
|
305ca05f04 | ||
|
|
f4d7d1920a | ||
|
|
458aad0161 | ||
|
|
9e3132461f | ||
|
|
8301291215 | ||
|
|
daed14e228 | ||
|
|
7db82c5ba5 | ||
|
|
91d03c22c2 | ||
|
|
bc188f0d2b | ||
|
|
3981225824 | ||
|
|
1886d9d72f | ||
|
|
2a7dec8892 | ||
|
|
35d4a5b297 | ||
|
|
26345bfa50 | ||
|
|
8c9b8676dd | ||
|
|
5593c7a75f | ||
|
|
5b70c14ffe | ||
|
|
5e4d611044 | ||
|
|
7ab9d57156 | ||
|
|
9ea6f51126 | ||
|
|
649fe4f4f0 | ||
|
|
53df86c585 | ||
|
|
87c708e3c1 | ||
|
|
ba927b0490 | ||
|
|
2a09725c98 | ||
|
|
9fa0d17933 | ||
|
|
8d9f8a996a | ||
|
|
8814b60d0b | ||
|
|
541c07c53a | ||
|
|
6cbed80815 | ||
|
|
a3c1fdc0f4 | ||
|
|
ddf789b01c | ||
|
|
ab86cc0bf3 | ||
|
|
dc8fab5820 | ||
|
|
813c4f845a | ||
|
|
6ed42e9521 | ||
|
|
fb01ba60ec | ||
|
|
ea4210ded5 | ||
|
|
75e5cf54d6 | ||
|
|
7cebc85a95 | ||
|
|
c00faafa6d | ||
|
|
c9b14f0742 | ||
|
|
9624137c0d | ||
|
|
3eb433368f | ||
|
|
58691f4f5f | ||
|
|
5e9295abd5 | ||
|
|
6ed0ae55b2 | ||
|
|
84448d0aa1 | ||
|
|
31ced24966 | ||
|
|
24681840af | ||
|
|
5d28aa4f2e | ||
|
|
7dabdf3085 | ||
|
|
d7cbd5570c | ||
|
|
80011d4aea | ||
|
|
c6fe440a01 | ||
|
|
ba8d6dc3fa | ||
|
|
378f4f8304 | ||
|
|
aa414485a5 | ||
|
|
3a35a5d66a | ||
|
|
43b0a72b09 | ||
|
|
521ac2d13f | ||
|
|
30520c50c2 | ||
|
|
819f07eba3 | ||
|
|
d8d6aa7136 | ||
|
|
0bdcfea276 | ||
|
|
718f76313f | ||
|
|
ed75a365d8 | ||
|
|
f5ff13ffe4 | ||
|
|
44c69b1716 | ||
|
|
559ca7ffb8 | ||
|
|
524f34e8c1 | ||
|
|
d749be11f0 | ||
|
|
13dc9ff3cb | ||
|
|
24b4fce326 | ||
|
|
f506210bf8 | ||
|
|
cb0cbd06ce | ||
|
|
3356239089 | ||
|
|
9a5b2edc08 | ||
|
|
2d41b4bd83 | ||
|
|
58f7758ee1 | ||
|
|
9dbb642883 | ||
|
|
c5222e4d1d | ||
|
|
a5a695a17c | ||
|
|
7d1dc86cfb | ||
|
|
03224f914a | ||
|
|
1cf1f07232 | ||
|
|
838d36a74e | ||
|
|
6c9300d01b | ||
|
|
3059f7efef | ||
|
|
fbce7d9afa | ||
|
|
1c9f3ed9fa | ||
|
|
8c47d2d400 | ||
|
|
8a18f25b5d | ||
|
|
e7ae0058ed | ||
|
|
98bf3a3e33 | ||
|
|
379fcd4494 | ||
|
|
8589777bac | ||
|
|
c10a21ecbd | ||
|
|
eddc616b14 | ||
|
|
70fc1a9579 | ||
|
|
f27ec4d6a4 | ||
|
|
1e6e956e2d | ||
|
|
0a2a3530d1 | ||
|
|
9e4c820e26 | ||
|
|
05290bfe9e | ||
|
|
607f4ce353 | ||
|
|
4b819ead1d | ||
|
|
d07d9d3f31 | ||
|
|
d08776bf19 | ||
|
|
7a61fd44fd | ||
|
|
151da1487c | ||
|
|
e3f6dfad5b | ||
|
|
32080ce918 | ||
|
|
aedb2138c8 | ||
|
|
90e6938f1c | ||
|
|
eadff35250 | ||
|
|
487450ec64 | ||
|
|
f2f730bab5 | ||
|
|
ceecee37a7 | ||
|
|
394377bea9 | ||
|
|
2b47818deb | ||
|
|
9f72465052 | ||
|
|
ac33b4a322 | ||
|
|
911c844e54 | ||
|
|
12b9655565 | ||
|
|
2935717a06 | ||
|
|
2c8d2cfa21 | ||
|
|
d67c0eb757 | ||
|
|
7bcbcd5a7f | ||
|
|
1c06e7f36e | ||
|
|
407297adc0 | ||
|
|
b082b31562 | ||
|
|
fcbeb0f82f | ||
|
|
7094f8f265 | ||
|
|
48aaa13420 | ||
|
|
d2434cf91f | ||
|
|
8dbe0d2793 | ||
|
|
7b308cf984 | ||
|
|
520af504aa | ||
|
|
4bee130599 | ||
|
|
19a9ed3e19 | ||
|
|
0dac1b93ae | ||
|
|
e824863de1 | ||
|
|
9ff8becd62 | ||
|
|
fcaa1bcfa8 | ||
|
|
39f51bcc4f | ||
|
|
1b904ee2c9 | ||
|
|
e2baafc426 | ||
|
|
6dabe73002 | ||
|
|
d9b2b99c86 | ||
|
|
bdf23a7c4e | ||
|
|
c1723e0806 | ||
|
|
93590bd482 | ||
|
|
fbaa9d9e58 | ||
|
|
ba78a8558c | ||
|
|
513be04352 | ||
|
|
15317aa11b | ||
|
|
93e107f333 | ||
|
|
655b334b0a | ||
|
|
95cc1c58a6 | ||
|
|
0511d3e599 | ||
|
|
92a41b5c46 | ||
|
|
be16837c8c | ||
|
|
bf5f26cb12 | ||
|
|
ccbcef7eff | ||
|
|
d02a97b7f0 | ||
|
|
e0ca413c54 | ||
|
|
a272fc1c05 | ||
|
|
2c3f74d4ea | ||
|
|
c65b2fc0fd | ||
|
|
fd725adf27 | ||
|
|
ceba6da91f | ||
|
|
064e7d42ee | ||
|
|
cfdbf0c614 | ||
|
|
436777fe34 | ||
|
|
f08aa46222 | ||
|
|
e099ac514a | ||
|
|
873755f619 | ||
|
|
2094b7cf83 | ||
|
|
6ffdd81bb1 | ||
|
|
a69cb17602 | ||
|
|
7c82a9fd6e | ||
|
|
10d9a6c83d | ||
|
|
f6ed650ef6 | ||
|
|
df9ce6add9 | ||
|
|
28ee47d501 | ||
|
|
df2da479ad | ||
|
|
46eb9d8baf | ||
|
|
b6be871a21 | ||
|
|
ce2367fc0a | ||
|
|
7789e8cfea | ||
|
|
51a9effcaa | ||
|
|
fc3b953a8e | ||
|
|
a2ded3cecc | ||
|
|
ffb1390b51 | ||
|
|
3b92591c05 | ||
|
|
f173ddcf00 | ||
|
|
f78306f624 | ||
|
|
9852c9301e | ||
|
|
2e4f3de604 | ||
|
|
300dd97353 | ||
|
|
8e29ade83a | ||
|
|
971c770f6a | ||
|
|
7d31a06ae4 | ||
|
|
c5319ad7b1 | ||
|
|
f8bdb28ea6 | ||
|
|
2f8806d7c2 | ||
|
|
7d0a181c12 | ||
|
|
27cb7e53ed | ||
|
|
ee5154b510 | ||
|
|
080837201a | ||
|
|
656e6c0d94 | ||
|
|
b018f61bab | ||
|
|
b03b306848 | ||
|
|
19bf5c2b3e | ||
|
|
c22a716cf9 | ||
|
|
220c65da92 | ||
|
|
675f4b86f8 | ||
|
|
d31b3522b2 | ||
|
|
4ed2bab1d7 | ||
|
|
a572872806 | ||
|
|
e3ca23db0b | ||
|
|
67eb16a53f | ||
|
|
d7421cd1a3 | ||
|
|
c2e68ced66 | ||
|
|
10cf0db050 | ||
|
|
08f1a1dcfe | ||
|
|
11bf352295 | ||
|
|
5fd560b30a | ||
|
|
e6d01ca246 | ||
|
|
0ddf2fa00d | ||
|
|
8776143ec2 | ||
|
|
080c8e7af3 | ||
|
|
64998e762b | ||
|
|
b508da5ccc | ||
|
|
84a492655a | ||
|
|
9b9cfe4138 | ||
|
|
f362a7086a | ||
|
|
9e9ec57a5f | ||
|
|
da6a153985 | ||
|
|
b4bde3f510 | ||
|
|
8a5cebd635 | ||
|
|
ddf2733d3c | ||
|
|
cf65bfbcd0 | ||
|
|
03cce830bc | ||
|
|
913cf4c3e9 | ||
|
|
2ccce0beaf | ||
|
|
52239f71cd | ||
|
|
d9265af2e8 | ||
|
|
5877f6a627 | ||
|
|
e3e982c051 | ||
|
|
17f09ff3de | ||
|
|
7a73416c03 | ||
|
|
0f799d44ad | ||
|
|
24ebd44f87 | ||
|
|
572b10e655 | ||
|
|
60361c176b | ||
|
|
b232a2c58f | ||
|
|
2a44ac56fb | ||
|
|
d0340a3257 | ||
|
|
e708a53ddb | ||
|
|
23cdd70198 | ||
|
|
ba4bc30a78 | ||
|
|
e516ea146d | ||
|
|
61af638fe4 | ||
|
|
7a0af4142f | ||
|
|
1aa8d596a3 | ||
|
|
a457810623 | ||
|
|
4bccb7ab84 | ||
|
|
fcd5b2ce0a | ||
|
|
57a1184a16 | ||
|
|
ef176efed8 | ||
|
|
9943e0317d | ||
|
|
ae11c1c904 | ||
|
|
f937e069ca | ||
|
|
c9326da47b | ||
|
|
4698c05f9c | ||
|
|
15fd2cd5a0 | ||
|
|
193eb11095 | ||
|
|
59cc0096cd | ||
|
|
6bd7390eb8 | ||
|
|
eabeb18f34 | ||
|
|
4b6f539ba3 | ||
|
|
ea55fc5f41 | ||
|
|
94787229a4 | ||
|
|
0cb162022c | ||
|
|
5ff2ca311e | ||
|
|
44b8d452a8 | ||
|
|
82ccb1ab23 | ||
|
|
feb8b94e30 | ||
|
|
5adc73e277 | ||
|
|
c4ac983fc7 | ||
|
|
5ba7ba3aac | ||
|
|
e879479b3d | ||
|
|
7b49463297 | ||
|
|
66600c3373 | ||
|
|
19401c4bc6 | ||
|
|
bfc8660c5e | ||
|
|
6a83dc56ba | ||
|
|
82df9d8cad | ||
|
|
dd30fef078 | ||
|
|
79feb5a1cc | ||
|
|
0665524b11 | ||
|
|
d45367e840 | ||
|
|
cb0d988efc | ||
|
|
fc0c556967 | ||
|
|
00970164db | ||
|
|
7c3d76e9fe | ||
|
|
190c1f9620 | ||
|
|
f532325147 | ||
|
|
278dcb8808 | ||
|
|
309c25e10b | ||
|
|
6df728ea3e | ||
|
|
dcf4ef6d74 | ||
|
|
4de1369a5a | ||
|
|
2ccfdb1280 | ||
|
|
9fbf800639 | ||
|
|
577daf64df | ||
|
|
0b1943e9b3 | ||
|
|
30bd2dd876 | ||
|
|
cecd4d4179 | ||
|
|
364baab18d | ||
|
|
bb3d4d2171 | ||
|
|
2355faf899 | ||
|
|
858e0b24ff | ||
|
|
f7d0ed3988 |
10
.github/pull_request_template.md
vendored
Normal file
10
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- Thank you for contributing to Mol* -->
|
||||
|
||||
# Description
|
||||
|
||||
|
||||
## Actions
|
||||
|
||||
- [ ] Added description of changes to the `[Unreleased]` section of `CHANGELOG.md`
|
||||
- [ ] Updated headers of modified files
|
||||
- [ ] Added my name to `package.json`'s `contributors`
|
||||
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -6,7 +6,6 @@
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
"wayou.vscode-todo-highlight"
|
||||
|
||||
67
CHANGELOG.md
67
CHANGELOG.md
@@ -6,6 +6,73 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Make `PluginContext.initContainer` checkered canvas background optional
|
||||
|
||||
## [v3.23.0] - 2022-10-19
|
||||
|
||||
- Add `PluginContext.initContainer/mount/unmount` methods; these should make it easier to reuse a plugin context with both custom and built-in UI
|
||||
- Add `PluginContext.canvas3dInitialized`
|
||||
- `createPluginUI` now resolves after the 3d canvas has been initialized
|
||||
- Change EM Volume Streaming default from `Whote Structure` to `Auto`
|
||||
|
||||
## [v3.22.0] - 2022-10-17
|
||||
|
||||
- Replace `VolumeIsosurfaceParams.pickingGranularity` param with `Volume.PickingGranuality`
|
||||
|
||||
## [v3.21.0] - 2022-10-17
|
||||
|
||||
- Add `VolumeIsosurfaceParams.pickingGranularity` param
|
||||
- Prevent component controls collapsing when option is selected
|
||||
|
||||
## [v3.20.0] - 2022-10-16
|
||||
|
||||
- [Breaking] Rename the ``model-index`` color theme to ``trajectory-index``
|
||||
- Add a new ``model-index`` color theme that uniquely colors each loaded model
|
||||
- Add the new ``model-index`` and ``structure-index`` color themes as an option for the carbon color in the ``element-symbol`` and ``ilustrative`` color themes
|
||||
- Add ``structure-index`` color theme that uniquely colors each root structure
|
||||
- Add ``nearest`` method to ``Lookup3D``
|
||||
- Add mipmap-based blur for skybox backgrounds
|
||||
|
||||
## [v3.19.0] - 2022-10-01
|
||||
|
||||
- Fix "empty textures" error on empty canvas
|
||||
- Optimize BinaryCIF integer packing encoder
|
||||
- Fix dual depth peeling when post-processing is off or when rendering direct-volumes
|
||||
- Add ``cameraClipping.minNear`` parameter
|
||||
- Fix black artifacts on specular highlights with transparent background
|
||||
|
||||
## [v3.18.0] - 2022-09-17
|
||||
|
||||
- Integration of Dual depth peeling - OIT method
|
||||
- Stereo camera improvements
|
||||
- Fix param updates not applied
|
||||
- Better param ranges and description
|
||||
- Add timer.mark for left/right camera
|
||||
|
||||
## [v3.17.0] - 2022-09-11
|
||||
|
||||
- [Fix] Clone ``Canvas3DParams`` when creating a ``Canvas3D`` instance to prevent shared state between multiple instances
|
||||
- Add ``includeResidueTest`` option to ``alignAndSuperposeWithSIFTSMapping``
|
||||
- Add ``parentDisplay`` param for interactions representation.
|
||||
- [Experimental] Add support for PyMOL, VMD, and Jmol atom expressions in selection scripts
|
||||
- Support for ``failIfMajorPerformanceCaveat`` webgl attribute. Add ``PluginConfig.General.AllowMajorPerformanceCaveat`` and ``allow-major-performance-caveat`` Viewer GET param.
|
||||
- Fix handling of PDB TER records (#549)
|
||||
- Add support for getting multiple loci from a representation (``.getAllLoci()``)
|
||||
- Add ``key`` property to intra- and inter-bonds for referencing source data
|
||||
- Fix click event triggered after move
|
||||
|
||||
## [v3.16.0] - 2022-08-25
|
||||
|
||||
- Support ``globalColorParams`` and ``globalSymmetryParams`` in common representation params
|
||||
- Support ``label`` parameter in ``Viewer.loadStructureFromUrl``
|
||||
- Fix ``ViewportHelpContent`` Mouse Controls section
|
||||
|
||||
## [v3.15.0] - 2022-08-23
|
||||
|
||||
- Fix wboit in Safari >=15 (add missing depth renderbuffer to wboit pass)
|
||||
- Add 'Around Camera' option to Volume streaming
|
||||
- Avoid queuing more than one update in Volume streaming
|
||||
|
||||
## [v3.14.0] - 2022-08-20
|
||||
|
||||
- Expose inter-bonds compute params in structure
|
||||
|
||||
3835
package-lock.json
generated
3835
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "3.14.0",
|
||||
"version": "3.23.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -89,70 +89,73 @@
|
||||
"Ludovic Autin <autin@scripps.edu>",
|
||||
"Michal Malý <michal.maly@ibt.cas.cz>",
|
||||
"Jiří Černý <jiri.cerny@ibt.cas.cz>",
|
||||
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
|
||||
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>",
|
||||
"Adam Midlik <midlik@gmail.com>",
|
||||
"Koya Sakuma <koya.sakuma.work@gmail.com>",
|
||||
"Gianluca Tomasello <giagitom@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^3.2.1",
|
||||
"@graphql-codegen/cli": "^2.11.6",
|
||||
"@graphql-codegen/cli": "^2.13.7",
|
||||
"@graphql-codegen/time": "^3.2.1",
|
||||
"@graphql-codegen/typescript": "^2.7.3",
|
||||
"@graphql-codegen/typescript": "^2.7.4",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.3",
|
||||
"@graphql-codegen/typescript-operations": "^2.5.3",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.6",
|
||||
"@graphql-codegen/typescript-operations": "^2.5.4",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.1",
|
||||
"@types/jest": "^28.1.7",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/jest": "^29.1.2",
|
||||
"@types/react": "^18.0.21",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
||||
"@typescript-eslint/parser": "^5.40.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^7.3.0",
|
||||
"concurrently": "^7.4.0",
|
||||
"cpx2": "^4.2.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint": "^8.25.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"graphql": "^16.6.0",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^28.1.3",
|
||||
"jest": "^29.2.0",
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.54.5",
|
||||
"sass-loader": "^13.0.2",
|
||||
"simple-git": "^3.12.0",
|
||||
"sass": "^1.55.0",
|
||||
"sass-loader": "^13.1.0",
|
||||
"simple-git": "^3.14.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^28.0.8",
|
||||
"typescript": "^4.7.4",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.8.4",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.10",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/benchmark": "^2.1.2",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^16.11.51",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/node": "^16.11.66",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
"body-parser": "^1.20.0",
|
||||
"body-parser": "^1.20.1",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.1",
|
||||
"express": "^4.18.2",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^9.0.15",
|
||||
"immutable": "^4.1.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"rxjs": "^7.5.6",
|
||||
"swagger-ui-dist": "^4.14.0",
|
||||
"rxjs": "^7.5.7",
|
||||
"swagger-ui-dist": "^4.14.3",
|
||||
"tslib": "^2.4.0",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
|
||||
@@ -58,20 +58,22 @@ class Viewer {
|
||||
}
|
||||
|
||||
static async create(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
|
||||
const o = { ...DefaultViewerOptions, ...{
|
||||
layoutIsExpanded: false,
|
||||
layoutShowControls: false,
|
||||
layoutShowRemoteState: false,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: false,
|
||||
layoutShowLeftPanel: true,
|
||||
const o = {
|
||||
...DefaultViewerOptions, ...{
|
||||
layoutIsExpanded: false,
|
||||
layoutShowControls: false,
|
||||
layoutShowRemoteState: false,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: false,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: true,
|
||||
viewportShowControls: false,
|
||||
viewportShowSettings: false,
|
||||
viewportShowSelectionMode: false,
|
||||
viewportShowAnimation: false,
|
||||
} };
|
||||
viewportShowExpand: true,
|
||||
viewportShowControls: false,
|
||||
viewportShowSettings: false,
|
||||
viewportShowSelectionMode: false,
|
||||
viewportShowAnimation: false,
|
||||
}
|
||||
};
|
||||
const defaultSpec = DefaultPluginUISpec();
|
||||
|
||||
const spec: PluginUISpec = {
|
||||
@@ -135,18 +137,16 @@ class Viewer {
|
||||
}
|
||||
};
|
||||
|
||||
plugin.behaviors.canvas3d.initialized.subscribe(v => {
|
||||
if (v) {
|
||||
PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
backgroundColor: ColorNames.white,
|
||||
},
|
||||
camera: {
|
||||
...plugin.canvas3d!.props.camera,
|
||||
helper: { axes: { name: 'off', params: {} } }
|
||||
}
|
||||
} });
|
||||
PluginCommands.Canvas3D.SetSettings(plugin, {
|
||||
settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
backgroundColor: ColorNames.white,
|
||||
},
|
||||
camera: {
|
||||
...plugin.canvas3d!.props.camera,
|
||||
helper: { axes: { name: 'off', params: {} } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ class Viewer {
|
||||
structures.push({ ref: structureProperties?.ref || structure.ref });
|
||||
}
|
||||
|
||||
// remove current structuresfrom hierarchy as they will be merged
|
||||
// remove current structures from hierarchy as they will be merged
|
||||
// TODO only works with using loadStructuresFromUrlsAndMerge once
|
||||
// need some more API metho to work with the hierarchy
|
||||
this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -202,14 +202,14 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
|
||||
interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`)
|
||||
interactions: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
|
||||
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
|
||||
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, parentDisplay: 'between' }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
|
||||
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
|
||||
};
|
||||
|
||||
|
||||
@@ -88,7 +88,9 @@ const DefaultViewerOptions = {
|
||||
pickScale: PluginConfig.General.PickScale.defaultValue,
|
||||
pickPadding: PluginConfig.General.PickPadding.defaultValue,
|
||||
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
|
||||
enableDpoit: PluginConfig.General.EnableDpoit.defaultValue,
|
||||
preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
|
||||
allowMajorPerformanceCaveat: PluginConfig.General.AllowMajorPerformanceCaveat.defaultValue,
|
||||
|
||||
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
|
||||
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
|
||||
@@ -158,7 +160,9 @@ export class Viewer {
|
||||
[PluginConfig.General.PickScale, o.pickScale],
|
||||
[PluginConfig.General.PickPadding, o.pickPadding],
|
||||
[PluginConfig.General.EnableWboit, o.enableWboit],
|
||||
[PluginConfig.General.EnableDpoit, o.enableDpoit],
|
||||
[PluginConfig.General.PreferWebGl1, o.preferWebgl1],
|
||||
[PluginConfig.General.AllowMajorPerformanceCaveat, o.allowMajorPerformanceCaveat],
|
||||
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
|
||||
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
|
||||
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
|
||||
@@ -199,7 +203,7 @@ export class Viewer {
|
||||
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
|
||||
}
|
||||
|
||||
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
|
||||
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions & { label?: string }) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
@@ -208,6 +212,7 @@ export class Viewer {
|
||||
url: Asset.Url(url),
|
||||
format: format as any,
|
||||
isBinary,
|
||||
label: options?.label,
|
||||
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
|
||||
}
|
||||
}
|
||||
@@ -496,4 +501,4 @@ export const ViewerAutoPreset = StructureRepresentationPresetProvider({
|
||||
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,15 @@
|
||||
viewer.loadPdb('7bv2');
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
|
||||
// viewer.loadStructureFromUrl('my url', 'pdb', false, {
|
||||
// representationParams: {
|
||||
// theme: {
|
||||
// globalName: 'uniform',
|
||||
// globalColorParams: { value: 0xff0000 }
|
||||
// }
|
||||
// },
|
||||
// label: 'my structure'
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -60,7 +60,9 @@
|
||||
var pickScale = getParam('pick-scale', '[^&]+').trim();
|
||||
var pickPadding = getParam('pick-padding', '[^&]+').trim();
|
||||
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
|
||||
var enableDpoit = getParam('enable-dpoit', '[^&]+').trim() === '1';
|
||||
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
|
||||
var allowMajorPerformanceCaveat = getParam('allow-major-performance-caveat', '[^&]+').trim() === '1';
|
||||
|
||||
molstar.Viewer.create('app', {
|
||||
layoutShowControls: !hideControls,
|
||||
@@ -74,8 +76,10 @@
|
||||
pixelScale: parseFloat(pixelScale) || 1,
|
||||
pickScale: parseFloat(pickScale) || 0.25,
|
||||
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
|
||||
enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
|
||||
enableWboit: (disableWboit || enableDpoit) ? false : void 0, // use default value if disable-wboit is not set
|
||||
enableDpoit: enableDpoit ? true : void 0,
|
||||
preferWebgl1: preferWebgl1,
|
||||
allowMajorPerformanceCaveat: allowMajorPerformanceCaveat,
|
||||
}).then(viewer => {
|
||||
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
|
||||
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
|
||||
|
||||
@@ -71,6 +71,7 @@ export function getFieldType(type: string, description: string, values?: string[
|
||||
case 'ec-type':
|
||||
case 'ucode-alphanum-csv':
|
||||
case 'id_list':
|
||||
case 'entity_id_list':
|
||||
return ListCol('str', ',', description);
|
||||
case 'id_list_spc':
|
||||
return ListCol('str', ' ', description);
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { AlphaOrbitalsExample } from '.';
|
||||
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
|
||||
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
|
||||
import { PluginContextContainer } from '../../mol-plugin-ui/plugin';
|
||||
|
||||
export function mountControls(orbitals: AlphaOrbitalsExample, parent: Element) {
|
||||
ReactDOM.render(<PluginContextContainer plugin={orbitals.plugin}>
|
||||
createRoot(parent).render(<PluginContextContainer plugin={orbitals.plugin}>
|
||||
<Controls orbitals={orbitals} />
|
||||
</PluginContextContainer>, parent);
|
||||
</PluginContextContainer>);
|
||||
}
|
||||
|
||||
function Controls({ orbitals }: { orbitals: AlphaOrbitalsExample }) {
|
||||
|
||||
@@ -82,24 +82,20 @@ export class AlphaOrbitalsExample {
|
||||
|
||||
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
this.plugin.behaviors.canvas3d.initialized.subscribe(init => {
|
||||
if (!init) return;
|
||||
|
||||
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Error',
|
||||
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Error',
|
||||
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
});
|
||||
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
}
|
||||
|
||||
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
|
||||
|
||||
@@ -72,6 +72,7 @@ export const Backgrounds = PluginBehavior.create<{ }>({
|
||||
lightness: 0,
|
||||
saturation: 0,
|
||||
opacity: 1,
|
||||
blur: 0.3,
|
||||
}
|
||||
}
|
||||
}, 'Purple Nebula Skybox'],
|
||||
|
||||
@@ -60,6 +60,8 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
const merged = merge(
|
||||
this.controls.behaviors.params,
|
||||
this.plugin.canvas3d!.reprCount
|
||||
|
||||
@@ -118,11 +118,13 @@ export class Mp4Controls extends PluginComponent {
|
||||
}
|
||||
|
||||
private init() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
this.subscribe(this.plugin.managers.animation.events.updated.pipe(debounceTime(16)), () => {
|
||||
this.sync();
|
||||
});
|
||||
|
||||
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
|
||||
this.subscribe(this.plugin.canvas3d.resized, () => this.syncInfo());
|
||||
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
|
||||
|
||||
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2022 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>
|
||||
@@ -260,7 +260,8 @@ namespace Camera {
|
||||
radius: 0,
|
||||
radiusMax: 10,
|
||||
fog: 50,
|
||||
clipFar: true
|
||||
clipFar: true,
|
||||
minNear: 5,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -276,6 +277,7 @@ namespace Camera {
|
||||
radiusMax: number
|
||||
fog: number
|
||||
clipFar: boolean
|
||||
minNear: number
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
@@ -292,6 +294,7 @@ namespace Camera {
|
||||
if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
|
||||
if (typeof source.fog !== 'undefined') out.fog = source.fog;
|
||||
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
|
||||
if (typeof source.minNear !== 'undefined') out.minNear = source.minNear;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -303,6 +306,7 @@ namespace Camera {
|
||||
&& a.radiusMax === b.radiusMax
|
||||
&& a.fog === b.fog
|
||||
&& a.clipFar === b.clipFar
|
||||
&& a.minNear === b.minNear
|
||||
&& Vec3.exactEquals(a.position, b.position)
|
||||
&& Vec3.exactEquals(a.up, b.up)
|
||||
&& Vec3.exactEquals(a.target, b.target);
|
||||
@@ -370,7 +374,7 @@ function updatePers(camera: Camera) {
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
let { radius, radiusMax, mode, fog, clipFar } = camera.state;
|
||||
let { radius, radiusMax, mode, fog, clipFar, minNear } = camera.state;
|
||||
if (radius < 0.01) radius = 0.01;
|
||||
|
||||
const normalizedFar = clipFar ? radius : radiusMax;
|
||||
@@ -384,12 +388,12 @@ function updateClip(camera: Camera) {
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(Math.min(radiusMax, 5), near);
|
||||
far = Math.max(5, far);
|
||||
near = Math.max(Math.min(radiusMax, minNear), near);
|
||||
far = Math.max(minNear, far);
|
||||
} else {
|
||||
// not too close to 0 as it causes issues with outline rendering
|
||||
near = Math.max(Math.min(radiusMax, 5), near);
|
||||
far = Math.max(5, far);
|
||||
near = Math.max(Math.min(radiusMax, minNear), near);
|
||||
far = Math.max(minNear, far);
|
||||
}
|
||||
|
||||
if (near === far) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 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>
|
||||
@@ -13,8 +13,8 @@ import { Camera, ICamera } from '../camera';
|
||||
import { Viewport } from './util';
|
||||
|
||||
export const StereoCameraParams = {
|
||||
eyeSeparation: PD.Numeric(0.064, { min: 0.01, max: 0.5, step: 0.001 }),
|
||||
focus: PD.Numeric(10, { min: 1, max: 100, step: 0.01 }),
|
||||
eyeSeparation: PD.Numeric(0.062, { min: 0.02, max: 0.1, step: 0.001 }, { description: 'Distance between left and right camera.' }),
|
||||
focus: PD.Numeric(10, { min: 1, max: 20, step: 0.1 }, { description: 'Apparent object distance.' }),
|
||||
};
|
||||
export const DefaultStereoCameraProps = PD.getDefaultValues(StereoCameraParams);
|
||||
export type StereoCameraProps = PD.Values<typeof StereoCameraParams>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
@@ -39,9 +40,10 @@ import { Helper } from './helper/helper';
|
||||
import { Passes } from './passes/passes';
|
||||
import { shallowEqual } from '../mol-util';
|
||||
import { MarkingParams } from './passes/marking';
|
||||
import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit } from '../mol-gl/webgl/render-item';
|
||||
import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit, GraphicsRenderVariantsDpoit } from '../mol-gl/webgl/render-item';
|
||||
import { degToRad, radToDeg } from '../mol-math/misc';
|
||||
import { AssetManager } from '../mol-util/assets';
|
||||
import { deepClone } from '../mol-util/object';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
camera: PD.Group({
|
||||
@@ -63,6 +65,7 @@ export const Canvas3DParams = {
|
||||
cameraClipping: PD.Group({
|
||||
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
|
||||
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
|
||||
minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
|
||||
}, { pivot: 'radius' }),
|
||||
viewport: PD.MappedStatic('canvas', {
|
||||
canvas: PD.Group({}),
|
||||
@@ -83,6 +86,7 @@ export const Canvas3DParams = {
|
||||
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
|
||||
sceneRadiusFactor: PD.Numeric(1, { min: 1, max: 10, step: 0.1 }),
|
||||
transparentBackground: PD.Boolean(false),
|
||||
dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
|
||||
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
@@ -116,6 +120,7 @@ interface Canvas3DContext {
|
||||
|
||||
namespace Canvas3DContext {
|
||||
export const DefaultAttribs = {
|
||||
failIfMajorPerformanceCaveat: false,
|
||||
/** true by default to avoid issues with Safari (Jan 2021) */
|
||||
antialias: true,
|
||||
/** true to support multiple Canvas3D objects with a single context */
|
||||
@@ -125,14 +130,19 @@ namespace Canvas3DContext {
|
||||
/** extra pixels to around target to check in case target is empty */
|
||||
pickPadding: 1,
|
||||
enableWboit: true,
|
||||
enableDpoit: false,
|
||||
preferWebGl1: false
|
||||
};
|
||||
export type Attribs = typeof DefaultAttribs
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
|
||||
const a = { ...DefaultAttribs, ...attribs };
|
||||
const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
|
||||
|
||||
if (a.enableWboit && a.enableDpoit) throw new Error('Multiple transparency methods not allowed.');
|
||||
|
||||
const { failIfMajorPerformanceCaveat, antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
|
||||
const gl = getGLContext(canvas, {
|
||||
failIfMajorPerformanceCaveat,
|
||||
antialias,
|
||||
preserveDrawingBuffer,
|
||||
alpha: true, // the renderer requires an alpha channel
|
||||
@@ -285,7 +295,7 @@ namespace Canvas3D {
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
|
||||
|
||||
export function create({ webgl, input, passes, attribs, assetManager }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
|
||||
const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
|
||||
const p: Canvas3DProps = { ...deepClone(DefaultCanvas3DParams), ...deepClone(props) };
|
||||
|
||||
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
|
||||
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();
|
||||
@@ -302,8 +312,7 @@ namespace Canvas3D {
|
||||
let width = 128;
|
||||
let height = 128;
|
||||
updateViewport();
|
||||
|
||||
const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
|
||||
const scene = Scene.create(webgl, passes.draw.dpoitEnabled ? GraphicsRenderVariantsDpoit : (passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended));
|
||||
|
||||
function getSceneRadius() {
|
||||
return scene.boundingSphere.radius * p.sceneRadiusFactor;
|
||||
@@ -314,6 +323,7 @@ namespace Canvas3D {
|
||||
mode: p.camera.mode,
|
||||
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
|
||||
clipFar: p.cameraClipping.far,
|
||||
minNear: p.cameraClipping.minNear,
|
||||
fov: degToRad(p.camera.fov),
|
||||
}, { x, y, width, height }, { pixelScale: attribs.pixelScale });
|
||||
const stereoCamera = new StereoCamera(camera, p.camera.stereo.params);
|
||||
@@ -679,10 +689,11 @@ namespace Canvas3D {
|
||||
cameraFog: camera.state.fog > 0
|
||||
? { name: 'on' as const, params: { intensity: camera.state.fog } }
|
||||
: { name: 'off' as const, params: {} },
|
||||
cameraClipping: { far: camera.state.clipFar, radius },
|
||||
cameraClipping: { far: camera.state.clipFar, radius, minNear: camera.state.minNear },
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
sceneRadiusFactor: p.sceneRadiusFactor,
|
||||
transparentBackground: p.transparentBackground,
|
||||
dpoitIterations: p.dpoitIterations,
|
||||
viewport: p.viewport,
|
||||
|
||||
postprocessing: { ...p.postprocessing },
|
||||
@@ -805,6 +816,9 @@ namespace Canvas3D {
|
||||
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
|
||||
cameraState.clipFar = props.cameraClipping.far;
|
||||
}
|
||||
if (props.cameraClipping.minNear !== undefined && props.cameraClipping.minNear !== camera.state.minNear) {
|
||||
cameraState.minNear = props.cameraClipping.minNear;
|
||||
}
|
||||
if (props.cameraClipping.radius !== undefined) {
|
||||
const radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius);
|
||||
if (radius > 0 && radius !== cameraState.radius) {
|
||||
@@ -817,9 +831,13 @@ namespace Canvas3D {
|
||||
|
||||
if (props.camera?.helper) helper.camera.setProps(props.camera.helper);
|
||||
if (props.camera?.manualReset !== undefined) p.camera.manualReset = props.camera.manualReset;
|
||||
if (props.camera?.stereo !== undefined) Object.assign(p.camera.stereo, props.camera.stereo);
|
||||
if (props.camera?.stereo !== undefined) {
|
||||
Object.assign(p.camera.stereo, props.camera.stereo);
|
||||
stereoCamera.setProps(p.camera.stereo.params);
|
||||
}
|
||||
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs;
|
||||
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground;
|
||||
if (props.dpoitIterations !== undefined) p.dpoitIterations = props.dpoitIterations;
|
||||
if (props.viewport !== undefined) {
|
||||
const doNotUpdate = p.viewport === props.viewport ||
|
||||
(p.viewport.name === props.viewport.name && shallowEqual(p.viewport.params, props.viewport.params));
|
||||
@@ -855,7 +873,7 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
|
||||
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, passes.draw.dpoitEnabled, props);
|
||||
},
|
||||
getRenderObjects(): GraphicsRenderObject[] {
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
@@ -920,4 +938,4 @@ namespace Canvas3D {
|
||||
Viewport.set(controls.viewport, x, y, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ const SkyboxParams = {
|
||||
pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }),
|
||||
}, { isExpanded: true, label: 'Files' }),
|
||||
}),
|
||||
blur: PD.Numeric(0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'Note, this only works in WebGL2 or when "EXT_shader_texture_lod" is available.' }),
|
||||
...SharedParams,
|
||||
};
|
||||
type SkyboxProps = PD.Values<typeof SkyboxParams>
|
||||
@@ -170,6 +171,7 @@ export class BackgroundPass {
|
||||
Mat4.invert(m, m);
|
||||
ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m);
|
||||
|
||||
ValueCell.updateIfChanged(this.renderable.values.uBlur, props.blur);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
|
||||
@@ -367,7 +369,7 @@ function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces:
|
||||
const cubeAssets = getCubeAssets(assetManager, faces);
|
||||
const cubeFaces = getCubeFaces(assetManager, cubeAssets);
|
||||
const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz];
|
||||
const texture = ctx.resources.cubeTexture(cubeFaces, false, onload);
|
||||
const texture = ctx.resources.cubeTexture(cubeFaces, true, onload);
|
||||
return { texture, assets };
|
||||
}
|
||||
|
||||
@@ -424,12 +426,15 @@ const BackgroundSchema = {
|
||||
uGradientColorA: UniformSpec('v3'),
|
||||
uGradientColorB: UniformSpec('v3'),
|
||||
uGradientRatio: UniformSpec('f'),
|
||||
uBlur: UniformSpec('f'),
|
||||
uOpacity: UniformSpec('f'),
|
||||
uSaturation: UniformSpec('f'),
|
||||
uLightness: UniformSpec('f'),
|
||||
dVariant: DefineSpec('string', ['skybox', 'image', 'verticalGradient', 'horizontalGradient', 'radialGradient']),
|
||||
};
|
||||
const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag);
|
||||
const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag, {
|
||||
shaderTextureLod: 'optional'
|
||||
});
|
||||
type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>>
|
||||
|
||||
function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable {
|
||||
@@ -448,6 +453,7 @@ function getBackgroundRenderable(ctx: WebGLContext, width: number, height: numbe
|
||||
uGradientColorA: ValueCell.create(Vec3()),
|
||||
uGradientColorB: ValueCell.create(Vec3()),
|
||||
uGradientRatio: ValueCell.create(0.5),
|
||||
uBlur: ValueCell.create(0),
|
||||
uOpacity: ValueCell.create(1),
|
||||
uSaturation: ValueCell.create(0),
|
||||
uLightness: ValueCell.create(0),
|
||||
|
||||
309
src/mol-canvas3d/passes/dpoit.ts
Normal file
309
src/mol-canvas3d/passes/dpoit.ts
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*
|
||||
* Adapted from https://github.com/tsherif/webgl2examples, The MIT License, Copyright © 2017 Tarek Sherif, Shuai Shao
|
||||
*/
|
||||
|
||||
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
|
||||
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
|
||||
import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
import { evaluateDpoit_frag } from '../../mol-gl/shader/evaluate-dpoit.frag';
|
||||
import { blendBackDpoit_frag } from '../../mol-gl/shader/blend-back-dpoit.frag';
|
||||
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
|
||||
import { isWebGL2 } from '../../mol-gl/webgl/compat';
|
||||
|
||||
const BlendBackDpoitSchema = {
|
||||
...QuadSchema,
|
||||
tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const BlendBackDpoitShaderCode = ShaderCode('blend-back-dpoit', quad_vert, blendBackDpoit_frag);
|
||||
type BlendBackDpoitRenderable = ComputeRenderable<Values<typeof BlendBackDpoitSchema>>
|
||||
|
||||
function getBlendBackDpoitRenderable(ctx: WebGLContext, dopitBlendBackTexture: Texture): BlendBackDpoitRenderable {
|
||||
const values: Values<typeof BlendBackDpoitSchema> = {
|
||||
...QuadValues,
|
||||
tDpoitBackColor: ValueCell.create(dopitBlendBackTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(dopitBlendBackTexture.getWidth(), dopitBlendBackTexture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...BlendBackDpoitSchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', BlendBackDpoitShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
const EvaluateDpoitSchema = {
|
||||
...QuadSchema,
|
||||
tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
};
|
||||
const EvaluateDpoitShaderCode = ShaderCode('evaluate-dpoit', quad_vert, evaluateDpoit_frag);
|
||||
type EvaluateDpoitRenderable = ComputeRenderable<Values<typeof EvaluateDpoitSchema>>
|
||||
|
||||
function getEvaluateDpoitRenderable(ctx: WebGLContext, dpoitFrontColorTexture: Texture): EvaluateDpoitRenderable {
|
||||
const values: Values<typeof EvaluateDpoitSchema> = {
|
||||
...QuadValues,
|
||||
tDpoitFrontColor: ValueCell.create(dpoitFrontColorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(dpoitFrontColorTexture.getWidth(), dpoitFrontColorTexture.getHeight())),
|
||||
};
|
||||
|
||||
const schema = { ...EvaluateDpoitSchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', EvaluateDpoitShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
|
||||
export class DpoitPass {
|
||||
private readonly DEPTH_CLEAR_VALUE = -99999.0; // NOTE same constant is set in shaders
|
||||
private readonly MAX_DEPTH = 1.0;
|
||||
private readonly MIN_DEPTH = 0.0;
|
||||
|
||||
private passCount = 0;
|
||||
private writeId: number;
|
||||
private readId: number;
|
||||
|
||||
private readonly blendBackRenderable: BlendBackDpoitRenderable;
|
||||
private readonly renderable: EvaluateDpoitRenderable;
|
||||
|
||||
private readonly depthFramebuffers: Framebuffer[];
|
||||
private readonly colorFramebuffers: Framebuffer[];
|
||||
|
||||
private readonly depthTextures: Texture[];
|
||||
private readonly colorFrontTextures: Texture[];
|
||||
private readonly colorBackTextures: Texture[];
|
||||
|
||||
private _supported = false;
|
||||
get supported() {
|
||||
return this._supported;
|
||||
}
|
||||
|
||||
bind() {
|
||||
const { state, gl, extensions: { blendMinMax } } = this.webgl;
|
||||
|
||||
// initialize
|
||||
this.passCount = 0;
|
||||
|
||||
this.depthFramebuffers[0].bind();
|
||||
state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.depthFramebuffers[1].bind();
|
||||
state.clearColor(-this.MIN_DEPTH, this.MAX_DEPTH, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.colorFramebuffers[0].bind();
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.colorFramebuffers[1].bind();
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.depthFramebuffers[0].bind();
|
||||
state.blendEquation(blendMinMax!.MAX);
|
||||
state.depthMask(false);
|
||||
|
||||
return {
|
||||
depth: this.depthTextures[1],
|
||||
frontColor: this.colorFrontTextures[1],
|
||||
backColor: this.colorBackTextures[1]
|
||||
};
|
||||
}
|
||||
|
||||
bindDualDepthPeeling() {
|
||||
const { state, gl, extensions: { blendMinMax } } = this.webgl;
|
||||
|
||||
this.readId = this.passCount % 2;
|
||||
this.writeId = 1 - this.readId; // ping-pong: 0 or 1
|
||||
|
||||
this.passCount += 1; // increment for next pass
|
||||
|
||||
this.depthFramebuffers[this.writeId].bind();
|
||||
state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.colorFramebuffers[this.writeId].bind();
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.depthFramebuffers[this.writeId].bind();
|
||||
state.blendEquation(blendMinMax!.MAX);
|
||||
state.depthMask(false);
|
||||
|
||||
return {
|
||||
depth: this.depthTextures[this.readId],
|
||||
frontColor: this.colorFrontTextures[this.readId],
|
||||
backColor: this.colorBackTextures[this.readId]
|
||||
};
|
||||
}
|
||||
|
||||
renderBlendBack() {
|
||||
if (isTimingMode) this.webgl.timer.mark('DpoitPass.renderBlendBack');
|
||||
const { state, gl } = this.webgl;
|
||||
|
||||
state.blendEquation(gl.FUNC_ADD);
|
||||
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
ValueCell.update(this.blendBackRenderable.values.tDpoitBackColor, this.colorBackTextures[this.writeId]);
|
||||
|
||||
this.blendBackRenderable.update();
|
||||
this.blendBackRenderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.renderBlendBack');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (isTimingMode) this.webgl.timer.mark('DpoitPass.render');
|
||||
const { state, gl } = this.webgl;
|
||||
|
||||
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
ValueCell.update(this.renderable.values.tDpoitFrontColor, this.colorFrontTextures[this.writeId]);
|
||||
|
||||
this.renderable.update();
|
||||
this.renderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.render');
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
const [w, h] = this.renderable.values.uTexSize.ref.value;
|
||||
if (width !== w || height !== h) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
this.depthTextures[i].define(width, height);
|
||||
this.colorFrontTextures[i].define(width, height);
|
||||
this.colorBackTextures[i].define(width, height);
|
||||
}
|
||||
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.blendBackRenderable.values.uTexSize, Vec2.set(this.blendBackRenderable.values.uTexSize.ref.value, width, height));
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this._supported) this._init();
|
||||
}
|
||||
|
||||
private _init() {
|
||||
const { extensions: { drawBuffers } } = this.webgl;
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// depth
|
||||
this.depthFramebuffers[i].bind();
|
||||
drawBuffers!.drawBuffers([
|
||||
drawBuffers!.COLOR_ATTACHMENT0,
|
||||
drawBuffers!.COLOR_ATTACHMENT1,
|
||||
drawBuffers!.COLOR_ATTACHMENT2
|
||||
]);
|
||||
|
||||
this.colorFrontTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color0');
|
||||
this.colorBackTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color1');
|
||||
this.depthTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color2');
|
||||
|
||||
// color
|
||||
this.colorFramebuffers[i].bind();
|
||||
drawBuffers!.drawBuffers([
|
||||
drawBuffers!.COLOR_ATTACHMENT0,
|
||||
drawBuffers!.COLOR_ATTACHMENT1
|
||||
]);
|
||||
|
||||
this.colorFrontTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color0');
|
||||
this.colorBackTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color1');
|
||||
}
|
||||
}
|
||||
|
||||
static isSupported(webgl: WebGLContext) {
|
||||
const { extensions: { drawBuffers, textureFloat, colorBufferFloat, blendMinMax } } = webgl;
|
||||
if (!textureFloat || !colorBufferFloat || !drawBuffers || !blendMinMax) {
|
||||
if (isDebugMode) {
|
||||
const missing: string[] = [];
|
||||
if (!textureFloat) missing.push('textureFloat');
|
||||
if (!colorBufferFloat) missing.push('colorBufferFloat');
|
||||
if (!drawBuffers) missing.push('drawBuffers');
|
||||
if (!blendMinMax) missing.push('blendMinMax');
|
||||
console.log(`Missing "${missing.join('", "')}" extensions required for "dpoit"`);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, width: number, height: number) {
|
||||
if (!DpoitPass.isSupported(webgl)) return;
|
||||
|
||||
const { resources, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
|
||||
|
||||
// textures
|
||||
|
||||
if (isWebGL2(webgl.gl)) {
|
||||
this.depthTextures = [
|
||||
resources.texture('image-float32', 'rg', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rg', 'float', 'nearest')
|
||||
];
|
||||
|
||||
this.colorFrontTextures = colorBufferHalfFloat && textureHalfFloat ? [
|
||||
resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
|
||||
resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
] : [
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
];
|
||||
|
||||
this.colorBackTextures = colorBufferHalfFloat && textureHalfFloat ? [
|
||||
resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
|
||||
resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
|
||||
] : [
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
];
|
||||
} else {
|
||||
// in webgl1 drawbuffers must be in the same format for some reason
|
||||
|
||||
this.depthTextures = [
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
];
|
||||
|
||||
this.colorFrontTextures = [
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
];
|
||||
|
||||
this.colorBackTextures = [
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest'),
|
||||
resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
];
|
||||
}
|
||||
|
||||
this.depthTextures[0].define(width, height);
|
||||
this.depthTextures[1].define(width, height);
|
||||
|
||||
this.colorFrontTextures[0].define(width, height);
|
||||
this.colorFrontTextures[1].define(width, height);
|
||||
|
||||
this.colorBackTextures[0].define(width, height);
|
||||
this.colorBackTextures[1].define(width, height);
|
||||
|
||||
// framebuffers
|
||||
|
||||
this.depthFramebuffers = [resources.framebuffer(), resources.framebuffer()];
|
||||
this.colorFramebuffers = [resources.framebuffer(), resources.framebuffer()];
|
||||
|
||||
// renderables
|
||||
|
||||
this.blendBackRenderable = getBlendBackDpoitRenderable(webgl, this.colorBackTextures[0]);
|
||||
this.renderable = getEvaluateDpoitRenderable(webgl, this.colorFrontTextures[0]);
|
||||
|
||||
this._supported = true;
|
||||
this._init();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
@@ -17,6 +18,7 @@ import { Helper } from '../helper/helper';
|
||||
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
import { WboitPass } from './wboit';
|
||||
import { DpoitPass } from './dpoit';
|
||||
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
|
||||
import { MarkingPass, MarkingProps } from './marking';
|
||||
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
|
||||
@@ -27,6 +29,7 @@ type Props = {
|
||||
postprocessing: PostprocessingProps;
|
||||
marking: MarkingProps;
|
||||
transparentBackground: boolean;
|
||||
dpoitIterations: number;
|
||||
}
|
||||
|
||||
type RenderContext = {
|
||||
@@ -52,6 +55,7 @@ export class DrawPass {
|
||||
private copyFboPostprocessing: CopyRenderable;
|
||||
|
||||
private readonly wboit: WboitPass | undefined;
|
||||
private readonly dpoit: DpoitPass | undefined;
|
||||
private readonly marking: MarkingPass;
|
||||
readonly postprocessing: PostprocessingPass;
|
||||
private readonly antialiasing: AntialiasingPass;
|
||||
@@ -60,9 +64,12 @@ export class DrawPass {
|
||||
return !!this.wboit?.supported;
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, width: number, height: number, enableWboit: boolean) {
|
||||
const { extensions, resources, isWebGL2 } = webgl;
|
||||
get dpoitEnabled() {
|
||||
return !!this.dpoit?.supported;
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, width: number, height: number, enableWboit: boolean, enableDpoit: boolean) {
|
||||
const { extensions, resources, isWebGL2 } = webgl;
|
||||
this.drawTarget = createNullRenderTarget(webgl.gl);
|
||||
this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
|
||||
this.packedDepth = !extensions.depthTexture;
|
||||
@@ -78,6 +85,7 @@ export class DrawPass {
|
||||
}
|
||||
|
||||
this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
|
||||
this.dpoit = enableDpoit ? new DpoitPass(webgl, width, height) : undefined;
|
||||
this.marking = new MarkingPass(webgl, width, height);
|
||||
this.postprocessing = new PostprocessingPass(webgl, assetManager, this);
|
||||
this.antialiasing = new AntialiasingPass(webgl, this);
|
||||
@@ -88,6 +96,7 @@ export class DrawPass {
|
||||
|
||||
reset() {
|
||||
this.wboit?.reset();
|
||||
this.dpoit?.reset();
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
@@ -111,12 +120,70 @@ export class DrawPass {
|
||||
this.wboit.setSize(width, height);
|
||||
}
|
||||
|
||||
if (this.dpoit?.supported) {
|
||||
this.dpoit.setSize(width, height);
|
||||
}
|
||||
|
||||
this.marking.setSize(width, height);
|
||||
this.postprocessing.setSize(width, height);
|
||||
this.antialiasing.setSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
private _renderDpoit(renderer: Renderer, camera: ICamera, scene: Scene, iterations: number, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
|
||||
if (!this.dpoit?.supported) throw new Error('expected dpoit to be supported');
|
||||
|
||||
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
renderer.clear(true);
|
||||
|
||||
// render opaque primitives
|
||||
if (scene.hasOpaque) {
|
||||
renderer.renderDpoitOpaque(scene.primitives, camera, null);
|
||||
}
|
||||
|
||||
if (PostprocessingPass.isEnabled(postprocessingProps)) {
|
||||
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
|
||||
this.depthTargetTransparent.bind();
|
||||
renderer.clearDepth(true);
|
||||
if (scene.opacityAverage < 1) {
|
||||
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
|
||||
}
|
||||
}
|
||||
|
||||
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
|
||||
}
|
||||
|
||||
this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
|
||||
// render transparent primitives
|
||||
if (scene.opacityAverage < 1) {
|
||||
const target = PostprocessingPass.isEnabled(postprocessingProps)
|
||||
? this.postprocessing.target : this.colorTarget;
|
||||
|
||||
const dpoitTextures = this.dpoit.bind();
|
||||
renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
if (isTimingMode) this.webgl.timer.mark('DpoitPass.layer');
|
||||
const dpoitTextures = this.dpoit.bindDualDepthPeeling();
|
||||
renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
|
||||
|
||||
target.bind();
|
||||
this.dpoit.renderBlendBack();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.layer');
|
||||
}
|
||||
|
||||
// evaluate dpoit
|
||||
target.bind();
|
||||
this.dpoit.render();
|
||||
}
|
||||
|
||||
// render transparent volumes
|
||||
if (scene.volumes.renderables.length > 0) {
|
||||
renderer.renderDpoitVolume(scene.volumes, camera, this.depthTextureOpaque);
|
||||
}
|
||||
}
|
||||
|
||||
private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
|
||||
if (!this.wboit?.supported) throw new Error('expected wboit to be supported');
|
||||
|
||||
@@ -254,13 +321,15 @@ export class DrawPass {
|
||||
|
||||
if (this.wboitEnabled) {
|
||||
this._renderWboit(renderer, camera, scene, transparentBackground, props.postprocessing);
|
||||
} else if (this.dpoitEnabled) {
|
||||
this._renderDpoit(renderer, camera, scene, props.dpoitIterations, transparentBackground, props.postprocessing);
|
||||
} else {
|
||||
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, transparentBackground, props.postprocessing);
|
||||
}
|
||||
|
||||
const target = postprocessingEnabled
|
||||
? this.postprocessing.target
|
||||
: !toDrawingBuffer || volumeRendering || this.wboitEnabled
|
||||
: !toDrawingBuffer || volumeRendering || this.wboitEnabled || this.dpoitEnabled
|
||||
? this.colorTarget
|
||||
: this.drawTarget;
|
||||
|
||||
@@ -303,7 +372,7 @@ export class DrawPass {
|
||||
this.webgl.state.disable(this.webgl.gl.DEPTH_TEST);
|
||||
if (postprocessingEnabled) {
|
||||
this.copyFboPostprocessing.render();
|
||||
} else if (volumeRendering || this.wboitEnabled) {
|
||||
} else if (volumeRendering || this.wboitEnabled || this.dpoitEnabled) {
|
||||
this.copyFboTarget.render();
|
||||
}
|
||||
}
|
||||
@@ -323,8 +392,12 @@ export class DrawPass {
|
||||
renderer.setPixelRatio(this.webgl.pixelRatio);
|
||||
|
||||
if (StereoCamera.is(camera)) {
|
||||
if (isTimingMode) this.webgl.timer.mark('StereoCamera.left');
|
||||
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.left');
|
||||
if (isTimingMode) this.webgl.timer.mark('StereoCamera.right');
|
||||
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.right');
|
||||
} else {
|
||||
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
}
|
||||
@@ -339,4 +412,4 @@ export class DrawPass {
|
||||
}
|
||||
return this.colorTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { AssetManager } from '../../mol-util/assets';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
marking: PD.Group(MarkingParams),
|
||||
@@ -48,10 +49,10 @@ export class ImagePass {
|
||||
get width() { return this._width; }
|
||||
get height() { return this._height; }
|
||||
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, props: Partial<ImageProps>) {
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, enableDpoit: boolean, props: Partial<ImageProps>) {
|
||||
this.props = { ...PD.getDefaultValues(ImageParams), ...props };
|
||||
|
||||
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, enableWboit);
|
||||
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, enableWboit, enableDpoit);
|
||||
this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
|
||||
this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ type Props = {
|
||||
postprocessing: PostprocessingProps
|
||||
marking: MarkingProps
|
||||
transparentBackground: boolean;
|
||||
dpoitIterations: number;
|
||||
}
|
||||
|
||||
type RenderContext = {
|
||||
|
||||
@@ -15,16 +15,19 @@ export class Passes {
|
||||
readonly pick: PickPass;
|
||||
readonly multiSample: MultiSamplePass;
|
||||
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, attribs: Partial<{ pickScale: number, enableWboit: boolean }> = {}) {
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, attribs: Partial<{ pickScale: number, enableWboit: boolean, enableDpoit: boolean }> = {}) {
|
||||
const { gl } = webgl;
|
||||
this.draw = new DrawPass(webgl, assetManager, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false);
|
||||
this.draw = new DrawPass(webgl, assetManager, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false, attribs.enableDpoit || false);
|
||||
this.pick = new PickPass(webgl, this.draw, attribs.pickScale || 0.25);
|
||||
this.multiSample = new MultiSamplePass(webgl, this.draw);
|
||||
}
|
||||
|
||||
updateSize() {
|
||||
const { gl } = this.webgl;
|
||||
this.draw.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
|
||||
// Avoid setting dimensions to 0x0 because it causes "empty textures are not allowed" error.
|
||||
const width = Math.max(gl.drawingBufferWidth, 2);
|
||||
const height = Math.max(gl.drawingBufferHeight, 2);
|
||||
this.draw.setSize(width, height);
|
||||
this.pick.syncSize();
|
||||
this.multiSample.syncSize();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { createTexture, loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Vec2, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
@@ -74,6 +74,7 @@ export class SmaaPass {
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
state.colorMask(true, true, true, true);
|
||||
state.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
@@ -191,8 +192,8 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights
|
||||
const width = edgesTexture.getWidth();
|
||||
const height = edgesTexture.getHeight();
|
||||
|
||||
const areaTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
const searchTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
const areaTexture = ctx.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
|
||||
const searchTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
|
||||
const values: Values<typeof WeightsSchema> = {
|
||||
...QuadValues,
|
||||
|
||||
@@ -18,6 +18,8 @@ import { evaluateWboit_frag } from '../../mol-gl/shader/evaluate-wboit.frag';
|
||||
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
|
||||
import { isWebGL2 } from '../../mol-gl/webgl/compat';
|
||||
import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
|
||||
|
||||
const EvaluateWboitSchema = {
|
||||
...QuadSchema,
|
||||
@@ -50,6 +52,7 @@ export class WboitPass {
|
||||
private readonly framebuffer: Framebuffer;
|
||||
private readonly textureA: Texture;
|
||||
private readonly textureB: Texture;
|
||||
private readonly depthRenderbuffer: Renderbuffer;
|
||||
|
||||
private _supported = false;
|
||||
get supported() {
|
||||
@@ -87,6 +90,7 @@ export class WboitPass {
|
||||
if (width !== w || height !== h) {
|
||||
this.textureA.define(width, height);
|
||||
this.textureB.define(width, height);
|
||||
this.depthRenderbuffer.setSize(width, height);
|
||||
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
|
||||
}
|
||||
}
|
||||
@@ -106,6 +110,8 @@ export class WboitPass {
|
||||
|
||||
this.textureA.attachFramebuffer(this.framebuffer, 'color0');
|
||||
this.textureB.attachFramebuffer(this.framebuffer, 'color1');
|
||||
|
||||
this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
|
||||
}
|
||||
|
||||
static isSupported(webgl: WebGLContext) {
|
||||
@@ -128,7 +134,7 @@ export class WboitPass {
|
||||
constructor(private webgl: WebGLContext, width: number, height: number) {
|
||||
if (!WboitPass.isSupported(webgl)) return;
|
||||
|
||||
const { resources } = webgl;
|
||||
const { resources, gl } = webgl;
|
||||
|
||||
this.textureA = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
this.textureA.define(width, height);
|
||||
@@ -136,6 +142,10 @@ export class WboitPass {
|
||||
this.textureB = resources.texture('image-float32', 'rgba', 'float', 'nearest');
|
||||
this.textureB.define(width, height);
|
||||
|
||||
this.depthRenderbuffer = isWebGL2(gl)
|
||||
? resources.renderbuffer('depth32f', 'depth', width, height)
|
||||
: resources.renderbuffer('depth16', 'depth', width, height);
|
||||
|
||||
this.renderable = getEvaluateWboitRenderable(webgl, this.textureA, this.textureB);
|
||||
this.framebuffer = resources.framebuffer();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -88,7 +88,7 @@ export class FontAtlas {
|
||||
this.scratchCanvas.width = this.maxWidth;
|
||||
this.scratchCanvas.height = this.lineHeight;
|
||||
|
||||
this.scratchContext = this.scratchCanvas.getContext('2d')!;
|
||||
this.scratchContext = this.scratchCanvas.getContext('2d', { willReadFrequently: true })!;
|
||||
this.scratchContext.font = `${p.fontStyle} ${p.fontVariant} ${p.fontWeight} ${fontSize}px ${p.fontFamily}`;
|
||||
this.scratchContext.fillStyle = 'black';
|
||||
this.scratchContext.textBaseline = 'middle';
|
||||
|
||||
@@ -387,6 +387,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
const type = isInstanceType ? 'volumeInstance' : 'volume';
|
||||
if (isTimingMode) webgl.timer.markEnd('calcTextureMeshColorSmoothing');
|
||||
|
||||
// printTextureImage(readTexture(webgl, texture), { scale: 0.75 });
|
||||
|
||||
return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
|
||||
}
|
||||
|
||||
|
||||
@@ -53,17 +53,17 @@ describe('renderer', () => {
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(9);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 5 : 0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 6 : 0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(12);
|
||||
|
||||
scene.remove(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(1);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(12);
|
||||
|
||||
ctx.resources.destroy();
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
|
||||
@@ -197,7 +197,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('createHistogramPyramid');
|
||||
|
||||
// printTexture(ctx, pyramidTex, 2)
|
||||
// printTextureImage(readTexture(ctx, pyramidTex), { scale: 0.75 });
|
||||
|
||||
//
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
|
||||
// console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim);
|
||||
// console.log('volumeData', volumeData);
|
||||
// console.log('at', readTexture(ctx, activeVoxelsTex));
|
||||
// printTextureImage(readTexture(ctx, activeVoxelsTex), { scale: 0.75 });
|
||||
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('calcActiveVoxels');
|
||||
|
||||
@@ -199,6 +199,10 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('createIsosurfaceBuffers');
|
||||
|
||||
// printTextureImage(readTexture(ctx, vertexTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
|
||||
// printTextureImage(readTexture(ctx, groupTexture, new Uint8Array(width * height * 4)), { scale: 0.75 });
|
||||
// printTextureImage(readTexture(ctx, normalTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
|
||||
|
||||
return { vertexTexture, groupTexture, normalTexture, vertexCount: count };
|
||||
}
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) {
|
||||
const ReadTextureName = 'read-texture';
|
||||
const ReadAlphaTextureName = 'read-alpha-texture';
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture) {
|
||||
export function readTexture<T extends Uint8Array | Float32Array | Int32Array = Uint8Array>(ctx: WebGLContext, texture: Texture, array?: T) {
|
||||
const { gl, resources } = ctx;
|
||||
if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
if (!array && texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
|
||||
|
||||
if (!ctx.namedFramebuffers[ReadTextureName]) {
|
||||
ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer();
|
||||
@@ -86,7 +86,7 @@ export function readTexture(ctx: WebGLContext, texture: Texture) {
|
||||
|
||||
const width = texture.getWidth();
|
||||
const height = texture.getHeight();
|
||||
const array = new Uint8Array(width * height * 4);
|
||||
if (!array) array = new Uint8Array(width * height * 4) as T;
|
||||
framebuffer.bind();
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
ctx.readPixels(0, 0, width, height, array);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../mol-util';
|
||||
@@ -167,6 +168,11 @@ export type GlobalUniformValues = Values<GlobalUniformSchema>
|
||||
|
||||
export const GlobalTextureSchema = {
|
||||
tDepth: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
|
||||
|
||||
// dpoit
|
||||
tDpoitDepth: TextureSpec('texture', 'rg', 'float', 'nearest'),
|
||||
tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest')
|
||||
} as const;
|
||||
export type GlobalTextureSchema = typeof GlobalTextureSchema
|
||||
export type GlobalTextureValues = Values<GlobalTextureSchema>
|
||||
@@ -236,7 +242,7 @@ export const TransparencySchema = {
|
||||
uTransparencyGridDim: UniformSpec('v3'),
|
||||
uTransparencyGridTransform: UniformSpec('v4'),
|
||||
tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
|
||||
dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
|
||||
dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance'])
|
||||
} as const;
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
@@ -327,4 +333,4 @@ export const BaseSchema = {
|
||||
invariantBoundingSphere: ValueSpec('sphere'),
|
||||
} as const;
|
||||
export type BaseSchema = typeof BaseSchema
|
||||
export type BaseValues = Values<BaseSchema>
|
||||
export type BaseValues = Values<BaseSchema>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,6 +8,7 @@ import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
import { TextureFilter } from '../webgl/texture';
|
||||
import { arrayMinMax } from '../../mol-util/array';
|
||||
|
||||
export function calculateTextureInfo(n: number, itemSize: number) {
|
||||
n = Math.max(n, 2); // observed issues with 1 pixel textures
|
||||
@@ -42,7 +43,8 @@ export function createTextureImage<T extends Uint8Array | Float32Array>(n: numbe
|
||||
const DefaultPrintImageOptions = {
|
||||
scale: 1,
|
||||
pixelated: false,
|
||||
id: 'molstar.debug.image'
|
||||
id: 'molstar.debug.image',
|
||||
normalize: false,
|
||||
};
|
||||
export type PrintImageOptions = typeof DefaultPrintImageOptions
|
||||
|
||||
@@ -58,7 +60,17 @@ export function printTextureImage(textureImage: TextureImage<any>, options: Part
|
||||
}
|
||||
}
|
||||
} else if (itemSize === 4) {
|
||||
data.set(array);
|
||||
if (options.normalize) {
|
||||
const [min, max] = arrayMinMax(array);
|
||||
for (let i = 0, il = width * height * 4; i < il; i += 4) {
|
||||
data[i] = ((array[i] - min) / (max - min)) * 255;
|
||||
data[i + 1] = ((array[i + 1] - min) / (max - min)) * 255;
|
||||
data[i + 2] = ((array[i + 2] - min) / (max - min)) * 255;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
data.set(array);
|
||||
}
|
||||
} else {
|
||||
console.warn(`itemSize '${itemSize}' not supported`);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { Viewport } from '../mol-canvas3d/camera/util';
|
||||
@@ -70,6 +71,9 @@ interface Renderer {
|
||||
renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
renderDpoitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
renderDpoitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => void
|
||||
renderDpoitVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
|
||||
|
||||
setProps: (props: Partial<RendererProps>) => void
|
||||
setViewport: (x: number, y: number, width: number, height: number) => void
|
||||
@@ -141,7 +145,7 @@ namespace Renderer {
|
||||
const enum Flag {
|
||||
None = 0,
|
||||
BlendedFront = 1,
|
||||
BlendedBack = 2
|
||||
BlendedBack = 2,
|
||||
}
|
||||
|
||||
const enum Mask {
|
||||
@@ -268,7 +272,7 @@ namespace Renderer {
|
||||
}
|
||||
|
||||
if (r.values.dGeometryType.ref.value === 'directVolume') {
|
||||
if (variant !== 'colorWboit' && variant !== 'colorBlended') {
|
||||
if (variant !== 'colorDpoit' && variant !== 'colorWboit' && variant !== 'colorBlended') {
|
||||
return; // only color supported
|
||||
}
|
||||
|
||||
@@ -602,6 +606,71 @@ namespace Renderer {
|
||||
if (isTimingMode) ctx.timer.markEnd('Renderer.renderWboitTransparent');
|
||||
};
|
||||
|
||||
const renderDpoitOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
||||
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitOpaque');
|
||||
state.disable(gl.BLEND);
|
||||
state.enable(gl.DEPTH_TEST);
|
||||
state.depthMask(true);
|
||||
|
||||
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
|
||||
|
||||
const { renderables } = group;
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
|
||||
// TODO: simplify, handle in renderable.state???
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
|
||||
renderObject(r, 'colorDpoit', Flag.None);
|
||||
}
|
||||
}
|
||||
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitOpaque');
|
||||
};
|
||||
|
||||
const renderDpoitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => {
|
||||
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitTransparent');
|
||||
|
||||
state.enable(gl.BLEND);
|
||||
|
||||
arrayMapUpsert(sharedTexturesList, 'tDpoitDepth', dpoitTextures.depth);
|
||||
arrayMapUpsert(sharedTexturesList, 'tDpoitFrontColor', dpoitTextures.frontColor);
|
||||
arrayMapUpsert(sharedTexturesList, 'tDpoitBackColor', dpoitTextures.backColor);
|
||||
|
||||
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
||||
|
||||
const { renderables } = group;
|
||||
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
|
||||
// TODO: simplify, handle in renderable.state???
|
||||
// uAlpha is updated in "render" so we need to recompute it here
|
||||
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
|
||||
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
|
||||
renderObject(r, 'colorDpoit', Flag.None);
|
||||
}
|
||||
}
|
||||
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitTransparent');
|
||||
};
|
||||
|
||||
const renderDpoitVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
|
||||
if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitVolume');
|
||||
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
state.enable(gl.BLEND);
|
||||
|
||||
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
|
||||
|
||||
const { renderables } = group;
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i];
|
||||
if (r.values.dGeometryType.ref.value === 'directVolume') {
|
||||
renderObject(r, 'colorDpoit', Flag.None);
|
||||
}
|
||||
}
|
||||
if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitVolume');
|
||||
};
|
||||
|
||||
return {
|
||||
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
|
||||
state.enable(gl.SCISSOR_TEST);
|
||||
@@ -645,6 +714,9 @@ namespace Renderer {
|
||||
renderBlendedVolume,
|
||||
renderWboitOpaque,
|
||||
renderWboitTransparent,
|
||||
renderDpoitOpaque,
|
||||
renderDpoitTransparent,
|
||||
renderDpoitVolume,
|
||||
|
||||
setProps: (props: Partial<RendererProps>) => {
|
||||
if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
|
||||
@@ -762,4 +834,4 @@ namespace Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
export { Renderer };
|
||||
export { Renderer };
|
||||
|
||||
@@ -45,8 +45,8 @@ function calculateBoundingSphere(renderables: GraphicsRenderable[], boundingSphe
|
||||
}
|
||||
|
||||
function renderableSort(a: GraphicsRenderable, b: GraphicsRenderable) {
|
||||
const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
|
||||
const drawProgramIdB = (b.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
|
||||
const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit') || a.getProgram('colorDpoit')).id;
|
||||
const drawProgramIdB = (b.getProgram('colorBlended') || b.getProgram('colorWboit') || b.getProgram('colorDpoit')).id;
|
||||
const materialIdA = a.materialId;
|
||||
const materialIdB = b.materialId;
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ import { texture3d_from_1d_trilinear } from './shader/chunks/texture3d-from-1d-t
|
||||
import { texture3d_from_2d_linear } from './shader/chunks/texture3d-from-2d-linear.glsl';
|
||||
import { texture3d_from_2d_nearest } from './shader/chunks/texture3d-from-2d-nearest.glsl';
|
||||
import { wboit_write } from './shader/chunks/wboit-write.glsl';
|
||||
import { dpoit_write } from './shader/chunks/dpoit-write.glsl';
|
||||
|
||||
const ShaderChunks: { [k: string]: string } = {
|
||||
apply_fog,
|
||||
@@ -99,7 +100,8 @@ const ShaderChunks: { [k: string]: string } = {
|
||||
texture3d_from_1d_trilinear,
|
||||
texture3d_from_2d_linear,
|
||||
texture3d_from_2d_nearest,
|
||||
wboit_write
|
||||
wboit_write,
|
||||
dpoit_write
|
||||
};
|
||||
|
||||
const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gm;
|
||||
|
||||
@@ -6,6 +6,7 @@ precision mediump sampler2D;
|
||||
#if defined(dVariant_skybox)
|
||||
uniform samplerCube tSkybox;
|
||||
uniform mat4 uViewDirectionProjectionInverse;
|
||||
uniform float uBlur;
|
||||
uniform float uOpacity;
|
||||
uniform float uSaturation;
|
||||
uniform float uLightness;
|
||||
@@ -49,7 +50,11 @@ vec3 lightenColor(vec3 c, float amount) {
|
||||
void main() {
|
||||
#if defined(dVariant_skybox)
|
||||
vec4 t = uViewDirectionProjectionInverse * vPosition;
|
||||
gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w));
|
||||
#ifdef enabledShaderTextureLod
|
||||
gl_FragColor = textureCubeLodEXT(tSkybox, normalize(t.xyz / t.w), uBlur * 8.0);
|
||||
#else
|
||||
gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w));
|
||||
#endif
|
||||
gl_FragColor.a = uOpacity;
|
||||
gl_FragColor.rgb = lightenColor(saturateColor(gl_FragColor.rgb, uSaturation), uLightness);
|
||||
#elif defined(dVariant_image)
|
||||
|
||||
20
src/mol-gl/shader/blend-back-dpoit.frag.ts
Normal file
20
src/mol-gl/shader/blend-back-dpoit.frag.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
export const blendBackDpoit_frag = `
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tDpoitBackColor;
|
||||
uniform vec2 uTexSize;
|
||||
|
||||
void main() {
|
||||
vec2 coords = gl_FragCoord.xy / uTexSize;
|
||||
gl_FragColor = texture2D(tDpoitBackColor, coords);
|
||||
if (gl_FragColor.a == 0.0) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -12,8 +12,19 @@ if (!uTransparentBackground) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
}
|
||||
} else {
|
||||
// pre-multiplied alpha expected for transparent background
|
||||
gl_FragColor.rgb *= fogAlpha;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
#if defined(dRenderVariant_colorDpoit)
|
||||
if (gl_FragColor.a < 1.0) {
|
||||
// transparent objects are blended with background color
|
||||
gl_FragColor.a = fogAlpha;
|
||||
} else {
|
||||
// opaque objects need to be pre-multiplied alpha
|
||||
gl_FragColor.rgb *= fogAlpha;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
}
|
||||
#else
|
||||
// pre-multiplied alpha expected for transparent background
|
||||
gl_FragColor.rgb *= fogAlpha;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
@@ -57,6 +57,7 @@ export const apply_light_color = `
|
||||
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
|
||||
|
||||
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
|
||||
outgoingLight = clamp(outgoingLight, 0.01, 0.99); // prevents black artifacts on specular highlight with transparent background
|
||||
|
||||
gl_FragColor = vec4(outgoingLight, color.a);
|
||||
#endif
|
||||
|
||||
@@ -86,7 +86,7 @@ export const assign_material_color = `
|
||||
// apply per-group transparency
|
||||
#if defined(dTransparency) && (defined(dRenderVariant_pick) || defined(dRenderVariant_color))
|
||||
float ta = 1.0 - vTransparency;
|
||||
if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better
|
||||
if (vTransparency < 0.09) ta = 1.0; // hard cutoff looks better
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
if (ta < uPickingAlphaThreshold)
|
||||
|
||||
@@ -39,6 +39,12 @@ uniform int uMarkingType;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(dRenderVariant_colorDpoit)
|
||||
#define MAX_DPOIT_DEPTH 99999.0 // NOTE constant also set in TypeScript
|
||||
uniform sampler2D tDpoitDepth;
|
||||
uniform sampler2D tDpoitFrontColor;
|
||||
#endif
|
||||
|
||||
varying vec3 vModelPosition;
|
||||
varying vec3 vViewPosition;
|
||||
|
||||
|
||||
@@ -44,10 +44,9 @@ varying vec3 vModelPosition;
|
||||
varying vec3 vViewPosition;
|
||||
|
||||
#if defined(noNonInstancedActiveAttribs)
|
||||
#define VertexID gl_VertexID // for testing
|
||||
// // int() is needed for some Safari versions
|
||||
// // see https://bugs.webkit.org/show_bug.cgi?id=244152
|
||||
// #define VertexID int(gl_VertexID)
|
||||
// int() is needed for some Safari versions
|
||||
// see https://bugs.webkit.org/show_bug.cgi?id=244152
|
||||
#define VertexID int(gl_VertexID)
|
||||
#else
|
||||
attribute float aVertex;
|
||||
#define VertexID int(aVertex)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const common = `
|
||||
// TODO find a better place for these convenience defines
|
||||
|
||||
#if defined(dRenderVariant_colorBlended) || defined(dRenderVariant_colorWboit)
|
||||
#if defined(dRenderVariant_colorBlended) || defined(dRenderVariant_colorWboit) || defined(dRenderVariant_colorDpoit)
|
||||
#define dRenderVariant_color
|
||||
#endif
|
||||
|
||||
|
||||
70
src/mol-gl/shader/chunks/dpoit-write.glsl.ts
Normal file
70
src/mol-gl/shader/chunks/dpoit-write.glsl.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
export const dpoit_write = `
|
||||
#if defined(dRenderVariant_colorDpoit)
|
||||
if (uRenderMask == MaskOpaque) {
|
||||
if (preFogAlpha < 1.0) {
|
||||
discard;
|
||||
}
|
||||
} else if (uRenderMask == MaskTransparent) {
|
||||
// the 'fragmentDepth > 0.99' check is to handle precision issues with packed depth
|
||||
vec2 coords = gl_FragCoord.xy / uDrawingBufferSize;
|
||||
if (preFogAlpha != 1.0 && (fragmentDepth < getDepth(coords) || fragmentDepth > 0.99)) {
|
||||
#ifdef dTransparentBackfaces_off
|
||||
if (interior) discard;
|
||||
#endif
|
||||
|
||||
// adapted from https://github.com/tsherif/webgl2examples
|
||||
// The MIT License, Copyright 2017 Tarek Sherif, Shuai Shao
|
||||
|
||||
vec2 lastDepth = texture2D(tDpoitDepth, coords).rg;
|
||||
vec4 lastFrontColor = texture2D(tDpoitFrontColor, coords);
|
||||
|
||||
vec4 fragColor = gl_FragColor;
|
||||
|
||||
// depth value always increases
|
||||
// so we can use MAX blend equation
|
||||
gl_FragData[2].rg = vec2(-MAX_DPOIT_DEPTH);
|
||||
|
||||
// front color always increases
|
||||
// so we can use MAX blend equation
|
||||
gl_FragColor = lastFrontColor;
|
||||
|
||||
// back color is separately blend afterwards each pass
|
||||
gl_FragData[1] = vec4(0.0);
|
||||
|
||||
float nearestDepth = -lastDepth.x;
|
||||
float furthestDepth = lastDepth.y;
|
||||
float alphaMultiplier = 1.0 - lastFrontColor.a;
|
||||
|
||||
if (fragmentDepth < nearestDepth || fragmentDepth > furthestDepth) {
|
||||
// Skip this depth since it's been peeled.
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragmentDepth > nearestDepth && fragmentDepth < furthestDepth) {
|
||||
// This needs to be peeled.
|
||||
// The ones remaining after MAX blended for
|
||||
// all need-to-peel will be peeled next pass.
|
||||
gl_FragData[2].rg = vec2(-fragmentDepth, fragmentDepth);
|
||||
return;
|
||||
}
|
||||
|
||||
// write to back and front color buffer
|
||||
if (fragmentDepth == nearestDepth) {
|
||||
gl_FragColor.rgb += fragColor.rgb * fragColor.a * alphaMultiplier;
|
||||
gl_FragColor.a = 1.0 - alphaMultiplier * (1.0 - fragColor.a);
|
||||
} else {
|
||||
gl_FragData[1] += fragColor;
|
||||
}
|
||||
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
`;
|
||||
@@ -109,14 +109,14 @@ void main() {
|
||||
|
||||
vec3 vViewPosition = vModelPosition + intersection.x * rayDir;
|
||||
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
|
||||
gl_FragDepthEXT = calcDepth(vViewPosition);
|
||||
float fragmentDepth = calcDepth(vViewPosition);
|
||||
|
||||
if (fragmentDepth < 0.0) discard;
|
||||
if (fragmentDepth > 1.0) discard;
|
||||
|
||||
gl_FragDepthEXT = fragmentDepth;
|
||||
|
||||
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
|
||||
|
||||
if (gl_FragDepthEXT < 0.0) discard;
|
||||
if (gl_FragDepthEXT > 1.0) discard;
|
||||
|
||||
float fragmentDepth = gl_FragDepthEXT;
|
||||
#include assign_material_color
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
@@ -142,6 +142,7 @@ void main() {
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -356,4 +356,4 @@ void main() {
|
||||
float preFogAlpha = clamp(preFogAlphaBlended, 0.0, 1.0);
|
||||
#include wboit_write
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
17
src/mol-gl/shader/evaluate-dpoit.frag.ts
Normal file
17
src/mol-gl/shader/evaluate-dpoit.frag.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
export const evaluateDpoit_frag = `
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tDpoitFrontColor;
|
||||
uniform vec2 uTexSize;
|
||||
|
||||
void main() {
|
||||
vec2 coords = gl_FragCoord.xy / uTexSize;
|
||||
gl_FragColor = texture2D(tDpoitFrontColor, coords);
|
||||
}
|
||||
`;
|
||||
@@ -159,6 +159,7 @@ void main() {
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -39,6 +39,7 @@ void main(){
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -62,6 +62,7 @@ void main() {
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -55,6 +55,7 @@ void main(){
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -70,17 +70,17 @@ void main(void){
|
||||
}
|
||||
|
||||
vec3 vViewPosition = cameraPos;
|
||||
gl_FragDepthEXT = calcDepth(vViewPosition);
|
||||
if (!flag && gl_FragDepthEXT >= 0.0) {
|
||||
gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius);
|
||||
float fragmentDepth = calcDepth(vViewPosition);
|
||||
if (!flag && fragmentDepth >= 0.0) {
|
||||
fragmentDepth = 0.0 + (0.0000001 / vRadius);
|
||||
}
|
||||
|
||||
if (fragmentDepth < 0.0) discard;
|
||||
if (fragmentDepth > 1.0) discard;
|
||||
|
||||
gl_FragDepthEXT = fragmentDepth;
|
||||
|
||||
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
|
||||
|
||||
if (gl_FragDepthEXT < 0.0) discard;
|
||||
if (gl_FragDepthEXT > 1.0) discard;
|
||||
|
||||
float fragmentDepth = gl_FragDepthEXT;
|
||||
#include assign_material_color
|
||||
|
||||
#if defined(dRenderVariant_pick)
|
||||
@@ -105,6 +105,7 @@ void main(void){
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -83,6 +83,7 @@ void main(){
|
||||
#include apply_marker_color
|
||||
#include apply_fog
|
||||
#include wboit_write
|
||||
#include dpoit_write
|
||||
#endif
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { createAttributeBuffers, ElementsBuffer, AttributeKind } from './buffer';
|
||||
@@ -49,11 +50,12 @@ export interface RenderItem<T extends string> {
|
||||
|
||||
//
|
||||
|
||||
const GraphicsRenderVariant = { colorBlended: '', colorWboit: '', pick: '', depth: '', marking: '' };
|
||||
const GraphicsRenderVariant = { colorBlended: '', colorWboit: '', colorDpoit: '', pick: '', depth: '', marking: '' };
|
||||
export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariant
|
||||
export const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
|
||||
export const GraphicsRenderVariantsBlended = GraphicsRenderVariants.filter(v => v !== 'colorWboit');
|
||||
export const GraphicsRenderVariantsWboit = GraphicsRenderVariants.filter(v => v !== 'colorBlended');
|
||||
export const GraphicsRenderVariantsBlended = GraphicsRenderVariants.filter(v => !['colorWboit', 'colorDpoit'].includes(v));
|
||||
export const GraphicsRenderVariantsWboit = GraphicsRenderVariants.filter(v => !['colorBlended', 'colorDpoit'].includes(v));
|
||||
export const GraphicsRenderVariantsDpoit = GraphicsRenderVariants.filter(v => !['colorWboit', 'colorBlended'].includes(v));
|
||||
|
||||
const ComputeRenderVariant = { compute: '' };
|
||||
export type ComputeRenderVariant = keyof typeof ComputeRenderVariant
|
||||
@@ -367,4 +369,4 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from './context';
|
||||
@@ -31,7 +32,7 @@ export type TextureKindValue = {
|
||||
export type TextureValueType = ValueOf<TextureKindValue>
|
||||
export type TextureKind = keyof TextureKindValue
|
||||
export type TextureType = 'ubyte' | 'ushort' | 'float' | 'fp16' | 'int'
|
||||
export type TextureFormat = 'alpha' | 'rgb' | 'rgba' | 'depth'
|
||||
export type TextureFormat = 'alpha' | 'rg' | 'rgb' | 'rgba' | 'depth'
|
||||
/** Numbers are shortcuts for color attachment */
|
||||
export type TextureAttachment = 'depth' | 'stencil' | 'color0' | 'color1' | 'color2' | 'color3' | 'color4' | 'color5' | 'color6' | 'color7' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
export type TextureFilter = 'nearest' | 'linear'
|
||||
@@ -63,6 +64,10 @@ export function getFormat(gl: GLRenderingContext, format: TextureFormat, type: T
|
||||
case 'rgb':
|
||||
if (isWebGL2(gl) && type === 'int') return gl.RGB_INTEGER;
|
||||
return gl.RGB;
|
||||
case 'rg':
|
||||
if (isWebGL2(gl) && type === 'float') return gl.RG;
|
||||
else if (isWebGL2(gl) && type === 'int') return gl.RG_INTEGER;
|
||||
else throw new Error('texture format "rg" requires webgl2 and type "float" or int"');
|
||||
case 'rgba':
|
||||
if (isWebGL2(gl) && type === 'int') return gl.RGBA_INTEGER;
|
||||
return gl.RGBA;
|
||||
@@ -80,6 +85,13 @@ export function getInternalFormat(gl: GLRenderingContext, format: TextureFormat,
|
||||
case 'fp16': return gl.R16F;
|
||||
case 'int': return gl.R32I;
|
||||
}
|
||||
case 'rg':
|
||||
switch (type) {
|
||||
case 'ubyte': return gl.RG;
|
||||
case 'float': return gl.RG32F;
|
||||
case 'fp16': return gl.RG16F;
|
||||
case 'int': return gl.RG32I;
|
||||
}
|
||||
case 'rgb':
|
||||
switch (type) {
|
||||
case 'ubyte': return gl.RGB;
|
||||
@@ -112,6 +124,7 @@ function getByteCount(format: TextureFormat, type: TextureType, width: number, h
|
||||
function getFormatSize(format: TextureFormat) {
|
||||
switch (format) {
|
||||
case 'alpha': return 1;
|
||||
case 'rg': return 2;
|
||||
case 'rgb': return 3;
|
||||
case 'rgba': return 4;
|
||||
case 'depth': return 4;
|
||||
|
||||
@@ -264,28 +264,35 @@ export namespace ArrayEncoding {
|
||||
return false;
|
||||
}
|
||||
|
||||
function packingSize(data: Int32Array, upperLimit: number) {
|
||||
function packingSizeUnsigned(data: Int32Array, upperLimit: number) {
|
||||
let size = 0;
|
||||
for (let i = 0, n = data.length; i < n; i++) {
|
||||
size += (data[i] / upperLimit) | 0;
|
||||
}
|
||||
size += data.length;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
function packingSizeSigned(data: Int32Array, upperLimit: number) {
|
||||
const lowerLimit = -upperLimit - 1;
|
||||
let size = 0;
|
||||
for (let i = 0, n = data.length; i < n; i++) {
|
||||
const value = data[i];
|
||||
if (value === 0) {
|
||||
size += 1;
|
||||
} else if (value > 0) {
|
||||
size += Math.ceil(value / upperLimit);
|
||||
if (value % upperLimit === 0) size += 1;
|
||||
if (value >= 0) {
|
||||
size += (value / upperLimit) | 0;
|
||||
} else {
|
||||
size += Math.ceil(value / lowerLimit);
|
||||
if (value % lowerLimit === 0) size += 1;
|
||||
size += (value / lowerLimit) | 0;
|
||||
}
|
||||
}
|
||||
size += data.length;
|
||||
return size;
|
||||
}
|
||||
|
||||
function determinePacking(data: Int32Array): { isSigned: boolean, size: number, bytesPerElement: number } {
|
||||
const signed = isSigned(data);
|
||||
const size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF);
|
||||
const size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF);
|
||||
const size8 = signed ? packingSizeSigned(data, 0x7F) : packingSizeUnsigned(data, 0xFF);
|
||||
const size16 = signed ? packingSizeSigned(data, 0x7FFF) : packingSizeUnsigned(data, 0xFFFF);
|
||||
|
||||
if (data.length * 4 < size16 * 2) {
|
||||
// 4 byte packing is the most effective
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.360, IHM 1.17, MA 1.4.2.
|
||||
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.360, IHM 1.17, MA 1.4.2.
|
||||
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.360, IHM 1.17, MA 1.4.2.
|
||||
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.362, IHM 1.17, MA 1.4.3.
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { GridLookup3D } from '../../geometry';
|
||||
@@ -24,9 +25,17 @@ describe('GridLookup3d', () => {
|
||||
expect(r.count).toBe(1);
|
||||
expect(r.indices[0]).toBe(0);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 1);
|
||||
expect(r.count).toBe(1);
|
||||
expect(r.indices[0]).toBe(0);
|
||||
|
||||
r = grid.find(0, 0, 0, 1);
|
||||
expect(r.count).toBe(3);
|
||||
expect(sortArray(r.indices)).toEqual([0, 1, 2]);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 3);
|
||||
expect(r.count).toBe(3);
|
||||
expect(sortArray(r.indices)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it('radius', () => {
|
||||
@@ -38,9 +47,17 @@ describe('GridLookup3d', () => {
|
||||
expect(r.count).toBe(1);
|
||||
expect(r.indices[0]).toBe(0);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 1);
|
||||
expect(r.count).toBe(1);
|
||||
expect(r.indices[0]).toBe(0);
|
||||
|
||||
r = grid.find(0, 0, 0, 0.5);
|
||||
expect(r.count).toBe(2);
|
||||
expect(sortArray(r.indices)).toEqual([0, 1]);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 3);
|
||||
expect(r.count).toBe(3);
|
||||
expect(sortArray(r.indices)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it('indexed', () => {
|
||||
@@ -51,8 +68,15 @@ describe('GridLookup3d', () => {
|
||||
let r = grid.find(0, 0, 0, 0);
|
||||
expect(r.count).toBe(0);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 1);
|
||||
expect(r.count).toBe(1);
|
||||
|
||||
r = grid.find(0, 0, 0, 0.5);
|
||||
expect(r.count).toBe(1);
|
||||
expect(sortArray(r.indices)).toEqual([0]);
|
||||
|
||||
r = grid.nearest(0, 0, 0, 3);
|
||||
expect(r.count).toBe(1);
|
||||
expect(sortArray(r.indices)).toEqual([0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -204,7 +204,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
|
||||
render(texture, false);
|
||||
}
|
||||
|
||||
// printTexture(webgl, minDistTex, 0.75);
|
||||
// printTextureImage(readTexture(webgl, minDistTex), { scale: 0.75 });
|
||||
|
||||
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius };
|
||||
}
|
||||
|
||||
@@ -41,8 +41,9 @@ export namespace Result {
|
||||
export interface Lookup3D<T = number> {
|
||||
// The result is mutated with each call to find.
|
||||
find(x: number, y: number, z: number, radius: number, result?: Result<T>): Result<T>,
|
||||
nearest(x: number, y: number, z: number, k: number, stopIf?: Function, result?: Result<T>): Result<T>,
|
||||
check(x: number, y: number, z: number, radius: number): boolean,
|
||||
readonly boundary: { readonly box: Box3D, readonly sphere: Sphere3D }
|
||||
/** transient result */
|
||||
readonly result: Result<T>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { Result, Lookup3D } from './common';
|
||||
@@ -12,6 +13,7 @@ import { PositionData } from '../common';
|
||||
import { Vec3 } from '../../linear-algebra';
|
||||
import { OrderedSet } from '../../../mol-data/int';
|
||||
import { Boundary } from '../boundary';
|
||||
import { FibonacciHeap } from '../../../mol-util/fibonacci-heap';
|
||||
|
||||
interface GridLookup3D<T = number> extends Lookup3D<T> {
|
||||
readonly buckets: { readonly offset: ArrayLike<number>, readonly count: ArrayLike<number>, readonly array: ArrayLike<number> }
|
||||
@@ -40,6 +42,17 @@ class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> {
|
||||
return ret;
|
||||
}
|
||||
|
||||
nearest(x: number, y: number, z: number, k: number = 1, stopIf?: Function, result?: Result<T>): Result<T> {
|
||||
this.ctx.x = x;
|
||||
this.ctx.y = y;
|
||||
this.ctx.z = z;
|
||||
this.ctx.k = k;
|
||||
this.ctx.stopIf = stopIf;
|
||||
const ret = result ?? this.result;
|
||||
queryNearest(this.ctx, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
check(x: number, y: number, z: number, radius: number): boolean {
|
||||
this.ctx.x = x;
|
||||
this.ctx.y = y;
|
||||
@@ -221,12 +234,14 @@ interface QueryContext {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
k: number,
|
||||
stopIf?: Function,
|
||||
radius: number,
|
||||
isCheck: boolean
|
||||
}
|
||||
|
||||
function createContext(grid: Grid3D): QueryContext {
|
||||
return { grid, x: 0.1, y: 0.1, z: 0.1, radius: 0.1, isCheck: false };
|
||||
return { grid, x: 0.1, y: 0.1, z: 0.1, k: 1, stopIf: undefined, radius: 0.1, isCheck: false };
|
||||
}
|
||||
|
||||
function query<T extends number = number>(ctx: QueryContext, result: Result<T>): boolean {
|
||||
@@ -277,4 +292,152 @@ function query<T extends number = number>(ctx: QueryContext, result: Result<T>):
|
||||
}
|
||||
}
|
||||
return result.count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
const tmpDirVec = Vec3();
|
||||
const tmpVec = Vec3();
|
||||
const tmpSetG = new Set<number>();
|
||||
const tmpSetG2 = new Set<number>();
|
||||
const tmpArrG1 = [0.1];
|
||||
const tmpArrG2 = [0.1];
|
||||
const tmpArrG3 = [0.1];
|
||||
const tmpHeapG = new FibonacciHeap();
|
||||
function queryNearest<T extends number = number>(ctx: QueryContext, result: Result<T>): boolean {
|
||||
const { min, expandedBox: box, boundingSphere: { center }, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices, radius }, delta, maxRadius } = ctx.grid;
|
||||
const { x, y, z, k, stopIf } = ctx;
|
||||
const indicesCount = OrderedSet.size(indices);
|
||||
Result.reset(result);
|
||||
if (indicesCount === 0 || k <= 0) return false;
|
||||
let gX, gY, gZ, stop = false, gCount = 1, expandGrid = true, nextGCount = 0, arrG = tmpArrG1, nextArrG = tmpArrG2, maxRange = 0, expandRange = true, gridId: number, gridPointsFinished = false;
|
||||
const expandedArrG = tmpArrG3, sqMaxRadius = maxRadius * maxRadius;
|
||||
arrG.length = 0;
|
||||
expandedArrG.length = 0;
|
||||
tmpSetG.clear();
|
||||
tmpHeapG.clear();
|
||||
Vec3.set(tmpVec, x, y, z);
|
||||
if (!Box3D.containsVec3(box, tmpVec)) {
|
||||
// intersect ray pointing to box center
|
||||
Box3D.nearestIntersectionWithRay(tmpVec, box, tmpVec, Vec3.normalize(tmpDirVec, Vec3.sub(tmpDirVec, center, tmpVec)));
|
||||
gX = Math.max(0, Math.min(sX - 1, Math.floor((tmpVec[0] - min[0]) / delta[0])));
|
||||
gY = Math.max(0, Math.min(sY - 1, Math.floor((tmpVec[1] - min[1]) / delta[1])));
|
||||
gZ = Math.max(0, Math.min(sZ - 1, Math.floor((tmpVec[2] - min[2]) / delta[2])));
|
||||
} else {
|
||||
gX = Math.floor((x - min[0]) / delta[0]);
|
||||
gY = Math.floor((y - min[1]) / delta[1]);
|
||||
gZ = Math.floor((z - min[2]) / delta[2]);
|
||||
}
|
||||
const dX = maxRadius !== 0 ? Math.max(1, Math.min(sX - 1, Math.ceil(maxRadius / delta[0]))) : 1;
|
||||
const dY = maxRadius !== 0 ? Math.max(1, Math.min(sY - 1, Math.ceil(maxRadius / delta[1]))) : 1;
|
||||
const dZ = maxRadius !== 0 ? Math.max(1, Math.min(sZ - 1, Math.ceil(maxRadius / delta[2]))) : 1;
|
||||
arrG.push(gX, gY, gZ, (((gX * sY) + gY) * sZ) + gZ);
|
||||
while (result.count < indicesCount) {
|
||||
const arrGLen = gCount * 4;
|
||||
for (let ig = 0; ig < arrGLen; ig += 4) {
|
||||
gridId = arrG[ig + 3];
|
||||
if (!tmpSetG.has(gridId)) {
|
||||
tmpSetG.add(gridId);
|
||||
gridPointsFinished = tmpSetG.size >= grid.length;
|
||||
const bucketIdx = grid[gridId];
|
||||
if (bucketIdx !== 0) {
|
||||
const _maxRange = maxRange;
|
||||
const ki = bucketIdx - 1;
|
||||
const offset = bucketOffset[ki];
|
||||
const count = bucketCounts[ki];
|
||||
const end = offset + count;
|
||||
for (let i = offset; i < end; i++) {
|
||||
const bIdx = bucketArray[i];
|
||||
const idx = OrderedSet.getAt(indices, bIdx);
|
||||
const dx = px[idx] - x;
|
||||
const dy = py[idx] - y;
|
||||
const dz = pz[idx] - z;
|
||||
let distSq = dx * dx + dy * dy + dz * dz;
|
||||
if (maxRadius !== 0) {
|
||||
const r = radius![idx];
|
||||
distSq -= r * r;
|
||||
}
|
||||
if (expandRange && distSq > maxRange) {
|
||||
maxRange = distSq;
|
||||
}
|
||||
tmpHeapG.insert(distSq, bIdx);
|
||||
}
|
||||
if (_maxRange < maxRange) expandRange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// find next grid points
|
||||
nextArrG.length = 0;
|
||||
nextGCount = 0;
|
||||
tmpSetG2.clear();
|
||||
for (let ig = 0; ig < arrGLen; ig += 4) {
|
||||
gX = arrG[ig];
|
||||
gY = arrG[ig + 1];
|
||||
gZ = arrG[ig + 2];
|
||||
// fill grid points array with valid adiacent positions
|
||||
for (let ix = -dX; ix <= dX; ix++) {
|
||||
const xPos = gX + ix;
|
||||
if (xPos < 0 || xPos >= sX) continue;
|
||||
for (let iy = -dY; iy <= dY; iy++) {
|
||||
const yPos = gY + iy;
|
||||
if (yPos < 0 || yPos >= sY) continue;
|
||||
for (let iz = -dZ; iz <= dZ; iz++) {
|
||||
const zPos = gZ + iz;
|
||||
if (zPos < 0 || zPos >= sZ) continue;
|
||||
gridId = (((xPos * sY) + yPos) * sZ) + zPos;
|
||||
if (tmpSetG2.has(gridId)) continue; // already scanned
|
||||
tmpSetG2.add(gridId);
|
||||
if (tmpSetG.has(gridId)) continue; // already visited
|
||||
if (!expandGrid) {
|
||||
const xP = min[0] + xPos * delta[0] - x;
|
||||
const yP = min[1] + yPos * delta[1] - y;
|
||||
const zP = min[2] + zPos * delta[2] - z;
|
||||
const distSqG = (xP * xP) + (yP * yP) + (zP * zP) - sqMaxRadius; // is sqMaxRadius necessary?
|
||||
if (distSqG > maxRange) {
|
||||
expandedArrG.push(xPos, yPos, zPos, gridId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
nextArrG.push(xPos, yPos, zPos, gridId);
|
||||
nextGCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
expandGrid = false;
|
||||
if (nextGCount === 0) {
|
||||
if (k === 1) {
|
||||
const node = tmpHeapG.findMinimum();
|
||||
if (node) {
|
||||
const { key: squaredDistance, value: index } = node!;
|
||||
// const squaredDistance = node!.key, index = node!.value;
|
||||
Result.add(result, index as number, squaredDistance as number);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
while (!tmpHeapG.isEmpty() && (gridPointsFinished || tmpHeapG.findMinimum()!.key as number <= maxRange) && result.count < k) {
|
||||
const node = tmpHeapG.extractMinimum();
|
||||
const squaredDistance = node!.key, index = node!.value;
|
||||
Result.add(result, index as number, squaredDistance as number);
|
||||
if (stopIf && !stop) {
|
||||
stop = stopIf(index, squaredDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.count >= k || stop || result.count >= indicesCount) return result.count > 0;
|
||||
expandGrid = true;
|
||||
expandRange = true;
|
||||
if (expandedArrG.length > 0) {
|
||||
for (let i = 0, l = expandedArrG.length; i < l; i++) {
|
||||
arrG.push(expandedArrG[i]);
|
||||
}
|
||||
expandedArrG.length = 0;
|
||||
gCount = arrG.length;
|
||||
}
|
||||
} else {
|
||||
const tmp = arrG;
|
||||
arrG = nextArrG;
|
||||
nextArrG = tmp;
|
||||
gCount = nextGCount;
|
||||
}
|
||||
}
|
||||
return result.count > 0;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,48 @@ namespace Box3D {
|
||||
a.max[2] < b.min[2] || a.min[2] > b.max[2]
|
||||
);
|
||||
}
|
||||
|
||||
// const tmpTransformV = Vec3();
|
||||
export function nearestIntersectionWithRay(out: Vec3, box: Box3D, origin: Vec3, dir: Vec3): Vec3 {
|
||||
const [minX, minY, minZ] = box.min;
|
||||
const [maxX, maxY, maxZ] = box.max;
|
||||
const [x, y, z] = origin;
|
||||
const invDirX = 1.0 / dir[0];
|
||||
const invDirY = 1.0 / dir[1];
|
||||
const invDirZ = 1.0 / dir[2];
|
||||
let tmin, tmax, tymin, tymax, tzmin, tzmax;
|
||||
if (invDirX >= 0) {
|
||||
tmin = (minX - x) * invDirX;
|
||||
tmax = (maxX - x) * invDirX;
|
||||
} else {
|
||||
tmin = (maxX - x) * invDirX;
|
||||
tmax = (minX - x) * invDirX;
|
||||
}
|
||||
if (invDirY >= 0) {
|
||||
tymin = (minY - y) * invDirY;
|
||||
tymax = (maxY - y) * invDirY;
|
||||
} else {
|
||||
tymin = (maxY - y) * invDirY;
|
||||
tymax = (minY - y) * invDirY;
|
||||
}
|
||||
if (invDirZ >= 0) {
|
||||
tzmin = (minZ - z) * invDirZ;
|
||||
tzmax = (maxZ - z) * invDirZ;
|
||||
} else {
|
||||
tzmin = (maxZ - z) * invDirZ;
|
||||
tzmax = (minZ - z) * invDirZ;
|
||||
}
|
||||
if (tymin > tmin)
|
||||
tmin = tymin;
|
||||
if (tymax < tmax)
|
||||
tmax = tymax;
|
||||
if (tzmin > tmin)
|
||||
tmin = tzmin;
|
||||
if (tzmax < tmax)
|
||||
tmax = tzmax;
|
||||
Vec3.scale(out, dir, tmin);
|
||||
return Vec3.set(out, out[0] + x, out[1] + y, out[2] + z);
|
||||
}
|
||||
}
|
||||
|
||||
export { Box3D };
|
||||
export { Box3D };
|
||||
|
||||
@@ -277,6 +277,12 @@ namespace Sphere3D {
|
||||
export function distance(a: Sphere3D, b: Sphere3D) {
|
||||
return Vec3.distance(a.center, b.center) - a.radius + b.radius;
|
||||
}
|
||||
|
||||
/** Get the distance of v from sphere. If negative, v is inside sphere */
|
||||
export function distanceToVec(sphere: Sphere3D, v: Vec3): number {
|
||||
const { center, radius } = sphere;
|
||||
return Vec3.distance(v, center) - radius;
|
||||
}
|
||||
}
|
||||
|
||||
export { Sphere3D };
|
||||
export { Sphere3D };
|
||||
|
||||
@@ -35,7 +35,7 @@ function Vec3() {
|
||||
|
||||
namespace Vec3 {
|
||||
export function zero(): Vec3 {
|
||||
const out = [0.1, 0.0, 0.0];
|
||||
const out = [0.1, 0.0, 0.0]; // ensure backing array of type double
|
||||
out[0] = 0;
|
||||
return out as any;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ const DnaAtomIdsList = [
|
||||
/** Used to reduce false positives for atom name-based type guessing */
|
||||
const NonPolymerNames = new Set([
|
||||
'FMN', 'NCN', 'FNS', 'FMA', 'ATP', 'ADP', 'AMP', 'GTP', 'GDP', 'GMP', // Mononucleotides
|
||||
'LIG'
|
||||
]);
|
||||
|
||||
const StandardComponents = (function () {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function getAtomSiteTemplate(data: string, count: number) {
|
||||
};
|
||||
}
|
||||
|
||||
export function getAtomSite(sites: AtomSiteTemplate, hasTer: boolean): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
|
||||
export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
|
||||
const pdbx_PDB_model_num = CifField.ofStrings(sites.pdbx_PDB_model_num);
|
||||
const auth_asym_id = CifField.ofTokens(sites.auth_asym_id);
|
||||
const auth_seq_id = CifField.ofTokens(sites.auth_seq_id);
|
||||
@@ -67,21 +67,17 @@ export function getAtomSite(sites: AtomSiteTemplate, hasTer: boolean): { [K in k
|
||||
const seqId = auth_seq_id.int(i);
|
||||
let atomId = auth_atom_id.str(i);
|
||||
|
||||
let asymIdChanged = false;
|
||||
|
||||
if (modelNum !== currModelNum) {
|
||||
asymIdCounts.clear();
|
||||
atomIdCounts.clear();
|
||||
currModelNum = modelNum;
|
||||
currAsymId = asymId;
|
||||
currSeqId = seqId;
|
||||
asymIdChanged = true;
|
||||
currLabelAsymId = asymId;
|
||||
} else if (currAsymId !== asymId) {
|
||||
atomIdCounts.clear();
|
||||
currAsymId = asymId;
|
||||
currSeqId = seqId;
|
||||
asymIdChanged = true;
|
||||
currLabelAsymId = asymId;
|
||||
} else if (currSeqId !== seqId) {
|
||||
atomIdCounts.clear();
|
||||
@@ -91,7 +87,7 @@ export function getAtomSite(sites: AtomSiteTemplate, hasTer: boolean): { [K in k
|
||||
if (asymIdCounts.has(asymId)) {
|
||||
// only change the chains name if there are TER records
|
||||
// otherwise assume repeated chain name use is from interleaved chains
|
||||
if (hasTer && asymIdChanged) {
|
||||
if (terIndices.has(i)) {
|
||||
const asymIdCount = asymIdCounts.get(asymId)! + 1;
|
||||
asymIdCounts.set(asymId, asymIdCount);
|
||||
currLabelAsymId = `${asymId}_${asymIdCount}`;
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
|
||||
let modelNum = 0, modelStr = '';
|
||||
let conectRange: [number, number] | undefined = undefined;
|
||||
let hasTer = false;
|
||||
const terIndices = new Set<number>();
|
||||
|
||||
for (let i = 0, _i = lines.count; i < _i; i++) {
|
||||
let s = indices[2 * i], e = indices[2 * i + 1];
|
||||
@@ -164,7 +164,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
break;
|
||||
case 'T':
|
||||
if (substringStartsWith(data, s, e, 'TER')) {
|
||||
hasTer = true;
|
||||
terIndices.add(atomSite.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
|
||||
atomSite.label_entity_id[i] = entityBuilder.getEntityId(compId, moleculeType, asymIds.value(i));
|
||||
}
|
||||
|
||||
const atom_site = getAtomSite(atomSite, hasTer);
|
||||
const atom_site = getAtomSite(atomSite, terIndices);
|
||||
if (!isPdbqt) delete atom_site.partial_charge;
|
||||
|
||||
if (conectRange) {
|
||||
|
||||
@@ -65,7 +65,7 @@ export namespace ComponentBond {
|
||||
return e;
|
||||
}
|
||||
|
||||
const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount } = data;
|
||||
const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount, pdbx_ordinal } = data;
|
||||
|
||||
let entry = addEntry(comp_id.value(0)!);
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
@@ -74,6 +74,7 @@ export namespace ComponentBond {
|
||||
const nameB = atom_id_2.value(i)!;
|
||||
const order = value_order.value(i)!;
|
||||
const aromatic = pdbx_aromatic_flag.value(i) === 'y';
|
||||
const key = pdbx_ordinal.value(i);
|
||||
|
||||
if (entry.id !== id) {
|
||||
entry = addEntry(id);
|
||||
@@ -89,29 +90,29 @@ export namespace ComponentBond {
|
||||
case 'quad': ord = 4; break;
|
||||
}
|
||||
|
||||
entry.add(nameA, nameB, ord, flags);
|
||||
entry.add(nameA, nameB, ord, flags, key);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export class Entry {
|
||||
readonly map: Map<string, Map<string, { order: number, flags: number }>> = new Map();
|
||||
readonly map: Map<string, Map<string, { order: number, flags: number, key: number }>> = new Map();
|
||||
|
||||
add(a: string, b: string, order: number, flags: number, swap = true) {
|
||||
add(a: string, b: string, order: number, flags: number, key: number, swap = true) {
|
||||
const e = this.map.get(a);
|
||||
if (e !== void 0) {
|
||||
const f = e.get(b);
|
||||
if (f === void 0) {
|
||||
e.set(b, { order, flags });
|
||||
e.set(b, { order, flags, key });
|
||||
}
|
||||
} else {
|
||||
const map = new Map<string, { order: number, flags: number }>();
|
||||
map.set(b, { order, flags });
|
||||
const map = new Map<string, { order: number, flags: number, key: number }>();
|
||||
map.set(b, { order, flags, key });
|
||||
this.map.set(a, map);
|
||||
}
|
||||
|
||||
if (swap) this.add(b, a, order, flags, false);
|
||||
if (swap) this.add(b, a, order, flags, key, false);
|
||||
}
|
||||
|
||||
constructor(public readonly id: string) { }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 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 { CubeFile } from '../../mol-io/reader/cube/parser';
|
||||
@@ -11,8 +12,9 @@ import { Task } from '../../mol-task';
|
||||
import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
|
||||
import { ModelFormat } from '../format';
|
||||
import { CustomProperties } from '../../mol-model/custom-property';
|
||||
import { clamp } from '../../mol-math/interpolate';
|
||||
|
||||
export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string, entryId?: string }): Task<Volume> {
|
||||
export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string, entryId?: string, clamp?: { min: number, max: number } }): Task<Volume> {
|
||||
return Task.create<Volume>('Create Volume', async () => {
|
||||
const { header, values: sourceValues } = source;
|
||||
const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
|
||||
@@ -24,6 +26,7 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
|
||||
// get every nth value from the source values
|
||||
const [h, k, l] = header.dim;
|
||||
const nth = (params?.dataIndex || 0) + 1;
|
||||
const { min, max } = params?.clamp || { min: -Infinity, max: Infinity };
|
||||
|
||||
let o = 0, s = 0;
|
||||
|
||||
@@ -31,7 +34,7 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
|
||||
for (let u = 0; u < h; u++) {
|
||||
for (let v = 0; v < k; v++) {
|
||||
for (let w = 0; w < l; w++) {
|
||||
values[o++] = sourceValues[s];
|
||||
values[o++] = clamp(sourceValues[s], min, max);
|
||||
s += nth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -71,12 +71,12 @@ namespace CustomModelProperty {
|
||||
},
|
||||
ref: (data: Model, add: boolean) => data.customProperties.reference(builder.descriptor, add),
|
||||
get: (data: Model) => get(data)?.data,
|
||||
set: (data: Model, props: Partial<PD.Values<Params>> = {}) => {
|
||||
set: (data: Model, props: Partial<PD.Values<Params>> = {}, value?: Value) => {
|
||||
const property = get(data);
|
||||
const p = PD.merge(builder.defaultParams, property.props, props);
|
||||
if (!PD.areEqual(builder.defaultParams, property.props, p)) {
|
||||
// this invalidates property.value
|
||||
set(data, p, undefined);
|
||||
set(data, p, value);
|
||||
// dispose of assets
|
||||
data.customProperties.assets(builder.descriptor);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace CustomModelProperty {
|
||||
getParams: () => ({ value: PD.Value(defaultValue, { isHidden: true }) }),
|
||||
isApplicable: () => true,
|
||||
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<PD.Values<typeof defaultParams>>) => {
|
||||
return { value: props.value ?? defaultValue };
|
||||
return { ...PD.getDefaultValues(defaultParams), ...props };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
import { InteractionFlag } from '../interactions/common';
|
||||
import { Unit } from '../../../mol-model/structure/structure';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { assertUnreachable } from '../../../mol-util/type-helpers';
|
||||
import { InteractionsSharedParams } from './shared';
|
||||
|
||||
function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InteractionsInterUnitParams>, mesh?: Mesh) {
|
||||
if (!structure.hasAtomic) return Mesh.createEmpty(mesh);
|
||||
@@ -31,7 +33,7 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
|
||||
const { contacts, unitsFeatures } = interactions;
|
||||
|
||||
const { edgeCount, edges } = contacts;
|
||||
const { sizeFactor } = props;
|
||||
const { sizeFactor, parentDisplay } = props;
|
||||
|
||||
if (!edgeCount) return Mesh.createEmpty(mesh);
|
||||
|
||||
@@ -70,14 +72,48 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
|
||||
|
||||
if (child) {
|
||||
const b = edges[edgeIndex];
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
if (!childUnitA) return true;
|
||||
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const { offsets, members } = unitsFeatures.get(b.unitA);
|
||||
for (let i = offsets[b.indexA], il = offsets[b.indexA + 1]; i < il; ++i) {
|
||||
const eA = unitA.elements[members[i]];
|
||||
if (!SortedArray.has(childUnitA.elements, eA)) return true;
|
||||
if (parentDisplay === 'stub') {
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
if (!childUnitA) return true;
|
||||
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const { offsets, members } = unitsFeatures.get(b.unitA);
|
||||
for (let i = offsets[b.indexA], il = offsets[b.indexA + 1]; i < il; ++i) {
|
||||
const eA = unitA.elements[members[i]];
|
||||
if (!SortedArray.has(childUnitA.elements, eA)) return true;
|
||||
}
|
||||
} else if (parentDisplay === 'full' || parentDisplay === 'between') {
|
||||
let flagA = false;
|
||||
let flagB = false;
|
||||
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
if (!childUnitA) {
|
||||
flagA = true;
|
||||
} else {
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const { offsets, members } = unitsFeatures.get(b.unitA);
|
||||
for (let i = offsets[b.indexA], il = offsets[b.indexA + 1]; i < il; ++i) {
|
||||
const eA = unitA.elements[members[i]];
|
||||
if (!SortedArray.has(childUnitA.elements, eA)) flagA = true;
|
||||
}
|
||||
}
|
||||
|
||||
const childUnitB = child.unitMap.get(b.unitB);
|
||||
if (!childUnitB) {
|
||||
flagB = true;
|
||||
} else {
|
||||
const unitB = structure.unitMap.get(b.unitB);
|
||||
const { offsets, members } = unitsFeatures.get(b.unitB);
|
||||
for (let i = offsets[b.indexB], il = offsets[b.indexB + 1]; i < il; ++i) {
|
||||
const eB = unitB.elements[members[i]];
|
||||
if (!SortedArray.has(childUnitB.elements, eB)) flagB = true;
|
||||
}
|
||||
}
|
||||
|
||||
return parentDisplay === 'full' ? flagA && flagB : flagA === flagB;
|
||||
} else {
|
||||
assertUnreachable(parentDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,10 +137,7 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
|
||||
export const InteractionsInterUnitParams = {
|
||||
...ComplexMeshParams,
|
||||
...LinkCylinderParams,
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
|
||||
includeParent: PD.Boolean(false),
|
||||
...InteractionsSharedParams,
|
||||
};
|
||||
export type InteractionsInterUnitParams = typeof InteractionsInterUnitParams
|
||||
|
||||
@@ -121,7 +154,8 @@ export function InteractionsInterUnitVisual(materialId: number): ComplexVisual<I
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.radialSegments !== currentProps.radialSegments
|
||||
newProps.radialSegments !== currentProps.radialSegments ||
|
||||
newProps.parentDisplay !== currentProps.parentDisplay
|
||||
);
|
||||
|
||||
const interactionsHash = InteractionsProvider.get(newStructure).version;
|
||||
|
||||
@@ -22,6 +22,8 @@ import { Interactions } from '../interactions/interactions';
|
||||
import { InteractionFlag } from '../interactions/common';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
|
||||
import { assertUnreachable } from '../../../mol-util/type-helpers';
|
||||
import { InteractionsSharedParams } from './shared';
|
||||
|
||||
async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<InteractionsIntraUnitParams>, mesh?: Mesh) {
|
||||
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
|
||||
@@ -38,7 +40,7 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
|
||||
|
||||
const { x, y, z, members, offsets } = features;
|
||||
const { edgeCount, a, b, edgeProps: { flag } } = contacts;
|
||||
const { sizeFactor } = props;
|
||||
const { sizeFactor, parentDisplay } = props;
|
||||
|
||||
if (!edgeCount) return Mesh.createEmpty(mesh);
|
||||
|
||||
@@ -60,10 +62,31 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
|
||||
if (flag[edgeIndex] === InteractionFlag.Filtered) return true;
|
||||
|
||||
if (childUnit) {
|
||||
const f = a[edgeIndex];
|
||||
for (let i = offsets[f], jl = offsets[f + 1]; i < jl; ++i) {
|
||||
const e = unit.elements[members[offsets[i]]];
|
||||
if (!SortedArray.has(childUnit.elements, e)) return true;
|
||||
if (parentDisplay === 'stub') {
|
||||
const f = a[edgeIndex];
|
||||
for (let i = offsets[f], il = offsets[f + 1]; i < il; ++i) {
|
||||
const e = unit.elements[members[offsets[i]]];
|
||||
if (!SortedArray.has(childUnit.elements, e)) return true;
|
||||
}
|
||||
} else if (parentDisplay === 'full' || parentDisplay === 'between') {
|
||||
let flagA = false;
|
||||
let flagB = false;
|
||||
|
||||
const fA = a[edgeIndex];
|
||||
for (let i = offsets[fA], il = offsets[fA + 1]; i < il; ++i) {
|
||||
const eA = unit.elements[members[offsets[i]]];
|
||||
if (!SortedArray.has(childUnit.elements, eA)) flagA = true;
|
||||
}
|
||||
|
||||
const fB = b[edgeIndex];
|
||||
for (let i = offsets[fB], il = offsets[fB + 1]; i < il; ++i) {
|
||||
const eB = unit.elements[members[offsets[i]]];
|
||||
if (!SortedArray.has(childUnit.elements, eB)) flagB = true;
|
||||
}
|
||||
|
||||
return parentDisplay === 'full' ? flagA && flagB : flagA === flagB;
|
||||
} else {
|
||||
assertUnreachable(parentDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,10 +109,7 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
|
||||
export const InteractionsIntraUnitParams = {
|
||||
...UnitsMeshParams,
|
||||
...LinkCylinderParams,
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
|
||||
includeParent: PD.Boolean(false),
|
||||
...InteractionsSharedParams,
|
||||
};
|
||||
export type InteractionsIntraUnitParams = typeof InteractionsIntraUnitParams
|
||||
|
||||
@@ -106,7 +126,8 @@ export function InteractionsIntraUnitVisual(materialId: number): UnitsVisual<Int
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.radialSegments !== currentProps.radialSegments
|
||||
newProps.radialSegments !== currentProps.radialSegments ||
|
||||
newProps.parentDisplay !== currentProps.parentDisplay
|
||||
);
|
||||
|
||||
const interactionsHash = InteractionsProvider.get(newStructureGroup.structure).version;
|
||||
|
||||
16
src/mol-model-props/computed/representations/shared.ts
Normal file
16
src/mol-model-props/computed/representations/shared.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export const InteractionsSharedParams = {
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
|
||||
includeParent: PD.Boolean(false),
|
||||
parentDisplay: PD.Select('stub', PD.arrayToOptions(['stub', 'full', 'between'] as const), { description: 'Only has an effect when "includeParent" is enabled. "Stub" shows just the child side of interactions to the parent. "Full" shows both sides of interactions to the parent. "Between" shows only interactions to the parent.' }),
|
||||
};
|
||||
export type InteractionsSharedParams = typeof InteractionsSharedParams
|
||||
@@ -241,7 +241,9 @@ namespace Loci {
|
||||
? Structure.toStructureElementLoci(loci.structure)
|
||||
: ShapeGroup.isLoci(loci)
|
||||
? Shape.Loci(loci.shape)
|
||||
: loci;
|
||||
: Volume.Cell.isLoci(loci)
|
||||
? Volume.Loci(loci.volume)
|
||||
: loci;
|
||||
},
|
||||
'elementInstances': (loci: Loci) => {
|
||||
return StructureElement.Loci.is(loci)
|
||||
|
||||
@@ -213,6 +213,9 @@ export namespace Model {
|
||||
export type Index = number;
|
||||
export const Index = CustomModelProperty.createSimple<Index>('index', 'static');
|
||||
|
||||
export type MaxIndex = number;
|
||||
export const MaxIndex = CustomModelProperty.createSimple<MaxIndex>('max_index', 'static');
|
||||
|
||||
export function getRoot(model: Model) {
|
||||
return model.parent || model;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import * as modifiers from './query/queries/modifiers';
|
||||
import * as filters from './query/queries/filters';
|
||||
import * as combinators from './query/queries/combinators';
|
||||
import * as internal from './query/queries/internal';
|
||||
import * as atomset from './query/queries/atom-set';
|
||||
import { Predicates as pred } from './query/predicates';
|
||||
|
||||
export const Queries = {
|
||||
@@ -20,7 +21,8 @@ export const Queries = {
|
||||
modifiers,
|
||||
combinators,
|
||||
pred,
|
||||
internal
|
||||
internal,
|
||||
atomset
|
||||
};
|
||||
|
||||
export { StructureSelection, StructureQuery };
|
||||
export { StructureSelection, StructureQuery };
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2022 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 { Structure, StructureElement, Unit } from '../structure';
|
||||
@@ -113,6 +114,7 @@ class QueryContextBondInfo<U extends Unit = Unit> {
|
||||
bIndex: StructureElement.UnitIndex = 0 as StructureElement.UnitIndex;
|
||||
type: BondType = BondType.Flag.None;
|
||||
order: number = 0;
|
||||
key: number = -1;
|
||||
|
||||
private testFn: QueryPredicate = defaultBondTest;
|
||||
|
||||
|
||||
36
src/mol-model/structure/query/queries/atom-set.ts
Normal file
36
src/mol-model/structure/query/queries/atom-set.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Koya Sakuma
|
||||
* Adapted from MolQL implemtation of atom-set.ts
|
||||
*
|
||||
* Copyright (c) 2017 MolQL contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { StructureQuery } from '../query';
|
||||
import { StructureSelection } from '../selection';
|
||||
import { getCurrentStructureProperties } from './filters';
|
||||
import { QueryContext, QueryFn } from '../context';
|
||||
|
||||
|
||||
export function atomCount(ctx: QueryContext) {
|
||||
return ctx.currentStructure.elementCount;
|
||||
}
|
||||
|
||||
|
||||
export function countQuery(query: StructureQuery) {
|
||||
return (ctx: QueryContext) => {
|
||||
const sel = query(ctx);
|
||||
return StructureSelection.structureCount(sel);
|
||||
};
|
||||
}
|
||||
|
||||
export function propertySet(prop: QueryFn<any>) {
|
||||
return (ctx: QueryContext) => {
|
||||
const set = new Set();
|
||||
return getCurrentStructureProperties(ctx, prop, set);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2022 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 { SetUtils } from '../../../../mol-util/set';
|
||||
import { Unit } from '../../structure';
|
||||
import { QueryContext, QueryFn, QueryPredicate } from '../context';
|
||||
import { QueryContext, QueryFn } from '../context';
|
||||
import { StructureQuery } from '../query';
|
||||
import { StructureSelection } from '../selection';
|
||||
import { structureAreIntersecting } from '../utils/structure-set';
|
||||
@@ -16,7 +17,7 @@ import { Structure } from '../../structure/structure';
|
||||
import { StructureElement } from '../../structure/element';
|
||||
import { SortedArray } from '../../../../mol-data/int';
|
||||
|
||||
export function pick(query: StructureQuery, pred: QueryPredicate): StructureQuery {
|
||||
export function pick(query: StructureQuery, pred: QueryFn<any>): StructureQuery {
|
||||
return ctx => {
|
||||
const sel = query(ctx);
|
||||
const ret = StructureSelection.LinearBuilder(ctx.inputStructure);
|
||||
@@ -50,9 +51,7 @@ export function first(query: StructureQuery): StructureQuery {
|
||||
};
|
||||
}
|
||||
|
||||
export interface UnitTypeProperties { atomic?: QueryFn, coarse?: QueryFn }
|
||||
|
||||
export function getCurrentStructureProperties(ctx: QueryContext, props: UnitTypeProperties, set: Set<any>) {
|
||||
export function getCurrentStructureProperties(ctx: QueryContext, props: QueryFn<any>, set: Set<any>) {
|
||||
const { units } = ctx.currentStructure;
|
||||
const l = ctx.pushCurrentElement();
|
||||
|
||||
@@ -61,9 +60,9 @@ export function getCurrentStructureProperties(ctx: QueryContext, props: UnitType
|
||||
l.unit = unit;
|
||||
const elements = unit.elements;
|
||||
|
||||
let fn;
|
||||
if (Unit.isAtomic(unit)) fn = props.atomic;
|
||||
else fn = props.coarse;
|
||||
const fn = props;
|
||||
// if (Unit.isAtomic(unit)) fn = props.atomic;
|
||||
// else fn = props.coarse;
|
||||
if (!fn) continue;
|
||||
|
||||
for (let j = 0, _j = elements.length; j < _j; j++) {
|
||||
@@ -77,7 +76,7 @@ export function getCurrentStructureProperties(ctx: QueryContext, props: UnitType
|
||||
return set;
|
||||
}
|
||||
|
||||
function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props: UnitTypeProperties) {
|
||||
function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props: QueryFn<any>) {
|
||||
const set = new Set();
|
||||
|
||||
const sel = query(ctx);
|
||||
@@ -92,7 +91,7 @@ function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props:
|
||||
return set;
|
||||
}
|
||||
|
||||
export function withSameAtomProperties(query: StructureQuery, propertySource: StructureQuery, props: UnitTypeProperties): StructureQuery {
|
||||
export function withSameAtomProperties(query: StructureQuery, propertySource: StructureQuery, props: QueryFn<any>): StructureQuery {
|
||||
return ctx => {
|
||||
const sel = query(ctx);
|
||||
const propSet = getSelectionProperties(ctx, propertySource, props);
|
||||
@@ -102,7 +101,7 @@ export function withSameAtomProperties(query: StructureQuery, propertySource: St
|
||||
StructureSelection.forEach(sel, (s, i) => {
|
||||
ctx.currentStructure = s;
|
||||
const currentProps = getCurrentStructureProperties(ctx, props, new Set());
|
||||
if (SetUtils.isSuperset(currentProps, propSet)) {
|
||||
if (SetUtils.isSuperset(propSet, currentProps)) {
|
||||
ret.add(s);
|
||||
}
|
||||
|
||||
@@ -248,7 +247,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
|
||||
|
||||
const inputUnit = input.unitMap.get(unit.id) as Unit.Atomic;
|
||||
|
||||
const { offset, b, edgeProps: { flags, order } } = inputUnit.bonds;
|
||||
const { offset, b, edgeProps: { flags, order, key } } = inputUnit.bonds;
|
||||
const bondedUnits = interBonds.getConnectedUnits(unit.id);
|
||||
const buCount = bondedUnits.length;
|
||||
|
||||
@@ -273,6 +272,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
|
||||
atomicBond.bIndex = b[l] as StructureElement.UnitIndex;
|
||||
atomicBond.type = flags[l];
|
||||
atomicBond.order = order[l];
|
||||
atomicBond.key = key[l];
|
||||
if (atomicBond.test(queryCtx, true)) return true;
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
|
||||
atomicBond.bIndex = bond.indexB;
|
||||
atomicBond.type = bond.props.flag;
|
||||
atomicBond.order = bond.props.order;
|
||||
atomicBond.key = bond.props.key;
|
||||
if (atomicBond.test(queryCtx, true)) return true;
|
||||
}
|
||||
}
|
||||
@@ -342,4 +343,4 @@ export function isConnectedTo({ query, target, disjunct, invert, bondTest }: IsC
|
||||
|
||||
return ret.getSelection();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2022 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>
|
||||
@@ -322,7 +322,7 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
|
||||
for (const unit of structure.units) {
|
||||
if (unit.kind !== Unit.Kind.Atomic) continue;
|
||||
|
||||
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order } } = unit.bonds;
|
||||
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order, key } } = unit.bonds;
|
||||
atomicBond.a.unit = unit;
|
||||
atomicBond.b.unit = unit;
|
||||
for (let i = 0 as StructureElement.UnitIndex, _i = unit.elements.length; i < _i; i++) {
|
||||
@@ -335,6 +335,7 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
|
||||
atomicBond.b.element = unit.elements[intraBondB[lI]];
|
||||
atomicBond.type = flags[lI];
|
||||
atomicBond.order = order[lI];
|
||||
atomicBond.key = key[lI];
|
||||
// No need to "swap test" because each bond direction will be visited eventually.
|
||||
if (atomicBond.test(ctx, false)) {
|
||||
const b = structure.subsetBuilder(false);
|
||||
@@ -358,6 +359,7 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
|
||||
atomicBond.bIndex = bond.indexB;
|
||||
atomicBond.order = bond.props.order;
|
||||
atomicBond.type = bond.props.flag;
|
||||
atomicBond.key = bond.props.key;
|
||||
|
||||
// No need to "swap test" because each bond direction will be visited eventually.
|
||||
if (atomicBond.test(ctx, false)) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2022 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 { Segmentation, SortedArray } from '../../../../mol-data/int';
|
||||
@@ -370,7 +371,7 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
|
||||
}
|
||||
|
||||
const inputUnitA = inputStructure.unitMap.get(unit.id) as Unit.Atomic;
|
||||
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order } } = inputUnitA.bonds;
|
||||
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order, key } } = inputUnitA.bonds;
|
||||
|
||||
atomicBond.setStructure(inputStructure);
|
||||
|
||||
@@ -397,6 +398,7 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
|
||||
atomicBond.b.element = bElement;
|
||||
atomicBond.type = flags[lI];
|
||||
atomicBond.order = order[lI];
|
||||
atomicBond.key = key[lI];
|
||||
|
||||
if (atomicBond.test(ctx, true)) {
|
||||
builder.addToUnit(unit.id, bElement);
|
||||
@@ -427,6 +429,7 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
|
||||
atomicBond.b.element = bElement;
|
||||
atomicBond.type = bond.props.flag;
|
||||
atomicBond.order = bond.props.order;
|
||||
atomicBond.key = bond.props.key;
|
||||
|
||||
if (atomicBond.test(ctx, true)) {
|
||||
builder.addToUnit(bondedUnit.unitB, bElement);
|
||||
|
||||
@@ -1357,6 +1357,9 @@ namespace Structure {
|
||||
export type Index = number;
|
||||
export const Index = CustomStructureProperty.createSimple<Index>('index', 'root');
|
||||
|
||||
export type MaxIndex = number;
|
||||
export const MaxIndex = CustomStructureProperty.createSimple<MaxIndex>('max_index', 'root');
|
||||
|
||||
const PrincipalAxesProp = '__PrincipalAxes__';
|
||||
export function getPrincipalAxes(structure: Structure): PrincipalAxes {
|
||||
if (structure.currentPropertyData[PrincipalAxesProp]) return structure.currentPropertyData[PrincipalAxesProp];
|
||||
|
||||
@@ -15,16 +15,17 @@ import { InterUnitGraph } from '../../../../../mol-math/graph/inter-unit-graph';
|
||||
type IntraUnitBonds = IntAdjacencyGraph<StructureElement.UnitIndex, {
|
||||
readonly order: ArrayLike<number>,
|
||||
readonly flags: ArrayLike<BondType.Flag>
|
||||
readonly key: ArrayLike<number>,
|
||||
}, {
|
||||
/** can remap even with dynamicBonds on, e.g., for water molecules */
|
||||
readonly canRemap?: boolean
|
||||
}>
|
||||
|
||||
namespace IntraUnitBonds {
|
||||
export const Empty: IntraUnitBonds = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [] });
|
||||
export const Empty: IntraUnitBonds = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [], key: [] });
|
||||
}
|
||||
|
||||
type InterUnitEdgeProps = { readonly order: number, readonly flag: BondType.Flag }
|
||||
type InterUnitEdgeProps = { readonly order: number, readonly flag: BondType.Flag, readonly key: number }
|
||||
|
||||
class InterUnitBonds extends InterUnitGraph<number, StructureElement.UnitIndex, InterUnitEdgeProps> {
|
||||
/** Get inter-unit bond given a bond-location */
|
||||
|
||||
@@ -80,7 +80,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
|
||||
|
||||
if (!props.forceCompute && indexPairs) {
|
||||
const { maxDistance } = indexPairs;
|
||||
const { offset, b, edgeProps: { order, distance, flag } } = indexPairs.bonds;
|
||||
const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
|
||||
|
||||
const srcA = sourceIndex.value(aI);
|
||||
const aeI = getElementIdx(type_symbolA.value(aI));
|
||||
@@ -113,7 +113,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
|
||||
}
|
||||
|
||||
if (add) {
|
||||
builder.add(_aI, _bI, { order: order[i], flag: flag[i] });
|
||||
builder.add(_aI, _bI, { order: order[i], flag: flag[i], key: key[i] });
|
||||
}
|
||||
}
|
||||
continue; // assume `indexPairs` supplies all bonds
|
||||
@@ -131,7 +131,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
|
||||
// check if the bond is within MAX_RADIUS for this pair of units
|
||||
if (getDistance(unitA, aI, unitB, p.atomIndex) > maxRadius) continue;
|
||||
|
||||
builder.add(_aI, _bI, { order: se.order, flag: se.flags });
|
||||
builder.add(_aI, _bI, { order: se.order, flag: se.flags, key: se.rowIndex });
|
||||
added = true;
|
||||
}
|
||||
// assume, for an atom, that if any inter unit bond is given
|
||||
@@ -187,7 +187,8 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
|
||||
const compIdB = label_comp_idB.value(residueIndexB[bI]);
|
||||
builder.add(_aI, _bI, {
|
||||
order: getInterBondOrderFromTable(compIdA, compIdB, atomIdA, atomIdB),
|
||||
flag: (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed
|
||||
flag: (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed,
|
||||
key: -1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,17 +24,19 @@ import { Model } from '../../../model/model';
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3distance = Vec3.distance;
|
||||
|
||||
function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
|
||||
function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], _key: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
|
||||
const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
|
||||
const flags = new Uint16Array(builder.slotCount);
|
||||
const order = new Int8Array(builder.slotCount);
|
||||
const key = new Uint32Array(builder.slotCount);
|
||||
for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
|
||||
builder.addNextEdge();
|
||||
builder.assignProperty(flags, _flags[i]);
|
||||
builder.assignProperty(order, _order[i]);
|
||||
builder.assignProperty(key, _key[i]);
|
||||
}
|
||||
|
||||
return builder.createGraph({ flags, order }, { canRemap });
|
||||
return builder.createGraph({ flags, order, key }, { canRemap });
|
||||
}
|
||||
|
||||
const tmpDistVecA = Vec3();
|
||||
@@ -53,7 +55,7 @@ function findIndexPairBonds(unit: Unit.Atomic) {
|
||||
const { type_symbol } = unit.model.atomicHierarchy.atoms;
|
||||
const atomCount = unit.elements.length;
|
||||
const { maxDistance } = indexPairs;
|
||||
const { offset, b, edgeProps: { order, distance, flag } } = indexPairs.bonds;
|
||||
const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
|
||||
|
||||
const { atomSourceIndex: sourceIndex } = unit.model.atomicHierarchy;
|
||||
const { invertedIndex } = Model.getInvertedAtomSourceIndex(unit.model);
|
||||
@@ -62,6 +64,7 @@ function findIndexPairBonds(unit: Unit.Atomic) {
|
||||
const atomB: StructureElement.UnitIndex[] = [];
|
||||
const flags: number[] = [];
|
||||
const orders: number[] = [];
|
||||
const keys: number[] = [];
|
||||
|
||||
for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
|
||||
const aI = atoms[_aI];
|
||||
@@ -104,11 +107,12 @@ function findIndexPairBonds(unit: Unit.Atomic) {
|
||||
atomB[atomB.length] = _bI;
|
||||
orders[orders.length] = order[i];
|
||||
flags[flags.length] = flag[i];
|
||||
keys[keys.length] = key[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getGraph(atomA, atomB, orders, flags, atomCount, false);
|
||||
return getGraph(atomA, atomB, orders, flags, keys, atomCount, false);
|
||||
}
|
||||
|
||||
function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBonds {
|
||||
@@ -132,9 +136,10 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
|
||||
const atomB: StructureElement.UnitIndex[] = [];
|
||||
const flags: number[] = [];
|
||||
const order: number[] = [];
|
||||
const key: number[] = [];
|
||||
|
||||
let lastResidue = -1;
|
||||
let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0;
|
||||
let componentMap: Map<string, Map<string, { flags: number, order: number, key: number }>> | undefined = void 0;
|
||||
|
||||
let isWatery = true, isDictionaryBased = true, isSequenced = true;
|
||||
|
||||
@@ -162,6 +167,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
|
||||
atomB[atomB.length] = _bI;
|
||||
flags[flags.length] = se.flags;
|
||||
order[order.length] = se.order;
|
||||
key[key.length] = se.rowIndex;
|
||||
|
||||
if (!hasStructConn) structConnAdded.clear();
|
||||
hasStructConn = true;
|
||||
@@ -230,6 +236,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
|
||||
flag |= BondType.Flag.MetallicCoordination;
|
||||
}
|
||||
flags[flags.length] = flag;
|
||||
key[key.length] = e.key;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -243,6 +250,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
|
||||
atomB[atomB.length] = _bI;
|
||||
order[order.length] = getIntraBondOrderFromTable(compId, atomIdA, label_atom_id.value(bI));
|
||||
flags[flags.length] = (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed;
|
||||
key[key.length] = -1;
|
||||
|
||||
const seqIdB = label_seq_id.value(rbI);
|
||||
|
||||
@@ -253,7 +261,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
|
||||
}
|
||||
|
||||
const canRemap = isWatery || (isDictionaryBased && isSequenced);
|
||||
return getGraph(atomA, atomB, order, flags, atomCount, canRemap);
|
||||
return getGraph(atomA, atomB, order, flags, key, atomCount, canRemap);
|
||||
}
|
||||
|
||||
function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Gianluca Tomasello <giagitom@gmail.com>
|
||||
*/
|
||||
|
||||
import { Structure } from '../structure';
|
||||
@@ -13,6 +14,7 @@ import { StructureUniqueSubsetBuilder } from './unique-subset-builder';
|
||||
import { StructureElement } from '../element';
|
||||
import { Unit } from '../unit';
|
||||
import { UnitIndex } from '../element/util';
|
||||
import { FibonacciHeap } from '../../../../mol-util/fibonacci-heap';
|
||||
|
||||
export interface StructureResult extends Result<StructureElement.UnitIndex> {
|
||||
units: Unit[]
|
||||
@@ -54,6 +56,7 @@ export function StructureLookup3DResultContext(): StructureLookup3DResultContext
|
||||
export class StructureLookup3D {
|
||||
private unitLookup: Lookup3D;
|
||||
private pivot = Vec3();
|
||||
private heap = new FibonacciHeap();
|
||||
|
||||
findUnitIndices(x: number, y: number, z: number, radius: number): Result<number> {
|
||||
return this.unitLookup.find(x, y, z, radius);
|
||||
@@ -86,6 +89,54 @@ export class StructureLookup3D {
|
||||
return ctx.result;
|
||||
}
|
||||
|
||||
nearest(x: number, y: number, z: number, k: number = 1, ctx?: StructureLookup3DResultContext): StructureResult {
|
||||
return this._nearest(x, y, z, k, ctx ?? this.findContext);
|
||||
}
|
||||
|
||||
_nearest(x: number, y: number, z: number, k: number, ctx: StructureLookup3DResultContext): StructureResult {
|
||||
const result = ctx.result, heap = this.heap;
|
||||
Result.reset(result);
|
||||
heap.clear();
|
||||
const { units } = this.structure;
|
||||
let elementsCount = 0;
|
||||
const closeUnits = this.unitLookup.nearest(x, y, z, units.length, (uid: number) => (elementsCount += units[uid].elements.length) >= k, ctx.closeUnitsResult); // sort units based on distance to the point
|
||||
if (closeUnits.count === 0) return result;
|
||||
let totalCount = 0, maxDistResult = -Number.MAX_VALUE;
|
||||
for (let t = 0, _t = closeUnits.count; t < _t; t++) {
|
||||
const unitSqDist = closeUnits.squaredDistances[t];
|
||||
if (totalCount >= k && maxDistResult < unitSqDist) break;
|
||||
Vec3.set(this.pivot, x, y, z);
|
||||
const unit = units[closeUnits.indices[t]];
|
||||
if (!unit.conformation.operator.isIdentity) {
|
||||
Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse);
|
||||
}
|
||||
const unitLookup = unit.lookup3d;
|
||||
const groupResult = unitLookup.nearest(this.pivot[0], this.pivot[1], this.pivot[2], k, void 0, ctx.unitGroupResult);
|
||||
if (groupResult.count === 0) continue;
|
||||
totalCount += groupResult.count;
|
||||
maxDistResult = Math.max(maxDistResult, groupResult.squaredDistances[groupResult.count - 1]);
|
||||
for (let j = 0, _j = groupResult.count; j < _j; j++) {
|
||||
heap.insert(groupResult.squaredDistances[j], { index: groupResult.indices[j], unit: unit });
|
||||
}
|
||||
}
|
||||
if (k === 1) {
|
||||
const node = heap.findMinimum();
|
||||
if (node) {
|
||||
const { key: squaredDistance } = node;
|
||||
const { unit, index } = node.value as { index: UnitIndex, unit: Unit };
|
||||
StructureResult.add(result, unit as Unit, index as UnitIndex, squaredDistance as number);
|
||||
}
|
||||
} else {
|
||||
while (!heap.isEmpty() && result.count < k) {
|
||||
const node = heap.extractMinimum();
|
||||
const { key: squaredDistance } = node!;
|
||||
const { unit, index } = node!.value as { index: UnitIndex, unit: Unit };
|
||||
StructureResult.add(result, unit as Unit, index as UnitIndex, squaredDistance as number);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
findIntoBuilder(x: number, y: number, z: number, radius: number, builder: StructureUniqueSubsetBuilder) {
|
||||
const { units } = this.structure;
|
||||
const closeUnits = this.unitLookup.find(x, y, z, radius);
|
||||
@@ -217,4 +268,4 @@ export class StructureLookup3D {
|
||||
const position = { x: xs, y: ys, z: zs, radius, indices: OrderedSet.ofBounds(0, unitCount) };
|
||||
this.unitLookup = GridLookup3D(position, boundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
import { Segmentation } from '../../../../mol-data/int';
|
||||
import { MinimizeRmsd } from '../../../../mol-math/linear-algebra/3d/minimize-rmsd';
|
||||
import { SIFTSMapping } from '../../../../mol-model-props/sequence/sifts-mapping';
|
||||
import { ElementIndex } from '../../model/indexing';
|
||||
import { ElementIndex, ResidueIndex } from '../../model/indexing';
|
||||
import { StructureElement } from '../element';
|
||||
import { Structure } from '../structure';
|
||||
import { Unit } from '../unit';
|
||||
|
||||
@@ -24,11 +25,16 @@ export interface AlignmentResult {
|
||||
failedPairs: [number, number][]
|
||||
}
|
||||
|
||||
export function alignAndSuperposeWithSIFTSMapping(structures: Structure[], options?: { traceOnly?: boolean }): AlignmentResult {
|
||||
type IncludeResidueTest = (traceElementOrFirstAtom: StructureElement.Location<Unit.Atomic>, residueIndex: ResidueIndex, startIndex: ElementIndex, endIndex: ElementIndex) => boolean
|
||||
|
||||
export function alignAndSuperposeWithSIFTSMapping(
|
||||
structures: Structure[],
|
||||
options?: { traceOnly?: boolean, includeResidueTest?: IncludeResidueTest }
|
||||
): AlignmentResult {
|
||||
const indexMap = new Map<string, IndexEntry>();
|
||||
|
||||
for (let i = 0; i < structures.length; i++) {
|
||||
buildIndex(structures[i], indexMap, i, options?.traceOnly ?? true);
|
||||
buildIndex(structures[i], indexMap, i, options?.traceOnly ?? true, options?.includeResidueTest ?? _includeAllResidues);
|
||||
}
|
||||
|
||||
const index = Array.from(indexMap.values());
|
||||
@@ -137,11 +143,16 @@ interface IndexEntry {
|
||||
pivots: { [i: number]: [unit: Unit.Atomic, start: ElementIndex, end: ElementIndex] | undefined }
|
||||
}
|
||||
|
||||
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number, traceOnly: boolean) {
|
||||
function _includeAllResidues() { return true; }
|
||||
|
||||
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number, traceOnly: boolean, includeTest: IncludeResidueTest) {
|
||||
const loc = StructureElement.Location.create<Unit.Atomic>(structure);
|
||||
|
||||
for (const unit of structure.units) {
|
||||
if (unit.kind !== Unit.Kind.Atomic) continue;
|
||||
|
||||
const { elements, model } = unit;
|
||||
loc.unit = unit;
|
||||
|
||||
const map = SIFTSMapping.Provider.get(model).value;
|
||||
if (!map) return;
|
||||
@@ -161,9 +172,11 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
|
||||
|
||||
if (!dbName[rI]) continue;
|
||||
|
||||
const traceElement = traceElementIndex[rI];
|
||||
|
||||
let start, end;
|
||||
if (traceOnly) {
|
||||
start = traceElementIndex[rI];
|
||||
start = traceElement;
|
||||
if (start === -1) continue;
|
||||
end = start + 1 as ElementIndex;
|
||||
} else {
|
||||
@@ -171,6 +184,9 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
|
||||
end = elements[residueSegment.end - 1] + 1 as ElementIndex;
|
||||
}
|
||||
|
||||
loc.element = (traceElement >= 0 ? traceElement : start) as ElementIndex;
|
||||
if (!includeTest(loc, rI, start, end)) continue;
|
||||
|
||||
const key = `${dbName[rI]}-${accession[rI]}-${num[rI]}`;
|
||||
|
||||
if (!index.has(key)) {
|
||||
|
||||
@@ -219,4 +219,14 @@ export namespace Volume {
|
||||
return Sphere3D.expand(bs, bs, Mat4.getMaxScaleOnAxis(transform) * 10);
|
||||
}
|
||||
}
|
||||
|
||||
export type PickingGranularity = 'volume' | 'object' | 'voxel';
|
||||
export const PickingGranularity = {
|
||||
set(volume: Volume, granularity: PickingGranularity) {
|
||||
volume._propertyData['__picking_granularity__'] = granularity;
|
||||
},
|
||||
get(volume: Volume): PickingGranularity {
|
||||
return volume._propertyData['__picking_granularity__'] ?? 'voxel';
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -90,6 +90,7 @@ const DownloadStructure = StateAction.build({
|
||||
url: PD.Url(''),
|
||||
format: PD.Select<BuiltInTrajectoryFormat>('mmcif', PD.arrayToOptions(BuiltInTrajectoryFormats.map(f => f[0]), f => f)),
|
||||
isBinary: PD.Boolean(false),
|
||||
label: PD.Optional(PD.Text('')),
|
||||
options
|
||||
}, { isFlat: true, label: 'URL' })
|
||||
})
|
||||
@@ -104,7 +105,7 @@ const DownloadStructure = StateAction.build({
|
||||
|
||||
switch (src.name) {
|
||||
case 'url':
|
||||
downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary }];
|
||||
downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary, label: src.params.label || undefined }];
|
||||
format = src.params.format;
|
||||
break;
|
||||
case 'pdb':
|
||||
|
||||
@@ -86,7 +86,7 @@ const allModels = TrajectoryHierarchyPresetProvider({
|
||||
id: 'preset-trajectory-all-models',
|
||||
display: {
|
||||
name: 'All Models', group: 'Preset',
|
||||
description: 'Shows all models; colored by model-index.'
|
||||
description: 'Shows all models; colored by trajectory-index.'
|
||||
},
|
||||
isApplicable: o => {
|
||||
return o.data.frameCount > 1;
|
||||
@@ -115,7 +115,7 @@ const allModels = TrajectoryHierarchyPresetProvider({
|
||||
|
||||
const quality = structure.obj ? getStructureQuality(structure.obj.data, { elementCountFactor: tr.frameCount }) : 'medium';
|
||||
const representationPreset = params.representationPreset || plugin.config.get(PluginConfig.Structure.DefaultRepresentationPreset) || PresetStructureRepresentations.auto.id;
|
||||
await builder.representation.applyPreset(structureProperties, representationPreset, { theme: { globalName: 'model-index' }, quality });
|
||||
await builder.representation.applyPreset(structureProperties, representationPreset, { theme: { globalName: 'trajectory-index' }, quality });
|
||||
}
|
||||
|
||||
return { models, structures };
|
||||
|
||||
@@ -41,8 +41,10 @@ export namespace StructureRepresentationPresetProvider {
|
||||
quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)),
|
||||
theme: PD.Optional(PD.Group({
|
||||
globalName: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
|
||||
globalColorParams: PD.Optional(PD.Value<any>({}, { isHidden: true })),
|
||||
carbonColor: PD.Optional(PD.Select('chain-id', PD.arrayToOptions(['chain-id', 'operator-name', 'element-symbol'] as const))),
|
||||
symmetryColor: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
|
||||
symmetryColorParams: PD.Optional(PD.Value<any>({}, { isHidden: true })),
|
||||
focus: PD.Optional(PD.Group({
|
||||
name: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
|
||||
params: PD.Optional(PD.Value<ColorTheme.BuiltInParams<ColorTheme.BuiltIn>>({} as any))
|
||||
@@ -76,13 +78,15 @@ export namespace StructureRepresentationPresetProvider {
|
||||
if (params.ignoreLight !== void 0) typeParams.ignoreLight = !!params.ignoreLight;
|
||||
const color: ColorTheme.BuiltIn | undefined = params.theme?.globalName ? params.theme?.globalName : void 0;
|
||||
const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = params.theme?.carbonColor !== undefined
|
||||
? { carbonColor: getCarbonColorParams(params.theme?.carbonColor) }
|
||||
: { };
|
||||
? { carbonColor: getCarbonColorParams(params.theme?.carbonColor), ...params.theme?.globalColorParams }
|
||||
: { ...params.theme?.globalColorParams };
|
||||
const symmetryColor: ColorTheme.BuiltIn | undefined = structure && params.theme?.symmetryColor
|
||||
? isSymmetry(structure) ? params.theme?.symmetryColor : color
|
||||
: color;
|
||||
const symmetryColorParams = params.theme?.symmetryColorParams ? { ...params.theme?.globalColorParams, ...params.theme?.symmetryColorParams } : { ...params.theme?.globalColorParams };
|
||||
const globalColorParams = params.theme?.globalColorParams ? { ...params.theme?.globalColorParams } : undefined;
|
||||
|
||||
return { update, builder, color, symmetryColor, typeParams, ballAndStickColor };
|
||||
return { update, builder, color, symmetryColor, symmetryColorParams, globalColorParams, typeParams, ballAndStickColor };
|
||||
}
|
||||
|
||||
export function updateFocusRepr<T extends ColorTheme.BuiltIn>(plugin: PluginContext, structure: Structure, themeName: T | undefined, themeParams: ColorTheme.BuiltInParams<T> | undefined) {
|
||||
@@ -177,18 +181,18 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
|
||||
const waterType = (components.water?.obj?.data?.elementCount || 0) > 50_000 ? 'line' : 'ball-and-stick';
|
||||
const lipidType = (components.lipid?.obj?.data?.elementCount || 0) > 20_000 ? 'line' : 'ball-and-stick';
|
||||
|
||||
const { update, builder, typeParams, color, symmetryColor, ballAndStickColor } = reprBuilder(plugin, params, structure);
|
||||
const { update, builder, typeParams, color, symmetryColor, symmetryColorParams, globalColorParams, ballAndStickColor } = reprBuilder(plugin, params, structure);
|
||||
|
||||
const representations = {
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'polymer' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' }),
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'ligand' }),
|
||||
nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'non-standard' }),
|
||||
branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color, colorParams: ballAndStickColor }, { tag: 'branched-ball-and-stick' }),
|
||||
branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }),
|
||||
water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6, visuals: waterType === 'line' ? ['intra-bond', 'element-point'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'water' }),
|
||||
ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ion' }),
|
||||
lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6, visuals: lipidType === 'line' ? ['intra-bond'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'lipid' }),
|
||||
coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id' }, { tag: 'coarse' })
|
||||
branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color, colorParams: globalColorParams }, { tag: 'branched-snfg-3d' }),
|
||||
water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6, visuals: waterType === 'line' ? ['intra-bond', 'element-point'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'water' }),
|
||||
ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'ion' }),
|
||||
lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6, visuals: lipidType === 'line' ? ['intra-bond'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'lipid' }),
|
||||
coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id', colorParams: globalColorParams }, { tag: 'coarse' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: false });
|
||||
@@ -223,11 +227,11 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({
|
||||
smoothness: structure.isCoarseGrained ? 1.0 : 1.5,
|
||||
};
|
||||
|
||||
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
|
||||
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
|
||||
|
||||
const representations = {
|
||||
protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'protein' }),
|
||||
nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'nucleic' })
|
||||
protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'protein' }),
|
||||
nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'nucleic' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -275,11 +279,11 @@ const coarseSurface = StructureRepresentationPresetProvider({
|
||||
});
|
||||
}
|
||||
|
||||
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
|
||||
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
|
||||
|
||||
const representations = {
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }),
|
||||
lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'lipid' })
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' }),
|
||||
lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'lipid' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -309,10 +313,10 @@ const polymerCartoon = StructureRepresentationPresetProvider({
|
||||
sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2
|
||||
};
|
||||
|
||||
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
|
||||
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
|
||||
|
||||
const representations = {
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'polymer' })
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
@@ -367,9 +371,9 @@ const atomicDetail = StructureRepresentationPresetProvider({
|
||||
});
|
||||
}
|
||||
|
||||
const { update, builder, typeParams, color, ballAndStickColor } = reprBuilder(plugin, params, structure);
|
||||
const { update, builder, typeParams, color, ballAndStickColor, globalColorParams } = reprBuilder(plugin, params, structure);
|
||||
const colorParams = lowResidueElementRatio && !bondsGiven
|
||||
? { carbonColor: { name: 'element-symbol', params: {} } }
|
||||
? { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams }
|
||||
: ballAndStickColor;
|
||||
|
||||
const representations = {
|
||||
@@ -377,7 +381,7 @@ const atomicDetail = StructureRepresentationPresetProvider({
|
||||
};
|
||||
if (showCarbohydrateSymbol) {
|
||||
Object.assign(representations, {
|
||||
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
|
||||
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color, colorParams: globalColorParams }, { tag: 'snfg-3d' }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ export class StructureRepresentationBuilder {
|
||||
}
|
||||
|
||||
applyPreset<K extends keyof PresetStructureRepresentations>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, preset: K, params?: StructureRepresentationPresetProvider.Params<PresetStructureRepresentations[K]>): Promise<StructureRepresentationPresetProvider.State<PresetStructureRepresentations[K]>> | undefined
|
||||
applyPreset<P = any, S = {}>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<S> | undefined
|
||||
applyPreset<P = any, S extends {} = {}>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<S> | undefined
|
||||
applyPreset(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, providerId: string, params?: any): Promise<any> | undefined
|
||||
applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationPresetProvider, params?: any): Promise<any> | undefined {
|
||||
const provider = this.resolveProvider(providerRef);
|
||||
|
||||
@@ -42,7 +42,7 @@ export class PluginComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export class StatefulPluginComponent<State> extends PluginComponent {
|
||||
export class StatefulPluginComponent<State extends {}> extends PluginComponent {
|
||||
private _state: State;
|
||||
|
||||
protected updateState(...states: Partial<State>[]): boolean {
|
||||
|
||||
@@ -75,7 +75,7 @@ export const CifCoreProvider: TrajectoryFormatProvider = {
|
||||
visuals: defaultVisuals
|
||||
};
|
||||
|
||||
function directTrajectory<P>(transformer: StateTransformer<PluginStateObject.Data.String | PluginStateObject.Data.Binary, PluginStateObject.Molecule.Trajectory, P>, transformerParams?: P): TrajectoryFormatProvider['parse'] {
|
||||
function directTrajectory<P extends {}>(transformer: StateTransformer<PluginStateObject.Data.String | PluginStateObject.Data.Binary, PluginStateObject.Molecule.Trajectory, P>, transformerParams?: P): TrajectoryFormatProvider['parse'] {
|
||||
return async (plugin, data, params) => {
|
||||
const state = plugin.state.data;
|
||||
const trajectory = await state.build().to(data)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user