Compare commits
419 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c8d2cfa21 | ||
|
|
d67c0eb757 | ||
|
|
7094f8f265 | ||
|
|
19a9ed3e19 | ||
|
|
0dac1b93ae | ||
|
|
e824863de1 | ||
|
|
9ff8becd62 | ||
|
|
fcaa1bcfa8 | ||
|
|
1b904ee2c9 | ||
|
|
d9b2b99c86 | ||
|
|
bdf23a7c4e | ||
|
|
e474e9b090 | ||
|
|
837f9a6c74 | ||
|
|
c357aed7bb | ||
|
|
59ffddfd8d | ||
|
|
fb3accaa36 | ||
|
|
b3e79544ad | ||
|
|
2ee0f3bf97 | ||
|
|
a56b5edc4e | ||
|
|
f2d71b6551 | ||
|
|
ef560ddc03 | ||
|
|
2e30ffe1bc | ||
|
|
325b5e9297 | ||
|
|
ae9e04b8d4 | ||
|
|
ab0010122b | ||
|
|
08d736ecdc | ||
|
|
9c362c8ffd | ||
|
|
62c8778560 | ||
|
|
2fe0665e12 | ||
|
|
14a957f517 | ||
|
|
087010d0a1 | ||
|
|
f92657310a | ||
|
|
19e91400b5 | ||
|
|
7885fb7b4f | ||
|
|
331bec11ee | ||
|
|
f6ed650ef6 | ||
|
|
df9ce6add9 | ||
|
|
28ee47d501 | ||
|
|
df2da479ad | ||
|
|
46eb9d8baf | ||
|
|
b6be871a21 | ||
|
|
ce2367fc0a | ||
|
|
f219cd6c8b | ||
|
|
e697624064 | ||
|
|
92ffdeb5bf | ||
|
|
ddefe7e542 | ||
|
|
fb4019c041 | ||
|
|
46026e047e | ||
|
|
0dfad5a757 | ||
|
|
a0495f8aae | ||
|
|
1610f05b83 | ||
|
|
8202b75cda | ||
|
|
4904bae5a6 | ||
|
|
04c06db02c | ||
|
|
a96f94b676 | ||
|
|
ebdfc694c2 | ||
|
|
7f29340797 | ||
|
|
ef176efed8 | ||
|
|
113d0b5141 | ||
|
|
163285b0a9 | ||
|
|
9f1cf5377a | ||
|
|
c37636215b | ||
|
|
1f77b19ced | ||
|
|
9853ebf02f | ||
|
|
6e13aa0bc9 | ||
|
|
1b7f0e0f1e | ||
|
|
18cb3360b5 | ||
|
|
6fec598b96 | ||
|
|
40096ecdfb | ||
|
|
43061b80b8 | ||
|
|
aa3d657d42 | ||
|
|
b0ef385769 | ||
|
|
dcf24e6292 | ||
|
|
2fdd77737c | ||
|
|
31c98ef1ba | ||
|
|
ceeec2c13a | ||
|
|
cc82e0cff8 | ||
|
|
29fc6c59e9 | ||
|
|
aa931fab7b | ||
|
|
8e2585a5c0 | ||
|
|
c115047f74 | ||
|
|
0ac58cb137 | ||
|
|
492e0977c3 | ||
|
|
e8a09e81f3 | ||
|
|
4fcc2c6208 | ||
|
|
e3523dc5fe | ||
|
|
acf6c31a36 | ||
|
|
339b2e696c | ||
|
|
6417fd49d6 | ||
|
|
374fd4db65 | ||
|
|
0b70dd9e38 | ||
|
|
55b19a7922 | ||
|
|
beb1b2655e | ||
|
|
6a81e48c3a | ||
|
|
f9841dd3df | ||
|
|
b563c773c1 | ||
|
|
dcda649d9d | ||
|
|
d6cfd23ae5 | ||
|
|
b69f62c9a4 | ||
|
|
582ee7d623 | ||
|
|
7c4202186d | ||
|
|
7c56e4c09d | ||
|
|
b10b466c61 | ||
|
|
80d1986c61 | ||
|
|
7f9e413604 | ||
|
|
4dfbc3830f | ||
|
|
46cdefa9ee | ||
|
|
f857ea6095 | ||
|
|
994920f99f | ||
|
|
130d4096d5 | ||
|
|
d5659529d7 | ||
|
|
f6e06ab16e | ||
|
|
4245eaf1b2 | ||
|
|
c41bd702e2 | ||
|
|
3911145f87 | ||
|
|
350f5c4266 | ||
|
|
ed4056bc8b | ||
|
|
0d96fa21b7 | ||
|
|
0ee8d33d36 | ||
|
|
64cedec12b | ||
|
|
a16eaca42e | ||
|
|
366b3b1b75 | ||
|
|
33963c085a | ||
|
|
f3b18ef518 | ||
|
|
bca1b45fd4 | ||
|
|
3448d5ef03 | ||
|
|
0f5a6194ff | ||
|
|
9266faec59 | ||
|
|
94347c6dde | ||
|
|
7a07701be0 | ||
|
|
42519a4f75 | ||
|
|
c898c16430 | ||
|
|
318863bd18 | ||
|
|
ce94760d02 | ||
|
|
99759b5282 | ||
|
|
d9d8562ed9 | ||
|
|
dee55ea874 | ||
|
|
da413cc9e6 | ||
|
|
c72e93a13d | ||
|
|
21f910a3ca | ||
|
|
2c51edb4c2 | ||
|
|
da2c893721 | ||
|
|
e7ce693e50 | ||
|
|
29e8fe7904 | ||
|
|
baf3a6077e | ||
|
|
e030e7a32d | ||
|
|
125566ed75 | ||
|
|
c51cb67519 | ||
|
|
57f086b530 | ||
|
|
d1e17785b8 | ||
|
|
774328a1d8 | ||
|
|
175a0f48fa | ||
|
|
60b91ff032 | ||
|
|
2b003bc5b0 | ||
|
|
029a2fcab1 | ||
|
|
aa47f7fe4a | ||
|
|
a828113d9b | ||
|
|
ab4d509eda | ||
|
|
1296f32fa8 | ||
|
|
5aa1ec9876 | ||
|
|
f2cf1ab226 | ||
|
|
2d34c2a40b | ||
|
|
a7181e865c | ||
|
|
0a71b788b3 | ||
|
|
1ed3d84043 | ||
|
|
f472b75d0d | ||
|
|
072a9d1ccd | ||
|
|
8e26d1be68 | ||
|
|
c5871e9025 | ||
|
|
f26911b358 | ||
|
|
3a595b80b5 | ||
|
|
45760ddd41 | ||
|
|
447d068bf1 | ||
|
|
7e1642a4a3 | ||
|
|
7781267e78 | ||
|
|
f824fdcfed | ||
|
|
79dd441967 | ||
|
|
9dcf9c0785 | ||
|
|
83569462c6 | ||
|
|
947e169c3a | ||
|
|
e6b36c52d1 | ||
|
|
7fed3b84fa | ||
|
|
cbc941f193 | ||
|
|
a7ef0fb85f | ||
|
|
d10c36eaf5 | ||
|
|
e4c3a66753 | ||
|
|
f58f2cdc90 | ||
|
|
8f2676e91e | ||
|
|
89d397898e | ||
|
|
2dc32be9ee | ||
|
|
769220bd82 | ||
|
|
c75aa5dd52 | ||
|
|
88f1cfd8c4 | ||
|
|
108279c1aa | ||
|
|
8150490aac | ||
|
|
9497aa6362 | ||
|
|
3769da48a1 | ||
|
|
3c21fcd53a | ||
|
|
102ef2795d | ||
|
|
0befa253c2 | ||
|
|
87189cee58 | ||
|
|
3284f13fc6 | ||
|
|
70d219b120 | ||
|
|
a5ed3a08ea | ||
|
|
3665e7e999 | ||
|
|
9b583b23ae | ||
|
|
d602415e98 | ||
|
|
2c49a423e2 | ||
|
|
8a266e70c8 | ||
|
|
0df3bcd65d | ||
|
|
f5ecf5648e | ||
|
|
821f82fc3f | ||
|
|
92305fe628 | ||
|
|
17fe57b8a5 | ||
|
|
47433a51d3 | ||
|
|
e090827ced | ||
|
|
856e6a8b74 | ||
|
|
a813b4d40e | ||
|
|
98afc27442 | ||
|
|
9d4f28a395 | ||
|
|
50266d9a56 | ||
|
|
602a532cf2 | ||
|
|
b23d610c94 | ||
|
|
119c43d527 | ||
|
|
124feeb790 | ||
|
|
2c0e7e84da | ||
|
|
0d1e105343 | ||
|
|
f040c89ab3 | ||
|
|
5e9d8298ef | ||
|
|
7766ca2793 | ||
|
|
fb2f22f120 | ||
|
|
146fed3504 | ||
|
|
0b7a6e3375 | ||
|
|
f1fbdeaca0 | ||
|
|
ee7e37f6bc | ||
|
|
861f665ab3 | ||
|
|
456de23ad4 | ||
|
|
6d3578c17e | ||
|
|
57da7267e2 | ||
|
|
578b764406 | ||
|
|
f65a38a085 | ||
|
|
d187757bbc | ||
|
|
df83b24cf4 | ||
|
|
8e31ce0f5b | ||
|
|
4f69eb7963 | ||
|
|
4b9009216b | ||
|
|
895076c837 | ||
|
|
6e398ee64a | ||
|
|
c2177272b5 | ||
|
|
4877de5839 | ||
|
|
3cb9d10126 | ||
|
|
23c53cd9fb | ||
|
|
3471743a63 | ||
|
|
1b0b1809ef | ||
|
|
0833cffead | ||
|
|
a0a8ae88b7 | ||
|
|
e415cbeca4 | ||
|
|
4c15c93381 | ||
|
|
bd19822112 | ||
|
|
b87beb4a6e | ||
|
|
62a58facb2 | ||
|
|
5fa8178df7 | ||
|
|
d6043e7d1f | ||
|
|
8e432dfbb4 | ||
|
|
324ab3744b | ||
|
|
33ee4d0418 | ||
|
|
cbb104ccba | ||
|
|
0bda5461ae | ||
|
|
4096a03de1 | ||
|
|
fbee5f83df | ||
|
|
84a1b19850 | ||
|
|
1df5bd6d03 | ||
|
|
8bd4221a85 | ||
|
|
4d97ccdfb3 | ||
|
|
ca5e57ddbf | ||
|
|
ed6511799b | ||
|
|
9b1223ec15 | ||
|
|
97210ee67a | ||
|
|
308d1003ad | ||
|
|
ce9e193958 | ||
|
|
2a83afa8c1 | ||
|
|
8891fa328b | ||
|
|
a23c06c456 | ||
|
|
34e87121e1 | ||
|
|
6e5c20f442 | ||
|
|
8ac3bec451 | ||
|
|
5128d0f405 | ||
|
|
6ae2121391 | ||
|
|
749e0c5a47 | ||
|
|
7b55ef85e1 | ||
|
|
23ec35d1f9 | ||
|
|
ff089c2b9f | ||
|
|
1ab088718a | ||
|
|
0cb2e5857a | ||
|
|
7c5ae5d7ee | ||
|
|
6e2665d98d | ||
|
|
b3b4692237 | ||
|
|
55ff1d4999 | ||
|
|
511c839237 | ||
|
|
384cd6e5d9 | ||
|
|
6fd9dcc72e | ||
|
|
12ca06fe91 | ||
|
|
652f6c651b | ||
|
|
7dd808a772 | ||
|
|
945e55f8a7 | ||
|
|
866a30abe5 | ||
|
|
f0e33e1e4e | ||
|
|
8723ca38b4 | ||
|
|
efffca0026 | ||
|
|
6c5eb3035f | ||
|
|
0bf385f2ca | ||
|
|
9d5f51f513 | ||
|
|
a1448131d8 | ||
|
|
664cacc7ac | ||
|
|
1aec37dd05 | ||
|
|
3ff2c0840e | ||
|
|
5ca3c3ac52 | ||
|
|
714ee50965 | ||
|
|
ae1df3c5aa | ||
|
|
28afb39550 | ||
|
|
3466a8a024 | ||
|
|
90db3321f5 | ||
|
|
bf4e5ed7c2 | ||
|
|
d3b2c20c26 | ||
|
|
c3afabb4b1 | ||
|
|
18cc9790d1 | ||
|
|
d5ed3aa674 | ||
|
|
cfb9c9acfe | ||
|
|
67feef0b1d | ||
|
|
e0192ab5aa | ||
|
|
eae7c11c55 | ||
|
|
b8251e1ade | ||
|
|
2ff2b9f348 | ||
|
|
164e3f3343 | ||
|
|
4901a1bd87 | ||
|
|
cd194cca65 | ||
|
|
1748efbc18 | ||
|
|
4d60b40403 | ||
|
|
6e5a41879f | ||
|
|
c5f9eb54da | ||
|
|
aebbfeb061 | ||
|
|
a0a5a6b578 | ||
|
|
6bdafb85d7 | ||
|
|
0dd7debf5d | ||
|
|
962b9ee7af | ||
|
|
15bcc5df88 | ||
|
|
8495d834c8 | ||
|
|
7282399709 | ||
|
|
780bdd6e7e | ||
|
|
ad08e7c67f | ||
|
|
ff089964ca | ||
|
|
990191529a | ||
|
|
ce1bec12b4 | ||
|
|
703ea9af53 | ||
|
|
df0227ae1e | ||
|
|
486d12b6ac | ||
|
|
9c18375ab4 | ||
|
|
e1708aed68 | ||
|
|
a42d778b84 | ||
|
|
7692b59c7c | ||
|
|
82de9b36b3 | ||
|
|
49b3c8f65f | ||
|
|
90ad32d936 | ||
|
|
2509e91f1a | ||
|
|
80dc2219e4 | ||
|
|
931cb0fa7d | ||
|
|
4383f2ea90 | ||
|
|
add79dc242 | ||
|
|
b6e142f04c | ||
|
|
105f6c3041 | ||
|
|
4babbb65c1 | ||
|
|
878159f7ed | ||
|
|
c320386019 | ||
|
|
1e7a0159f0 | ||
|
|
bbf4f1d1d3 | ||
|
|
8cd1c69c76 | ||
|
|
2372a878ac | ||
|
|
9bec644997 | ||
|
|
650d38dff8 | ||
|
|
097277e397 | ||
|
|
82a4d5eedf | ||
|
|
2a00248812 | ||
|
|
9841c773cb | ||
|
|
b21a78ad14 | ||
|
|
7329fa597d | ||
|
|
f1d8f0ecb4 | ||
|
|
1d127f2364 | ||
|
|
b244405cc3 | ||
|
|
38adfe0ca6 | ||
|
|
6f0d798847 | ||
|
|
6e58bfd2b0 | ||
|
|
bc2e8d8ac4 | ||
|
|
7be654d47f | ||
|
|
bfe46e3604 | ||
|
|
008b597fc5 | ||
|
|
c4b4f2e3b1 | ||
|
|
76ee97301b | ||
|
|
289dc09eae | ||
|
|
f23f84f0f3 | ||
|
|
62259f3295 | ||
|
|
854a430a12 | ||
|
|
d27cdb5637 | ||
|
|
7d12d9ee90 | ||
|
|
d70a4ff347 | ||
|
|
49541558d1 | ||
|
|
7b00a1227c | ||
|
|
7800603c81 | ||
|
|
fca00c8116 | ||
|
|
00fa549e44 | ||
|
|
36181b6b87 | ||
|
|
88a95162e9 | ||
|
|
9c1d59a2c8 | ||
|
|
fdd894956a | ||
|
|
eb6dc0859d | ||
|
|
b99026bba2 | ||
|
|
6fa50eb8d5 | ||
|
|
e5046f15a9 | ||
|
|
09f1c066a0 | ||
|
|
ed2f0b34c9 |
4
.github/workflows/node.yml
vendored
@@ -9,12 +9,12 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 17
|
||||
node-version: 16
|
||||
- run: npm ci
|
||||
- run: sudo apt-get install xvfb
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
- name: Test
|
||||
run: xvfb-run --auto-servernum npm run jest
|
||||
run: npm install --no-save "gl@^5.0.0" && xvfb-run --auto-servernum npm run jest
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
232
CHANGELOG.md
@@ -6,6 +6,238 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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
|
||||
- Improve performance of inter/intra-bonds compute
|
||||
- Fix defaultAttribs handling in Canvas3DContext.fromCanvas
|
||||
- Confal pyramids extension improvements
|
||||
- Add custom labels to Confal pyramids
|
||||
- Improve naming of some internal types in Confal pyramids extension coordinate
|
||||
- Add example mmCIF file with categories necessary to display Confal pyramids
|
||||
- Change the lookup logic of NtC steps from residues
|
||||
- Add support for download of gzipped files
|
||||
- Don't filter IndexPairBonds by element-based rules in MOL/SDF and MOL2 (without symmetry) models
|
||||
- Fix Glycam Saccharide Names used by default
|
||||
- Fix GPU surfaces rendering in Safari with WebGL2
|
||||
- Add ``fov`` (Field of View) Canvas3D parameter
|
||||
- Add ``sceneRadiusFactor`` Canvas3D parameter
|
||||
- Add background pass (skybox, image, horizontal/radial gradient)
|
||||
- Set simple-settings presets via ``PluginConfig.Background.Styles``
|
||||
- Example presets in new backgrounds extension
|
||||
- Load skybox/image from URL or File (saved in session)
|
||||
- Opacity, saturation, lightness controls for skybox/image
|
||||
- Coverage (viewport or canvas) controls for image/gradient
|
||||
- [Breaking] ``AssetManager`` needs to be passed to various graphics related classes
|
||||
- Fix SSAO renderable initialization
|
||||
- Reduce number of webgl state changes
|
||||
- Add ``viewport`` and ``scissor`` to state object
|
||||
- Add ``hasOpaque`` to scene object
|
||||
- Handle edge cases where some renderables would not get (correctly) rendered
|
||||
- Fix text background rendering for opaque text
|
||||
- Fix helper scenes not shown when rendering directly to draw target
|
||||
- Fix ``CustomElementProperty`` coloring not working
|
||||
|
||||
## [v3.13.0] - 2022-07-24
|
||||
|
||||
- Fix: only update camera state if manualReset is off (#494)
|
||||
- Improve handling principal axes of points in a plane
|
||||
- Add 'material' annotation support for textures
|
||||
- More effort to avoid using ``flat`` qualifier in shaders: add ``dVaryingGroup``
|
||||
- Enable ``immediateUpdate`` for iso level in isosurface and volume streaming controls
|
||||
- Add support to download CCD from configurable URL
|
||||
|
||||
## [v3.12.1] - 2022-07-20
|
||||
|
||||
- Fix plugin behavior dispose logic to correctly unsubscribe observables.
|
||||
|
||||
## [v3.12.0] - 2022-07-17
|
||||
|
||||
- Add ``colorMarker`` option to Renderer. This disables the highlight and select marker at a shader level for faster rendering of large scenes in some cases.
|
||||
- Bind shared textures only once per pass, not for each render item
|
||||
- Fix missing 'material' annotation for some uniforms, causing unnecessary uniform updates
|
||||
- Remove use of ``isnan`` in impostor shaders, not needed and causing slowdown
|
||||
- Avoid using ``flat`` qualifier in shaders, causing slowdown
|
||||
- Improve CellPack's ``adjustStyle`` option (disable ``colorMarker``, set component options, enable marking w/o ghost)
|
||||
- Scan all entities when looking for ``struct_conn`` entries (fixes issue when the same ``label_asym_id`` is used in more than one entity)
|
||||
|
||||
## [v3.11.0] - 2022-07-04
|
||||
|
||||
- Add ``instanceGranularity`` option for marker, transparency, clipping, overpaint, substance data to save memory
|
||||
- CellPack extension tweaks
|
||||
- Use instancing to create DNA/RNA curves to save memory
|
||||
- Enable ``instanceGranularity`` by default
|
||||
- Add ``adjustStyle`` option to LoadCellPackModel action (stylized, no multi-sample, no far clipping, chain picking)
|
||||
- Structure Superposition now respects pivot's coordinate system
|
||||
|
||||
## [v3.10.2] - 2022-06-26
|
||||
|
||||
- Fix superfluous shader varying
|
||||
- Improve use of gl_VertexID when possible
|
||||
|
||||
## [v3.10.1] - 2022-06-26
|
||||
|
||||
- Fix groupCount when updating TextureMesh-based visuals
|
||||
|
||||
## [v3.10.0] - 2022-06-24
|
||||
|
||||
- Add support for Glycam saccharide names
|
||||
- Add ``PluginConfig.Viewport.ShowTrajectoryControls`` config option
|
||||
|
||||
## [v3.9.1] - 2022-06-19
|
||||
|
||||
- Fix missing ``super.componentWillUnmount()`` calls (@simeonborko)
|
||||
- Fix missing ``uGroupCount`` update for visuals
|
||||
- Fix missing aromatic bond display
|
||||
|
||||
## [v3.9.0] - 2022-05-30
|
||||
|
||||
- Improve picking by using drawbuffers (when available) to reduce number of drawcalls
|
||||
- GPU timing support
|
||||
- Add ``timing-mode`` Viewer GET param
|
||||
- Add support for webgl timer queries
|
||||
- Add timer marks around GPU render & compute operations
|
||||
- Volume Server CIF: Add check that a data block contains volume data before parsing
|
||||
- Fix ``Scene.clear`` not clearing primitives & volumes arrays (@JonStargaryen)
|
||||
- Fix rendering volumes when wboit is switched off and postprocessing is enabled
|
||||
|
||||
## [v3.8.2] - 2022-05-22
|
||||
|
||||
- Fix ``Scene.opacityAverage`` not taking xray shaded into account
|
||||
|
||||
## [v3.8.1] - 2022-05-14
|
||||
|
||||
- Fix issues with marking camera/handle helper (#433)
|
||||
- Fix issues with array uniforms when running with headless-gl
|
||||
- Fix Polymer Chain Instance coloring
|
||||
- Improve performance of scene marker/opacity average calculation
|
||||
|
||||
## [v3.8.0] - 2022-04-30
|
||||
|
||||
- Add support for outlines around transparent objects
|
||||
- Improve per-group transparency when wboit is switched off
|
||||
- Improve ``ColorTheme`` typing with ``ColorType`` generic.
|
||||
- Defaults to ``ColorTypeLocation``
|
||||
- Set when using ``ColorTypeDirect`` or ``ColorTypeGrid``
|
||||
- Fix case handling of ``struct_conf`` mmCIF enumeration field (#425)
|
||||
- Fix ``allowTransparentBackfaces`` for per-group transparency
|
||||
- Fix ``FormatRegistry.isApplicable`` returning true for unregistered formats
|
||||
- Fix: handle building of ``GridLookup3D`` with zero cell size
|
||||
- Fix ``ignoreLight`` for direct-volume rendering with webgl1
|
||||
- Fix (non-black) outlines when using transparent background
|
||||
|
||||
## [v3.7.0] - 2022-04-13
|
||||
|
||||
- Fix ``xrayShaded`` for texture-mesh geometries
|
||||
- [Breaking] Change ``allowTransparentBackfaces`` to ``transparentBackfaces`` with options ``off``, ``on``, ``opaque``. This was only added in 3.6.0, so allowing a breaking change here.
|
||||
- ``off``: don't show (default)
|
||||
- ``on``: show with transparency
|
||||
- ``opaque``: show fully opaque
|
||||
- Add option to disable file drop overlay.
|
||||
|
||||
## [v3.6.2] - 2022-04-05
|
||||
|
||||
- ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
|
||||
- React 18 friendly ``useBehavior`` hook.
|
||||
|
||||
## [v3.6.1] - 2022-04-03
|
||||
|
||||
- Fix React18 related UI regressions.
|
||||
|
||||
## [v3.6.0] - 2022-04-03
|
||||
|
||||
- Check that model and coordinates have same element count when creating a trajectory
|
||||
- Fix aromatic rings assignment: do not mix flags and planarity test
|
||||
- Improve bonds assignment of coarse grained models: check for IndexPairBonds and exhaustive StructConn
|
||||
- Fix unit mapping in bondedAtomicPairs MolScript query
|
||||
- Improve pdb parsing: handle non unique atom and chain names (fixes #156)
|
||||
- Fix volume streaming for entries with multiple contour lists
|
||||
- Add ``allowTransparentBackfaces`` parameter to support double-sided rendering of transparent geometries
|
||||
- Fix handling of case insensitive mmCIF enumeration fields (including entity.type)
|
||||
- Fix ``disable-wboit`` Viewer GET param
|
||||
- Add support for React 18.
|
||||
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
|
||||
- In Mol* 4.0, React 18 will become the default option.
|
||||
|
||||
## [v3.5.0] - 2022-03-25
|
||||
|
||||
- Fix issues with bounding-sphere & color-smoothing (mostly for small geometries)
|
||||
- Support BCIF => CIF conversion in ``cif2bcif`` CLI tool
|
||||
|
||||
## [v3.4.0] - 2022-03-13
|
||||
|
||||
- Fix handling of mmcif with empty ``label_*`` fields
|
||||
- Improve saccharide detection (compare against list from CCD)
|
||||
- Fix legend label of hydrophobicity color theme
|
||||
- Add ``LoadTrajectory`` action
|
||||
- Add ``CustomImportControls`` to left panel
|
||||
- Add Zenodo import extension (load structures, trajectories, volumes, and zip files)
|
||||
- Fix loading of some compressed files within sessions
|
||||
- Fix wrong element assignment for atoms with Charmm ion names
|
||||
- Fix handling of empty symmetry cell data
|
||||
- Add support for ``trr`` and ``nctraj`` coordinates files
|
||||
- Add support for ``prmtop`` and ``top`` topology files
|
||||
|
||||
## [v3.3.1] - 2022-02-27
|
||||
|
||||
- Fix issue with unit boundary reuse (do at visual level instead)
|
||||
- Add option to ignore ions for inter-unit bond computation
|
||||
|
||||
## [v3.3.0] - 2022-02-27
|
||||
|
||||
- Fix parsing contour-level from emdb v3 header files
|
||||
- Fix invalid CSS (#376)
|
||||
- Fix "texture not renderable" & "texture not bound" warnings (#319)
|
||||
- Fix visual for bonds between two aromatic rings
|
||||
- Fix visual for delocalized bonds (parsed from mmcif and mol2)
|
||||
- Fix ring computation algorithm
|
||||
- Add ``UnitResonance`` property with info about delocalized triplets
|
||||
- Resolve marking in main renderer loop to improve overall performance
|
||||
- Use ``throttleTime`` instead of ``debounceTime`` in sequence viewer for better responsiveness
|
||||
- Change line geometry default ``scaleFactor`` to 2 (3 is too big after fixing line rendering)
|
||||
- Trajectory animation performance improvements
|
||||
- Reuse ``Model.CoarseGrained`` for coordinate trajectories
|
||||
- Avoid calculating ``InterUnitBonds`` when ``Structure.parent`` ones are empty
|
||||
- Reuse unit boundary if sphere has not changed too much
|
||||
- Don't show 'inter-bond' and 'element-cross' visuals in line representations of polymerAndLigand preset
|
||||
- Fix additional mononucleotides detected as polymer components
|
||||
- Fix and improve ``canRemap`` handling in ``IntraUnitBonds``
|
||||
- Reuse occlusion for secondary passes during multi-sampling
|
||||
- Check if marking passes are needed before doing them
|
||||
- Add ``resolutionScale`` parameter to allow trading quality of occlusion for performance
|
||||
|
||||
## [v3.2.0] - 2022-02-17
|
||||
|
||||
- Rename "best database mapping" to "SIFTS Mapping"
|
||||
- Add schema and export support for ``atom_site.pdbx_sifts_xref_*`` fields
|
||||
- Add schema export support for ``atom_site.pdbx_label_index`` field
|
||||
- Add `traceOnly` parameter to chain/UniProt-based structure alignment
|
||||
- Store ``IndexPairBonds`` as a dynamic property.
|
||||
|
||||
## [v3.1.0] - 2022-02-06
|
||||
|
||||
- Fix ``xrayShaded`` & ``ignoreLight`` params not working at the same time
|
||||
- Add ``ignoreLight`` to component params
|
||||
- Tweaks for cleaner default representation style
|
||||
- Cartoon: use ``nucleotide-ring`` instead of ``nucleotide-block``
|
||||
- Focus: use ``xrayShaded`` instead of opacity; adjust target size; don't show non-covalent interactions twice
|
||||
- Fix representation preset side effects (changing post-processing parameters, see #363)
|
||||
- Add Quick Styles panel (default, illustrative, stylized)
|
||||
- Fix exported structure missing secondary-structure categories (#364)
|
||||
- Fix volume streaming error message: distinguish between missing data and server error (#364)
|
||||
|
||||
## [v3.0.2] - 2022-01-30
|
||||
|
||||
- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
|
||||
|
||||
12
README.md
@@ -120,10 +120,13 @@ and navigate to `build/viewer`
|
||||
|
||||
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-ions.js src/mol-model/structure/model/types/ions.ts
|
||||
|
||||
**Saccharide names**
|
||||
|
||||
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-saccharides.js src/mol-model/structure/model/types/saccharides.ts
|
||||
|
||||
**GraphQL schemas**
|
||||
|
||||
node node_modules//@graphql-codegen/cli/bin -c src/extensions/rcsb/graphql/codegen.yml
|
||||
node node_modules/@graphql-codegen/cli/cjs/bin -c src/extensions/rcsb/graphql/codegen.yml
|
||||
|
||||
### Other scripts
|
||||
**Create chem comp bond table**
|
||||
@@ -138,7 +141,7 @@ and navigate to `build/viewer`
|
||||
|
||||
export NODE_PATH="lib"; node build/state-docs
|
||||
|
||||
**Convert any CIF to BinaryCIF**
|
||||
**Convert any CIF to BinaryCIF (or vice versa)**
|
||||
|
||||
node lib/commonjs/servers/model/preprocess -i file.cif -ob file.bcif
|
||||
|
||||
@@ -148,6 +151,11 @@ Or
|
||||
|
||||
node lib/commonjs/cli/cif2bcif
|
||||
|
||||
E.g.
|
||||
|
||||
node lib/commonjs/cli/cif2bcif src.cif out.bcif.gz
|
||||
node lib/commonjs/cli/cif2bcif src.bcif.gz out.cif
|
||||
|
||||
## Development
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -24,6 +24,11 @@ atom_site.auth_asym_id
|
||||
atom_site.auth_seq_id
|
||||
atom_site.pdbx_PDB_model_num
|
||||
atom_site.ihm_model_id
|
||||
atom_site.pdbx_label_index
|
||||
atom_site.pdbx_sifts_xref_db_name
|
||||
atom_site.pdbx_sifts_xref_db_acc
|
||||
atom_site.pdbx_sifts_xref_db_num
|
||||
atom_site.pdbx_sifts_xref_db_res
|
||||
|
||||
atom_site_anisotrop.id
|
||||
atom_site_anisotrop.U
|
||||
|
||||
|
44
docs/file-formats.md
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
Support file formats and their extensions.
|
||||
|
||||
## Structure
|
||||
|
||||
- MMCIF and CIFCORE (mmCIF and coreCIF schemas): cif, bcif, mmcif, mcif
|
||||
- GRO: gro
|
||||
- MOL: mol
|
||||
- MOL2: mol2
|
||||
- PDB/PDBQT: pdb, ent, pdbqt
|
||||
- SDF: sdf, sd
|
||||
- XYZ: xyz
|
||||
|
||||
|
||||
## Topology
|
||||
|
||||
Need to be loaded together with Coordinates.
|
||||
|
||||
- PRMTOP: prmtop, parm7
|
||||
- PSF: psf
|
||||
- TOP: top
|
||||
|
||||
## Coordinates
|
||||
|
||||
Need to be loaded together with a Structure or Topology.
|
||||
|
||||
- DCD: dcd
|
||||
- NCTRAJ: nc, nctraj
|
||||
- TRR: trr
|
||||
- XTC: xtc
|
||||
|
||||
|
||||
## Volume
|
||||
|
||||
- CCP4/MRC/MAP: ccp4, mrc, map
|
||||
- CUBE (may include a Structure): cub, cube
|
||||
- DSN6/BRIX: dsn6, brix
|
||||
- DX and DXBIN: dx, dxbin
|
||||
- DSCIF (DensityServer CIF schema): cif, bcif
|
||||
|
||||
|
||||
## Shape
|
||||
|
||||
- PLY
|
||||
@@ -13,6 +13,7 @@
|
||||
* DZ has C1 instead of N1 (e.g. 6I4N)
|
||||
* DP has N5 instead of C5 and C7 instead of N7 (e.g. 6I4N)
|
||||
* Beta & Gamma peptides (e.g. 1GAC, 6PQF)
|
||||
* Helices of D-amino acids (e.g. 7QDI)
|
||||
* Mixed (heterogeneous) all-atom/trace-only RNA model (1JGQ)
|
||||
* Polymers with residues with missing trace atoms (e.g. 2QFJ)
|
||||
* Modified RNA bases (1y26, 5L4O)
|
||||
@@ -34,6 +35,14 @@
|
||||
* ACE (many, e.g. 5AGU, 1E1X)
|
||||
* ACY in 7ABY
|
||||
* NH2 (many, e.g. 6Y13)
|
||||
* Ligands with many rings
|
||||
* STU (e.g. 1U59) - many fused rings
|
||||
* HT (e.g. 127D) - rings connected by a single bond
|
||||
* J2C (e.g. 7EFJ) - rings connected by a single atom
|
||||
* RBF (e.g. 7QF2) - three linearly fused rings
|
||||
* TA1 (e.g. 1JFF) - many fused rings (incl. a 8-member rings)
|
||||
* BPA (e.g. 1JDG) - many fused rings
|
||||
* CLR (e.g. 3GKI) - four fused rings
|
||||
|
||||
Assembly symmetries
|
||||
* 5M30 (Assembly 1, C3 local and pseudo)
|
||||
|
||||
1694
examples/1bna_confal_pyramids.cif
Normal file
42360
package-lock.json
generated
95
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "3.0.2",
|
||||
"version": "3.16.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -20,7 +20,7 @@
|
||||
"rebuild": "npm run clean && npm run build",
|
||||
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
|
||||
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
|
||||
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
|
||||
"build-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/",
|
||||
"build-webpack": "webpack --mode production --config ./webpack.config.production.js",
|
||||
"build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
|
||||
"watch": "concurrently -c \"green,green,gray,gray\" --names \"tsc,srv,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-servers\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
@@ -28,7 +28,7 @@
|
||||
"watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
|
||||
"watch-tsc": "tsc --watch --incremental",
|
||||
"watch-servers": "tsc --build tsconfig.commonjs.json --watch --incremental",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --stats minimal",
|
||||
"watch-webpack-viewer": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.js",
|
||||
"watch-webpack-viewer-debug": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.debug.js",
|
||||
@@ -75,7 +75,9 @@
|
||||
"node_modules",
|
||||
"lib"
|
||||
],
|
||||
"testURL": "http://localhost/",
|
||||
"testEnvironmentOptions": {
|
||||
"url": "http://localhost/"
|
||||
},
|
||||
"testRegex": "\\.spec\\.ts$"
|
||||
},
|
||||
"author": "Mol* Contributors",
|
||||
@@ -87,74 +89,77 @@
|
||||
"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>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^3.1.1",
|
||||
"@graphql-codegen/cli": "^2.4.0",
|
||||
"@graphql-codegen/time": "^3.1.1",
|
||||
"@graphql-codegen/typescript": "^2.4.2",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.3.3",
|
||||
"@graphql-codegen/typescript-operations": "^2.2.2",
|
||||
"@graphql-codegen/add": "^3.2.1",
|
||||
"@graphql-codegen/cli": "^2.11.6",
|
||||
"@graphql-codegen/time": "^3.2.1",
|
||||
"@graphql-codegen/typescript": "^2.7.3",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^4.5.3",
|
||||
"@graphql-codegen/typescript-operations": "^2.5.3",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"@types/gl": "^4.1.1",
|
||||
"@types/jest": "^28.1.7",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"concurrently": "^7.0.0",
|
||||
"cpx2": "^4.1.2",
|
||||
"concurrently": "^7.3.0",
|
||||
"cpx2": "^4.2.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.7.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^8.22.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"graphql": "^16.2.0",
|
||||
"http-server": "^14.1.0",
|
||||
"jest": "^27.4.7",
|
||||
"mini-css-extract-plugin": "^2.5.2",
|
||||
"fs-extra": "^10.1.0",
|
||||
"graphql": "^16.6.0",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^28.1.3",
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "^1.49.0",
|
||||
"sass-loader": "^12.4.0",
|
||||
"simple-git": "^2.48.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.54.5",
|
||||
"sass-loader": "^13.0.2",
|
||||
"simple-git": "^3.12.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.1.3",
|
||||
"typescript": "^4.5.5",
|
||||
"webpack": "^5.67.0",
|
||||
"webpack-cli": "^4.9.1"
|
||||
"ts-jest": "^28.0.8",
|
||||
"typescript": "^4.7.4",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.10",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "^16.11.21",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/node": "^16.11.51",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/swagger-ui-dist": "3.30.1",
|
||||
"argparse": "^2.0.1",
|
||||
"body-parser": "^1.19.1",
|
||||
"body-parser": "^1.20.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.2",
|
||||
"express": "^4.18.1",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^9.0.12",
|
||||
"immutable": "^4.0.0",
|
||||
"immer": "^9.0.15",
|
||||
"immutable": "^4.1.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"rxjs": "^7.5.2",
|
||||
"swagger-ui-dist": "^4.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"rxjs": "^7.5.6",
|
||||
"swagger-ui-dist": "^4.14.0",
|
||||
"tslib": "^2.4.0",
|
||||
"util.promisify": "^1.1.1",
|
||||
"xhr2": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react": "^18.1.0 || ^17.0.2 || ^16.14.0",
|
||||
"react-dom": "^18.1.0 || ^17.0.2 || ^16.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
|
||||
@@ -44,10 +44,11 @@ function occlusionStyle(plugin: PluginContext) {
|
||||
postprocessing: {
|
||||
...plugin.canvas3d!.props.postprocessing,
|
||||
occlusion: { name: 'on', params: {
|
||||
samples: 64,
|
||||
radius: 8,
|
||||
bias: 1.0,
|
||||
blurKernelSize: 13
|
||||
bias: 0.8,
|
||||
blurKernelSize: 15,
|
||||
radius: 5,
|
||||
samples: 32,
|
||||
resolutionScale: 1
|
||||
} },
|
||||
outline: { name: 'on', params: {
|
||||
scale: 1.0,
|
||||
|
||||
@@ -17,20 +17,22 @@ import { ModelExport } from '../../extensions/model-export';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { ZenodoImport } from '../../extensions/zenodo';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
|
||||
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
|
||||
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
|
||||
import { BuiltInTopologyFormat } from '../../mol-plugin-state/formats/topology';
|
||||
import { BuiltInCoordinatesFormat } from '../../mol-plugin-state/formats/coordinates';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
@@ -43,15 +45,18 @@ import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import '../../mol-util/polyfill';
|
||||
import { ObjectKeys } from '../../mol-util/type-helpers';
|
||||
import { SaccharideCompIdMapType } from '../../mol-model/structure/structure/carbohydrates/constants';
|
||||
import { Backgrounds } from '../../extensions/backgrounds';
|
||||
|
||||
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
|
||||
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
|
||||
export { setDebugMode, setProductionMode, setTimingMode } from '../../mol-util/debug';
|
||||
|
||||
const CustomFormats = [
|
||||
['g3d', G3dProvider] as const
|
||||
];
|
||||
|
||||
const Extensions = {
|
||||
'backgrounds': PluginSpec.Behavior(Backgrounds),
|
||||
'cellpack': PluginSpec.Behavior(CellPack),
|
||||
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
|
||||
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
|
||||
@@ -63,6 +68,7 @@ const Extensions = {
|
||||
'mp4-export': PluginSpec.Behavior(Mp4Export),
|
||||
'geo-export': PluginSpec.Behavior(GeometryExport),
|
||||
'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
|
||||
'zenodo-import': PluginSpec.Behavior(ZenodoImport),
|
||||
};
|
||||
|
||||
const DefaultViewerOptions = {
|
||||
@@ -89,11 +95,13 @@ const DefaultViewerOptions = {
|
||||
viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
|
||||
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
|
||||
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
|
||||
viewportShowTrajectoryControls: PluginConfig.Viewport.ShowTrajectoryControls.defaultValue,
|
||||
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
|
||||
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
|
||||
volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
|
||||
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
|
||||
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
|
||||
saccharideCompIdMapType: 'default' as SaccharideCompIdMapType,
|
||||
};
|
||||
type ViewerOptions = typeof DefaultViewerOptions;
|
||||
|
||||
@@ -156,6 +164,7 @@ export class Viewer {
|
||||
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
|
||||
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
|
||||
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
|
||||
[PluginConfig.Viewport.ShowTrajectoryControls, o.viewportShowTrajectoryControls],
|
||||
[PluginConfig.State.DefaultServer, o.pluginStateServer],
|
||||
[PluginConfig.State.CurrentServer, o.pluginStateServer],
|
||||
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
|
||||
@@ -163,6 +172,7 @@ export class Viewer {
|
||||
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
|
||||
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
|
||||
[PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
|
||||
[PluginConfig.Structure.SaccharideCompIdMapType, o.saccharideCompIdMapType],
|
||||
]
|
||||
};
|
||||
|
||||
@@ -189,7 +199,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: {
|
||||
@@ -198,6 +208,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 },
|
||||
}
|
||||
}
|
||||
@@ -394,7 +405,7 @@ export class Viewer {
|
||||
async loadTrajectory(params: LoadTrajectoryParams) {
|
||||
const plugin = this.plugin;
|
||||
|
||||
let model: StateObjectSelector, coords: StateObjectSelector;
|
||||
let model: StateObjectSelector;
|
||||
|
||||
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
|
||||
const data = params.model.kind === 'model-data'
|
||||
@@ -412,14 +423,12 @@ export class Viewer {
|
||||
model = await provider!.parse(plugin, data);
|
||||
}
|
||||
|
||||
{
|
||||
const data = params.coordinates.kind === 'coordinates-data'
|
||||
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
|
||||
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
|
||||
const data = params.coordinates.kind === 'coordinates-data'
|
||||
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
|
||||
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
|
||||
|
||||
const provider = plugin.dataFormats.get(params.coordinates.format);
|
||||
coords = await provider!.parse(plugin, data);
|
||||
}
|
||||
const provider = plugin.dataFormats.get(params.coordinates.format);
|
||||
const coords = await provider!.parse(plugin, data);
|
||||
|
||||
const trajectory = await plugin.build().toRoot()
|
||||
.apply(TrajectoryFromModelAndCoordinates, {
|
||||
@@ -453,11 +462,11 @@ export interface VolumeIsovalueInfo {
|
||||
export interface LoadTrajectoryParams {
|
||||
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
|
||||
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
|
||||
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
| { kind: 'topology-url', url: string, format: BuiltInTopologyFormat, isBinary?: boolean }
|
||||
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInTopologyFormat },
|
||||
modelLabel?: string,
|
||||
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
coordinates: { kind: 'coordinates-url', url: string, format: BuiltInCoordinatesFormat, isBinary?: boolean }
|
||||
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInCoordinatesFormat },
|
||||
coordinatesLabel?: string,
|
||||
preset?: keyof PresetTrajectoryHierarchy
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -46,7 +46,10 @@
|
||||
}
|
||||
|
||||
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
|
||||
if (debugMode) molstar.setDebugMode(debugMode, debugMode);
|
||||
if (debugMode) molstar.setDebugMode(debugMode);
|
||||
|
||||
var timingMode = getParam('timing-mode', '[^&]+').trim() === '1';
|
||||
if (timingMode) molstar.setTimingMode(timingMode);
|
||||
|
||||
var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
|
||||
var collapseLeftPanel = getParam('collapse-left-panel', '[^&]+').trim() === '1';
|
||||
@@ -71,7 +74,7 @@
|
||||
pixelScale: parseFloat(pixelScale) || 1,
|
||||
pickScale: parseFloat(pickScale) || 0.25,
|
||||
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
|
||||
enableWboit: disableWboit ? true : void 0, // use default value if disable-wboit is not set
|
||||
enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
|
||||
preferWebgl1: preferWebgl1,
|
||||
}).then(viewer => {
|
||||
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 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 Josh McMenemy <josh.mcmenemy@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
@@ -14,7 +15,7 @@ const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
import { DatabaseCollection } from '../../mol-data/db';
|
||||
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
|
||||
import { ensureDataAvailable, readCCD } from './util';
|
||||
import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
|
||||
|
||||
function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
|
||||
const ionNames: string[] = [];
|
||||
@@ -31,11 +32,11 @@ function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
|
||||
|
||||
function writeIonNamesFile(filePath: string, ionNames: string[]) {
|
||||
const output = `/**
|
||||
* 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.
|
||||
*
|
||||
* Code-generated ion names params file. Names extracted from CCD components.
|
||||
*
|
||||
* @author molstar/chem-comp-dict/create-table cli
|
||||
* @author molstar/chem-comp-dict/create-ions cli
|
||||
*/
|
||||
|
||||
export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});
|
||||
@@ -43,8 +44,8 @@ export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").re
|
||||
writeFile(filePath, output);
|
||||
}
|
||||
|
||||
async function run(out: string, forceDownload = false) {
|
||||
await ensureDataAvailable(forceDownload);
|
||||
async function run(out: string, options = DefaultDataOptions) {
|
||||
await ensureDataAvailable(options);
|
||||
const ccd = await readCCD();
|
||||
const ionNames = extractIonNames(ccd);
|
||||
if (!fs.existsSync(path.dirname(out))) {
|
||||
@@ -64,10 +65,15 @@ parser.add_argument('--forceDownload', '-f', {
|
||||
action: 'store_true',
|
||||
help: 'Force download of CCD and PVCD.'
|
||||
});
|
||||
parser.add_argument('--ccdUrl', '-c', {
|
||||
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
|
||||
required: false
|
||||
});
|
||||
interface Args {
|
||||
out: string,
|
||||
forceDownload?: boolean,
|
||||
ccdUrl?: string
|
||||
}
|
||||
const args: Args = parser.parse_args();
|
||||
|
||||
run(args.out, args.forceDownload);
|
||||
run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });
|
||||
|
||||
82
src/cli/chem-comp-dict/create-saccharides.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as path from 'path';
|
||||
import util from 'util';
|
||||
import fs from 'fs';
|
||||
require('util.promisify').shim();
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
import { DatabaseCollection } from '../../mol-data/db';
|
||||
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
|
||||
import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
|
||||
|
||||
function extractSaccharideNames(ccd: DatabaseCollection<CCD_Schema>) {
|
||||
const saccharideNames: string[] = [];
|
||||
for (const k in ccd) {
|
||||
const { chem_comp } = ccd[k];
|
||||
const type = chem_comp.type.value(0).toUpperCase();
|
||||
if (type.includes('SACCHARIDE')) {
|
||||
saccharideNames.push(chem_comp.id.value(0));
|
||||
}
|
||||
}
|
||||
// these are extra saccharides that don't have SACCHARIDE in their type
|
||||
saccharideNames.push(
|
||||
'UMQ', // UNDECYL-MALTOSIDE, via GlyFinder
|
||||
'SQD', // SULFOQUINOVOSYLDIACYLGLYCEROL, via GlyFinder
|
||||
);
|
||||
return saccharideNames;
|
||||
}
|
||||
|
||||
function writeSaccharideNamesFile(filePath: string, ionNames: string[]) {
|
||||
const output = `/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated ion names params file. Names extracted from CCD components.
|
||||
*
|
||||
* @author molstar/cli/chem-comp-dict/create-saccharides
|
||||
*/
|
||||
|
||||
export const SaccharideNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});
|
||||
`;
|
||||
writeFile(filePath, output);
|
||||
}
|
||||
|
||||
async function run(out: string, options = DefaultDataOptions) {
|
||||
await ensureDataAvailable(options);
|
||||
const ccd = await readCCD();
|
||||
const saccharideNames = extractSaccharideNames(ccd);
|
||||
if (!fs.existsSync(path.dirname(out))) {
|
||||
fs.mkdirSync(path.dirname(out));
|
||||
}
|
||||
writeSaccharideNamesFile(out, saccharideNames);
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
add_help: true,
|
||||
description: 'Extract and save SaccharideNames from CCD.'
|
||||
});
|
||||
parser.add_argument('out', {
|
||||
help: 'Generated file output path.'
|
||||
});
|
||||
parser.add_argument('--forceDownload', '-f', {
|
||||
action: 'store_true',
|
||||
help: 'Force download of CCD and PVCD.'
|
||||
});
|
||||
parser.add_argument('--ccdUrl', '-c', {
|
||||
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
|
||||
required: false
|
||||
});
|
||||
interface Args {
|
||||
out: string,
|
||||
forceDownload?: boolean,
|
||||
ccdUrl?: string
|
||||
}
|
||||
const args: Args = parser.parse_args();
|
||||
|
||||
run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });
|
||||
@@ -18,7 +18,7 @@ import { SetUtils } from '../../mol-util/set';
|
||||
import { DefaultMap } from '../../mol-util/map';
|
||||
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
|
||||
import { ccd_chemCompAtom_schema } from '../../mol-io/reader/cif/schema/ccd-extras';
|
||||
import { ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
|
||||
import { DefaultDataOptions, ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
|
||||
|
||||
type CCB = Table<CCD_Schema['chem_comp_bond']>
|
||||
type CCA = Table<CCD_Schema['chem_comp_atom']>
|
||||
@@ -239,8 +239,8 @@ function createAtoms(ccd: DatabaseCollection<CCD_Schema>, pvcd: DatabaseCollecti
|
||||
);
|
||||
}
|
||||
|
||||
async function run(out: string, binary = false, forceDownload = false, ccaOut?: string) {
|
||||
await ensureDataAvailable(forceDownload);
|
||||
async function run(out: string, binary = false, options = DefaultDataOptions, ccaOut?: string) {
|
||||
await ensureDataAvailable(options);
|
||||
const ccd = await readCCD();
|
||||
const pvcd = await readPVCD();
|
||||
|
||||
@@ -283,12 +283,22 @@ parser.add_argument('--ccaOut', '-a', {
|
||||
help: 'Optional generated file output path for chem_comp_atom data.',
|
||||
required: false
|
||||
});
|
||||
parser.add_argument('--ccdUrl', '-c', {
|
||||
help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
|
||||
required: false
|
||||
});
|
||||
parser.add_argument('--pvcdUrl', '-p', {
|
||||
help: 'Fetch the PVCD from a custom URL. This forces download of the PVCD.',
|
||||
required: false
|
||||
});
|
||||
interface Args {
|
||||
out: string,
|
||||
forceDownload?: boolean,
|
||||
binary?: boolean,
|
||||
ccaOut?: string
|
||||
ccaOut?: string,
|
||||
ccdUrl?: string,
|
||||
pvcdUrl?: string
|
||||
}
|
||||
const args: Args = parser.parse_args();
|
||||
|
||||
run(args.out, args.binary, args.forceDownload, args.ccaOut);
|
||||
run(args.out, args.binary, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl, pvcdUrl: args.pvcdUrl }, args.ccaOut);
|
||||
|
||||
@@ -35,9 +35,9 @@ export async function ensureAvailable(path: string, url: string, forceDownload =
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDataAvailable(forceDownload = false) {
|
||||
await ensureAvailable(CCD_PATH, CCD_URL, forceDownload);
|
||||
await ensureAvailable(PVCD_PATH, PVCD_URL, forceDownload);
|
||||
export async function ensureDataAvailable(options: DataOptions) {
|
||||
await ensureAvailable(CCD_PATH, options.ccdUrl || CCD_URL, !!options.ccdUrl || options.forceDownload);
|
||||
await ensureAvailable(PVCD_PATH, options.pvcdUrl || PVCD_URL, !!options.pvcdUrl || options.forceDownload);
|
||||
}
|
||||
|
||||
export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
|
||||
@@ -68,6 +68,16 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
|
||||
return encoder.getData();
|
||||
}
|
||||
|
||||
export type DataOptions = {
|
||||
ccdUrl?: string,
|
||||
pvcdUrl?: string,
|
||||
forceDownload?: boolean
|
||||
}
|
||||
|
||||
export const DefaultDataOptions: DataOptions = {
|
||||
forceDownload: false
|
||||
};
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', '..', '..', 'build/data');
|
||||
const CCD_PATH = path.join(DATA_DIR, 'components.cif');
|
||||
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif');
|
||||
|
||||
@@ -71,7 +71,7 @@ function classify(name: string, field: CifField): CifWriter.Field {
|
||||
}
|
||||
|
||||
export function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
|
||||
return Task.create<Uint8Array>('BinaryCIF', async ctx => {
|
||||
return Task.create<Uint8Array>('Convert CIF', async ctx => {
|
||||
const encodingProvider: BinaryEncodingProvider = hints
|
||||
? CifWriter.createEncodingProviderFromJsonConfig(hints)
|
||||
: { get: (c, f) => void 0 };
|
||||
|
||||
@@ -18,7 +18,7 @@ async function process(srcPath: string, outPath: string, configPath?: string, fi
|
||||
const config = configPath ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : void 0;
|
||||
const filter = filterPath ? fs.readFileSync(filterPath, 'utf8') : void 0;
|
||||
|
||||
const res = await convert(srcPath, false, config, filter);
|
||||
const res = await convert(srcPath, srcPath.toLowerCase().indexOf('.bcif') > 0, config, filter);
|
||||
await write(outPath, res);
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ function run(args: Args) {
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
add_help: true,
|
||||
description: 'Convert any CIF file to a BCIF file'
|
||||
description: 'Convert any BCIF file to a CIF file or vice versa'
|
||||
});
|
||||
parser.add_argument('src', {
|
||||
help: 'Source CIF path'
|
||||
help: 'Source file path'
|
||||
});
|
||||
parser.add_argument('out', {
|
||||
help: 'Output BCIF path'
|
||||
help: 'Output file path'
|
||||
});
|
||||
parser.add_argument('-c', '--config', {
|
||||
help: 'Optional encoding strategy/precision config path',
|
||||
|
||||
@@ -161,7 +161,7 @@ const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.
|
||||
const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`;
|
||||
const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic';
|
||||
const MA_DIC_PATH = `${DIC_DIR}/ma-extension.dic`;
|
||||
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/MA-dictionary/master/mmcif_ma.dic';
|
||||
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/ModelCIF/master/dist/mmcif_ma.dic';
|
||||
|
||||
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
|
||||
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,15 +13,17 @@ export function getFieldType(type: string, description: string, values?: string[
|
||||
switch (type) {
|
||||
// mmCIF
|
||||
case 'code':
|
||||
case 'ucode':
|
||||
case 'line':
|
||||
case 'uline':
|
||||
case 'text':
|
||||
case 'char':
|
||||
case 'uchar3':
|
||||
case 'uchar1':
|
||||
case 'boolean':
|
||||
return values && values.length ? EnumCol(values, 'str', description) : StrCol(description);
|
||||
case 'ucode':
|
||||
case 'uline':
|
||||
case 'uchar3':
|
||||
case 'uchar1':
|
||||
// only force lower-case for enums
|
||||
return values && values.length ? EnumCol(values.map(x => x.toLowerCase()), 'lstr', description) : StrCol(description);
|
||||
case 'aliasname':
|
||||
case 'name':
|
||||
case 'idname':
|
||||
@@ -49,6 +51,7 @@ export function getFieldType(type: string, description: string, values?: string[
|
||||
case 'operation_expression':
|
||||
case 'point_symmetry':
|
||||
case '4x3_matrix':
|
||||
case '3x4_matrix':
|
||||
case '3x4_matrices':
|
||||
case 'point_group':
|
||||
case 'point_group_helical':
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -10,7 +10,7 @@ import { FieldPath } from '../../../mol-io/reader/cif/schema';
|
||||
|
||||
function header(name: string, info: string, moldataImportPath: string) {
|
||||
return `/**
|
||||
* 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.
|
||||
*
|
||||
* Code-generated '${name}' schema file. ${info}
|
||||
*
|
||||
@@ -35,13 +35,17 @@ function getTypeShorthands(schema: Database, fields?: Filter) {
|
||||
const { columns } = schema.tables[table];
|
||||
Object.keys(columns).forEach(columnName => {
|
||||
if (fields && !fields[table][columnName]) return;
|
||||
types.add(schema.tables[table].columns[columnName].type);
|
||||
const col = schema.tables[table].columns[columnName];
|
||||
if (col.type === 'enum') types.add(col.subType);
|
||||
types.add(col.type);
|
||||
});
|
||||
});
|
||||
const shorthands: string[] = [];
|
||||
types.forEach(type => {
|
||||
switch (type) {
|
||||
case 'str': shorthands.push('const str = Schema.str;'); break;
|
||||
case 'ustr': shorthands.push('const ustr = Schema.ustr;'); break;
|
||||
case 'lstr': shorthands.push('const lstr = Schema.lstr;'); break;
|
||||
case 'int': shorthands.push('const int = Schema.int;'); break;
|
||||
case 'float': shorthands.push('const float = Schema.float;'); break;
|
||||
case 'coord': shorthands.push('const coord = Schema.coord;'); break;
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -29,8 +29,8 @@ export function FloatCol(description: string): FloatCol { return { type: 'float'
|
||||
export type CoordCol = { type: 'coord' } & BaseCol
|
||||
export function CoordCol(description: string): CoordCol { return { type: 'coord', description }; }
|
||||
|
||||
export type EnumCol = { type: 'enum', subType: 'int' | 'str', values: string[] } & BaseCol
|
||||
export function EnumCol(values: string[], subType: 'int' | 'str', description: string): EnumCol {
|
||||
export type EnumCol = { type: 'enum', subType: 'int' | 'str' | 'ustr' | 'lstr', values: string[] } & BaseCol
|
||||
export function EnumCol(values: string[], subType: 'int' | 'str' | 'ustr' | 'lstr', description: string): EnumCol {
|
||||
return { type: 'enum', description, values, subType };
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
</a>
|
||||
</div>
|
||||
<script>
|
||||
function getParam(name, regex) {
|
||||
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
|
||||
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
|
||||
if (debugMode) AlphaOrbitalsExample.setDebugMode(debugMode);
|
||||
|
||||
var timingMode = getParam('timing-mode', '[^&]+').trim() === '1';
|
||||
if (timingMode) AlphaOrbitalsExample.setTimingMode(timingMode);
|
||||
|
||||
AlphaOrbitalsExample.init('app')
|
||||
</script>
|
||||
<!-- __MOLSTAR_ANALYTICS__ -->
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ import { SphericalBasisOrder } from '../../extensions/alpha-orbitals/spherical-f
|
||||
import { BasisAndOrbitals, CreateOrbitalDensityVolume, CreateOrbitalRepresentation3D, CreateOrbitalVolume, StaticBasisAndOrbitals } from '../../extensions/alpha-orbitals/transforms';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
@@ -25,6 +25,8 @@ import { DemoMoleculeSDF, DemoOrbitals } from './example-data';
|
||||
import './index.html';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
import { setDebugMode, setTimingMode } from '../../mol-util/debug';
|
||||
|
||||
interface DemoInput {
|
||||
moleculeSdf: string,
|
||||
basis: Basis,
|
||||
@@ -80,20 +82,24 @@ export class AlphaOrbitalsExample {
|
||||
|
||||
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
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).`
|
||||
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
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
});
|
||||
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
}
|
||||
|
||||
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
|
||||
@@ -218,4 +224,6 @@ export class AlphaOrbitalsExample {
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
|
||||
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
|
||||
(window as any).AlphaOrbitalsExample.setDebugMode = setDebugMode;
|
||||
(window as any).AlphaOrbitalsExample.setTimingMode = setTimingMode;
|
||||
@@ -9,7 +9,7 @@ import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/model-index';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
@@ -24,7 +24,7 @@ const Canvas3DPresets = {
|
||||
illustrative: {
|
||||
canvas3d: <Preset>{
|
||||
postprocessing: {
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
|
||||
outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } }
|
||||
},
|
||||
renderer: {
|
||||
@@ -36,7 +36,7 @@ const Canvas3DPresets = {
|
||||
occlusion: {
|
||||
canvas3d: <Preset>{
|
||||
postprocessing: {
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
|
||||
outline: { name: 'off', params: {} }
|
||||
},
|
||||
renderer: {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/mod
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { createPluginUI } from '../../mol-plugin-ui';
|
||||
import { createPluginUI } from '../../mol-plugin-ui/react18';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -8,6 +8,7 @@ import { sortArray } from '../../mol-data/util';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Task } from '../../mol-task';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
|
||||
import { gpuComputeAlphaOrbitalsDensityGridValues } from './gpu/compute';
|
||||
|
||||
@@ -19,9 +20,9 @@ export function createSphericalCollocationDensityGrid(
|
||||
|
||||
let matrix: Float32Array;
|
||||
if (canComputeGrid3dOnGPU(webgl)) {
|
||||
// console.time('gpu');
|
||||
matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl!, cubeGrid, orbitals);
|
||||
// console.timeEnd('gpu');
|
||||
if (isTimingMode) webgl.timer.mark('createSphericalCollocationDensityGrid');
|
||||
matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl, cubeGrid, orbitals);
|
||||
if (isTimingMode) webgl.timer.markEnd('createSphericalCollocationDensityGrid');
|
||||
} else {
|
||||
throw new Error('Missing OES_texture_float WebGL extension.');
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* Inspired by https://github.com/dgasmith/gau2grid.
|
||||
*
|
||||
@@ -10,12 +10,11 @@ import { sortArray } from '../../mol-data/util';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Task } from '../../mol-task';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { sphericalCollocation } from './collocation';
|
||||
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
|
||||
import { gpuComputeAlphaOrbitalsGridValues } from './gpu/compute';
|
||||
|
||||
// setDebugMode(true);
|
||||
|
||||
export function createSphericalCollocationGrid(
|
||||
params: CubeGridComputationParams, orbital: AlphaOrbital, webgl?: WebGLContext
|
||||
): Task<CubeGrid> {
|
||||
@@ -24,9 +23,9 @@ export function createSphericalCollocationGrid(
|
||||
|
||||
let matrix: Float32Array;
|
||||
if (canComputeGrid3dOnGPU(webgl)) {
|
||||
// console.time('gpu');
|
||||
matrix = await gpuComputeAlphaOrbitalsGridValues(ctx, webgl!, cubeGrid, orbital);
|
||||
// console.timeEnd('gpu');
|
||||
if (isTimingMode) webgl.timer.mark('createSphericalCollocationGrid');
|
||||
matrix = await gpuComputeAlphaOrbitalsGridValues(ctx, webgl, cubeGrid, orbital);
|
||||
if (isTimingMode) webgl.timer.markEnd('createSphericalCollocationGrid');
|
||||
} else {
|
||||
// console.time('cpu');
|
||||
matrix = await sphericalCollocation(cubeGrid, orbital, ctx);
|
||||
|
||||
BIN
src/extensions/backgrounds/images/cells.jpg
Normal file
|
After Width: | Height: | Size: 181 KiB |
90
src/extensions/backgrounds/index.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
|
||||
// from https://visualsonline.cancer.gov/details.cfm?imageid=2304, public domain
|
||||
import image_cells from './images/cells.jpg';
|
||||
|
||||
// created with http://alexcpeterson.com/spacescape/
|
||||
import face_nebula_nx from './skyboxes/nebula/nebula_left2.jpg';
|
||||
import face_nebula_ny from './skyboxes/nebula/nebula_bottom4.jpg';
|
||||
import face_nebula_nz from './skyboxes/nebula/nebula_back6.jpg';
|
||||
import face_nebula_px from './skyboxes/nebula/nebula_right1.jpg';
|
||||
import face_nebula_py from './skyboxes/nebula/nebula_top3.jpg';
|
||||
import face_nebula_pz from './skyboxes/nebula/nebula_front5.jpg';
|
||||
|
||||
export const Backgrounds = PluginBehavior.create<{ }>({
|
||||
name: 'extension-backgrounds',
|
||||
category: 'misc',
|
||||
display: {
|
||||
name: 'Backgrounds'
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ }> {
|
||||
register(): void {
|
||||
this.ctx.config.set(PluginConfig.Background.Styles, [
|
||||
[{
|
||||
variant: {
|
||||
name: 'radialGradient',
|
||||
params: {
|
||||
centerColor: Color(0xFFFFFF),
|
||||
edgeColor: Color(0x808080),
|
||||
ratio: 0.2,
|
||||
coverage: 'viewport',
|
||||
}
|
||||
}
|
||||
}, 'Light Radial Gradient'],
|
||||
[{
|
||||
variant: {
|
||||
name: 'image',
|
||||
params: {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: image_cells
|
||||
},
|
||||
lightness: 0,
|
||||
saturation: 0,
|
||||
opacity: 1,
|
||||
coverage: 'viewport',
|
||||
}
|
||||
}
|
||||
}, 'Normal Cells Image'],
|
||||
[{
|
||||
variant: {
|
||||
name: 'skybox',
|
||||
params: {
|
||||
faces: {
|
||||
name: 'urls',
|
||||
params: {
|
||||
nx: face_nebula_nx,
|
||||
ny: face_nebula_ny,
|
||||
nz: face_nebula_nz,
|
||||
px: face_nebula_px,
|
||||
py: face_nebula_py,
|
||||
pz: face_nebula_pz,
|
||||
}
|
||||
},
|
||||
lightness: 0,
|
||||
saturation: 0,
|
||||
opacity: 1,
|
||||
}
|
||||
}
|
||||
}, 'Purple Nebula Skybox'],
|
||||
]);
|
||||
}
|
||||
|
||||
update() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.config.set(PluginConfig.Background.Styles, []);
|
||||
}
|
||||
},
|
||||
params: () => ({ })
|
||||
});
|
||||
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_back6.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_bottom4.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_front5.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_left2.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_right1.jpg
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_top3.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
10
src/extensions/backgrounds/typings.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
declare module '*.jpg' {
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
* @author Ludovic Autin <ludovic.autin@gmail.com>
|
||||
@@ -12,7 +12,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Ingredient, CellPacking, CompartmentPrimitives } from './data';
|
||||
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
|
||||
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
|
||||
import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb';
|
||||
import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
|
||||
import { SymmetryOperator } from '../../mol-math/geometry';
|
||||
@@ -22,10 +22,6 @@ import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, Structure
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { getMatFromResamplePoints } from './curve';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
import { CifCategory, CifField } from '../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../mol-data/db';
|
||||
import { createModels } from '../../mol-model-formats/structure/basic/parser';
|
||||
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
@@ -141,7 +137,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
|
||||
|
||||
function getTransformLegacy(trans: Vec3, rot: Quat) {
|
||||
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2]);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
|
||||
Mat4.transpose(m, m);
|
||||
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0));
|
||||
Mat4.setTranslation(m, trans);
|
||||
@@ -150,7 +146,7 @@ function getTransformLegacy(trans: Vec3, rot: Quat) {
|
||||
|
||||
function getTransform(trans: Vec3, rot: Quat) {
|
||||
const q: Quat = Quat.create(-rot[0], rot[1], rot[2], -rot[3]);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
|
||||
const p: Vec3 = Vec3.create(-trans[0], trans[1], trans[2]);
|
||||
Mat4.setTranslation(m, p);
|
||||
return m;
|
||||
@@ -213,110 +209,10 @@ function getAssembly(name: string, transforms: Mat4[], structure: Structure) {
|
||||
return builder.getStructure();
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed');
|
||||
|
||||
const { db } = model.sourceData.data;
|
||||
const d = db.atom_site;
|
||||
const n = d._rowCount;
|
||||
const rowCount = n * transforms.length;
|
||||
|
||||
const { offsets, count } = model.atomicHierarchy.chainAtomSegments;
|
||||
|
||||
const x = d.Cartn_x.toArray();
|
||||
const y = d.Cartn_y.toArray();
|
||||
const z = d.Cartn_z.toArray();
|
||||
|
||||
const Cartn_x = new Float32Array(rowCount);
|
||||
const Cartn_y = new Float32Array(rowCount);
|
||||
const Cartn_z = new Float32Array(rowCount);
|
||||
const map = new Uint32Array(rowCount);
|
||||
const seq = new Int32Array(rowCount);
|
||||
let offset = 0;
|
||||
for (let c = 0; c < count; ++c) {
|
||||
const cStart = offsets[c];
|
||||
const cEnd = offsets[c + 1];
|
||||
const cLength = cEnd - cStart;
|
||||
for (let t = 0, tl = transforms.length; t < tl; ++t) {
|
||||
const m = transforms[t];
|
||||
for (let j = cStart; j < cEnd; ++j) {
|
||||
const i = offset + j - cStart;
|
||||
const xj = x[j], yj = y[j], zj = z[j];
|
||||
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12];
|
||||
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13];
|
||||
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14];
|
||||
map[i] = j;
|
||||
seq[i] = t + 1;
|
||||
}
|
||||
offset += cLength;
|
||||
}
|
||||
}
|
||||
|
||||
function multColumn<T>(column: Column<T>) {
|
||||
const array = column.toArray();
|
||||
return Column.ofLambda({
|
||||
value: row => array[map[row]],
|
||||
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
|
||||
rowCount, schema: column.schema
|
||||
});
|
||||
}
|
||||
|
||||
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
|
||||
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
|
||||
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
|
||||
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
|
||||
auth_seq_id: CifField.ofNumbers(seq),
|
||||
|
||||
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
|
||||
Cartn_x: CifField.ofNumbers(Cartn_x),
|
||||
Cartn_y: CifField.ofNumbers(Cartn_y),
|
||||
Cartn_z: CifField.ofNumbers(Cartn_z),
|
||||
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
|
||||
id: CifField.ofColumn(Column.ofLambda({
|
||||
value: row => row,
|
||||
areValuesEqual: (rowA, rowB) => rowA === rowB,
|
||||
rowCount, schema: d.id.schema,
|
||||
})),
|
||||
|
||||
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
|
||||
|
||||
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
|
||||
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
|
||||
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
|
||||
label_seq_id: CifField.ofNumbers(seq),
|
||||
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
|
||||
|
||||
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
|
||||
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
|
||||
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
|
||||
};
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', db.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
};
|
||||
|
||||
return {
|
||||
header: name,
|
||||
categoryNames: Object.keys(categories),
|
||||
categories
|
||||
};
|
||||
}
|
||||
|
||||
async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model);
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = MmcifFormat.fromFrame(cif);
|
||||
const models = await createModels(format.data.db, format, ctx);
|
||||
return models.representative;
|
||||
});
|
||||
|
||||
const curveModel = await plugin.runTask(curveModelTask);
|
||||
// ingredient.source.selection = undefined;
|
||||
return getStructure(plugin, curveModel, ingredient);
|
||||
async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const structure = Structure.ofModel(model);
|
||||
const assembly = getAssembly(name, transforms, structure);
|
||||
return assembly;
|
||||
}
|
||||
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache, location: 'surface' | 'interior' | 'cytoplasme') {
|
||||
@@ -337,7 +233,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
if (!model) return;
|
||||
let structure: Structure;
|
||||
if (nbCurve) {
|
||||
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
|
||||
structure = await getCurve(name, getCurveTransforms(ingredient), model);
|
||||
} else {
|
||||
if ((!results || results.length === 0)) return;
|
||||
let bu: string|undefined = source.bu ? source.bu : undefined;
|
||||
@@ -361,7 +257,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
structure = Structure.transform(structure, m1);
|
||||
if (ingredient.offset) {
|
||||
const o: Vec3 = Vec3.create(ingredient.offset[0], ingredient.offset[1], ingredient.offset[2]);
|
||||
if (!Vec3.exactEquals(o, Vec3.zero())) { // -1, 1, 4e-16 ??
|
||||
if (!Vec3.exactEquals(o, Vec3())) { // -1, 1, 4e-16 ??
|
||||
if (location !== 'surface') {
|
||||
Vec3.negate(o, o);
|
||||
}
|
||||
@@ -375,7 +271,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
|
||||
if (!Vec3.exactEquals(p, Vec3.unitZ)) {
|
||||
const q: Quat = Quat.identity();
|
||||
Quat.rotationTo(q, p, Vec3.unitZ);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4(), q);
|
||||
structure = Structure.transform(structure, m);
|
||||
}
|
||||
}
|
||||
@@ -519,6 +415,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
.apply(StructureFromAssemblies, undefined, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
const membraneParams = {
|
||||
ignoreLight: params.preset.adjustStyle,
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
@@ -535,6 +432,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
const membraneParams = {
|
||||
ignoreLight: params.preset.adjustStyle,
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
@@ -618,6 +516,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
|
||||
|
||||
const packingParams = {
|
||||
traceOnly: params.preset.traceOnly,
|
||||
ignoreLight: params.preset.adjustStyle,
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackPackingPreset.apply(packing, packingParams, plugin);
|
||||
@@ -669,7 +568,8 @@ const LoadCellPackModelParams = {
|
||||
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredient files' }),
|
||||
preset: PD.Group({
|
||||
traceOnly: PD.Boolean(false),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation']))
|
||||
adjustStyle: PD.Boolean(true),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation'] as const))
|
||||
}, { isExpanded: true })
|
||||
};
|
||||
type LoadCellPackModelParams = PD.Values<typeof LoadCellPackModelParams>
|
||||
@@ -679,5 +579,43 @@ export const LoadCellPackModel = StateAction.build({
|
||||
params: LoadCellPackModelParams,
|
||||
from: PSO.Root
|
||||
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
|
||||
if (params.preset.adjustStyle) {
|
||||
ctx.managers.interactivity.setProps({ granularity: 'chain' });
|
||||
ctx.managers.structure.component.setOptions({
|
||||
... ctx.managers.structure.component.state.options,
|
||||
visualQuality: 'custom',
|
||||
ignoreLight: true,
|
||||
showHydrogens: false,
|
||||
});
|
||||
ctx.canvas3d?.setProps({
|
||||
multiSample: { mode: 'off' },
|
||||
cameraClipping: { far: false },
|
||||
renderer: { colorMarker: false },
|
||||
marking: {
|
||||
enabled: true,
|
||||
ghostEdgeStrength: 1,
|
||||
},
|
||||
postprocessing: {
|
||||
occlusion: {
|
||||
name: 'on',
|
||||
params: {
|
||||
samples: 32,
|
||||
radius: 8,
|
||||
bias: 1,
|
||||
blurKernelSize: 15,
|
||||
resolutionScale: 1,
|
||||
}
|
||||
},
|
||||
outline: {
|
||||
name: 'on',
|
||||
params: {
|
||||
scale: 1,
|
||||
threshold: 0.33,
|
||||
color: ColorNames.black,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
await loadPackings(ctx, taskCtx, state, params);
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
* @author Ludovic Autin <ludovic.autin@gmail.com>
|
||||
@@ -13,7 +13,8 @@ import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
|
||||
export const CellpackPackingPresetParams = {
|
||||
traceOnly: PD.Boolean(true),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'] as const)),
|
||||
};
|
||||
export type CellpackPackingPresetParams = PD.ValuesFor<typeof CellpackPackingPresetParams>
|
||||
|
||||
@@ -27,7 +28,9 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const reprProps = {
|
||||
ignoreHydrogens: true,
|
||||
traceOnly: params.traceOnly
|
||||
traceOnly: params.traceOnly,
|
||||
instanceGranularity: true,
|
||||
ignoreLight: params.ignoreLight,
|
||||
};
|
||||
const components = {
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
|
||||
@@ -37,8 +40,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
Object.assign(reprProps, {
|
||||
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
|
||||
});
|
||||
} else if (params.representation === 'spacefill' && params.traceOnly) {
|
||||
Object.assign(reprProps, { sizeFactor: 2 });
|
||||
} else if (params.representation === 'spacefill') {
|
||||
Object.assign(reprProps, { sizeFactor: params.traceOnly ? 2 : 1 });
|
||||
}
|
||||
|
||||
// default is generated
|
||||
@@ -57,7 +60,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
//
|
||||
|
||||
export const CellpackMembranePresetParams = {
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'] as const)),
|
||||
};
|
||||
export type CellpackMembranePresetParams = PD.ValuesFor<typeof CellpackMembranePresetParams>
|
||||
|
||||
@@ -71,6 +75,8 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const reprProps = {
|
||||
ignoreHydrogens: true,
|
||||
instanceGranularity: true,
|
||||
ignoreLight: params.ignoreLight,
|
||||
};
|
||||
const components = {
|
||||
membrane: await presetStaticComponent(plugin, structureCell, 'all', { label: 'Membrane' })
|
||||
@@ -84,7 +90,7 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
|
||||
const representations = {
|
||||
membrane: builder.buildRepresentation(update, components.membrane, { type: 'gaussian-surface', typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
|
||||
membrane: builder.buildRepresentation(update, components.membrane, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { ConfalPyramidsColorThemeProvider } from './color';
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsRepresentationProvider } from './representation';
|
||||
import { Loci } from '../../../mol-model/loci';
|
||||
import { ConfalPyramidsTypes } from './types';
|
||||
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
|
||||
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../../mol-state';
|
||||
@@ -56,21 +56,10 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
|
||||
description: 'Schematic depiction of conformer class and confal value.',
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
|
||||
|
||||
private provider = ConfalPyramidsProvider;
|
||||
|
||||
private labelConfalPyramids = {
|
||||
label: (loci: Loci): string | undefined => {
|
||||
if (!this.params.showToolTip) return void 0;
|
||||
|
||||
/* TODO: Implement this */
|
||||
return void 0;
|
||||
}
|
||||
};
|
||||
|
||||
register(): void {
|
||||
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
|
||||
this.ctx.managers.lociLabels.addProvider(this.labelConfalPyramids);
|
||||
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
|
||||
@@ -88,7 +77,6 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
|
||||
|
||||
unregister() {
|
||||
this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
|
||||
this.ctx.managers.lociLabels.removeProvider(this.labelConfalPyramids);
|
||||
|
||||
this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
|
||||
@@ -101,3 +89,13 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
|
||||
showToolTip: PD.Boolean(true)
|
||||
})
|
||||
});
|
||||
|
||||
export function confalPyramidLabel(halfPyramid: ConfalPyramidsTypes.HalfPyramid) {
|
||||
const { step } = halfPyramid;
|
||||
return `
|
||||
<b>${step.auth_asym_id_1}</b> |
|
||||
<b>${step.label_comp_id_1} ${step.auth_seq_id_1}${step.PDB_ins_code_1}${step.label_alt_id_1.length > 0 ? ` (alt ${step.label_alt_id_1})` : ''}
|
||||
${step.label_comp_id_2} ${step.auth_seq_id_2}${step.PDB_ins_code_2}${step.label_alt_id_2.length > 0 ? ` (alt ${step.label_alt_id_2})` : ''} </b><br />
|
||||
<i>NtC:</i> ${step.NtC} | <i>Confal score:</i> ${step.confal_score} | <i>RMSD:</i> ${step.rmsd.toFixed(2)}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -11,149 +11,248 @@ import { Location } from '../../../mol-model/location';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { ColorTheme } from '../../../mol-theme/color';
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Color, ColorMap } from '../../../mol-util/color';
|
||||
import { getColorMapParams } from '../../../mol-util/color/params';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { TableLegend } from '../../../mol-util/legend';
|
||||
import { iterableToArray } from '../../../mol-data/util';
|
||||
import { ObjectKeys } from '../../../mol-util/type-helpers';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Assigns colors to confal pyramids';
|
||||
|
||||
const DefaultClassColors = {
|
||||
A: 0xFFC1C1,
|
||||
B: 0xC8CFFF,
|
||||
BII: 0x0059DA,
|
||||
miB: 0x3BE8FB,
|
||||
Z: 0x01F60E,
|
||||
IC: 0xFA5CFB,
|
||||
OPN: 0xE90000,
|
||||
SYN: 0xFFFF01,
|
||||
N: 0xF2F2F2,
|
||||
};
|
||||
const ErrorColor = Color(0xFFA10A);
|
||||
|
||||
type ConformerClasses = 'A' | 'B' | 'BII' | 'miB' | 'Z' | 'IC' | 'OPN' | 'SYN' | 'N';
|
||||
const PyramidsColors = ColorMap({
|
||||
NANT_Upr: DefaultClassColors.N,
|
||||
NANT_Lwr: DefaultClassColors.N,
|
||||
AA00_Upr: DefaultClassColors.A,
|
||||
AA00_Lwr: DefaultClassColors.A,
|
||||
AA02_Upr: DefaultClassColors.A,
|
||||
AA02_Lwr: DefaultClassColors.A,
|
||||
AA03_Upr: DefaultClassColors.A,
|
||||
AA03_Lwr: DefaultClassColors.A,
|
||||
AA04_Upr: DefaultClassColors.A,
|
||||
AA04_Lwr: DefaultClassColors.A,
|
||||
AA08_Upr: DefaultClassColors.A,
|
||||
AA08_Lwr: DefaultClassColors.A,
|
||||
AA09_Upr: DefaultClassColors.A,
|
||||
AA09_Lwr: DefaultClassColors.A,
|
||||
AA01_Upr: DefaultClassColors.A,
|
||||
AA01_Lwr: DefaultClassColors.A,
|
||||
AA05_Upr: DefaultClassColors.A,
|
||||
AA05_Lwr: DefaultClassColors.A,
|
||||
AA06_Upr: DefaultClassColors.A,
|
||||
AA06_Lwr: DefaultClassColors.A,
|
||||
AA10_Upr: DefaultClassColors.A,
|
||||
AA10_Lwr: DefaultClassColors.A,
|
||||
AA11_Upr: DefaultClassColors.A,
|
||||
AA11_Lwr: DefaultClassColors.A,
|
||||
AA07_Upr: DefaultClassColors.A,
|
||||
AA07_Lwr: DefaultClassColors.A,
|
||||
AA12_Upr: DefaultClassColors.A,
|
||||
AA12_Lwr: DefaultClassColors.A,
|
||||
AA13_Upr: DefaultClassColors.A,
|
||||
AA13_Lwr: DefaultClassColors.A,
|
||||
AB01_Upr: DefaultClassColors.A,
|
||||
AB01_Lwr: DefaultClassColors.B,
|
||||
AB02_Upr: DefaultClassColors.A,
|
||||
AB02_Lwr: DefaultClassColors.B,
|
||||
AB03_Upr: DefaultClassColors.A,
|
||||
AB03_Lwr: DefaultClassColors.B,
|
||||
AB04_Upr: DefaultClassColors.A,
|
||||
AB04_Lwr: DefaultClassColors.B,
|
||||
AB05_Upr: DefaultClassColors.A,
|
||||
AB05_Lwr: DefaultClassColors.B,
|
||||
BA01_Upr: DefaultClassColors.B,
|
||||
BA01_Lwr: DefaultClassColors.A,
|
||||
BA05_Upr: DefaultClassColors.B,
|
||||
BA05_Lwr: DefaultClassColors.A,
|
||||
BA09_Upr: DefaultClassColors.B,
|
||||
BA09_Lwr: DefaultClassColors.A,
|
||||
BA08_Upr: DefaultClassColors.BII,
|
||||
BA08_Lwr: DefaultClassColors.A,
|
||||
BA10_Upr: DefaultClassColors.B,
|
||||
BA10_Lwr: DefaultClassColors.A,
|
||||
BA13_Upr: DefaultClassColors.BII,
|
||||
BA13_Lwr: DefaultClassColors.A,
|
||||
BA16_Upr: DefaultClassColors.BII,
|
||||
BA16_Lwr: DefaultClassColors.A,
|
||||
BA17_Upr: DefaultClassColors.BII,
|
||||
BA17_Lwr: DefaultClassColors.A,
|
||||
BB00_Upr: DefaultClassColors.B,
|
||||
BB00_Lwr: DefaultClassColors.B,
|
||||
BB01_Upr: DefaultClassColors.B,
|
||||
BB01_Lwr: DefaultClassColors.B,
|
||||
BB17_Upr: DefaultClassColors.B,
|
||||
BB17_Lwr: DefaultClassColors.B,
|
||||
BB02_Upr: DefaultClassColors.B,
|
||||
BB02_Lwr: DefaultClassColors.B,
|
||||
BB03_Upr: DefaultClassColors.B,
|
||||
BB03_Lwr: DefaultClassColors.B,
|
||||
BB11_Upr: DefaultClassColors.B,
|
||||
BB11_Lwr: DefaultClassColors.B,
|
||||
BB16_Upr: DefaultClassColors.B,
|
||||
BB16_Lwr: DefaultClassColors.B,
|
||||
BB04_Upr: DefaultClassColors.B,
|
||||
BB04_Lwr: DefaultClassColors.BII,
|
||||
BB05_Upr: DefaultClassColors.B,
|
||||
BB05_Lwr: DefaultClassColors.BII,
|
||||
BB07_Upr: DefaultClassColors.BII,
|
||||
BB07_Lwr: DefaultClassColors.BII,
|
||||
BB08_Upr: DefaultClassColors.BII,
|
||||
BB08_Lwr: DefaultClassColors.BII,
|
||||
BB10_Upr: DefaultClassColors.miB,
|
||||
BB10_Lwr: DefaultClassColors.miB,
|
||||
BB12_Upr: DefaultClassColors.miB,
|
||||
BB12_Lwr: DefaultClassColors.miB,
|
||||
BB13_Upr: DefaultClassColors.miB,
|
||||
BB13_Lwr: DefaultClassColors.miB,
|
||||
BB14_Upr: DefaultClassColors.miB,
|
||||
BB14_Lwr: DefaultClassColors.miB,
|
||||
BB15_Upr: DefaultClassColors.miB,
|
||||
BB15_Lwr: DefaultClassColors.miB,
|
||||
BB20_Upr: DefaultClassColors.miB,
|
||||
BB20_Lwr: DefaultClassColors.miB,
|
||||
IC01_Upr: DefaultClassColors.IC,
|
||||
IC01_Lwr: DefaultClassColors.IC,
|
||||
IC02_Upr: DefaultClassColors.IC,
|
||||
IC02_Lwr: DefaultClassColors.IC,
|
||||
IC03_Upr: DefaultClassColors.IC,
|
||||
IC03_Lwr: DefaultClassColors.IC,
|
||||
IC04_Upr: DefaultClassColors.IC,
|
||||
IC04_Lwr: DefaultClassColors.IC,
|
||||
IC05_Upr: DefaultClassColors.IC,
|
||||
IC05_Lwr: DefaultClassColors.IC,
|
||||
IC06_Upr: DefaultClassColors.IC,
|
||||
IC06_Lwr: DefaultClassColors.IC,
|
||||
IC07_Upr: DefaultClassColors.IC,
|
||||
IC07_Lwr: DefaultClassColors.IC,
|
||||
OP01_Upr: DefaultClassColors.OPN,
|
||||
OP01_Lwr: DefaultClassColors.OPN,
|
||||
OP02_Upr: DefaultClassColors.OPN,
|
||||
OP02_Lwr: DefaultClassColors.OPN,
|
||||
OP03_Upr: DefaultClassColors.OPN,
|
||||
OP03_Lwr: DefaultClassColors.OPN,
|
||||
OP04_Upr: DefaultClassColors.OPN,
|
||||
OP04_Lwr: DefaultClassColors.OPN,
|
||||
OP05_Upr: DefaultClassColors.OPN,
|
||||
OP05_Lwr: DefaultClassColors.OPN,
|
||||
OP06_Upr: DefaultClassColors.OPN,
|
||||
OP06_Lwr: DefaultClassColors.OPN,
|
||||
OP07_Upr: DefaultClassColors.OPN,
|
||||
OP07_Lwr: DefaultClassColors.OPN,
|
||||
OP08_Upr: DefaultClassColors.OPN,
|
||||
OP08_Lwr: DefaultClassColors.OPN,
|
||||
OP09_Upr: DefaultClassColors.OPN,
|
||||
OP09_Lwr: DefaultClassColors.OPN,
|
||||
OP10_Upr: DefaultClassColors.OPN,
|
||||
OP10_Lwr: DefaultClassColors.OPN,
|
||||
OP11_Upr: DefaultClassColors.OPN,
|
||||
OP11_Lwr: DefaultClassColors.OPN,
|
||||
OP12_Upr: DefaultClassColors.OPN,
|
||||
OP12_Lwr: DefaultClassColors.OPN,
|
||||
OP13_Upr: DefaultClassColors.OPN,
|
||||
OP13_Lwr: DefaultClassColors.OPN,
|
||||
OP14_Upr: DefaultClassColors.OPN,
|
||||
OP14_Lwr: DefaultClassColors.OPN,
|
||||
OP15_Upr: DefaultClassColors.OPN,
|
||||
OP15_Lwr: DefaultClassColors.OPN,
|
||||
OP16_Upr: DefaultClassColors.OPN,
|
||||
OP16_Lwr: DefaultClassColors.OPN,
|
||||
OP17_Upr: DefaultClassColors.OPN,
|
||||
OP17_Lwr: DefaultClassColors.OPN,
|
||||
OP18_Upr: DefaultClassColors.OPN,
|
||||
OP18_Lwr: DefaultClassColors.OPN,
|
||||
OP19_Upr: DefaultClassColors.OPN,
|
||||
OP19_Lwr: DefaultClassColors.OPN,
|
||||
OP20_Upr: DefaultClassColors.OPN,
|
||||
OP20_Lwr: DefaultClassColors.OPN,
|
||||
OP21_Upr: DefaultClassColors.OPN,
|
||||
OP21_Lwr: DefaultClassColors.OPN,
|
||||
OP22_Upr: DefaultClassColors.OPN,
|
||||
OP22_Lwr: DefaultClassColors.OPN,
|
||||
OP23_Upr: DefaultClassColors.OPN,
|
||||
OP23_Lwr: DefaultClassColors.OPN,
|
||||
OP24_Upr: DefaultClassColors.OPN,
|
||||
OP24_Lwr: DefaultClassColors.OPN,
|
||||
OP25_Upr: DefaultClassColors.OPN,
|
||||
OP25_Lwr: DefaultClassColors.OPN,
|
||||
OP26_Upr: DefaultClassColors.OPN,
|
||||
OP26_Lwr: DefaultClassColors.OPN,
|
||||
OP27_Upr: DefaultClassColors.OPN,
|
||||
OP27_Lwr: DefaultClassColors.OPN,
|
||||
OP28_Upr: DefaultClassColors.OPN,
|
||||
OP28_Lwr: DefaultClassColors.OPN,
|
||||
OP29_Upr: DefaultClassColors.OPN,
|
||||
OP29_Lwr: DefaultClassColors.OPN,
|
||||
OP30_Upr: DefaultClassColors.OPN,
|
||||
OP30_Lwr: DefaultClassColors.OPN,
|
||||
OP31_Upr: DefaultClassColors.OPN,
|
||||
OP31_Lwr: DefaultClassColors.OPN,
|
||||
OPS1_Upr: DefaultClassColors.OPN,
|
||||
OPS1_Lwr: DefaultClassColors.OPN,
|
||||
OP1S_Upr: DefaultClassColors.OPN,
|
||||
OP1S_Lwr: DefaultClassColors.SYN,
|
||||
AAS1_Upr: DefaultClassColors.SYN,
|
||||
AAS1_Lwr: DefaultClassColors.A,
|
||||
AB1S_Upr: DefaultClassColors.A,
|
||||
AB1S_Lwr: DefaultClassColors.SYN,
|
||||
AB2S_Upr: DefaultClassColors.A,
|
||||
AB2S_Lwr: DefaultClassColors.SYN,
|
||||
BB1S_Upr: DefaultClassColors.B,
|
||||
BB1S_Lwr: DefaultClassColors.SYN,
|
||||
BB2S_Upr: DefaultClassColors.B,
|
||||
BB2S_Lwr: DefaultClassColors.SYN,
|
||||
BBS1_Upr: DefaultClassColors.SYN,
|
||||
BBS1_Lwr: DefaultClassColors.B,
|
||||
ZZ01_Upr: DefaultClassColors.Z,
|
||||
ZZ01_Lwr: DefaultClassColors.Z,
|
||||
ZZ02_Upr: DefaultClassColors.Z,
|
||||
ZZ02_Lwr: DefaultClassColors.Z,
|
||||
ZZ1S_Upr: DefaultClassColors.Z,
|
||||
ZZ1S_Lwr: DefaultClassColors.SYN,
|
||||
ZZ2S_Upr: DefaultClassColors.Z,
|
||||
ZZ2S_Lwr: DefaultClassColors.SYN,
|
||||
ZZS1_Upr: DefaultClassColors.SYN,
|
||||
ZZS1_Lwr: DefaultClassColors.Z,
|
||||
ZZS2_Upr: DefaultClassColors.SYN,
|
||||
ZZS2_Lwr: DefaultClassColors.Z,
|
||||
});
|
||||
type PyramidsColors = typeof PyramidsColors;
|
||||
|
||||
const ColorMapping: ReadonlyMap<ConformerClasses, Color> = new Map([
|
||||
['A', Color(0xFFC1C1)],
|
||||
['B', Color(0xC8CFFF)],
|
||||
['BII', Color(0x0059DA)],
|
||||
['miB', Color(0x3BE8FB)],
|
||||
['Z', Color(0x01F60E)],
|
||||
['IC', Color(0xFA5CFB)],
|
||||
['OPN', Color(0xE90000)],
|
||||
['SYN', Color(0xFFFF01)],
|
||||
['N', Color(0xF2F2F2)],
|
||||
]);
|
||||
export const ConfalPyramidsColorThemeParams = {
|
||||
colors: PD.MappedStatic('default', {
|
||||
'default': PD.EmptyGroup(),
|
||||
'custom': PD.Group(getColorMapParams(PyramidsColors))
|
||||
}),
|
||||
};
|
||||
export type ConfalPyramidsColorThemeParams = typeof ConfalPyramidsColorThemeParams;
|
||||
|
||||
const NtCToClasses: ReadonlyMap<string, [ConformerClasses, ConformerClasses]> = new Map([
|
||||
['NANT', ['N', 'N']],
|
||||
['AA00', ['A', 'A']],
|
||||
['AA02', ['A', 'A']],
|
||||
['AA03', ['A', 'A']],
|
||||
['AA04', ['A', 'A']],
|
||||
['AA08', ['A', 'A']],
|
||||
['AA09', ['A', 'A']],
|
||||
['AA01', ['A', 'A']],
|
||||
['AA05', ['A', 'A']],
|
||||
['AA06', ['A', 'A']],
|
||||
['AA10', ['A', 'A']],
|
||||
['AA11', ['A', 'A']],
|
||||
['AA07', ['A', 'A']],
|
||||
['AA12', ['A', 'A']],
|
||||
['AA13', ['A', 'A']],
|
||||
['AB01', ['A', 'B']],
|
||||
['AB02', ['A', 'B']],
|
||||
['AB03', ['A', 'B']],
|
||||
['AB04', ['A', 'B']],
|
||||
['AB05', ['A', 'B']],
|
||||
['BA01', ['B', 'A']],
|
||||
['BA05', ['B', 'A']],
|
||||
['BA09', ['B', 'A']],
|
||||
['BA08', ['BII', 'A']],
|
||||
['BA10', ['B', 'A']],
|
||||
['BA13', ['BII', 'A']],
|
||||
['BA16', ['BII', 'A']],
|
||||
['BA17', ['BII', 'A']],
|
||||
['BB00', ['B', 'B']],
|
||||
['BB01', ['B', 'B']],
|
||||
['BB17', ['B', 'B']],
|
||||
['BB02', ['B', 'B']],
|
||||
['BB03', ['B', 'B']],
|
||||
['BB11', ['B', 'B']],
|
||||
['BB16', ['B', 'B']],
|
||||
['BB04', ['B', 'BII']],
|
||||
['BB05', ['B', 'BII']],
|
||||
['BB07', ['BII', 'BII']],
|
||||
['BB08', ['BII', 'BII']],
|
||||
['BB10', ['miB', 'miB']],
|
||||
['BB12', ['miB', 'miB']],
|
||||
['BB13', ['miB', 'miB']],
|
||||
['BB14', ['miB', 'miB']],
|
||||
['BB15', ['miB', 'miB']],
|
||||
['BB20', ['miB', 'miB']],
|
||||
['IC01', ['IC', 'IC']],
|
||||
['IC02', ['IC', 'IC']],
|
||||
['IC03', ['IC', 'IC']],
|
||||
['IC04', ['IC', 'IC']],
|
||||
['IC05', ['IC', 'IC']],
|
||||
['IC06', ['IC', 'IC']],
|
||||
['IC07', ['IC', 'IC']],
|
||||
['OP01', ['OPN', 'OPN']],
|
||||
['OP02', ['OPN', 'OPN']],
|
||||
['OP03', ['OPN', 'OPN']],
|
||||
['OP04', ['OPN', 'OPN']],
|
||||
['OP05', ['OPN', 'OPN']],
|
||||
['OP06', ['OPN', 'OPN']],
|
||||
['OP07', ['OPN', 'OPN']],
|
||||
['OP08', ['OPN', 'OPN']],
|
||||
['OP09', ['OPN', 'OPN']],
|
||||
['OP10', ['OPN', 'OPN']],
|
||||
['OP11', ['OPN', 'OPN']],
|
||||
['OP12', ['OPN', 'OPN']],
|
||||
['OP13', ['OPN', 'OPN']],
|
||||
['OP14', ['OPN', 'OPN']],
|
||||
['OP15', ['OPN', 'OPN']],
|
||||
['OP16', ['OPN', 'OPN']],
|
||||
['OP17', ['OPN', 'OPN']],
|
||||
['OP18', ['OPN', 'OPN']],
|
||||
['OP19', ['OPN', 'OPN']],
|
||||
['OP20', ['OPN', 'OPN']],
|
||||
['OP21', ['OPN', 'OPN']],
|
||||
['OP22', ['OPN', 'OPN']],
|
||||
['OP23', ['OPN', 'OPN']],
|
||||
['OP24', ['OPN', 'OPN']],
|
||||
['OP25', ['OPN', 'OPN']],
|
||||
['OP26', ['OPN', 'OPN']],
|
||||
['OP27', ['OPN', 'OPN']],
|
||||
['OP28', ['OPN', 'OPN']],
|
||||
['OP29', ['OPN', 'OPN']],
|
||||
['OP30', ['OPN', 'OPN']],
|
||||
['OP31', ['OPN', 'OPN']],
|
||||
['OPS1', ['OPN', 'OPN']],
|
||||
['OP1S', ['OPN', 'SYN']],
|
||||
['AAS1', ['SYN', 'A']],
|
||||
['AB1S', ['A', 'SYN']],
|
||||
['AB2S', ['A', 'SYN']],
|
||||
['BB1S', ['B', 'SYN']],
|
||||
['BB2S', ['B', 'SYN']],
|
||||
['BBS1', ['SYN', 'B']],
|
||||
['ZZ01', ['Z', 'Z']],
|
||||
['ZZ02', ['Z', 'Z']],
|
||||
['ZZ1S', ['Z', 'SYN']],
|
||||
['ZZ2S', ['Z', 'SYN']],
|
||||
['ZZS1', ['SYN', 'Z']],
|
||||
['ZZS2', ['SYN', 'Z']],
|
||||
]);
|
||||
|
||||
function getConformerColor(ntc: string, useLower: boolean): Color {
|
||||
const item = NtCToClasses.get(ntc);
|
||||
if (!item) return ErrorColor;
|
||||
return ColorMapping.get(useLower ? item[1] : item[0]) ?? ErrorColor;
|
||||
}
|
||||
|
||||
export const ConfalPyramidsColorThemeParams = {};
|
||||
export type ConfalPyramidsColorThemeParams = typeof ConfalPyramidsColorThemeParams
|
||||
export function getConfalPyramidsColorThemeParams(ctx: ThemeDataContext) {
|
||||
return ConfalPyramidsColorThemeParams; // TODO return copy
|
||||
return PD.clone(ConfalPyramidsColorThemeParams);
|
||||
}
|
||||
|
||||
export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values<ConfalPyramidsColorThemeParams>): ColorTheme<ConfalPyramidsColorThemeParams> {
|
||||
const colorMap = props.colors.name === 'default' ? PyramidsColors : props.colors.params;
|
||||
|
||||
function color(location: Location, isSecondary: boolean): Color {
|
||||
if (CPT.isLocation(location)) {
|
||||
const { pyramid, isLower } = location.data;
|
||||
return getConformerColor(pyramid.NtC, isLower);
|
||||
const { step, isLower } = location.data;
|
||||
const key = step.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
|
||||
return colorMap[key] ?? ErrorColor;
|
||||
}
|
||||
|
||||
return DefaultColor;
|
||||
return ErrorColor;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -162,12 +261,7 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend: TableLegend(iterableToArray(ColorMapping.entries()).map(([conformer, color]) => {
|
||||
return [conformer, color] as [string, Color];
|
||||
}).concat([
|
||||
['Error', ErrorColor],
|
||||
['Unknown', DefaultColor]
|
||||
]))
|
||||
legend: TableLegend(ObjectKeys(colorMap).map(k => [k.replace('_', ' '), colorMap[k]] as [string, Color]).concat([['Error', ErrorColor]])),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
|
||||
|
||||
export type ConfalPyramids = PropertyWrapper<CPT.PyramidsData | undefined >;
|
||||
export type ConfalPyramids = PropertyWrapper<CPT.Steps | undefined>;
|
||||
|
||||
export namespace ConfalPyramids {
|
||||
export const Schema = {
|
||||
@@ -105,36 +105,42 @@ export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramids
|
||||
|
||||
type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>;
|
||||
|
||||
function createPyramidsFromCif(model: Model,
|
||||
steps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
|
||||
stepsSummary: StepsSummaryTable): CPT.PyramidsData {
|
||||
const pyramids = new Array<CPT.Pyramid>();
|
||||
const names = new Map<string, number>();
|
||||
const locations = new Array<CPT.Location>();
|
||||
let hasMultipleModels = false;
|
||||
function createPyramidsFromCif(
|
||||
model: Model,
|
||||
cifSteps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
|
||||
stepsSummary: StepsSummaryTable
|
||||
): CPT.Steps {
|
||||
const steps = new Array<CPT.Step>();
|
||||
const mapping = new Array<CPT.MappedChains>();
|
||||
|
||||
const {
|
||||
id, PDB_model_number, name,
|
||||
auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
|
||||
auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
|
||||
_rowCount } = steps;
|
||||
_rowCount
|
||||
} = cifSteps;
|
||||
|
||||
if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
|
||||
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
const model_num = PDB_model_number.value(i);
|
||||
if (model_num !== model.modelNum) {
|
||||
hasMultipleModels = true;
|
||||
continue; // We are only interested in data for the current model
|
||||
}
|
||||
const {
|
||||
NtC,
|
||||
confal_score,
|
||||
rmsd
|
||||
} = getSummaryData(id.value(i), i, stepsSummary);
|
||||
const modelNum = PDB_model_number.value(i);
|
||||
const chainId = auth_asym_id_1.value(i);
|
||||
const seqId = auth_seq_id_1.value(i);
|
||||
const modelIdx = modelNum - 1;
|
||||
|
||||
const { _NtC, _confal_score } = getNtCAndConfalScore(id.value(i), i, stepsSummary);
|
||||
if (mapping.length <= modelIdx || !mapping[modelIdx])
|
||||
mapping[modelIdx] = new Map<string, CPT.MappedResidues>();
|
||||
|
||||
const pyramid = {
|
||||
PDB_model_number: model_num,
|
||||
const step = {
|
||||
PDB_model_number: modelNum,
|
||||
name: name.value(i),
|
||||
auth_asym_id_1: auth_asym_id_1.value(i),
|
||||
auth_seq_id_1: auth_seq_id_1.value(i),
|
||||
auth_asym_id_1: chainId,
|
||||
auth_seq_id_1: seqId,
|
||||
label_comp_id_1: label_comp_id_1.value(i),
|
||||
label_alt_id_1: label_alt_id_1.value(i),
|
||||
PDB_ins_code_1: PDB_ins_code_1.value(i),
|
||||
@@ -143,30 +149,41 @@ function createPyramidsFromCif(model: Model,
|
||||
label_comp_id_2: label_comp_id_2.value(i),
|
||||
label_alt_id_2: label_alt_id_2.value(i),
|
||||
PDB_ins_code_2: PDB_ins_code_2.value(i),
|
||||
confal_score: _confal_score,
|
||||
NtC: _NtC
|
||||
confal_score,
|
||||
NtC,
|
||||
rmsd,
|
||||
};
|
||||
|
||||
pyramids.push(pyramid);
|
||||
names.set(pyramid.name, pyramids.length - 1);
|
||||
steps.push(step);
|
||||
|
||||
locations.push(CPT.Location(pyramid, false));
|
||||
locations.push(CPT.Location(pyramid, true));
|
||||
const mappedChains = mapping[modelIdx];
|
||||
const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>();
|
||||
const stepsForResidue = residuesOnChain.get(seqId) ?? [];
|
||||
stepsForResidue.push(steps.length - 1);
|
||||
|
||||
residuesOnChain.set(seqId, stepsForResidue);
|
||||
mappedChains.set(chainId, residuesOnChain);
|
||||
mapping[modelIdx] = mappedChains;
|
||||
}
|
||||
|
||||
return { pyramids, names, locations, hasMultipleModels };
|
||||
return { steps, mapping };
|
||||
}
|
||||
|
||||
function getNtCAndConfalScore(id: number, i: number, stepsSummary: StepsSummaryTable) {
|
||||
const { step_id, confal_score, assigned_NtC } = stepsSummary;
|
||||
function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) {
|
||||
const {
|
||||
step_id,
|
||||
confal_score,
|
||||
assigned_NtC,
|
||||
cartesian_rmsd_closest_NtC_representative,
|
||||
} = stepsSummary;
|
||||
|
||||
// Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
|
||||
for (let j = i; j < stepsSummary._rowCount; j++) {
|
||||
if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
// Safety net for cases where the previous assumption is not met
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
|
||||
if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
|
||||
}
|
||||
throw new Error('Inconsistent mmCIF data');
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsUtil } from './util';
|
||||
import { ConfalPyramidsIterator } from './util';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { Interval } from '../../../mol-data/int';
|
||||
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
|
||||
@@ -16,14 +16,14 @@ import { PrimitiveBuilder } from '../../../mol-geo/primitive/primitive';
|
||||
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { EmptyLoci, Loci } from '../../../mol-model/loci';
|
||||
import { Structure, StructureProperties, Unit } from '../../../mol-model/structure';
|
||||
import { Structure, Unit } from '../../../mol-model/structure';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
|
||||
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
|
||||
import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
|
||||
import { VisualUpdateState } from '../../../mol-repr/util';
|
||||
import { VisualContext } from '../../../mol-repr/visual';
|
||||
import { getAltResidueLociFromId, StructureGroup } from '../../../mol-repr/structure/visual/util/common';
|
||||
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
@@ -32,6 +32,12 @@ const t = Mat4.identity();
|
||||
const w = Vec3.zero();
|
||||
const mp = Vec3.zero();
|
||||
|
||||
const posO3 = Vec3();
|
||||
const posP = Vec3();
|
||||
const posOP1 = Vec3();
|
||||
const posOP2 = Vec3();
|
||||
const posO5 = Vec3();
|
||||
|
||||
function calcMidpoint(mp: Vec3, v: Vec3, w: Vec3) {
|
||||
Vec3.sub(mp, v, w);
|
||||
Vec3.scale(mp, mp, 0.5);
|
||||
@@ -53,64 +59,76 @@ function createConfalPyramidsIterator(structureGroup: StructureGroup): LocationI
|
||||
const { structure, group } = structureGroup;
|
||||
const instanceCount = group.units.length;
|
||||
|
||||
const prop = ConfalPyramidsProvider.get(structure.model).value;
|
||||
if (prop === undefined || prop.data === undefined) {
|
||||
return LocationIterator(0, 1, 1, () => NullLocation);
|
||||
}
|
||||
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
|
||||
if (!data) return LocationIterator(0, 1, 1, () => NullLocation);
|
||||
|
||||
const { locations } = prop.data;
|
||||
const halfPyramidsCount = data.steps.length * 2;
|
||||
|
||||
const getLocation = (groupIndex: number, instanceIndex: number) => {
|
||||
if (locations.length <= groupIndex) return NullLocation;
|
||||
return locations[groupIndex];
|
||||
if (halfPyramidsCount <= groupIndex) return NullLocation;
|
||||
const idx = Math.floor(groupIndex / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
|
||||
return CPT.Location(data.steps[idx], groupIndex % 2 === 1);
|
||||
};
|
||||
return LocationIterator(locations.length, instanceCount, 1, getLocation);
|
||||
return LocationIterator(halfPyramidsCount, instanceCount, 1, getLocation);
|
||||
}
|
||||
|
||||
function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ConfalPyramidsMeshParams>, mesh?: Mesh) {
|
||||
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
|
||||
|
||||
const prop = ConfalPyramidsProvider.get(structure.model).value;
|
||||
if (prop === undefined || prop.data === undefined) return Mesh.createEmpty(mesh);
|
||||
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
|
||||
if (!data) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { pyramids } = prop.data;
|
||||
if (pyramids.length === 0) return Mesh.createEmpty(mesh);
|
||||
const { steps, mapping } = data;
|
||||
if (steps.length === 0) return Mesh.createEmpty(mesh);
|
||||
const vertexCount = (6 * steps.length) / mapping.length;
|
||||
|
||||
const mb = MeshBuilder.createState(512, 512, mesh);
|
||||
const mb = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh);
|
||||
|
||||
const handler = (pyramid: CPT.Pyramid, first: ConfalPyramidsUtil.FirstResidueAtoms, second: ConfalPyramidsUtil.SecondResidueAtoms, firsLocIndex: number, secondLocIndex: number) => {
|
||||
if (firsLocIndex === -1 || secondLocIndex === -1)
|
||||
throw new Error('Invalid location index');
|
||||
const it = new ConfalPyramidsIterator(structure, unit);
|
||||
while (it.hasNext) {
|
||||
const allPoints = it.move();
|
||||
|
||||
const scale = (pyramid.confal_score - 20.0) / 100.0;
|
||||
const O3 = first.O3.pos;
|
||||
const OP1 = second.OP1.pos; const OP2 = second.OP2.pos; const O5 = second.O5.pos; const P = second.P.pos;
|
||||
for (const points of allPoints) {
|
||||
const { O3, P, OP1, OP2, O5, confalScore } = points;
|
||||
const scale = (confalScore - 20.0) / 100.0;
|
||||
// Steps can be drawn in a different order than they are stored.
|
||||
// To make sure that we can get from the drawn pyramid back to the step in represents,
|
||||
// we need to use an appropriate groupId. The stepIdx passed from the iterator
|
||||
// is an index into the array of all steps in the structure.
|
||||
// Since a step is drawn as two "half-pyramids" we need two ids to map to a single step.
|
||||
// To do that, we just multiply the index by 2. idx*2 marks the "upper" half-pyramid,
|
||||
// (idx*2)+1 the "lower" half-pyramid.
|
||||
const groupIdx = points.stepIdx * 2;
|
||||
|
||||
shiftVertex(O3, P, scale);
|
||||
shiftVertex(OP1, P, scale);
|
||||
shiftVertex(OP2, P, scale);
|
||||
shiftVertex(O5, P, scale);
|
||||
calcMidpoint(mp, O3, O5);
|
||||
unit.conformation.invariantPosition(O3, posO3);
|
||||
unit.conformation.invariantPosition(P, posP);
|
||||
unit.conformation.invariantPosition(OP1, posOP1);
|
||||
unit.conformation.invariantPosition(OP2, posOP2);
|
||||
unit.conformation.invariantPosition(O5, posO5);
|
||||
|
||||
mb.currentGroup = firsLocIndex;
|
||||
let pb = PrimitiveBuilder(3);
|
||||
/* Upper part (for first residue in step) */
|
||||
pb.add(O3, OP1, OP2);
|
||||
pb.add(O3, mp, OP1);
|
||||
pb.add(O3, OP2, mp);
|
||||
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
|
||||
shiftVertex(posO3, posP, scale);
|
||||
shiftVertex(posOP1, posP, scale);
|
||||
shiftVertex(posOP2, posP, scale);
|
||||
shiftVertex(posO5, posP, scale);
|
||||
calcMidpoint(mp, posO3, posO5);
|
||||
|
||||
/* Lower part (for second residue in step */
|
||||
mb.currentGroup = secondLocIndex;
|
||||
pb = PrimitiveBuilder(3);
|
||||
pb.add(mp, O5, OP1);
|
||||
pb.add(mp, OP2, O5);
|
||||
pb.add(O5, OP2, OP1);
|
||||
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
|
||||
};
|
||||
mb.currentGroup = groupIdx;
|
||||
let pb = PrimitiveBuilder(3);
|
||||
/* Upper part (for first residue in step) */
|
||||
pb.add(posO3, posOP1, posOP2);
|
||||
pb.add(posO3, mp, posOP1);
|
||||
pb.add(posO3, posOP2, mp);
|
||||
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
|
||||
|
||||
const walker = new ConfalPyramidsUtil.UnitWalker(structure, unit, handler);
|
||||
walker.walk();
|
||||
/* Lower part (for second residue in step) */
|
||||
mb.currentGroup = groupIdx + 1;
|
||||
pb = PrimitiveBuilder(3);
|
||||
pb.add(mp, posO5, posOP1);
|
||||
pb.add(mp, posOP2, posO5);
|
||||
pb.add(posO5, posOP2, posOP1);
|
||||
MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
|
||||
}
|
||||
}
|
||||
|
||||
return MeshBuilder.getMesh(mb);
|
||||
}
|
||||
@@ -124,16 +142,17 @@ function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGro
|
||||
const unit = structureGroup.group.units[instanceId];
|
||||
if (!Unit.isAtomic(unit)) return EmptyLoci;
|
||||
|
||||
const prop = ConfalPyramidsProvider.get(structure.model).value;
|
||||
if (prop === undefined || prop.data === undefined) return EmptyLoci;
|
||||
const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
|
||||
if (!data) return EmptyLoci;
|
||||
|
||||
const { locations } = prop.data;
|
||||
const halfPyramidsCount = data.steps.length * 2;
|
||||
|
||||
if (locations.length <= groupId) return EmptyLoci;
|
||||
const altId = StructureProperties.atom.label_alt_id(CPT.toElementLocation(locations[groupId]));
|
||||
const rI = unit.residueIndex[locations[groupId].element.element];
|
||||
if (halfPyramidsCount <= groupId) return EmptyLoci;
|
||||
|
||||
return getAltResidueLociFromId(structure, unit, rI, altId);
|
||||
const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
|
||||
const step = data.steps[idx];
|
||||
|
||||
return CPT.Loci({ step, isLower: groupId % 2 === 1 }, [{}]);
|
||||
}
|
||||
|
||||
function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
*/
|
||||
|
||||
import { DataLocation } from '../../../mol-model/location';
|
||||
import { ElementIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
|
||||
import { DataLoci } from '../../../mol-model/loci';
|
||||
import { confalPyramidLabel } from './behavior';
|
||||
|
||||
export namespace ConfalPyramidsTypes {
|
||||
export type Pyramid = {
|
||||
export const DataTag = 'dnatco-confal-half-pyramid';
|
||||
|
||||
export type Step = {
|
||||
PDB_model_number: number,
|
||||
name: string,
|
||||
auth_asym_id_1: string,
|
||||
@@ -23,38 +26,40 @@ export namespace ConfalPyramidsTypes {
|
||||
label_alt_id_2: string,
|
||||
PDB_ins_code_2: string,
|
||||
confal_score: number,
|
||||
NtC: string
|
||||
NtC: string,
|
||||
rmsd: number,
|
||||
}
|
||||
|
||||
export interface PyramidsData {
|
||||
pyramids: Array<Pyramid>,
|
||||
names: Map<string, number>,
|
||||
locations: Array<Location>,
|
||||
hasMultipleModels: boolean
|
||||
export type MappedChains = Map<string, MappedResidues>;
|
||||
export type MappedResidues = Map<number, number[]>;
|
||||
|
||||
export interface Steps {
|
||||
steps: Array<Step>,
|
||||
mapping: MappedChains[],
|
||||
}
|
||||
|
||||
export interface LocationData {
|
||||
readonly pyramid: Pyramid
|
||||
readonly isLower: boolean;
|
||||
export interface HalfPyramid {
|
||||
step: Step,
|
||||
isLower: boolean,
|
||||
}
|
||||
|
||||
export interface Element {
|
||||
structure: Structure;
|
||||
unit: Unit.Atomic;
|
||||
element: ElementIndex;
|
||||
}
|
||||
export interface Location extends DataLocation<HalfPyramid, {}> {}
|
||||
|
||||
export interface Location extends DataLocation<LocationData, Element> {}
|
||||
|
||||
export function Location(pyramid: Pyramid, isLower: boolean, structure?: Structure, unit?: Unit.Atomic, element?: ElementIndex) {
|
||||
return DataLocation('pyramid', { pyramid, isLower }, { structure: structure as any, unit: unit as any, element: element as any });
|
||||
export function Location(step: Step, isLower: boolean) {
|
||||
return DataLocation(DataTag, { step, isLower }, {});
|
||||
}
|
||||
|
||||
export function isLocation(x: any): x is Location {
|
||||
return !!x && x.kind === 'data-location' && x.tag === 'pyramid';
|
||||
return !!x && x.kind === 'data-location' && x.tag === DataTag;
|
||||
}
|
||||
|
||||
export function toElementLocation(location: Location) {
|
||||
return StructureElement.Location.create(location.element.structure, location.element.unit, location.element.element);
|
||||
export interface Loci extends DataLoci<HalfPyramid, {}> {}
|
||||
|
||||
export function Loci(data: HalfPyramid, elements: ReadonlyArray<{}>): Loci {
|
||||
return DataLoci(DataTag, data, elements, undefined, () => confalPyramidLabel(data));
|
||||
}
|
||||
|
||||
export function isLoci(x: any): x is Loci {
|
||||
return !!x && x.kind === 'data-loci' && x.tag === DataTag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,293 +7,121 @@
|
||||
|
||||
import { ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { OrderedSet, Segmentation } from '../../../mol-data/int';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Segmentation } from '../../../mol-data/int';
|
||||
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure';
|
||||
|
||||
export namespace ConfalPyramidsUtil {
|
||||
type Residue = Segmentation.Segment<ResidueIndex>;
|
||||
type Residue = Segmentation.Segment<ResidueIndex>;
|
||||
|
||||
export type AtomInfo = {
|
||||
pos: Vec3,
|
||||
index: ElementIndex,
|
||||
fakeAltId: string,
|
||||
};
|
||||
export type Pyramid = {
|
||||
O3: ElementIndex,
|
||||
P: ElementIndex,
|
||||
OP1: ElementIndex,
|
||||
OP2: ElementIndex,
|
||||
O5: ElementIndex,
|
||||
confalScore: number,
|
||||
stepIdx: number,
|
||||
};
|
||||
|
||||
export type FirstResidueAtoms = {
|
||||
O3: AtomInfo,
|
||||
};
|
||||
const EmptyStepIndices = new Array<number>();
|
||||
|
||||
export type SecondResidueAtoms = {
|
||||
OP1: AtomInfo,
|
||||
OP2: AtomInfo,
|
||||
O5: AtomInfo,
|
||||
P: AtomInfo,
|
||||
};
|
||||
function copyResidue(r?: Residue) {
|
||||
return r ? { index: r.index, start: r.start, end: r.end } : void 0;
|
||||
}
|
||||
|
||||
type ResidueInfo = {
|
||||
PDB_model_num: number,
|
||||
asym_id: string,
|
||||
auth_asym_id: string,
|
||||
seq_id: number,
|
||||
auth_seq_id: number,
|
||||
comp_id: string,
|
||||
alt_id: string,
|
||||
ins_code: string,
|
||||
};
|
||||
function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string): ElementIndex {
|
||||
for (let eI = residue.start; eI < residue.end; eI++) {
|
||||
loc.element = loc.unit.elements[eI];
|
||||
const elName = StructureProperties.atom.label_atom_id(loc);
|
||||
const elAltId = StructureProperties.atom.label_alt_id(loc);
|
||||
|
||||
export type Handler = (pyramid: CPT.Pyramid, first: FirstResidueAtoms, second: SecondResidueAtoms, firstLocIndex: number, secondLocIndex: number) => void;
|
||||
|
||||
function residueInfoFromLocation(loc: StructureElement.Location): ResidueInfo {
|
||||
return {
|
||||
PDB_model_num: StructureProperties.unit.model_num(loc),
|
||||
asym_id: StructureProperties.chain.label_asym_id(loc),
|
||||
auth_asym_id: StructureProperties.chain.auth_asym_id(loc),
|
||||
seq_id: StructureProperties.residue.label_seq_id(loc),
|
||||
auth_seq_id: StructureProperties.residue.auth_seq_id(loc),
|
||||
comp_id: StructureProperties.atom.label_comp_id(loc),
|
||||
alt_id: StructureProperties.atom.label_alt_id(loc),
|
||||
ins_code: StructureProperties.residue.pdbx_PDB_ins_code(loc)
|
||||
};
|
||||
if (names.includes(elName) && (elAltId === altId || elAltId.length === 0))
|
||||
return loc.element;
|
||||
}
|
||||
|
||||
export function hasMultipleModels(unit: Unit.Atomic): boolean {
|
||||
return -1 as ElementIndex;
|
||||
}
|
||||
|
||||
function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue, altIdOne: string, altIdTwo: string, confalScore: number, stepIdx: number): Pyramid {
|
||||
const O3 = getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne);
|
||||
const P = getAtomIndex(loc, two, ['P'], altIdTwo);
|
||||
const OP1 = getAtomIndex(loc, two, ['OP1'], altIdTwo);
|
||||
const OP2 = getAtomIndex(loc, two, ['OP2'], altIdTwo);
|
||||
const O5 = getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo);
|
||||
|
||||
return { O3, P, OP1, OP2, O5, confalScore, stepIdx };
|
||||
}
|
||||
|
||||
export class ConfalPyramidsIterator {
|
||||
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
|
||||
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
|
||||
private residueOne?: Residue;
|
||||
private residueTwo: Residue;
|
||||
private data?: CPT.Steps;
|
||||
private loc: StructureElement.Location;
|
||||
|
||||
private getStepIndices(r: Residue) {
|
||||
this.loc.element = this.loc.unit.elements[r.start];
|
||||
|
||||
const modelIdx = StructureProperties.unit.model_num(this.loc) - 1;
|
||||
const chainId = StructureProperties.chain.auth_asym_id(this.loc);
|
||||
const seqId = StructureProperties.residue.auth_seq_id(this.loc);
|
||||
|
||||
const chains = this.data!.mapping[modelIdx];
|
||||
if (!chains) return EmptyStepIndices;
|
||||
const residues = chains.get(chainId);
|
||||
if (!residues) return EmptyStepIndices;
|
||||
return residues.get(seqId) ?? EmptyStepIndices;
|
||||
}
|
||||
|
||||
private moveStep() {
|
||||
this.residueOne = copyResidue(this.residueTwo);
|
||||
this.residueTwo = copyResidue(this.residueIt.move())!;
|
||||
|
||||
return this.toPyramids(this.residueOne!, this.residueTwo);
|
||||
}
|
||||
|
||||
private toPyramids(one: Residue, two: Residue) {
|
||||
const indices = this.getStepIndices(one);
|
||||
|
||||
const points = [];
|
||||
for (const idx of indices) {
|
||||
const step = this.data!.steps[idx];
|
||||
points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.confal_score, idx));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
constructor(structure: Structure, unit: Unit) {
|
||||
this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
|
||||
this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
|
||||
|
||||
const prop = ConfalPyramidsProvider.get(unit.model).value;
|
||||
if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
|
||||
return prop.data.hasMultipleModels;
|
||||
this.data = prop?.data;
|
||||
|
||||
if (this.chainIt.hasNext) {
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
if (this.residueIt.hasNext)
|
||||
this.residueTwo = this.residueIt.move();
|
||||
}
|
||||
|
||||
this.loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
|
||||
}
|
||||
|
||||
function getPossibleAltIdsIndices(eIFirst: ElementIndex, eILast: ElementIndex, structure: Structure, unit: Unit.Atomic): string[] {
|
||||
const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
|
||||
|
||||
const uIFirst = OrderedSet.indexOf(unit.elements, eIFirst);
|
||||
const uILast = OrderedSet.indexOf(unit.elements, eILast);
|
||||
|
||||
const possibleAltIds: string[] = [];
|
||||
for (let uI = uIFirst; uI <= uILast; uI++) {
|
||||
loc.element = unit.elements[uI];
|
||||
const altId = StructureProperties.atom.label_alt_id(loc);
|
||||
if (altId !== '' && !possibleAltIds.includes(altId)) possibleAltIds.push(altId);
|
||||
}
|
||||
|
||||
return possibleAltIds;
|
||||
get hasNext() {
|
||||
if (!this.data)
|
||||
return false;
|
||||
return this.residueIt.hasNext
|
||||
? true
|
||||
: this.chainIt.hasNext;
|
||||
}
|
||||
|
||||
function getPossibleAltIdsResidue(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] {
|
||||
return getPossibleAltIdsIndices(unit.elements[residue.start], unit.elements[residue.end - 1], structure, unit);
|
||||
}
|
||||
|
||||
class Utility {
|
||||
protected getPyramidByName(name: string): { pyramid: CPT.Pyramid | undefined, index: number } {
|
||||
const index = this.data.names.get(name);
|
||||
if (index === undefined) return { pyramid: undefined, index: -1 };
|
||||
|
||||
return { pyramid: this.data.pyramids[index], index };
|
||||
move() {
|
||||
if (this.residueIt.hasNext) {
|
||||
return this.moveStep();
|
||||
} else {
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
return this.moveStep();
|
||||
}
|
||||
|
||||
protected stepToName(entry_id: string, modelNum: number, locFirst: StructureElement.Location, locSecond: StructureElement.Location, fakeAltId_1: string, fakeAltId_2: string) {
|
||||
const first = residueInfoFromLocation(locFirst);
|
||||
const second = residueInfoFromLocation(locSecond);
|
||||
const model_id = this.hasMultipleModels ? `-m${modelNum}` : '';
|
||||
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
|
||||
const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : '');
|
||||
const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : '';
|
||||
const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : '';
|
||||
|
||||
return `${entry_id}${model_id}_${first.auth_asym_id}_${first.comp_id}${alt_id_1}_${first.auth_seq_id}${ins_code_1}_${second.comp_id}${alt_id_2}_${second.auth_seq_id}${ins_code_2}`;
|
||||
}
|
||||
|
||||
constructor(unit: Unit.Atomic) {
|
||||
const prop = ConfalPyramidsProvider.get(unit.model).value;
|
||||
if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
|
||||
|
||||
this.data = prop.data;
|
||||
this.hasMultipleModels = hasMultipleModels(unit);
|
||||
|
||||
this.entryId = unit.model.entryId.toLowerCase();
|
||||
this.modelNum = unit.model.modelNum;
|
||||
}
|
||||
|
||||
protected readonly data: CPT.PyramidsData;
|
||||
protected readonly hasMultipleModels: boolean;
|
||||
protected readonly entryId: string;
|
||||
protected readonly modelNum: number;
|
||||
}
|
||||
|
||||
export class UnitWalker extends Utility {
|
||||
private getAtomIndices(names: string[], residue: Residue): ElementIndex[] {
|
||||
let rI = residue.start;
|
||||
const rILast = residue.end - 1;
|
||||
const indices: ElementIndex[] = [];
|
||||
|
||||
for (; rI !== rILast; rI++) {
|
||||
const eI = this.unit.elements[rI];
|
||||
const loc = StructureElement.Location.create(this.structure, this.unit, eI);
|
||||
const thisName = StructureProperties.atom.label_atom_id(loc);
|
||||
if (names.includes(thisName)) indices.push(eI);
|
||||
}
|
||||
|
||||
if (indices.length === 0)
|
||||
throw new Error(`Element ${name} not found on residue ${residue.index}`);
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
private getAtomPositions(indices: ElementIndex[]): Vec3[] {
|
||||
const pos = this.unit.conformation.invariantPosition;
|
||||
const positions: Vec3[] = [];
|
||||
|
||||
for (const eI of indices) {
|
||||
const v = Vec3.zero();
|
||||
pos(eI, v);
|
||||
positions.push(v);
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
private handleStep(firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[]) {
|
||||
const modelNum = this.hasMultipleModels ? this.modelNum : -1;
|
||||
let ok = false;
|
||||
|
||||
const firstLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
|
||||
const secondLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
|
||||
for (let i = 0; i < firstAtoms.length; i++) {
|
||||
const first = firstAtoms[i];
|
||||
for (let j = 0; j < secondAtoms.length; j++) {
|
||||
const second = secondAtoms[j];
|
||||
firstLoc.element = first.O3.index;
|
||||
secondLoc.element = second.OP1.index;
|
||||
|
||||
const name = this.stepToName(this.entryId, modelNum, firstLoc, secondLoc, first.O3.fakeAltId, second.OP1.fakeAltId);
|
||||
const { pyramid, index } = this.getPyramidByName(name);
|
||||
if (pyramid !== undefined) {
|
||||
const setLoc = (loc: CPT.Location, eI: ElementIndex) => {
|
||||
loc.element.structure = this.structure;
|
||||
loc.element.unit = this.unit;
|
||||
loc.element.element = eI;
|
||||
};
|
||||
|
||||
const locIndex = index * 2;
|
||||
setLoc(this.data.locations[locIndex], firstLoc.element);
|
||||
setLoc(this.data.locations[locIndex + 1], secondLoc.element);
|
||||
this.handler(pyramid, first, second, locIndex, locIndex + 1);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) throw new Error('Bogus step');
|
||||
}
|
||||
|
||||
private processFirstResidue(residue: Residue, possibleAltIds: string[]) {
|
||||
const indO3 = this.getAtomIndices(['O3\'', 'O3*'], residue);
|
||||
const posO3 = this.getAtomPositions(indO3);
|
||||
|
||||
const altPos: FirstResidueAtoms[] = [
|
||||
{ O3: { pos: posO3[0], index: indO3[0], fakeAltId: '' } }
|
||||
];
|
||||
|
||||
for (let i = 1; i < indO3.length; i++) {
|
||||
altPos.push({ O3: { pos: posO3[i], index: indO3[i], fakeAltId: '' } });
|
||||
}
|
||||
|
||||
if (altPos.length === 1 && possibleAltIds.length > 1) {
|
||||
/* We have some alternate positions on the residue but O3 does not have any - fake them */
|
||||
altPos[0].O3.fakeAltId = possibleAltIds[0];
|
||||
|
||||
for (let i = 1; i < possibleAltIds.length; i++)
|
||||
altPos.push({ O3: { pos: posO3[0], index: indO3[0], fakeAltId: possibleAltIds[i] } });
|
||||
}
|
||||
|
||||
return altPos;
|
||||
}
|
||||
|
||||
private processSecondResidue(residue: Residue, possibleAltIds: string[]) {
|
||||
const indOP1 = this.getAtomIndices(['OP1'], residue);
|
||||
const indOP2 = this.getAtomIndices(['OP2'], residue);
|
||||
const indO5 = this.getAtomIndices(['O5\'', 'O5*'], residue);
|
||||
const indP = this.getAtomIndices(['P'], residue);
|
||||
|
||||
const posOP1 = this.getAtomPositions(indOP1);
|
||||
const posOP2 = this.getAtomPositions(indOP2);
|
||||
const posO5 = this.getAtomPositions(indO5);
|
||||
const posP = this.getAtomPositions(indP);
|
||||
|
||||
const infoOP1: AtomInfo[] = [];
|
||||
/* We use OP1 as "pivotal" atom. There is no specific reason
|
||||
* to pick OP1, it is as good a choice as any other atom
|
||||
*/
|
||||
if (indOP1.length === 1 && possibleAltIds.length > 1) {
|
||||
/* No altIds on OP1, fake them */
|
||||
for (const altId of possibleAltIds)
|
||||
infoOP1.push({ pos: posOP1[0], index: indOP1[0], fakeAltId: altId });
|
||||
} else {
|
||||
for (let i = 0; i < indOP1.length; i++)
|
||||
infoOP1.push({ pos: posOP1[i], index: indOP1[i], fakeAltId: '' });
|
||||
}
|
||||
|
||||
const mkInfo = (i: number, indices: ElementIndex[], positions: Vec3[], altId: string) => {
|
||||
if (i >= indices.length) {
|
||||
const last = indices.length - 1;
|
||||
return { pos: positions[last], index: indices[last], fakeAltId: altId };
|
||||
}
|
||||
|
||||
return { pos: positions[i], index: indices[i], fakeAltId: altId };
|
||||
};
|
||||
|
||||
const altPos: SecondResidueAtoms[] = [];
|
||||
for (let i = 0; i < infoOP1.length; i++) {
|
||||
const altId = infoOP1[i].fakeAltId;
|
||||
|
||||
const OP2 = mkInfo(i, indOP2, posOP2, altId);
|
||||
const O5 = mkInfo(i, indO5, posO5, altId);
|
||||
const P = mkInfo(i, indP, posP, altId);
|
||||
|
||||
altPos.push({ OP1: infoOP1[i], OP2, O5, P });
|
||||
}
|
||||
|
||||
return altPos;
|
||||
}
|
||||
|
||||
private step(residue: Residue): { firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[] } {
|
||||
const firstPossibleAltIds = getPossibleAltIdsResidue(residue, this.structure, this.unit);
|
||||
const firstAtoms = this.processFirstResidue(residue, firstPossibleAltIds);
|
||||
|
||||
residue = this.residueIt.move();
|
||||
|
||||
const secondPossibleAltIds = getPossibleAltIdsResidue(residue, this.structure, this.unit);
|
||||
const secondAtoms = this.processSecondResidue(residue, secondPossibleAltIds);
|
||||
|
||||
return { firstAtoms, secondAtoms };
|
||||
}
|
||||
|
||||
walk() {
|
||||
while (this.chainIt.hasNext) {
|
||||
this.residueIt.setSegment(this.chainIt.move());
|
||||
|
||||
let residue = this.residueIt.move();
|
||||
while (this.residueIt.hasNext) {
|
||||
try {
|
||||
const { firstAtoms, secondAtoms } = this.step(residue);
|
||||
|
||||
this.handleStep(firstAtoms, secondAtoms);
|
||||
} catch (error) {
|
||||
/* Skip and move along */
|
||||
residue = this.residueIt.move();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private structure: Structure, private unit: Unit.Atomic, private handler: Handler) {
|
||||
super(unit);
|
||||
|
||||
this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
|
||||
this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
|
||||
}
|
||||
|
||||
private chainIt: Segmentation.SegmentIterator<ChainIndex>;
|
||||
private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this._controls?.dispose();
|
||||
this._controls = void 0;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
|
||||
const dt = durationMs / N;
|
||||
|
||||
await ctx.update({ message: 'Rendering...', isIndeterminate: false, current: 0, max: N + 1 });
|
||||
await params.pass.updateBackground();
|
||||
|
||||
await plugin.managers.animation.play(params.animation.definition, params.animation.params);
|
||||
stoppedAnimation = false;
|
||||
|
||||
@@ -102,6 +102,7 @@ export class Mp4EncoderUI extends CollapsableControls<{}, State> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this._controls?.dispose();
|
||||
this._controls = void 0;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
// Generated on 2022-01-15T19:52:34-08:00
|
||||
// Generated on 2022-08-20T16:36:05-07:00
|
||||
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
@@ -13,11 +13,8 @@ export type Scalars = {
|
||||
Boolean: boolean;
|
||||
Int: number;
|
||||
Float: number;
|
||||
/** Built-in scalar representing an instant in time */
|
||||
Date: any;
|
||||
/** Built-in scalar for dynamic values */
|
||||
ObjectScalar: any;
|
||||
/** Use SPQR's SchemaPrinter to remove this from SDL */
|
||||
UNREPRESENTABLE: any;
|
||||
};
|
||||
|
||||
@@ -477,6 +474,8 @@ export type ClustersMembers = {
|
||||
export type CoreAssembly = {
|
||||
/** Get PDB entry that includes this assembly. */
|
||||
readonly entry?: Maybe<CoreEntry>;
|
||||
/** Get all pairwise polymer interfaces for this PDB assembly. */
|
||||
readonly interfaces?: Maybe<ReadonlyArray<Maybe<CoreInterface>>>;
|
||||
readonly pdbx_struct_assembly?: Maybe<PdbxStructAssembly>;
|
||||
readonly pdbx_struct_assembly_auth_evidence?: Maybe<ReadonlyArray<Maybe<PdbxStructAssemblyAuthEvidence>>>;
|
||||
readonly pdbx_struct_assembly_gen?: Maybe<ReadonlyArray<Maybe<PdbxStructAssemblyGen>>>;
|
||||
@@ -657,6 +656,8 @@ export type CoreEntry = {
|
||||
readonly em_staining?: Maybe<ReadonlyArray<Maybe<EmStaining>>>;
|
||||
readonly em_vitrification?: Maybe<ReadonlyArray<Maybe<EmVitrification>>>;
|
||||
readonly entry?: Maybe<Entry>;
|
||||
/** Get all groups for this PDB entry. */
|
||||
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
|
||||
readonly exptl?: Maybe<ReadonlyArray<Maybe<Exptl>>>;
|
||||
readonly exptl_crystal?: Maybe<ReadonlyArray<Maybe<ExptlCrystal>>>;
|
||||
readonly exptl_crystal_grow?: Maybe<ReadonlyArray<Maybe<ExptlCrystalGrow>>>;
|
||||
@@ -701,6 +702,7 @@ export type CoreEntry = {
|
||||
readonly rcsb_associated_holdings?: Maybe<CurrentEntry>;
|
||||
readonly rcsb_binding_affinity?: Maybe<ReadonlyArray<Maybe<RcsbBindingAffinity>>>;
|
||||
readonly rcsb_entry_container_identifiers: RcsbEntryContainerIdentifiers;
|
||||
readonly rcsb_entry_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbEntryGroupMembership>>>;
|
||||
readonly rcsb_entry_info: RcsbEntryInfo;
|
||||
readonly rcsb_external_references?: Maybe<ReadonlyArray<Maybe<RcsbExternalReferences>>>;
|
||||
/**
|
||||
@@ -724,6 +726,16 @@ export type CoreEntry = {
|
||||
readonly symmetry?: Maybe<Symmetry>;
|
||||
};
|
||||
|
||||
export type CoreInterface = {
|
||||
readonly rcsb_id: Scalars['String'];
|
||||
readonly rcsb_interface_container_identifiers: RcsbInterfaceContainerIdentifiers;
|
||||
readonly rcsb_interface_info?: Maybe<RcsbInterfaceInfo>;
|
||||
/** List of operations for each interface partner. */
|
||||
readonly rcsb_interface_operator: ReadonlyArray<Maybe<ReadonlyArray<Maybe<ReadonlyArray<Maybe<Scalars['String']>>>>>>;
|
||||
readonly rcsb_interface_partner: ReadonlyArray<Maybe<RcsbInterfacePartner>>;
|
||||
readonly rcsb_latest_revision?: Maybe<RcsbLatestRevision>;
|
||||
};
|
||||
|
||||
export type CoreNonpolymerEntity = {
|
||||
/** Get PDB entry that contains this non-polymer entity. */
|
||||
readonly entry?: Maybe<CoreEntry>;
|
||||
@@ -839,6 +851,8 @@ export type CorePolymerEntity = {
|
||||
readonly pdbx_entity_src_syn?: Maybe<ReadonlyArray<Maybe<PdbxEntitySrcSyn>>>;
|
||||
/** Get all unique Pfam annotations associated with this molecular entity. */
|
||||
readonly pfams?: Maybe<ReadonlyArray<Maybe<CorePfam>>>;
|
||||
/** Get all groups for this PDB entity. */
|
||||
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
|
||||
/** Get all unique polymer instances (chains) for this molecular entity. */
|
||||
readonly polymer_entity_instances?: Maybe<ReadonlyArray<Maybe<CorePolymerEntityInstance>>>;
|
||||
/** Get a BIRD chemical components described in this molecular entity. */
|
||||
@@ -875,6 +889,7 @@ export type CorePolymerEntity = {
|
||||
readonly rcsb_polymer_entity_container_identifiers: RcsbPolymerEntityContainerIdentifiers;
|
||||
readonly rcsb_polymer_entity_feature?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeature>>>;
|
||||
readonly rcsb_polymer_entity_feature_summary?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeatureSummary>>>;
|
||||
readonly rcsb_polymer_entity_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembership>>>;
|
||||
readonly rcsb_polymer_entity_keywords?: Maybe<RcsbPolymerEntityKeywords>;
|
||||
readonly rcsb_polymer_entity_name_com?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameCom>>>;
|
||||
readonly rcsb_polymer_entity_name_sys?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameSys>>>;
|
||||
@@ -3186,6 +3201,79 @@ export type GeneName = {
|
||||
readonly value?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type GroupEntry = {
|
||||
/** Get provenance associated with this group. */
|
||||
readonly group_provenance?: Maybe<GroupProvenance>;
|
||||
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
|
||||
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
|
||||
readonly rcsb_group_info: RcsbGroupInfo;
|
||||
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
|
||||
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
|
||||
/** A unique textual identifier for a group */
|
||||
readonly rcsb_id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GroupMembersAlignmentScores = {
|
||||
readonly query_coverage: Scalars['Int'];
|
||||
readonly query_length: Scalars['Int'];
|
||||
readonly target_coverage: Scalars['Int'];
|
||||
readonly target_length: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type GroupPolymerEntity = {
|
||||
/** Get provenance associated with this group. */
|
||||
readonly group_provenance?: Maybe<GroupProvenance>;
|
||||
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
|
||||
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
|
||||
readonly rcsb_group_info: RcsbGroupInfo;
|
||||
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
|
||||
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
|
||||
/** A unique textual identifier for a group */
|
||||
readonly rcsb_id: Scalars['String'];
|
||||
readonly rcsb_polymer_entity_group_members_rankings?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankings>>>;
|
||||
readonly rcsb_polymer_entity_group_sequence_alignment?: Maybe<RcsbPolymerEntityGroupSequenceAlignment>;
|
||||
};
|
||||
|
||||
export type GroupProvenance = {
|
||||
readonly rcsb_group_aggregation_method?: Maybe<RcsbGroupAggregationMethod>;
|
||||
readonly rcsb_group_provenance_container_identifiers?: Maybe<RcsbGroupProvenanceContainerIdentifiers>;
|
||||
/** A unique textual identifier for a group provenance */
|
||||
readonly rcsb_id?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type InterfacePartnerFeatureAdditionalProperties = {
|
||||
/**
|
||||
* The additional property name.
|
||||
*
|
||||
* Allowable values:
|
||||
* TO_BE_DEFINED
|
||||
*
|
||||
*/
|
||||
readonly name?: Maybe<Scalars['String']>;
|
||||
/** The value(s) of the additional property. */
|
||||
readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['ObjectScalar']>>>;
|
||||
};
|
||||
|
||||
export type InterfacePartnerFeatureFeaturePositions = {
|
||||
/** An identifier for the monomer at which this segment of the feature begins. */
|
||||
readonly beg_seq_id: Scalars['Int'];
|
||||
/** An identifier for the monomer at which this segment of the feature ends. */
|
||||
readonly end_seq_id?: Maybe<Scalars['Int']>;
|
||||
/** The value(s) of the feature over the monomer segment. */
|
||||
readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
|
||||
};
|
||||
|
||||
export type MethodDetails = {
|
||||
/** A description of special aspects of the clustering process */
|
||||
readonly description?: Maybe<Scalars['String']>;
|
||||
/** Defines the name of the description associated with the clustering process */
|
||||
readonly name?: Maybe<Scalars['String']>;
|
||||
/** Defines the type of the description associated with the clustering process */
|
||||
readonly type?: Maybe<Scalars['String']>;
|
||||
/** Defines the value associated with the clustering process */
|
||||
readonly value?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
export type PdbxAuditRevisionCategory = {
|
||||
/**
|
||||
* The category updated in the pdbx_audit_revision_category record.
|
||||
@@ -6745,6 +6833,16 @@ export type Query = {
|
||||
readonly entries?: Maybe<ReadonlyArray<Maybe<CoreEntry>>>;
|
||||
/** Get PDB entry given the PDB id. */
|
||||
readonly entry?: Maybe<CoreEntry>;
|
||||
/** Given a group ID get a group object formed by aggregating individual structures that share a degree of similarity */
|
||||
readonly entry_group?: Maybe<GroupEntry>;
|
||||
/** Given a list of group IDs get a list of group objects formed by aggregating structures that share a degree of similarity */
|
||||
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
|
||||
/** Given a group provenance ID get an object that describes aggregation method used to create groups */
|
||||
readonly group_provenance?: Maybe<GroupProvenance>;
|
||||
/** Get a pairwise polymeric interface given the PDB ID, ASSEMBLY ID and INTERFACE ID. */
|
||||
readonly interface?: Maybe<CoreInterface>;
|
||||
/** Get a list of pairwise polymeric interfaces given a list of INTERFACE IDs. Here INTERFACE ID is a compound identifier that includes entry_id, assembly_id and interface_id e.g. 1XXX-1.1. */
|
||||
readonly interfaces?: Maybe<ReadonlyArray<Maybe<CoreInterface>>>;
|
||||
/** Get a list of PDB non-polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. */
|
||||
readonly nonpolymer_entities?: Maybe<ReadonlyArray<Maybe<CoreNonpolymerEntity>>>;
|
||||
/** Get a PDB non-polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
|
||||
@@ -6757,6 +6855,10 @@ export type Query = {
|
||||
readonly polymer_entities?: Maybe<ReadonlyArray<Maybe<CorePolymerEntity>>>;
|
||||
/** Get a PDB polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
|
||||
readonly polymer_entity?: Maybe<CorePolymerEntity>;
|
||||
/** Given a group ID get a group object formed by aggregating polymer entities that share a degree of similarity */
|
||||
readonly polymer_entity_group?: Maybe<GroupPolymerEntity>;
|
||||
/** Given a list of group IDs get a list of group objects formed by aggregating polymer entities that share a degree of similarity */
|
||||
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
|
||||
/** Get a PDB polymer entity instance (chain), given the PDB ID and ENTITY INSTANCE ID. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
|
||||
readonly polymer_entity_instance?: Maybe<CorePolymerEntityInstance>;
|
||||
/** Get a list of PDB polymer entity instances (chains), given the list of ENTITY INSTANCE IDs. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
|
||||
@@ -6831,6 +6933,38 @@ export type QueryEntryArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryEntry_GroupArgs = {
|
||||
group_id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryEntry_GroupsArgs = {
|
||||
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryGroup_ProvenanceArgs = {
|
||||
group_provenance_id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryInterfaceArgs = {
|
||||
assembly_id: Scalars['String'];
|
||||
entry_id: Scalars['String'];
|
||||
interface_id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryInterfacesArgs = {
|
||||
interface_ids: ReadonlyArray<Scalars['String']>;
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryNonpolymer_EntitiesArgs = {
|
||||
entity_ids: ReadonlyArray<Scalars['String']>;
|
||||
@@ -6870,6 +7004,18 @@ export type QueryPolymer_EntityArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryPolymer_Entity_GroupArgs = {
|
||||
group_id: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryPolymer_Entity_GroupsArgs = {
|
||||
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
/** Query root */
|
||||
export type QueryPolymer_Entity_InstanceArgs = {
|
||||
asym_id: Scalars['String'];
|
||||
@@ -6952,6 +7098,8 @@ export type RcsbAssemblyContainerIdentifiers = {
|
||||
readonly assembly_id: Scalars['String'];
|
||||
/** Entry identifier for the container. */
|
||||
readonly entry_id: Scalars['String'];
|
||||
/** List of binary interface Ids within the assembly (it points to interface id collection). */
|
||||
readonly interface_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
|
||||
/**
|
||||
* A unique identifier for each object in this assembly container formed by
|
||||
* a dash separated concatenation of entry and assembly identifiers.
|
||||
@@ -7010,6 +7158,24 @@ export type RcsbAssemblyInfo = {
|
||||
* This is the total count of non-polymer entity instances generated in the assembly coordinate data.
|
||||
*/
|
||||
readonly nonpolymer_entity_instance_count?: Maybe<Scalars['Int']>;
|
||||
/** Number of heterologous (both binding sites are different) interface entities */
|
||||
readonly num_heterologous_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of heteromeric (both partners are different polymeric entities) interface entities */
|
||||
readonly num_heteromeric_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of homomeric (both partners are the same polymeric entity) interface entities */
|
||||
readonly num_homomeric_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of polymer-polymer interface entities, grouping equivalent interfaces at the entity level (i.e. same entity_ids on either side, with similar but not identical binding sites) */
|
||||
readonly num_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of geometrically equivalent (i.e. same asym_ids on either side) polymer-polymer interfaces in the assembly */
|
||||
readonly num_interfaces?: Maybe<Scalars['Int']>;
|
||||
/** Number of isologous (both binding sites are same, i.e. interface is symmetric) interface entities */
|
||||
readonly num_isologous_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of nucleic acid-nucleic acid interface entities */
|
||||
readonly num_na_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of protein-nucleic acid interface entities */
|
||||
readonly num_prot_na_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** Number of protein-protein interface entities */
|
||||
readonly num_protein_interface_entities?: Maybe<Scalars['Int']>;
|
||||
/** The assembly non-hydrogen polymer entity atomic coordinate count. */
|
||||
readonly polymer_atom_count?: Maybe<Scalars['Int']>;
|
||||
/**
|
||||
@@ -7085,6 +7251,10 @@ export type RcsbAssemblyInfo = {
|
||||
* This is the total count of solvent entity instances generated in the assembly coordinate data.
|
||||
*/
|
||||
readonly solvent_entity_instance_count?: Maybe<Scalars['Int']>;
|
||||
/** Total buried surface area calculated as the sum of buried surface areas over all interfaces */
|
||||
readonly total_assembly_buried_surface_area?: Maybe<Scalars['Float']>;
|
||||
/** Total number of interfacing residues in the assembly, calculated as the sum of interfacing residues over all interfaces */
|
||||
readonly total_number_interface_residues?: Maybe<Scalars['Int']>;
|
||||
/**
|
||||
* The number of unmodeled polymer monomers in the assembly coordinate data. This is
|
||||
* the total count of monomers with unreported coordinate data for all polymer
|
||||
@@ -8590,6 +8760,25 @@ export type RcsbEntryContainerIdentifiers = {
|
||||
readonly water_entity_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
|
||||
};
|
||||
|
||||
export type RcsbEntryGroupMembership = {
|
||||
/**
|
||||
* Method used to establish group membership
|
||||
*
|
||||
* Allowable values:
|
||||
* matching_deposit_group_id
|
||||
*
|
||||
*/
|
||||
readonly aggregation_method: Scalars['String'];
|
||||
/**
|
||||
* A unique identifier for a group of entries
|
||||
*
|
||||
* Examples:
|
||||
* G_1001001
|
||||
*
|
||||
*/
|
||||
readonly group_id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbEntryInfo = {
|
||||
/** The number of assemblies defined for this entry including the deposited assembly. */
|
||||
readonly assembly_count?: Maybe<Scalars['Int']>;
|
||||
@@ -8817,6 +9006,220 @@ export type RcsbGenomicLineage = {
|
||||
readonly name?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbGroupAccessionInfo = {
|
||||
/** Identifies the version of the groups solution */
|
||||
readonly version: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type RcsbGroupAggregationMethod = {
|
||||
/** The details on a method used to calculate cluster solutions */
|
||||
readonly method: RcsbGroupAggregationMethodMethod;
|
||||
readonly similarity_criteria?: Maybe<RcsbGroupAggregationMethodSimilarityCriteria>;
|
||||
/**
|
||||
* Specifies the type of similarity criteria used to aggregate members into higher levels in the hierarchy
|
||||
*
|
||||
* Allowable values:
|
||||
* sequence_identity, matching_uniprot_accession, matching_deposit_group_id
|
||||
*
|
||||
*/
|
||||
readonly type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbGroupAggregationMethodMethod = {
|
||||
/** Additional details describing the clustering process */
|
||||
readonly details?: Maybe<ReadonlyArray<Maybe<MethodDetails>>>;
|
||||
/**
|
||||
* The name of the software or the method used to calculate cluster solutions
|
||||
*
|
||||
* Allowable values:
|
||||
* mmseqs2, matching_reference_identity
|
||||
*
|
||||
*/
|
||||
readonly name: Scalars['String'];
|
||||
/**
|
||||
* The version of the software.
|
||||
*
|
||||
* Examples:
|
||||
* v1.0, 3.1-2, unknown
|
||||
*
|
||||
*/
|
||||
readonly version?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbGroupAggregationMethodSimilarityCriteria = {
|
||||
/**
|
||||
* A function or similarity measure that quantifies the similarity between two members
|
||||
*
|
||||
* Allowable values:
|
||||
* rmsd, sequence_identity
|
||||
*
|
||||
*/
|
||||
readonly similarity_function?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbGroupContainerIdentifiers = {
|
||||
/** A unique textual identifier for a group */
|
||||
readonly group_id: Scalars['String'];
|
||||
/** Member identifiers representing a group */
|
||||
readonly group_member_ids: ReadonlyArray<Maybe<Scalars['String']>>;
|
||||
/**
|
||||
* A unique group provenance identifier
|
||||
*
|
||||
* Allowable values:
|
||||
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
|
||||
*
|
||||
*/
|
||||
readonly group_provenance_id: Scalars['String'];
|
||||
/** Member identifiers representing a higher level in the groping hierarchy that has parent-child relationship */
|
||||
readonly parent_member_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
|
||||
};
|
||||
|
||||
export type RcsbGroupInfo = {
|
||||
readonly group_description?: Maybe<Scalars['String']>;
|
||||
readonly group_members_count: Scalars['Int'];
|
||||
/**
|
||||
* Granularity of group members identifiers
|
||||
*
|
||||
* Allowable values:
|
||||
* assembly, entry, polymer_entity, polymer_entity_instance
|
||||
*
|
||||
*/
|
||||
readonly group_members_granularity: Scalars['String'];
|
||||
readonly group_name?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbGroupProvenanceContainerIdentifiers = {
|
||||
/**
|
||||
* A unique group provenance identifier
|
||||
*
|
||||
* Allowable values:
|
||||
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
|
||||
*
|
||||
*/
|
||||
readonly group_provenance_id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbGroupRelated = {
|
||||
/**
|
||||
* A unique code assigned to a reference related the group
|
||||
*
|
||||
* Examples:
|
||||
* P69905
|
||||
*
|
||||
*/
|
||||
readonly resource_accession_code?: Maybe<Scalars['String']>;
|
||||
/**
|
||||
* Defines the type of the resource describing related references
|
||||
*
|
||||
* Examples:
|
||||
* UniProt
|
||||
*
|
||||
*/
|
||||
readonly resource_name?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbGroupStatistics = {
|
||||
/** The desired lower limit for the similarity between two members that belong to the same group */
|
||||
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
|
||||
/** Similarity score between two most similar group members */
|
||||
readonly similarity_score_max?: Maybe<Scalars['Float']>;
|
||||
/** Similarity score between two least similar group members */
|
||||
readonly similarity_score_min?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
export type RcsbInterfaceContainerIdentifiers = {
|
||||
/** This item references an assembly in pdbx_struct_assembly */
|
||||
readonly assembly_id: Scalars['String'];
|
||||
/** Entry identifier for the container. */
|
||||
readonly entry_id: Scalars['String'];
|
||||
/**
|
||||
* Identifier for NCS-equivalent interfaces within the assembly (same entity_ids on both sides)
|
||||
*
|
||||
* Examples:
|
||||
* 1, 2
|
||||
*
|
||||
*/
|
||||
readonly interface_entity_id?: Maybe<Scalars['String']>;
|
||||
/**
|
||||
* Identifier for the geometrically equivalent (same asym_ids on either side) interfaces within the assembly
|
||||
*
|
||||
* Examples:
|
||||
* 1, 2
|
||||
*
|
||||
*/
|
||||
readonly interface_id: Scalars['String'];
|
||||
/**
|
||||
* Unique identifier for the document
|
||||
*
|
||||
* Examples:
|
||||
* 2UZI-1.A.B?1
|
||||
*
|
||||
*/
|
||||
readonly rcsb_id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbInterfaceInfo = {
|
||||
/** Total interface buried surface area */
|
||||
readonly interface_area?: Maybe<Scalars['Float']>;
|
||||
/** Allowable values: homo, hetero. */
|
||||
readonly interface_character?: Maybe<Scalars['String']>;
|
||||
/** Number of core interface residues, defined as those that bury >90% accessible surface area with respect to the unbound state */
|
||||
readonly num_core_interface_residues?: Maybe<Scalars['Int']>;
|
||||
/** Number of interface residues, defined as those with burial fraction > 0 */
|
||||
readonly num_interface_residues?: Maybe<Scalars['Int']>;
|
||||
/** Allowable values: Nucleic acid (only), Protein (only), Protein/NA. */
|
||||
readonly polymer_composition?: Maybe<Scalars['String']>;
|
||||
/** The Jaccard score (intersection over union) of interface contacts in homomeric interfaces, comparing contact sets left-right vs right-left. High values indicate isologous (symmetric) interfaces, with value=1 if perfectly symmetric (e.g. crystallographic symmetry) */
|
||||
readonly self_jaccard_contact_score?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
export type RcsbInterfacePartner = {
|
||||
readonly interface_partner_feature?: Maybe<ReadonlyArray<Maybe<RcsbInterfacePartnerInterfacePartnerFeature>>>;
|
||||
readonly interface_partner_identifier?: Maybe<RcsbInterfacePartnerInterfacePartnerIdentifier>;
|
||||
};
|
||||
|
||||
export type RcsbInterfacePartnerInterfacePartnerFeature = {
|
||||
readonly additional_properties?: Maybe<ReadonlyArray<Maybe<InterfacePartnerFeatureAdditionalProperties>>>;
|
||||
/**
|
||||
* Identifies the version of the feature assignment.
|
||||
*
|
||||
* Examples:
|
||||
* V4_0_2
|
||||
*
|
||||
*/
|
||||
readonly assignment_version?: Maybe<Scalars['String']>;
|
||||
/** A description for the feature. */
|
||||
readonly description?: Maybe<Scalars['String']>;
|
||||
/** An identifier for the feature. */
|
||||
readonly feature_id?: Maybe<Scalars['String']>;
|
||||
readonly feature_positions?: Maybe<ReadonlyArray<Maybe<InterfacePartnerFeatureFeaturePositions>>>;
|
||||
/** A name for the feature. */
|
||||
readonly name?: Maybe<Scalars['String']>;
|
||||
/**
|
||||
* Code identifying the individual, organization or program that assigned the feature.
|
||||
*
|
||||
* Examples:
|
||||
* NACCESS
|
||||
*
|
||||
*/
|
||||
readonly provenance_source?: Maybe<Scalars['String']>;
|
||||
/**
|
||||
* A type or category of the feature.
|
||||
*
|
||||
* Allowable values:
|
||||
* ASA_UNBOUND, ASA_BOUND
|
||||
*
|
||||
*/
|
||||
readonly type?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbInterfacePartnerInterfacePartnerIdentifier = {
|
||||
/** Instance identifier for this container. */
|
||||
readonly asym_id: Scalars['String'];
|
||||
/** Polymer entity identifier for the container. */
|
||||
readonly entity_id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbLatestRevision = {
|
||||
/** The major version number of the latest revision. */
|
||||
readonly major_revision?: Maybe<Scalars['Int']>;
|
||||
@@ -9733,6 +10136,7 @@ export type RcsbPolymerEntity = {
|
||||
*
|
||||
*/
|
||||
readonly rcsb_multiple_source_flag?: Maybe<Scalars['String']>;
|
||||
readonly rcsb_polymer_name_combined?: Maybe<RcsbPolymerEntityRcsbPolymerNameCombined>;
|
||||
/**
|
||||
* The number of biological sources for the polymer entity. Multiple source contributions
|
||||
* may come from the same organism (taxonomy).
|
||||
@@ -10059,6 +10463,69 @@ export type RcsbPolymerEntityFeatureSummary = {
|
||||
readonly type?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupMembersRankings = {
|
||||
readonly group_members: ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankingsGroupMembers>>;
|
||||
/**
|
||||
* Defines ranking option applicable to group members
|
||||
*
|
||||
* Allowable values:
|
||||
* coverage
|
||||
*
|
||||
*/
|
||||
readonly ranking_criteria_type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupMembersRankingsGroupMembers = {
|
||||
readonly member_id: Scalars['String'];
|
||||
/** Quantifies the criteria used for ranking */
|
||||
readonly original_score?: Maybe<Scalars['Float']>;
|
||||
/** Reflects a relationship between group members such that, for any two members the first is ranked higher (smaller rank value) than the second */
|
||||
readonly rank: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupMembership = {
|
||||
/**
|
||||
* Method used to establish group membership
|
||||
*
|
||||
* Allowable values:
|
||||
* sequence_identity, matching_uniprot_accession
|
||||
*
|
||||
*/
|
||||
readonly aggregation_method: Scalars['String'];
|
||||
/**
|
||||
* A unique identifier for a group of entities
|
||||
*
|
||||
* Examples:
|
||||
* 1_100, P00003
|
||||
*
|
||||
*/
|
||||
readonly group_id: Scalars['String'];
|
||||
/** Degree of similarity expressed as a floating-point number */
|
||||
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupSequenceAlignment = {
|
||||
/** Abstract reference where group members can be aligned to generate a MSA */
|
||||
readonly abstract_reference: RcsbPolymerEntityGroupSequenceAlignmentAbstractReference;
|
||||
/** Alignment with a core_entity canonical sequence */
|
||||
readonly group_members_alignment: ReadonlyArray<Maybe<RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment>>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupSequenceAlignmentAbstractReference = {
|
||||
/** Abstract reference length */
|
||||
readonly length: Scalars['Int'];
|
||||
/** Sequence that represents the abstract reference */
|
||||
readonly sequence?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment = {
|
||||
/** Alignment region encoded as a triplet [query_begin, target_begin, length] */
|
||||
readonly aligned_regions: ReadonlyArray<Maybe<ReadonlyArray<Maybe<Scalars['Int']>>>>;
|
||||
readonly member_id: Scalars['String'];
|
||||
/** Alignment scores */
|
||||
readonly scores: GroupMembersAlignmentScores;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityInstanceContainerIdentifiers = {
|
||||
/** Instance identifier for this container. */
|
||||
readonly asym_id: Scalars['String'];
|
||||
@@ -10177,6 +10644,13 @@ export type RcsbPolymerEntityRcsbMacromolecularNamesCombined = {
|
||||
readonly provenance_source?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerEntityRcsbPolymerNameCombined = {
|
||||
/** Protein name annotated by the UniProtKB or macromolecular name assigned by the PDB */
|
||||
readonly names?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
|
||||
/** Allowable values: PDB Preferred Name, PDB Description, UniProt Name. */
|
||||
readonly provenance_source?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type RcsbPolymerInstanceAnnotation = {
|
||||
/** An identifier for the annotation. */
|
||||
readonly annotation_id?: Maybe<Scalars['String']>;
|
||||
@@ -10263,7 +10737,7 @@ export type RcsbPolymerInstanceFeature = {
|
||||
* A type or category of the feature.
|
||||
*
|
||||
* Allowable values:
|
||||
* ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ
|
||||
* ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ, ASA
|
||||
*
|
||||
*/
|
||||
readonly type?: Maybe<Scalars['String']>;
|
||||
@@ -13145,4 +13619,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
|
||||
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null> | null } | null> } | null>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null, readonly start: ReadonlyArray<number | null>, readonly end: ReadonlyArray<number | null> } | null> | null } | null> | null } | null };
|
||||
|
||||
@@ -55,7 +55,16 @@ function createIntraUnitClashCylinderMesh(ctx: VisualContext, unit: Unit, struct
|
||||
radius: (edgeIndex: number) => magnitude[edgeIndex] * sizeFactor,
|
||||
};
|
||||
|
||||
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
const { mesh: m, boundingSphere } = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
if (boundingSphere) {
|
||||
m.setBoundingSphere(boundingSphere);
|
||||
} else if (m.triangleCount > 0) {
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
export const IntraUnitClashParams = {
|
||||
@@ -169,7 +178,16 @@ function createInterUnitClashCylinderMesh(ctx: VisualContext, structure: Structu
|
||||
radius: (edgeIndex: number) => edges[edgeIndex].props.magnitude * sizeFactor
|
||||
};
|
||||
|
||||
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
const { mesh: m, boundingSphere } = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
if (boundingSphere) {
|
||||
m.setBoundingSphere(boundingSphere);
|
||||
} else {
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
export const InterUnitClashParams = {
|
||||
|
||||
30
src/extensions/zenodo/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
|
||||
import { ZenodoImportUI } from './ui';
|
||||
|
||||
export const ZenodoImport = PluginBehavior.create<{ }>({
|
||||
name: 'extension-zenodo-import',
|
||||
category: 'misc',
|
||||
display: {
|
||||
name: 'Zenodo Export'
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ }> {
|
||||
register(): void {
|
||||
this.ctx.customImportControls.set('zenodo-import', ZenodoImportUI as any);
|
||||
}
|
||||
|
||||
update() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.customImportControls.delete('zenodo-import');
|
||||
}
|
||||
},
|
||||
params: () => ({ })
|
||||
});
|
||||
302
src/extensions/zenodo/ui.tsx
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { DownloadFile } from '../../mol-plugin-state/actions/file';
|
||||
import { DownloadStructure, LoadTrajectory } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { CoordinatesFormatCategory } from '../../mol-plugin-state/formats/coordinates';
|
||||
import { TopologyFormatCategory } from '../../mol-plugin-state/formats/topology';
|
||||
import { TrajectoryFormatCategory } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { VolumeFormatCategory } from '../../mol-plugin-state/formats/volume';
|
||||
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
|
||||
import { Button } from '../../mol-plugin-ui/controls/common';
|
||||
import { OpenInBrowserSvg } from '../../mol-plugin-ui/controls/icons';
|
||||
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { formatBytes } from '../../mol-util';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
|
||||
type ZenodoFile = {
|
||||
bucket: string
|
||||
checksum: string
|
||||
key: string
|
||||
links: {
|
||||
[key: string]: string
|
||||
self: string
|
||||
}
|
||||
size: number
|
||||
type: string
|
||||
}
|
||||
|
||||
type ZenodoRecord = {
|
||||
id: number
|
||||
conceptdoi: string
|
||||
conceptrecid: string
|
||||
created: string
|
||||
doi: string
|
||||
files: ZenodoFile[]
|
||||
revision: number
|
||||
updated: string
|
||||
metadata: {
|
||||
title: string
|
||||
}
|
||||
}
|
||||
|
||||
interface State {
|
||||
busy?: boolean
|
||||
recordValues: PD.Values<typeof ZenodoImportParams>
|
||||
importValues?: PD.Values<ImportParams>
|
||||
importParams?: ImportParams
|
||||
record?: ZenodoRecord
|
||||
files?: ZenodoFile[]
|
||||
}
|
||||
|
||||
const ZenodoImportParams = {
|
||||
record: PD.Text('', { description: 'Zenodo ID.' })
|
||||
};
|
||||
|
||||
function createImportParams(files: ZenodoFile[], plugin: PluginContext) {
|
||||
const modelOpts: [string, string][] = [];
|
||||
const topologyOpts: [string, string][] = [];
|
||||
const coordinatesOpts: [string, string][] = [];
|
||||
const volumeOpts: [string, string][] = [];
|
||||
const compressedOpts: [string, string][] = [];
|
||||
|
||||
const structureExts = new Map<string, { format: string, isBinary: boolean }>();
|
||||
const coordinatesExts = new Map<string, { format: string, isBinary: boolean }>();
|
||||
const topologyExts = new Map<string, { format: string, isBinary: boolean }>();
|
||||
const volumeExts = new Map<string, { format: string, isBinary: boolean }>();
|
||||
|
||||
for (const { provider: { category, binaryExtensions, stringExtensions }, name } of plugin.dataFormats.list) {
|
||||
if (category === TrajectoryFormatCategory) {
|
||||
if (binaryExtensions) for (const e of binaryExtensions) structureExts.set(e, { format: name, isBinary: true });
|
||||
if (stringExtensions) for (const e of stringExtensions) structureExts.set(e, { format: name, isBinary: false });
|
||||
} else if (category === VolumeFormatCategory) {
|
||||
if (binaryExtensions) for (const e of binaryExtensions) volumeExts.set(e, { format: name, isBinary: true });
|
||||
if (stringExtensions) for (const e of stringExtensions) volumeExts.set(e, { format: name, isBinary: false });
|
||||
} else if (category === CoordinatesFormatCategory) {
|
||||
if (binaryExtensions) for (const e of binaryExtensions) coordinatesExts.set(e, { format: name, isBinary: true });
|
||||
if (stringExtensions) for (const e of stringExtensions) coordinatesExts.set(e, { format: name, isBinary: false });
|
||||
} else if (category === TopologyFormatCategory) {
|
||||
if (binaryExtensions) for (const e of binaryExtensions) topologyExts.set(e, { format: name, isBinary: true });
|
||||
if (stringExtensions) for (const e of stringExtensions) topologyExts.set(e, { format: name, isBinary: false });
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const label = `${file.key} (${formatBytes(file.size)})`;
|
||||
if (structureExts.has(file.type)) {
|
||||
const { format, isBinary } = structureExts.get(file.type)!;
|
||||
modelOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
|
||||
topologyOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
|
||||
} else if (volumeExts.has(file.type)) {
|
||||
const { format, isBinary } = volumeExts.get(file.type)!;
|
||||
volumeOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
|
||||
} else if (topologyExts.has(file.type)) {
|
||||
const { format, isBinary } = topologyExts.get(file.type)!;
|
||||
topologyOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
|
||||
} else if (coordinatesExts.has(file.type)) {
|
||||
const { format, isBinary } = coordinatesExts.get(file.type)!;
|
||||
coordinatesOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
|
||||
} else if (file.type === 'zip') {
|
||||
compressedOpts.push([`${file.links.self}|${file.type}|true`, label]);
|
||||
}
|
||||
}
|
||||
|
||||
const params: PD.Params = {};
|
||||
let defaultType = '';
|
||||
|
||||
if (modelOpts.length) {
|
||||
defaultType = 'structure';
|
||||
params.structure = PD.Select(modelOpts[0][0], modelOpts);
|
||||
}
|
||||
|
||||
if (topologyOpts.length && coordinatesOpts.length) {
|
||||
if (!defaultType) defaultType = 'trajectory';
|
||||
params.trajectory = PD.Group({
|
||||
topology: PD.Select(topologyOpts[0][0], topologyOpts),
|
||||
coordinates: PD.Select(coordinatesOpts[0][0], coordinatesOpts),
|
||||
}, { isFlat: true });
|
||||
}
|
||||
|
||||
if (volumeOpts.length) {
|
||||
if (!defaultType) defaultType = 'volume';
|
||||
params.volume = PD.Select(volumeOpts[0][0], volumeOpts);
|
||||
}
|
||||
|
||||
if (compressedOpts.length) {
|
||||
if (!defaultType) defaultType = 'compressed';
|
||||
params.compressed = PD.Select(compressedOpts[0][0], compressedOpts);
|
||||
}
|
||||
|
||||
return {
|
||||
type: PD.MappedStatic(defaultType, Object.keys(params).length ? params : { '': PD.EmptyGroup() })
|
||||
};
|
||||
}
|
||||
type ImportParams = ReturnType<typeof createImportParams>
|
||||
|
||||
export class ZenodoImportUI extends CollapsableControls<{}, State> {
|
||||
protected defaultState(): State & CollapsableState {
|
||||
return {
|
||||
header: 'Zenodo Import',
|
||||
isCollapsed: true,
|
||||
brand: { accent: 'cyan', svg: OpenInBrowserSvg },
|
||||
recordValues: PD.getDefaultValues(ZenodoImportParams),
|
||||
importValues: undefined,
|
||||
importParams: undefined,
|
||||
record: undefined,
|
||||
files: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private recordParamsOnChange = (values: any) => {
|
||||
this.setState({ recordValues: values });
|
||||
};
|
||||
|
||||
private importParamsOnChange = (values: any) => {
|
||||
this.setState({ importValues: values });
|
||||
};
|
||||
|
||||
private loadRecord = async () => {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const record: ZenodoRecord = await this.plugin.runTask(this.plugin.fetch({ url: `https://zenodo.org/api/records/${this.state.recordValues.record}`, type: 'json' }));
|
||||
const importParams = createImportParams(record.files, this.plugin);
|
||||
this.setState({
|
||||
record,
|
||||
files: record.files,
|
||||
busy: false,
|
||||
importValues: PD.getDefaultValues(importParams),
|
||||
importParams
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.plugin.log.error(`Failed to load Zenodo record '${this.state.recordValues.record}'`);
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
};
|
||||
|
||||
private loadFile = async (values: PD.Values<ImportParams>) => {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
|
||||
const t = values.type;
|
||||
if (t.name === 'structure') {
|
||||
const defaultParams = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
|
||||
const [url, format, isBinary] = t.params.split('|');
|
||||
|
||||
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
url,
|
||||
format: format as any,
|
||||
isBinary: isBinary === 'true',
|
||||
options: defaultParams.source.params.options,
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else if (t.name === 'trajectory') {
|
||||
const [topologyUrl, topologyFormat, topologyIsBinary] = t.params.topology.split('|');
|
||||
const [coordinatesUrl, coordinatesFormat, coordinatesIsBinary] = t.params.coordinates.split('|');
|
||||
|
||||
await this.plugin.runTask(this.plugin.state.data.applyAction(LoadTrajectory, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
model: {
|
||||
url: topologyUrl,
|
||||
format: topologyFormat as any,
|
||||
isBinary: topologyIsBinary === 'true',
|
||||
},
|
||||
coordinates: {
|
||||
url: coordinatesUrl,
|
||||
format: coordinatesFormat as any,
|
||||
isBinary: coordinatesIsBinary === 'true',
|
||||
},
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else if (t.name === 'volume') {
|
||||
const [url, format, isBinary] = t.params.split('|');
|
||||
|
||||
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
url,
|
||||
format: format as any,
|
||||
isBinary: isBinary === 'true',
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else if (t.name === 'compressed') {
|
||||
const [url, format, isBinary] = t.params.split('|');
|
||||
|
||||
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadFile, {
|
||||
url,
|
||||
format: format as any,
|
||||
isBinary: isBinary === 'true',
|
||||
visuals: true
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.plugin.log.error(`Failed to load Zenodo file`);
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
};
|
||||
|
||||
private clearRecord = () => {
|
||||
this.setState({
|
||||
importValues: undefined,
|
||||
importParams: undefined,
|
||||
record: undefined,
|
||||
files: undefined
|
||||
});
|
||||
};
|
||||
|
||||
private renderLoadRecord() {
|
||||
return <div style={{ marginBottom: 10 }}>
|
||||
<ParameterControls params={ZenodoImportParams} values={this.state.recordValues} onChangeValues={this.recordParamsOnChange} isDisabled={this.state.busy} />
|
||||
<Button onClick={this.loadRecord} style={{ marginTop: 1 }} disabled={this.state.busy || !this.state.recordValues.record}>
|
||||
Load Record
|
||||
</Button>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderRecordInfo(record: ZenodoRecord) {
|
||||
return <div style={{ marginBottom: 10 }}>
|
||||
<div className='msp-help-text'>
|
||||
<div>Record {`${record.id}`}: <i>{`${record.metadata.title}`}</i></div>
|
||||
</div>
|
||||
<Button onClick={this.clearRecord} style={{ marginTop: 1 }} disabled={this.state.busy}>
|
||||
Clear
|
||||
</Button>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderImportFile(params: ImportParams, values: PD.Values<ImportParams>) {
|
||||
return values.type.name ? <div style={{ marginBottom: 10 }}>
|
||||
<ParameterControls params={params} values={this.state.importValues} onChangeValues={this.importParamsOnChange} isDisabled={this.state.busy} />
|
||||
<Button onClick={() => this.loadFile(values)} style={{ marginTop: 1 }} disabled={this.state.busy}>
|
||||
Import File
|
||||
</Button>
|
||||
</div> : <div className='msp-help-text' style={{ marginBottom: 10 }}>
|
||||
<div>No supported files</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
protected renderControls(): JSX.Element | null {
|
||||
return <>
|
||||
{!this.state.record ? this.renderLoadRecord() : null}
|
||||
{this.state.record ? this.renderRecordInfo(this.state.record) : null}
|
||||
{this.state.importParams && this.state.importValues ? this.renderImportFile(this.state.importParams, this.state.importValues) : null}
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Scene } from '../mol-gl/scene';
|
||||
import { assertUnreachable } from '../mol-util/type-helpers';
|
||||
|
||||
export { ICamera, Camera };
|
||||
|
||||
@@ -84,7 +85,7 @@ class Camera implements ICamera {
|
||||
switch (this.state.mode) {
|
||||
case 'orthographic': updateOrtho(this); break;
|
||||
case 'perspective': updatePers(this); break;
|
||||
default: throw new Error('unknown camera mode');
|
||||
default: assertUnreachable(this.state.mode);
|
||||
}
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -30,7 +30,7 @@ import { PickData } from './passes/pick';
|
||||
import { PickHelper } from './passes/pick';
|
||||
import { ImagePass, ImageProps } from './passes/image';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { isDebugMode } from '../mol-util/debug';
|
||||
import { isDebugMode, isTimingMode } from '../mol-util/debug';
|
||||
import { CameraHelperParams } from './helper/camera-helper';
|
||||
import { produce } from 'immer';
|
||||
import { HandleHelperParams } from './helper/handle-helper';
|
||||
@@ -40,6 +40,8 @@ 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 { degToRad, radToDeg } from '../mol-math/misc';
|
||||
import { AssetManager } from '../mol-util/assets';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
camera: PD.Group({
|
||||
@@ -49,6 +51,7 @@ export const Canvas3DParams = {
|
||||
on: PD.Group(StereoCameraParams),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, hideIf: p => p?.mode !== 'perspective' }),
|
||||
fov: PD.Numeric(45, { min: 10, max: 130, step: 1 }, { label: 'Field of View' }),
|
||||
manualReset: PD.Boolean(false, { isHidden: true }),
|
||||
}, { pivot: 'mode' }),
|
||||
cameraFog: PD.MappedStatic('on', {
|
||||
@@ -78,6 +81,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),
|
||||
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
@@ -106,6 +110,7 @@ interface Canvas3DContext {
|
||||
readonly attribs: Readonly<Canvas3DContext.Attribs>
|
||||
readonly contextLost: BehaviorSubject<now.Timestamp>
|
||||
readonly contextRestored: BehaviorSubject<now.Timestamp>
|
||||
readonly assetManager: AssetManager
|
||||
dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => void
|
||||
}
|
||||
|
||||
@@ -124,7 +129,7 @@ namespace Canvas3DContext {
|
||||
};
|
||||
export type Attribs = typeof DefaultAttribs
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, attribs: Partial<Attribs> = {}): Canvas3DContext {
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
|
||||
const a = { ...DefaultAttribs, ...attribs };
|
||||
const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
|
||||
const gl = getGLContext(canvas, {
|
||||
@@ -139,7 +144,7 @@ namespace Canvas3DContext {
|
||||
|
||||
const input = InputObserver.fromElement(canvas, { pixelScale, preventGestures: true });
|
||||
const webgl = createContext(gl, { pixelScale });
|
||||
const passes = new Passes(webgl, attribs);
|
||||
const passes = new Passes(webgl, assetManager, a);
|
||||
|
||||
if (isDebugMode) {
|
||||
const loseContextExt = gl.getExtension('WEBGL_lose_context');
|
||||
@@ -192,6 +197,7 @@ namespace Canvas3DContext {
|
||||
attribs: a,
|
||||
contextLost,
|
||||
contextRestored: webgl.contextRestored,
|
||||
assetManager,
|
||||
dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => {
|
||||
input.dispose();
|
||||
|
||||
@@ -236,7 +242,7 @@ interface Canvas3D {
|
||||
/** Sets drawPaused = false without starting the built in animation loop */
|
||||
resume(): void
|
||||
identify(x: number, y: number): PickData | undefined
|
||||
mark(loci: Representation.Loci, action: MarkerAction, noDraw?: boolean): void
|
||||
mark(loci: Representation.Loci, action: MarkerAction): void
|
||||
getLoci(pickingId: PickingId | undefined): Representation.Loci
|
||||
|
||||
notifyDidDraw: boolean,
|
||||
@@ -278,7 +284,7 @@ namespace Canvas3D {
|
||||
export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
|
||||
|
||||
export function create({ webgl, input, passes, attribs }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
|
||||
export function create({ webgl, input, passes, attribs, assetManager }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
|
||||
const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
|
||||
|
||||
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
|
||||
@@ -299,11 +305,16 @@ namespace Canvas3D {
|
||||
|
||||
const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
|
||||
|
||||
function getSceneRadius() {
|
||||
return scene.boundingSphere.radius * p.sceneRadiusFactor;
|
||||
}
|
||||
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.camera.mode,
|
||||
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
|
||||
clipFar: p.cameraClipping.far
|
||||
clipFar: p.cameraClipping.far,
|
||||
fov: degToRad(p.camera.fov),
|
||||
}, { x, y, width, height }, { pixelScale: attribs.pixelScale });
|
||||
const stereoCamera = new StereoCamera(camera, p.camera.stereo.params);
|
||||
|
||||
@@ -315,6 +326,10 @@ namespace Canvas3D {
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction);
|
||||
const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
|
||||
|
||||
passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
|
||||
if (changed) requestDraw();
|
||||
});
|
||||
|
||||
let cameraResetRequested = false;
|
||||
let nextCameraResetDuration: number | undefined = void 0;
|
||||
let nextCameraResetSnapshot: Camera.SnapshotProvider | undefined = void 0;
|
||||
@@ -345,34 +360,43 @@ namespace Canvas3D {
|
||||
return { loci, repr };
|
||||
}
|
||||
|
||||
function mark(reprLoci: Representation.Loci, action: MarkerAction, noDraw = false) {
|
||||
let markBuffer: [reprLoci: Representation.Loci, action: MarkerAction][] = [];
|
||||
|
||||
function mark(reprLoci: Representation.Loci, action: MarkerAction) {
|
||||
// NOTE: might try to optimize a case with opposite actions for the
|
||||
// same loci. Tho this might end up being more expensive (and error prone)
|
||||
// then just applying everything "naively".
|
||||
markBuffer.push([reprLoci, action]);
|
||||
}
|
||||
|
||||
function resolveMarking() {
|
||||
let changed = false;
|
||||
for (const [r, l] of markBuffer) {
|
||||
changed = applyMark(r, l) || changed;
|
||||
}
|
||||
markBuffer = [];
|
||||
if (changed) {
|
||||
scene.update(void 0, true);
|
||||
helper.handle.scene.update(void 0, true);
|
||||
helper.camera.scene.update(void 0, true);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
function applyMark(reprLoci: Representation.Loci, action: MarkerAction) {
|
||||
const { repr, loci } = reprLoci;
|
||||
let changed = false;
|
||||
if (repr) {
|
||||
changed = repr.mark(loci, action);
|
||||
changed = repr.mark(loci, action) || changed;
|
||||
} else {
|
||||
changed = helper.handle.mark(loci, action);
|
||||
changed = helper.camera.mark(loci, action) || changed;
|
||||
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
|
||||
}
|
||||
if (changed) {
|
||||
if (noDraw) {
|
||||
// Even with `noDraw` make sure changes will be rendered.
|
||||
// Note that with this calling mark (with or without `noDraw`) multiple times
|
||||
// during a JS event loop iteration will only result in a single render call.
|
||||
forceNextRender = true;
|
||||
} else {
|
||||
scene.update(void 0, true);
|
||||
helper.handle.scene.update(void 0, true);
|
||||
helper.camera.scene.update(void 0, true);
|
||||
const prevPickDirty = pickHelper.dirty;
|
||||
draw({ force: true, allowMulti: true });
|
||||
pickHelper.dirty = prevPickDirty; // marking does not change picking buffers
|
||||
}
|
||||
}
|
||||
changed = helper.handle.mark(loci, action) || changed;
|
||||
changed = helper.camera.mark(loci, action) || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
function render(force: boolean, allowMulti: boolean) {
|
||||
function render(force: boolean) {
|
||||
if (webgl.isContextLost) return false;
|
||||
|
||||
let resized = false;
|
||||
@@ -386,6 +410,8 @@ namespace Canvas3D {
|
||||
y > gl.drawingBufferHeight || y + height < 0
|
||||
) return false;
|
||||
|
||||
const markingUpdated = resolveMarking() && (renderer.props.colorMarker || p.marking.enabled);
|
||||
|
||||
let didRender = false;
|
||||
controls.update(currentTime);
|
||||
const cameraChanged = camera.update();
|
||||
@@ -393,23 +419,27 @@ namespace Canvas3D {
|
||||
const shouldRender = force || cameraChanged || resized || forceNextRender;
|
||||
forceNextRender = false;
|
||||
|
||||
const multiSampleChanged = multiSampleHelper.update(shouldRender, p.multiSample);
|
||||
const multiSampleChanged = multiSampleHelper.update(markingUpdated || shouldRender, p.multiSample);
|
||||
|
||||
if (shouldRender || multiSampleChanged) {
|
||||
if (shouldRender || multiSampleChanged || markingUpdated) {
|
||||
let cam: Camera | StereoCamera = camera;
|
||||
if (p.camera.stereo.name === 'on') {
|
||||
stereoCamera.update();
|
||||
cam = stereoCamera;
|
||||
}
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('Canvas3D.render');
|
||||
const ctx = { renderer, camera: cam, scene, helper };
|
||||
if (MultiSamplePass.isEnabled(p.multiSample)) {
|
||||
const forceOn = !cameraChanged && allowMulti && !controls.isAnimating;
|
||||
const forceOn = !cameraChanged && markingUpdated && !controls.isAnimating;
|
||||
multiSampleHelper.render(ctx, p, true, forceOn);
|
||||
} else {
|
||||
passes.draw.render(ctx, p, true);
|
||||
}
|
||||
pickHelper.dirty = true;
|
||||
if (isTimingMode) webgl.timer.markEnd('Canvas3D.render');
|
||||
|
||||
// if only marking has updated, do not set the flag to dirty
|
||||
pickHelper.dirty = pickHelper.dirty || shouldRender;
|
||||
didRender = true;
|
||||
}
|
||||
|
||||
@@ -421,9 +451,9 @@ namespace Canvas3D {
|
||||
let currentTime = 0;
|
||||
let drawPaused = false;
|
||||
|
||||
function draw(options?: { force?: boolean, allowMulti?: boolean }) {
|
||||
function draw(options?: { force?: boolean }) {
|
||||
if (drawPaused) return;
|
||||
if (render(!!options?.force, !!options?.allowMulti) && notifyDidDraw) {
|
||||
if (render(!!options?.force) && notifyDidDraw) {
|
||||
didDraw.next(now() - startTime as now.Timestamp);
|
||||
}
|
||||
}
|
||||
@@ -508,7 +538,7 @@ namespace Canvas3D {
|
||||
const focus = camera.getFocus(center, radius);
|
||||
const next = typeof nextCameraResetSnapshot === 'function' ? nextCameraResetSnapshot(scene, camera) : nextCameraResetSnapshot;
|
||||
const snapshot = next ? { ...focus, ...next } : focus;
|
||||
camera.setState({ ...snapshot, radiusMax: scene.boundingSphere.radius }, duration);
|
||||
camera.setState({ ...snapshot, radiusMax: getSceneRadius() }, duration);
|
||||
}
|
||||
|
||||
nextCameraResetDuration = void 0;
|
||||
@@ -559,7 +589,7 @@ namespace Canvas3D {
|
||||
}
|
||||
if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
|
||||
|
||||
camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
|
||||
if (!p.camera.manualReset) camera.setState({ radiusMax: getSceneRadius() }, 0);
|
||||
reprCount.next(reprRenderObjects.size);
|
||||
if (isDebugMode) consoleStats();
|
||||
|
||||
@@ -635,7 +665,7 @@ namespace Canvas3D {
|
||||
|
||||
function getProps(): Canvas3DProps {
|
||||
const radius = scene.boundingSphere.radius > 0
|
||||
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
|
||||
? 100 - Math.round((camera.transition.target.radius / getSceneRadius()) * 100)
|
||||
: 0;
|
||||
|
||||
return {
|
||||
@@ -643,6 +673,7 @@ namespace Canvas3D {
|
||||
mode: camera.state.mode,
|
||||
helper: { ...helper.camera.props },
|
||||
stereo: { ...p.camera.stereo },
|
||||
fov: Math.round(radToDeg(camera.state.fov)),
|
||||
manualReset: !!p.camera.manualReset
|
||||
},
|
||||
cameraFog: camera.state.fog > 0
|
||||
@@ -650,6 +681,7 @@ namespace Canvas3D {
|
||||
: { name: 'off' as const, params: {} },
|
||||
cameraClipping: { far: camera.state.clipFar, radius },
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
sceneRadiusFactor: p.sceneRadiusFactor,
|
||||
transparentBackground: p.transparentBackground,
|
||||
viewport: p.viewport,
|
||||
|
||||
@@ -752,10 +784,19 @@ namespace Canvas3D {
|
||||
? produce(getProps(), properties as any)
|
||||
: properties;
|
||||
|
||||
if (props.sceneRadiusFactor !== undefined) {
|
||||
p.sceneRadiusFactor = props.sceneRadiusFactor;
|
||||
camera.setState({ radiusMax: getSceneRadius() }, 0);
|
||||
}
|
||||
|
||||
const cameraState: Partial<Camera.Snapshot> = Object.create(null);
|
||||
if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) {
|
||||
cameraState.mode = props.camera.mode;
|
||||
}
|
||||
const oldFov = Math.round(radToDeg(camera.state.fov));
|
||||
if (props.camera && props.camera.fov !== undefined && props.camera.fov !== oldFov) {
|
||||
cameraState.fov = degToRad(props.camera.fov);
|
||||
}
|
||||
if (props.cameraFog !== undefined && props.cameraFog.params) {
|
||||
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
|
||||
if (newFog !== camera.state.fog) cameraState.fog = newFog;
|
||||
@@ -765,7 +806,7 @@ namespace Canvas3D {
|
||||
cameraState.clipFar = props.cameraClipping.far;
|
||||
}
|
||||
if (props.cameraClipping.radius !== undefined) {
|
||||
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
|
||||
const radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius);
|
||||
if (radius > 0 && radius !== cameraState.radius) {
|
||||
// if radius = 0, NaNs happen
|
||||
cameraState.radius = Math.max(radius, 0.01);
|
||||
@@ -790,6 +831,12 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
if (props.postprocessing?.background) {
|
||||
Object.assign(p.postprocessing.background, props.postprocessing.background);
|
||||
passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
|
||||
if (changed && !doNotRequestDraw) requestDraw();
|
||||
});
|
||||
}
|
||||
if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing);
|
||||
if (props.marking) Object.assign(p.marking, props.marking);
|
||||
if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
|
||||
@@ -808,7 +855,7 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
|
||||
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
|
||||
},
|
||||
getRenderObjects(): GraphicsRenderObject[] {
|
||||
const renderObjects: GraphicsRenderObject[] = [];
|
||||
@@ -831,6 +878,8 @@ namespace Canvas3D {
|
||||
dispose: () => {
|
||||
contextRestoredSub.unsubscribe();
|
||||
|
||||
markBuffer = [];
|
||||
|
||||
scene.clear();
|
||||
helper.debug.clear();
|
||||
controls.dispose();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2021 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>
|
||||
*/
|
||||
@@ -17,7 +17,7 @@ import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { DataLoci, EmptyLoci, isEveryLoci, Loci } from '../../mol-model/loci';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { Visual } from '../../mol-repr/visual';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
@@ -113,8 +113,10 @@ export class CameraHelper {
|
||||
|
||||
mark(loci: Loci, action: MarkerAction) {
|
||||
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
|
||||
if (!isCameraAxesLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
if (!isEveryLoci(loci)) {
|
||||
if (!isCameraAxesLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
}
|
||||
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -20,7 +20,7 @@ import produce from 'immer';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { Camera } from '../camera';
|
||||
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
|
||||
import { DataLoci, EmptyLoci, isEveryLoci, Loci } from '../../mol-model/loci';
|
||||
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
|
||||
import { Visual } from '../../mol-repr/visual';
|
||||
import { Interval } from '../../mol-data/int';
|
||||
@@ -121,8 +121,10 @@ export class HandleHelper {
|
||||
|
||||
mark(loci: Loci, action: MarkerAction) {
|
||||
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
|
||||
if (!isHandleLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
if (!isEveryLoci(loci)) {
|
||||
if (!isHandleLoci(loci)) return false;
|
||||
if (loci.data !== this) return false;
|
||||
}
|
||||
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
|
||||
}
|
||||
|
||||
|
||||
461
src/mol-canvas3d/passes/background.ts
Normal file
@@ -0,0 +1,461 @@
|
||||
/**
|
||||
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { QuadPositions, } from '../../mol-gl/compute/util';
|
||||
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { AttributeSpec, DefineSpec, TextureSpec, UniformSpec, Values, ValueSpec } from '../../mol-gl/renderable/schema';
|
||||
import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { background_frag } from '../../mol-gl/shader/background.frag';
|
||||
import { background_vert } from '../../mol-gl/shader/background.vert';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createNullTexture, CubeFaces, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
|
||||
import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { Camera, ICamera } from '../camera';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra/3d/vec2';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Asset, AssetManager } from '../../mol-util/assets';
|
||||
import { Vec4 } from '../../mol-math/linear-algebra/3d/vec4';
|
||||
|
||||
const SharedParams = {
|
||||
opacity: PD.Numeric(1, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
saturation: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
|
||||
lightness: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
|
||||
};
|
||||
|
||||
const SkyboxParams = {
|
||||
faces: PD.MappedStatic('urls', {
|
||||
urls: PD.Group({
|
||||
nx: PD.Text('', { label: 'Negative X / Left' }),
|
||||
ny: PD.Text('', { label: 'Negative Y / Bottom' }),
|
||||
nz: PD.Text('', { label: 'Negative Z / Back' }),
|
||||
px: PD.Text('', { label: 'Positive X / Right' }),
|
||||
py: PD.Text('', { label: 'Positive Y / Top' }),
|
||||
pz: PD.Text('', { label: 'Positive Z / Front' }),
|
||||
}, { isExpanded: true, label: 'URLs' }),
|
||||
files: PD.Group({
|
||||
nx: PD.File({ label: 'Negative X / Left', accept: 'image/*' }),
|
||||
ny: PD.File({ label: 'Negative Y / Bottom', accept: 'image/*' }),
|
||||
nz: PD.File({ label: 'Negative Z / Back', accept: 'image/*' }),
|
||||
px: PD.File({ label: 'Positive X / Right', accept: 'image/*' }),
|
||||
py: PD.File({ label: 'Positive Y / Top', accept: 'image/*' }),
|
||||
pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }),
|
||||
}, { isExpanded: true, label: 'Files' }),
|
||||
}),
|
||||
...SharedParams,
|
||||
};
|
||||
type SkyboxProps = PD.Values<typeof SkyboxParams>
|
||||
|
||||
const ImageParams = {
|
||||
source: PD.MappedStatic('url', {
|
||||
url: PD.Text(''),
|
||||
file: PD.File({ accept: 'image/*' }),
|
||||
}),
|
||||
...SharedParams,
|
||||
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
|
||||
};
|
||||
type ImageProps = PD.Values<typeof ImageParams>
|
||||
|
||||
const HorizontalGradientParams = {
|
||||
topColor: PD.Color(Color(0xDDDDDD)),
|
||||
bottomColor: PD.Color(Color(0xEEEEEE)),
|
||||
ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
|
||||
};
|
||||
|
||||
const RadialGradientParams = {
|
||||
centerColor: PD.Color(Color(0xDDDDDD)),
|
||||
edgeColor: PD.Color(Color(0xEEEEEE)),
|
||||
ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
|
||||
};
|
||||
|
||||
export const BackgroundParams = {
|
||||
variant: PD.MappedStatic('off', {
|
||||
off: PD.EmptyGroup(),
|
||||
skybox: PD.Group(SkyboxParams, { isExpanded: true }),
|
||||
image: PD.Group(ImageParams, { isExpanded: true }),
|
||||
horizontalGradient: PD.Group(HorizontalGradientParams, { isExpanded: true }),
|
||||
radialGradient: PD.Group(RadialGradientParams, { isExpanded: true }),
|
||||
}, { label: 'Environment' }),
|
||||
};
|
||||
export type BackgroundProps = PD.Values<typeof BackgroundParams>
|
||||
|
||||
export class BackgroundPass {
|
||||
private renderable: BackgroundRenderable;
|
||||
|
||||
private skybox: {
|
||||
texture: Texture
|
||||
props: SkyboxProps
|
||||
assets: Asset[]
|
||||
loaded: boolean
|
||||
} | undefined;
|
||||
|
||||
private image: {
|
||||
texture: Texture
|
||||
props: ImageProps
|
||||
asset: Asset
|
||||
loaded: boolean
|
||||
} | undefined;
|
||||
|
||||
private readonly camera = new Camera();
|
||||
private readonly target = Vec3();
|
||||
private readonly position = Vec3();
|
||||
private readonly dir = Vec3();
|
||||
|
||||
readonly texture: Texture;
|
||||
|
||||
constructor(private readonly webgl: WebGLContext, private readonly assetManager: AssetManager, width: number, height: number) {
|
||||
this.renderable = getBackgroundRenderable(webgl, width, height);
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
const [w, h] = this.renderable.values.uTexSize.ref.value;
|
||||
|
||||
if (width !== w || height !== h) {
|
||||
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
|
||||
}
|
||||
}
|
||||
|
||||
private clearSkybox() {
|
||||
if (this.skybox !== undefined) {
|
||||
this.skybox.texture.destroy();
|
||||
this.skybox.assets.forEach(a => this.assetManager.release(a));
|
||||
this.skybox = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private updateSkybox(camera: ICamera, props: SkyboxProps, onload?: (changed: boolean) => void) {
|
||||
const tf = this.skybox?.props.faces;
|
||||
const f = props.faces.params;
|
||||
if (!f.nx || !f.ny || !f.nz || !f.px || !f.py || !f.pz) {
|
||||
this.clearSkybox();
|
||||
onload?.(false);
|
||||
return;
|
||||
}
|
||||
if (!this.skybox || !tf || !areSkyboxTexturePropsEqual(props.faces, this.skybox.props.faces)) {
|
||||
this.clearSkybox();
|
||||
const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, errored => {
|
||||
if (this.skybox) this.skybox.loaded = !errored;
|
||||
onload?.(true);
|
||||
});
|
||||
this.skybox = { texture, props: { ...props }, assets, loaded: false };
|
||||
ValueCell.update(this.renderable.values.tSkybox, texture);
|
||||
this.renderable.update();
|
||||
} else {
|
||||
onload?.(false);
|
||||
}
|
||||
if (!this.skybox) return;
|
||||
|
||||
let cam = camera;
|
||||
if (camera.state.mode === 'orthographic') {
|
||||
this.camera.setState({ ...camera.state, mode: 'perspective' });
|
||||
this.camera.update();
|
||||
cam = this.camera;
|
||||
}
|
||||
|
||||
const m = this.renderable.values.uViewDirectionProjectionInverse.ref.value;
|
||||
Vec3.sub(this.dir, cam.state.position, cam.state.target);
|
||||
Vec3.setMagnitude(this.dir, this.dir, 0.1);
|
||||
Vec3.copy(this.position, this.dir);
|
||||
Mat4.lookAt(m, this.position, this.target, cam.state.up);
|
||||
Mat4.mul(m, cam.projection, m);
|
||||
Mat4.invert(m, m);
|
||||
ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m);
|
||||
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
|
||||
ValueCell.updateIfChanged(this.renderable.values.dVariant, 'skybox');
|
||||
this.renderable.update();
|
||||
}
|
||||
|
||||
private clearImage() {
|
||||
if (this.image !== undefined) {
|
||||
this.image.texture.destroy();
|
||||
this.assetManager.release(this.image.asset);
|
||||
this.image = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private updateImage(props: ImageProps, onload?: (loaded: boolean) => void) {
|
||||
if (!props.source.params) {
|
||||
this.clearImage();
|
||||
onload?.(false);
|
||||
return;
|
||||
}
|
||||
if (!this.image || !this.image.props.source.params || !areImageTexturePropsEqual(props.source, this.image.props.source)) {
|
||||
this.clearImage();
|
||||
const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, errored => {
|
||||
if (this.image) this.image.loaded = !errored;
|
||||
onload?.(true);
|
||||
});
|
||||
this.image = { texture, props: { ...props }, asset, loaded: false };
|
||||
ValueCell.update(this.renderable.values.tImage, texture);
|
||||
this.renderable.update();
|
||||
} else {
|
||||
onload?.(false);
|
||||
}
|
||||
if (!this.image) return;
|
||||
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, props.coverage === 'viewport' ? true : false);
|
||||
ValueCell.updateIfChanged(this.renderable.values.dVariant, 'image');
|
||||
this.renderable.update();
|
||||
}
|
||||
|
||||
private updateImageScaling() {
|
||||
const v = this.renderable.values;
|
||||
const [w, h] = v.uTexSize.ref.value;
|
||||
const iw = this.image?.texture.getWidth() || 0;
|
||||
const ih = this.image?.texture.getHeight() || 0;
|
||||
const r = w / h;
|
||||
const ir = iw / ih;
|
||||
// responsive scaling with offset
|
||||
if (r < ir) {
|
||||
ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, iw * h / ih, h));
|
||||
} else {
|
||||
ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, w, ih * w / iw));
|
||||
}
|
||||
const [rw, rh] = v.uImageScale.ref.value;
|
||||
const sr = rw / rh;
|
||||
if (sr > r) {
|
||||
ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, (1 - r / sr) / 2, 0));
|
||||
} else {
|
||||
ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, 0, (1 - sr / r) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
private updateGradient(colorA: Color, colorB: Color, ratio: number, variant: 'horizontalGradient' | 'radialGradient', viewportAdjusted: boolean) {
|
||||
ValueCell.update(this.renderable.values.uGradientColorA, Color.toVec3Normalized(this.renderable.values.uGradientColorA.ref.value, colorA));
|
||||
ValueCell.update(this.renderable.values.uGradientColorB, Color.toVec3Normalized(this.renderable.values.uGradientColorB.ref.value, colorB));
|
||||
ValueCell.updateIfChanged(this.renderable.values.uGradientRatio, ratio);
|
||||
ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, viewportAdjusted);
|
||||
ValueCell.updateIfChanged(this.renderable.values.dVariant, variant);
|
||||
this.renderable.update();
|
||||
}
|
||||
|
||||
update(camera: ICamera, props: BackgroundProps, onload?: (changed: boolean) => void) {
|
||||
if (props.variant.name === 'off') {
|
||||
this.clearSkybox();
|
||||
this.clearImage();
|
||||
onload?.(false);
|
||||
return;
|
||||
} else if (props.variant.name === 'skybox') {
|
||||
this.clearImage();
|
||||
this.updateSkybox(camera, props.variant.params, onload);
|
||||
} else if (props.variant.name === 'image') {
|
||||
this.clearSkybox();
|
||||
this.updateImage(props.variant.params, onload);
|
||||
} else if (props.variant.name === 'horizontalGradient') {
|
||||
this.clearSkybox();
|
||||
this.clearImage();
|
||||
this.updateGradient(props.variant.params.topColor, props.variant.params.bottomColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
|
||||
onload?.(false);
|
||||
} else if (props.variant.name === 'radialGradient') {
|
||||
this.clearSkybox();
|
||||
this.clearImage();
|
||||
this.updateGradient(props.variant.params.centerColor, props.variant.params.edgeColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
|
||||
onload?.(false);
|
||||
}
|
||||
|
||||
const { x, y, width, height } = camera.viewport;
|
||||
ValueCell.update(this.renderable.values.uViewport, Vec4.set(this.renderable.values.uViewport.ref.value, x, y, width, height));
|
||||
}
|
||||
|
||||
isEnabled(props: BackgroundProps) {
|
||||
return !!(
|
||||
(this.skybox && this.skybox.loaded) ||
|
||||
(this.image && this.image.loaded) ||
|
||||
props.variant.name === 'horizontalGradient' ||
|
||||
props.variant.name === 'radialGradient'
|
||||
);
|
||||
}
|
||||
|
||||
private isReady() {
|
||||
return !!(
|
||||
(this.skybox && this.skybox.loaded) ||
|
||||
(this.image && this.image.loaded) ||
|
||||
this.renderable.values.dVariant.ref.value === 'horizontalGradient' ||
|
||||
this.renderable.values.dVariant.ref.value === 'radialGradient'
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isReady()) return;
|
||||
|
||||
if (this.renderable.values.dVariant.ref.value === 'image') {
|
||||
this.updateImageScaling();
|
||||
}
|
||||
|
||||
if (isTimingMode) this.webgl.timer.mark('BackgroundPass.render');
|
||||
this.renderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('BackgroundPass.render');
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.clearSkybox();
|
||||
this.clearImage();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const SkyboxName = 'background-skybox';
|
||||
|
||||
type CubeAssets = { [k in keyof CubeFaces]: Asset };
|
||||
|
||||
function getCubeAssets(assetManager: AssetManager, faces: SkyboxProps['faces']): CubeAssets {
|
||||
if (faces.name === 'urls') {
|
||||
return {
|
||||
nx: Asset.getUrlAsset(assetManager, faces.params.nx),
|
||||
ny: Asset.getUrlAsset(assetManager, faces.params.ny),
|
||||
nz: Asset.getUrlAsset(assetManager, faces.params.nz),
|
||||
px: Asset.getUrlAsset(assetManager, faces.params.px),
|
||||
py: Asset.getUrlAsset(assetManager, faces.params.py),
|
||||
pz: Asset.getUrlAsset(assetManager, faces.params.pz),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
nx: faces.params.nx!,
|
||||
ny: faces.params.ny!,
|
||||
nz: faces.params.nz!,
|
||||
px: faces.params.px!,
|
||||
py: faces.params.py!,
|
||||
pz: faces.params.pz!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getCubeFaces(assetManager: AssetManager, cubeAssets: CubeAssets): CubeFaces {
|
||||
const resolve = (asset: Asset) => {
|
||||
return assetManager.resolve(asset, 'binary').run().then(a => new Blob([a.data]));
|
||||
};
|
||||
|
||||
return {
|
||||
nx: resolve(cubeAssets.nx),
|
||||
ny: resolve(cubeAssets.ny),
|
||||
nz: resolve(cubeAssets.nz),
|
||||
px: resolve(cubeAssets.px),
|
||||
py: resolve(cubeAssets.py),
|
||||
pz: resolve(cubeAssets.pz),
|
||||
};
|
||||
}
|
||||
|
||||
function getSkyboxHash(faces: SkyboxProps['faces']) {
|
||||
if (faces.name === 'urls') {
|
||||
return `${SkyboxName}_${faces.params.nx}|${faces.params.ny}|${faces.params.nz}|${faces.params.px}|${faces.params.py}|${faces.params.pz}`;
|
||||
} else {
|
||||
return `${SkyboxName}_${faces.params.nx?.id}|${faces.params.ny?.id}|${faces.params.nz?.id}|${faces.params.px?.id}|${faces.params.py?.id}|${faces.params.pz?.id}`;
|
||||
}
|
||||
}
|
||||
|
||||
function areSkyboxTexturePropsEqual(facesA: SkyboxProps['faces'], facesB: SkyboxProps['faces']) {
|
||||
return getSkyboxHash(facesA) === getSkyboxHash(facesB);
|
||||
}
|
||||
|
||||
function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: (errored?: boolean) => void): { texture: Texture, assets: Asset[] } {
|
||||
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);
|
||||
return { texture, assets };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const ImageName = 'background-image';
|
||||
|
||||
function getImageHash(source: ImageProps['source']) {
|
||||
if (source.name === 'url') {
|
||||
return `${ImageName}_${source.params}`;
|
||||
} else {
|
||||
return `${ImageName}_${source.params?.id}`;
|
||||
}
|
||||
}
|
||||
|
||||
function areImageTexturePropsEqual(sourceA: ImageProps['source'], sourceB: ImageProps['source']) {
|
||||
return getImageHash(sourceA) === getImageHash(sourceB);
|
||||
}
|
||||
|
||||
function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: (errored?: boolean) => void): { texture: Texture, asset: Asset } {
|
||||
const texture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
texture.load(img);
|
||||
onload?.();
|
||||
};
|
||||
img.onerror = () => {
|
||||
onload?.(true);
|
||||
};
|
||||
const asset = source.name === 'url'
|
||||
? Asset.getUrlAsset(assetManager, source.params)
|
||||
: source.params!;
|
||||
assetManager.resolve(asset, 'binary').run().then(a => {
|
||||
const blob = new Blob([a.data]);
|
||||
img.src = URL.createObjectURL(blob);
|
||||
});
|
||||
return { texture, asset };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const BackgroundSchema = {
|
||||
drawCount: ValueSpec('number'),
|
||||
instanceCount: ValueSpec('number'),
|
||||
aPosition: AttributeSpec('float32', 2, 0),
|
||||
tSkybox: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
|
||||
tImage: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
|
||||
uImageScale: UniformSpec('v2'),
|
||||
uImageOffset: UniformSpec('v2'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
uViewport: UniformSpec('v4'),
|
||||
uViewportAdjusted: UniformSpec('b'),
|
||||
uViewDirectionProjectionInverse: UniformSpec('m4'),
|
||||
uGradientColorA: UniformSpec('v3'),
|
||||
uGradientColorB: UniformSpec('v3'),
|
||||
uGradientRatio: 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);
|
||||
type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>>
|
||||
|
||||
function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable {
|
||||
const values: Values<typeof BackgroundSchema> = {
|
||||
drawCount: ValueCell.create(6),
|
||||
instanceCount: ValueCell.create(1),
|
||||
aPosition: ValueCell.create(QuadPositions),
|
||||
tSkybox: ValueCell.create(createNullTexture()),
|
||||
tImage: ValueCell.create(createNullTexture()),
|
||||
uImageScale: ValueCell.create(Vec2()),
|
||||
uImageOffset: ValueCell.create(Vec2()),
|
||||
uTexSize: ValueCell.create(Vec2.create(width, height)),
|
||||
uViewport: ValueCell.create(Vec4()),
|
||||
uViewportAdjusted: ValueCell.create(true),
|
||||
uViewDirectionProjectionInverse: ValueCell.create(Mat4()),
|
||||
uGradientColorA: ValueCell.create(Vec3()),
|
||||
uGradientColorB: ValueCell.create(Vec3()),
|
||||
uGradientRatio: ValueCell.create(0.5),
|
||||
uOpacity: ValueCell.create(1),
|
||||
uSaturation: ValueCell.create(0),
|
||||
uLightness: ValueCell.create(0),
|
||||
dVariant: ValueCell.create('skybox'),
|
||||
};
|
||||
|
||||
const schema = { ...BackgroundSchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', SkyboxShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
@@ -11,51 +11,21 @@ import { Renderer } from '../../mol-gl/renderer';
|
||||
import { Scene } from '../../mol-gl/scene';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera, ICamera } from '../camera';
|
||||
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
|
||||
import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
|
||||
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { Helper } from '../helper/helper';
|
||||
|
||||
import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
import { depthMerge_frag } from '../../mol-gl/shader/depth-merge.frag';
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
import { WboitPass } from './wboit';
|
||||
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
|
||||
import { MarkingPass, MarkingProps } from './marking';
|
||||
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
|
||||
|
||||
const DepthMergeSchema = {
|
||||
...QuadSchema,
|
||||
tDepthPrimitives: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
|
||||
tDepthVolumes: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
dPackedDepth: DefineSpec('boolean'),
|
||||
};
|
||||
const DepthMergeShaderCode = ShaderCode('depth-merge', quad_vert, depthMerge_frag);
|
||||
type DepthMergeRenderable = ComputeRenderable<Values<typeof DepthMergeSchema>>
|
||||
|
||||
function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Texture, depthTextureVolumes: Texture, packedDepth: boolean): DepthMergeRenderable {
|
||||
const values: Values<typeof DepthMergeSchema> = {
|
||||
...QuadValues,
|
||||
tDepthPrimitives: ValueCell.create(depthTexturePrimitives),
|
||||
tDepthVolumes: ValueCell.create(depthTextureVolumes),
|
||||
uTexSize: ValueCell.create(Vec2.create(depthTexturePrimitives.getWidth(), depthTexturePrimitives.getHeight())),
|
||||
dPackedDepth: ValueCell.create(packedDepth),
|
||||
};
|
||||
|
||||
const schema = { ...DepthMergeSchema };
|
||||
const renderItem = createComputeRenderItem(ctx, 'triangles', DepthMergeShaderCode, schema, values);
|
||||
|
||||
return createComputeRenderable(renderItem, values);
|
||||
}
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { AssetManager } from '../../mol-util/assets';
|
||||
|
||||
type Props = {
|
||||
postprocessing: PostprocessingProps
|
||||
marking: MarkingProps
|
||||
postprocessing: PostprocessingProps;
|
||||
marking: MarkingProps;
|
||||
transparentBackground: boolean;
|
||||
}
|
||||
|
||||
@@ -70,21 +40,18 @@ export class DrawPass {
|
||||
private readonly drawTarget: RenderTarget;
|
||||
|
||||
readonly colorTarget: RenderTarget;
|
||||
readonly depthTexture: Texture;
|
||||
readonly depthTexturePrimitives: Texture;
|
||||
readonly depthTextureTransparent: Texture;
|
||||
readonly depthTextureOpaque: Texture;
|
||||
|
||||
readonly packedDepth: boolean;
|
||||
|
||||
private depthTarget: RenderTarget;
|
||||
private depthTargetPrimitives: RenderTarget | null;
|
||||
private depthTargetVolumes: RenderTarget | null;
|
||||
private depthTextureVolumes: Texture;
|
||||
private depthMerge: DepthMergeRenderable;
|
||||
private depthTargetTransparent: RenderTarget;
|
||||
private depthTargetOpaque: RenderTarget | null;
|
||||
|
||||
private copyFboTarget: CopyRenderable;
|
||||
private copyFboPostprocessing: CopyRenderable;
|
||||
|
||||
private wboit: WboitPass | undefined;
|
||||
private readonly wboit: WboitPass | undefined;
|
||||
private readonly marking: MarkingPass;
|
||||
readonly postprocessing: PostprocessingPass;
|
||||
private readonly antialiasing: AntialiasingPass;
|
||||
@@ -93,31 +60,26 @@ export class DrawPass {
|
||||
return !!this.wboit?.supported;
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, width: number, height: number, enableWboit: boolean) {
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, width: number, height: number, enableWboit: boolean) {
|
||||
const { extensions, resources, isWebGL2 } = webgl;
|
||||
|
||||
this.drawTarget = createNullRenderTarget(webgl.gl);
|
||||
|
||||
this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
|
||||
this.packedDepth = !extensions.depthTexture;
|
||||
|
||||
this.depthTarget = webgl.createRenderTarget(width, height);
|
||||
this.depthTexture = this.depthTarget.texture;
|
||||
this.depthTargetTransparent = webgl.createRenderTarget(width, height);
|
||||
this.depthTextureTransparent = this.depthTargetTransparent.texture;
|
||||
|
||||
this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
|
||||
this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
|
||||
this.depthTargetOpaque = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
|
||||
|
||||
this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
|
||||
this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
|
||||
this.depthTextureOpaque = this.depthTargetOpaque ? this.depthTargetOpaque.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexturePrimitives.define(width, height);
|
||||
this.depthTextureVolumes.define(width, height);
|
||||
this.depthTextureOpaque.define(width, height);
|
||||
}
|
||||
this.depthMerge = getDepthMergeRenderable(webgl, this.depthTexturePrimitives, this.depthTextureVolumes, this.packedDepth);
|
||||
|
||||
this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
|
||||
this.marking = new MarkingPass(webgl, width, height);
|
||||
this.postprocessing = new PostprocessingPass(webgl, this);
|
||||
this.postprocessing = new PostprocessingPass(webgl, assetManager, this);
|
||||
this.antialiasing = new AntialiasingPass(webgl, this);
|
||||
|
||||
this.copyFboTarget = createCopyRenderable(webgl, this.colorTarget.texture);
|
||||
@@ -134,22 +96,14 @@ export class DrawPass {
|
||||
|
||||
if (width !== w || height !== h) {
|
||||
this.colorTarget.setSize(width, height);
|
||||
this.depthTarget.setSize(width, height);
|
||||
this.depthTargetTransparent.setSize(width, height);
|
||||
|
||||
if (this.depthTargetPrimitives) {
|
||||
this.depthTargetPrimitives.setSize(width, height);
|
||||
if (this.depthTargetOpaque) {
|
||||
this.depthTargetOpaque.setSize(width, height);
|
||||
} else {
|
||||
this.depthTexturePrimitives.define(width, height);
|
||||
this.depthTextureOpaque.define(width, height);
|
||||
}
|
||||
|
||||
if (this.depthTargetVolumes) {
|
||||
this.depthTargetVolumes.setSize(width, height);
|
||||
} else {
|
||||
this.depthTextureVolumes.define(width, height);
|
||||
}
|
||||
|
||||
ValueCell.update(this.depthMerge.values.uTexSize, Vec2.set(this.depthMerge.values.uTexSize.ref.value, width, height));
|
||||
|
||||
ValueCell.update(this.copyFboTarget.values.uTexSize, Vec2.set(this.copyFboTarget.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.copyFboPostprocessing.values.uTexSize, Vec2.set(this.copyFboPostprocessing.values.uTexSize.ref.value, width, height));
|
||||
|
||||
@@ -163,121 +117,127 @@ export class DrawPass {
|
||||
}
|
||||
}
|
||||
|
||||
private _depthMerge() {
|
||||
const { state, gl } = this.webgl;
|
||||
|
||||
this.depthMerge.update();
|
||||
this.depthTarget.bind();
|
||||
state.disable(gl.BLEND);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.disable(gl.CULL_FACE);
|
||||
state.depthMask(false);
|
||||
state.clearColor(1, 1, 1, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
this.depthMerge.render();
|
||||
}
|
||||
|
||||
private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
|
||||
if (!this.wboit?.supported) throw new Error('expected wboit to be supported');
|
||||
|
||||
this.colorTarget.bind();
|
||||
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
renderer.clear(true);
|
||||
|
||||
// render opaque primitives
|
||||
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
this.colorTarget.bind();
|
||||
renderer.clearDepth();
|
||||
renderer.renderWboitOpaque(scene.primitives, camera, null);
|
||||
|
||||
// render opaque volumes
|
||||
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
this.colorTarget.bind();
|
||||
renderer.clearDepth();
|
||||
renderer.renderWboitOpaque(scene.volumes, camera, this.depthTexturePrimitives);
|
||||
|
||||
// merge depth of opaque primitives and volumes
|
||||
this._depthMerge();
|
||||
if (scene.hasOpaque) {
|
||||
renderer.renderWboitOpaque(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);
|
||||
}
|
||||
|
||||
// render transparent primitives and volumes
|
||||
this.wboit.bind();
|
||||
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexture);
|
||||
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexture);
|
||||
if (scene.opacityAverage < 1 || scene.volumes.renderables.length > 0) {
|
||||
this.wboit.bind();
|
||||
if (scene.opacityAverage < 1) {
|
||||
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTextureOpaque);
|
||||
}
|
||||
if (scene.volumes.renderables.length > 0) {
|
||||
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTextureOpaque);
|
||||
}
|
||||
|
||||
// evaluate wboit
|
||||
if (PostprocessingPass.isEnabled(postprocessingProps)) {
|
||||
this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
|
||||
this.postprocessing.target.bind();
|
||||
} else {
|
||||
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
this.colorTarget.bind();
|
||||
// evaluate wboit
|
||||
if (PostprocessingPass.isEnabled(postprocessingProps)) {
|
||||
this.postprocessing.target.bind();
|
||||
} else {
|
||||
this.colorTarget.bind();
|
||||
}
|
||||
this.wboit.render();
|
||||
}
|
||||
this.wboit.render();
|
||||
}
|
||||
|
||||
private _renderBlended(renderer: Renderer, camera: ICamera, scene: Scene, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
|
||||
if (toDrawingBuffer) {
|
||||
this.drawTarget.bind();
|
||||
} else {
|
||||
this.colorTarget.bind();
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
} else {
|
||||
this.colorTarget.bind();
|
||||
}
|
||||
}
|
||||
|
||||
renderer.clear(true);
|
||||
renderer.renderBlendedOpaque(scene.primitives, camera, null);
|
||||
if (scene.hasOpaque) {
|
||||
renderer.renderBlendedOpaque(scene.primitives, camera, null);
|
||||
}
|
||||
|
||||
if (!toDrawingBuffer) {
|
||||
// do a depth pass if not rendering to drawing buffer and
|
||||
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (this.depthTargetPrimitives) {
|
||||
this.depthTargetPrimitives.bind();
|
||||
renderer.clear(false);
|
||||
// TODO: this should only render opaque
|
||||
renderer.renderDepth(scene.primitives, camera, null);
|
||||
if (this.depthTargetOpaque) {
|
||||
this.depthTargetOpaque.bind();
|
||||
renderer.clearDepth(true);
|
||||
renderer.renderDepthOpaque(scene.primitives, camera, null);
|
||||
this.colorTarget.bind();
|
||||
}
|
||||
|
||||
// do direct-volume rendering
|
||||
if (!this.packedDepth) {
|
||||
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
|
||||
renderer.clearDepth(); // from previous frame
|
||||
}
|
||||
renderer.renderBlendedVolumeOpaque(scene.volumes, camera, this.depthTexturePrimitives);
|
||||
|
||||
// do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (this.depthTargetVolumes) {
|
||||
this.depthTargetVolumes.bind();
|
||||
renderer.clear(false);
|
||||
renderer.renderDepth(scene.volumes, camera, this.depthTexturePrimitives);
|
||||
this.colorTarget.bind();
|
||||
}
|
||||
|
||||
// merge depths from primitive and volume rendering
|
||||
this._depthMerge();
|
||||
this.colorTarget.bind();
|
||||
|
||||
if (PostprocessingPass.isEnabled(postprocessingProps)) {
|
||||
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
|
||||
}
|
||||
renderer.renderBlendedVolumeTransparent(scene.volumes, camera, this.depthTexturePrimitives);
|
||||
if (!this.packedDepth) {
|
||||
this.depthTextureOpaque.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
|
||||
} else {
|
||||
this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer);
|
||||
}
|
||||
|
||||
const target = PostprocessingPass.isEnabled(postprocessingProps)
|
||||
? this.postprocessing.target : this.colorTarget;
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexturePrimitives.attachFramebuffer(target.framebuffer, 'depth');
|
||||
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);
|
||||
|
||||
if (!this.packedDepth) {
|
||||
this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
|
||||
} else {
|
||||
this.colorTarget.depthRenderbuffer?.attachFramebuffer(this.postprocessing.target.framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (scene.volumes.renderables.length > 0) {
|
||||
const target = PostprocessingPass.isEnabled(postprocessingProps)
|
||||
? this.postprocessing.target : this.colorTarget;
|
||||
|
||||
if (!this.packedDepth) {
|
||||
this.depthTextureOpaque.detachFramebuffer(target.framebuffer, 'depth');
|
||||
} else {
|
||||
this.colorTarget.depthRenderbuffer?.detachFramebuffer(target.framebuffer);
|
||||
}
|
||||
target.bind();
|
||||
|
||||
renderer.renderBlendedVolume(scene.volumes, camera, this.depthTextureOpaque);
|
||||
|
||||
if (!this.packedDepth) {
|
||||
this.depthTextureOpaque.attachFramebuffer(target.framebuffer, 'depth');
|
||||
} else {
|
||||
this.colorTarget.depthRenderbuffer?.attachFramebuffer(target.framebuffer);
|
||||
}
|
||||
target.bind();
|
||||
}
|
||||
target.bind();
|
||||
}
|
||||
|
||||
renderer.renderBlendedTransparent(scene.primitives, camera, null);
|
||||
if (scene.opacityAverage < 1) {
|
||||
renderer.renderBlendedTransparent(scene.primitives, camera, null);
|
||||
}
|
||||
}
|
||||
|
||||
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, props: Props) {
|
||||
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
|
||||
const volumeRendering = scene.volumes.renderables.length > 0;
|
||||
const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing);
|
||||
const antialiasingEnabled = AntialiasingPass.isEnabled(props.postprocessing);
|
||||
@@ -287,28 +247,26 @@ export class DrawPass {
|
||||
renderer.setViewport(x, y, width, height);
|
||||
renderer.update(camera);
|
||||
|
||||
if (props.transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
|
||||
if (transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
|
||||
this.drawTarget.bind();
|
||||
renderer.clear(false);
|
||||
}
|
||||
|
||||
if (this.wboitEnabled) {
|
||||
this._renderWboit(renderer, camera, scene, props.transparentBackground, props.postprocessing);
|
||||
this._renderWboit(renderer, camera, scene, transparentBackground, props.postprocessing);
|
||||
} else {
|
||||
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, props.transparentBackground, props.postprocessing);
|
||||
this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, transparentBackground, props.postprocessing);
|
||||
}
|
||||
|
||||
if (postprocessingEnabled) {
|
||||
this.postprocessing.target.bind();
|
||||
} else if (!toDrawingBuffer || volumeRendering || this.wboitEnabled) {
|
||||
this.colorTarget.bind();
|
||||
} else {
|
||||
this.drawTarget.bind();
|
||||
}
|
||||
const target = postprocessingEnabled
|
||||
? this.postprocessing.target
|
||||
: !toDrawingBuffer || volumeRendering || this.wboitEnabled
|
||||
? this.colorTarget
|
||||
: this.drawTarget;
|
||||
|
||||
if (markingEnabled) {
|
||||
if (markingEnabled && scene.markerAverage > 0) {
|
||||
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
|
||||
if (markingDepthTest) {
|
||||
if (markingDepthTest && scene.markerAverage !== 1) {
|
||||
this.marking.depthTarget.bind();
|
||||
renderer.clear(false, true);
|
||||
renderer.renderMarkingDepth(scene.primitives, camera, null);
|
||||
@@ -319,20 +277,22 @@ export class DrawPass {
|
||||
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
|
||||
|
||||
this.marking.update(props.marking);
|
||||
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
|
||||
this.marking.render(camera.viewport, target);
|
||||
} else {
|
||||
target.bind();
|
||||
}
|
||||
|
||||
if (helper.debug.isEnabled) {
|
||||
helper.debug.syncVisibility();
|
||||
renderer.renderBlended(helper.debug.scene, camera, null);
|
||||
renderer.renderBlended(helper.debug.scene, camera);
|
||||
}
|
||||
if (helper.handle.isEnabled) {
|
||||
renderer.renderBlended(helper.handle.scene, camera, null);
|
||||
renderer.renderBlended(helper.handle.scene, camera);
|
||||
}
|
||||
if (helper.camera.isEnabled) {
|
||||
helper.camera.update(camera);
|
||||
renderer.update(helper.camera.camera);
|
||||
renderer.renderBlended(helper.camera.scene, helper.camera.camera, null);
|
||||
renderer.renderBlended(helper.camera.scene, helper.camera.camera);
|
||||
}
|
||||
|
||||
if (antialiasingEnabled) {
|
||||
@@ -352,17 +312,23 @@ export class DrawPass {
|
||||
}
|
||||
|
||||
render(ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
|
||||
if (isTimingMode) this.webgl.timer.mark('DrawPass.render');
|
||||
const { renderer, camera, scene, helper } = ctx;
|
||||
renderer.setTransparentBackground(props.transparentBackground);
|
||||
|
||||
this.postprocessing.setTransparentBackground(props.transparentBackground);
|
||||
const transparentBackground = props.transparentBackground || this.postprocessing.background.isEnabled(props.postprocessing.background);
|
||||
|
||||
renderer.setTransparentBackground(transparentBackground);
|
||||
renderer.setDrawingBufferSize(this.colorTarget.getWidth(), this.colorTarget.getHeight());
|
||||
renderer.setPixelRatio(this.webgl.pixelRatio);
|
||||
|
||||
if (StereoCamera.is(camera)) {
|
||||
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, props);
|
||||
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, props);
|
||||
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
} else {
|
||||
this._render(renderer, camera, scene, helper, toDrawingBuffer, props);
|
||||
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
|
||||
}
|
||||
if (isTimingMode) this.webgl.timer.markEnd('DrawPass.render');
|
||||
}
|
||||
|
||||
getColorTarget(postprocessingProps: PostprocessingProps): RenderTarget {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
import { fxaa_frag } from '../../mol-gl/shader/fxaa.frag';
|
||||
import { Viewport } from '../camera/util';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
|
||||
export const FxaaParams = {
|
||||
edgeThresholdMin: PD.Numeric(0.0312, { min: 0.0312, max: 0.0833, step: 0.0001 }, { description: 'Trims the algorithm from processing darks.' }),
|
||||
@@ -43,8 +44,8 @@ export class FxaaPass {
|
||||
state.depthMask(false);
|
||||
|
||||
const { x, y, width, height } = viewport;
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
state.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -83,6 +84,7 @@ export class FxaaPass {
|
||||
}
|
||||
|
||||
render(viewport: Viewport, target: RenderTarget | undefined) {
|
||||
if (isTimingMode) this.webgl.timer.mark('FxaaPass.render');
|
||||
if (target) {
|
||||
target.bind();
|
||||
} else {
|
||||
@@ -90,6 +92,7 @@ export class FxaaPass {
|
||||
}
|
||||
this.updateState(viewport);
|
||||
this.renderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('FxaaPass.render');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -18,6 +18,7 @@ import { PixelData } from '../../mol-util/image';
|
||||
import { Helper } from '../helper/helper';
|
||||
import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
|
||||
import { MarkingParams } from './marking';
|
||||
import { AssetManager } from '../../mol-util/assets';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
@@ -47,10 +48,10 @@ export class ImagePass {
|
||||
get width() { return this._width; }
|
||||
get height() { return this._height; }
|
||||
|
||||
constructor(private webgl: WebGLContext, 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, props: Partial<ImageProps>) {
|
||||
this.props = { ...PD.getDefaultValues(ImageParams), ...props };
|
||||
|
||||
this.drawPass = new DrawPass(webgl, 128, 128, enableWboit);
|
||||
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, enableWboit);
|
||||
this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
|
||||
this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
|
||||
|
||||
@@ -63,6 +64,14 @@ export class ImagePass {
|
||||
this.setSize(1024, 768);
|
||||
}
|
||||
|
||||
updateBackground() {
|
||||
return new Promise<void>(resolve => {
|
||||
this.drawPass.postprocessing.background.update(this.camera, this.props.postprocessing.background, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
if (width === this._width && height === this._height) return;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Viewport } from '../camera/util';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { edge_frag } from '../../mol-gl/shader/marking/edge.frag';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
|
||||
export const MarkingParams = {
|
||||
enabled: PD.Boolean(true),
|
||||
@@ -63,8 +64,8 @@ export class MarkingPass {
|
||||
state.depthMask(false);
|
||||
|
||||
const { x, y, width, height } = viewport;
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -81,8 +82,8 @@ export class MarkingPass {
|
||||
state.depthMask(false);
|
||||
|
||||
const { x, y, width, height } = viewport;
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
@@ -117,6 +118,7 @@ export class MarkingPass {
|
||||
}
|
||||
|
||||
render(viewport: Viewport, target: RenderTarget | undefined) {
|
||||
if (isTimingMode) this.webgl.timer.mark('MarkingPass.render');
|
||||
this.edgesTarget.bind();
|
||||
this.setEdgeState(viewport);
|
||||
this.edge.render();
|
||||
@@ -128,6 +130,7 @@ export class MarkingPass {
|
||||
}
|
||||
this.setOverlayState(viewport);
|
||||
this.overlay.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('MarkingPass.render');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -25,6 +25,7 @@ import { StereoCamera } from '../camera/stereo';
|
||||
import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
import { compose_frag } from '../../mol-gl/shader/compose.frag';
|
||||
import { MarkingProps } from './marking';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
|
||||
const ComposeSchema = {
|
||||
...QuadSchema,
|
||||
@@ -126,6 +127,7 @@ export class MultiSamplePass {
|
||||
const { camera } = ctx;
|
||||
const { compose, composeTarget, drawPass, webgl } = this;
|
||||
const { gl, state } = webgl;
|
||||
if (isTimingMode) webgl.timer.mark('MultiSamplePass.renderMultiSample');
|
||||
|
||||
// based on the Multisample Anti-Aliasing Render Pass
|
||||
// contributed to three.js by bhouston / http://clara.io/
|
||||
@@ -157,6 +159,14 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight);
|
||||
|
||||
// render scene
|
||||
if (i === 0) {
|
||||
drawPass.postprocessing.setOcclusionOffset(0, 0);
|
||||
} else {
|
||||
drawPass.postprocessing.setOcclusionOffset(
|
||||
offset[0] / width,
|
||||
offset[1] / height
|
||||
);
|
||||
}
|
||||
drawPass.render(ctx, props, false);
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -166,8 +176,8 @@ export class MultiSamplePass {
|
||||
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.depthMask(false);
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
if (i === 0) {
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -175,25 +185,29 @@ export class MultiSamplePass {
|
||||
compose.render();
|
||||
}
|
||||
|
||||
drawPass.postprocessing.setOcclusionOffset(0, 0);
|
||||
|
||||
ValueCell.update(compose.values.uWeight, 1.0);
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture);
|
||||
compose.update();
|
||||
|
||||
this.bindOutputTarget(toDrawingBuffer);
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
state.disable(gl.BLEND);
|
||||
compose.render();
|
||||
|
||||
camera.viewOffset.enabled = false;
|
||||
camera.update();
|
||||
if (isTimingMode) webgl.timer.markEnd('MultiSamplePass.renderMultiSample');
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample(sampleIndex: number, ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
|
||||
const { camera } = ctx;
|
||||
const { compose, composeTarget, holdTarget, drawPass, webgl } = this;
|
||||
const { gl, state } = webgl;
|
||||
if (isTimingMode) webgl.timer.mark('MultiSamplePass.renderTemporalMultiSample');
|
||||
|
||||
// based on the Multisample Anti-Aliasing Render Pass
|
||||
// contributed to three.js by bhouston / http://clara.io/
|
||||
@@ -217,8 +231,8 @@ export class MultiSamplePass {
|
||||
state.disable(gl.BLEND);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.depthMask(false);
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
compose.render();
|
||||
sampleIndex += 1;
|
||||
} else {
|
||||
@@ -236,6 +250,14 @@ export class MultiSamplePass {
|
||||
camera.update();
|
||||
|
||||
// render scene
|
||||
if (sampleIndex === 0) {
|
||||
drawPass.postprocessing.setOcclusionOffset(0, 0);
|
||||
} else {
|
||||
drawPass.postprocessing.setOcclusionOffset(
|
||||
offset[0] / width,
|
||||
offset[1] / height
|
||||
);
|
||||
}
|
||||
drawPass.render(ctx, props, false);
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -245,8 +267,8 @@ export class MultiSamplePass {
|
||||
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
state.depthMask(false);
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
if (sampleIndex === 0) {
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -258,9 +280,11 @@ export class MultiSamplePass {
|
||||
}
|
||||
}
|
||||
|
||||
drawPass.postprocessing.setOcclusionOffset(0, 0);
|
||||
|
||||
this.bindOutputTarget(toDrawingBuffer);
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
const accumulationWeight = sampleIndex * sampleWeight;
|
||||
if (accumulationWeight > 0) {
|
||||
@@ -281,6 +305,7 @@ export class MultiSamplePass {
|
||||
|
||||
camera.viewOffset.enabled = false;
|
||||
camera.update();
|
||||
if (isTimingMode) webgl.timer.markEnd('MultiSamplePass.renderTemporalMultiSample');
|
||||
|
||||
return sampleIndex >= offsetList.length ? -2 : sampleIndex;
|
||||
}
|
||||
@@ -291,23 +316,23 @@ const JitterVectors = [
|
||||
[0, 0]
|
||||
],
|
||||
[
|
||||
[4, 4], [-4, -4]
|
||||
[0, 0], [-4, -4]
|
||||
],
|
||||
[
|
||||
[-2, -6], [6, -2], [-6, 2], [2, 6]
|
||||
[0, 0], [6, -2], [-6, 2], [2, 6]
|
||||
],
|
||||
[
|
||||
[1, -3], [-1, 3], [5, 1], [-3, -5],
|
||||
[0, 0], [-1, 3], [5, 1], [-3, -5],
|
||||
[-5, 5], [-7, -1], [3, 7], [7, -7]
|
||||
],
|
||||
[
|
||||
[1, 1], [-1, -3], [-3, 2], [4, -1],
|
||||
[0, 0], [-1, -3], [-3, 2], [4, -1],
|
||||
[-5, -2], [2, 5], [5, 3], [3, -5],
|
||||
[-2, 6], [0, -7], [-4, -6], [-6, 4],
|
||||
[-8, 0], [7, -4], [6, 7], [-7, -8]
|
||||
],
|
||||
[
|
||||
[-4, -7], [-7, -5], [-3, -5], [-5, -4],
|
||||
[0, 0], [-7, -5], [-3, -5], [-5, -4],
|
||||
[-1, -4], [-2, -2], [-6, -1], [-4, 0],
|
||||
[-7, 1], [-1, 2], [-6, 3], [-3, 3],
|
||||
[-7, 6], [-3, 6], [-5, 7], [-1, 7],
|
||||
|
||||
@@ -8,15 +8,16 @@ import { DrawPass } from './draw';
|
||||
import { PickPass } from './pick';
|
||||
import { MultiSamplePass } from './multi-sample';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { AssetManager } from '../../mol-util/assets';
|
||||
|
||||
export class Passes {
|
||||
readonly draw: DrawPass;
|
||||
readonly pick: PickPass;
|
||||
readonly multiSample: MultiSamplePass;
|
||||
|
||||
constructor(private webgl: WebGLContext, attribs: Partial<{ pickScale: number, enableWboit: boolean }> = {}) {
|
||||
constructor(private webgl: WebGLContext, assetManager: AssetManager, attribs: Partial<{ pickScale: number, enableWboit: boolean }> = {}) {
|
||||
const { gl } = webgl;
|
||||
this.draw = new DrawPass(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false);
|
||||
this.draw = new DrawPass(webgl, assetManager, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false);
|
||||
this.pick = new PickPass(webgl, this.draw, attribs.pickScale || 0.25);
|
||||
this.multiSample = new MultiSamplePass(webgl, this.draw);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -7,11 +7,15 @@
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { PickType, Renderer } from '../../mol-gl/renderer';
|
||||
import { Scene } from '../../mol-gl/scene';
|
||||
import { isWebGL2 } from '../../mol-gl/webgl/compat';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
|
||||
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { spiral2d } from '../../mol-math/misc';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { unpackRGBToInt, unpackRGBAToDepth } from '../../mol-util/number-packing';
|
||||
import { Camera, ICamera } from '../camera';
|
||||
import { StereoCamera } from '../camera/stereo';
|
||||
@@ -25,10 +29,24 @@ const NullId = Math.pow(2, 24) - 2;
|
||||
export type PickData = { id: PickingId, position: Vec3 }
|
||||
|
||||
export class PickPass {
|
||||
readonly objectPickTarget: RenderTarget;
|
||||
readonly instancePickTarget: RenderTarget;
|
||||
readonly groupPickTarget: RenderTarget;
|
||||
readonly depthPickTarget: RenderTarget;
|
||||
private readonly objectPickTarget: RenderTarget;
|
||||
private readonly instancePickTarget: RenderTarget;
|
||||
private readonly groupPickTarget: RenderTarget;
|
||||
private readonly depthPickTarget: RenderTarget;
|
||||
|
||||
private readonly framebuffer: Framebuffer;
|
||||
|
||||
private readonly objectPickTexture: Texture;
|
||||
private readonly instancePickTexture: Texture;
|
||||
private readonly groupPickTexture: Texture;
|
||||
private readonly depthPickTexture: Texture;
|
||||
|
||||
private readonly objectPickFramebuffer: Framebuffer;
|
||||
private readonly instancePickFramebuffer: Framebuffer;
|
||||
private readonly groupPickFramebuffer: Framebuffer;
|
||||
private readonly depthPickFramebuffer: Framebuffer;
|
||||
|
||||
private readonly depthRenderbuffer: Renderbuffer;
|
||||
|
||||
private pickWidth: number;
|
||||
private pickHeight: number;
|
||||
@@ -38,10 +56,89 @@ export class PickPass {
|
||||
this.pickWidth = Math.ceil(drawPass.colorTarget.getWidth() * pickScale);
|
||||
this.pickHeight = Math.ceil(drawPass.colorTarget.getHeight() * pickScale);
|
||||
|
||||
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
const { resources, extensions: { drawBuffers }, gl } = webgl;
|
||||
|
||||
if (drawBuffers) {
|
||||
this.objectPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.objectPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
|
||||
this.instancePickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.instancePickTexture.define(this.pickWidth, this.pickHeight);
|
||||
|
||||
this.groupPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.groupPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
|
||||
this.depthPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.depthPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
|
||||
this.framebuffer = resources.framebuffer();
|
||||
|
||||
this.objectPickFramebuffer = resources.framebuffer();
|
||||
this.instancePickFramebuffer = resources.framebuffer();
|
||||
this.groupPickFramebuffer = resources.framebuffer();
|
||||
this.depthPickFramebuffer = resources.framebuffer();
|
||||
|
||||
this.framebuffer.bind();
|
||||
drawBuffers!.drawBuffers([
|
||||
drawBuffers!.COLOR_ATTACHMENT0,
|
||||
drawBuffers!.COLOR_ATTACHMENT1,
|
||||
drawBuffers!.COLOR_ATTACHMENT2,
|
||||
drawBuffers!.COLOR_ATTACHMENT3,
|
||||
]);
|
||||
|
||||
this.objectPickTexture.attachFramebuffer(this.framebuffer, 'color0');
|
||||
this.instancePickTexture.attachFramebuffer(this.framebuffer, 'color1');
|
||||
this.groupPickTexture.attachFramebuffer(this.framebuffer, 'color2');
|
||||
this.depthPickTexture.attachFramebuffer(this.framebuffer, 'color3');
|
||||
|
||||
this.depthRenderbuffer = isWebGL2(gl)
|
||||
? resources.renderbuffer('depth32f', 'depth', this.pickWidth, this.pickHeight)
|
||||
: resources.renderbuffer('depth16', 'depth', this.pickWidth, this.pickHeight);
|
||||
|
||||
this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
|
||||
|
||||
this.objectPickTexture.attachFramebuffer(this.objectPickFramebuffer, 'color0');
|
||||
this.instancePickTexture.attachFramebuffer(this.instancePickFramebuffer, 'color0');
|
||||
this.groupPickTexture.attachFramebuffer(this.groupPickFramebuffer, 'color0');
|
||||
this.depthPickTexture.attachFramebuffer(this.depthPickFramebuffer, 'color0');
|
||||
} else {
|
||||
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
|
||||
}
|
||||
}
|
||||
|
||||
bindObject() {
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.objectPickFramebuffer.bind();
|
||||
} else {
|
||||
this.objectPickTarget.bind();
|
||||
}
|
||||
}
|
||||
|
||||
bindInstance() {
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.instancePickFramebuffer.bind();
|
||||
} else {
|
||||
this.instancePickTarget.bind();
|
||||
}
|
||||
}
|
||||
|
||||
bindGroup() {
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.groupPickFramebuffer.bind();
|
||||
} else {
|
||||
this.groupPickTarget.bind();
|
||||
}
|
||||
}
|
||||
|
||||
bindDepth() {
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.depthPickFramebuffer.bind();
|
||||
} else {
|
||||
this.depthPickTarget.bind();
|
||||
}
|
||||
}
|
||||
|
||||
get drawingBufferHeight() {
|
||||
@@ -57,21 +154,30 @@ export class PickPass {
|
||||
this.pickWidth = pickWidth;
|
||||
this.pickHeight = pickHeight;
|
||||
|
||||
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.objectPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
this.instancePickTexture.define(this.pickWidth, this.pickHeight);
|
||||
this.groupPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
this.depthPickTexture.define(this.pickWidth, this.pickHeight);
|
||||
|
||||
this.depthRenderbuffer.setSize(this.pickWidth, this.pickHeight);
|
||||
} else {
|
||||
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) {
|
||||
const depth = this.drawPass.depthTexturePrimitives;
|
||||
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
|
||||
renderer.clear(false);
|
||||
|
||||
renderer.update(camera);
|
||||
renderer.renderPick(scene.primitives, camera, variant, null, pickType);
|
||||
renderer.renderPick(scene.volumes, camera, variant, depth, pickType);
|
||||
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
|
||||
|
||||
if (helper.handle.isEnabled) {
|
||||
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
|
||||
}
|
||||
|
||||
if (helper.camera.isEnabled) {
|
||||
helper.camera.update(camera);
|
||||
@@ -81,18 +187,23 @@ export class PickPass {
|
||||
}
|
||||
|
||||
render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
|
||||
this.objectPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
|
||||
if (this.webgl.extensions.drawBuffers) {
|
||||
this.framebuffer.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.None);
|
||||
} else {
|
||||
this.objectPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
|
||||
|
||||
this.instancePickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
|
||||
this.instancePickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
|
||||
|
||||
this.groupPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
|
||||
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
|
||||
this.groupPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
|
||||
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
|
||||
|
||||
this.depthPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
|
||||
this.depthPickTarget.bind();
|
||||
this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,19 +258,21 @@ export class PickHelper {
|
||||
}
|
||||
|
||||
private syncBuffers() {
|
||||
if (isTimingMode) this.webgl.timer.mark('PickHelper.syncBuffers');
|
||||
const { pickX, pickY, pickWidth, pickHeight } = this;
|
||||
|
||||
this.pickPass.objectPickTarget.bind();
|
||||
this.pickPass.bindObject();
|
||||
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.objectBuffer);
|
||||
|
||||
this.pickPass.instancePickTarget.bind();
|
||||
this.pickPass.bindInstance();
|
||||
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.instanceBuffer);
|
||||
|
||||
this.pickPass.groupPickTarget.bind();
|
||||
this.pickPass.bindGroup();
|
||||
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.groupBuffer);
|
||||
|
||||
this.pickPass.depthPickTarget.bind();
|
||||
this.pickPass.bindDepth();
|
||||
this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.depthBuffer);
|
||||
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.syncBuffers');
|
||||
}
|
||||
|
||||
private getBufferIdx(x: number, y: number): number {
|
||||
@@ -178,11 +291,12 @@ export class PickHelper {
|
||||
}
|
||||
|
||||
private render(camera: Camera | StereoCamera) {
|
||||
if (isTimingMode) this.webgl.timer.mark('PickHelper.render');
|
||||
const { pickX, pickY, pickWidth, pickHeight, halfPickWidth } = this;
|
||||
const { renderer, scene, helper } = this;
|
||||
|
||||
renderer.setTransparentBackground(false);
|
||||
renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
|
||||
renderer.setDrawingBufferSize(pickWidth, pickHeight);
|
||||
renderer.setPixelRatio(this.pickScale);
|
||||
|
||||
if (StereoCamera.is(camera)) {
|
||||
@@ -197,6 +311,7 @@ export class PickHelper {
|
||||
}
|
||||
|
||||
this.dirty = false;
|
||||
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.render');
|
||||
}
|
||||
|
||||
private identifyInternal(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
|
||||
@@ -217,8 +332,10 @@ export class PickHelper {
|
||||
) return;
|
||||
|
||||
if (this.dirty) {
|
||||
if (isTimingMode) this.webgl.timer.mark('PickHelper.identify');
|
||||
this.render(camera);
|
||||
this.syncBuffers();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('PickHelper.identify');
|
||||
}
|
||||
|
||||
const xv = x - viewport.x;
|
||||
@@ -240,6 +357,7 @@ export class PickHelper {
|
||||
if (groupId === -1 || groupId === NullId) return;
|
||||
|
||||
const z = this.getDepth(xp, yp);
|
||||
// console.log('z', z);
|
||||
const position = Vec3.create(x, viewport.height - y, z);
|
||||
if (StereoCamera.is(camera)) {
|
||||
const halfWidth = Math.floor(viewport.width / 2);
|
||||
@@ -254,7 +372,7 @@ export class PickHelper {
|
||||
cameraUnproject(position, position, viewport, camera.inverseProjectionView);
|
||||
}
|
||||
|
||||
// console.log({ { objectId, instanceId, groupId }, position} );
|
||||
// console.log({ id: { objectId, instanceId, groupId }, position });
|
||||
return { id: { objectId, instanceId, groupId }, position };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
|
||||
*/
|
||||
|
||||
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
|
||||
import { CopyRenderable, createCopyRenderable, QuadSchema, QuadValues } from '../../mol-gl/compute/util';
|
||||
import { TextureSpec, Values, UniformSpec, DefineSpec } from '../../mol-gl/renderable/schema';
|
||||
import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
@@ -27,10 +27,14 @@ import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { FxaaParams, FxaaPass } from './fxaa';
|
||||
import { SmaaParams, SmaaPass } from './smaa';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
import { BackgroundParams, BackgroundPass } from './background';
|
||||
import { AssetManager } from '../../mol-util/assets';
|
||||
|
||||
const OutlinesSchema = {
|
||||
...QuadSchema,
|
||||
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
dOrthographic: DefineSpec('number'),
|
||||
@@ -41,11 +45,15 @@ const OutlinesSchema = {
|
||||
};
|
||||
type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>>
|
||||
|
||||
function getOutlinesRenderable(ctx: WebGLContext, depthTexture: Texture): OutlinesRenderable {
|
||||
function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture): OutlinesRenderable {
|
||||
const width = depthTextureOpaque.getWidth();
|
||||
const height = depthTextureOpaque.getHeight();
|
||||
|
||||
const values: Values<typeof OutlinesSchema> = {
|
||||
...QuadValues,
|
||||
tDepth: ValueCell.create(depthTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(depthTexture.getWidth(), depthTexture.getHeight())),
|
||||
tDepthOpaque: ValueCell.create(depthTextureOpaque),
|
||||
tDepthTransparent: ValueCell.create(depthTextureTransparent),
|
||||
uTexSize: ValueCell.create(Vec2.create(width, height)),
|
||||
|
||||
dOrthographic: ValueCell.create(0),
|
||||
uNear: ValueCell.create(1),
|
||||
@@ -85,7 +93,7 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender
|
||||
...QuadValues,
|
||||
tDepth: ValueCell.create(depthTexture),
|
||||
|
||||
uSamples: ValueCell.create([0.0, 0.0, 1.0]),
|
||||
uSamples: ValueCell.create(getSamples(32)),
|
||||
dNSamples: ValueCell.create(32),
|
||||
|
||||
uProjection: ValueCell.create(Mat4.identity()),
|
||||
@@ -132,7 +140,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
|
||||
tSsaoDepth: ValueCell.create(ssaoDepthTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
|
||||
|
||||
uKernel: ValueCell.create([0.0]),
|
||||
uKernel: ValueCell.create(getBlurKernel(15)),
|
||||
dOcclusionKernelSize: ValueCell.create(15),
|
||||
|
||||
uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
|
||||
@@ -165,15 +173,26 @@ function getBlurKernel(kernelSize: number): number[] {
|
||||
return kernel;
|
||||
}
|
||||
|
||||
function getSamples(vectorSamples: Vec3[], nSamples: number): number[] {
|
||||
const RandomHemisphereVector: Vec3[] = [];
|
||||
for (let i = 0; i < 256; i++) {
|
||||
const v = Vec3();
|
||||
v[0] = Math.random() * 2.0 - 1.0;
|
||||
v[1] = Math.random() * 2.0 - 1.0;
|
||||
v[2] = Math.random();
|
||||
Vec3.normalize(v, v);
|
||||
Vec3.scale(v, v, Math.random());
|
||||
RandomHemisphereVector.push(v);
|
||||
}
|
||||
|
||||
function getSamples(nSamples: number): number[] {
|
||||
const samples = [];
|
||||
for (let i = 0; i < nSamples; i++) {
|
||||
let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples);
|
||||
scale = 0.1 + scale * (1.0 - 0.1);
|
||||
|
||||
samples.push(vectorSamples[i][0] * scale);
|
||||
samples.push(vectorSamples[i][1] * scale);
|
||||
samples.push(vectorSamples[i][2] * scale);
|
||||
samples.push(RandomHemisphereVector[i][0] * scale);
|
||||
samples.push(RandomHemisphereVector[i][1] * scale);
|
||||
samples.push(RandomHemisphereVector[i][2] * scale);
|
||||
}
|
||||
|
||||
return samples;
|
||||
@@ -183,7 +202,8 @@ const PostprocessingSchema = {
|
||||
...QuadSchema,
|
||||
tSsaoDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
@@ -199,6 +219,7 @@ const PostprocessingSchema = {
|
||||
uMaxPossibleViewZDiff: UniformSpec('f'),
|
||||
|
||||
dOcclusionEnable: DefineSpec('boolean'),
|
||||
uOcclusionOffset: UniformSpec('v2'),
|
||||
|
||||
dOutlineEnable: DefineSpec('boolean'),
|
||||
dOutlineScale: DefineSpec('number'),
|
||||
@@ -206,12 +227,13 @@ const PostprocessingSchema = {
|
||||
};
|
||||
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
|
||||
|
||||
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
|
||||
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
|
||||
const values: Values<typeof PostprocessingSchema> = {
|
||||
...QuadValues,
|
||||
tSsaoDepth: ValueCell.create(ssaoDepthTexture),
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
tDepth: ValueCell.create(depthTexture),
|
||||
tDepthOpaque: ValueCell.create(depthTextureOpaque),
|
||||
tDepthTransparent: ValueCell.create(depthTextureTransparent),
|
||||
tOutlines: ValueCell.create(outlinesTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
|
||||
@@ -227,6 +249,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
uMaxPossibleViewZDiff: ValueCell.create(0.5),
|
||||
|
||||
dOcclusionEnable: ValueCell.create(true),
|
||||
uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)),
|
||||
|
||||
dOutlineEnable: ValueCell.create(false),
|
||||
dOutlineScale: ValueCell.create(1),
|
||||
@@ -244,9 +267,10 @@ export const PostprocessingParams = {
|
||||
occlusion: PD.MappedStatic('on', {
|
||||
on: PD.Group({
|
||||
samples: PD.Numeric(32, { min: 1, max: 256, step: 1 }),
|
||||
radius: PD.Numeric(5, { min: 0, max: 10, step: 0.1 }, { description: 'Final radius is 2^x.' }),
|
||||
radius: PD.Numeric(5, { min: 0, max: 10, step: 0.1 }, { description: 'Final occlusion radius is 2^x' }),
|
||||
bias: PD.Numeric(0.8, { min: 0, max: 3, step: 0.1 }),
|
||||
blurKernelSize: PD.Numeric(15, { min: 1, max: 25, step: 2 }),
|
||||
resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
|
||||
@@ -263,12 +287,17 @@ export const PostprocessingParams = {
|
||||
smaa: PD.Group(SmaaParams),
|
||||
off: PD.Group({})
|
||||
}, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }),
|
||||
background: PD.Group(BackgroundParams, { isFlat: true }),
|
||||
};
|
||||
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
|
||||
|
||||
export class PostprocessingPass {
|
||||
static isEnabled(props: PostprocessingProps) {
|
||||
return props.occlusion.name === 'on' || props.outline.name === 'on';
|
||||
return props.occlusion.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off';
|
||||
}
|
||||
|
||||
static isOutlineEnabled(props: PostprocessingProps) {
|
||||
return props.outline.name === 'on';
|
||||
}
|
||||
|
||||
readonly target: RenderTarget;
|
||||
@@ -276,11 +305,13 @@ export class PostprocessingPass {
|
||||
private readonly outlinesTarget: RenderTarget;
|
||||
private readonly outlinesRenderable: OutlinesRenderable;
|
||||
|
||||
private readonly randomHemisphereVector: Vec3[];
|
||||
private readonly ssaoFramebuffer: Framebuffer;
|
||||
private readonly ssaoBlurFirstPassFramebuffer: Framebuffer;
|
||||
private readonly ssaoBlurSecondPassFramebuffer: Framebuffer;
|
||||
|
||||
private readonly downsampledDepthTarget: RenderTarget;
|
||||
private readonly downsampleDepthRenderable: CopyRenderable;
|
||||
|
||||
private readonly ssaoDepthTexture: Texture;
|
||||
private readonly ssaoDepthBlurProxyTexture: Texture;
|
||||
|
||||
@@ -290,41 +321,35 @@ export class PostprocessingPass {
|
||||
|
||||
private nSamples: number;
|
||||
private blurKernelSize: number;
|
||||
private downsampleFactor: number;
|
||||
|
||||
private readonly renderable: PostprocessingRenderable;
|
||||
|
||||
private ssaoScale: number;
|
||||
private calcSsaoScale() {
|
||||
// downscale ssao for high pixel-ratios
|
||||
return Math.min(1, 1 / this.webgl.pixelRatio);
|
||||
return Math.min(1, 1 / this.webgl.pixelRatio) * this.downsampleFactor;
|
||||
}
|
||||
|
||||
constructor(private webgl: WebGLContext, drawPass: DrawPass) {
|
||||
this.ssaoScale = this.calcSsaoScale();
|
||||
private readonly bgColor = Vec3();
|
||||
readonly background: BackgroundPass;
|
||||
|
||||
const { colorTarget, depthTexture } = drawPass;
|
||||
constructor(private readonly webgl: WebGLContext, assetManager: AssetManager, private readonly drawPass: DrawPass) {
|
||||
const { colorTarget, depthTextureTransparent, depthTextureOpaque } = drawPass;
|
||||
const width = colorTarget.getWidth();
|
||||
const height = colorTarget.getHeight();
|
||||
|
||||
this.nSamples = 1;
|
||||
this.blurKernelSize = 1;
|
||||
this.downsampleFactor = 1;
|
||||
this.ssaoScale = this.calcSsaoScale();
|
||||
|
||||
// needs to be linear for anti-aliasing pass
|
||||
this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
|
||||
|
||||
this.outlinesTarget = webgl.createRenderTarget(width, height, false);
|
||||
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTexture);
|
||||
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent);
|
||||
|
||||
this.randomHemisphereVector = [];
|
||||
for (let i = 0; i < 256; i++) {
|
||||
const v = Vec3();
|
||||
v[0] = Math.random() * 2.0 - 1.0;
|
||||
v[1] = Math.random() * 2.0 - 1.0;
|
||||
v[2] = Math.random();
|
||||
Vec3.normalize(v, v);
|
||||
Vec3.scale(v, v, Math.random());
|
||||
this.randomHemisphereVector.push(v);
|
||||
}
|
||||
this.ssaoFramebuffer = webgl.resources.framebuffer();
|
||||
this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer();
|
||||
this.ssaoBlurSecondPassFramebuffer = webgl.resources.framebuffer();
|
||||
@@ -332,20 +357,25 @@ export class PostprocessingPass {
|
||||
const sw = Math.floor(width * this.ssaoScale);
|
||||
const sh = Math.floor(height * this.ssaoScale);
|
||||
|
||||
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.downsampledDepthTarget = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear');
|
||||
this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTextureOpaque);
|
||||
|
||||
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
this.ssaoDepthTexture.define(sw, sh);
|
||||
this.ssaoDepthTexture.attachFramebuffer(this.ssaoFramebuffer, 'color0');
|
||||
|
||||
this.ssaoDepthBlurProxyTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
|
||||
this.ssaoDepthBlurProxyTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
this.ssaoDepthBlurProxyTexture.define(sw, sh);
|
||||
this.ssaoDepthBlurProxyTexture.attachFramebuffer(this.ssaoBlurFirstPassFramebuffer, 'color0');
|
||||
|
||||
this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0');
|
||||
|
||||
this.ssaoRenderable = getSsaoRenderable(webgl, depthTexture);
|
||||
this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
|
||||
this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
|
||||
this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, this.outlinesTarget.texture, this.ssaoDepthTexture);
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture);
|
||||
|
||||
this.background = new BackgroundPass(webgl, assetManager, width, height);
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
@@ -359,14 +389,18 @@ export class PostprocessingPass {
|
||||
const sh = Math.floor(height * this.ssaoScale);
|
||||
this.target.setSize(width, height);
|
||||
this.outlinesTarget.setSize(width, height);
|
||||
this.downsampledDepthTarget.setSize(sw, sh);
|
||||
this.ssaoDepthTexture.define(sw, sh);
|
||||
this.ssaoDepthBlurProxyTexture.define(sw, sh);
|
||||
|
||||
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.outlinesRenderable.values.uTexSize, Vec2.set(this.outlinesRenderable.values.uTexSize.ref.value, width, height));
|
||||
ValueCell.update(this.downsampleDepthRenderable.values.uTexSize, Vec2.set(this.downsampleDepthRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
|
||||
this.background.setSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +450,7 @@ export class PostprocessingPass {
|
||||
needsUpdateSsao = true;
|
||||
|
||||
this.nSamples = props.occlusion.params.samples;
|
||||
ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.randomHemisphereVector, this.nSamples));
|
||||
ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.nSamples));
|
||||
ValueCell.updateIfChanged(this.ssaoRenderable.values.dNSamples, this.nSamples);
|
||||
}
|
||||
ValueCell.updateIfChanged(this.ssaoRenderable.values.uRadius, Math.pow(2, props.occlusion.params.radius));
|
||||
@@ -434,6 +468,30 @@ export class PostprocessingPass {
|
||||
ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
|
||||
}
|
||||
|
||||
if (this.downsampleFactor !== props.occlusion.params.resolutionScale) {
|
||||
needsUpdateSsao = true;
|
||||
|
||||
this.downsampleFactor = props.occlusion.params.resolutionScale;
|
||||
this.ssaoScale = this.calcSsaoScale();
|
||||
|
||||
const sw = Math.floor(w * this.ssaoScale);
|
||||
const sh = Math.floor(h * this.ssaoScale);
|
||||
|
||||
this.downsampledDepthTarget.setSize(sw, sh);
|
||||
this.ssaoDepthTexture.define(sw, sh);
|
||||
this.ssaoDepthBlurProxyTexture.define(sw, sh);
|
||||
|
||||
if (this.ssaoScale === 1) {
|
||||
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureTransparent);
|
||||
} else {
|
||||
ValueCell.update(this.ssaoRenderable.values.tDepth, this.downsampledDepthTarget.texture);
|
||||
}
|
||||
|
||||
ValueCell.update(this.downsampleDepthRenderable.values.uTexSize, Vec2.set(this.downsampleDepthRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
|
||||
}
|
||||
}
|
||||
|
||||
if (props.outline.name === 'on') {
|
||||
@@ -490,11 +548,24 @@ export class PostprocessingPass {
|
||||
state.depthMask(false);
|
||||
|
||||
const { x, y, width, height } = camera.viewport;
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
}
|
||||
|
||||
private occlusionOffset: [x: number, y: number] = [0, 0];
|
||||
setOcclusionOffset(x: number, y: number) {
|
||||
this.occlusionOffset[0] = x;
|
||||
this.occlusionOffset[1] = y;
|
||||
ValueCell.update(this.renderable.values.uOcclusionOffset, Vec2.set(this.renderable.values.uOcclusionOffset.ref.value, x, y));
|
||||
}
|
||||
|
||||
private transparentBackground = false;
|
||||
setTransparentBackground(value: boolean) {
|
||||
this.transparentBackground = value;
|
||||
}
|
||||
|
||||
render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
|
||||
if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render');
|
||||
this.updateState(camera, transparentBackground, backgroundColor, props);
|
||||
|
||||
if (props.outline.name === 'on') {
|
||||
@@ -502,14 +573,13 @@ export class PostprocessingPass {
|
||||
this.outlinesRenderable.render();
|
||||
}
|
||||
|
||||
if (props.occlusion.name === 'on') {
|
||||
const { x, y, width, height } = camera.viewport;
|
||||
const sx = Math.floor(x * this.ssaoScale);
|
||||
const sy = Math.floor(y * this.ssaoScale);
|
||||
const sw = Math.ceil(width * this.ssaoScale);
|
||||
const sh = Math.ceil(height * this.ssaoScale);
|
||||
this.webgl.gl.viewport(sx, sy, sw, sh);
|
||||
this.webgl.gl.scissor(sx, sy, sw, sh);
|
||||
// don't render occlusion if offset is given,
|
||||
// which will reuse the existing occlusion
|
||||
if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {
|
||||
if (this.ssaoScale < 1) {
|
||||
this.downsampledDepthTarget.bind();
|
||||
this.downsampleDepthRenderable.render();
|
||||
}
|
||||
|
||||
this.ssaoFramebuffer.bind();
|
||||
this.ssaoRenderable.render();
|
||||
@@ -519,9 +589,6 @@ export class PostprocessingPass {
|
||||
|
||||
this.ssaoBlurSecondPassFramebuffer.bind();
|
||||
this.ssaoBlurSecondPassRenderable.render();
|
||||
|
||||
this.webgl.gl.viewport(x, y, width, height);
|
||||
this.webgl.gl.scissor(x, y, width, height);
|
||||
}
|
||||
|
||||
if (toDrawingBuffer) {
|
||||
@@ -531,10 +598,26 @@ export class PostprocessingPass {
|
||||
}
|
||||
|
||||
const { gl, state } = this.webgl;
|
||||
state.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
this.background.update(camera, props.background);
|
||||
if (this.background.isEnabled(props.background)) {
|
||||
if (this.transparentBackground) {
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
} else {
|
||||
Color.toVec3Normalized(this.bgColor, backgroundColor);
|
||||
state.clearColor(this.bgColor[0], this.bgColor[1], this.bgColor[2], 1);
|
||||
}
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
state.enable(gl.BLEND);
|
||||
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
this.background.render();
|
||||
} else {
|
||||
state.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
this.renderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('PostprocessingPass.render');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { weights_frag } from '../../mol-gl/shader/smaa/weights.frag';
|
||||
import { edges_vert } from '../../mol-gl/shader/smaa/edges.vert';
|
||||
import { edges_frag } from '../../mol-gl/shader/smaa/edges.frag';
|
||||
import { Viewport } from '../camera/util';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
|
||||
|
||||
export const SmaaParams = {
|
||||
edgeThreshold: PD.Numeric(0.1, { min: 0.05, max: 0.15, step: 0.01 }),
|
||||
@@ -71,8 +71,8 @@ export class SmaaPass {
|
||||
state.depthMask(false);
|
||||
|
||||
const { x, y, width, height } = viewport;
|
||||
gl.viewport(x, y, width, height);
|
||||
gl.scissor(x, y, width, height);
|
||||
state.viewport(x, y, width, height);
|
||||
state.scissor(x, y, width, height);
|
||||
|
||||
state.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -120,6 +120,7 @@ export class SmaaPass {
|
||||
}
|
||||
|
||||
render(viewport: Viewport, target: RenderTarget | undefined) {
|
||||
if (isTimingMode) this.webgl.timer.mark('SmaaPass.render');
|
||||
this.edgesTarget.bind();
|
||||
this.updateState(viewport);
|
||||
this.edgesRenderable.render();
|
||||
@@ -135,8 +136,8 @@ export class SmaaPass {
|
||||
}
|
||||
this.updateState(viewport);
|
||||
this.blendRenderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('SmaaPass.render');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -17,7 +17,9 @@ import { quad_vert } from '../../mol-gl/shader/quad.vert';
|
||||
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 } from '../../mol-util/debug';
|
||||
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() {
|
||||
@@ -71,6 +74,7 @@ export class WboitPass {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (isTimingMode) this.webgl.timer.mark('WboitPass.render');
|
||||
const { state, gl } = this.webgl;
|
||||
|
||||
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
@@ -78,6 +82,7 @@ export class WboitPass {
|
||||
|
||||
this.renderable.update();
|
||||
this.renderable.render();
|
||||
if (isTimingMode) this.webgl.timer.markEnd('WboitPass.render');
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
@@ -85,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));
|
||||
}
|
||||
}
|
||||
@@ -104,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) {
|
||||
@@ -126,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);
|
||||
@@ -134,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) 2017-2018 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>
|
||||
@@ -61,6 +61,29 @@ describe('column', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('string column', () => {
|
||||
const xs = ['A', 'b', null, undefined];
|
||||
const xsArr = xs.map(x => x ?? '');
|
||||
const xsLC = xs.map(x => (x ?? '').toLowerCase());
|
||||
const arr = Column.ofArray({ array: xs as any, schema: Column.Schema.str });
|
||||
const arrLC = Column.ofArray({ array: xs as any, schema: Column.Schema.Str({ transform: 'lowercase' }) });
|
||||
const aliasedLC = Column.ofArray({ array: xs as any, schema: Column.Schema.Aliased<'a' | 'b'>(Column.Schema.lstr) });
|
||||
|
||||
it('value', () => {
|
||||
for (let i = 0; i < xs.length; i++) {
|
||||
expect(arr.value(i)).toBe(xs[i] ?? '');
|
||||
expect(arrLC.value(i)).toBe(xsLC[i] ?? '');
|
||||
expect(aliasedLC.value(i)).toBe(xsLC[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('array', () => {
|
||||
expect(arr.toArray()).toEqual(xsArr);
|
||||
expect(arrLC.toArray()).toEqual(xsLC);
|
||||
expect(aliasedLC.toArray()).toEqual(xsLC);
|
||||
});
|
||||
});
|
||||
|
||||
describe('table', () => {
|
||||
const schema = {
|
||||
x: Column.Schema.int,
|
||||
|
||||
@@ -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>
|
||||
@@ -25,38 +25,39 @@ interface Column<T> {
|
||||
namespace Column {
|
||||
export type ArrayCtor<T> = { new(size: number): ArrayLike<T> }
|
||||
|
||||
export type Schema<T = any> = Schema.Str | Schema.Int | Schema.Float | Schema.Coordinate | Schema.Aliased<T> | Schema.Tensor | Schema.List<number|string>
|
||||
export type Schema<T = any> = Schema.Str | Schema.Int | Schema.Float | Schema.Coordinate | Schema.Aliased<T> | Schema.Tensor | Schema.List<number | string>
|
||||
|
||||
export namespace Schema {
|
||||
// T also serves as a default value for undefined columns
|
||||
|
||||
type Base<T extends string> = { valueType: T }
|
||||
export type Str = { '@type': 'str', T: string } & Base<'str'>
|
||||
export type Str = { '@type': 'str', T: string, transform?: 'uppercase' | 'lowercase' } & Base<'str'>
|
||||
export type Int = { '@type': 'int', T: number } & Base<'int'>
|
||||
export type Float = { '@type': 'float', T: number } & Base<'float'>
|
||||
export type Coordinate = { '@type': 'coord', T: number } & Base<'float'>
|
||||
|
||||
export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space, baseType: Int | Float } & Base<'tensor'>
|
||||
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<T extends string ? 'str' : 'int'>
|
||||
export type List<T extends number|string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
|
||||
export type Aliased<T> = { '@type': 'aliased', T: T, transform?: T extends string ? 'uppercase' | 'lowercase' : never } & Base<T extends string ? 'str' : 'int'>
|
||||
export type List<T extends number | string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
|
||||
|
||||
export const str: Str = { '@type': 'str', T: '', valueType: 'str' };
|
||||
export const ustr: Str = { '@type': 'str', T: '', valueType: 'str', transform: 'uppercase' };
|
||||
export const lstr: Str = { '@type': 'str', T: '', valueType: 'str', transform: 'lowercase' };
|
||||
export const int: Int = { '@type': 'int', T: 0, valueType: 'int' };
|
||||
export const coord: Coordinate = { '@type': 'coord', T: 0, valueType: 'float' };
|
||||
export const float: Float = { '@type': 'float', T: 0, valueType: 'float' };
|
||||
|
||||
export function Str(defaultValue = ''): Str { return { '@type': 'str', T: defaultValue, valueType: 'str' }; };
|
||||
export function Str(options?: { defaultValue?: string, transform?: 'uppercase' | 'lowercase' }): Str { return { '@type': 'str', T: options?.defaultValue ?? '', transform: options?.transform, valueType: 'str' }; };
|
||||
export function Int(defaultValue = 0): Int { return { '@type': 'int', T: defaultValue, valueType: 'int' }; };
|
||||
export function Float(defaultValue = 0): Float { return { '@type': 'float', T: defaultValue, valueType: 'float' }; };
|
||||
export function Tensor(space: Tensors.Space, baseType: Int | Float = float): Tensor { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor', baseType }; }
|
||||
export function Vector(dim: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.Vector(dim, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }
|
||||
export function Matrix(rows: number, cols: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.ColumnMajorMatrix(rows, cols, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }
|
||||
|
||||
export function Aliased<T>(t: Str | Int, defaultValue?: T): Aliased<T> {
|
||||
if (typeof defaultValue !== 'undefined') return { ...t, T: defaultValue } as any as Aliased<T>;
|
||||
export function Aliased<T>(t: Str | Int): Aliased<T> {
|
||||
return t as any as Aliased<T>;
|
||||
}
|
||||
export function List<T extends number|string>(separator: string, itemParse: (x: string) => T, defaultValue: T[] = []): List<T> {
|
||||
export function List<T extends number | string>(separator: string, itemParse: (x: string) => T, defaultValue: T[] = []): List<T> {
|
||||
return { '@type': 'list', T: defaultValue, separator, itemParse, valueType: 'list' };
|
||||
}
|
||||
}
|
||||
@@ -287,8 +288,13 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqua
|
||||
|
||||
function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Column.ArraySpec<T>): Column<T['T']> {
|
||||
const rowCount = array.length;
|
||||
const defaultValue = schema.T;
|
||||
const value: Column<T['T']>['value'] = schema.valueType === 'str'
|
||||
? row => { const v = array[row]; return typeof v === 'string' ? v : '' + v; }
|
||||
? (schema as Column.Schema.Str).transform === 'lowercase'
|
||||
? row => { const v = array[row]; return typeof v === 'string' ? v.toLowerCase() : `${v ?? defaultValue}`.toLowerCase(); }
|
||||
: (schema as Column.Schema.Str).transform === 'uppercase'
|
||||
? row => { const v = array[row]; return typeof v === 'string' ? v.toUpperCase() : `${v ?? defaultValue}`.toUpperCase(); }
|
||||
: row => { const v = array[row]; return typeof v === 'string' ? v : `${v ?? defaultValue}`; }
|
||||
: row => array[row];
|
||||
|
||||
const isTyped = ColumnHelpers.isTypedArray(array);
|
||||
@@ -300,15 +306,35 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
value,
|
||||
valueKind: valueKind ? valueKind : row => Column.ValueKind.Present,
|
||||
toArray: schema.valueType === 'str'
|
||||
? params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) {
|
||||
const v = array[start + i];
|
||||
ret[i] = typeof v === 'string' ? v : '' + v;
|
||||
? (schema as Column.Schema.Str).transform === 'lowercase'
|
||||
? params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) {
|
||||
const v = array[start + i];
|
||||
ret[i] = typeof v === 'string' ? v.toLowerCase() : `${v ?? defaultValue}`.toLowerCase();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
: (schema as Column.Schema.Str).transform === 'uppercase'
|
||||
? params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) {
|
||||
const v = array[start + i];
|
||||
ret[i] = typeof v === 'string' ? v.toUpperCase() : `${v ?? defaultValue}`.toUpperCase();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) {
|
||||
const v = array[start + i];
|
||||
ret[i] = typeof v === 'string' ? v : `${v ?? defaultValue}`;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
: isTyped
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -83,6 +83,7 @@ export namespace BaseGeometry {
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
|
||||
material: Material.getParam(),
|
||||
clip: PD.Group(Clip.Params),
|
||||
instanceGranularity: PD.Boolean(false, { description: 'Use instance granularity for marker, transparency, clipping, overpaint, substance data to save memory.' }),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -110,6 +111,7 @@ export namespace BaseGeometry {
|
||||
uRoughness: ValueCell.create(props.material.roughness),
|
||||
uBumpiness: ValueCell.create(props.material.bumpiness),
|
||||
dLightCount: ValueCell.create(1),
|
||||
dColorMarker: ValueCell.create(true),
|
||||
|
||||
dClipObjectCount: ValueCell.create(clip.objects.count),
|
||||
dClipVariant: ValueCell.create(clip.variant),
|
||||
@@ -118,6 +120,8 @@ export namespace BaseGeometry {
|
||||
uClipObjectPosition: ValueCell.create(clip.objects.position),
|
||||
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
|
||||
uClipObjectScale: ValueCell.create(clip.objects.scale),
|
||||
|
||||
instanceGranularity: ValueCell.create(props.instanceGranularity),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,6 +139,8 @@ export namespace BaseGeometry {
|
||||
ValueCell.update(values.uClipObjectPosition, clip.objects.position);
|
||||
ValueCell.update(values.uClipObjectRotation, clip.objects.rotation);
|
||||
ValueCell.update(values.uClipObjectScale, clip.objects.scale);
|
||||
|
||||
ValueCell.updateIfChanged(values.instanceGranularity, props.instanceGranularity);
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
|
||||
@@ -9,10 +9,13 @@ import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Clipping } from '../../mol-theme/clipping';
|
||||
|
||||
export type ClippingType = 'instance' | 'groupInstance';
|
||||
|
||||
export type ClippingData = {
|
||||
tClipping: ValueCell<TextureImage<Uint8Array>>
|
||||
uClippingTexDim: ValueCell<Vec2>
|
||||
dClipping: ValueCell<boolean>,
|
||||
dClipping: ValueCell<boolean>
|
||||
dClippingType: ValueCell<string>
|
||||
}
|
||||
|
||||
export function applyClippingGroups(array: Uint8Array, start: number, end: number, groups: Clipping.Groups) {
|
||||
@@ -24,18 +27,20 @@ export function clearClipping(array: Uint8Array, start: number, end: number) {
|
||||
array.fill(0, start, end);
|
||||
}
|
||||
|
||||
export function createClipping(count: number, clippingData?: ClippingData): ClippingData {
|
||||
export function createClipping(count: number, type: ClippingType, clippingData?: ClippingData): ClippingData {
|
||||
const clipping = createTextureImage(Math.max(1, count), 1, Uint8Array, clippingData && clippingData.tClipping.ref.value.array);
|
||||
if (clippingData) {
|
||||
ValueCell.update(clippingData.tClipping, clipping);
|
||||
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(clipping.width, clipping.height));
|
||||
ValueCell.updateIfChanged(clippingData.dClipping, count > 0);
|
||||
ValueCell.updateIfChanged(clippingData.dClippingType, type);
|
||||
return clippingData;
|
||||
} else {
|
||||
return {
|
||||
tClipping: ValueCell.create(clipping),
|
||||
uClippingTexDim: ValueCell.create(Vec2.create(clipping.width, clipping.height)),
|
||||
dClipping: ValueCell.create(count > 0),
|
||||
dClippingType: ValueCell.create(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -52,6 +57,7 @@ export function createEmptyClipping(clippingData?: ClippingData): ClippingData {
|
||||
tClipping: ValueCell.create(emptyClippingTexture),
|
||||
uClippingTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dClipping: ValueCell.create(false),
|
||||
dClippingType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -11,11 +11,13 @@ import { Color } from '../../mol-util/color';
|
||||
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { LocationIterator } from '../util/location-iterator';
|
||||
import { NullLocation } from '../../mol-model/location';
|
||||
import { LocationColor, ColorTheme } from '../../mol-theme/color';
|
||||
import { Geometry } from './geometry';
|
||||
import { LocationColor, ColorTheme, ColorVolume } from '../../mol-theme/color';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
|
||||
export type ColorTypeLocation = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance';
|
||||
export type ColorTypeGrid = 'volume' | 'volumeInstance';
|
||||
export type ColorTypeDirect = 'direct';
|
||||
export type ColorType = ColorTypeLocation | ColorTypeGrid | ColorTypeDirect;
|
||||
|
||||
export type ColorData = {
|
||||
uColor: ValueCell<Vec3>,
|
||||
@@ -29,7 +31,7 @@ export type ColorData = {
|
||||
dUsePalette: ValueCell<boolean>,
|
||||
}
|
||||
|
||||
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
|
||||
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
|
||||
const data = _createColors(locationIt, positionIt, colorTheme, colorData);
|
||||
if (colorTheme.palette) {
|
||||
ValueCell.updateIfChanged(data.dUsePalette, true);
|
||||
@@ -40,16 +42,19 @@ export function createColors(locationIt: LocationIterator, positionIt: LocationI
|
||||
return data;
|
||||
}
|
||||
|
||||
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
|
||||
switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
|
||||
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
|
||||
switch (colorTheme.granularity) {
|
||||
case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
|
||||
case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
|
||||
case 'instance':
|
||||
return locationIt.nonInstanceable
|
||||
? createGroupColor(locationIt, colorTheme.color, colorData)
|
||||
: createInstanceColor(locationIt, colorTheme.color, colorData);
|
||||
case 'group': return createGroupColor(locationIt, colorTheme.color, colorData);
|
||||
case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData);
|
||||
case 'vertex': return createVertexColor(positionIt, colorTheme.color, colorData);
|
||||
case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
|
||||
case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
|
||||
case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
|
||||
case 'volume': return createGridColor(colorTheme.grid, 'volume', colorData);
|
||||
case 'volumeInstance': return createGridColor(colorTheme.grid, 'volumeInstance', colorData);
|
||||
case 'direct': return createDirectColor(colorData);
|
||||
}
|
||||
}
|
||||
@@ -207,12 +212,6 @@ function createVertexInstanceColor(locationIt: LocationIterator, color: Location
|
||||
|
||||
//
|
||||
|
||||
interface ColorVolume {
|
||||
colors: Texture
|
||||
dimension: Vec3
|
||||
transform: Vec4
|
||||
}
|
||||
|
||||
export function createGridColor(grid: ColorVolume, type: ColorType, colorData?: ColorData): ColorData {
|
||||
const { colors, dimension, transform } = grid;
|
||||
const width = colors.getWidth();
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -157,6 +157,7 @@ export namespace Cylinders {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
|
||||
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
};
|
||||
@@ -200,7 +201,9 @@ export namespace Cylinders {
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const size = createSizes(locationIt, theme.size);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
@@ -241,6 +244,7 @@ export namespace Cylinders {
|
||||
uDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
@@ -258,6 +262,7 @@ export namespace Cylinders {
|
||||
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -211,7 +211,9 @@ export namespace DirectVolume {
|
||||
const positionIt = Utils.createPositionIterator(directVolume, transform);
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
|
||||
@@ -143,7 +143,9 @@ namespace Image {
|
||||
const positionIt = Utils.createPositionIterator(image, transform);
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -165,7 +165,7 @@ export namespace Lines {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
|
||||
sizeFactor: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }),
|
||||
lineSizeAttenuation: PD.Boolean(false),
|
||||
};
|
||||
export type Params = typeof Params
|
||||
@@ -208,7 +208,9 @@ export namespace Lines {
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const size = createSizes(locationIt, theme.size);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,12 +8,15 @@ import { ValueCell } from '../../mol-util/value-cell';
|
||||
import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
|
||||
export type MarkerType = 'instance' | 'groupInstance';
|
||||
|
||||
export type MarkerData = {
|
||||
uMarker: ValueCell<number>,
|
||||
uMarker: ValueCell<number>
|
||||
tMarker: ValueCell<TextureImage<Uint8Array>>
|
||||
uMarkerTexDim: ValueCell<Vec2>
|
||||
markerAverage: ValueCell<number>
|
||||
markerStatus: ValueCell<number>
|
||||
dMarkerType: ValueCell<string>
|
||||
}
|
||||
|
||||
const MarkerCountLut = new Uint8Array(0x0303 + 1);
|
||||
@@ -64,7 +67,7 @@ export function getMarkersAverage(array: Uint8Array, count: number): number {
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
export function createMarkers(count: number, markerData?: MarkerData): MarkerData {
|
||||
export function createMarkers(count: number, type: MarkerType, markerData?: MarkerData): MarkerData {
|
||||
const markers = createTextureImage(Math.max(1, count), 1, Uint8Array, markerData && markerData.tMarker.ref.value.array);
|
||||
const average = getMarkersAverage(markers.array, count);
|
||||
const status = average === 0 ? 0 : -1;
|
||||
@@ -74,6 +77,7 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
|
||||
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
|
||||
ValueCell.updateIfChanged(markerData.markerAverage, average);
|
||||
ValueCell.updateIfChanged(markerData.markerStatus, status);
|
||||
ValueCell.updateIfChanged(markerData.dMarkerType, type);
|
||||
return markerData;
|
||||
} else {
|
||||
return {
|
||||
@@ -82,6 +86,7 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
|
||||
uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
|
||||
markerAverage: ValueCell.create(average),
|
||||
markerStatus: ValueCell.create(status),
|
||||
dMarkerType: ValueCell.create(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -102,6 +107,7 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
|
||||
uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
markerAverage: ValueCell.create(0),
|
||||
markerStatus: ValueCell.create(0),
|
||||
dMarkerType: ValueCell.create('groupInstance'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -34,13 +34,15 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
|
||||
|
||||
const isInstanceType = colorType.endsWith('Instance');
|
||||
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
|
||||
const pad = 1 + resolution;
|
||||
const expandedBox = Box3D.expand(Box3D(), box, Vec3.create(pad, pad, pad));
|
||||
|
||||
const scaleFactor = 1 / resolution;
|
||||
const scaledBox = Box3D.scale(Box3D(), box, scaleFactor);
|
||||
const scaledBox = Box3D.scale(Box3D(), expandedBox, scaleFactor);
|
||||
const gridDim = Box3D.size(Vec3(), scaledBox);
|
||||
Vec3.ceil(gridDim, gridDim);
|
||||
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
|
||||
const { min } = box;
|
||||
const { min } = expandedBox;
|
||||
|
||||
const [xn, yn] = gridDim;
|
||||
const { width, height } = getVolumeTexture2dLayout(gridDim);
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -45,6 +45,8 @@ export interface Mesh {
|
||||
readonly normalBuffer: ValueCell<Float32Array>,
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
/** Indicates that group may vary within a triangle, wrapped in a value cell */
|
||||
readonly varyingGroup: ValueCell<boolean>,
|
||||
|
||||
/** Bounding sphere of the mesh */
|
||||
readonly boundingSphere: Sphere3D
|
||||
@@ -95,6 +97,7 @@ export namespace Mesh {
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
normalBuffer: ValueCell.create(normals),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
varyingGroup: ValueCell.create(false),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(mesh);
|
||||
if (newHash !== currentHash) {
|
||||
@@ -625,6 +628,7 @@ export namespace Mesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
|
||||
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
};
|
||||
@@ -665,7 +669,9 @@ export namespace Mesh {
|
||||
const positionIt = createPositionIterator(mesh, transform);
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
@@ -683,6 +689,7 @@ export namespace Mesh {
|
||||
aNormal: mesh.normalBuffer,
|
||||
aGroup: mesh.groupBuffer,
|
||||
elements: mesh.indexBuffer,
|
||||
dVaryingGroup: mesh.varyingGroup,
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
||||
@@ -700,6 +707,7 @@ export namespace Mesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
|
||||
@@ -720,6 +728,7 @@ export namespace Mesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type OverpaintType = 'instance' | 'groupInstance' | 'volumeInstance';
|
||||
|
||||
export type OverpaintData = {
|
||||
tOverpaint: ValueCell<TextureImage<Uint8Array>>
|
||||
uOverpaintTexDim: ValueCell<Vec2>
|
||||
@@ -34,12 +36,13 @@ export function clearOverpaint(array: Uint8Array, start: number, end: number) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData {
|
||||
export function createOverpaint(count: number, type: OverpaintType, overpaintData?: OverpaintData): OverpaintData {
|
||||
const overpaint = createTextureImage(Math.max(1, count), 4, Uint8Array, overpaintData && overpaintData.tOverpaint.ref.value.array);
|
||||
if (overpaintData) {
|
||||
ValueCell.update(overpaintData.tOverpaint, overpaint);
|
||||
ValueCell.update(overpaintData.uOverpaintTexDim, Vec2.create(overpaint.width, overpaint.height));
|
||||
ValueCell.updateIfChanged(overpaintData.dOverpaint, count > 0);
|
||||
ValueCell.updateIfChanged(overpaintData.dOverpaintType, type);
|
||||
return overpaintData;
|
||||
} else {
|
||||
return {
|
||||
@@ -50,7 +53,7 @@ export function createOverpaint(count: number, overpaintData?: OverpaintData): O
|
||||
tOverpaintGrid: ValueCell.create(createNullTexture()),
|
||||
uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dOverpaintType: ValueCell.create('groupInstance'),
|
||||
dOverpaintType: ValueCell.create(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,9 @@ export namespace Points {
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const size = createSizes(locationIt, theme.size);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -129,6 +129,7 @@ export namespace Spheres {
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
|
||||
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
};
|
||||
@@ -170,7 +171,9 @@ export namespace Spheres {
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const size = createSizes(locationIt, theme.size);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const material = createEmptySubstance();
|
||||
@@ -208,6 +211,7 @@ export namespace Spheres {
|
||||
uDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
};
|
||||
@@ -225,6 +229,7 @@ export namespace Spheres {
|
||||
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Material } from '../../mol-util/material';
|
||||
|
||||
export type SubstanceType = 'instance' | 'groupInstance' | 'volumeInstance';
|
||||
|
||||
export type SubstanceData = {
|
||||
tSubstance: ValueCell<TextureImage<Uint8Array>>
|
||||
uSubstanceTexDim: ValueCell<Vec2>
|
||||
@@ -34,12 +36,13 @@ export function clearSubstance(array: Uint8Array, start: number, end: number) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function createSubstance(count: number, substanceData?: SubstanceData): SubstanceData {
|
||||
export function createSubstance(count: number, type: SubstanceType, substanceData?: SubstanceData): SubstanceData {
|
||||
const substance = createTextureImage(Math.max(1, count), 4, Uint8Array, substanceData && substanceData.tSubstance.ref.value.array);
|
||||
if (substanceData) {
|
||||
ValueCell.update(substanceData.tSubstance, substance);
|
||||
ValueCell.update(substanceData.uSubstanceTexDim, Vec2.create(substance.width, substance.height));
|
||||
ValueCell.updateIfChanged(substanceData.dSubstance, count > 0);
|
||||
ValueCell.updateIfChanged(substanceData.dSubstanceType, type);
|
||||
return substanceData;
|
||||
} else {
|
||||
return {
|
||||
@@ -50,7 +53,7 @@ export function createSubstance(count: number, substanceData?: SubstanceData): S
|
||||
tSubstanceGrid: ValueCell.create(createNullTexture()),
|
||||
uSubstanceGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uSubstanceGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dSubstanceType: ValueCell.create('groupInstance'),
|
||||
dSubstanceType: ValueCell.create(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Text } from './text';
|
||||
import { getFontAtlas } from './font-atlas';
|
||||
import { assertUnreachable } from '../../../mol-util/type-helpers';
|
||||
|
||||
const quadIndices = new Uint16Array([
|
||||
0, 1, 2,
|
||||
@@ -237,7 +238,7 @@ export namespace TextBuilder {
|
||||
yBaseCenter = yTop;
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported attachment');
|
||||
assertUnreachable(attachment);
|
||||
}
|
||||
caAdd2(mappings, xTip, yTip); // tip
|
||||
caAdd2(mappings, xBaseA, yBaseA); // base A
|
||||
|
||||
@@ -211,7 +211,9 @@ export namespace Text {
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const size = createSizes(locationIt, theme.size);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const substance = createEmptySubstance();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -20,26 +20,25 @@ import { accumulate_frag } from '../../../mol-gl/shader/compute/color-smoothing/
|
||||
import { accumulate_vert } from '../../../mol-gl/shader/compute/color-smoothing/accumulate.vert';
|
||||
import { isWebGL2 } from '../../../mol-gl/webgl/compat';
|
||||
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
|
||||
import { isTimingMode } from '../../../mol-util/debug';
|
||||
|
||||
export const ColorAccumulateSchema = {
|
||||
drawCount: ValueSpec('number'),
|
||||
instanceCount: ValueSpec('number'),
|
||||
stride: ValueSpec('number'),
|
||||
|
||||
uTotalCount: UniformSpec('i'),
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i', 'material'),
|
||||
|
||||
aTransform: AttributeSpec('float32', 16, 1),
|
||||
aInstance: AttributeSpec('float32', 1, 1),
|
||||
aSample: AttributeSpec('float32', 1, 0),
|
||||
|
||||
uGeoTexDim: UniformSpec('v2', 'buffered'),
|
||||
tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
|
||||
uGeoTexDim: UniformSpec('v2', 'material'),
|
||||
tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
|
||||
tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
|
||||
|
||||
uColorTexDim: UniformSpec('v2'),
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uColorTexDim: UniformSpec('v2', 'material'),
|
||||
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest', 'material'),
|
||||
dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
|
||||
|
||||
uCurrentSlice: UniformSpec('f'),
|
||||
@@ -87,8 +86,6 @@ function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box:
|
||||
ValueCell.updateIfChanged(v.instanceCount, input.instanceCount);
|
||||
ValueCell.updateIfChanged(v.stride, stride);
|
||||
|
||||
ValueCell.updateIfChanged(v.uTotalCount, input.vertexCount);
|
||||
ValueCell.updateIfChanged(v.uInstanceCount, input.instanceCount);
|
||||
ValueCell.updateIfChanged(v.uGroupCount, input.groupCount);
|
||||
|
||||
ValueCell.update(v.aTransform, input.transformBuffer);
|
||||
@@ -125,8 +122,6 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
|
||||
instanceCount: ValueCell.create(input.instanceCount),
|
||||
stride: ValueCell.create(stride),
|
||||
|
||||
uTotalCount: ValueCell.create(input.vertexCount),
|
||||
uInstanceCount: ValueCell.create(input.instanceCount),
|
||||
uGroupCount: ValueCell.create(input.groupCount),
|
||||
|
||||
aTransform: ValueCell.create(input.transformBuffer),
|
||||
@@ -255,17 +250,20 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
const { drawBuffers } = webgl.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('calcTextureMeshColorSmoothing');
|
||||
const { gl, resources, state, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
|
||||
|
||||
const isInstanceType = input.colorType.endsWith('Instance');
|
||||
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
|
||||
const pad = 1 + resolution;
|
||||
const expandedBox = Box3D.expand(Box3D(), box, Vec3.create(pad, pad, pad));
|
||||
|
||||
const scaleFactor = 1 / resolution;
|
||||
const scaledBox = Box3D.scale(Box3D(), box, scaleFactor);
|
||||
const scaledBox = Box3D.scale(Box3D(), expandedBox, scaleFactor);
|
||||
const gridDim = Box3D.size(Vec3(), scaledBox);
|
||||
Vec3.ceil(gridDim, gridDim);
|
||||
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
|
||||
const { min } = box;
|
||||
const { min } = expandedBox;
|
||||
|
||||
const [dx, dy, dz] = gridDim;
|
||||
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
|
||||
@@ -308,7 +306,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
countTexture.attachFramebuffer(framebuffer, 1);
|
||||
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
|
||||
const accumulateRenderable = getAccumulateRenderable(webgl, input, expandedBox, resolution, stride);
|
||||
state.currentRenderItemId = -1;
|
||||
|
||||
framebuffer.bind();
|
||||
@@ -319,9 +317,10 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
|
||||
const { uCurrentSlice, uCurrentX, uCurrentY } = accumulateRenderable.values;
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('ColorAccumulate.render');
|
||||
setAccumulateDefaults(webgl);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
state.viewport(0, 0, width, height);
|
||||
state.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
ValueCell.update(uCurrentY, 0);
|
||||
let currCol = 0;
|
||||
@@ -337,8 +336,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
// console.log({ i, currX, currY });
|
||||
ValueCell.update(uCurrentX, currX);
|
||||
ValueCell.update(uCurrentSlice, i);
|
||||
gl.viewport(currX, currY, dx, dy);
|
||||
gl.scissor(currX, currY, dx, dy);
|
||||
state.viewport(currX, currY, dx, dy);
|
||||
state.scissor(currX, currY, dx, dy);
|
||||
accumulateRenderable.render();
|
||||
++currCol;
|
||||
currX += dx;
|
||||
@@ -347,6 +346,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
accumulateTexture.detachFramebuffer(framebuffer, 0);
|
||||
countTexture.detachFramebuffer(framebuffer, 1);
|
||||
drawBuffers.drawBuffers([gl.COLOR_ATTACHMENT0, gl.NONE]);
|
||||
if (isTimingMode) webgl.timer.markEnd('ColorAccumulate.render');
|
||||
|
||||
// const accImage = new Float32Array(width * height * 4);
|
||||
// accumulateTexture.attachFramebuffer(framebuffer, 0);
|
||||
@@ -362,6 +362,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
|
||||
// normalize
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('ColorNormalize.render');
|
||||
if (!texture) texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
|
||||
texture.define(width, height);
|
||||
|
||||
@@ -370,10 +371,11 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
|
||||
setNormalizeDefaults(webgl);
|
||||
texture.attachFramebuffer(framebuffer, 0);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
state.viewport(0, 0, width, height);
|
||||
state.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
normalizeRenderable.render();
|
||||
if (isTimingMode) webgl.timer.markEnd('ColorNormalize.render');
|
||||
|
||||
// const normImage = new Uint8Array(width * height * 4);
|
||||
// texture.attachFramebuffer(framebuffer, 0);
|
||||
@@ -383,6 +385,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
|
||||
|
||||
const gridTransform = Vec4.create(min[0], min[1], min[2], scaleFactor);
|
||||
const type = isInstanceType ? 'volumeInstance' : 'volume';
|
||||
if (isTimingMode) webgl.timer.markEnd('calcTextureMeshColorSmoothing');
|
||||
|
||||
return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -24,6 +24,7 @@ import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
|
||||
import { createEmptyClipping } from '../clipping-data';
|
||||
import { NullLocation } from '../../../mol-model/location';
|
||||
import { createEmptySubstance } from '../substance-data';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
|
||||
export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
@@ -37,6 +38,7 @@ export interface TextureMesh {
|
||||
readonly vertexTexture: ValueCell<Texture>,
|
||||
readonly groupTexture: ValueCell<Texture>,
|
||||
readonly normalTexture: ValueCell<Texture>,
|
||||
readonly varyingGroup: ValueCell<boolean>,
|
||||
readonly doubleBuffer: TextureMesh.DoubleBuffer
|
||||
|
||||
readonly boundingSphere: Sphere3D
|
||||
@@ -91,6 +93,7 @@ export namespace TextureMesh {
|
||||
vertexTexture: ValueCell.create(vertexTexture),
|
||||
groupTexture: ValueCell.create(groupTexture),
|
||||
normalTexture: ValueCell.create(normalTexture),
|
||||
varyingGroup: ValueCell.create(false),
|
||||
doubleBuffer: new DoubleBuffer(),
|
||||
boundingSphere: Sphere3D.clone(boundingSphere),
|
||||
meta: {}
|
||||
@@ -113,6 +116,7 @@ export namespace TextureMesh {
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
|
||||
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
|
||||
};
|
||||
@@ -125,8 +129,8 @@ export namespace TextureMesh {
|
||||
createValuesSimple,
|
||||
updateValues,
|
||||
updateBoundingSphere,
|
||||
createRenderableState: BaseGeometry.createRenderableState,
|
||||
updateRenderableState: BaseGeometry.updateRenderableState,
|
||||
createRenderableState,
|
||||
updateRenderableState,
|
||||
createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation)
|
||||
};
|
||||
|
||||
@@ -135,7 +139,9 @@ export namespace TextureMesh {
|
||||
const positionIt = Utils.createPositionIterator(textureMesh, transform);
|
||||
|
||||
const color = createColors(locationIt, positionIt, theme.color);
|
||||
const marker = createMarkers(instanceCount * groupCount);
|
||||
const marker = props.instanceGranularity
|
||||
? createMarkers(instanceCount, 'instance')
|
||||
: createMarkers(instanceCount * groupCount, 'groupInstance');
|
||||
const overpaint = createEmptyOverpaint();
|
||||
const transparency = createEmptyTransparency();
|
||||
const substance = createEmptySubstance();
|
||||
@@ -153,6 +159,7 @@ export namespace TextureMesh {
|
||||
tPosition: textureMesh.vertexTexture,
|
||||
tGroup: textureMesh.groupTexture,
|
||||
tNormal: textureMesh.normalTexture,
|
||||
dVaryingGroup: textureMesh.varyingGroup,
|
||||
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
||||
@@ -172,6 +179,7 @@ export namespace TextureMesh {
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
dXrayShaded: ValueCell.create(props.xrayShaded),
|
||||
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
|
||||
uBumpFrequency: ValueCell.create(props.bumpFrequency),
|
||||
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
|
||||
|
||||
@@ -192,6 +200,7 @@ export namespace TextureMesh {
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
||||
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
|
||||
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
|
||||
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
|
||||
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
|
||||
}
|
||||
@@ -208,4 +217,16 @@ export namespace TextureMesh {
|
||||
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
|
||||
}
|
||||
}
|
||||
|
||||
function createRenderableState(props: PD.Values<Params>): RenderableState {
|
||||
const state = BaseGeometry.createRenderableState(props);
|
||||
updateRenderableState(state, props);
|
||||
return state;
|
||||
}
|
||||
|
||||
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
BaseGeometry.updateRenderableState(state, props);
|
||||
state.opaque = state.opaque && !props.xrayShaded;
|
||||
state.writeDepth = state.opaque;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -9,6 +9,8 @@ import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
|
||||
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
|
||||
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
|
||||
export type TransparencyType = 'instance' | 'groupInstance' | 'volumeInstance';
|
||||
|
||||
export type TransparencyData = {
|
||||
tTransparency: ValueCell<TextureImage<Uint8Array>>
|
||||
uTransparencyTexDim: ValueCell<Vec2>
|
||||
@@ -29,6 +31,7 @@ export function applyTransparencyValue(array: Uint8Array, start: number, end: nu
|
||||
}
|
||||
|
||||
export function getTransparencyAverage(array: Uint8Array, count: number): number {
|
||||
if (count === 0 || array.length < count) return 0;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
sum += array[i];
|
||||
@@ -40,13 +43,14 @@ export function clearTransparency(array: Uint8Array, start: number, end: number)
|
||||
array.fill(0, start, end);
|
||||
}
|
||||
|
||||
export function createTransparency(count: number, transparencyData?: TransparencyData): TransparencyData {
|
||||
export function createTransparency(count: number, type: TransparencyType, transparencyData?: TransparencyData): TransparencyData {
|
||||
const transparency = createTextureImage(Math.max(1, count), 1, Uint8Array, transparencyData && transparencyData.tTransparency.ref.value.array);
|
||||
if (transparencyData) {
|
||||
ValueCell.update(transparencyData.tTransparency, transparency);
|
||||
ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height));
|
||||
ValueCell.updateIfChanged(transparencyData.dTransparency, count > 0);
|
||||
ValueCell.updateIfChanged(transparencyData.transparencyAverage, getTransparencyAverage(transparency.array, count));
|
||||
ValueCell.updateIfChanged(transparencyData.dTransparencyType, type);
|
||||
return transparencyData;
|
||||
} else {
|
||||
return {
|
||||
@@ -58,7 +62,7 @@ export function createTransparency(count: number, transparencyData?: Transparenc
|
||||
tTransparencyGrid: ValueCell.create(createNullTexture()),
|
||||
uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
|
||||
dTransparencyType: ValueCell.create('groupInstance'),
|
||||
dTransparencyType: ValueCell.create(type),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('renderer', () => {
|
||||
expect(ctx.gl.drawingBufferHeight).toBe(32);
|
||||
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(1);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(0);
|
||||
@@ -52,7 +52,7 @@ describe('renderer', () => {
|
||||
scene.add(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(8);
|
||||
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);
|
||||
@@ -60,7 +60,7 @@ describe('renderer', () => {
|
||||
scene.remove(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).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);
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -18,8 +18,9 @@ import { createComputeRenderItem } from '../webgl/render-item';
|
||||
import { createComputeRenderable } from '../renderable';
|
||||
import { isLittleEndian } from '../../mol-util/is-little-endian';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { isTimingMode } from '../../mol-util/debug';
|
||||
|
||||
export function canComputeGrid3dOnGPU(webgl?: WebGLContext) {
|
||||
export function canComputeGrid3dOnGPU(webgl?: WebGLContext): webgl is WebGLContext {
|
||||
return !!webgl?.extensions.textureFloat;
|
||||
}
|
||||
|
||||
@@ -159,7 +160,8 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
|
||||
|
||||
const array = new Uint8Array(uWidth * uWidth * 4);
|
||||
if (spec.cumulative) {
|
||||
const { gl } = webgl;
|
||||
const { gl, state } = webgl;
|
||||
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderCumulative');
|
||||
|
||||
const states = spec.cumulative.states(params);
|
||||
|
||||
@@ -167,7 +169,7 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
|
||||
tex[1].define(uWidth, uWidth);
|
||||
|
||||
resetGl(webgl, uWidth);
|
||||
gl.clearColor(0, 0, 0, 0);
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
|
||||
tex[0].attachFramebuffer(framebuffer, 'color0');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
@@ -175,12 +177,13 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
|
||||
tex[1].attachFramebuffer(framebuffer, 'color0');
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
if (spec.cumulative.yieldPeriod) {
|
||||
if (spec.cumulative.yieldPeriod && !isTimingMode) {
|
||||
await ctx.update({ message: 'Computing...', isIndeterminate: false, current: 0, max: states.length });
|
||||
}
|
||||
|
||||
const yieldPeriod = Math.max(1, spec.cumulative.yieldPeriod ?? 1 | 0);
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderBatch');
|
||||
for (let i = 0; i < states.length; i++) {
|
||||
ValueCell.update(cells.tCumulativeSum, tex[(i + 1) % 2]);
|
||||
tex[i % 2].attachFramebuffer(framebuffer, 'color0');
|
||||
@@ -191,31 +194,39 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
|
||||
|
||||
if (spec.cumulative.yieldPeriod && i !== states.length - 1) {
|
||||
if (i % yieldPeriod === yieldPeriod - 1) {
|
||||
webgl.readPixels(0, 0, 1, 1, array);
|
||||
webgl.waitForGpuCommandsCompleteSync();
|
||||
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderBatch');
|
||||
if (isTimingMode) webgl.timer.mark('Grid3dCompute.renderBatch');
|
||||
}
|
||||
if (ctx.shouldUpdate) {
|
||||
if (ctx.shouldUpdate && !isTimingMode) {
|
||||
await ctx.update({ current: i + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderBatch');
|
||||
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.renderCumulative');
|
||||
} else {
|
||||
if (isTimingMode) webgl.timer.mark('Grid3dCompute.render');
|
||||
tex[0].define(uWidth, uWidth);
|
||||
tex[0].attachFramebuffer(framebuffer, 'color0');
|
||||
framebuffer.bind();
|
||||
resetGl(webgl, uWidth);
|
||||
renderable.update();
|
||||
renderable.render();
|
||||
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.render');
|
||||
}
|
||||
|
||||
if (isTimingMode) webgl.timer.mark('Grid3dCompute.readPixels');
|
||||
webgl.readPixels(0, 0, uWidth, uWidth, array);
|
||||
if (isTimingMode) webgl.timer.markEnd('Grid3dCompute.readPixels');
|
||||
return new Float32Array(array.buffer, array.byteOffset, nx * ny * nz);
|
||||
};
|
||||
}
|
||||
|
||||
function resetGl(webgl: WebGLContext, w: number) {
|
||||
const { gl, state } = webgl;
|
||||
gl.viewport(0, 0, w, w);
|
||||
gl.scissor(0, 0, w, w);
|
||||
state.viewport(0, 0, w, w);
|
||||
state.scissor(0, 0, w, w);
|
||||
state.disable(gl.SCISSOR_TEST);
|
||||
state.disable(gl.BLEND);
|
||||
state.disable(gl.DEPTH_TEST);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -19,6 +19,7 @@ import { isPowerOfTwo } from '../../../mol-math/misc';
|
||||
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
|
||||
import { reduction_frag } from '../../../mol-gl/shader/histogram-pyramid/reduction.frag';
|
||||
import { isWebGL2 } from '../../webgl/compat';
|
||||
import { isTimingMode } from '../../../mol-util/debug';
|
||||
|
||||
const HistopyramidReductionSchema = {
|
||||
...QuadSchema,
|
||||
@@ -120,7 +121,8 @@ export interface HistogramPyramid {
|
||||
}
|
||||
|
||||
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
|
||||
const { gl } = ctx;
|
||||
if (isTimingMode) ctx.timer.mark('createHistogramPyramid');
|
||||
const { gl, state } = ctx;
|
||||
const w = inputTexture.getWidth();
|
||||
const h = inputTexture.getHeight();
|
||||
|
||||
@@ -144,7 +146,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
|
||||
const framebuffer = getFramebuffer('pyramid', ctx);
|
||||
pyramidTex.attachFramebuffer(framebuffer, 0);
|
||||
|
||||
gl.viewport(0, 0, maxSizeX, maxSizeY);
|
||||
state.viewport(0, 0, maxSizeX, maxSizeY);
|
||||
if (isWebGL2(gl)) {
|
||||
gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
|
||||
} else {
|
||||
@@ -155,7 +157,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
|
||||
for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i));
|
||||
|
||||
const renderable = getHistopyramidReductionRenderable(ctx, inputTexture, levelTexturesFramebuffers[0].texture);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
state.currentRenderItemId = -1;
|
||||
setRenderingDefaults(ctx);
|
||||
|
||||
let offset = 0;
|
||||
@@ -174,15 +176,15 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
|
||||
ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture);
|
||||
renderable.update();
|
||||
}
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
gl.viewport(0, 0, size, size);
|
||||
gl.scissor(0, 0, size, size);
|
||||
state.currentRenderItemId = -1;
|
||||
state.viewport(0, 0, size, size);
|
||||
state.scissor(0, 0, size, size);
|
||||
if (isWebGL2(gl)) {
|
||||
gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
|
||||
} else {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
}
|
||||
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
|
||||
state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
|
||||
renderable.render();
|
||||
|
||||
pyramidTex.bind(0);
|
||||
@@ -193,6 +195,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
|
||||
}
|
||||
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('createHistogramPyramid');
|
||||
|
||||
// printTexture(ctx, pyramidTex, 2)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -16,6 +16,7 @@ import { QuadSchema, QuadValues } from '../util';
|
||||
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
|
||||
import { sum_frag } from '../../../mol-gl/shader/histogram-pyramid/sum.frag';
|
||||
import { isWebGL2 } from '../../webgl/compat';
|
||||
import { isTimingMode } from '../../../mol-util/debug';
|
||||
|
||||
const HistopyramidSumSchema = {
|
||||
...QuadSchema,
|
||||
@@ -66,7 +67,8 @@ const sumBytes = new Uint8Array(4);
|
||||
const sumInts = new Int32Array(4);
|
||||
|
||||
export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
|
||||
const { gl, resources } = ctx;
|
||||
if (isTimingMode) ctx.timer.mark('getHistopyramidSum');
|
||||
const { gl, state, resources } = ctx;
|
||||
|
||||
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
@@ -87,12 +89,13 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture
|
||||
|
||||
setRenderingDefaults(ctx);
|
||||
|
||||
gl.viewport(0, 0, 1, 1);
|
||||
state.viewport(0, 0, 1, 1);
|
||||
renderable.render();
|
||||
gl.finish();
|
||||
|
||||
ctx.readPixels(0, 0, 1, 1, isWebGL2(gl) ? sumInts : sumBytes);
|
||||
ctx.unbindFramebuffer();
|
||||
if (isTimingMode) ctx.timer.markEnd('getHistopyramidSum');
|
||||
|
||||
return isWebGL2(gl)
|
||||
? sumInts[0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -16,6 +16,7 @@ import { QuadSchema, QuadValues } from '../util';
|
||||
import { getTriCount } from './tables';
|
||||
import { quad_vert } from '../../../mol-gl/shader/quad.vert';
|
||||
import { activeVoxels_frag } from '../../../mol-gl/shader/marching-cubes/active-voxels.frag';
|
||||
import { isTimingMode } from '../../../mol-util/debug';
|
||||
|
||||
const ActiveVoxelsSchema = {
|
||||
...QuadSchema,
|
||||
@@ -83,7 +84,8 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
|
||||
const { gl, resources } = ctx;
|
||||
if (isTimingMode) ctx.timer.mark('calcActiveVoxels');
|
||||
const { gl, state, resources } = ctx;
|
||||
const width = volumeData.getWidth();
|
||||
const height = volumeData.getHeight();
|
||||
|
||||
@@ -104,10 +106,10 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
|
||||
|
||||
activeVoxelsTex.attachFramebuffer(framebuffer, 0);
|
||||
setRenderingDefaults(ctx);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
state.viewport(0, 0, width, height);
|
||||
state.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
|
||||
state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
|
||||
renderable.render();
|
||||
|
||||
// console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim);
|
||||
@@ -115,6 +117,7 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
|
||||
// console.log('at', readTexture(ctx, activeVoxelsTex));
|
||||
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('calcActiveVoxels');
|
||||
|
||||
return activeVoxelsTex;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 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>
|
||||
*/
|
||||
@@ -19,6 +19,7 @@ import { quad_vert } from '../../../mol-gl/shader/quad.vert';
|
||||
import { isosurface_frag } from '../../../mol-gl/shader/marching-cubes/isosurface.frag';
|
||||
import { calcActiveVoxels } from './active-voxels';
|
||||
import { isWebGL2 } from '../../webgl/compat';
|
||||
import { isTimingMode } from '../../../mol-util/debug';
|
||||
|
||||
const IsosurfaceSchema = {
|
||||
...QuadSchema,
|
||||
@@ -41,12 +42,13 @@ const IsosurfaceSchema = {
|
||||
|
||||
dPackedGroup: DefineSpec('boolean'),
|
||||
dAxisOrder: DefineSpec('string', ['012', '021', '102', '120', '201', '210']),
|
||||
dConstantGroup: DefineSpec('boolean'),
|
||||
};
|
||||
type IsosurfaceValues = Values<typeof IsosurfaceSchema>
|
||||
|
||||
const IsosurfaceName = 'isosurface';
|
||||
|
||||
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3): ComputeRenderable<IsosurfaceValues> {
|
||||
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
|
||||
if (ctx.namedComputeRenderables[IsosurfaceName]) {
|
||||
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
|
||||
|
||||
@@ -65,17 +67,18 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
|
||||
ValueCell.update(v.uGridTransform, transform);
|
||||
ValueCell.update(v.uScale, scale);
|
||||
|
||||
ValueCell.update(v.dPackedGroup, packedGroup);
|
||||
ValueCell.updateIfChanged(v.dPackedGroup, packedGroup);
|
||||
ValueCell.updateIfChanged(v.dAxisOrder, axisOrder.join(''));
|
||||
ValueCell.updateIfChanged(v.dConstantGroup, constantGroup);
|
||||
|
||||
ctx.namedComputeRenderables[IsosurfaceName].update();
|
||||
} else {
|
||||
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
|
||||
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
|
||||
}
|
||||
return ctx.namedComputeRenderables[IsosurfaceName];
|
||||
}
|
||||
|
||||
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3) {
|
||||
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
|
||||
// console.log('uSize', Math.pow(2, levels))
|
||||
const values: IsosurfaceValues = {
|
||||
...QuadValues,
|
||||
@@ -98,6 +101,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
|
||||
|
||||
dPackedGroup: ValueCell.create(packedGroup),
|
||||
dAxisOrder: ValueCell.create(axisOrder.join('')),
|
||||
dConstantGroup: ValueCell.create(constantGroup),
|
||||
};
|
||||
|
||||
const schema = { ...IsosurfaceSchema };
|
||||
@@ -118,11 +122,12 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { drawBuffers } = ctx.extensions;
|
||||
if (!drawBuffers) throw new Error('need WebGL draw buffers');
|
||||
|
||||
const { gl, resources, extensions } = ctx;
|
||||
if (isTimingMode) ctx.timer.mark('createIsosurfaceBuffers');
|
||||
const { gl, state, resources, extensions } = ctx;
|
||||
const { pyramidTex, height, levels, scale, count } = histogramPyramid;
|
||||
const width = pyramidTex.getWidth();
|
||||
|
||||
@@ -176,7 +181,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
groupTexture.attachFramebuffer(framebuffer, 1);
|
||||
normalTexture.attachFramebuffer(framebuffer, 2);
|
||||
|
||||
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
|
||||
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
|
||||
ctx.state.currentRenderItemId = -1;
|
||||
|
||||
framebuffer.bind();
|
||||
@@ -187,11 +192,12 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
]);
|
||||
|
||||
setRenderingDefaults(ctx);
|
||||
gl.viewport(0, 0, width, height);
|
||||
state.viewport(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
renderable.render();
|
||||
|
||||
gl.finish();
|
||||
if (isTimingMode) ctx.timer.markEnd('createIsosurfaceBuffers');
|
||||
|
||||
return { vertexTexture, groupTexture, normalTexture, vertexCount: count };
|
||||
}
|
||||
@@ -207,21 +213,12 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
*
|
||||
* Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
|
||||
*/
|
||||
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
// console.time('calcActiveVoxels');
|
||||
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
|
||||
if (isTimingMode) ctx.timer.mark('extractIsosurface');
|
||||
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
|
||||
// ctx.waitForGpuCommandsCompleteSync();
|
||||
// console.timeEnd('calcActiveVoxels');
|
||||
|
||||
// console.time('createHistogramPyramid');
|
||||
const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
|
||||
// ctx.waitForGpuCommandsCompleteSync();
|
||||
// console.timeEnd('createHistogramPyramid');
|
||||
|
||||
// console.time('createIsosurfaceBuffers');
|
||||
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, vertexTexture, groupTexture, normalTexture);
|
||||
// ctx.waitForGpuCommandsCompleteSync();
|
||||
// console.timeEnd('createIsosurfaceBuffers');
|
||||
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
|
||||
if (isTimingMode) ctx.timer.markEnd('extractIsosurface');
|
||||
|
||||
return gv;
|
||||
}
|
||||
@@ -125,8 +125,8 @@ export function readAlphaTexture(ctx: WebGLContext, texture: Texture) {
|
||||
state.clearColor(0, 0, 0, 0);
|
||||
state.blendFunc(gl.ONE, gl.ONE);
|
||||
state.blendEquation(gl.FUNC_ADD);
|
||||
gl.viewport(0, 0, width, height);
|
||||
gl.scissor(0, 0, width, height);
|
||||
state.viewport(0, 0, width, height);
|
||||
state.scissor(0, 0, width, height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
copy.render();
|
||||
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -10,7 +10,6 @@ import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant } from './
|
||||
import { ValueCell } from '../mol-util';
|
||||
import { idFactory } from '../mol-util/id-factory';
|
||||
import { clamp } from '../mol-math/interpolate';
|
||||
import { Textures } from './webgl/texture';
|
||||
|
||||
const getNextRenderableId = idFactory();
|
||||
|
||||
@@ -30,7 +29,7 @@ export interface Renderable<T extends RenderableValues> {
|
||||
readonly values: T
|
||||
readonly state: RenderableState
|
||||
|
||||
render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => void
|
||||
render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => void
|
||||
getProgram: (variant: GraphicsRenderVariant) => Program
|
||||
update: () => void
|
||||
dispose: () => void
|
||||
@@ -43,11 +42,11 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
|
||||
values,
|
||||
state,
|
||||
|
||||
render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => {
|
||||
render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => {
|
||||
if (values.uAlpha && values.alpha) {
|
||||
ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1));
|
||||
}
|
||||
renderItem.render(variant, sharedTexturesList);
|
||||
renderItem.render(variant, sharedTexturesCount);
|
||||
},
|
||||
getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant),
|
||||
update: () => renderItem.update(),
|
||||
@@ -73,7 +72,7 @@ export function createComputeRenderable<T extends Values<RenderableSchema>>(rend
|
||||
id: getNextRenderableId(),
|
||||
values,
|
||||
|
||||
render: () => renderItem.render('compute'),
|
||||
render: () => renderItem.render('compute', 0),
|
||||
update: () => renderItem.update(),
|
||||
dispose: () => renderItem.destroy()
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2021 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>
|
||||
*/
|
||||
@@ -23,11 +23,12 @@ export const CylindersSchema = {
|
||||
elements: ElementsSpec('uint32'),
|
||||
|
||||
padding: ValueSpec('number'),
|
||||
uDoubleSided: UniformSpec('b'),
|
||||
uDoubleSided: UniformSpec('b', 'material'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
dXrayShaded: DefineSpec('boolean'),
|
||||
uBumpFrequency: UniformSpec('f'),
|
||||
uBumpAmplitude: UniformSpec('f'),
|
||||
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
|
||||
uBumpFrequency: UniformSpec('f', 'material'),
|
||||
uBumpAmplitude: UniformSpec('f', 'material'),
|
||||
};
|
||||
export type CylindersSchema = typeof CylindersSchema
|
||||
export type CylindersValues = Values<CylindersSchema>
|
||||
|
||||