mirror of
https://github.com/molstar/molstar.git
synced 2026-06-06 14:44:22 +08:00
Compare commits
719 Commits
v0.2.7
...
v0.5.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d31f1ba07 | ||
|
|
d722d17a02 | ||
|
|
522d929f5a | ||
|
|
42037074cb | ||
|
|
1e3f17efdf | ||
|
|
7389e0075d | ||
|
|
a080114690 | ||
|
|
1293848d9b | ||
|
|
16b2d9e873 | ||
|
|
7a64334261 | ||
|
|
009b20c95a | ||
|
|
296bea1b88 | ||
|
|
e0775607cc | ||
|
|
a1fb4b8bf3 | ||
|
|
29ae78c193 | ||
|
|
46cfb42cce | ||
|
|
94ef9f4dbe | ||
|
|
b864e992ef | ||
|
|
57ed9a4226 | ||
|
|
b2c93cbeda | ||
|
|
60540dadee | ||
|
|
c0236650f0 | ||
|
|
fb3017cfff | ||
|
|
929c91a48c | ||
|
|
bfd9595c1c | ||
|
|
c091f9a8ae | ||
|
|
d5b71b302b | ||
|
|
67103232be | ||
|
|
f471fc3d2b | ||
|
|
2a0783d005 | ||
|
|
7234ad261b | ||
|
|
001d1fb8d3 | ||
|
|
35a56bf37c | ||
|
|
9715ff061e | ||
|
|
9866db9ced | ||
|
|
fc2288a583 | ||
|
|
11d1bbe222 | ||
|
|
bc07256d8e | ||
|
|
3826bdb0c3 | ||
|
|
9609dddd47 | ||
|
|
ad7b318da0 | ||
|
|
66c76ea6b5 | ||
|
|
29e47c1e90 | ||
|
|
e08a074a1c | ||
|
|
d7ebb30e05 | ||
|
|
8330068434 | ||
|
|
42dea4a2eb | ||
|
|
4de0ae6628 | ||
|
|
5a714af309 | ||
|
|
20f73fdae7 | ||
|
|
aca91cf18f | ||
|
|
974a7d7520 | ||
|
|
f9d7545f7d | ||
|
|
0e135c4645 | ||
|
|
961f847765 | ||
|
|
b83fed4701 | ||
|
|
651fd2aca7 | ||
|
|
2dd863e711 | ||
|
|
bac5ea7ea6 | ||
|
|
312db7eec3 | ||
|
|
31850b3a71 | ||
|
|
ee35e39611 | ||
|
|
872130d65c | ||
|
|
b903554f52 | ||
|
|
c3fba20780 | ||
|
|
870d2da8ed | ||
|
|
22d17f6c8e | ||
|
|
6bbf12a660 | ||
|
|
ab4ae742db | ||
|
|
7725071ae2 | ||
|
|
c7ab6ebec7 | ||
|
|
d56abadf4c | ||
|
|
7eb2952ec5 | ||
|
|
21cf2d5437 | ||
|
|
5af0c448c6 | ||
|
|
2ca9392c5a | ||
|
|
a5a6f2dcc4 | ||
|
|
91f5207d68 | ||
|
|
c89848dec0 | ||
|
|
116d3ec319 | ||
|
|
20266a229b | ||
|
|
0a26e84f8f | ||
|
|
93bfa7a575 | ||
|
|
c09991573c | ||
|
|
556b7b26d4 | ||
|
|
a011600766 | ||
|
|
2e5439c385 | ||
|
|
3d6ae08437 | ||
|
|
03228f7952 | ||
|
|
e46a8c4369 | ||
|
|
01ef92a795 | ||
|
|
61a5d18be6 | ||
|
|
9c2c48bd59 | ||
|
|
df6d49b2ac | ||
|
|
2476bf76b5 | ||
|
|
1c4a397249 | ||
|
|
4930018a55 | ||
|
|
205f4c31d6 | ||
|
|
89d3c87919 | ||
|
|
d0a861d39c | ||
|
|
9f7b96c727 | ||
|
|
e1d2a8b41d | ||
|
|
e71bf9c288 | ||
|
|
693ea3a40e | ||
|
|
921d23e73f | ||
|
|
709944c859 | ||
|
|
9341c19bd5 | ||
|
|
1cee84d9af | ||
|
|
bea5fe5474 | ||
|
|
c473f2b284 | ||
|
|
d5c163ac48 | ||
|
|
85dcef1b2e | ||
|
|
8c1acc6758 | ||
|
|
fbb7f0a6a1 | ||
|
|
3b7c8963df | ||
|
|
d58f4a73b6 | ||
|
|
09cfd85603 | ||
|
|
846a301122 | ||
|
|
6a29925733 | ||
|
|
5633350b63 | ||
|
|
5d5ffcdb36 | ||
|
|
a819835984 | ||
|
|
227721bfd3 | ||
|
|
31e2cc5f07 | ||
|
|
3f02cf0561 | ||
|
|
a57a9f0386 | ||
|
|
11a6df6e19 | ||
|
|
8d5e1feac9 | ||
|
|
e19e3d7380 | ||
|
|
78c7664be3 | ||
|
|
39b29fe0ea | ||
|
|
5636edc1d1 | ||
|
|
7dbdc75cad | ||
|
|
fd92c916b7 | ||
|
|
f5e880839e | ||
|
|
6e70172c45 | ||
|
|
22563bf671 | ||
|
|
cad95403aa | ||
|
|
d375595e08 | ||
|
|
b16d13cc35 | ||
|
|
f03471ee39 | ||
|
|
6f1f65487d | ||
|
|
b863aaedb3 | ||
|
|
b77994d01f | ||
|
|
30e01c07f7 | ||
|
|
1e018b82df | ||
|
|
49e6966037 | ||
|
|
85b1df46cd | ||
|
|
6908a2cd2b | ||
|
|
4379d818ab | ||
|
|
4434dfb79a | ||
|
|
9621f67e84 | ||
|
|
21a0684353 | ||
|
|
3601955433 | ||
|
|
d91483c485 | ||
|
|
8b3d07906f | ||
|
|
5f8a4b6be4 | ||
|
|
f8ff919787 | ||
|
|
6dfe975fc7 | ||
|
|
2650f8d3dd | ||
|
|
f172b6ceaa | ||
|
|
8086af1bf7 | ||
|
|
5813840e17 | ||
|
|
e2d595394a | ||
|
|
9b24e6fb1f | ||
|
|
0f33144935 | ||
|
|
6d384166d5 | ||
|
|
8b766dc242 | ||
|
|
1fa64c836c | ||
|
|
af700c1481 | ||
|
|
7dcf16cd97 | ||
|
|
f86b46076c | ||
|
|
6fe58d7b73 | ||
|
|
8c2a4b5cae | ||
|
|
b2c31f5166 | ||
|
|
0f4d0ff986 | ||
|
|
2b73f9cafd | ||
|
|
a86438a265 | ||
|
|
c0b5102d31 | ||
|
|
feab3a38f9 | ||
|
|
b1fb9c5c47 | ||
|
|
9fb65f46a1 | ||
|
|
7a2e85b856 | ||
|
|
f85e3e76fd | ||
|
|
c6ef02d0a6 | ||
|
|
81a6e3cf4e | ||
|
|
48a75927f6 | ||
|
|
067a85ef88 | ||
|
|
c4757313e6 | ||
|
|
88d8998bd2 | ||
|
|
97e6884487 | ||
|
|
8fd56dbc38 | ||
|
|
f9c97ad5cb | ||
|
|
f893aba522 | ||
|
|
f2740aaee2 | ||
|
|
fb15cc135a | ||
|
|
d9579914b4 | ||
|
|
5a98bfd8ef | ||
|
|
149e6ebf84 | ||
|
|
74c8e512a7 | ||
|
|
52bf9b87fa | ||
|
|
ac8d71215e | ||
|
|
c7cfedb874 | ||
|
|
faae40c9e8 | ||
|
|
288912ccea | ||
|
|
26e47c3e33 | ||
|
|
15a8ea6598 | ||
|
|
aeda6d3312 | ||
|
|
79f430efb3 | ||
|
|
4fe70de2ff | ||
|
|
56fd6d6a5b | ||
|
|
b5076edc8d | ||
|
|
4fbb9e232c | ||
|
|
b6324d9cee | ||
|
|
f668f725e8 | ||
|
|
e45041f2f2 | ||
|
|
d285076acb | ||
|
|
9047aae87c | ||
|
|
44b00edcb6 | ||
|
|
a476c2a167 | ||
|
|
1b2b168624 | ||
|
|
ce238bcd1d | ||
|
|
c119a82d83 | ||
|
|
4b97686a26 | ||
|
|
c13350b098 | ||
|
|
c9c6df4861 | ||
|
|
2e73681aae | ||
|
|
42b4c25ca3 | ||
|
|
5a5939408f | ||
|
|
7041bac8ca | ||
|
|
f8ab02fdab | ||
|
|
b75ba4b4aa | ||
|
|
baa80d08dd | ||
|
|
144d40cd38 | ||
|
|
c6b4610adf | ||
|
|
f2c5cd978b | ||
|
|
63823698c7 | ||
|
|
f6ca679a57 | ||
|
|
142c46c127 | ||
|
|
1bd59523b2 | ||
|
|
ef67925858 | ||
|
|
5dae376bcb | ||
|
|
14ed325579 | ||
|
|
b007409521 | ||
|
|
5830252df7 | ||
|
|
02ad9587fc | ||
|
|
4596336c13 | ||
|
|
2a413256ed | ||
|
|
8b9178249d | ||
|
|
1d0ba36d7c | ||
|
|
3a8211937f | ||
|
|
0d576f7011 | ||
|
|
d90e81b0e5 | ||
|
|
89be1767e9 | ||
|
|
aee6921140 | ||
|
|
a55572d52f | ||
|
|
525deafd83 | ||
|
|
8064b969be | ||
|
|
6d6e12bbd0 | ||
|
|
825ae48000 | ||
|
|
764aef3181 | ||
|
|
d226abadc1 | ||
|
|
ccd1fa3a0f | ||
|
|
65a692e3bf | ||
|
|
2daac955f4 | ||
|
|
162797e43a | ||
|
|
784b29805a | ||
|
|
67b60cff60 | ||
|
|
28443de11f | ||
|
|
59cf60c2ba | ||
|
|
8e14a1e2ec | ||
|
|
7dd2e5ec98 | ||
|
|
bd3e052130 | ||
|
|
6fbe7efcab | ||
|
|
572a574bf3 | ||
|
|
347a0a43ea | ||
|
|
28abd00a6e | ||
|
|
06354d6d1d | ||
|
|
2b31d54e18 | ||
|
|
58644b3d5c | ||
|
|
4c732b3b2d | ||
|
|
36d3a0728c | ||
|
|
b0c696e401 | ||
|
|
2a28b5421f | ||
|
|
269176e0a6 | ||
|
|
cd23a68268 | ||
|
|
48e81c8b10 | ||
|
|
649c294929 | ||
|
|
ab0dcb0de9 | ||
|
|
cb2fc8f95b | ||
|
|
5deec4391d | ||
|
|
1873e506e0 | ||
|
|
11e660a81a | ||
|
|
635ba2a7e3 | ||
|
|
1a2fb55d03 | ||
|
|
a5785e526e | ||
|
|
af9d50f5ec | ||
|
|
ac06b9c018 | ||
|
|
36d8df1442 | ||
|
|
a312c4ef1d | ||
|
|
0c6b80d370 | ||
|
|
16f9129acc | ||
|
|
1af9554cc7 | ||
|
|
cc8999cbc9 | ||
|
|
c7b4099758 | ||
|
|
f060fc228f | ||
|
|
8d3c091d2e | ||
|
|
cabf7c1613 | ||
|
|
b44149bbaf | ||
|
|
18befade7f | ||
|
|
8fd8eb703b | ||
|
|
fa0c29c1d1 | ||
|
|
65ed5c98bf | ||
|
|
f0ecf74aad | ||
|
|
c56ed0af28 | ||
|
|
ddb8231c00 | ||
|
|
bc24e8f236 | ||
|
|
2edb31e7ea | ||
|
|
ad619d9b8d | ||
|
|
61f738ce3d | ||
|
|
73ae95ed06 | ||
|
|
6d4c1aa3ea | ||
|
|
34f230a882 | ||
|
|
a68d81e495 | ||
|
|
9687c38e06 | ||
|
|
f69670c368 | ||
|
|
a0b537ef64 | ||
|
|
e8663b5bfc | ||
|
|
9f12fbde70 | ||
|
|
940b16ebd2 | ||
|
|
dc5aa0c30d | ||
|
|
b70b9c44fc | ||
|
|
88a4cf1aa4 | ||
|
|
49eefa131f | ||
|
|
9f4a60572f | ||
|
|
8f9579bcaf | ||
|
|
845dd4115d | ||
|
|
3e2a1227ce | ||
|
|
dcfec014a5 | ||
|
|
f5598ba93e | ||
|
|
a5fadb6e8a | ||
|
|
738ba2d6fd | ||
|
|
ebcc45d1fd | ||
|
|
272a911d35 | ||
|
|
a725e577be | ||
|
|
a36ead9ef1 | ||
|
|
f516072d12 | ||
|
|
a54b36b057 | ||
|
|
a18484a5dd | ||
|
|
e685c0a342 | ||
|
|
a3b1d11bb3 | ||
|
|
7a7643c6bc | ||
|
|
3643bd04f1 | ||
|
|
65c2faaced | ||
|
|
32a91ca98c | ||
|
|
9f6e98a5d0 | ||
|
|
0964a9fd50 | ||
|
|
a0b865cd07 | ||
|
|
a5f73b5c20 | ||
|
|
d10be352be | ||
|
|
6f3d8ddd96 | ||
|
|
1a14e65dcf | ||
|
|
2757dfdb75 | ||
|
|
1b5526235e | ||
|
|
d4836c5fde | ||
|
|
530810e366 | ||
|
|
9f2a4828f4 | ||
|
|
70cc3a612e | ||
|
|
6917ba6230 | ||
|
|
710c06672e | ||
|
|
5d9e14aad1 | ||
|
|
9963cb3770 | ||
|
|
5731ef20b3 | ||
|
|
ab89d34440 | ||
|
|
d9bac2916c | ||
|
|
edaa9610f6 | ||
|
|
90255b1a1b | ||
|
|
639f137ad2 | ||
|
|
29609e04c2 | ||
|
|
736aaf4433 | ||
|
|
2d7248962f | ||
|
|
a13c98c36c | ||
|
|
e298499326 | ||
|
|
6e1e463e1d | ||
|
|
2a2261beaf | ||
|
|
14623f5df2 | ||
|
|
32889779a2 | ||
|
|
911a7e3636 | ||
|
|
bcf8ae2734 | ||
|
|
92df3b1f42 | ||
|
|
5bbed1f9a3 | ||
|
|
0a391760b8 | ||
|
|
e7eeb8cb55 | ||
|
|
bee4f38209 | ||
|
|
ff4dcfc220 | ||
|
|
ad726a97e4 | ||
|
|
b533542792 | ||
|
|
dbe33cc046 | ||
|
|
3ddf5f4bec | ||
|
|
f02d49f181 | ||
|
|
4df68697f2 | ||
|
|
eeb09d9184 | ||
|
|
d8509a145b | ||
|
|
e3cb799638 | ||
|
|
ce9469c85b | ||
|
|
e62b166805 | ||
|
|
a1f283c641 | ||
|
|
2b14c398a8 | ||
|
|
d8480d058b | ||
|
|
b1ac1a36d4 | ||
|
|
3c2d014ad3 | ||
|
|
d940d26312 | ||
|
|
99028b5c99 | ||
|
|
ac94f0659d | ||
|
|
bd85a5a153 | ||
|
|
9aa375a45f | ||
|
|
999cb99eb8 | ||
|
|
12b53bc4bb | ||
|
|
952f3c451f | ||
|
|
ce06375a92 | ||
|
|
c0f60d48bb | ||
|
|
e40f08d467 | ||
|
|
9c977bf90f | ||
|
|
525f32feab | ||
|
|
cdb698f0d1 | ||
|
|
c65d09f4a8 | ||
|
|
7d4e48431c | ||
|
|
6a7268a9c9 | ||
|
|
eea1137abc | ||
|
|
a130121698 | ||
|
|
153f039ee6 | ||
|
|
b7216d28f4 | ||
|
|
ee34139094 | ||
|
|
9addea79b7 | ||
|
|
2c88ace91c | ||
|
|
6eae95f964 | ||
|
|
b44ed7c45a | ||
|
|
b2d2546e39 | ||
|
|
4dbabdf350 | ||
|
|
df63d8fe0f | ||
|
|
c9116575fa | ||
|
|
6707673c7f | ||
|
|
1f82446843 | ||
|
|
9942b0e389 | ||
|
|
f57e8501c4 | ||
|
|
1e9fa003db | ||
|
|
7868b22918 | ||
|
|
e2c80956dc | ||
|
|
5d5af58bdf | ||
|
|
8f78106816 | ||
|
|
1923289c51 | ||
|
|
14c22147d5 | ||
|
|
6d39bc4a20 | ||
|
|
cfaecb590d | ||
|
|
2894c18ba1 | ||
|
|
e6633c2fc6 | ||
|
|
fe341f0851 | ||
|
|
34a387d4bd | ||
|
|
f975d9c8dc | ||
|
|
563ff98c83 | ||
|
|
0b9ecb76b9 | ||
|
|
b8de1de3a8 | ||
|
|
ac2f48684e | ||
|
|
9928b17a76 | ||
|
|
723a6a1fbd | ||
|
|
51ac75943d | ||
|
|
6bcb5eac71 | ||
|
|
f2f1e355c2 | ||
|
|
4cc0754f11 | ||
|
|
cd30caa12d | ||
|
|
41bc74e543 | ||
|
|
96a908698d | ||
|
|
77a561024f | ||
|
|
5d0176e686 | ||
|
|
3a6013145e | ||
|
|
c5e2471f34 | ||
|
|
e9802f2191 | ||
|
|
2d0097fddc | ||
|
|
1a245db4c9 | ||
|
|
6a694f9298 | ||
|
|
acd1f43018 | ||
|
|
71ed9a2db9 | ||
|
|
2b5d9bd213 | ||
|
|
32c8664829 | ||
|
|
26e6e0ab72 | ||
|
|
6d9c9bd884 | ||
|
|
2a74d5eb94 | ||
|
|
04d39e0e8d | ||
|
|
c5e64b39db | ||
|
|
4911585a85 | ||
|
|
590c00114f | ||
|
|
111eded34a | ||
|
|
d767900fb1 | ||
|
|
9074d5390a | ||
|
|
91027459ca | ||
|
|
a2eda6c5af | ||
|
|
338489febd | ||
|
|
94e7820baf | ||
|
|
a47df57709 | ||
|
|
f73d64f732 | ||
|
|
cfaef4c567 | ||
|
|
3ec2c6ded4 | ||
|
|
2afaa35170 | ||
|
|
efcf4a77c6 | ||
|
|
221e1de4e7 | ||
|
|
f2de2983c8 | ||
|
|
82c8499789 | ||
|
|
d66ccdb255 | ||
|
|
8d13c514b0 | ||
|
|
f57aafba19 | ||
|
|
0a7406db15 | ||
|
|
2f97e8b329 | ||
|
|
22a8758852 | ||
|
|
bc6bc1d57a | ||
|
|
4a692b9a88 | ||
|
|
0094f800dc | ||
|
|
9bd616f36f | ||
|
|
4acc36628d | ||
|
|
e550413778 | ||
|
|
ccdc894979 | ||
|
|
322bc71d52 | ||
|
|
7c55e4d234 | ||
|
|
8c8058290c | ||
|
|
9d413bf0eb | ||
|
|
8fde5dd1bf | ||
|
|
2f9ecb9396 | ||
|
|
85092279fa | ||
|
|
d0189523e4 | ||
|
|
a26d03205a | ||
|
|
862eda6dd4 | ||
|
|
4a7f0fc206 | ||
|
|
6bbee58d39 | ||
|
|
989faed410 | ||
|
|
198e2f2043 | ||
|
|
6ae3d4110d | ||
|
|
c34e1be43b | ||
|
|
17a440ad9c | ||
|
|
04dcfc7adb | ||
|
|
ca66e334c1 | ||
|
|
54bba4c92f | ||
|
|
9c046b808c | ||
|
|
ca5d1865cc | ||
|
|
d8f1aa5bc9 | ||
|
|
6ac790e083 | ||
|
|
d0870e4bbb | ||
|
|
5138595f36 | ||
|
|
d9aa5684a9 | ||
|
|
8e2521a7a9 | ||
|
|
7dd7a117cb | ||
|
|
defbadf4d7 | ||
|
|
131e88080a | ||
|
|
fb78b886c1 | ||
|
|
aed0b87b16 | ||
|
|
1316cc6a40 | ||
|
|
944d370c14 | ||
|
|
74f9aa6af6 | ||
|
|
c20c9c9917 | ||
|
|
4801435d72 | ||
|
|
33fd105ef7 | ||
|
|
3ea3fb8984 | ||
|
|
b4bbc544ca | ||
|
|
5f880e920b | ||
|
|
bcce801dd7 | ||
|
|
00f9dcee4a | ||
|
|
505af2bc96 | ||
|
|
c217aab5fc | ||
|
|
5d5fd0028f | ||
|
|
c88693dfdd | ||
|
|
0a16ec1bd2 | ||
|
|
6f36a3c724 | ||
|
|
71496bd1ef | ||
|
|
c5a99a7c12 | ||
|
|
cfaf33d696 | ||
|
|
e24c76d2bf | ||
|
|
b6273205a2 | ||
|
|
b38193aa19 | ||
|
|
f9cfacae23 | ||
|
|
ac46317dc6 | ||
|
|
2b492a5a61 | ||
|
|
a2133657f0 | ||
|
|
e8de45789f | ||
|
|
3d2bd167ca | ||
|
|
504c8626dc | ||
|
|
f4b29dc7e0 | ||
|
|
b34c5c743b | ||
|
|
c57311d6c0 | ||
|
|
4d786dc697 | ||
|
|
062e3e055a | ||
|
|
1465174a45 | ||
|
|
cc00ada5a3 | ||
|
|
cbf312b62d | ||
|
|
2be3144086 | ||
|
|
da3acd9d19 | ||
|
|
34b048479b | ||
|
|
ca92931bf2 | ||
|
|
0d3daeb823 | ||
|
|
58b1d7e0eb | ||
|
|
c5997ed056 | ||
|
|
da1deee7f3 | ||
|
|
a4eaff3175 | ||
|
|
211cfc0bd3 | ||
|
|
c0f85b691d | ||
|
|
6b93d58ea6 | ||
|
|
5bce423b49 | ||
|
|
01b0dde503 | ||
|
|
ed1bc8cb7d | ||
|
|
abe559261b | ||
|
|
b0cdf22cb8 | ||
|
|
a61ba71f1e | ||
|
|
7061d57559 | ||
|
|
83a1e6c87c | ||
|
|
c18888b8de | ||
|
|
2d80935e00 | ||
|
|
4287d158b6 | ||
|
|
4e9b569178 | ||
|
|
5d626d291b | ||
|
|
afa4a01c44 | ||
|
|
3c4a23c5a3 | ||
|
|
8d92c976d9 | ||
|
|
1a9adfad29 | ||
|
|
a9e9a5974d | ||
|
|
27160aa8fe | ||
|
|
6a71af00cf | ||
|
|
6c0938db50 | ||
|
|
b76173c82f | ||
|
|
0bcf2b1ff4 | ||
|
|
d074415a26 | ||
|
|
42f3e38026 | ||
|
|
74c16ee7ba | ||
|
|
f6ef22b917 | ||
|
|
c3f3d7efda | ||
|
|
61d44efdc4 | ||
|
|
ad200c86ec | ||
|
|
4aecf4e0b4 | ||
|
|
d81f37c78b | ||
|
|
c2979ce5ab | ||
|
|
09c7edce88 | ||
|
|
beefb79258 | ||
|
|
529c6ac81c | ||
|
|
84b988ea96 | ||
|
|
35e978efc9 | ||
|
|
19559d01f7 | ||
|
|
56cec343e2 | ||
|
|
20c9bd0130 | ||
|
|
1336997c58 | ||
|
|
b178fdefdc | ||
|
|
453d60060a | ||
|
|
901fac97a0 | ||
|
|
29b6a88343 | ||
|
|
a2217c7fc6 | ||
|
|
3eec30aa42 | ||
|
|
f352e19e90 | ||
|
|
be65ef89bc | ||
|
|
cfc46073c0 | ||
|
|
1819a2bbda | ||
|
|
aa3a42f94e | ||
|
|
4bff55b612 | ||
|
|
dbc4e09909 | ||
|
|
0a074c8a66 | ||
|
|
8b314ebb75 | ||
|
|
4dc0791aeb | ||
|
|
e3483f11b1 | ||
|
|
244a678ab7 | ||
|
|
8e1d44fabc | ||
|
|
954a5b58e0 | ||
|
|
53223bc72b | ||
|
|
a11a1fa07e | ||
|
|
9ab4001544 | ||
|
|
3e3b71d230 | ||
|
|
186929269b | ||
|
|
afa7a04af0 | ||
|
|
2861d12f04 | ||
|
|
65e1212b2f | ||
|
|
47136c8b71 | ||
|
|
375b829562 | ||
|
|
2718d42b01 | ||
|
|
93d4118c0a | ||
|
|
3a2a47af12 | ||
|
|
0eacdfca85 | ||
|
|
a7388be25f | ||
|
|
5fcdcb1275 | ||
|
|
4635bdffb0 | ||
|
|
72b1c36111 | ||
|
|
fc5ff601a8 | ||
|
|
8dd142fc9f | ||
|
|
97f24293e2 | ||
|
|
f4beba5215 | ||
|
|
e2abe0f52a | ||
|
|
5d18643374 | ||
|
|
769f2a30c2 | ||
|
|
0e040d7744 | ||
|
|
7600d0a44e | ||
|
|
9113d6d189 | ||
|
|
fd9dac86b9 | ||
|
|
fe1f3bd4bb | ||
|
|
d950718110 | ||
|
|
4be903b9d5 | ||
|
|
8050644869 | ||
|
|
347c1986df | ||
|
|
eac4a8988b | ||
|
|
25137f29d2 | ||
|
|
4601fe74bb | ||
|
|
f0b54e9cbf | ||
|
|
fdb45c3624 | ||
|
|
f3b1ec0ed8 | ||
|
|
79510614b9 | ||
|
|
f8c9bc6812 | ||
|
|
85d35aab90 | ||
|
|
f5b09dbd10 | ||
|
|
57ea322fd6 | ||
|
|
c31aab5594 | ||
|
|
a5556e8c41 | ||
|
|
0ce2966c47 | ||
|
|
f2c04a13af | ||
|
|
60ba0de219 | ||
|
|
99e515604e | ||
|
|
84dfa60fc2 | ||
|
|
a753095f92 | ||
|
|
ce0ff2fed8 |
65
.eslintrc.json
Normal file
65
.eslintrc.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-types": "warn",
|
||||
"@typescript-eslint/class-name-casing": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
"warn",
|
||||
4
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"off",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "none",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-namespace-keyword": "warn",
|
||||
"@typescript-eslint/quotes": [
|
||||
"warn",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"off",
|
||||
null
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "warn",
|
||||
"arrow-parens": [
|
||||
"off",
|
||||
"as-needed"
|
||||
],
|
||||
"comma-dangle": "off",
|
||||
"eqeqeq": [
|
||||
"warn",
|
||||
"smart"
|
||||
],
|
||||
"import/order": "off",
|
||||
"no-eval": "warn",
|
||||
"no-new-wrappers": "warn",
|
||||
"no-trailing-spaces": "warn",
|
||||
"no-unsafe-finally": "warn",
|
||||
"no-var": "warn",
|
||||
"spaced-comment": "warn"
|
||||
}
|
||||
}
|
||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -4,7 +4,9 @@
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
"wayou.vscode-todo-highlight"
|
||||
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Mol* Viewer",
|
||||
"url": "http://localhost:1338/build/viewer/index.html",
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -6,4 +6,7 @@
|
||||
"*.vert.ts": "glsl",
|
||||
"*.gql.ts": "graphql"
|
||||
},
|
||||
"eslint.options": {
|
||||
"ignorePattern": ["webpack.config.js", "scripts/*"],
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,14 @@ The core of Mol* currently consists of these modules (see under `src/`):
|
||||
- `mol-state` State representation tree with state saving and automatic updates.
|
||||
- `mol-app` Components for builduing UIs.
|
||||
- `mol-plugin` Allow to define modular Mol* plugin instances utilizing `mol-state` and `mol-canvas3d`.
|
||||
- `mol-plugin-ui` React based user interface for the Mol* plugin. Some components of the UI are usable outside the main plugin and can be integrated to 3rd party solutions.
|
||||
- `mol-util` Useful things that do not fit elsewhere.
|
||||
|
||||
Moreover, the project contains the imlementation of `servers`, including
|
||||
|
||||
- `servers/model` A tool for accessing coordinate and annotation data of molecular structures.
|
||||
- `servers/volume` A tool for accessing volumetric experimental data related to molecular structures.
|
||||
- `servers/plugin-state` A basic server to store Mol* Plugin states.
|
||||
|
||||
The project also contains performance tests (`perf-tests`), `examples`, and basic proof of concept `apps` (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
|
||||
|
||||
@@ -62,6 +64,8 @@ This project builds on experience from previous solutions:
|
||||
### Build with debug mode enabled:
|
||||
DEBUG=molstar npm run watch
|
||||
|
||||
Debug/production mode in browsers can be turned on/off during runtime by calling ``setMolStarDebugMode(true/false, true/false)`` from the dev console.
|
||||
|
||||
### Build for production:
|
||||
NODE_ENV=production npm run build
|
||||
|
||||
@@ -87,10 +91,11 @@ Install CIFTools `npm install ciftools -g`
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
|
||||
|
||||
**GraphQL schemas**
|
||||
|
||||
node data/rcsb-graphql/codegen.js
|
||||
./node_modules/.bin/graphql-codegen -c ./data/rcsb-graphql/codegen.yml
|
||||
|
||||
### Other scripts
|
||||
**Create chem comp bond table**
|
||||
@@ -143,7 +148,7 @@ To get syntax highlighting for shader and graphql files add the following to Vis
|
||||
|
||||
## Deploy
|
||||
npm run test
|
||||
NODE_ENV=production npm run build
|
||||
npm run build
|
||||
node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
const { generate } = require('graphql-code-generator')
|
||||
const path = require('path')
|
||||
|
||||
const basePath = path.join(__dirname, '..', '..', 'src', 'mol-model-props', 'rcsb', 'graphql')
|
||||
|
||||
generate({
|
||||
schema: 'http://rest-staging.rcsb.org/graphql',
|
||||
documents: {
|
||||
[path.join(basePath, 'symmetry.gql.ts')]: {
|
||||
loader: path.join(__dirname, 'loader.js')
|
||||
},
|
||||
},
|
||||
generates: {
|
||||
[path.join(basePath, 'types.ts')]: {
|
||||
plugins: ['time', 'typescript-common', 'typescript-client']
|
||||
}
|
||||
},
|
||||
overwrite: true,
|
||||
config: path.join(__dirname, 'codegen.json')
|
||||
}, true).then(
|
||||
() => console.log('done')
|
||||
).catch(e => console.error(e))
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"flattenTypes": true,
|
||||
"generatorConfig": {
|
||||
"immutableTypes": true
|
||||
}
|
||||
}
|
||||
12
data/rcsb-graphql/codegen.yml
Normal file
12
data/rcsb-graphql/codegen.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
schema: https://data-beta.rcsb.org/graphql
|
||||
documents: './src/mol-model-props/rcsb/graphql/symmetry.gql.ts'
|
||||
generates:
|
||||
'./src/mol-model-props/rcsb/graphql/types.d.ts':
|
||||
plugins:
|
||||
- add: '/* eslint-disable */'
|
||||
- time
|
||||
- typescript
|
||||
- typescript-operations
|
||||
config:
|
||||
immutableTypes: true
|
||||
skipTypename: true
|
||||
@@ -1,14 +0,0 @@
|
||||
const { parse } = require('graphql');
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
module.exports = function(docString, config) {
|
||||
const str = readFileSync(docString, { encoding: 'utf-8' }).trim()
|
||||
.replace(/^export default `/, '')
|
||||
.replace(/`$/, '')
|
||||
return [
|
||||
{
|
||||
filePath: docString,
|
||||
content: parse(str)
|
||||
}
|
||||
];
|
||||
};
|
||||
884
examples/867861-core.cif
Normal file
884
examples/867861-core.cif
Normal file
@@ -0,0 +1,884 @@
|
||||
#######################################################################
|
||||
#
|
||||
# This file contains crystal structure data downloaded from the
|
||||
# Cambridge Structural Database (CSD) hosted by the Cambridge
|
||||
# Crystallographic Data Centre (CCDC).
|
||||
#
|
||||
# Full information about CCDC data access policies and citation
|
||||
# guidelines are available at http://www.ccdc.cam.ac.uk/access/V1
|
||||
#
|
||||
# Audit and citation data items may have been added by the CCDC.
|
||||
# Please retain this information to preserve the provenance of
|
||||
# this file and to allow appropriate attribution of the data.
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
data_n1379
|
||||
_audit_block_doi 10.5517/ccy42jn
|
||||
_database_code_depnum_ccdc_archive 'CCDC 867861'
|
||||
loop_
|
||||
_citation_id
|
||||
_citation_doi
|
||||
_citation_year
|
||||
1 10.1002/chem.201202070 2012
|
||||
_audit_update_record
|
||||
;
|
||||
2012-02-20 deposited with the CCDC.
|
||||
2016-10-08 downloaded from the CCDC.
|
||||
;
|
||||
|
||||
_audit_creation_method SHELXL-97
|
||||
_chemical_name_systematic
|
||||
;
|
||||
?
|
||||
;
|
||||
_chemical_name_common ?
|
||||
_chemical_melting_point ?
|
||||
_chemical_formula_moiety 'C76 H90 N10 O14 4(C2 F3 O2) 4(C2 H3 N)'
|
||||
_chemical_formula_sum 'C92 H102 F12 N14 O22'
|
||||
_chemical_formula_weight 1983.88
|
||||
|
||||
loop_
|
||||
_atom_type_symbol
|
||||
_atom_type_description
|
||||
_atom_type_scat_dispersion_real
|
||||
_atom_type_scat_dispersion_imag
|
||||
_atom_type_scat_source
|
||||
C C 0.0181 0.0091 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
|
||||
H H 0.0000 0.0000 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
|
||||
N N 0.0311 0.0180 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
|
||||
O O 0.0492 0.0322 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
|
||||
F F 0.0727 0.0534 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
|
||||
|
||||
_symmetry_cell_setting Triclinic
|
||||
_symmetry_space_group_name_H-M P-1
|
||||
|
||||
loop_
|
||||
_symmetry_equiv_pos_as_xyz
|
||||
'x, y, z'
|
||||
'-x, -y, -z'
|
||||
|
||||
_cell_length_a 11.0829(8)
|
||||
_cell_length_b 14.6829(10)
|
||||
_cell_length_c 16.8532(17)
|
||||
_cell_angle_alpha 105.728(6)
|
||||
_cell_angle_beta 100.310(6)
|
||||
_cell_angle_gamma 110.620(4)
|
||||
_cell_volume 2353.3(3)
|
||||
_cell_formula_units_Z 1
|
||||
_cell_measurement_temperature 100(2)
|
||||
_cell_measurement_reflns_used 5934
|
||||
_cell_measurement_theta_min 2.86
|
||||
_cell_measurement_theta_max 64.30
|
||||
|
||||
_exptl_crystal_description plate
|
||||
_exptl_crystal_colour violet
|
||||
_exptl_crystal_size_max 0.57
|
||||
_exptl_crystal_size_mid 0.18
|
||||
_exptl_crystal_size_min 0.05
|
||||
_exptl_crystal_density_meas ?
|
||||
_exptl_crystal_density_diffrn 1.400
|
||||
_exptl_crystal_density_method ?
|
||||
_exptl_crystal_F_000 1036
|
||||
_exptl_absorpt_coefficient_mu 0.995
|
||||
_exptl_absorpt_correction_type integration
|
||||
_exptl_absorpt_correction_T_min 0.6022
|
||||
_exptl_absorpt_correction_T_max 0.9482
|
||||
_exptl_absorpt_process_details 'XPREP, face-indexed'
|
||||
|
||||
_exptl_special_details
|
||||
;
|
||||
?
|
||||
;
|
||||
|
||||
_diffrn_ambient_temperature 100(2)
|
||||
_diffrn_radiation_wavelength 1.54178
|
||||
_diffrn_radiation_type CuK\a
|
||||
_diffrn_radiation_source microsource
|
||||
_diffrn_radiation_monochromator 'Quazar optics'
|
||||
_diffrn_measurement_device_type 'Bruker APEX-II CCD'
|
||||
_diffrn_measurement_method '\f and \w scans'
|
||||
_diffrn_detector_area_resol_mean ?
|
||||
_diffrn_reflns_number 16613
|
||||
_diffrn_reflns_av_R_equivalents 0.1477
|
||||
_diffrn_reflns_av_sigmaI/netI 0.1112
|
||||
_diffrn_reflns_limit_h_min -12
|
||||
_diffrn_reflns_limit_h_max 8
|
||||
_diffrn_reflns_limit_k_min -17
|
||||
_diffrn_reflns_limit_k_max 17
|
||||
_diffrn_reflns_limit_l_min -19
|
||||
_diffrn_reflns_limit_l_max 19
|
||||
_diffrn_reflns_theta_min 2.86
|
||||
_diffrn_reflns_theta_max 64.94
|
||||
_reflns_number_total 7680
|
||||
_reflns_number_gt 5560
|
||||
_reflns_threshold_expression >2sigma(I)
|
||||
|
||||
_computing_data_collection 'Bruker APEX2'
|
||||
_computing_cell_refinement 'Bruker SAINT'
|
||||
_computing_data_reduction 'Bruker SAINT'
|
||||
_computing_structure_solution 'SHELXS-97 (Sheldrick, 2008)'
|
||||
_computing_structure_refinement 'SHELXL-97 (Sheldrick, 2008)'
|
||||
_computing_molecular_graphics 'Bruker SHELXTL'
|
||||
_computing_publication_material 'Bruker SHELXTL'
|
||||
|
||||
_refine_special_details
|
||||
;
|
||||
Refinement of F^2^ against ALL reflections. The weighted R-factor wR and
|
||||
goodness of fit S are based on F^2^, conventional R-factors R are based
|
||||
on F, with F set to zero for negative F^2^. The threshold expression of
|
||||
F^2^ > 2sigma(F^2^) is used only for calculating R-factors(gt) etc. and is
|
||||
not relevant to the choice of reflections for refinement. R-factors based
|
||||
on F^2^ are statistically about twice as large as those based on F, and R-
|
||||
factors based on ALL data will be even larger.
|
||||
Rigid bond restraints (esd 0.002) were imposed on the displacement parameters,
|
||||
as well as restraints on similar amplitudes (esd 0.002) separated by less
|
||||
than 1.7 Ang. on C27, C29, and N102.
|
||||
Distance restraints were refined on the bond between C28 and C29.
|
||||
;
|
||||
|
||||
_refine_ls_structure_factor_coef Fsqd
|
||||
_refine_ls_matrix_type full
|
||||
_refine_ls_weighting_scheme calc
|
||||
_refine_ls_weighting_details
|
||||
'calc w=1/[\s^2^(Fo^2^)+(0.1912P)^2^+4.8134P] where P=(Fo^2^+2Fc^2^)/3'
|
||||
_atom_sites_solution_primary direct
|
||||
_atom_sites_solution_secondary difmap
|
||||
_atom_sites_solution_hydrogens geom
|
||||
_refine_ls_hydrogen_treatment mixed
|
||||
_refine_ls_extinction_method none
|
||||
_refine_ls_extinction_coef ?
|
||||
_refine_ls_number_reflns 7680
|
||||
_refine_ls_number_parameters 633
|
||||
_refine_ls_number_restraints 1
|
||||
_refine_ls_R_factor_all 0.1349
|
||||
_refine_ls_R_factor_gt 0.1101
|
||||
_refine_ls_wR_factor_ref 0.3402
|
||||
_refine_ls_wR_factor_gt 0.3102
|
||||
_refine_ls_goodness_of_fit_ref 1.089
|
||||
_refine_ls_restrained_S_all 1.096
|
||||
_refine_ls_shift/su_max 0.000
|
||||
_refine_ls_shift/su_mean 0.000
|
||||
|
||||
loop_
|
||||
_atom_site_label
|
||||
_atom_site_type_symbol
|
||||
_atom_site_fract_x
|
||||
_atom_site_fract_y
|
||||
_atom_site_fract_z
|
||||
_atom_site_U_iso_or_equiv
|
||||
_atom_site_adp_type
|
||||
_atom_site_occupancy
|
||||
_atom_site_symmetry_multiplicity
|
||||
_atom_site_calc_flag
|
||||
_atom_site_refinement_flags
|
||||
_atom_site_disorder_assembly
|
||||
_atom_site_disorder_group
|
||||
C1 C -0.3373(4) -0.2086(3) -0.2547(3) 0.0229(10) Uani 1 1 d . . .
|
||||
H1 H -0.3820 -0.2827 -0.2795 0.027 Uiso 1 1 calc R . .
|
||||
C2 C -0.1956(4) -0.0511(3) -0.2602(3) 0.0215(10) Uani 1 1 d . . .
|
||||
H2 H -0.1401 -0.0147 -0.2886 0.026 Uiso 1 1 calc R . .
|
||||
C3 C -0.3583(4) -0.1574(3) -0.1814(3) 0.0213(9) Uani 1 1 d . . .
|
||||
H3 H -0.4168 -0.1960 -0.1558 0.026 Uiso 1 1 calc R . .
|
||||
C4 C -0.2146(4) 0.0039(3) -0.1873(3) 0.0180(9) Uani 1 1 d . . .
|
||||
H4 H -0.1736 0.0780 -0.1660 0.022 Uiso 1 1 calc R . .
|
||||
C5 C -0.2943(4) -0.0488(3) -0.1440(3) 0.0163(9) Uani 1 1 d . . .
|
||||
C6 C -0.3053(4) 0.0091(3) -0.0600(3) 0.0170(9) Uani 1 1 d . . .
|
||||
C7 C -0.3538(4) -0.0444(3) -0.0071(3) 0.0193(9) Uani 1 1 d . . .
|
||||
H7 H -0.3850 -0.1181 -0.0264 0.023 Uiso 1 1 calc R . .
|
||||
C8 C -0.2620(4) 0.1168(3) -0.0291(3) 0.0194(9) Uani 1 1 d . . .
|
||||
H8 H -0.2304 0.1553 -0.0640 0.023 Uiso 1 1 calc R . .
|
||||
C9 C -0.3563(4) 0.0094(3) 0.0725(3) 0.0218(10) Uani 1 1 d . . .
|
||||
H9 H -0.3904 -0.0276 0.1077 0.026 Uiso 1 1 calc R . .
|
||||
C10 C -0.2648(4) 0.1679(3) 0.0515(3) 0.0190(9) Uani 1 1 d . . .
|
||||
H10 H -0.2340 0.2416 0.0724 0.023 Uiso 1 1 calc R . .
|
||||
C11 C -0.3024(4) 0.1707(4) 0.1919(3) 0.0227(10) Uani 1 1 d . . .
|
||||
H11A H -0.3772 0.1277 0.2088 0.027 Uiso 1 1 calc R . .
|
||||
H11B H -0.3094 0.2371 0.1964 0.027 Uiso 1 1 calc R . .
|
||||
C12 C -0.1670(4) 0.1920(3) 0.2509(2) 0.0184(9) Uani 1 1 d . . .
|
||||
C13 C -0.1547(5) 0.1179(4) 0.2849(3) 0.0223(10) Uani 1 1 d . . .
|
||||
H13 H -0.2328 0.0589 0.2788 0.027 Uiso 1 1 calc R . .
|
||||
C14 C -0.0541(4) 0.2821(3) 0.2667(2) 0.0204(9) Uani 1 1 d . . .
|
||||
H14 H -0.0631 0.3352 0.2475 0.024 Uiso 1 1 calc R . .
|
||||
C15 C -0.0272(5) 0.1313(4) 0.3277(3) 0.0231(10) Uani 1 1 d . . .
|
||||
H15 H -0.0192 0.0806 0.3506 0.028 Uiso 1 1 calc R . .
|
||||
C16 C 0.0731(4) 0.2946(3) 0.3110(2) 0.0195(9) Uani 1 1 d . . .
|
||||
C17 C 0.0890(4) 0.2163(3) 0.3381(2) 0.0204(10) Uani 1 1 d . . .
|
||||
C18 C 0.2252(5) 0.2149(4) 0.3687(3) 0.0273(11) Uani 1 1 d . . .
|
||||
H18A H 0.2965 0.2872 0.3968 0.033 Uiso 1 1 calc R . .
|
||||
H18B H 0.2237 0.1802 0.4116 0.033 Uiso 1 1 calc R . .
|
||||
C19 C 0.2862(4) 0.4582(4) 0.3974(3) 0.0247(10) Uani 1 1 d . . .
|
||||
H19 H 0.3032 0.4532 0.4530 0.030 Uiso 1 1 calc R . .
|
||||
C20 C 0.3577(5) 0.5371(4) 0.3739(3) 0.0279(11) Uani 1 1 d . . .
|
||||
C21 C 0.4805(5) 0.6358(4) 0.4267(3) 0.0333(12) Uani 1 1 d . . .
|
||||
H21A H 0.4882 0.6869 0.3975 0.040 Uiso 1 1 calc R . .
|
||||
H21B H 0.4699 0.6656 0.4837 0.040 Uiso 1 1 calc R . .
|
||||
C22 C 0.6303(6) 0.5857(4) 0.3599(3) 0.0374(12) Uani 1 1 d . . .
|
||||
H22A H 0.6523 0.6416 0.3355 0.045 Uiso 1 1 calc R . .
|
||||
H22B H 0.5494 0.5242 0.3176 0.045 Uiso 1 1 calc R . .
|
||||
C23 C 0.7460(6) 0.5575(5) 0.3762(4) 0.0413(13) Uani 1 1 d . . .
|
||||
H23A H 0.7730 0.5406 0.3231 0.050 Uiso 1 1 calc R . .
|
||||
H23B H 0.8246 0.6169 0.4226 0.050 Uiso 1 1 calc R . .
|
||||
C24 C 0.7952(6) 0.4206(5) 0.3967(4) 0.0496(16) Uani 1 1 d . . .
|
||||
H24A H 0.8893 0.4743 0.4264 0.060 Uiso 1 1 calc R . .
|
||||
H24B H 0.7870 0.3874 0.3353 0.060 Uiso 1 1 calc R . .
|
||||
C25 C 0.7663(11) 0.3428(7) 0.4361(4) 0.074(3) Uani 1 1 d . . .
|
||||
H25A H 0.8372 0.3165 0.4378 0.089 Uiso 1 1 calc R . .
|
||||
H25B H 0.7691 0.3754 0.4965 0.089 Uiso 1 1 calc R . .
|
||||
C26 C 0.6441(8) 0.1838(7) 0.4342(5) 0.071(2) Uani 1 1 d . . .
|
||||
H26A H 0.7153 0.1600 0.4249 0.085 Uiso 1 1 calc R . .
|
||||
H26B H 0.6577 0.2137 0.4970 0.085 Uiso 1 1 calc R . .
|
||||
C27 C 0.5204(10) 0.1080(8) 0.3921(7) 0.125(5) Uani 1 1 d . . .
|
||||
H27A H 0.5220 0.0408 0.3913 0.150 Uiso 1 1 calc R . .
|
||||
H27B H 0.4964 0.1039 0.3313 0.150 Uiso 1 1 calc R . .
|
||||
C28 C 0.3730(9) 0.0886(7) 0.4988(5) 0.097(4) Uani 1 1 d D . .
|
||||
H28A H 0.4284 0.0506 0.5097 0.116 Uiso 1 1 calc R . .
|
||||
H28B H 0.4078 0.1526 0.5510 0.116 Uiso 1 1 calc R . .
|
||||
C29 C 0.2559(11) 0.033(2) 0.4964(8) 0.281(16) Uani 1 1 d D . .
|
||||
H29A H 0.2452 0.0690 0.5518 0.337 Uiso 1 1 calc R . .
|
||||
H29B H 0.2589 -0.0322 0.5000 0.337 Uiso 1 1 calc R . .
|
||||
C30 C 0.0569(12) -0.1026(9) 0.4169(5) 0.089(3) Uani 1 1 d . . .
|
||||
H30A H 0.0442 -0.1134 0.4708 0.107 Uiso 1 1 calc R . .
|
||||
H30B H 0.1035 -0.1448 0.3928 0.107 Uiso 1 1 calc R . .
|
||||
C31 C -0.0702(10) -0.1360(6) 0.3569(6) 0.083(3) Uani 1 1 d . . .
|
||||
H31A H -0.1307 -0.2064 0.3530 0.100 Uiso 1 1 calc R . .
|
||||
H31B H -0.1113 -0.0878 0.3776 0.100 Uiso 1 1 calc R . .
|
||||
C32 C -0.1964(9) -0.1774(5) 0.2168(5) 0.067(2) Uani 1 1 d . . .
|
||||
H32A H -0.2564 -0.2441 0.2195 0.080 Uiso 1 1 calc R . .
|
||||
H32B H -0.2342 -0.1258 0.2343 0.080 Uiso 1 1 calc R . .
|
||||
C33 C -0.1881(7) -0.1926(5) 0.1297(4) 0.0593(19) Uani 1 1 d . . .
|
||||
H33A H -0.2803 -0.2327 0.0880 0.071 Uiso 1 1 calc R . .
|
||||
H33B H -0.1354 -0.2340 0.1169 0.071 Uiso 1 1 calc R . .
|
||||
C34 C -0.0909(4) -0.0989(4) 0.0440(3) 0.0231(10) Uani 1 1 d . . .
|
||||
C35 C -0.1178(5) -0.1907(4) -0.0206(3) 0.0290(11) Uani 1 1 d . . .
|
||||
H35 H -0.1660 -0.2560 -0.0162 0.035 Uiso 1 1 calc R . .
|
||||
C36 C -0.0214(4) -0.0005(3) 0.0371(3) 0.0182(9) Uani 1 1 d . . .
|
||||
C37 C 0.0055(4) 0.0950(3) 0.1019(3) 0.0212(10) Uani 1 1 d . . .
|
||||
H37 H -0.0240 0.0948 0.1515 0.025 Uiso 1 1 calc R . .
|
||||
C38 C 0.0734(4) 0.1868(4) 0.0931(3) 0.0249(10) Uani 1 1 d . . .
|
||||
H38 H 0.0914 0.2503 0.1372 0.030 Uiso 1 1 calc R . .
|
||||
N1 N -0.2553(3) -0.1567(3) -0.2921(2) 0.0202(8) Uani 1 1 d . . .
|
||||
N2 N -0.3112(3) 0.1137(3) 0.1013(2) 0.0181(8) Uani 1 1 d . . .
|
||||
N3 N 0.1854(4) 0.3880(3) 0.3242(2) 0.0221(8) Uani 1 1 d . . .
|
||||
N4 N 0.1947(4) 0.4236(3) 0.2574(2) 0.0281(9) Uani 1 1 d . . .
|
||||
N5 N 0.2991(4) 0.5136(3) 0.2881(3) 0.0315(10) Uani 1 1 d . . .
|
||||
O1 O 0.6028(3) 0.6210(3) 0.4395(2) 0.0301(8) Uani 1 1 d . . .
|
||||
O2 O 0.7045(4) 0.4689(3) 0.4018(2) 0.0429(10) Uani 1 1 d . . .
|
||||
O3 O 0.6381(7) 0.2580(4) 0.3907(4) 0.0743(17) Uani 1 1 d . . .
|
||||
O4 O 0.4041(4) 0.1218(3) 0.4321(2) 0.0412(10) Uani 1 1 d . . .
|
||||
O5 O 0.1363(5) 0.0023(6) 0.4355(3) 0.0786(19) Uani 1 1 d . . .
|
||||
O6 O -0.0597(6) -0.1394(4) 0.2749(3) 0.0757(17) Uani 1 1 d . . .
|
||||
O7 O -0.1244(3) -0.0939(3) 0.1185(2) 0.0320(8) Uani 1 1 d . . .
|
||||
C101 C 0.6294(5) 0.3676(4) 0.1211(3) 0.0286(11) Uani 1 1 d . . .
|
||||
C102 C 0.5798(8) 0.4458(5) 0.1022(5) 0.066(2) Uani 1 1 d . . .
|
||||
C103 C 0.5077(5) 0.8961(4) 0.2466(3) 0.0342(12) Uani 1 1 d . . .
|
||||
C104 C 0.3819(8) 0.8078(6) 0.2433(5) 0.077(3) Uani 1 1 d . . .
|
||||
C105 C 0.0707(7) 0.4215(5) 0.0313(4) 0.0516(16) Uani 1 1 d . . .
|
||||
C106 C 0.2104(7) 0.4969(6) 0.0645(5) 0.066(2) Uani 1 1 d . . .
|
||||
H10A H 0.2627 0.4718 0.0303 0.099 Uiso 1 1 calc R . .
|
||||
H10B H 0.2471 0.5066 0.1251 0.099 Uiso 1 1 calc R . .
|
||||
H10C H 0.2161 0.5637 0.0606 0.099 Uiso 1 1 calc R . .
|
||||
C107 C 0.0569(7) 0.6249(7) 0.2999(5) 0.071(2) Uani 1 1 d . . .
|
||||
C108 C 0.0316(8) 0.5978(7) 0.2128(5) 0.077(2) Uani 1 1 d . . .
|
||||
H10D H 0.0994 0.5753 0.1954 0.115 Uiso 1 1 calc R . .
|
||||
H10E H 0.0360 0.6581 0.1969 0.115 Uiso 1 1 calc R . .
|
||||
H10F H -0.0588 0.5404 0.1833 0.115 Uiso 1 1 calc R . .
|
||||
O101 O 0.5421(4) 0.2774(3) 0.0882(3) 0.0463(10) Uani 1 1 d . . .
|
||||
O102 O 0.7472(4) 0.4014(3) 0.1666(3) 0.0485(11) Uani 1 1 d . . .
|
||||
O103 O 0.4882(4) 0.9389(3) 0.1955(2) 0.0356(9) Uani 1 1 d . . .
|
||||
O104 O 0.6107(4) 0.9176(3) 0.3029(3) 0.0517(11) Uani 1 1 d . . .
|
||||
F101 F 0.4877(8) 0.4530(7) 0.1408(4) 0.148(3) Uani 1 1 d . . .
|
||||
F102 F 0.5188(6) 0.4177(4) 0.0191(3) 0.105(2) Uani 1 1 d . . .
|
||||
F103 F 0.6733(8) 0.5407(3) 0.1298(5) 0.160(4) Uani 1 1 d . . .
|
||||
F104 F 0.2811(5) 0.7655(4) 0.1734(3) 0.098(2) Uani 1 1 d . . .
|
||||
F105 F 0.3328(8) 0.8406(9) 0.3070(4) 0.233(7) Uani 1 1 d . . .
|
||||
F106 F 0.4002(9) 0.7351(6) 0.2617(7) 0.224(6) Uani 1 1 d . . .
|
||||
N101 N -0.0392(7) 0.3613(5) 0.0045(4) 0.0715(19) Uani 1 1 d . . .
|
||||
N102 N 0.0774(10) 0.6479(13) 0.3716(6) 0.185(7) Uani 1 1 d . . .
|
||||
|
||||
loop_
|
||||
_atom_site_aniso_label
|
||||
_atom_site_aniso_U_11
|
||||
_atom_site_aniso_U_22
|
||||
_atom_site_aniso_U_33
|
||||
_atom_site_aniso_U_23
|
||||
_atom_site_aniso_U_13
|
||||
_atom_site_aniso_U_12
|
||||
C1 0.015(2) 0.023(2) 0.022(2) -0.0003(18) -0.0032(18) 0.0097(17)
|
||||
C2 0.021(2) 0.032(2) 0.012(2) 0.0047(17) -0.0022(17) 0.0168(19)
|
||||
C3 0.0099(19) 0.030(2) 0.021(2) 0.0059(18) -0.0007(17) 0.0104(17)
|
||||
C4 0.017(2) 0.027(2) 0.0111(19) 0.0045(16) -0.0012(16) 0.0157(18)
|
||||
C5 0.0087(18) 0.025(2) 0.0134(19) 0.0028(16) -0.0032(15) 0.0121(16)
|
||||
C6 0.0075(18) 0.031(2) 0.014(2) 0.0067(17) -0.0017(15) 0.0130(17)
|
||||
C7 0.0074(18) 0.029(2) 0.019(2) 0.0047(17) 0.0003(16) 0.0093(17)
|
||||
C8 0.0135(19) 0.028(2) 0.013(2) 0.0039(17) -0.0035(16) 0.0120(17)
|
||||
C9 0.013(2) 0.031(2) 0.020(2) 0.0072(18) 0.0012(17) 0.0105(18)
|
||||
C10 0.0112(19) 0.029(2) 0.018(2) 0.0066(17) 0.0025(16) 0.0122(17)
|
||||
C11 0.019(2) 0.038(2) 0.014(2) 0.0056(18) 0.0059(17) 0.0178(19)
|
||||
C12 0.019(2) 0.029(2) 0.0068(18) -0.0007(16) 0.0013(16) 0.0169(18)
|
||||
C13 0.024(2) 0.030(2) 0.013(2) 0.0023(17) 0.0047(18) 0.0159(19)
|
||||
C14 0.027(2) 0.027(2) 0.0079(19) -0.0015(16) 0.0012(17) 0.0200(19)
|
||||
C15 0.030(2) 0.034(2) 0.012(2) 0.0056(17) 0.0059(18) 0.023(2)
|
||||
C16 0.023(2) 0.028(2) 0.0057(18) -0.0018(16) 0.0027(16) 0.0154(19)
|
||||
C17 0.024(2) 0.032(2) 0.0063(18) -0.0012(16) 0.0008(16) 0.0209(19)
|
||||
C18 0.028(2) 0.040(3) 0.010(2) -0.0024(18) -0.0038(18) 0.025(2)
|
||||
C19 0.023(2) 0.034(2) 0.012(2) -0.0015(18) -0.0024(17) 0.017(2)
|
||||
C20 0.024(2) 0.031(2) 0.019(2) 0.0008(19) -0.0030(19) 0.011(2)
|
||||
C21 0.027(3) 0.034(3) 0.028(3) -0.001(2) 0.001(2) 0.014(2)
|
||||
C22 0.038(3) 0.046(3) 0.027(3) 0.011(2) 0.010(2) 0.018(2)
|
||||
C23 0.039(3) 0.054(3) 0.033(3) 0.012(3) 0.015(2) 0.022(3)
|
||||
C24 0.051(3) 0.061(4) 0.034(3) -0.002(3) 0.006(3) 0.038(3)
|
||||
C25 0.145(8) 0.082(5) 0.039(4) 0.023(4) 0.042(5) 0.090(6)
|
||||
C26 0.068(5) 0.096(6) 0.055(4) 0.020(4) 0.007(4) 0.053(5)
|
||||
C27 0.105(7) 0.115(7) 0.120(8) -0.033(6) -0.051(6) 0.106(7)
|
||||
C28 0.082(6) 0.105(6) 0.049(4) 0.052(4) -0.036(4) -0.012(5)
|
||||
C29 0.074(7) 0.62(4) 0.114(10) 0.252(18) 0.001(7) 0.029(14)
|
||||
C30 0.149(9) 0.141(9) 0.054(5) 0.065(5) 0.057(6) 0.113(8)
|
||||
C31 0.110(7) 0.062(5) 0.082(6) 0.050(4) 0.030(5) 0.021(5)
|
||||
C32 0.094(6) 0.046(4) 0.061(4) 0.021(3) 0.040(4) 0.021(4)
|
||||
C33 0.056(4) 0.048(4) 0.046(4) 0.013(3) 0.018(3) -0.007(3)
|
||||
C34 0.014(2) 0.035(2) 0.014(2) 0.0059(18) -0.0019(17) 0.0096(18)
|
||||
C35 0.025(2) 0.032(2) 0.021(2) 0.0050(19) -0.0033(19) 0.010(2)
|
||||
C36 0.0082(18) 0.030(2) 0.0130(19) 0.0013(17) -0.0030(15) 0.0125(17)
|
||||
C37 0.017(2) 0.032(2) 0.013(2) -0.0004(17) -0.0039(16) 0.0181(19)
|
||||
C38 0.024(2) 0.029(2) 0.016(2) -0.0031(18) -0.0035(18) 0.017(2)
|
||||
N1 0.0187(18) 0.032(2) 0.0087(16) -0.0003(14) -0.0050(14) 0.0193(16)
|
||||
N2 0.0109(16) 0.033(2) 0.0095(16) 0.0031(14) -0.0003(13) 0.0141(15)
|
||||
N3 0.0216(18) 0.030(2) 0.0120(17) -0.0001(15) -0.0016(14) 0.0163(16)
|
||||
N4 0.029(2) 0.029(2) 0.0150(18) 0.0034(16) -0.0032(16) 0.0089(17)
|
||||
N5 0.033(2) 0.032(2) 0.021(2) 0.0036(17) -0.0041(17) 0.0140(18)
|
||||
O1 0.0235(17) 0.0363(18) 0.0205(16) 0.0026(14) 0.0003(13) 0.0105(14)
|
||||
O2 0.054(2) 0.060(2) 0.031(2) 0.0152(18) 0.0199(18) 0.040(2)
|
||||
O3 0.137(5) 0.064(3) 0.083(4) 0.044(3) 0.084(4) 0.073(4)
|
||||
O4 0.0309(19) 0.049(2) 0.043(2) 0.0191(18) -0.0025(16) 0.0215(17)
|
||||
O5 0.045(3) 0.155(6) 0.048(3) 0.062(3) 0.024(2) 0.031(3)
|
||||
O6 0.093(4) 0.048(3) 0.058(3) 0.026(2) -0.016(3) 0.012(3)
|
||||
O7 0.0255(17) 0.045(2) 0.0179(16) 0.0091(14) 0.0051(14) 0.0090(15)
|
||||
C101 0.033(3) 0.030(3) 0.021(2) 0.0047(19) 0.011(2) 0.014(2)
|
||||
C102 0.082(5) 0.053(4) 0.047(4) 0.000(3) -0.010(4) 0.039(4)
|
||||
C103 0.041(3) 0.035(3) 0.021(2) 0.008(2) -0.002(2) 0.017(2)
|
||||
C104 0.073(5) 0.069(5) 0.048(4) 0.042(4) -0.023(4) -0.012(4)
|
||||
C105 0.055(4) 0.045(3) 0.055(4) 0.018(3) 0.001(3) 0.028(3)
|
||||
C106 0.057(4) 0.060(4) 0.070(5) 0.018(4) 0.001(4) 0.026(4)
|
||||
C107 0.041(4) 0.113(7) 0.059(5) 0.035(4) 0.016(3) 0.028(4)
|
||||
C108 0.057(4) 0.081(5) 0.061(5) 0.019(4) 0.011(4) 0.003(4)
|
||||
O101 0.033(2) 0.038(2) 0.060(3) 0.0192(19) 0.0048(19) 0.0098(17)
|
||||
O102 0.041(2) 0.038(2) 0.053(2) 0.0099(18) -0.0001(19) 0.0125(18)
|
||||
O103 0.038(2) 0.043(2) 0.0269(18) 0.0176(16) 0.0086(15) 0.0154(17)
|
||||
O104 0.043(2) 0.055(2) 0.045(2) 0.023(2) -0.0085(19) 0.0130(19)
|
||||
F101 0.159(6) 0.220(8) 0.099(4) 0.007(5) 0.016(4) 0.166(7)
|
||||
F102 0.148(5) 0.067(3) 0.074(3) 0.030(2) -0.031(3) 0.045(3)
|
||||
F103 0.177(7) 0.030(2) 0.180(6) 0.007(3) -0.087(5) 0.030(3)
|
||||
F104 0.073(3) 0.084(3) 0.072(3) 0.054(2) -0.037(2) -0.028(2)
|
||||
F105 0.132(6) 0.291(12) 0.076(4) 0.035(6) 0.034(4) -0.108(8)
|
||||
F106 0.161(7) 0.104(5) 0.286(11) 0.146(7) -0.127(7) -0.041(5)
|
||||
N101 0.063(4) 0.050(3) 0.082(4) 0.025(3) -0.013(3) 0.018(3)
|
||||
N102 0.077(6) 0.35(2) 0.072(6) 0.079(9) 0.015(5) 0.027(9)
|
||||
|
||||
_geom_special_details
|
||||
;
|
||||
All esds (except the esd in the dihedral angle between two l.s. planes)
|
||||
are estimated using the full covariance matrix. The cell esds are taken
|
||||
into account individually in the estimation of esds in distances, angles
|
||||
and torsion angles; correlations between esds in cell parameters are only
|
||||
used when they are defined by crystal symmetry. An approximate (isotropic)
|
||||
treatment of cell esds is used for estimating esds involving l.s. planes.
|
||||
;
|
||||
|
||||
loop_
|
||||
_geom_bond_atom_site_label_1
|
||||
_geom_bond_atom_site_label_2
|
||||
_geom_bond_distance
|
||||
_geom_bond_site_symmetry_2
|
||||
_geom_bond_publ_flag
|
||||
C1 N1 1.334(6) . ?
|
||||
C1 C3 1.367(6) . ?
|
||||
C1 H1 0.9500 . ?
|
||||
C2 N1 1.353(6) . ?
|
||||
C2 C4 1.371(6) . ?
|
||||
C2 H2 0.9500 . ?
|
||||
C3 C5 1.394(6) . ?
|
||||
C3 H3 0.9500 . ?
|
||||
C4 C5 1.403(6) . ?
|
||||
C4 H4 0.9500 . ?
|
||||
C5 C6 1.490(6) . ?
|
||||
C6 C8 1.390(6) . ?
|
||||
C6 C7 1.397(6) . ?
|
||||
C7 C9 1.371(6) . ?
|
||||
C7 H7 0.9500 . ?
|
||||
C8 C10 1.375(6) . ?
|
||||
C8 H8 0.9500 . ?
|
||||
C9 N2 1.342(6) . ?
|
||||
C9 H9 0.9500 . ?
|
||||
C10 N2 1.352(6) . ?
|
||||
C10 H10 0.9500 . ?
|
||||
C11 N2 1.497(5) . ?
|
||||
C11 C12 1.517(6) . ?
|
||||
C11 H11A 0.9900 . ?
|
||||
C11 H11B 0.9900 . ?
|
||||
C12 C14 1.382(6) . ?
|
||||
C12 C13 1.396(6) . ?
|
||||
C13 C15 1.384(6) . ?
|
||||
C13 H13 0.9500 . ?
|
||||
C14 C16 1.395(6) . ?
|
||||
C14 H14 0.9500 . ?
|
||||
C15 C17 1.383(7) . ?
|
||||
C15 H15 0.9500 . ?
|
||||
C16 C17 1.400(6) . ?
|
||||
C16 N3 1.418(6) . ?
|
||||
C17 C18 1.515(6) . ?
|
||||
C18 N1 1.505(5) 2 ?
|
||||
C18 H18A 0.9900 . ?
|
||||
C18 H18B 0.9900 . ?
|
||||
C19 N3 1.356(5) . ?
|
||||
C19 C20 1.357(7) . ?
|
||||
C19 H19 0.9500 . ?
|
||||
C20 N5 1.366(6) . ?
|
||||
C20 C21 1.491(6) . ?
|
||||
C21 O1 1.435(6) . ?
|
||||
C21 H21A 0.9900 . ?
|
||||
C21 H21B 0.9900 . ?
|
||||
C22 O1 1.429(6) . ?
|
||||
C22 C23 1.484(8) . ?
|
||||
C22 H22A 0.9900 . ?
|
||||
C22 H22B 0.9900 . ?
|
||||
C23 O2 1.431(7) . ?
|
||||
C23 H23A 0.9900 . ?
|
||||
C23 H23B 0.9900 . ?
|
||||
C24 O2 1.420(7) . ?
|
||||
C24 C25 1.442(11) . ?
|
||||
C24 H24A 0.9900 . ?
|
||||
C24 H24B 0.9900 . ?
|
||||
C25 O3 1.416(11) . ?
|
||||
C25 H25A 0.9900 . ?
|
||||
C25 H25B 0.9900 . ?
|
||||
C26 C27 1.331(12) . ?
|
||||
C26 O3 1.481(9) . ?
|
||||
C26 H26A 0.9900 . ?
|
||||
C26 H26B 0.9900 . ?
|
||||
C27 O4 1.605(11) . ?
|
||||
C27 H27A 0.9900 . ?
|
||||
C27 H27B 0.9900 . ?
|
||||
C28 C29 1.252(12) . ?
|
||||
C28 O4 1.391(8) . ?
|
||||
C28 H28A 0.9900 . ?
|
||||
C28 H28B 0.9900 . ?
|
||||
C29 O5 1.361(11) . ?
|
||||
C29 H29A 0.9900 . ?
|
||||
C29 H29B 0.9900 . ?
|
||||
C30 O5 1.388(12) . ?
|
||||
C30 C31 1.407(13) . ?
|
||||
C30 H30A 0.9900 . ?
|
||||
C30 H30B 0.9900 . ?
|
||||
C31 O6 1.396(10) . ?
|
||||
C31 H31A 0.9900 . ?
|
||||
C31 H31B 0.9900 . ?
|
||||
C32 C33 1.449(9) . ?
|
||||
C32 O6 1.464(10) . ?
|
||||
C32 H32A 0.9900 . ?
|
||||
C32 H32B 0.9900 . ?
|
||||
C33 O7 1.453(7) . ?
|
||||
C33 H33A 0.9900 . ?
|
||||
C33 H33B 0.9900 . ?
|
||||
C34 O7 1.362(5) . ?
|
||||
C34 C35 1.377(7) . ?
|
||||
C34 C36 1.425(7) . ?
|
||||
C35 C38 1.404(7) 2 ?
|
||||
C35 H35 0.9500 . ?
|
||||
C36 C36 1.417(9) 2 ?
|
||||
C36 C37 1.417(6) . ?
|
||||
C37 C38 1.356(7) . ?
|
||||
C37 H37 0.9500 . ?
|
||||
C38 C35 1.404(7) 2 ?
|
||||
C38 H38 0.9500 . ?
|
||||
N1 C18 1.505(5) 2 ?
|
||||
N3 N4 1.370(5) . ?
|
||||
N4 N5 1.300(6) . ?
|
||||
C101 O101 1.227(6) . ?
|
||||
C101 O102 1.232(6) . ?
|
||||
C101 C102 1.518(9) . ?
|
||||
C102 F103 1.301(9) . ?
|
||||
C102 F102 1.318(8) . ?
|
||||
C102 F101 1.324(11) . ?
|
||||
C103 O104 1.224(6) . ?
|
||||
C103 O103 1.227(6) . ?
|
||||
C103 C104 1.512(9) . ?
|
||||
C104 F106 1.260(10) . ?
|
||||
C104 F104 1.290(7) . ?
|
||||
C104 F105 1.341(14) . ?
|
||||
C105 N101 1.139(9) . ?
|
||||
C105 C106 1.443(10) . ?
|
||||
C106 H10A 0.9800 . ?
|
||||
C106 H10B 0.9800 . ?
|
||||
C106 H10C 0.9800 . ?
|
||||
C107 N102 1.118(11) . ?
|
||||
C107 C108 1.360(11) . ?
|
||||
C108 H10D 0.9800 . ?
|
||||
C108 H10E 0.9800 . ?
|
||||
C108 H10F 0.9800 . ?
|
||||
|
||||
loop_
|
||||
_geom_angle_atom_site_label_1
|
||||
_geom_angle_atom_site_label_2
|
||||
_geom_angle_atom_site_label_3
|
||||
_geom_angle
|
||||
_geom_angle_site_symmetry_1
|
||||
_geom_angle_site_symmetry_3
|
||||
_geom_angle_publ_flag
|
||||
N1 C1 C3 121.3(4) . . ?
|
||||
N1 C1 H1 119.3 . . ?
|
||||
C3 C1 H1 119.3 . . ?
|
||||
N1 C2 C4 120.4(4) . . ?
|
||||
N1 C2 H2 119.8 . . ?
|
||||
C4 C2 H2 119.8 . . ?
|
||||
C1 C3 C5 120.4(4) . . ?
|
||||
C1 C3 H3 119.8 . . ?
|
||||
C5 C3 H3 119.8 . . ?
|
||||
C2 C4 C5 120.4(4) . . ?
|
||||
C2 C4 H4 119.8 . . ?
|
||||
C5 C4 H4 119.8 . . ?
|
||||
C3 C5 C4 116.9(4) . . ?
|
||||
C3 C5 C6 121.8(4) . . ?
|
||||
C4 C5 C6 121.2(4) . . ?
|
||||
C8 C6 C7 117.6(4) . . ?
|
||||
C8 C6 C5 122.4(4) . . ?
|
||||
C7 C6 C5 119.9(4) . . ?
|
||||
C9 C7 C6 120.1(4) . . ?
|
||||
C9 C7 H7 120.0 . . ?
|
||||
C6 C7 H7 120.0 . . ?
|
||||
C10 C8 C6 120.4(4) . . ?
|
||||
C10 C8 H8 119.8 . . ?
|
||||
C6 C8 H8 119.8 . . ?
|
||||
N2 C9 C7 120.9(4) . . ?
|
||||
N2 C9 H9 119.5 . . ?
|
||||
C7 C9 H9 119.5 . . ?
|
||||
N2 C10 C8 120.3(4) . . ?
|
||||
N2 C10 H10 119.8 . . ?
|
||||
C8 C10 H10 119.8 . . ?
|
||||
N2 C11 C12 107.7(3) . . ?
|
||||
N2 C11 H11A 110.2 . . ?
|
||||
C12 C11 H11A 110.2 . . ?
|
||||
N2 C11 H11B 110.2 . . ?
|
||||
C12 C11 H11B 110.2 . . ?
|
||||
H11A C11 H11B 108.5 . . ?
|
||||
C14 C12 C13 119.9(4) . . ?
|
||||
C14 C12 C11 120.0(4) . . ?
|
||||
C13 C12 C11 120.0(4) . . ?
|
||||
C15 C13 C12 119.2(4) . . ?
|
||||
C15 C13 H13 120.4 . . ?
|
||||
C12 C13 H13 120.4 . . ?
|
||||
C12 C14 C16 119.7(4) . . ?
|
||||
C12 C14 H14 120.2 . . ?
|
||||
C16 C14 H14 120.2 . . ?
|
||||
C17 C15 C13 122.1(4) . . ?
|
||||
C17 C15 H15 118.9 . . ?
|
||||
C13 C15 H15 118.9 . . ?
|
||||
C14 C16 C17 121.1(4) . . ?
|
||||
C14 C16 N3 117.0(4) . . ?
|
||||
C17 C16 N3 121.8(4) . . ?
|
||||
C15 C17 C16 117.5(4) . . ?
|
||||
C15 C17 C18 119.0(4) . . ?
|
||||
C16 C17 C18 123.2(4) . . ?
|
||||
N1 C18 C17 109.0(3) 2 . ?
|
||||
N1 C18 H18A 109.9 2 . ?
|
||||
C17 C18 H18A 109.9 . . ?
|
||||
N1 C18 H18B 109.9 2 . ?
|
||||
C17 C18 H18B 109.9 . . ?
|
||||
H18A C18 H18B 108.3 . . ?
|
||||
N3 C19 C20 105.0(4) . . ?
|
||||
N3 C19 H19 127.5 . . ?
|
||||
C20 C19 H19 127.5 . . ?
|
||||
C19 C20 N5 108.7(4) . . ?
|
||||
C19 C20 C21 129.7(4) . . ?
|
||||
N5 C20 C21 121.6(5) . . ?
|
||||
O1 C21 C20 113.0(4) . . ?
|
||||
O1 C21 H21A 109.0 . . ?
|
||||
C20 C21 H21A 109.0 . . ?
|
||||
O1 C21 H21B 109.0 . . ?
|
||||
C20 C21 H21B 109.0 . . ?
|
||||
H21A C21 H21B 107.8 . . ?
|
||||
O1 C22 C23 109.1(4) . . ?
|
||||
O1 C22 H22A 109.9 . . ?
|
||||
C23 C22 H22A 109.9 . . ?
|
||||
O1 C22 H22B 109.9 . . ?
|
||||
C23 C22 H22B 109.9 . . ?
|
||||
H22A C22 H22B 108.3 . . ?
|
||||
O2 C23 C22 108.0(5) . . ?
|
||||
O2 C23 H23A 110.1 . . ?
|
||||
C22 C23 H23A 110.1 . . ?
|
||||
O2 C23 H23B 110.1 . . ?
|
||||
C22 C23 H23B 110.1 . . ?
|
||||
H23A C23 H23B 108.4 . . ?
|
||||
O2 C24 C25 110.9(6) . . ?
|
||||
O2 C24 H24A 109.5 . . ?
|
||||
C25 C24 H24A 109.5 . . ?
|
||||
O2 C24 H24B 109.5 . . ?
|
||||
C25 C24 H24B 109.5 . . ?
|
||||
H24A C24 H24B 108.0 . . ?
|
||||
O3 C25 C24 112.1(6) . . ?
|
||||
O3 C25 H25A 109.2 . . ?
|
||||
C24 C25 H25A 109.2 . . ?
|
||||
O3 C25 H25B 109.2 . . ?
|
||||
C24 C25 H25B 109.2 . . ?
|
||||
H25A C25 H25B 107.9 . . ?
|
||||
C27 C26 O3 98.8(7) . . ?
|
||||
C27 C26 H26A 112.0 . . ?
|
||||
O3 C26 H26A 112.0 . . ?
|
||||
C27 C26 H26B 112.0 . . ?
|
||||
O3 C26 H26B 112.0 . . ?
|
||||
H26A C26 H26B 109.7 . . ?
|
||||
C26 C27 O4 114.9(7) . . ?
|
||||
C26 C27 H27A 108.5 . . ?
|
||||
O4 C27 H27A 108.5 . . ?
|
||||
C26 C27 H27B 108.5 . . ?
|
||||
O4 C27 H27B 108.5 . . ?
|
||||
H27A C27 H27B 107.5 . . ?
|
||||
C29 C28 O4 124.1(6) . . ?
|
||||
C29 C28 H28A 106.3 . . ?
|
||||
O4 C28 H28A 106.3 . . ?
|
||||
C29 C28 H28B 106.3 . . ?
|
||||
O4 C28 H28B 106.3 . . ?
|
||||
H28A C28 H28B 106.4 . . ?
|
||||
C28 C29 O5 128.7(9) . . ?
|
||||
C28 C29 H29A 105.1 . . ?
|
||||
O5 C29 H29A 105.1 . . ?
|
||||
C28 C29 H29B 105.1 . . ?
|
||||
O5 C29 H29B 105.1 . . ?
|
||||
H29A C29 H29B 105.9 . . ?
|
||||
O5 C30 C31 110.9(6) . . ?
|
||||
O5 C30 H30A 109.5 . . ?
|
||||
C31 C30 H30A 109.5 . . ?
|
||||
O5 C30 H30B 109.5 . . ?
|
||||
C31 C30 H30B 109.5 . . ?
|
||||
H30A C30 H30B 108.1 . . ?
|
||||
O6 C31 C30 111.7(8) . . ?
|
||||
O6 C31 H31A 109.3 . . ?
|
||||
C30 C31 H31A 109.3 . . ?
|
||||
O6 C31 H31B 109.3 . . ?
|
||||
C30 C31 H31B 109.3 . . ?
|
||||
H31A C31 H31B 107.9 . . ?
|
||||
C33 C32 O6 108.1(6) . . ?
|
||||
C33 C32 H32A 110.1 . . ?
|
||||
O6 C32 H32A 110.1 . . ?
|
||||
C33 C32 H32B 110.1 . . ?
|
||||
O6 C32 H32B 110.1 . . ?
|
||||
H32A C32 H32B 108.4 . . ?
|
||||
C32 C33 O7 111.8(5) . . ?
|
||||
C32 C33 H33A 109.3 . . ?
|
||||
O7 C33 H33A 109.3 . . ?
|
||||
C32 C33 H33B 109.3 . . ?
|
||||
O7 C33 H33B 109.3 . . ?
|
||||
H33A C33 H33B 107.9 . . ?
|
||||
O7 C34 C35 124.0(4) . . ?
|
||||
O7 C34 C36 115.2(4) . . ?
|
||||
C35 C34 C36 120.8(4) . . ?
|
||||
C34 C35 C38 119.3(5) . 2 ?
|
||||
C34 C35 H35 120.4 . . ?
|
||||
C38 C35 H35 120.4 2 . ?
|
||||
C36 C36 C37 119.6(5) 2 . ?
|
||||
C36 C36 C34 118.4(5) 2 . ?
|
||||
C37 C36 C34 121.9(4) . . ?
|
||||
C38 C37 C36 119.9(4) . . ?
|
||||
C38 C37 H37 120.1 . . ?
|
||||
C36 C37 H37 120.1 . . ?
|
||||
C37 C38 C35 122.0(4) . 2 ?
|
||||
C37 C38 H38 119.0 . . ?
|
||||
C35 C38 H38 119.0 2 . ?
|
||||
C1 N1 C2 120.4(4) . . ?
|
||||
C1 N1 C18 120.5(4) . 2 ?
|
||||
C2 N1 C18 119.1(4) . 2 ?
|
||||
C9 N2 C10 120.6(4) . . ?
|
||||
C9 N2 C11 119.3(4) . . ?
|
||||
C10 N2 C11 119.9(4) . . ?
|
||||
C19 N3 N4 110.0(4) . . ?
|
||||
C19 N3 C16 130.4(4) . . ?
|
||||
N4 N3 C16 119.3(3) . . ?
|
||||
N5 N4 N3 107.0(3) . . ?
|
||||
N4 N5 C20 109.4(4) . . ?
|
||||
C22 O1 C21 112.0(4) . . ?
|
||||
C24 O2 C23 111.2(5) . . ?
|
||||
C25 O3 C26 101.1(6) . . ?
|
||||
C28 O4 C27 123.6(7) . . ?
|
||||
C29 O5 C30 109.4(11) . . ?
|
||||
C31 O6 C32 107.8(7) . . ?
|
||||
C34 O7 C33 116.5(4) . . ?
|
||||
O101 C101 O102 129.2(5) . . ?
|
||||
O101 C101 C102 113.1(5) . . ?
|
||||
O102 C101 C102 117.7(5) . . ?
|
||||
F103 C102 F102 108.6(7) . . ?
|
||||
F103 C102 F101 104.9(7) . . ?
|
||||
F102 C102 F101 105.0(7) . . ?
|
||||
F103 C102 C101 114.3(6) . . ?
|
||||
F102 C102 C101 112.4(5) . . ?
|
||||
F101 C102 C101 111.0(7) . . ?
|
||||
O104 C103 O103 130.2(5) . . ?
|
||||
O104 C103 C104 115.4(5) . . ?
|
||||
O103 C103 C104 114.3(5) . . ?
|
||||
F106 C104 F104 107.4(7) . . ?
|
||||
F106 C104 F105 99.1(9) . . ?
|
||||
F104 C104 F105 105.1(9) . . ?
|
||||
F106 C104 C103 115.6(8) . . ?
|
||||
F104 C104 C103 117.1(5) . . ?
|
||||
F105 C104 C103 110.5(7) . . ?
|
||||
N101 C105 C106 179.3(9) . . ?
|
||||
C105 C106 H10A 109.5 . . ?
|
||||
C105 C106 H10B 109.5 . . ?
|
||||
H10A C106 H10B 109.5 . . ?
|
||||
C105 C106 H10C 109.5 . . ?
|
||||
H10A C106 H10C 109.5 . . ?
|
||||
H10B C106 H10C 109.5 . . ?
|
||||
N102 C107 C108 179.3(14) . . ?
|
||||
C107 C108 H10D 109.5 . . ?
|
||||
C107 C108 H10E 109.5 . . ?
|
||||
H10D C108 H10E 109.5 . . ?
|
||||
C107 C108 H10F 109.5 . . ?
|
||||
H10D C108 H10F 109.5 . . ?
|
||||
H10E C108 H10F 109.5 . . ?
|
||||
|
||||
loop_
|
||||
_geom_torsion_atom_site_label_1
|
||||
_geom_torsion_atom_site_label_2
|
||||
_geom_torsion_atom_site_label_3
|
||||
_geom_torsion_atom_site_label_4
|
||||
_geom_torsion
|
||||
_geom_torsion_site_symmetry_1
|
||||
_geom_torsion_site_symmetry_2
|
||||
_geom_torsion_site_symmetry_3
|
||||
_geom_torsion_site_symmetry_4
|
||||
_geom_torsion_publ_flag
|
||||
N1 C1 C3 C5 0.3(6) . . . . ?
|
||||
N1 C2 C4 C5 1.1(6) . . . . ?
|
||||
C1 C3 C5 C4 3.1(6) . . . . ?
|
||||
C1 C3 C5 C6 -174.6(4) . . . . ?
|
||||
C2 C4 C5 C3 -3.8(5) . . . . ?
|
||||
C2 C4 C5 C6 173.9(3) . . . . ?
|
||||
C3 C5 C6 C8 -168.8(4) . . . . ?
|
||||
C4 C5 C6 C8 13.6(5) . . . . ?
|
||||
C3 C5 C6 C7 14.7(5) . . . . ?
|
||||
C4 C5 C6 C7 -162.9(4) . . . . ?
|
||||
C8 C6 C7 C9 -0.6(5) . . . . ?
|
||||
C5 C6 C7 C9 176.0(3) . . . . ?
|
||||
C7 C6 C8 C10 1.4(5) . . . . ?
|
||||
C5 C6 C8 C10 -175.2(3) . . . . ?
|
||||
C6 C7 C9 N2 -0.7(6) . . . . ?
|
||||
C6 C8 C10 N2 -0.8(6) . . . . ?
|
||||
N2 C11 C12 C14 88.4(5) . . . . ?
|
||||
N2 C11 C12 C13 -87.7(5) . . . . ?
|
||||
C14 C12 C13 C15 -5.7(6) . . . . ?
|
||||
C11 C12 C13 C15 170.4(4) . . . . ?
|
||||
C13 C12 C14 C16 4.9(6) . . . . ?
|
||||
C11 C12 C14 C16 -171.2(4) . . . . ?
|
||||
C12 C13 C15 C17 0.2(6) . . . . ?
|
||||
C12 C14 C16 C17 1.4(6) . . . . ?
|
||||
C12 C14 C16 N3 179.1(3) . . . . ?
|
||||
C13 C15 C17 C16 5.9(6) . . . . ?
|
||||
C13 C15 C17 C18 -167.5(4) . . . . ?
|
||||
C14 C16 C17 C15 -6.7(6) . . . . ?
|
||||
N3 C16 C17 C15 175.7(4) . . . . ?
|
||||
C14 C16 C17 C18 166.4(4) . . . . ?
|
||||
N3 C16 C17 C18 -11.2(6) . . . . ?
|
||||
C15 C17 C18 N1 83.8(5) . . . 2 ?
|
||||
C16 C17 C18 N1 -89.3(5) . . . 2 ?
|
||||
N3 C19 C20 N5 0.2(5) . . . . ?
|
||||
N3 C19 C20 C21 -179.5(5) . . . . ?
|
||||
C19 C20 C21 O1 78.8(7) . . . . ?
|
||||
N5 C20 C21 O1 -100.9(6) . . . . ?
|
||||
O1 C22 C23 O2 66.1(5) . . . . ?
|
||||
O2 C24 C25 O3 65.4(6) . . . . ?
|
||||
O3 C26 C27 O4 -86.2(9) . . . . ?
|
||||
O4 C28 C29 O5 -7(4) . . . . ?
|
||||
O5 C30 C31 O6 69.5(9) . . . . ?
|
||||
O6 C32 C33 O7 -71.5(8) . . . . ?
|
||||
O7 C34 C35 C38 -177.1(4) . . . 2 ?
|
||||
C36 C34 C35 C38 1.4(6) . . . 2 ?
|
||||
O7 C34 C36 C36 177.6(4) . . . 2 ?
|
||||
C35 C34 C36 C36 -1.1(7) . . . 2 ?
|
||||
O7 C34 C36 C37 -2.5(6) . . . . ?
|
||||
C35 C34 C36 C37 178.9(4) . . . . ?
|
||||
C36 C36 C37 C38 -0.8(7) 2 . . . ?
|
||||
C34 C36 C37 C38 179.2(4) . . . . ?
|
||||
C36 C37 C38 C35 0.5(6) . . . 2 ?
|
||||
C3 C1 N1 C2 -3.1(6) . . . . ?
|
||||
C3 C1 N1 C18 175.3(4) . . . 2 ?
|
||||
C4 C2 N1 C1 2.4(6) . . . . ?
|
||||
C4 C2 N1 C18 -176.0(3) . . . 2 ?
|
||||
C7 C9 N2 C10 1.4(6) . . . . ?
|
||||
C7 C9 N2 C11 -173.8(3) . . . . ?
|
||||
C8 C10 N2 C9 -0.6(6) . . . . ?
|
||||
C8 C10 N2 C11 174.5(3) . . . . ?
|
||||
C12 C11 N2 C9 85.7(4) . . . . ?
|
||||
C12 C11 N2 C10 -89.6(4) . . . . ?
|
||||
C20 C19 N3 N4 -0.2(5) . . . . ?
|
||||
C20 C19 N3 C16 -174.0(4) . . . . ?
|
||||
C14 C16 N3 C19 129.4(5) . . . . ?
|
||||
C17 C16 N3 C19 -52.9(6) . . . . ?
|
||||
C14 C16 N3 N4 -43.9(5) . . . . ?
|
||||
C17 C16 N3 N4 133.8(4) . . . . ?
|
||||
C19 N3 N4 N5 0.2(5) . . . . ?
|
||||
C16 N3 N4 N5 174.8(4) . . . . ?
|
||||
N3 N4 N5 C20 -0.1(5) . . . . ?
|
||||
C19 C20 N5 N4 -0.1(6) . . . . ?
|
||||
C21 C20 N5 N4 179.7(4) . . . . ?
|
||||
C23 C22 O1 C21 -171.9(4) . . . . ?
|
||||
C20 C21 O1 C22 63.3(6) . . . . ?
|
||||
C25 C24 O2 C23 170.3(5) . . . . ?
|
||||
C22 C23 O2 C24 164.7(4) . . . . ?
|
||||
C24 C25 O3 C26 170.9(5) . . . . ?
|
||||
C27 C26 O3 C25 177.5(7) . . . . ?
|
||||
C29 C28 O4 C27 -129(2) . . . . ?
|
||||
C26 C27 O4 C28 -85.6(11) . . . . ?
|
||||
C28 C29 O5 C30 136(3) . . . . ?
|
||||
C31 C30 O5 C29 177.4(11) . . . . ?
|
||||
C30 C31 O6 C32 178.6(7) . . . . ?
|
||||
C33 C32 O6 C31 -173.5(6) . . . . ?
|
||||
C35 C34 O7 C33 2.2(7) . . . . ?
|
||||
C36 C34 O7 C33 -176.4(5) . . . . ?
|
||||
C32 C33 O7 C34 169.3(6) . . . . ?
|
||||
O101 C101 C102 F103 -174.2(7) . . . . ?
|
||||
O102 C101 C102 F103 6.5(10) . . . . ?
|
||||
O101 C101 C102 F102 -49.8(9) . . . . ?
|
||||
O102 C101 C102 F102 130.9(7) . . . . ?
|
||||
O101 C101 C102 F101 67.5(7) . . . . ?
|
||||
O102 C101 C102 F101 -111.9(7) . . . . ?
|
||||
O104 C103 C104 F106 -34.8(12) . . . . ?
|
||||
O103 C103 C104 F106 149.3(9) . . . . ?
|
||||
O104 C103 C104 F104 -163.1(8) . . . . ?
|
||||
O103 C103 C104 F104 21.0(11) . . . . ?
|
||||
O104 C103 C104 F105 76.7(10) . . . . ?
|
||||
O103 C103 C104 F105 -99.2(8) . . . . ?
|
||||
|
||||
_diffrn_measured_fraction_theta_max 0.961
|
||||
_diffrn_reflns_theta_full 64.94
|
||||
_diffrn_measured_fraction_theta_full 0.961
|
||||
_refine_diff_density_max 1.693
|
||||
_refine_diff_density_min -0.992
|
||||
_refine_diff_density_rms 0.109
|
||||
|
||||
# start Validation Reply Form
|
||||
_vrf_PLAT213_I
|
||||
;
|
||||
PROBLEM: Atom C5 has ADP max/min Ratio ..... 5.1 oblat
|
||||
RESPONSE: The atoms of the glycol chain and of the CBPQT4+ ring showed elongated
|
||||
displacement parameters. Attempts to model this disorder did not significantly
|
||||
improve the refinement.
|
||||
;
|
||||
_vrf_PLAT222_I
|
||||
;
|
||||
PROBLEM: Large Non-Solvent H Uiso(max)/Uiso(min) .. 10.0 Ratio
|
||||
RESPONSE: Hydrogen atoms were refined as riding models with their isotropic
|
||||
displacement parameters linked to their parent atoms.
|
||||
In this case the parent atom exhibits disorder with an elongated
|
||||
displacement parameter and therefore the riding hydrogen atom is also large.
|
||||
;
|
||||
_vrf_PLAT241_I
|
||||
;
|
||||
PROBLEM: Check High Ueq as Compared to Neighbors for C27
|
||||
RESPONSE: C27 and C29 are part of the disordered glycol chain, however they
|
||||
are bonded to C26 and O5 which are relatively well-ordered parts of the
|
||||
structure.
|
||||
;
|
||||
|
||||
# end Validation Reply Form
|
||||
|
||||
|
||||
|
||||
15808
package-lock.json
generated
15808
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.7",
|
||||
"version": "0.5.0-dev.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -11,19 +11,21 @@
|
||||
"url": "https://github.com/molstar/molstar/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "tslint src/**/*.ts",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"test": "npm run lint && jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"build-tsc": "tsc",
|
||||
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/",
|
||||
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
|
||||
"build-webpack": "webpack --mode production",
|
||||
"watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
"watch-tsc": "tsc -watch",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/ --watch",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --display minimal",
|
||||
"serve": "http-server -p 1338",
|
||||
"model-server": "node lib/servers/model/server.js",
|
||||
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
|
||||
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/servers/plugin-state/index.js",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
@@ -62,56 +64,66 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^1.12.2",
|
||||
"@graphql-codegen/cli": "^1.12.2",
|
||||
"@graphql-codegen/time": "^1.12.2",
|
||||
"@graphql-codegen/typescript": "^1.12.2",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.12.2",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.12.2",
|
||||
"@graphql-codegen/typescript-operations": "^1.12.2",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^2.19.2",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^2.19.2",
|
||||
"@typescript-eslint/parser": "^2.19.2",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^4.1.2",
|
||||
"cpx": "^1.5.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"cpx2": "^2.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^4.2.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"graphql-code-generator": "^0.18.2",
|
||||
"graphql-codegen-time": "^0.18.2",
|
||||
"graphql-codegen-typescript-template": "^0.18.2",
|
||||
"http-server": "^0.11.1",
|
||||
"jest": "^24.9.0",
|
||||
"http-server": "^0.12.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"raw-loader": "^3.1.0",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"simple-git": "^1.124.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint": "^5.19.0",
|
||||
"typescript": "^3.5.3",
|
||||
"webpack": "^4.39.2",
|
||||
"webpack-cli": "^3.3.7"
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"pascal-case": "^3.1.1",
|
||||
"raw-loader": "^4.0.0",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"simple-git": "^1.131.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"ts-jest": "^25.2.0",
|
||||
"typescript": "^3.7.5",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^1.0.36",
|
||||
"@types/argparse": "^1.0.38",
|
||||
"@types/benchmark": "^1.0.31",
|
||||
"@types/compression": "1.0.1",
|
||||
"@types/express": "^4.17.1",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.2",
|
||||
"@types/node-fetch": "^2.5.0",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/swagger-ui-dist": "3.0.3",
|
||||
"@types/webgl2": "0.0.5",
|
||||
"@types/compression": "1.7.0",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/jest": "^25.1.2",
|
||||
"@types/node": "^13.7.0",
|
||||
"@types/node-fetch": "^2.5.4",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/swagger-ui-dist": "3.0.5",
|
||||
"argparse": "^1.0.10",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql": "^14.6.0",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"rxjs": "^6.5.2",
|
||||
"swagger-ui-dist": "^3.23.5",
|
||||
"util.promisify": "^1.0.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"rxjs": "^6.5.4",
|
||||
"swagger-ui-dist": "^3.25.0",
|
||||
"util.promisify": "^1.0.1",
|
||||
"xhr2": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
|
||||
@@ -9,9 +10,8 @@ import { Model, ElementIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export const StripedResidues = CustomElementProperty.create<number>({
|
||||
isStatic: true,
|
||||
label: 'Residue Stripes',
|
||||
name: 'basic-wrapper-residue-striping',
|
||||
display: 'Residue Stripes',
|
||||
getData(model: Model) {
|
||||
const map = new Map<ElementIndex, number>();
|
||||
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
|
||||
@@ -24,7 +24,7 @@ export const StripedResidues = CustomElementProperty.create<number>({
|
||||
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
|
||||
defaultColor: Color(0x777777)
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
return e === 0 ? 'Odd stripe' : 'Even stripe'
|
||||
}
|
||||
})
|
||||
@@ -4,9 +4,9 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin/ui/base';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin/ui/state/update-transform';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
|
||||
|
||||
export class BasicWrapperControls extends PluginUIComponent {
|
||||
|
||||
@@ -19,4 +19,12 @@ export class BasicWrapperControls extends PluginUIComponent {
|
||||
<TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomToastMessage extends PluginUIComponent {
|
||||
render() {
|
||||
return <>
|
||||
Custom <i>Toast</i> content. No timeout.
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export namespace StateHelper {
|
||||
}
|
||||
|
||||
export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, { tags: 'structure' })
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
|
||||
};
|
||||
|
||||
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
|
||||
|
||||
@@ -105,10 +105,18 @@
|
||||
|
||||
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
|
||||
|
||||
addHeader('Interactivity');
|
||||
addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());
|
||||
addControl('Clear Highlight', () => BasicMolStarWrapper.interactivity.clearHighlight());
|
||||
|
||||
addHeader('Tests');
|
||||
|
||||
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
|
||||
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
|
||||
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
|
||||
|
||||
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
|
||||
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -11,13 +11,17 @@ import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { StateTransforms } from '../../mol-plugin/state/transforms';
|
||||
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
|
||||
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
|
||||
import { StateBuilder, StateTransform } from '../../mol-state';
|
||||
import { StripedResidues } from './coloring';
|
||||
// import { BasicWrapperControls } from './controls';
|
||||
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
|
||||
import { CustomToastMessage } from './controls';
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { Script } from '../../mol-script/script';
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -37,12 +41,15 @@ class BasicWrapper {
|
||||
// left: 'none',
|
||||
// right: BasicWrapperControls
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.Descriptor.name, StripedResidues.colorTheme!);
|
||||
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
|
||||
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider!);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
@@ -56,12 +63,12 @@ class BasicWrapper {
|
||||
|
||||
return parsed
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [StripedResidues.Descriptor.name] }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
|
||||
}
|
||||
|
||||
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
@@ -104,11 +111,13 @@ class BasicWrapper {
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
const renderer = this.plugin.canvas3d.props.renderer;
|
||||
const renderer = this.plugin.canvas3d!.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
|
||||
}
|
||||
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
@@ -132,7 +141,7 @@ class BasicWrapper {
|
||||
|
||||
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: StripedResidues.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.Descriptor.name).defaultValues };
|
||||
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
for (const v of visuals) {
|
||||
tree.to(v).update(old => ({ ...old, colorTheme }));
|
||||
@@ -142,6 +151,22 @@ class BasicWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
interactivity = {
|
||||
highlightOn: () => {
|
||||
const seq_id = 7;
|
||||
const data = (this.plugin.state.dataState.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
|
||||
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
|
||||
'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
|
||||
'group-by': Q.struct.atomProperty.macromolecular.residueKey()
|
||||
}), data);
|
||||
const loci = StructureSelection.toLociWithSourceUnits(sel);
|
||||
this.plugin.interactivity.lociHighlights.highlightOnly({ loci });
|
||||
},
|
||||
clearHighlight: () => {
|
||||
this.plugin.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
|
||||
}
|
||||
}
|
||||
|
||||
tests = {
|
||||
staticSuperposition: async () => {
|
||||
const state = this.plugin.state.dataState;
|
||||
@@ -152,6 +177,28 @@ class BasicWrapper {
|
||||
dynamicSuperposition: async () => {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
|
||||
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
|
||||
},
|
||||
toggleValidationTooltip: async () => {
|
||||
const state = this.plugin.state.behaviorState;
|
||||
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
},
|
||||
showToasts: () => {
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
title: 'Toast 1',
|
||||
message: 'This is an example text, timeout 3s',
|
||||
key: 'toast-1',
|
||||
timeoutMs: 3000
|
||||
});
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
title: 'Toast 2',
|
||||
message: CustomToastMessage,
|
||||
key: 'toast-2'
|
||||
});
|
||||
},
|
||||
hideToasts: () => {
|
||||
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-1' });
|
||||
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-2' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,16 +50,16 @@ export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionT
|
||||
|
||||
export const StaticSuperpositionTestData: SuperpositionTestInput = [
|
||||
{ pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() },
|
||||
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows(
|
||||
[[0.406, 0.879, 0.248, -200.633],
|
||||
[0.693, -0.473, 0.544, 73.403],
|
||||
[0.596, -0.049, -0.802, -14.209],
|
||||
[0, 0, 0, 1]] )},
|
||||
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows(
|
||||
[[-0.053, -0.077, 0.996, -45.633],
|
||||
[-0.312, 0.949, 0.057, -12.255],
|
||||
[-0.949, -0.307, -0.074, 53.562],
|
||||
[0, 0, 0, 1]] )}
|
||||
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
|
||||
[0.406, 0.879, 0.248, -200.633],
|
||||
[0.693, -0.473, 0.544, 73.403],
|
||||
[0.596, -0.049, -0.802, -14.209],
|
||||
[0, 0, 0, 1]] )},
|
||||
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
|
||||
[-0.053, -0.077, 0.996, -45.633],
|
||||
[-0.312, 0.949, 0.057, -12.255],
|
||||
[-0.949, -0.307, -0.074, 53.562],
|
||||
[0, 0, 0, 1]] )}
|
||||
];
|
||||
|
||||
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
|
||||
@@ -86,7 +86,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
|
||||
|
||||
const query = compile<StructureSelection>(pivot);
|
||||
const xs = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
|
||||
const selections = xs.map(s => StructureSelection.toLoci(query(new QueryContext(s.obj!.data))));
|
||||
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.obj!.data))));
|
||||
|
||||
const transforms = superposeStructures(selections);
|
||||
const visuals = state.build();
|
||||
|
||||
@@ -226,8 +226,8 @@ const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'
|
||||
const PVCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/aa-variants-v1.cif'
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
|
||||
addHelp: true,
|
||||
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
|
||||
});
|
||||
parser.addArgument('out', {
|
||||
help: 'Generated file output path.'
|
||||
|
||||
@@ -13,7 +13,7 @@ import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/tran
|
||||
import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
|
||||
import { StateBuilder } from '../../../mol-state';
|
||||
import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -97,15 +97,15 @@ class LightingDemo {
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
|
||||
...props,
|
||||
multiSample: {
|
||||
...this.plugin.canvas3d.props.multiSample,
|
||||
...this.plugin.canvas3d!.props.multiSample,
|
||||
...props.multiSample
|
||||
},
|
||||
renderer: {
|
||||
...this.plugin.canvas3d.props.renderer,
|
||||
...this.plugin.canvas3d!.props.renderer,
|
||||
...props.renderer
|
||||
},
|
||||
postprocessing: {
|
||||
...this.plugin.canvas3d.props.postprocessing,
|
||||
...this.plugin.canvas3d!.props.postprocessing,
|
||||
...props.postprocessing
|
||||
},
|
||||
}});
|
||||
|
||||
@@ -22,6 +22,7 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
case 'color-list': return `One of ${oToS(param.options)}`;
|
||||
case 'vec3': return `3D vector [x, y, z]`;
|
||||
case 'file': return `JavaScript File Handle`;
|
||||
case 'file-list': return `JavaScript FileList Handle`;
|
||||
case 'select': return `One of ${oToS(param.options)}`;
|
||||
case 'text': return 'String';
|
||||
case 'interval': return `Interval [min, max]`;
|
||||
@@ -38,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
}
|
||||
}
|
||||
|
||||
function oToS(options: [string, string][]) {
|
||||
function oToS(options: readonly (readonly [string, string])[]) {
|
||||
return options.map(o => `'${o[0]}'`).join(', ');
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import { OrderedSet } from '../../mol-data/int';
|
||||
import { openCif, downloadCif } from './helpers';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
|
||||
import { Sequence } from '../../mol-model/sequence';
|
||||
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
|
||||
async function downloadFromPdb(pdb: string) {
|
||||
@@ -49,8 +52,10 @@ export function residueLabel(model: Model, rI: number) {
|
||||
export function printSecStructure(model: Model) {
|
||||
console.log('\nSecondary Structure\n=============');
|
||||
const { residues } = model.atomicHierarchy;
|
||||
const { key, elements } = model.properties.secondaryStructure;
|
||||
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
|
||||
if (!secondaryStructure) return
|
||||
|
||||
const { key, elements } = secondaryStructure
|
||||
const count = residues._rowCount;
|
||||
let rI = 0;
|
||||
while (rI < count) {
|
||||
@@ -64,14 +69,14 @@ export function printSecStructure(model: Model) {
|
||||
}
|
||||
}
|
||||
|
||||
export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
export function printBonds(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
if (showIntra) {
|
||||
console.log('\nIntra Unit Links\n=============');
|
||||
console.log('\nIntra Unit Bonds\n=============');
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
const elements = unit.elements;
|
||||
const { a, b, edgeCount } = unit.links;
|
||||
const { a, b, edgeCount } = unit.bonds;
|
||||
const { model } = unit;
|
||||
|
||||
if (!edgeCount) continue;
|
||||
@@ -85,20 +90,20 @@ export function printLinks(structure: Structure, showIntra: boolean, showInter:
|
||||
}
|
||||
|
||||
if (showInter) {
|
||||
console.log('\nInter Unit Links\n=============');
|
||||
const links = structure.links;
|
||||
console.log('\nInter Unit Bonds\n=============');
|
||||
const bonds = structure.interUnitBonds;
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
for (const pairLinks of links.getLinkedUnits(unit)) {
|
||||
if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
|
||||
for (const pairBonds of bonds.getConnectedUnits(unit)) {
|
||||
if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
|
||||
|
||||
const { unitA, unitB } = pairLinks;
|
||||
console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
|
||||
const { unitA, unitB } = pairBonds;
|
||||
console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
|
||||
|
||||
for (const aI of pairLinks.linkedElementIndices) {
|
||||
for (const link of pairLinks.getBonds(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
|
||||
for (const aI of pairBonds.connectedIndices) {
|
||||
for (const bond of pairBonds.getEdges(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[bond.indexB])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,9 +115,10 @@ export function printSequence(model: Model) {
|
||||
console.log('\nSequence\n=============');
|
||||
const { byEntityKey } = model.sequence;
|
||||
for (const key of Object.keys(byEntityKey)) {
|
||||
const seq = byEntityKey[+key];
|
||||
console.log(`${seq.entityId} (${seq.sequence.kind} ${seq.num.value(0)} (offset ${seq.sequence.offset}), ${seq.num.value(seq.num.rowCount - 1)}) (${seq.compId.value(0)}, ${seq.compId.value(seq.compId.rowCount - 1)})`);
|
||||
console.log(`${seq.sequence.sequence}`);
|
||||
const { sequence, entityId } = byEntityKey[+key];
|
||||
const { seqId, compId } = sequence
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${Sequence.getSequenceString(sequence)}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
@@ -146,7 +152,7 @@ export function printRings(structure: Structure) {
|
||||
|
||||
export function printUnits(structure: Structure) {
|
||||
console.log('\nUnits\n=============');
|
||||
const l = StructureElement.Location.create();
|
||||
const l = StructureElement.Location.create(structure);
|
||||
|
||||
for (const unit of structure.units) {
|
||||
l.unit = unit;
|
||||
@@ -159,14 +165,14 @@ export function printUnits(structure: Structure) {
|
||||
console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`);
|
||||
|
||||
const props = StructureProperties.coarse;
|
||||
const seq = l.unit.model.sequence;
|
||||
const modelSeq = l.unit.model.sequence;
|
||||
|
||||
for (let j = 0, _j = Math.min(size, 3); j < _j; j++) {
|
||||
l.element = OrderedSet.getAt(elements, j);
|
||||
|
||||
const residues: string[] = [];
|
||||
const start = props.seq_id_begin(l), end = props.seq_id_end(l);
|
||||
const compId = seq.byEntityKey[props.entityKey(l)].compId.value;
|
||||
const compId = modelSeq.byEntityKey[props.entityKey(l)].sequence.compId.value;
|
||||
for (let e = start; e <= end; e++) residues.push(compId(e));
|
||||
console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`);
|
||||
}
|
||||
@@ -177,7 +183,8 @@ export function printUnits(structure: Structure) {
|
||||
|
||||
export function printSymmetryInfo(model: Model) {
|
||||
console.log('\nSymmetry Info\n=============');
|
||||
const { symmetry } = model;
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
if (!symmetry) return
|
||||
const { size, anglesInRadians } = symmetry.spacegroup.cell;
|
||||
console.log(`Spacegroup: ${symmetry.spacegroup.name} size: ${Vec3.toString(size)} angles: ${Vec3.toString(anglesInRadians)}`);
|
||||
console.log(`Assembly names: ${symmetry.assemblies.map(a => a.id).join(', ')}`);
|
||||
@@ -212,8 +219,8 @@ async function run(frame: CifFrame, args: Args) {
|
||||
if (args.units) printUnits(structure);
|
||||
if (args.sym) printSymmetryInfo(models[0]);
|
||||
if (args.rings) printRings(structure);
|
||||
if (args.intraLinks) printLinks(structure, true, false);
|
||||
if (args.interLinks) printLinks(structure, false, true);
|
||||
if (args.intraBonds) printBonds(structure, true, false);
|
||||
if (args.interBonds) printBonds(structure, false, true);
|
||||
if (args.mod) printModRes(models[0]);
|
||||
if (args.sec) printSecStructure(models[0]);
|
||||
}
|
||||
@@ -240,8 +247,8 @@ parser.addArgument(['--seq'], { help: 'print sequence', action: 'storeTrue' });
|
||||
parser.addArgument(['--units'], { help: 'print units', action: 'storeTrue' });
|
||||
parser.addArgument(['--sym'], { help: 'print symmetry', action: 'storeTrue' });
|
||||
parser.addArgument(['--rings'], { help: 'print rings', action: 'storeTrue' });
|
||||
parser.addArgument(['--intraLinks'], { help: 'print intra unit links', action: 'storeTrue' });
|
||||
parser.addArgument(['--interLinks'], { help: 'print inter unit links', action: 'storeTrue' });
|
||||
parser.addArgument(['--intraBonds'], { help: 'print intra unit bonds', action: 'storeTrue' });
|
||||
parser.addArgument(['--interBonds'], { help: 'print inter unit bonds', action: 'storeTrue' });
|
||||
parser.addArgument(['--mod'], { help: 'print modified residues', action: 'storeTrue' });
|
||||
parser.addArgument(['--sec'], { help: 'print secoundary structure', action: 'storeTrue' });
|
||||
interface Args {
|
||||
@@ -254,8 +261,8 @@ interface Args {
|
||||
units?: boolean,
|
||||
sym?: boolean,
|
||||
rings?: boolean,
|
||||
intraLinks?: boolean,
|
||||
interLinks?: boolean,
|
||||
intraBonds?: boolean,
|
||||
interBonds?: boolean,
|
||||
mod?: boolean,
|
||||
sec?: boolean,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Table } from '../../mol-data/db';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { Task } from '../../mol-task';
|
||||
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
|
||||
import { createEmptyTheme } from '../../mol-theme/theme';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
require('util.promisify').shim();
|
||||
@@ -40,7 +40,7 @@ function print(data: Volume) {
|
||||
}
|
||||
|
||||
async function doMesh(data: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, createEmptyTheme(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
|
||||
|
||||
// Export the mesh in OBJ format.
|
||||
@@ -77,8 +77,8 @@ async function run(url: string, meshFilename: string) {
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Info about VolumeData from mol-model module'
|
||||
addHelp: true,
|
||||
description: 'Info about VolumeData from mol-model module'
|
||||
});
|
||||
parser.addArgument([ '--emdb', '-e' ], {
|
||||
help: 'EMDB id, for example 8116',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,10 +8,10 @@ import { StateAction } from '../../../../mol-state';
|
||||
import { PluginContext } from '../../../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { Ingredient, CellPacking } from './data';
|
||||
import { Ingredient, CellPacking, Cell } from './data';
|
||||
import { getFromPdb, getFromCellPackDB } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext } from '../../../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF, MmcifFormat } 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';
|
||||
@@ -28,6 +28,10 @@ import { compile } from '../../../../mol-script/runtime/query/compiler';
|
||||
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
|
||||
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
|
||||
import { ColorTheme } from '../../../../mol-theme/color';
|
||||
import { 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';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`
|
||||
@@ -118,6 +122,112 @@ function getAssembly(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(name: string, 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[0]
|
||||
})
|
||||
|
||||
const curveModel = await curveModelTask.run()
|
||||
return getStructure(curveModel)
|
||||
}
|
||||
|
||||
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const { name, source, results, nbCurve } = ingredient
|
||||
|
||||
@@ -133,10 +243,12 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const model = await getModel(source.pdb || name, baseUrl)
|
||||
if (!model) return
|
||||
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
const transforms = nbCurve ? getCurveTransforms(ingredient) : getResultTransforms(results)
|
||||
const assembly = getAssembly(transforms, structure)
|
||||
return assembly
|
||||
if (nbCurve) {
|
||||
return getCurve(name, getCurveTransforms(ingredient), model)
|
||||
} else {
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
return getAssembly(getResultTransforms(results), structure)
|
||||
}
|
||||
}
|
||||
|
||||
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
|
||||
@@ -144,28 +256,34 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
const { ingredients, name } = packing
|
||||
const structures: Structure[] = []
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) ctx.update(iName)
|
||||
if (ctx.shouldUpdate) await ctx.update(iName)
|
||||
const s = await getIngredientStructure(ingredients[iName], baseUrl)
|
||||
if (s) structures.push(s)
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
|
||||
const builder = Structure.Builder({ label: name })
|
||||
let offsetInvariantId = 0
|
||||
for (const s of structures) {
|
||||
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
|
||||
let maxInvariantId = 0
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId
|
||||
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId
|
||||
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, invariantId)
|
||||
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId)
|
||||
}
|
||||
offsetInvariantId += maxInvariantId
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
|
||||
const s = builder.getStructure()
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
const RepresentationOptions = PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'] as const)
|
||||
type RepresentationName = (typeof RepresentationOptions)[0][0]
|
||||
|
||||
export const LoadCellPackModel = StateAction.build({
|
||||
display: { name: 'Load CellPack Model' },
|
||||
params: {
|
||||
@@ -175,15 +293,12 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
]),
|
||||
['curveTest', 'Curve Test'],
|
||||
] as const),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
preset: PD.Group({
|
||||
traceOnly: PD.Boolean(false),
|
||||
representation: PD.Select('spacefill', [
|
||||
['spacefill', 'Spacefill'],
|
||||
['gaussian-surface', 'Gaussian Surface'],
|
||||
['point', 'Point'],
|
||||
])
|
||||
representation: PD.Select('spacefill', RepresentationOptions)
|
||||
}, { isExpanded: true })
|
||||
},
|
||||
from: PSO.Root
|
||||
@@ -192,14 +307,50 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
const root = state.build().toRoot();
|
||||
|
||||
const cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
let cellPackBuilder: any
|
||||
|
||||
if (params.id === 'curveTest') {
|
||||
const url = `${params.baseUrl}/extras/rna_allpoints.json`
|
||||
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
|
||||
const { points } = await (new Response(data)).json() as { points: number[] }
|
||||
const curve0: Vec3[] = []
|
||||
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j))
|
||||
}
|
||||
const cell: Cell = {
|
||||
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
|
||||
compartments: {
|
||||
'CurveCompartment': {
|
||||
interior: {
|
||||
ingredients: {
|
||||
'CurveIngredient': {
|
||||
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
|
||||
results: [],
|
||||
name: 'RNA',
|
||||
nbCurve: 1,
|
||||
curve0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
} else {
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
|
||||
const { packings } = cellPackObject.data
|
||||
let tree = state.build().to(cellPackBuilder.ref);
|
||||
const tree = state.build().to(cellPackBuilder.ref);
|
||||
|
||||
const isHiv = (
|
||||
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
|
||||
@@ -235,17 +386,17 @@ export const LoadCellPackModel = StateAction.build({
|
||||
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number]
|
||||
const p = { packing: i, baseUrl: params.baseUrl }
|
||||
|
||||
const expression = params.preset.traceOnly
|
||||
? MS.struct.generator.atomGroups({
|
||||
let cellpackTree = tree.apply(StructureFromCellpack, p)
|
||||
if (params.preset.traceOnly) {
|
||||
const expression = MS.struct.generator.atomGroups({
|
||||
'atom-test': MS.core.logic.or([
|
||||
MS.core.rel.eq([MS.ammp('label_atom_id'), 'CA']),
|
||||
MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
|
||||
])
|
||||
})
|
||||
: MS.struct.generator.all()
|
||||
|
||||
tree.apply(StructureFromCellpack, p)
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } })
|
||||
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } }) as any
|
||||
}
|
||||
cellpackTree
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
|
||||
repr: getReprParams(ctx, params.preset),
|
||||
@@ -256,7 +407,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
if (isHiv) {
|
||||
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
|
||||
tree.apply(StateTransforms.Data.Download, { url, isBinary: true }, { state: { isGhost: true } })
|
||||
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
@@ -270,10 +421,12 @@ export const LoadCellPackModel = StateAction.build({
|
||||
)
|
||||
}
|
||||
|
||||
console.time('cellpack')
|
||||
await state.updateTree(tree).runInContext(taskCtx);
|
||||
console.timeEnd('cellpack')
|
||||
}));
|
||||
|
||||
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
|
||||
function getReprParams(ctx: PluginContext, params: { representation: RepresentationName, traceOnly: boolean }) {
|
||||
const { representation, traceOnly } = params
|
||||
switch (representation) {
|
||||
case 'spacefill':
|
||||
@@ -300,6 +453,11 @@ function getReprParams(ctx: PluginContext, params: { representation: 'spacefill'
|
||||
ctx.structureRepresentation.registry.get('point'),
|
||||
() => ({ ignoreHydrogens: true })
|
||||
] as [any, any]
|
||||
case 'ellipsoid':
|
||||
return [
|
||||
ctx.structureRepresentation.registry.get('orientation'),
|
||||
() => ({})
|
||||
] as [any, any]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ async function parseCif(data: string|Uint8Array) {
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function parsePDBfile(data: string) {
|
||||
const comp = parsePDB(data);
|
||||
async function parsePDBfile(data: string, id: string) {
|
||||
const comp = parsePDB(data, id);
|
||||
const parsed = await comp.run();
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
@@ -26,9 +26,9 @@ async function downloadCif(url: string, isBinary: boolean) {
|
||||
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
|
||||
}
|
||||
|
||||
async function downloadPDB(url: string) {
|
||||
async function downloadPDB(url: string, id: string) {
|
||||
const data = await fetch(url);
|
||||
return parsePDBfile(await data.text());
|
||||
return parsePDBfile(await data.text(), id);
|
||||
}
|
||||
|
||||
export async function getFromPdb(id: string) {
|
||||
@@ -42,6 +42,7 @@ function getCellPackDataUrl(id: string, baseUrl: string) {
|
||||
}
|
||||
|
||||
export async function getFromCellPackDB(id: string, baseUrl: string) {
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl));
|
||||
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
|
||||
return parsed;
|
||||
}
|
||||
@@ -1,171 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
// /**
|
||||
// * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
// *
|
||||
// * @author David Sehnal <david.sehnal@gmail.com>
|
||||
// */
|
||||
|
||||
import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
|
||||
import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { PluginStateObject } from '../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition } from '../../../mol-util/param-definition';
|
||||
import { PluginCommands } from '../../../mol-plugin/command';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
|
||||
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
import { UUID } from '../../../mol-util';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { Camera } from '../../../mol-canvas3d/camera';
|
||||
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
|
||||
// import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
|
||||
// import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
// import { createModelTree } from '../../../mol-plugin/state/actions/structure';
|
||||
// import { PluginContext } from '../../../mol-plugin/context';
|
||||
// import { PluginStateObject } from '../../../mol-plugin/state/objects';
|
||||
// import { ParamDefinition } from '../../../mol-util/param-definition';
|
||||
// import { PluginCommands } from '../../../mol-plugin/command';
|
||||
// import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
// import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
|
||||
// import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
// import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
// import { UUID } from '../../../mol-util';
|
||||
// import { ColorNames } from '../../../mol-util/color/names';
|
||||
// import { Camera } from '../../../mol-canvas3d/camera';
|
||||
// import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
|
||||
// import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
|
||||
|
||||
export const CreateJoleculeState = StateAction.build({
|
||||
display: { name: 'Jolecule State Import' },
|
||||
params: { id: ParamDefinition.Text('1mbo') },
|
||||
from: PluginStateObject.Root
|
||||
})(async ({ ref, state, params }, plugin: PluginContext) => {
|
||||
try {
|
||||
const id = params.id.trim().toLowerCase();
|
||||
const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
|
||||
// export const CreateJoleculeState = StateAction.build({
|
||||
// display: { name: 'Jolecule State Import' },
|
||||
// params: { id: ParamDefinition.Text('1mbo') },
|
||||
// from: PluginStateObject.Root
|
||||
// })(async ({ ref, state, params }, plugin: PluginContext) => {
|
||||
// try {
|
||||
// const id = params.id.trim().toLowerCase();
|
||||
// const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
|
||||
|
||||
data.sort((a, b) => a.order - b.order);
|
||||
// data.sort((a, b) => a.order - b.order);
|
||||
|
||||
await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
|
||||
plugin.state.snapshots.clear();
|
||||
// await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
|
||||
// plugin.state.snapshots.clear();
|
||||
|
||||
const template = createTemplate(plugin, state, id);
|
||||
const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
|
||||
for (const s of snapshots) {
|
||||
plugin.state.snapshots.add(s);
|
||||
}
|
||||
// const template = createTemplate(plugin, state, id);
|
||||
// const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
|
||||
// for (const s of snapshots) {
|
||||
// plugin.state.snapshots.add(s);
|
||||
// }
|
||||
|
||||
PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
|
||||
} catch (e) {
|
||||
plugin.log.error(`Jolecule Failed: ${e}`);
|
||||
}
|
||||
});
|
||||
// PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
|
||||
// } catch (e) {
|
||||
// plugin.log.error(`Jolecule Failed: ${e}`);
|
||||
// }
|
||||
// });
|
||||
|
||||
interface JoleculeSnapshot {
|
||||
order: number,
|
||||
distances: { i_atom1: number, i_atom2: number }[],
|
||||
labels: { i_atom: number, text: string }[],
|
||||
camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
|
||||
selected: number[],
|
||||
text: string
|
||||
}
|
||||
// interface JoleculeSnapshot {
|
||||
// order: number,
|
||||
// distances: { i_atom1: number, i_atom2: number }[],
|
||||
// labels: { i_atom: number, text: string }[],
|
||||
// camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
|
||||
// selected: number[],
|
||||
// text: string
|
||||
// }
|
||||
|
||||
function createTemplate(plugin: PluginContext, state: State, id: string) {
|
||||
const b = new StateBuilder.Root(state.tree);
|
||||
const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
|
||||
const model = createModelTree(data, 'cif');
|
||||
const structure = model.apply(StateTransforms.Model.StructureFromModel, {});
|
||||
complexRepresentation(plugin, structure, { hideWater: true });
|
||||
return { tree: b.getTree(), structure: structure.ref };
|
||||
}
|
||||
// function createTemplate(plugin: PluginContext, state: State, id: string) {
|
||||
// const b = new StateBuilder.Root(state.tree);
|
||||
// const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
|
||||
// const model = createModelTree(data, 'cif');
|
||||
// const structure = model.apply(StateTransforms.Model.StructureFromModel);
|
||||
// createDefaultStructureComplex(plugin, structure);
|
||||
// return { tree: b.getTree(), structure: structure.ref };
|
||||
// }
|
||||
|
||||
const labelOptions: ParamDefinition.Values<Text.Params> = {
|
||||
...ParamDefinition.getDefaultValues(Text.Params),
|
||||
tether: true,
|
||||
sizeFactor: 1.3,
|
||||
attachment: 'bottom-right',
|
||||
offsetZ: 10,
|
||||
background: true,
|
||||
backgroundMargin: 0.2,
|
||||
backgroundColor: ColorNames.skyblue,
|
||||
backgroundOpacity: 0.9
|
||||
}
|
||||
|
||||
// const distanceLabelOptions = {
|
||||
// const labelOptions: ParamDefinition.Values<Text.Params> = {
|
||||
// ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// sizeFactor: 1,
|
||||
// offsetX: 0,
|
||||
// offsetY: 0,
|
||||
// tether: true,
|
||||
// sizeFactor: 1.3,
|
||||
// attachment: 'bottom-right',
|
||||
// offsetZ: 10,
|
||||
// background: true,
|
||||
// backgroundMargin: 0.2,
|
||||
// backgroundColor: ColorNames.snow,
|
||||
// backgroundColor: ColorNames.skyblue,
|
||||
// backgroundOpacity: 0.9
|
||||
// }
|
||||
|
||||
function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
|
||||
const b = new StateBuilder.Root(template.tree);
|
||||
// // const distanceLabelOptions = {
|
||||
// // ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// // sizeFactor: 1,
|
||||
// // offsetX: 0,
|
||||
// // offsetY: 0,
|
||||
// // offsetZ: 10,
|
||||
// // background: true,
|
||||
// // backgroundMargin: 0.2,
|
||||
// // backgroundColor: ColorNames.snow,
|
||||
// // backgroundOpacity: 0.9
|
||||
// // }
|
||||
|
||||
let i = 0;
|
||||
for (const l of params.e.labels) {
|
||||
const expression = createExpression([l.i_atom]);
|
||||
const group = b.to(template.structure)
|
||||
.group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
|
||||
// function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
|
||||
// const b = new StateBuilder.Root(template.tree);
|
||||
|
||||
group
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
.apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
options: labelOptions
|
||||
});
|
||||
// let i = 0;
|
||||
// for (const l of params.e.labels) {
|
||||
// const expression = createExpression([l.i_atom]);
|
||||
// const group = b.to(template.structure)
|
||||
// .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
|
||||
|
||||
group
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
|
||||
}
|
||||
if (params.e.selected && params.e.selected.length > 0) {
|
||||
b.to(template.structure)
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
|
||||
}
|
||||
// TODO
|
||||
// for (const l of params.e.distances) {
|
||||
// b.to('structure')
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l. || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
// }
|
||||
return PluginStateSnapshotManager.Entry({
|
||||
id: UUID.create22(),
|
||||
data: { tree: StateTree.toJSON(b.getTree()) },
|
||||
camera: {
|
||||
current: getCameraSnapshot(params.e.camera),
|
||||
transitionStyle: 'animate',
|
||||
transitionDurationInMs: 350
|
||||
}
|
||||
}, {
|
||||
name: params.e.text
|
||||
});
|
||||
}
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
|
||||
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
const direction = Vec3.sub(Vec3.zero(), e.pos, e.in);
|
||||
Vec3.normalize(direction, direction);
|
||||
const up = Vec3.sub(Vec3.zero(), e.pos, e.up);
|
||||
Vec3.normalize(up, up);
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
|
||||
// }
|
||||
// if (params.e.selected && params.e.selected.length > 0) {
|
||||
// b.to(template.structure)
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
|
||||
// }
|
||||
// // TODO
|
||||
// // for (const l of params.e.distances) {
|
||||
// // b.to('structure')
|
||||
// // .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
|
||||
// // .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// // target: { name: 'static-text', params: { value: l. || '' } },
|
||||
// // options: labelOptions
|
||||
// // });
|
||||
// // }
|
||||
// return PluginStateSnapshotManager.Entry({
|
||||
// id: UUID.create22(),
|
||||
// data: { tree: StateTree.toJSON(b.getTree()) },
|
||||
// camera: {
|
||||
// current: getCameraSnapshot(params.e.camera),
|
||||
// transitionStyle: 'animate',
|
||||
// transitionDurationInMs: 350
|
||||
// }
|
||||
// }, {
|
||||
// name: params.e.text
|
||||
// });
|
||||
// }
|
||||
|
||||
const s: Camera.Snapshot = {
|
||||
mode: 'perspective',
|
||||
position: Vec3.scaleAndAdd(Vec3.zero(), e.pos, direction, e.slab.zoom),
|
||||
target: e.pos,
|
||||
direction,
|
||||
up,
|
||||
near: e.slab.zoom + e.slab.z_front,
|
||||
far: e.slab.zoom + e.slab.z_back,
|
||||
fogNear: e.slab.zoom + e.slab.z_front,
|
||||
fogFar: e.slab.zoom + e.slab.z_back,
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1
|
||||
};
|
||||
return s;
|
||||
}
|
||||
// function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
// const direction = Vec3.sub(Vec3(), e.pos, e.in);
|
||||
// Vec3.normalize(direction, direction);
|
||||
// const up = Vec3.sub(Vec3(), e.pos, e.up);
|
||||
// Vec3.normalize(up, up);
|
||||
|
||||
function createExpression(atomIndices: number[]) {
|
||||
if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
// const s: Camera.Snapshot = {
|
||||
// mode: 'perspective',
|
||||
// fov: Math.PI / 4,
|
||||
// position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
// target: e.pos,
|
||||
// radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
// fog: 50,
|
||||
// up,
|
||||
// };
|
||||
// return s;
|
||||
// }
|
||||
|
||||
return MS.struct.generator.atomGroups({
|
||||
'atom-test': atomIndices.length === 1
|
||||
? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
: MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
'group-by': 0
|
||||
});
|
||||
}
|
||||
// function createExpression(atomIndices: number[]) {
|
||||
// if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
|
||||
// return MS.struct.generator.atomGroups({
|
||||
// 'atom-test': atomIndices.length === 1
|
||||
// ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
// : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
// 'group-by': 0
|
||||
// });
|
||||
// }
|
||||
BIN
src/apps/viewer/favicon.ico
Executable file
BIN
src/apps/viewer/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>Mol* Viewer</title>
|
||||
<style>
|
||||
* {
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import '../../mol-util/polyfill';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import './favicon.ico'
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { CreateJoleculeState } from './extensions/jolecule';
|
||||
import { LoadCellPackModel } from './extensions/cellpack/model';
|
||||
import { StructureFromCellpack } from './extensions/cellpack/state';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
function getParam(name: string, regex: string): string {
|
||||
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
|
||||
@@ -26,7 +27,7 @@ function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [
|
||||
...DefaultPluginSpec.actions,
|
||||
PluginSpec.Action(CreateJoleculeState),
|
||||
// PluginSpec.Action(CreateJoleculeState),
|
||||
PluginSpec.Action(LoadCellPackModel),
|
||||
PluginSpec.Action(StructureFromCellpack),
|
||||
],
|
||||
@@ -41,7 +42,8 @@ function init() {
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
|
||||
}
|
||||
}
|
||||
},
|
||||
config: DefaultPluginSpec.config
|
||||
};
|
||||
const plugin = createPlugin(document.getElementById('app')!, spec);
|
||||
trySetSnapshot(plugin);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
|
||||
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
|
||||
const EvolutionaryConservationPalette: Color[] = [
|
||||
[255, 255, 129], // insufficient
|
||||
@@ -23,13 +25,13 @@ const EvolutionaryConservationPalette: Color[] = [
|
||||
const EvolutionaryConservationDefaultColor = Color(0x999999);
|
||||
|
||||
export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
isStatic: true,
|
||||
name: 'proteopedia-wrapper-evolutionary-conservation',
|
||||
display: 'Evolutionary Conservation',
|
||||
async getData(model: Model) {
|
||||
const id = model.label.toLowerCase();
|
||||
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await req.json();
|
||||
label: 'Evolutionary Conservation',
|
||||
type: 'static',
|
||||
async getData(model: Model, ctx: CustomProperty.Context) {
|
||||
const id = model.entryId.toLowerCase();
|
||||
const url = `https://proteopedia.org/cgi-bin/cnsrf?${id}`
|
||||
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
|
||||
const annotations = (json && json.residueAnnotations) || [];
|
||||
|
||||
const conservationMap = new Map<string, number>();
|
||||
@@ -65,7 +67,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
},
|
||||
defaultColor: EvolutionaryConservationDefaultColor
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
|
||||
return e ? `Evolutionary Conservation: ${e}` : void 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
== v3.4 ==
|
||||
|
||||
* Fixed HET group reset.
|
||||
* Updated core.
|
||||
* Removed Camera Cliping.
|
||||
|
||||
== v3.3 ==
|
||||
|
||||
* Camera Clipping.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import { Unit, StructureProperties, StructureElement, Link } from '../../mol-model/structure';
|
||||
import { Unit, StructureProperties, StructureElement, Bond } from '../../mol-model/structure';
|
||||
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Location } from '../../mol-model/location';
|
||||
@@ -54,7 +54,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
|
||||
|
||||
if (ctx.structure) {
|
||||
const l = StructureElement.Location.create()
|
||||
const l = StructureElement.Location.create(ctx.structure)
|
||||
const { models } = ctx.structure
|
||||
const asymIdSerialMap = new Map<string, number>()
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
@@ -71,7 +71,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const asym_id = getAsymId(location.unit);
|
||||
const o = asymIdSerialMap.get(asym_id(location)) || 0;
|
||||
return colors[o % colorCount].color;
|
||||
} else if (Link.isLocation(location)) {
|
||||
} else if (Bond.isLocation(location)) {
|
||||
const asym_id = getAsymId(location.aUnit)
|
||||
l.unit = location.aUnit
|
||||
l.element = location.aUnit.elements[location.aIndex]
|
||||
|
||||
@@ -9,6 +9,7 @@ import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/re
|
||||
import { BuiltInColorThemeName } from '../../mol-theme/color';
|
||||
import { AminoAcidNames } from '../../mol-model/structure/model/types';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
export interface ModelInfo {
|
||||
hetResidues: { name: string, indices: ResidueIndex[] }[],
|
||||
@@ -18,9 +19,9 @@ export interface ModelInfo {
|
||||
|
||||
export namespace ModelInfo {
|
||||
async function getPreferredAssembly(ctx: PluginContext, model: Model) {
|
||||
if (model.label.length <= 3) return void 0;
|
||||
if (model.entryId.length <= 3) return void 0;
|
||||
try {
|
||||
const id = model.label.toLowerCase();
|
||||
const id = model.entryId.toLowerCase();
|
||||
const src = await ctx.runTask(ctx.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${id}` })) as string;
|
||||
const json = JSON.parse(src);
|
||||
const data = json && json[id];
|
||||
@@ -72,10 +73,11 @@ export namespace ModelInfo {
|
||||
}
|
||||
|
||||
const preferredAssemblyId = await pref;
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
|
||||
return {
|
||||
hetResidues: hetResidues,
|
||||
assemblies: model.symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })),
|
||||
assemblies: symmetry ? symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })) : [],
|
||||
preferredAssemblyId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -133,8 +133,8 @@
|
||||
addControl('Reset Position', () => PluginWrapper.camera.resetPosition());
|
||||
addControl('Toggle Spin', () => PluginWrapper.camera.toggleSpin());
|
||||
// Same as "wheel icon" and Viewport options
|
||||
addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
|
||||
@@ -28,16 +28,15 @@ import { BuiltInColorThemes } from '../../mol-theme/color';
|
||||
import { BuiltInSizeThemes } from '../../mol-theme/size';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { ParamDefinition } from '../../mol-util/param-definition';
|
||||
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
// import { Vec3 } from 'mol-math/linear-algebra';
|
||||
// import { ParamDefinition } from 'mol-util/param-definition';
|
||||
// import { Text } from 'mol-geo/geometry/text/text';
|
||||
require('../../mol-plugin/skin/light.scss')
|
||||
require('../../mol-plugin-ui/skin/light.scss')
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
static VERSION_MAJOR = 3;
|
||||
static VERSION_MINOR = 3;
|
||||
static VERSION_MINOR = 4;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -69,9 +68,9 @@ class MolStarProteopediaWrapper {
|
||||
const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
|
||||
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
|
||||
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
|
||||
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
|
||||
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
|
||||
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
|
||||
}
|
||||
|
||||
get state() {
|
||||
@@ -95,7 +94,7 @@ class MolStarProteopediaWrapper {
|
||||
const model = this.state.build().to(StateElements.Model);
|
||||
|
||||
const s = model
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [EvolutionaryConservation.propertyProvider.descriptor.name], properties: {} }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
|
||||
|
||||
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
|
||||
@@ -161,9 +160,9 @@ class MolStarProteopediaWrapper {
|
||||
root.delete(StateElements.WaterVisual);
|
||||
} else {
|
||||
root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.water && style.water.kind) || 'ball-and-stick',
|
||||
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.water && style.water.kind) || 'ball-and-stick',
|
||||
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,11 +230,13 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const renderer = this.plugin.canvas3d.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
|
||||
}
|
||||
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
@@ -298,7 +299,7 @@ class MolStarProteopediaWrapper {
|
||||
// }
|
||||
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
|
||||
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
if (!params || !!params.sequence) {
|
||||
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
@@ -315,9 +316,11 @@ class MolStarProteopediaWrapper {
|
||||
experimentalData = {
|
||||
init: async (parent: Element) => {
|
||||
const asm = this.state.select(StateElements.Assembly)[0].obj!;
|
||||
const params = ParamDefinition.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin));
|
||||
params.behaviorRef = StateElements.VolumeStreaming;
|
||||
const params = InitVolumeStreaming.createDefaultParams(asm, this.plugin);
|
||||
params.options.behaviorRef = StateElements.VolumeStreaming;
|
||||
params.defaultView = 'box';
|
||||
params.options.channelParams['fo-fc(+ve)'] = { wireframe: true };
|
||||
params.options.channelParams['fo-fc(-ve)'] = { wireframe: true };
|
||||
await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
|
||||
this.experimentalDataElement = parent;
|
||||
volumeStreamingControls(this.plugin, parent);
|
||||
@@ -335,7 +338,7 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
hetGroups = {
|
||||
reset: () => {
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocus);
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
|
||||
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
},
|
||||
@@ -382,7 +385,8 @@ class MolStarProteopediaWrapper {
|
||||
// const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
|
||||
// Vec3.normalize(position, position);
|
||||
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
|
||||
const snapshot = this.plugin.canvas3d.camera.getFocus(sphere.center, Math.max(sphere.radius, 5));
|
||||
const radius = Math.max(sphere.radius, 5)
|
||||
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius, radius);
|
||||
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
|
||||
}
|
||||
}
|
||||
@@ -414,8 +418,7 @@ class MolStarProteopediaWrapper {
|
||||
},
|
||||
download: async (url: string) => {
|
||||
try {
|
||||
const data = await this.plugin.runTask(this.plugin.fetch({ url }));
|
||||
const snapshot = JSON.parse(data);
|
||||
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
|
||||
await this.plugin.state.setSnapshot(snapshot);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { PluginUIComponent } from '../../../mol-plugin/ui/base';
|
||||
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
|
||||
import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
|
||||
import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
|
||||
import { PluginUIComponent } from '../../../mol-plugin-ui/base';
|
||||
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin-ui/plugin';
|
||||
import { AnimationControls } from '../../../mol-plugin-ui/state/animation';
|
||||
import { CameraSnapshots } from '../../../mol-plugin-ui/camera';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
|
||||
import { StateElements } from '../helpers';
|
||||
|
||||
export class ControlsWrapper extends PluginUIComponent {
|
||||
|
||||
@@ -28,9 +28,9 @@ function messageTree(root: Progress.Node, prefix = ''): string {
|
||||
|
||||
function createTask<T>(delayMs: number, r: T): Task<T> {
|
||||
return Task.create('delayed value ' + r, async ctx => {
|
||||
ctx.update('Processing delayed... ' + r, true);
|
||||
ctx.update(`Processing delayed ${r} after ${delayMs}ms`, true);
|
||||
await Scheduler.delay(delayMs);
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'hello from delayed... ' });
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: `hello from delayed ${r} ${delayMs}` });
|
||||
return r;
|
||||
}, () => console.log('On abort called ' + r));
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export function testTree() {
|
||||
const r = await c1 + await c2 + await c3;
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'Almost done...' });
|
||||
return r + 1;
|
||||
});
|
||||
}, () => console.log('On abort O'));
|
||||
}
|
||||
|
||||
export type ChunkedState = { i: number, current: number, total: number }
|
||||
@@ -115,7 +115,7 @@ async function test() {
|
||||
// const r = await Run(testTree(), abortingObserver, 250);
|
||||
// console.log(r);
|
||||
|
||||
const m = await ms({ i: 10 }).run(logP);
|
||||
const m = await testTree().run(abortingObserver, 50);
|
||||
console.log(m);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export interface ColorThemeComponentProps {
|
||||
colorTheme: ColorTheme<any>
|
||||
}
|
||||
|
||||
export interface ColorThemeComponentState {
|
||||
|
||||
}
|
||||
|
||||
export class ColorThemeComponent extends React.Component<ColorThemeComponentProps, ColorThemeComponentState> {
|
||||
state = {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const ct = this.props.colorTheme
|
||||
return <div>
|
||||
<span>Color Theme Info </span>
|
||||
|
||||
{ct.description ? <div><i>{ct.description}</i></div> : ''}
|
||||
{
|
||||
ct.legend && ct.legend.kind === 'scale-legend'
|
||||
? <div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '30px',
|
||||
background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`
|
||||
}}
|
||||
>
|
||||
<span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span>
|
||||
<span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span>
|
||||
</div>
|
||||
: ct.legend && ct.legend.kind === 'table-legend'
|
||||
? <div>
|
||||
{ct.legend.table.map((value, i) => {
|
||||
const [name, color] = value
|
||||
return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}>
|
||||
<div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div>
|
||||
{name}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,11 @@
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
|
||||
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { Object3D } from '../mol-gl/object3d';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
|
||||
export { Camera }
|
||||
|
||||
// TODO: slab controls that modify near/far planes?
|
||||
|
||||
class Camera implements Object3D {
|
||||
readonly updatedViewProjection = new BehaviorSubject<Camera>(this);
|
||||
|
||||
class Camera {
|
||||
readonly view: Mat4 = Mat4.identity();
|
||||
readonly projection: Mat4 = Mat4.identity();
|
||||
readonly projectionView: Mat4 = Mat4.identity();
|
||||
@@ -32,14 +26,17 @@ class Camera implements Object3D {
|
||||
width: 1, height: 1
|
||||
}
|
||||
|
||||
near = 1
|
||||
far = 10000
|
||||
fogNear = 5000
|
||||
fogFar = 10000
|
||||
zoom = 1
|
||||
|
||||
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
|
||||
|
||||
get position() { return this.state.position; }
|
||||
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
|
||||
|
||||
get direction() { return this.state.direction; }
|
||||
set direction(v: Vec3) { Vec3.copy(this.state.direction, v); }
|
||||
|
||||
get up() { return this.state.up; }
|
||||
set up(v: Vec3) { Vec3.copy(this.state.up, v); }
|
||||
|
||||
@@ -51,10 +48,12 @@ class Camera implements Object3D {
|
||||
private deltaDirection = Vec3.zero();
|
||||
private newPosition = Vec3.zero();
|
||||
|
||||
updateMatrices() {
|
||||
update() {
|
||||
const snapshot = this.state as Camera.Snapshot;
|
||||
const height = 2 * Math.tan(snapshot.fov / 2) * Vec3.distance(snapshot.position, snapshot.target);
|
||||
snapshot.zoom = this.viewport.height / height;
|
||||
this.zoom = this.viewport.height / height;
|
||||
|
||||
updateClip(this);
|
||||
|
||||
switch (this.state.mode) {
|
||||
case 'orthographic': updateOrtho(this); break;
|
||||
@@ -62,11 +61,7 @@ class Camera implements Object3D {
|
||||
default: throw new Error('unknown camera mode');
|
||||
}
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON.Value) || !Mat4.areEqual(this.view, this.prevView, EPSILON.Value);
|
||||
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
Mat4.invert(this.inverseProjectionView, this.projectionView)
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
|
||||
|
||||
if (changed) {
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
@@ -74,57 +69,47 @@ class Camera implements Object3D {
|
||||
|
||||
Mat4.copy(this.prevView, this.view);
|
||||
Mat4.copy(this.prevProjection, this.projection);
|
||||
this.updatedViewProjection.next(this);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
setState(snapshot: Partial<Camera.Snapshot>) {
|
||||
this.transition.apply(snapshot);
|
||||
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
|
||||
this.transition.apply(snapshot, durationMs);
|
||||
}
|
||||
|
||||
getSnapshot() {
|
||||
const ret = Camera.createDefaultSnapshot();
|
||||
Camera.copySnapshot(ret, this.state);
|
||||
return ret;
|
||||
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
|
||||
}
|
||||
|
||||
getFocus(target: Vec3, radius: number, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
getFocus(target: Vec3, radiusNear: number, radiusFar: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
const fov = this.state.fov
|
||||
const { width, height } = this.viewport
|
||||
const aspect = width / height
|
||||
const aspectFactor = (height < width ? 1 : aspect)
|
||||
const currentDistance = Vec3.distance(this.state.position, target)
|
||||
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
|
||||
const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
|
||||
|
||||
const deltaDistance = Math.abs(currentDistance - targetDistance)
|
||||
Vec3.sub(this.deltaDirection, this.target, this.position)
|
||||
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
|
||||
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance)
|
||||
Vec3.sub(this.newPosition, target, this.deltaDirection)
|
||||
|
||||
if (dir) {
|
||||
Vec3.setMagnitude(this.deltaDirection, dir, targetDistance)
|
||||
Vec3.add(this.newPosition, target, this.deltaDirection)
|
||||
} else {
|
||||
Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
|
||||
if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
|
||||
Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
|
||||
state.target = Vec3.clone(target)
|
||||
state.radiusNear = radiusNear
|
||||
state.radiusFar = radiusFar
|
||||
state.position = Vec3.clone(this.newPosition)
|
||||
if (up) Vec3.matchDirection(state.up, up, state.up)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
focus(target: Vec3, radiusNear: number, radiusFar: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
|
||||
if (radiusNear > 0 && radiusFar > 0) {
|
||||
this.setState(this.getFocus(target, radiusNear, radiusFar, up, dir), durationMs);
|
||||
}
|
||||
|
||||
return { target, position: Vec3.clone(this.newPosition) };
|
||||
}
|
||||
|
||||
focus(target: Vec3, radius: number, dir?: Vec3) {
|
||||
if (radius > 0) this.setState(this.getFocus(target, radius, dir));
|
||||
}
|
||||
|
||||
// lookAt(target: Vec3) {
|
||||
// cameraLookAt(this.position, this.up, this.direction, target);
|
||||
// }
|
||||
|
||||
// translate(v: Vec3) {
|
||||
// Vec3.add(this.position, this.position, v);
|
||||
// cameraLookAt(this.position, this.up, this.direction, this.target);
|
||||
// }
|
||||
|
||||
project(out: Vec4, point: Vec3) {
|
||||
return cameraProject(out, point, this.viewport, this.projectionView)
|
||||
}
|
||||
@@ -133,10 +118,6 @@ class Camera implements Object3D {
|
||||
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.updatedViewProjection.complete();
|
||||
}
|
||||
|
||||
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) {
|
||||
this.viewport = viewport;
|
||||
Camera.copySnapshot(this.state, state);
|
||||
@@ -147,13 +128,6 @@ class Camera implements Object3D {
|
||||
namespace Camera {
|
||||
export type Mode = 'perspective' | 'orthographic'
|
||||
|
||||
export interface ClippingInfo {
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offseted view in a larger frustum. This is useful for
|
||||
* - multi-window or multi-monitor/multi-machine setups
|
||||
@@ -181,64 +155,54 @@ namespace Camera {
|
||||
export function createDefaultSnapshot(): Snapshot {
|
||||
return {
|
||||
mode: 'perspective',
|
||||
fov: Math.PI / 4,
|
||||
|
||||
position: Vec3.create(0, 0, 100),
|
||||
direction: Vec3.create(0, 0, 1),
|
||||
up: Vec3.create(0, 1, 0),
|
||||
|
||||
target: Vec3.create(0, 0, 0),
|
||||
|
||||
near: 1,
|
||||
far: 10000,
|
||||
fogNear: 1,
|
||||
fogFar: 10000,
|
||||
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1,
|
||||
radiusNear: 10,
|
||||
radiusFar: 10,
|
||||
fog: 50,
|
||||
clipFar: true
|
||||
};
|
||||
}
|
||||
|
||||
export interface Snapshot {
|
||||
mode: Mode,
|
||||
mode: Mode
|
||||
fov: number
|
||||
|
||||
position: Vec3,
|
||||
// Normalized camera direction
|
||||
direction: Vec3,
|
||||
up: Vec3,
|
||||
target: Vec3,
|
||||
position: Vec3
|
||||
up: Vec3
|
||||
target: Vec3
|
||||
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number,
|
||||
|
||||
fov: number,
|
||||
zoom: number,
|
||||
radiusNear: number
|
||||
radiusFar: number
|
||||
fog: number
|
||||
clipFar: boolean
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
if (!source) return;
|
||||
if (!source) return out;
|
||||
|
||||
if (typeof source.mode !== 'undefined') out.mode = source.mode;
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
|
||||
if (typeof source.position !== 'undefined') Vec3.copy(out.position, source.position);
|
||||
if (typeof source.direction !== 'undefined') Vec3.copy(out.direction, source.direction);
|
||||
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
|
||||
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
|
||||
|
||||
if (typeof source.near !== 'undefined') out.near = source.near;
|
||||
if (typeof source.far !== 'undefined') out.far = source.far;
|
||||
if (typeof source.fogNear !== 'undefined') out.fogNear = source.fogNear;
|
||||
if (typeof source.fogFar !== 'undefined') out.fogFar = source.fogFar;
|
||||
if (typeof source.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
|
||||
if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
|
||||
if (typeof source.fog !== 'undefined') out.fog = source.fog;
|
||||
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
|
||||
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
if (typeof source.zoom !== 'undefined') out.zoom = source.zoom;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
const _center = Vec3.zero();
|
||||
function updateOrtho(camera: Camera) {
|
||||
const { viewport, state: { zoom, near, far }, viewOffset } = camera
|
||||
const { viewport, zoom, near, far, viewOffset } = camera
|
||||
|
||||
const fullLeft = -(viewport.width - viewport.x) / 2
|
||||
const fullRight = (viewport.width - viewport.x) / 2
|
||||
@@ -270,16 +234,15 @@ function updateOrtho(camera: Camera) {
|
||||
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updatePers(camera: Camera) {
|
||||
const aspect = camera.viewport.width / camera.viewport.height
|
||||
|
||||
const { state: { fov, near, far }, viewOffset } = camera
|
||||
const { near, far, viewOffset } = camera
|
||||
|
||||
let top = near * Math.tan(0.5 * fov)
|
||||
let top = near * Math.tan(0.5 * camera.state.fov)
|
||||
let height = 2 * top
|
||||
let width = aspect * height
|
||||
let left = -0.5 * width
|
||||
@@ -295,6 +258,31 @@ function updatePers(camera: Camera) {
|
||||
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
|
||||
|
||||
const cDist = Vec3.distance(camera.position, camera.target)
|
||||
let near = cDist - radiusNear
|
||||
let far = cDist + (clipFar ? radiusNear : radiusFar)
|
||||
|
||||
const fogNearFactor = -(50 - fog) / 50
|
||||
let fogNear = cDist - (radiusNear * fogNearFactor)
|
||||
let fogFar = far
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(5, near)
|
||||
far = Math.max(5, far)
|
||||
} else {
|
||||
near = Math.max(0, near)
|
||||
far = Math.max(0, far)
|
||||
}
|
||||
|
||||
camera.near = near;
|
||||
camera.far = far;
|
||||
camera.fogNear = fogNear;
|
||||
camera.fogFar = fogFar;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -22,7 +22,7 @@ class CameraTransitionManager {
|
||||
private current = Camera.createDefaultSnapshot();
|
||||
|
||||
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
|
||||
if (durationMs <= 0 || to.mode !== this.camera.state.mode) {
|
||||
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
|
||||
this.finish(to);
|
||||
return;
|
||||
}
|
||||
@@ -68,33 +68,23 @@ class CameraTransitionManager {
|
||||
namespace CameraTransitionManager {
|
||||
export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void
|
||||
|
||||
const _rot = Quat.identity();
|
||||
export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void {
|
||||
Camera.copySnapshot(out, target);
|
||||
|
||||
// Rotate direction
|
||||
const rot = Quat.identity();
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.direction, target.direction), t);
|
||||
Vec3.transformQuat(out.direction, source.direction, rot);
|
||||
|
||||
// Rotate up
|
||||
Quat.setIdentity(rot);
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, rot);
|
||||
Quat.slerp(_rot, Quat.Identity, Quat.rotationTo(_rot, source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, _rot);
|
||||
|
||||
// Lerp target
|
||||
// Lerp target, position & radius
|
||||
Vec3.lerp(out.target, source.target, target.target, t);
|
||||
Vec3.lerp(out.position, source.position, target.position, t);
|
||||
out.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
|
||||
// TODO take change of `clipFar` into account
|
||||
out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
|
||||
|
||||
// Update position
|
||||
const dist = -lerp(Vec3.distance(source.position, source.target), Vec3.distance(target.position, target.target), t);
|
||||
Vec3.scale(out.position, out.direction, dist);
|
||||
Vec3.add(out.position, out.position, out.target);
|
||||
|
||||
// Lerp other props
|
||||
out.zoom = lerp(source.zoom, target.zoom, t);
|
||||
// Lerp fov & fog
|
||||
out.fov = lerp(source.fov, target.fov, t);
|
||||
out.near = lerp(source.near, target.near, t);
|
||||
out.far = lerp(source.far, target.far, t);
|
||||
out.fogNear = lerp(source.fogNear, target.fogNear, t);
|
||||
out.fogFar = lerp(source.fogFar, target.fogFar, t);
|
||||
out.fog = lerp(source.fog, target.fog, t);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
|
||||
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra'
|
||||
|
||||
export { Viewport }
|
||||
|
||||
@@ -55,31 +55,6 @@ namespace Viewport {
|
||||
|
||||
//
|
||||
|
||||
const tmpVec3 = Vec3()
|
||||
|
||||
/** Modifies the direction & up vectors in place, both are normalized */
|
||||
export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
|
||||
Vec3.sub(tmpVec3, target, position)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
|
||||
if (!Vec3.isZero(tmpVec3)) {
|
||||
// change direction vector to look at target
|
||||
const d = Vec3.dot(tmpVec3, up)
|
||||
if (Math.abs(d - 1) < EPSILON.Value) { // parallel
|
||||
Vec3.scale(up, direction, -1)
|
||||
} else if (Math.abs(d + 1) < EPSILON.Value) { // anti parallel
|
||||
Vec3.copy(up, direction)
|
||||
}
|
||||
Vec3.copy(direction, tmpVec3)
|
||||
|
||||
// normalize up vector
|
||||
Vec3.cross(tmpVec3, direction, up)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
Vec3.cross(up, tmpVec3, direction)
|
||||
Vec3.normalize(up, up)
|
||||
}
|
||||
}
|
||||
|
||||
const NEAR_RANGE = 0
|
||||
const FAR_RANGE = 1
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 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>
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
@@ -26,20 +27,21 @@ import { SetUtils } from '../mol-util/set';
|
||||
import { Canvas3dInteractionHelper } from './helper/interaction-events';
|
||||
import { PostprocessingParams, PostprocessingPass } from './passes/postprocessing';
|
||||
import { MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
|
||||
import { GLRenderingContext } from '../mol-gl/webgl/compat';
|
||||
import { PixelData } from '../mol-util/image';
|
||||
import { readTexture } from '../mol-gl/compute/util';
|
||||
import { DrawPass } from './passes/draw';
|
||||
import { PickPass } from './passes/pick';
|
||||
import { Task } from '../mol-task';
|
||||
import { ImagePass, ImageProps } from './passes/image';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { isDebugMode } from '../mol-util/debug';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
// TODO: FPS cap?
|
||||
// maxFps: PD.Numeric(30),
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
|
||||
cameraClipDistance: PD.Numeric(0, { min: 0.0, max: 50.0, step: 0.1 }, { description: 'The distance between camera and scene at which to clip regardless of near clipping plane.' }),
|
||||
clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
|
||||
fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
|
||||
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
|
||||
cameraClipFar: PD.Boolean(true),
|
||||
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
|
||||
transparentBackground: PD.Boolean(false),
|
||||
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
@@ -55,28 +57,33 @@ export { Canvas3D }
|
||||
interface Canvas3D {
|
||||
readonly webgl: WebGLContext,
|
||||
|
||||
add: (repr: Representation.Any) => void
|
||||
remove: (repr: Representation.Any) => void
|
||||
update: (repr?: Representation.Any, keepBoundingSphere?: boolean) => void
|
||||
clear: () => void
|
||||
add(repr: Representation.Any): void
|
||||
remove(repr: Representation.Any): void
|
||||
/**
|
||||
* This function must be called if animate() is not set up so that add/remove actions take place.
|
||||
*/
|
||||
commit(): void
|
||||
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
|
||||
clear(): void
|
||||
|
||||
// draw: (force?: boolean) => void
|
||||
requestDraw: (force?: boolean) => void
|
||||
animate: () => void
|
||||
identify: (x: number, y: number) => PickingId | undefined
|
||||
mark: (loci: Representation.Loci, action: MarkerAction) => void
|
||||
getLoci: (pickingId: PickingId) => Representation.Loci
|
||||
requestDraw(force?: boolean): void
|
||||
animate(): void
|
||||
identify(x: number, y: number): PickingId | undefined
|
||||
mark(loci: Representation.Loci, action: MarkerAction): void
|
||||
getLoci(pickingId: PickingId): Representation.Loci
|
||||
|
||||
readonly didDraw: BehaviorSubject<now.Timestamp>
|
||||
readonly reprCount: BehaviorSubject<number>
|
||||
|
||||
handleResize: () => void
|
||||
handleResize(): void
|
||||
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
|
||||
resetCamera: () => void
|
||||
requestCameraReset(): void
|
||||
readonly camera: Camera
|
||||
downloadScreenshot: () => void
|
||||
getPixelData: (variant: GraphicsRenderVariant) => PixelData
|
||||
setProps: (props: Partial<Canvas3DProps>) => void
|
||||
readonly boundingSphere: Readonly<Sphere3D>
|
||||
downloadScreenshot(): void
|
||||
getPixelData(variant: GraphicsRenderVariant): PixelData
|
||||
setProps(props: Partial<Canvas3DProps>): void
|
||||
getImagePass(): ImagePass
|
||||
|
||||
/** Returns a copy of the current Canvas3D instance props */
|
||||
readonly props: Readonly<Canvas3DProps>
|
||||
@@ -84,29 +91,65 @@ interface Canvas3D {
|
||||
readonly stats: RendererStats
|
||||
readonly interaction: Canvas3dInteractionHelper['events']
|
||||
|
||||
dispose: () => void
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
|
||||
const DefaultRunTask = (task: Task<unknown>) => task.run()
|
||||
|
||||
namespace Canvas3D {
|
||||
export interface HighlightEvent { current: Representation.Loci, modifiers?: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
|
||||
const gl = getGLContext(canvas, {
|
||||
alpha: false,
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
depth: true,
|
||||
preserveDrawingBuffer: true
|
||||
preserveDrawingBuffer: true,
|
||||
premultipliedAlpha: false,
|
||||
})
|
||||
if (gl === null) throw new Error('Could not create a WebGL rendering context')
|
||||
const input = InputObserver.fromElement(canvas)
|
||||
return Canvas3D.create(gl, input, props, runTask)
|
||||
const webgl = createContext(gl)
|
||||
|
||||
if (isDebugMode) {
|
||||
const loseContextExt = gl.getExtension('WEBGL_lose_context')
|
||||
if (loseContextExt) {
|
||||
canvas.addEventListener('mousedown', e => {
|
||||
if (webgl.isContextLost) return
|
||||
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return
|
||||
|
||||
console.log('lose context')
|
||||
loseContextExt.loseContext()
|
||||
|
||||
setTimeout(() => {
|
||||
if (!webgl.isContextLost) return
|
||||
console.log('restore context')
|
||||
loseContextExt.restoreContext()
|
||||
}, 1000)
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.khronos.org/webgl/wiki/HandlingContextLost
|
||||
|
||||
canvas.addEventListener('webglcontextlost', e => {
|
||||
webgl.setContextLost()
|
||||
e.preventDefault()
|
||||
if (isDebugMode) console.log('context lost')
|
||||
}, false)
|
||||
|
||||
canvas.addEventListener('webglcontextrestored', () => {
|
||||
if (!webgl.isContextLost) return
|
||||
webgl.handleContextRestored()
|
||||
if (isDebugMode) console.log('context restored')
|
||||
}, false)
|
||||
|
||||
return Canvas3D.create(webgl, input, props, runTask)
|
||||
}
|
||||
|
||||
export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
|
||||
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
|
||||
const p = { ...DefaultCanvas3DParams, ...props }
|
||||
|
||||
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
|
||||
@@ -116,31 +159,37 @@ namespace Canvas3D {
|
||||
const startTime = now()
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
|
||||
|
||||
const camera = new Camera({
|
||||
near: 0.1,
|
||||
far: 10000,
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode
|
||||
})
|
||||
|
||||
const webgl = createContext(gl)
|
||||
const { gl, contextRestored } = webgl
|
||||
|
||||
let width = gl.drawingBufferWidth
|
||||
let height = gl.drawingBufferHeight
|
||||
|
||||
const scene = Scene.create(webgl)
|
||||
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode,
|
||||
fog: p.cameraFog,
|
||||
clipFar: p.cameraClipFar
|
||||
})
|
||||
|
||||
const controls = TrackballControls.create(input, camera, p.trackball)
|
||||
const renderer = Renderer.create(webgl, camera, p.renderer)
|
||||
const renderer = Renderer.create(webgl, p.renderer)
|
||||
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
|
||||
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, 0.5)
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
|
||||
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
|
||||
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
|
||||
|
||||
const contextRestoredSub = contextRestored.subscribe(() => {
|
||||
pickPass.pickDirty = true
|
||||
draw(true)
|
||||
})
|
||||
|
||||
let drawPending = false
|
||||
let cameraResetRequested: boolean | Vec3 = false
|
||||
let cameraResetRequested = false
|
||||
|
||||
function getLoci(pickingId: PickingId) {
|
||||
let loci: Loci = EmptyLoci
|
||||
@@ -148,7 +197,9 @@ namespace Canvas3D {
|
||||
reprRenderObjects.forEach((_, _repr) => {
|
||||
const _loci = _repr.getLoci(pickingId)
|
||||
if (!isEmptyLoci(_loci)) {
|
||||
if (!isEmptyLoci(loci)) console.warn('found another loci')
|
||||
if (!isEmptyLoci(loci)) {
|
||||
console.warn('found another loci, this should not happen')
|
||||
}
|
||||
loci = _loci
|
||||
repr = _repr
|
||||
}
|
||||
@@ -172,76 +223,35 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
let currentNear = -1, currentFar = -1, currentFogNear = -1, currentFogFar = -1
|
||||
function setClipping() {
|
||||
const cDist = Vec3.distance(camera.state.position, camera.state.target)
|
||||
const bRadius = Math.max(10, scene.boundingSphere.radius)
|
||||
|
||||
const nearFactor = (50 - p.clip[0]) / 50
|
||||
const farFactor = -(50 - p.clip[1]) / 50
|
||||
let near = cDist - (bRadius * nearFactor)
|
||||
let far = cDist + (bRadius * farFactor)
|
||||
|
||||
const fogNearFactor = (50 - p.fog[0]) / 50
|
||||
const fogFarFactor = -(50 - p.fog[1]) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + (bRadius * fogFarFactor)
|
||||
|
||||
if (camera.state.mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(5, p.cameraClipDistance, near)
|
||||
far = Math.max(5, far)
|
||||
fogNear = Math.max(5, fogNear)
|
||||
fogFar = Math.max(5, fogFar)
|
||||
} else if (camera.state.mode === 'orthographic') {
|
||||
if (p.cameraClipDistance > 0) {
|
||||
near = Math.max(p.cameraClipDistance, near)
|
||||
}
|
||||
}
|
||||
|
||||
if (near !== currentNear || far !== currentFar || fogNear !== currentFogNear || fogFar !== currentFogFar) {
|
||||
camera.setState({ near, far, fogNear, fogFar })
|
||||
currentNear = near, currentFar = far, currentFogNear = fogNear, currentFogFar = fogFar
|
||||
}
|
||||
}
|
||||
|
||||
function render(variant: 'pick' | 'draw', force: boolean) {
|
||||
if (scene.isCommiting) return false
|
||||
function render(force: boolean) {
|
||||
if (webgl.isContextLost) return false
|
||||
|
||||
let didRender = false
|
||||
controls.update(currentTime);
|
||||
// TODO: is this a good fix? Also, setClipping does not work if the user has manually set a clipping plane.
|
||||
if (!camera.transition.inTransition) setClipping();
|
||||
const cameraChanged = camera.updateMatrices();
|
||||
controls.update(currentTime)
|
||||
Viewport.set(camera.viewport, 0, 0, width, height)
|
||||
const cameraChanged = camera.update()
|
||||
multiSample.update(force || cameraChanged, currentTime)
|
||||
|
||||
if (force || cameraChanged || multiSample.enabled) {
|
||||
switch (variant) {
|
||||
case 'pick':
|
||||
pickPass.render()
|
||||
break;
|
||||
case 'draw':
|
||||
renderer.setViewport(0, 0, width, height);
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render()
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
break;
|
||||
renderer.setViewport(0, 0, width, height)
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render(true, p.transparentBackground)
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled, p.transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
didRender = true
|
||||
}
|
||||
|
||||
return didRender && cameraChanged;
|
||||
return didRender;
|
||||
}
|
||||
|
||||
let forceNextDraw = false;
|
||||
let currentTime = 0;
|
||||
|
||||
function draw(force?: boolean) {
|
||||
if (render('draw', !!force || forceNextDraw)) {
|
||||
if (render(!!force || forceNextDraw)) {
|
||||
didDraw.next(now() - startTime as now.Timestamp)
|
||||
}
|
||||
forceNextDraw = false;
|
||||
@@ -256,46 +266,88 @@ namespace Canvas3D {
|
||||
|
||||
function animate() {
|
||||
currentTime = now();
|
||||
|
||||
commit();
|
||||
|
||||
camera.transition.tick(currentTime);
|
||||
|
||||
draw(false);
|
||||
if (!camera.transition.inTransition) interactionHelper.tick(currentTime);
|
||||
if (!camera.transition.inTransition && !webgl.isContextLost) {
|
||||
interactionHelper.tick(currentTime);
|
||||
}
|
||||
requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
function identify(x: number, y: number): PickingId | undefined {
|
||||
return pickPass.identify(x, y)
|
||||
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
|
||||
}
|
||||
|
||||
function commit(renderObjects?: readonly GraphicsRenderObject[]) {
|
||||
scene.update(renderObjects, false)
|
||||
function commit() {
|
||||
commitScene();
|
||||
resolveCameraReset();
|
||||
}
|
||||
|
||||
runTask(scene.commit()).then(() => {
|
||||
if (cameraResetRequested && !scene.isCommiting) {
|
||||
const dir = typeof cameraResetRequested === 'boolean' ? undefined : cameraResetRequested
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, dir)
|
||||
cameraResetRequested = false
|
||||
}
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
})
|
||||
function resolveCameraReset() {
|
||||
if (!cameraResetRequested) return;
|
||||
const { center, radius } = scene.boundingSphere;
|
||||
camera.focus(center, radius, radius, p.cameraResetDurationMs);
|
||||
cameraResetRequested = false;
|
||||
}
|
||||
|
||||
const sceneCommitTimeoutMs = 250;
|
||||
function commitScene() {
|
||||
if (!scene.needsCommit) return;
|
||||
|
||||
const allCommited = scene.commit(sceneCommitTimeoutMs);
|
||||
if (debugHelper.isEnabled) debugHelper.update();
|
||||
if (allCommited) reprCount.next(reprRenderObjects.size);
|
||||
}
|
||||
|
||||
function add(repr: Representation.Any) {
|
||||
registerAutoUpdate(repr);
|
||||
|
||||
const oldRO = reprRenderObjects.get(repr)
|
||||
const newRO = new Set<GraphicsRenderObject>()
|
||||
repr.renderObjects.forEach(o => newRO.add(o))
|
||||
|
||||
if (oldRO) {
|
||||
if (!SetUtils.areEqual(newRO, oldRO)) {
|
||||
for (const o of Array.from(newRO)) { if (!oldRO.has(o)) scene.add(o); }
|
||||
for (const o of Array.from(oldRO)) { if (!newRO.has(o)) scene.remove(o) }
|
||||
newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o) })
|
||||
oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o) })
|
||||
}
|
||||
} else {
|
||||
repr.renderObjects.forEach(o => scene.add(o))
|
||||
}
|
||||
reprRenderObjects.set(repr, newRO)
|
||||
commit(repr.renderObjects)
|
||||
|
||||
scene.update(repr.renderObjects, false)
|
||||
}
|
||||
|
||||
function remove(repr: Representation.Any) {
|
||||
unregisterAutoUpdate(repr);
|
||||
|
||||
const renderObjects = reprRenderObjects.get(repr)
|
||||
if (renderObjects) {
|
||||
renderObjects.forEach(o => scene.remove(o))
|
||||
reprRenderObjects.delete(repr)
|
||||
scene.update(repr.renderObjects, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
function registerAutoUpdate(repr: Representation.Any) {
|
||||
if (reprUpdatedSubscriptions.has(repr)) return;
|
||||
|
||||
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
||||
if (!repr.state.syncManually) add(repr);
|
||||
}))
|
||||
}
|
||||
|
||||
function unregisterAutoUpdate(repr: Representation.Any) {
|
||||
const updatedSubscription = reprUpdatedSubscriptions.get(repr);
|
||||
if (updatedSubscription) {
|
||||
updatedSubscription.unsubscribe();
|
||||
reprUpdatedSubscriptions.delete(repr);
|
||||
}
|
||||
}
|
||||
|
||||
handleResize()
|
||||
@@ -303,24 +355,9 @@ namespace Canvas3D {
|
||||
return {
|
||||
webgl,
|
||||
|
||||
add: (repr: Representation.Any) => {
|
||||
add(repr)
|
||||
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
||||
if (!repr.state.syncManually) add(repr)
|
||||
}))
|
||||
},
|
||||
remove: (repr: Representation.Any) => {
|
||||
const updatedSubscription = reprUpdatedSubscriptions.get(repr)
|
||||
if (updatedSubscription) {
|
||||
updatedSubscription.unsubscribe()
|
||||
}
|
||||
const renderObjects = reprRenderObjects.get(repr)
|
||||
if (renderObjects) {
|
||||
renderObjects.forEach(o => scene.remove(o))
|
||||
reprRenderObjects.delete(repr)
|
||||
commit()
|
||||
}
|
||||
},
|
||||
add,
|
||||
remove,
|
||||
commit,
|
||||
update: (repr, keepSphere) => {
|
||||
if (repr) {
|
||||
if (!reprRenderObjects.has(repr)) return;
|
||||
@@ -330,9 +367,13 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
clear: () => {
|
||||
reprUpdatedSubscriptions.forEach(v => v.unsubscribe())
|
||||
reprUpdatedSubscriptions.clear()
|
||||
reprRenderObjects.clear()
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
},
|
||||
|
||||
// draw,
|
||||
@@ -343,15 +384,11 @@ namespace Canvas3D {
|
||||
getLoci,
|
||||
|
||||
handleResize,
|
||||
resetCamera: (dir?: Vec3) => {
|
||||
if (scene.isCommiting) {
|
||||
cameraResetRequested = dir || true
|
||||
} else {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, dir)
|
||||
requestDraw(true);
|
||||
}
|
||||
requestCameraReset: () => {
|
||||
cameraResetRequested = true;
|
||||
},
|
||||
camera,
|
||||
boundingSphere: scene.boundingSphere,
|
||||
downloadScreenshot: () => {
|
||||
// TODO
|
||||
},
|
||||
@@ -370,9 +407,14 @@ namespace Canvas3D {
|
||||
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
|
||||
camera.setState({ mode: props.cameraMode })
|
||||
}
|
||||
if (props.cameraClipDistance !== undefined) p.cameraClipDistance = props.cameraClipDistance
|
||||
if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]]
|
||||
if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]]
|
||||
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
|
||||
camera.setState({ fog: props.cameraFog })
|
||||
}
|
||||
if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
|
||||
camera.setState({ clipFar: props.cameraClipFar })
|
||||
}
|
||||
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
|
||||
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
|
||||
|
||||
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) multiSample.setProps(props.multiSample)
|
||||
@@ -381,13 +423,17 @@ namespace Canvas3D {
|
||||
if (props.debug) debugHelper.setProps(props.debug)
|
||||
requestDraw(true)
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
|
||||
},
|
||||
|
||||
get props() {
|
||||
return {
|
||||
cameraMode: camera.state.mode,
|
||||
cameraClipDistance: p.cameraClipDistance,
|
||||
clip: p.clip,
|
||||
fog: p.fog,
|
||||
cameraFog: camera.state.fog,
|
||||
cameraClipFar: camera.state.clipFar,
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
transparentBackground: p.transparentBackground,
|
||||
|
||||
postprocessing: { ...postprocessing.props },
|
||||
multiSample: { ...multiSample.props },
|
||||
@@ -406,12 +452,13 @@ namespace Canvas3D {
|
||||
return interactionHelper.events
|
||||
},
|
||||
dispose: () => {
|
||||
contextRestoredSub.unsubscribe()
|
||||
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
input.dispose()
|
||||
controls.dispose()
|
||||
renderer.dispose()
|
||||
camera.dispose()
|
||||
interactionHelper.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,29 @@
|
||||
*/
|
||||
|
||||
import { Quat, Vec2, Vec3, EPSILON } from '../../mol-math/linear-algebra';
|
||||
import { cameraLookAt, Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, ButtonsType, PinchInput } from '../../mol-util/input/input-observer';
|
||||
import { Object3D } from '../../mol-gl/object3d';
|
||||
import { Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Camera } from '../camera';
|
||||
import { absMax } from '../../mol-math/misc';
|
||||
import { Binding } from '../../mol-util/binding';
|
||||
|
||||
const B = ButtonsType
|
||||
const M = ModifiersKeys
|
||||
const Trigger = Binding.Trigger
|
||||
|
||||
export const DefaultTrackballBindings = {
|
||||
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate the 3D scene by dragging using ${triggers}'),
|
||||
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate the 3D scene around the z-axis by dragging using ${triggers}'),
|
||||
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan the 3D scene by dragging using ${triggers}'),
|
||||
dragZoom: Binding.Empty,
|
||||
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus the 3D scene by dragging using ${triggers}'),
|
||||
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom the 3D scene by dragging using ${triggers}'),
|
||||
|
||||
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom the 3D scene by scrolling using ${triggers}'),
|
||||
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Focus the 3D scene by scrolling using ${triggers}'),
|
||||
scrollFocusZoom: Binding.Empty,
|
||||
}
|
||||
|
||||
export const TrackballControlsParams = {
|
||||
noScroll: PD.Boolean(true, { isHidden: true }),
|
||||
@@ -21,14 +40,16 @@ export const TrackballControlsParams = {
|
||||
zoomSpeed: PD.Numeric(6.0, { min: 0.1, max: 10, step: 0.1 }),
|
||||
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
|
||||
|
||||
spin: PD.Boolean(false),
|
||||
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
|
||||
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
|
||||
|
||||
staticMoving: PD.Boolean(true, { isHidden: true }),
|
||||
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
|
||||
|
||||
minDistance: PD.Numeric(0.01, {}, { isHidden: true }),
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true })
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
|
||||
|
||||
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true })
|
||||
}
|
||||
export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
|
||||
|
||||
@@ -44,11 +65,10 @@ interface TrackballControls {
|
||||
dispose: () => void
|
||||
}
|
||||
namespace TrackballControls {
|
||||
export function create(input: InputObserver, object: Object3D & { target: Vec3 }, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
export function create(input: InputObserver, camera: Camera, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props }
|
||||
|
||||
const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
|
||||
const target: Vec3 = object.target
|
||||
const viewport = Viewport()
|
||||
|
||||
let disposed = false
|
||||
|
||||
@@ -60,91 +80,116 @@ namespace TrackballControls {
|
||||
let _isInteracting = false;
|
||||
|
||||
// For internal use
|
||||
const lastPosition = Vec3.zero()
|
||||
const lastPosition = Vec3()
|
||||
|
||||
const _eye = Vec3.zero()
|
||||
const _eye = Vec3()
|
||||
|
||||
const _movePrev = Vec2.zero()
|
||||
const _moveCurr = Vec2.zero()
|
||||
const _rotPrev = Vec2()
|
||||
const _rotCurr = Vec2()
|
||||
const _rotLastAxis = Vec3()
|
||||
let _rotLastAngle = 0
|
||||
|
||||
const _lastAxis = Vec3.zero()
|
||||
let _lastAngle = 0
|
||||
const _zRotPrev = Vec2()
|
||||
const _zRotCurr = Vec2()
|
||||
let _zRotLastAngle = 0
|
||||
|
||||
const _zoomStart = Vec2.zero()
|
||||
const _zoomEnd = Vec2.zero()
|
||||
const _zoomStart = Vec2()
|
||||
const _zoomEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2.zero()
|
||||
const _panEnd = Vec2.zero()
|
||||
const _focusStart = Vec2()
|
||||
const _focusEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2()
|
||||
const _panEnd = Vec2()
|
||||
|
||||
// Initial values for reseting
|
||||
const target0 = Vec3.clone(target)
|
||||
const position0 = Vec3.clone(object.position)
|
||||
const up0 = Vec3.clone(object.up)
|
||||
const target0 = Vec3.clone(camera.target)
|
||||
const position0 = Vec3.clone(camera.position)
|
||||
const up0 = Vec3.clone(camera.up)
|
||||
|
||||
const mouseOnScreenVec2 = Vec2.zero()
|
||||
const mouseOnScreenVec2 = Vec2()
|
||||
function getMouseOnScreen(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnScreenVec2,
|
||||
(pageX - viewport.x) / viewport.width,
|
||||
(pageY - viewport.y) / viewport.height
|
||||
);
|
||||
return mouseOnScreenVec2;
|
||||
}
|
||||
|
||||
const mouseOnCircleVec2 = Vec2.zero()
|
||||
const mouseOnCircleVec2 = Vec2()
|
||||
function getMouseOnCircle(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnCircleVec2,
|
||||
((pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5)),
|
||||
((viewport.height + 2 * (viewport.y - pageY)) / viewport.width) // screen.width intentional
|
||||
(pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5),
|
||||
(viewport.height + 2 * (viewport.y - pageY)) / viewport.width // screen.width intentional
|
||||
);
|
||||
return mouseOnCircleVec2;
|
||||
}
|
||||
|
||||
const rotAxis = Vec3.zero()
|
||||
const rotQuat = Quat.zero()
|
||||
const rotEyeDir = Vec3.zero()
|
||||
const rotObjUpDir = Vec3.zero()
|
||||
const rotObjSideDir = Vec3.zero()
|
||||
const rotMoveDir = Vec3.zero()
|
||||
const rotAxis = Vec3()
|
||||
const rotQuat = Quat()
|
||||
const rotEyeDir = Vec3()
|
||||
const rotObjUpDir = Vec3()
|
||||
const rotObjSideDir = Vec3()
|
||||
const rotMoveDir = Vec3()
|
||||
|
||||
function rotateCamera() {
|
||||
Vec3.set(rotMoveDir, _moveCurr[0] - _movePrev[0], _moveCurr[1] - _movePrev[1], 0);
|
||||
let angle = Vec3.magnitude(rotMoveDir);
|
||||
const dx = _rotCurr[0] - _rotPrev[0]
|
||||
const dy = _rotCurr[1] - _rotPrev[1]
|
||||
Vec3.set(rotMoveDir, dx, dy, 0);
|
||||
|
||||
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed;
|
||||
|
||||
if (angle) {
|
||||
Vec3.copy(_eye, object.position)
|
||||
Vec3.sub(_eye, _eye, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
Vec3.normalize(rotEyeDir, Vec3.copy(rotEyeDir, _eye))
|
||||
Vec3.normalize(rotObjUpDir, Vec3.copy(rotObjUpDir, object.up))
|
||||
Vec3.normalize(rotEyeDir, _eye)
|
||||
Vec3.normalize(rotObjUpDir, camera.up)
|
||||
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir))
|
||||
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, _moveCurr[1] - _movePrev[1])
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, _moveCurr[0] - _movePrev[0])
|
||||
|
||||
Vec3.add(rotMoveDir, Vec3.copy(rotMoveDir, rotObjUpDir), rotObjSideDir)
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy)
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx)
|
||||
|
||||
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir)
|
||||
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye))
|
||||
|
||||
angle *= p.rotateSpeed;
|
||||
Quat.setAxisAngle(rotQuat, rotAxis, angle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
|
||||
Vec3.copy(_lastAxis, rotAxis)
|
||||
_lastAngle = angle;
|
||||
} else if (!p.staticMoving && _lastAngle) {
|
||||
_lastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, Vec3.copy(_eye, object.position), target)
|
||||
Quat.setAxisAngle(rotQuat, _lastAxis, _lastAngle)
|
||||
Vec3.copy(_rotLastAxis, rotAxis)
|
||||
_rotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _rotLastAngle) {
|
||||
_rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
|
||||
const zRotQuat = Quat()
|
||||
|
||||
function zRotateCamera() {
|
||||
const dx = _zRotCurr[0] - _zRotPrev[0]
|
||||
const dy = _zRotCurr[1] - _zRotPrev[1]
|
||||
const angle = p.rotateSpeed * (-dx + dy) * -0.05
|
||||
|
||||
if (angle) {
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, angle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
_zRotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _zRotLastAngle) {
|
||||
_zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
|
||||
function zoomCamera() {
|
||||
@@ -160,9 +205,23 @@ namespace TrackballControls {
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2.zero()
|
||||
const panObjUp = Vec3.zero()
|
||||
const panOffset = Vec3.zero()
|
||||
function focusCamera() {
|
||||
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
|
||||
if (factor !== 0.0) {
|
||||
const radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
|
||||
camera.setState({ radiusNear })
|
||||
}
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
} else {
|
||||
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2()
|
||||
const panObjUp = Vec3()
|
||||
const panOffset = Vec3()
|
||||
|
||||
function panCamera() {
|
||||
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart)
|
||||
@@ -170,14 +229,14 @@ namespace TrackballControls {
|
||||
if (Vec2.squaredMagnitude(panMouseChange)) {
|
||||
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed)
|
||||
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), object.up)
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up)
|
||||
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0])
|
||||
|
||||
Vec3.setMagnitude(panObjUp, object.up, panMouseChange[1])
|
||||
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1])
|
||||
Vec3.add(panOffset, panOffset, panObjUp)
|
||||
|
||||
Vec3.add(object.position, object.position, panOffset)
|
||||
Vec3.add(target, target, panOffset)
|
||||
Vec3.add(camera.position, camera.position, panOffset)
|
||||
Vec3.add(camera.target, camera.target, panOffset)
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_panStart, _panEnd)
|
||||
@@ -193,14 +252,16 @@ namespace TrackballControls {
|
||||
function checkDistances() {
|
||||
if (Vec3.squaredMagnitude(_eye) > p.maxDistance * p.maxDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.maxDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
|
||||
if (Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.minDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,18 +271,19 @@ namespace TrackballControls {
|
||||
if (lastUpdated === t) return;
|
||||
if (p.spin) spin(t - lastUpdated);
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
rotateCamera()
|
||||
zRotateCamera()
|
||||
zoomCamera()
|
||||
focusCamera()
|
||||
panCamera()
|
||||
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
checkDistances()
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
|
||||
if (Vec3.squaredDistance(lastPosition, object.position) > EPSILON.Value) {
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
if (Vec3.squaredDistance(lastPosition, camera.position) > EPSILON) {
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
lastUpdated = t;
|
||||
@@ -229,53 +291,82 @@ namespace TrackballControls {
|
||||
|
||||
/** Reset object's vectors and the target vector to their initial values */
|
||||
function reset() {
|
||||
Vec3.copy(target, target0)
|
||||
Vec3.copy(object.position, position0)
|
||||
Vec3.copy(object.up, up0)
|
||||
Vec3.copy(camera.target, target0)
|
||||
Vec3.copy(camera.position, position0)
|
||||
Vec3.copy(camera.up, up0)
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
// listeners
|
||||
|
||||
function onDrag({ pageX, pageY, buttons, isStart }: DragInput) {
|
||||
function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
|
||||
_isInteracting = true;
|
||||
|
||||
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers)
|
||||
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers)
|
||||
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers)
|
||||
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers)
|
||||
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers)
|
||||
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers)
|
||||
|
||||
getMouseOnCircle(pageX, pageY)
|
||||
getMouseOnScreen(pageX, pageY)
|
||||
|
||||
if (isStart) {
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) {
|
||||
Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
if (dragRotateZ) {
|
||||
Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
if (dragZoom || dragFocusZoom) {
|
||||
Vec2.copy(_zoomStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_zoomEnd, _zoomStart)
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY))
|
||||
}
|
||||
if (dragFocus) {
|
||||
Vec2.copy(_focusStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_focusEnd, _focusStart)
|
||||
}
|
||||
if (dragPan) {
|
||||
Vec2.copy(_panStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_panEnd, _panStart)
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2)
|
||||
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
|
||||
if (dragFocusZoom) {
|
||||
const dist = Vec3.distance(camera.state.position, camera.state.target);
|
||||
camera.setState({ radiusNear: dist / 5 })
|
||||
}
|
||||
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
|
||||
}
|
||||
|
||||
function onInteractionEnd() {
|
||||
_isInteracting = false;
|
||||
}
|
||||
|
||||
function onWheel({ dy }: WheelInput) {
|
||||
_zoomEnd[1] += dy * 0.0001
|
||||
function onWheel({ dx, dy, dz, buttons, modifiers }: WheelInput) {
|
||||
const delta = absMax(dx, dy, dz)
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_zoomEnd[1] += delta * 0.0001
|
||||
}
|
||||
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
|
||||
_focusEnd[1] += delta * 0.0001
|
||||
}
|
||||
}
|
||||
|
||||
function onPinch({ fraction }: PinchInput) {
|
||||
_isInteracting = true;
|
||||
_zoomEnd[1] += (fraction - 1) * 0.1
|
||||
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_isInteracting = true;
|
||||
_zoomEnd[1] += (fraction - 1) * 0.1
|
||||
}
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
@@ -292,7 +383,7 @@ namespace TrackballControls {
|
||||
function spin(deltaT: number) {
|
||||
const frameSpeed = (p.spinSpeed || 0) / 1000;
|
||||
_spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
|
||||
if (!_isInteracting) Vec2.add(_moveCurr, _movePrev, _spinSpeed);
|
||||
if (!_isInteracting) Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
|
||||
}
|
||||
|
||||
// force an update at start
|
||||
|
||||
@@ -82,7 +82,7 @@ export class BoundingSphereHelper {
|
||||
})
|
||||
|
||||
this.scene.update(void 0, false)
|
||||
this.scene.syncCommit()
|
||||
this.scene.commit()
|
||||
}
|
||||
|
||||
syncVisibility() {
|
||||
|
||||
@@ -11,13 +11,15 @@ import InputObserver, { ModifiersKeys, ButtonsType } from '../../mol-util/input/
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
|
||||
type Canvas3D = import('../canvas3d').Canvas3D
|
||||
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
|
||||
type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
|
||||
|
||||
export class Canvas3dInteractionHelper {
|
||||
private ev = RxEventHelper.create();
|
||||
|
||||
readonly events = {
|
||||
highlight: this.ev<import('../canvas3d').Canvas3D.HighlightEvent>(),
|
||||
click: this.ev<import('../canvas3d').Canvas3D.ClickEvent>(),
|
||||
hover: this.ev<HoverEvent>(),
|
||||
click: this.ev<ClickEvent>(),
|
||||
};
|
||||
|
||||
private cX = -1;
|
||||
@@ -36,6 +38,7 @@ export class Canvas3dInteractionHelper {
|
||||
private inside = false;
|
||||
|
||||
private buttons: ButtonsType = ButtonsType.create(0);
|
||||
private button: ButtonsType.Flag = ButtonsType.create(0);
|
||||
private modifiers: ModifiersKeys = ModifiersKeys.None;
|
||||
|
||||
private identify(isClick: boolean, t: number) {
|
||||
@@ -48,18 +51,18 @@ export class Canvas3dInteractionHelper {
|
||||
if (!this.id) return;
|
||||
|
||||
if (isClick) {
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
return;
|
||||
}
|
||||
|
||||
// only highlight the latest
|
||||
if (!this.inside || this.currentIdentifyT !== t) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loci = this.getLoci(this.id);
|
||||
// only broadcast the latest hover
|
||||
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
|
||||
this.events.highlight.next({ current: loci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
}
|
||||
@@ -76,21 +79,24 @@ export class Canvas3dInteractionHelper {
|
||||
this.inside = false;
|
||||
if (this.prevLoci.loci !== EmptyLoci) {
|
||||
this.prevLoci = Representation.Loci.Empty;
|
||||
this.events.highlight.next({ current: this.prevLoci });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
}
|
||||
|
||||
move(x: number, y: number, modifiers: ModifiersKeys) {
|
||||
move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.inside = true;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
}
|
||||
|
||||
select(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
|
||||
select(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.identify(true, 0);
|
||||
}
|
||||
@@ -98,7 +104,7 @@ export class Canvas3dInteractionHelper {
|
||||
modify(modifiers: ModifiersKeys) {
|
||||
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
|
||||
this.modifiers = modifiers;
|
||||
this.events.highlight.next({ current: this.prevLoci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -106,17 +112,17 @@ export class Canvas3dInteractionHelper {
|
||||
}
|
||||
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
|
||||
input.move.subscribe(({x, y, inside, buttons, modifiers }) => {
|
||||
if (!inside || buttons) { return; }
|
||||
this.move(x, y, modifiers);
|
||||
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
|
||||
if (!inside) return;
|
||||
this.move(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.leave.subscribe(() => {
|
||||
this.leave();
|
||||
});
|
||||
|
||||
input.click.subscribe(({x, y, buttons, modifiers }) => {
|
||||
this.select(x, y, buttons, modifiers);
|
||||
input.click.subscribe(({x, y, buttons, button, modifiers }) => {
|
||||
this.select(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.modifiers.subscribe(modifiers => this.modify(modifiers));
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { createTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class DrawPass {
|
||||
colorTarget: RenderTarget
|
||||
@@ -18,14 +19,14 @@ export class DrawPass {
|
||||
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions } = webgl
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions, resources } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = webgl.createRenderTarget(width, height)
|
||||
this.packedDepth = !extensions.depthTexture
|
||||
this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
|
||||
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest')
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexture.define(width, height)
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
@@ -41,29 +42,33 @@ export class DrawPass {
|
||||
}
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
const { webgl, renderer, scene, debugHelper, colorTarget, depthTarget } = this
|
||||
const { gl } = webgl
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
colorTarget.bind()
|
||||
if (!this.packedDepth) {
|
||||
// TODO unlcear why it is not enough to call `attachFramebuffer` in `Texture.reset`
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
}
|
||||
}
|
||||
renderer.setViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
renderer.render(scene, 'color', true)
|
||||
|
||||
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight())
|
||||
renderer.render(scene, camera, 'color', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'color', false)
|
||||
renderer.render(debugHelper.scene, camera, 'color', false, transparentBackground)
|
||||
}
|
||||
|
||||
// do a depth pass if not rendering to drawing buffer and
|
||||
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (!toDrawingBuffer && depthTarget) {
|
||||
depthTarget.bind()
|
||||
renderer.render(scene, 'depth', true)
|
||||
renderer.render(scene, camera, 'depth', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'depth', false)
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
src/mol-canvas3d/passes/image.ts
Normal file
98
src/mol-canvas3d/passes/image.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { DrawPass } from './draw'
|
||||
import { PostprocessingPass, PostprocessingParams } from './postprocessing'
|
||||
import { MultiSamplePass, MultiSampleParams } from './multi-sample'
|
||||
import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
}
|
||||
export type ImageProps = PD.Values<typeof ImageParams>
|
||||
|
||||
export class ImagePass {
|
||||
private _width = 1024
|
||||
private _height = 768
|
||||
private _camera = new Camera()
|
||||
private _transparentBackground = false
|
||||
|
||||
private _colorTarget: RenderTarget
|
||||
get colorTarget() { return this._colorTarget }
|
||||
|
||||
readonly drawPass: DrawPass
|
||||
private readonly postprocessing: PostprocessingPass
|
||||
private readonly multiSample: MultiSamplePass
|
||||
|
||||
get width() { return this._width }
|
||||
get height() { return this._height }
|
||||
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
|
||||
const p = { ...PD.getDefaultValues(ImageParams), ...props }
|
||||
|
||||
this._transparentBackground = p.transparentBackground
|
||||
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
|
||||
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
|
||||
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
|
||||
|
||||
this.setSize(this._width, this._height)
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
if (width === this._width && height === this._height) return
|
||||
|
||||
this._width = width
|
||||
this._height = height
|
||||
|
||||
this.drawPass.setSize(width, height)
|
||||
this.postprocessing.setSize(width, height)
|
||||
this.multiSample.setSize(width, height)
|
||||
}
|
||||
|
||||
setProps(props: Partial<ImageProps> = {}) {
|
||||
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
|
||||
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) this.multiSample.setProps(props.multiSample)
|
||||
}
|
||||
|
||||
render() {
|
||||
Camera.copySnapshot(this._camera.state, this.camera.state)
|
||||
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
|
||||
this._camera.update()
|
||||
|
||||
this.renderer.setViewport(0, 0, this._width, this._height);
|
||||
|
||||
if (this.multiSample.enabled) {
|
||||
this.multiSample.render(false, this._transparentBackground)
|
||||
this._colorTarget = this.multiSample.colorTarget
|
||||
} else {
|
||||
this.drawPass.render(false, this._transparentBackground)
|
||||
if (this.postprocessing.enabled) {
|
||||
this.postprocessing.render(false)
|
||||
this._colorTarget = this.postprocessing.target
|
||||
} else {
|
||||
this._colorTarget = this.drawPass.colorTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getImageData(width: number, height: number) {
|
||||
this.setSize(width, height)
|
||||
this.render()
|
||||
const pd = this.colorTarget.getPixelData()
|
||||
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { RenderTarget, createRenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
import { PostprocessingPass } from './postprocessing';
|
||||
import { DrawPass } from './draw';
|
||||
@@ -35,7 +35,7 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
|
||||
const values: Values<typeof ComposeSchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
uWeight: ValueCell.create(1.0),
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
|
||||
|
||||
export class MultiSamplePass {
|
||||
props: MultiSampleProps
|
||||
colorTarget: RenderTarget
|
||||
|
||||
private composeTarget: RenderTarget
|
||||
private holdTarget: RenderTarget
|
||||
@@ -65,8 +66,9 @@ export class MultiSamplePass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
|
||||
const { gl } = webgl
|
||||
this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
|
||||
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props }
|
||||
}
|
||||
@@ -92,6 +94,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this.colorTarget.setSize(width, height)
|
||||
this.composeTarget.setSize(width, height)
|
||||
this.holdTarget.setSize(width, height)
|
||||
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
|
||||
@@ -102,15 +105,15 @@ export class MultiSamplePass {
|
||||
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
|
||||
}
|
||||
|
||||
render() {
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
if (this.props.mode === 'temporal') {
|
||||
this.renderTemporalMultiSample()
|
||||
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
|
||||
} else {
|
||||
this.renderMultiSample()
|
||||
this.renderMultiSample(toDrawingBuffer, transparentBackground)
|
||||
}
|
||||
}
|
||||
|
||||
private renderMultiSample() {
|
||||
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -128,14 +131,15 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
for (let i = 0; i < offsetList.length; ++i) {
|
||||
const offset = offsetList[i]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// the theory is that equal weights for each sample lead to an accumulation of rounding
|
||||
// errors. The following equation varies the sampleWeight per sample so that it is uniformly
|
||||
@@ -145,7 +149,7 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -168,16 +172,20 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample() {
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -197,7 +205,7 @@ export class MultiSamplePass {
|
||||
const i = this.sampleIndex
|
||||
|
||||
if (i === 0) {
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
@@ -215,7 +223,8 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
@@ -223,10 +232,10 @@ export class MultiSamplePass {
|
||||
for (let i = 0; i < numSamplesPerFrame; ++i) {
|
||||
const offset = offsetList[this.sampleIndex]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -252,7 +261,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
@@ -261,7 +274,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
|
||||
ValueCell.update(compose.values.tColor, holdTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
if (accumulationWeight === 0) state.disable(gl.BLEND)
|
||||
else state.enable(gl.BLEND)
|
||||
@@ -269,7 +286,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class PickPass {
|
||||
pickDirty = true
|
||||
@@ -26,7 +27,7 @@ export class PickPass {
|
||||
private pickWidth: number
|
||||
private pickHeight: number
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
|
||||
const { gl } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
@@ -35,9 +36,9 @@ export class PickPass {
|
||||
this.pickWidth = Math.round(width * this.pickScale)
|
||||
this.pickHeight = Math.round(height * this.pickScale)
|
||||
|
||||
this.objectPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
this.instancePickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
this.groupPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
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.setupBuffers()
|
||||
}
|
||||
@@ -64,14 +65,14 @@ export class PickPass {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { renderer, scene } = this
|
||||
const { renderer, scene, camera } = this
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, 'pickObject', true);
|
||||
renderer.render(scene, camera, 'pickObject', true, false);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, 'pickInstance', true);
|
||||
renderer.render(scene, camera, 'pickInstance', true, false);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, 'pickGroup', true);
|
||||
renderer.render(scene, camera, 'pickGroup', true, false);
|
||||
|
||||
this.pickDirty = false
|
||||
}
|
||||
@@ -96,6 +97,8 @@ export class PickPass {
|
||||
|
||||
identify(x: number, y: number): PickingId | undefined {
|
||||
const { webgl, pickScale } = this
|
||||
if (webgl.isContextLost) return
|
||||
|
||||
const { gl } = webgl
|
||||
if (this.pickDirty) {
|
||||
this.render()
|
||||
|
||||
@@ -14,7 +14,7 @@ import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { DrawPass } from './draw';
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
|
||||
@@ -27,7 +27,6 @@ const PostprocessingSchema = {
|
||||
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
dOrthographic: DefineSpec('number'),
|
||||
uNear: UniformSpec('f'),
|
||||
uFar: UniformSpec('f'),
|
||||
@@ -56,8 +55,6 @@ export const PostprocessingParams = {
|
||||
outlineEnable: PD.Boolean(false),
|
||||
outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
|
||||
outlineThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
|
||||
|
||||
useFog: PD.Boolean(true),
|
||||
}
|
||||
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
|
||||
|
||||
@@ -69,9 +66,8 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
tDepth: ValueCell.create(depthTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
|
||||
dUseFog: ValueCell.create(p.useFog),
|
||||
dOrthographic: ValueCell.create(0),
|
||||
uNear: ValueCell.create(1),
|
||||
uFar: ValueCell.create(10000),
|
||||
@@ -105,7 +101,7 @@ export class PostprocessingPass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, drawPass: DrawPass, props: Partial<PostprocessingProps>) {
|
||||
const { gl } = webgl
|
||||
this.target = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.target = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props }
|
||||
const { colorTarget, depthTexture, packedDepth } = drawPass
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props)
|
||||
@@ -151,19 +147,14 @@ export class PostprocessingPass {
|
||||
ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
|
||||
}
|
||||
|
||||
if (props.useFog !== undefined) {
|
||||
this.props.useFog = props.useFog
|
||||
ValueCell.update(this.renderable.values.dUseFog, props.useFog)
|
||||
}
|
||||
|
||||
this.renderable.update()
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.state.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.state.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.state.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.state.fogNear)
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
|
||||
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
|
||||
|
||||
const { gl, state } = this.webgl
|
||||
|
||||
@@ -4,16 +4,57 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
/** resize canvas to container element */
|
||||
/** Set canvas size taking `devicePixelRatio` into account */
|
||||
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
|
||||
canvas.width = Math.round(window.devicePixelRatio * width)
|
||||
canvas.height = Math.round(window.devicePixelRatio * height)
|
||||
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
|
||||
}
|
||||
|
||||
/** Resize canvas to container element taking `devicePixelRatio` into account */
|
||||
export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
|
||||
let w = window.innerWidth
|
||||
let h = window.innerHeight
|
||||
let width = window.innerWidth
|
||||
let height = window.innerHeight
|
||||
if (container !== document.body) {
|
||||
let bounds = container.getBoundingClientRect()
|
||||
w = bounds.right - bounds.left
|
||||
h = bounds.bottom - bounds.top
|
||||
width = bounds.right - bounds.left
|
||||
height = bounds.bottom - bounds.top
|
||||
}
|
||||
canvas.width = window.devicePixelRatio * w
|
||||
canvas.height = window.devicePixelRatio * h
|
||||
Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
|
||||
setCanvasSize(canvas, width, height)
|
||||
}
|
||||
|
||||
function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?: string, quality?: any) {
|
||||
const bin = atob(canvas.toDataURL(type, quality).split(',')[1])
|
||||
const len = bin.length
|
||||
const len32 = len >> 2
|
||||
const a8 = new Uint8Array(len)
|
||||
const a32 = new Uint32Array(a8.buffer, 0, len32)
|
||||
|
||||
let j = 0
|
||||
for (let i = 0; i < len32; ++i) {
|
||||
a32[i] = bin.charCodeAt(j++) |
|
||||
bin.charCodeAt(j++) << 8 |
|
||||
bin.charCodeAt(j++) << 16 |
|
||||
bin.charCodeAt(j++) << 24
|
||||
}
|
||||
|
||||
let tailLength = len & 3;
|
||||
while (tailLength--) a8[j] = bin.charCodeAt(j++)
|
||||
|
||||
callback(new Blob([a8], { type: type || 'image/png' }));
|
||||
}
|
||||
|
||||
export async function canvasToBlob(canvas: HTMLCanvasElement, type?: string, quality?: any): Promise<Blob> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (blob: Blob | null) => {
|
||||
if (blob) resolve(blob)
|
||||
else reject('no blob returned')
|
||||
}
|
||||
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
_canvasToBlob(canvas, callback, type, quality)
|
||||
} else {
|
||||
canvas.toBlob(callback, type, quality)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace Column {
|
||||
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<'str' | 'int'>
|
||||
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 const str: Str = { '@type': 'str', T: '', valueType: 'str' };
|
||||
@@ -73,6 +73,7 @@ namespace Column {
|
||||
rowCount: number,
|
||||
schema: T,
|
||||
valueKind?: (row: number) => ValueKind,
|
||||
areValuesEqual?: (rowA: number, rowB: number) => boolean
|
||||
}
|
||||
|
||||
export interface ArraySpec<T extends Schema> {
|
||||
@@ -91,7 +92,13 @@ namespace Column {
|
||||
return !!v && !!(v as Column<any>).schema && !!(v as Column<any>).value;
|
||||
}
|
||||
|
||||
export const enum ValueKind { Present = 0, NotPresent = 1, Unknown = 2 }
|
||||
export const enum ValueKind {
|
||||
Present = 0,
|
||||
/** Expressed in CIF as `.` */
|
||||
NotPresent = 1,
|
||||
/** Expressed in CIF as `?` */
|
||||
Unknown = 2
|
||||
}
|
||||
|
||||
export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> {
|
||||
return constColumn(schema['T'], rowCount, schema, ValueKind.NotPresent);
|
||||
@@ -130,6 +137,14 @@ namespace Column {
|
||||
return arrayColumn({ array, schema: Schema.str });
|
||||
}
|
||||
|
||||
export function ofStringAliasArray<T extends string>(array: ArrayLike<T>) {
|
||||
return arrayColumn<Schema.Aliased<T>>({ array, schema: Schema.Aliased(Schema.str) });
|
||||
}
|
||||
|
||||
export function ofStringListArray<T extends string>(array: ArrayLike<T[]>, separator = ',') {
|
||||
return arrayColumn<Schema.List<T>>({ array, schema: Schema.List<T>(separator, x => x as T) });
|
||||
}
|
||||
|
||||
export function ofIntTokens(tokens: Tokens) {
|
||||
const { count, data, indices } = tokens
|
||||
return lambdaColumn({
|
||||
@@ -247,7 +262,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
|
||||
}
|
||||
}
|
||||
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqual, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
return {
|
||||
schema: schema,
|
||||
__array: void 0,
|
||||
@@ -260,7 +275,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, sch
|
||||
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
|
||||
return array;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
|
||||
areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,19 +304,19 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
return ret;
|
||||
}
|
||||
: isTyped
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
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++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
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++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB]
|
||||
}
|
||||
}
|
||||
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number) {
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number): Column<T> {
|
||||
if (!column.isDefined) return Column.Undefined(end - start, column.schema);
|
||||
if (start === 0 && end === column.rowCount) return column;
|
||||
if (!!column.__array && ColumnHelpers.isTypedArray(column.__array)) return windowTyped(column, start, end);
|
||||
@@ -310,7 +325,8 @@ function windowColumn<T>(column: Column<T>, start: number, end: number) {
|
||||
|
||||
function windowTyped<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
const array = ColumnHelpers.typedArrayWindow(c.__array, { start, end });
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: c.valueKind }) as any;
|
||||
const vk = c.valueKind;
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: row => vk(start + row) }) as any;
|
||||
}
|
||||
|
||||
function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
@@ -342,9 +358,9 @@ function isIdentity(map: ArrayLike<number>, rowCount: number) {
|
||||
}
|
||||
|
||||
function columnView<T>(c: Column<T>, map: ArrayLike<number>, checkIdentity: boolean): Column<T> {
|
||||
if (!c.isDefined) return c;
|
||||
if (!c.isDefined || c.rowCount === 0) return c;
|
||||
if (checkIdentity && isIdentity(map, c.rowCount)) return c;
|
||||
if (!!c.__array) return arrayView(c, map);
|
||||
if (!!c.__array && typeof c.value(0) === typeof c.__array[0]) return arrayView(c, map);
|
||||
return viewFull(c, map);
|
||||
}
|
||||
|
||||
@@ -352,7 +368,8 @@ function arrayView<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {
|
||||
const array = c.__array!;
|
||||
const ret = new (array as any).constructor(map.length);
|
||||
for (let i = 0, _i = map.length; i < _i; i++) ret[i] = array[map[i]];
|
||||
return arrayColumn({ array: ret, schema: c.schema, valueKind: c.valueKind });
|
||||
const vk = c.valueKind;
|
||||
return arrayColumn({ array: ret, schema: c.schema, valueKind: row => vk(map[row]) });
|
||||
}
|
||||
|
||||
function viewFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Table {
|
||||
export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
|
||||
export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
|
||||
export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
|
||||
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & { [C in keyof S]?: Column<S[C]['T']> }
|
||||
export type PartialColumns<S extends Schema> = { [C in keyof S]?: Column<S[C]['T']> }
|
||||
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & PartialColumns<S>
|
||||
|
||||
export function is(t: any): t is Table<any> {
|
||||
return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema;
|
||||
@@ -47,6 +48,19 @@ namespace Table {
|
||||
return { _rowCount, _columns, _schema: schema, ...(columns as any) };
|
||||
}
|
||||
|
||||
export function ofPartialColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, partialColumns: PartialColumns<S>, rowCount: number): R {
|
||||
const ret = Object.create(null);
|
||||
const columns = Object.keys(schema);
|
||||
ret._rowCount = rowCount;
|
||||
ret._columns = columns;
|
||||
ret._schema = schema;
|
||||
for (const k of columns) {
|
||||
if (k in partialColumns) ret[k] = partialColumns[k]
|
||||
else ret[k] = Column.Undefined(rowCount, schema[k])
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function ofUndefinedColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rowCount: number): R {
|
||||
const ret = Object.create(null);
|
||||
const columns = Object.keys(schema);
|
||||
@@ -218,6 +232,16 @@ namespace Table {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function toArrays<S extends Schema>(table: Table<S>) {
|
||||
const arrays: { [k: string]: ArrayLike<any> } = {}
|
||||
const { _columns } = table;
|
||||
for (let i = 0; i < _columns.length; i++) {
|
||||
const c = _columns[i]
|
||||
arrays[c] = table[c].toArray();
|
||||
}
|
||||
return arrays as { [k in keyof S]: ArrayLike<S[k]['T']> }
|
||||
}
|
||||
|
||||
export function formatToString<S extends Schema>(table: Table<S>) {
|
||||
const sb = StringBuilder.create();
|
||||
|
||||
|
||||
@@ -90,14 +90,14 @@ class LinkedListImpl<T> implements LinkedList<T> {
|
||||
if (node.previous !== null) {
|
||||
node.previous.next = node.next;
|
||||
}
|
||||
else if (/*first == item*/ node.previous === null) {
|
||||
else if (/* first == item*/ node.previous === null) {
|
||||
this.first = node.next;
|
||||
}
|
||||
|
||||
if (node.next !== null) {
|
||||
node.next.previous = node.previous;
|
||||
}
|
||||
else if (/*last == item*/ node.next === null) {
|
||||
else if (/* last == item*/ node.next === null) {
|
||||
this.last = node.previous;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace UniqueArray {
|
||||
array[array.length] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function has<K, T>({ keys }: UniqueArray<K, T>, key: K) {
|
||||
return keys.has(key);
|
||||
}
|
||||
}
|
||||
|
||||
export { UniqueArray }
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import OrderedSet from '../ordered-set'
|
||||
import Interval from '../interval'
|
||||
import SortedArray from '../sorted-array';
|
||||
|
||||
describe('ordered set', () => {
|
||||
function ordSetToArray(set: OrderedSet) {
|
||||
@@ -81,6 +82,13 @@ describe('ordered set', () => {
|
||||
expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([12, 13, 16]))).toBe(false);
|
||||
});
|
||||
|
||||
it('isSubsetIS', () => {
|
||||
expect(OrderedSet.isSubset(
|
||||
Interval.ofRange(1271, 1295),
|
||||
OrderedSet.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
|
||||
)).toBe(true);
|
||||
});
|
||||
|
||||
it('access/membership', () => {
|
||||
expect(OrderedSet.has(empty, 10)).toBe(false);
|
||||
expect(OrderedSet.indexOf(empty, 10)).toBe(-1);
|
||||
@@ -156,6 +164,15 @@ describe('ordered set', () => {
|
||||
testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
|
||||
it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
|
||||
|
||||
testEq('idxIntersect 1', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([1, 2, 4]),
|
||||
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6]),
|
||||
SortedArray.ofSortedArray([2, 4, 5, 8])), [0, 2]);
|
||||
testEq('idxIntersect 2', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([0, 1]),
|
||||
SortedArray.ofSortedArray([1, 2]),
|
||||
SortedArray.ofSortedArray([1, 2])), [0, 1]);
|
||||
|
||||
testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []);
|
||||
testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []);
|
||||
testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -63,8 +63,102 @@ describe('sortedArray', () => {
|
||||
compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
|
||||
})
|
||||
|
||||
it('indicesOf 2', () => {
|
||||
compareArrays(
|
||||
SortedArray.indicesOf(
|
||||
SortedArray.ofSortedArray([0, 1, 2, 3, 4, 8, 9, 10]),
|
||||
SortedArray.ofSortedArray([1, 3, 4, 9, 10])
|
||||
),
|
||||
[1, 3, 4, 6, 7]
|
||||
);
|
||||
})
|
||||
|
||||
test('intersectionSize', SortedArray.intersectionSize(a1234, a2468), 2);
|
||||
|
||||
// console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
|
||||
// console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
|
||||
it('union1', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 836, 837, 838, 839, 840, 841, 842, 843]),
|
||||
SortedArray.ofSortedArray([835])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union2', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 832, 833]),
|
||||
SortedArray.ofSortedArray([831])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833])
|
||||
)
|
||||
})
|
||||
|
||||
it('union3ab', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835]),
|
||||
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union3ba', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843]),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union4', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([1, 3, 5, 7, 9]),
|
||||
SortedArray.ofSortedArray([2, 4, 6, 8])
|
||||
),
|
||||
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
)
|
||||
})
|
||||
|
||||
it('union5', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([2, 3, 4, 20, 21, 22]),
|
||||
SortedArray.ofSortedArray([10, 11, 12])
|
||||
),
|
||||
SortedArray.ofSortedArray([2, 3, 4, 10, 11, 12, 20, 21, 22])
|
||||
)
|
||||
})
|
||||
|
||||
it('union6', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819]),
|
||||
SortedArray.ofSortedArray([1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758])
|
||||
),
|
||||
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819])
|
||||
)
|
||||
})
|
||||
|
||||
it('union7', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([3766, 3767, 3768, 3770, 3773, 3780, 3783, 3787, 3797]),
|
||||
SortedArray.ofSortedArray([3769, 3790, 3794])
|
||||
),
|
||||
SortedArray.ofSortedArray([3766, 3767, 3768, 3769, 3770, 3773, 3780, 3783, 3787, 3790, 3794, 3797])
|
||||
)
|
||||
})
|
||||
|
||||
it('isSubset', () => {
|
||||
expect(SortedArray.isSubset(
|
||||
SortedArray.ofSortedArray([1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295]),
|
||||
SortedArray.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
|
||||
)).toBe(true);
|
||||
})
|
||||
});
|
||||
@@ -9,6 +9,7 @@ import Tuple from '../tuple'
|
||||
export const Empty = Tuple.Zero;
|
||||
export function ofRange(min: number, max: number) { return max < min ? Tuple.create(min, min) : Tuple.create(min, max + 1); }
|
||||
export function ofBounds(start: number, end: number) { return end <= start ? Tuple.create(start, start) : Tuple.create(start, end); }
|
||||
export function ofLength(length: number) { return length < 0 ? Tuple.create(0, 0) : Tuple.create(0, length); }
|
||||
export const is = Tuple.is;
|
||||
|
||||
export const start = Tuple.fst;
|
||||
|
||||
@@ -126,7 +126,7 @@ function isSubsetIS(a: I, b: S) {
|
||||
const minA = I.min(a), maxA = I.max(a);
|
||||
if (maxA - minA + 1 === 0) return false;
|
||||
const minB = S.min(b), maxB = S.max(b);
|
||||
return minB >= minA && maxA <= maxB;
|
||||
return minB >= minA && maxB <= maxA;
|
||||
}
|
||||
|
||||
function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
|
||||
@@ -286,4 +286,69 @@ export function forEach(set: OrderedSetImpl, f: (value: number, i: number, ctx:
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function forEachSegment(set: OrderedSetImpl, segment: (v: number) => number, f: (value: number, segIndex: number, ctx: any) => void, ctx: any) {
|
||||
if (I.is(set)) {
|
||||
let sI = 0;
|
||||
for (let i = I.min(set), _i = I.max(set); i <= _i; i++) {
|
||||
const s = segment(i);
|
||||
let endI = i + 1;
|
||||
while (endI < _i && segment(endI) === s) endI++;
|
||||
i = endI - 1;
|
||||
f(s, sI, ctx);
|
||||
sI++;
|
||||
}
|
||||
} else {
|
||||
let sI = 0;
|
||||
for (let i = 0, _i = set.length; i < _i; i++) {
|
||||
const s = segment(set[i]);
|
||||
let endI = i + 1;
|
||||
while (endI < _i && segment(set[endI]) === s) endI++;
|
||||
i = endI - 1;
|
||||
f(s, sI, ctx);
|
||||
sI++;
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function indexedIntersect(idxA: OrderedSetImpl, a: S, b: S): OrderedSetImpl {
|
||||
if (a === b) return idxA;
|
||||
const lenI = size(idxA), lenA = a.length, lenB = b.length;
|
||||
if (lenI === 0 || lenA === 0 || lenB === 0) return Empty;
|
||||
|
||||
const startJ = S.findPredecessorIndex(b, a[min(idxA)]);
|
||||
const endJ = S.findPredecessorIndex(b, a[max(idxA)] + 1);
|
||||
|
||||
let commonCount = 0;
|
||||
|
||||
let offset = 0;
|
||||
let O = 0;
|
||||
let j = startJ;
|
||||
while (O < lenI && j < endJ) {
|
||||
const x = a[getAt(idxA, O)], y = b[j];
|
||||
if (x < y) { O++; }
|
||||
else if (x > y) { j++; }
|
||||
else { commonCount++; O++; j++; }
|
||||
}
|
||||
|
||||
// no common elements
|
||||
if (commonCount === 0) return Empty;
|
||||
// A === B
|
||||
if (commonCount === lenA && commonCount === lenB) return idxA;
|
||||
|
||||
const indices = new Int32Array(commonCount);
|
||||
|
||||
offset = 0;
|
||||
O = 0;
|
||||
j = startJ;
|
||||
while (O < lenI && j < endJ) {
|
||||
const x = a[getAt(idxA, O)], y = b[j];
|
||||
if (x < y) { O++; }
|
||||
else if (x > y) { j++; }
|
||||
else { indices[offset++] = j; O++; j++; }
|
||||
}
|
||||
|
||||
return ofSortedArray(indices);
|
||||
}
|
||||
@@ -171,28 +171,30 @@ export function isSubset(a: Nums, b: Nums) {
|
||||
return equal === lenB;
|
||||
}
|
||||
|
||||
export function union(a: Nums, b: Nums) {
|
||||
export function union(a: Nums, b: Nums): Nums {
|
||||
if (a === b) return a;
|
||||
|
||||
const lenA = a.length, lenB = b.length;
|
||||
if (lenA === 0) return b;
|
||||
if (lenB === 0) return a;
|
||||
if (a[0] > b[0]) return union(b, a);
|
||||
|
||||
const { startI, startJ, endI, endJ } = getSuitableIntersectionRange(a, b);
|
||||
const commonCount = getCommonCount(a, b, startI, startJ, endI, endJ);
|
||||
|
||||
const lenA = a.length, lenB = b.length;
|
||||
// A === B || B is subset of A ==> A
|
||||
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
|
||||
// A is subset of B ===> B
|
||||
if (commonCount === lenA) return b;
|
||||
|
||||
const indices = new Int32Array(lenA + lenB - commonCount);
|
||||
let offset = 0;
|
||||
let i = 0, j = 0, offset = 0;
|
||||
|
||||
// insert the "prefixes"
|
||||
for (let k = 0; k < startI; k++) indices[offset++] = a[k];
|
||||
for (let k = 0; k < startJ; k++) indices[offset++] = b[k];
|
||||
for (i = 0; i < startI; i++) indices[offset++] = a[i];
|
||||
while (j < endJ && a[startI] > b[j]) indices[offset++] = b[j++];
|
||||
|
||||
// insert the common part
|
||||
let i = startI;
|
||||
let j = startJ;
|
||||
while (i < endI && j < endJ) {
|
||||
const x = a[i], y = b[j];
|
||||
if (x < y) { indices[offset++] = x; i++; }
|
||||
@@ -200,6 +202,10 @@ export function union(a: Nums, b: Nums) {
|
||||
else { indices[offset++] = x; i++; j++; }
|
||||
}
|
||||
|
||||
// insert the remaining common part
|
||||
for (; i < endI; i++) indices[offset++] = a[i];
|
||||
for (; j < endJ; j++) indices[offset++] = b[j];
|
||||
|
||||
// insert the "tail"
|
||||
for (; i < lenA; i++) indices[offset++] = a[i];
|
||||
for (; j < lenB; j++) indices[offset++] = b[j];
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Interval {
|
||||
export const ofRange: <T extends number = number>(min: T, max: T) => Interval<T> = Impl.ofRange as any;
|
||||
/** Create interval from bounds [start, end), i.e. [start, end - 1] */
|
||||
export const ofBounds: <T extends number = number>(start: T, end: T) => Interval<T> = Impl.ofBounds as any;
|
||||
/** Create interval from length [0, length), i.e. [0, length - 1] */
|
||||
export const ofLength: <T extends number = number>(length: T) => Interval<T> = Impl.ofLength as any;
|
||||
export const is: <T extends number = number>(v: any) => v is Interval<T> = Impl.is as any;
|
||||
|
||||
/** Test if a value is within the bounds of the interval */
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace OrderedSet {
|
||||
|
||||
export const union: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.union as any;
|
||||
export const intersect: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.intersect as any;
|
||||
export const indexedIntersect: <T extends number = number, S extends number = number>(idxA: OrderedSet<T>, a: SortedArray<S>, b: SortedArray<S>) => OrderedSet<T> = Base.indexedIntersect as any;
|
||||
/** Returns elements of `a` that are not in `b`, i.e `a` - `b` */
|
||||
export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any;
|
||||
|
||||
@@ -56,6 +57,10 @@ namespace OrderedSet {
|
||||
return Base.forEach(set as any, f as any, ctx);
|
||||
}
|
||||
|
||||
export function forEachSegment<T extends number, S extends number, Ctx>(set: OrderedSet<T>, segment: (v: T) => S, f: (v: S, sI: number, ctx: Ctx) => void, ctx?: Ctx): Ctx {
|
||||
return Base.forEachSegment(set as any, segment as any, f as any, ctx);
|
||||
}
|
||||
|
||||
export function isInterval<T extends number = number>(set: OrderedSet<T>): set is Interval<T> {
|
||||
return Interval.is(set);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
import { Interval, OrderedSet, SortedArray } from '../../int';
|
||||
import { IntervalIterator } from '../interval-iterator';
|
||||
|
||||
describe('interval', () => {
|
||||
describe('interval', () => {
|
||||
function testIterator(name: string, interval: Interval, set: OrderedSet, expectedValues: { index: number[], start: number[], end: number[]}) {
|
||||
it(`iterator, ${name}`, () => {
|
||||
const intervalIt = new IntervalIterator(interval, set)
|
||||
const { index, start, end } = expectedValues
|
||||
|
||||
|
||||
let i = 0
|
||||
while (intervalIt.hasNext) {
|
||||
const segment = intervalIt.move()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ function nextIndex(n: number) {
|
||||
return ripple | ones
|
||||
};
|
||||
|
||||
export class CombinationIterator<T> implements Iterator<T[]> {
|
||||
export class CombinationIterator<T> implements Iterator<ReadonlyArray<T>> {
|
||||
private value: T[]
|
||||
private index: number
|
||||
private maxIndex: number
|
||||
|
||||
@@ -9,8 +9,6 @@ import { ValueCell } from '../../mol-util';
|
||||
import { BaseValues } from '../../mol-gl/renderable/schema';
|
||||
import { LocationIterator } from '../util/location-iterator';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition'
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { TransformData, createIdentityTransform } from './transform-data';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
@@ -38,10 +36,6 @@ export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [Visua
|
||||
export namespace BaseGeometry {
|
||||
export const Params = {
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
|
||||
useFog: PD.Boolean(true),
|
||||
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
|
||||
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
|
||||
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
@@ -62,24 +56,13 @@ export namespace BaseGeometry {
|
||||
return {
|
||||
alpha: ValueCell.create(props.alpha),
|
||||
uAlpha: ValueCell.create(props.alpha),
|
||||
uHighlightColor: ValueCell.create(Color.toArrayNormalized(props.highlightColor, Vec3.zero(), 0)),
|
||||
uSelectColor: ValueCell.create(Color.toArrayNormalized(props.selectColor, Vec3.zero(), 0)),
|
||||
dUseFog: ValueCell.create(props.useFog),
|
||||
|
||||
uGroupCount: ValueCell.create(counts.groupCount),
|
||||
drawCount: ValueCell.create(counts.drawCount),
|
||||
}
|
||||
}
|
||||
|
||||
export function updateValues(values: BaseValues, props: PD.Values<Params>) {
|
||||
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
|
||||
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
|
||||
}
|
||||
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
|
||||
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
|
||||
}
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { transformPositionArray } from '../../../mol-geo/util';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
|
||||
@@ -26,12 +26,14 @@ import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
const VolumeBox = Box()
|
||||
const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
|
||||
|
||||
export interface DirectVolume {
|
||||
readonly kind: 'direct-volume',
|
||||
|
||||
readonly gridTexture: ValueCell<Texture>,
|
||||
readonly gridTextureDim: ValueCell<Vec3>,
|
||||
readonly gridDimension: ValueCell<Vec3>,
|
||||
@@ -41,32 +43,66 @@ export interface DirectVolume {
|
||||
readonly transform: ValueCell<Mat4>
|
||||
|
||||
/** Bounding sphere of the volume */
|
||||
boundingSphere?: Sphere3D
|
||||
boundingSphere: Sphere3D
|
||||
}
|
||||
|
||||
export namespace DirectVolume {
|
||||
export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume): DirectVolume {
|
||||
const { width, height, depth } = texture
|
||||
if (directVolume) {
|
||||
ValueCell.update(directVolume.gridDimension, gridDimension)
|
||||
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
|
||||
ValueCell.update(directVolume.bboxMin, bbox.min)
|
||||
ValueCell.update(directVolume.bboxMax, bbox.max)
|
||||
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
|
||||
ValueCell.update(directVolume.transform, transform)
|
||||
return directVolume
|
||||
} else {
|
||||
return {
|
||||
kind: 'direct-volume',
|
||||
gridDimension: ValueCell.create(gridDimension),
|
||||
gridTexture: ValueCell.create(texture),
|
||||
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
||||
bboxMin: ValueCell.create(bbox.min),
|
||||
bboxMax: ValueCell.create(bbox.max),
|
||||
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
||||
transform: ValueCell.create(transform),
|
||||
}
|
||||
return directVolume ?
|
||||
update(bbox, gridDimension, transform, texture, directVolume) :
|
||||
fromData(bbox, gridDimension, transform, texture)
|
||||
}
|
||||
|
||||
function hashCode(directVolume: DirectVolume) {
|
||||
return hashFnv32a([
|
||||
directVolume.bboxSize.ref.version, directVolume.gridDimension.ref.version,
|
||||
directVolume.gridTexture.ref.version, directVolume.transform.ref.version,
|
||||
])
|
||||
}
|
||||
|
||||
function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture): DirectVolume {
|
||||
const boundingSphere = Sphere3D()
|
||||
let currentHash = -1
|
||||
|
||||
const width = texture.getWidth()
|
||||
const height = texture.getHeight()
|
||||
const depth = texture.getDepth()
|
||||
|
||||
const directVolume = {
|
||||
kind: 'direct-volume' as const,
|
||||
gridDimension: ValueCell.create(gridDimension),
|
||||
gridTexture: ValueCell.create(texture),
|
||||
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
||||
bboxMin: ValueCell.create(bbox.min),
|
||||
bboxMax: ValueCell.create(bbox.max),
|
||||
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
||||
transform: ValueCell.create(transform),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(directVolume)
|
||||
if (newHash !== currentHash) {
|
||||
const b = getBoundingSphere(directVolume.gridDimension.ref.value, directVolume.transform.ref.value)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
}
|
||||
return directVolume
|
||||
}
|
||||
|
||||
function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume: DirectVolume): DirectVolume {
|
||||
const width = texture.getWidth()
|
||||
const height = texture.getHeight()
|
||||
const depth = texture.getDepth()
|
||||
|
||||
ValueCell.update(directVolume.gridDimension, gridDimension)
|
||||
ValueCell.update(directVolume.gridTexture, texture)
|
||||
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
|
||||
ValueCell.update(directVolume.bboxMin, bbox.min)
|
||||
ValueCell.update(directVolume.bboxMax, bbox.max)
|
||||
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
|
||||
ValueCell.update(directVolume.transform, transform)
|
||||
return directVolume
|
||||
}
|
||||
|
||||
export function createEmpty(directVolume?: DirectVolume): DirectVolume {
|
||||
@@ -108,7 +144,8 @@ export namespace DirectVolume {
|
||||
|
||||
const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(gridDimension.ref.value, gridTransform.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
|
||||
const transferTex = createTransferFunctionTexture(controlPoints, props.list)
|
||||
@@ -138,7 +175,7 @@ export namespace DirectVolume {
|
||||
dRenderMode: ValueCell.create(props.renderMode),
|
||||
tTransferTex: transferTex,
|
||||
|
||||
dGridTexType: ValueCell.create(gridTexture.ref.value.depth > 0 ? '3d' : '2d'),
|
||||
dGridTexType: ValueCell.create(gridTexture.ref.value.getDepth() > 0 ? '3d' : '2d'),
|
||||
uGridTexDim: gridTextureDim,
|
||||
tGridTex: gridTexture,
|
||||
}
|
||||
@@ -153,7 +190,6 @@ export namespace DirectVolume {
|
||||
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.uIsoValue, props.isoValueNorm)
|
||||
ValueCell.updateIfChanged(values.uAlpha, props.alpha)
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
|
||||
|
||||
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
|
||||
@@ -161,7 +197,9 @@ export namespace DirectVolume {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -188,11 +226,12 @@ const mTmp = Mat4.identity()
|
||||
const mTmp2 = Mat4.identity()
|
||||
const vHalfUnit = Vec3.create(0.5, 0.5, 0.5)
|
||||
const tmpVertices = new Float32Array(VolumeBox.vertices.length)
|
||||
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4, transform: Float32Array, transformCount: number) {
|
||||
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4) {
|
||||
tmpVertices.set(VolumeBox.vertices)
|
||||
Mat4.fromTranslation(mTmp, vHalfUnit)
|
||||
Mat4.mul(mTmp, Mat4.fromScaling(mTmp2, gridDimension), mTmp)
|
||||
Mat4.mul(mTmp, gridTransform, mTmp)
|
||||
transformPositionArray(mTmp, tmpVertices, 0, tmpVertices.length / 3)
|
||||
return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
|
||||
return calculateInvariantBoundingSphere(tmpVertices, tmpVertices.length / 3, 1)
|
||||
// return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -19,32 +19,30 @@ import { Spheres } from './spheres/spheres';
|
||||
import { arrayMax } from '../../mol-util/array';
|
||||
import { TransformData } from './transform-data';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { RenderObjectValuesType } from '../../mol-gl/render-object';
|
||||
import { ValueOf } from '../../mol-util/type-helpers';
|
||||
import { RenderObjectValues } from '../../mol-gl/render-object';
|
||||
import { TextureMesh } from './texture-mesh/texture-mesh';
|
||||
|
||||
export type GeometryKindType = {
|
||||
'mesh': Mesh,
|
||||
'points': Points,
|
||||
'spheres': Spheres,
|
||||
'text': Text,
|
||||
'lines': Lines,
|
||||
'direct-volume': DirectVolume,
|
||||
'texture-mesh': TextureMesh,
|
||||
}
|
||||
export type GeometryKindParams = {
|
||||
'mesh': Mesh.Params,
|
||||
'points': Points.Params,
|
||||
'spheres': Spheres.Params,
|
||||
'text': Text.Params,
|
||||
'lines': Lines.Params,
|
||||
'direct-volume': DirectVolume.Params,
|
||||
'texture-mesh': TextureMesh.Params,
|
||||
}
|
||||
export type GeometryKind = keyof GeometryKindType
|
||||
export type Geometry = ValueOf<GeometryKindType>
|
||||
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
|
||||
|
||||
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryKindParams[G['kind']], V = RenderObjectValuesType[G['kind']]> {
|
||||
export type Geometry<T extends GeometryKind = GeometryKind> =
|
||||
T extends 'mesh' ? Mesh :
|
||||
T extends 'points' ? Points :
|
||||
T extends 'spheres' ? Spheres :
|
||||
T extends 'text' ? Text :
|
||||
T extends 'lines' ? Lines :
|
||||
T extends 'direct-volume' ? DirectVolume :
|
||||
T extends 'texture-mesh' ? TextureMesh : never
|
||||
|
||||
type GeometryParams<T extends GeometryKind> =
|
||||
T extends 'mesh' ? Mesh.Params :
|
||||
T extends 'points' ? Points.Params :
|
||||
T extends 'spheres' ? Spheres.Params :
|
||||
T extends 'text' ? Text.Params :
|
||||
T extends 'lines' ? Lines.Params :
|
||||
T extends 'direct-volume' ? DirectVolume.Params :
|
||||
T extends 'texture-mesh' ? TextureMesh.Params : never
|
||||
|
||||
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryParams<G['kind']>, V = RenderObjectValues<G['kind']>> {
|
||||
Params: P
|
||||
createEmpty(geometry?: G): G
|
||||
createValues(geometry: G, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<P>): V
|
||||
@@ -56,7 +54,7 @@ export interface GeometryUtils<G extends Geometry, P extends PD.Params = Geometr
|
||||
}
|
||||
|
||||
export namespace Geometry {
|
||||
export type Params<G extends Geometry> = GeometryKindParams[G['kind']]
|
||||
export type Params<G extends Geometry> = GeometryParams<G['kind']>
|
||||
|
||||
export function getDrawCount(geometry: Geometry): number {
|
||||
switch (geometry.kind) {
|
||||
@@ -66,7 +64,7 @@ export namespace Geometry {
|
||||
case 'text': return geometry.charCount * 2 * 3
|
||||
case 'lines': return geometry.lineCount * 2 * 3
|
||||
case 'direct-volume': return 12 * 3
|
||||
case 'texture-mesh': return geometry.vertexCount.ref.value
|
||||
case 'texture-mesh': return geometry.vertexCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ export namespace Geometry {
|
||||
case 'direct-volume':
|
||||
return 1
|
||||
case 'texture-mesh':
|
||||
return geometry.groupCount.ref.value
|
||||
return geometry.groupCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +94,6 @@ export namespace Geometry {
|
||||
case 'direct-volume': return DirectVolume.Utils as any
|
||||
case 'texture-mesh': return TextureMesh.Utils as any
|
||||
}
|
||||
throw new Error('unknown geometry kind')
|
||||
}
|
||||
|
||||
export function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Lines } from './lines';
|
||||
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
@@ -12,12 +11,15 @@ import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
|
||||
export interface LinesBuilder {
|
||||
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void
|
||||
addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, group: number): void
|
||||
addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, group: number): void
|
||||
addCage(t: Mat4, cage: Cage, group: number): void
|
||||
getLines(): Lines
|
||||
}
|
||||
|
||||
const tmpVecA = Vec3.zero()
|
||||
const tmpVecB = Vec3.zero()
|
||||
const tmpVecA = Vec3()
|
||||
const tmpVecB = Vec3()
|
||||
const tmpDir = Vec3()
|
||||
|
||||
export namespace LinesBuilder {
|
||||
export function create(initialCount = 2048, chunkSize = 1024, lines?: Lines): LinesBuilder {
|
||||
@@ -42,8 +44,29 @@ export namespace LinesBuilder {
|
||||
ChunkedArray.add3(indices, offset + 1, offset + 3, offset + 2);
|
||||
}
|
||||
|
||||
const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, group: number) => {
|
||||
const d = Vec3.distance(start, end)
|
||||
const s = Math.floor(segmentCount / 2)
|
||||
const step = 1 / segmentCount
|
||||
|
||||
Vec3.sub(tmpDir, end, start)
|
||||
for (let j = 0; j < s; ++j) {
|
||||
const f = step * (j * 2 + 1)
|
||||
Vec3.setMagnitude(tmpDir, tmpDir, d * f)
|
||||
Vec3.add(tmpVecA, start, tmpDir)
|
||||
Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2))
|
||||
Vec3.add(tmpVecB, start, tmpDir)
|
||||
add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], group)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
add,
|
||||
addFixedCountDashes,
|
||||
addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, group: number) => {
|
||||
const d = Vec3.distance(start, end)
|
||||
addFixedCountDashes(start, end, d / segmentLength, group)
|
||||
},
|
||||
addCage: (t: Mat4, cage: Cage, group: number) => {
|
||||
const { vertices, edges } = cage
|
||||
for (let i = 0, il = edges.length; i < il; i += 2) {
|
||||
@@ -60,15 +83,7 @@ export namespace LinesBuilder {
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
const sb = ChunkedArray.compact(starts, true) as Float32Array
|
||||
const eb = ChunkedArray.compact(ends, true) as Float32Array
|
||||
return {
|
||||
kind: 'lines',
|
||||
lineCount: indices.elementCount / 2,
|
||||
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
|
||||
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
|
||||
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
|
||||
}
|
||||
return Lines.create(mb, ib, gb, sb, eb, indices.elementCount / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
|
||||
GroupMapping,
|
||||
createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -17,19 +19,22 @@ import { LinesValues } from '../../../mol-gl/renderable/lines';
|
||||
import { Mesh } from '../mesh/mesh';
|
||||
import { LinesBuilder } from './lines-builder';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
/** Wide line */
|
||||
export interface Lines {
|
||||
readonly kind: 'lines',
|
||||
|
||||
/** Number of lines */
|
||||
lineCount: number,
|
||||
|
||||
/** Mapping buffer as array of xy values wrapped in a value cell */
|
||||
readonly mappingBuffer: ValueCell<Float32Array>,
|
||||
/** Index buffer as array of vertex index triplets wrapped in a value cell */
|
||||
@@ -40,24 +45,27 @@ export interface Lines {
|
||||
readonly startBuffer: ValueCell<Float32Array>,
|
||||
/** Line end buffer as array of xyz values wrapped in a value cell */
|
||||
readonly endBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the lines */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to line indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Lines {
|
||||
export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines?: Lines): Lines {
|
||||
return lines ?
|
||||
update(mappings, indices, groups, starts, ends, lineCount, lines) :
|
||||
fromArrays(mappings, indices, groups, starts, ends, lineCount)
|
||||
}
|
||||
|
||||
export function createEmpty(lines?: Lines): Lines {
|
||||
const mb = lines ? lines.mappingBuffer.ref.value : new Float32Array(0)
|
||||
const ib = lines ? lines.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = lines ? lines.groupBuffer.ref.value : new Float32Array(0)
|
||||
const sb = lines ? lines.startBuffer.ref.value : new Float32Array(0)
|
||||
const eb = lines ? lines.endBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'lines',
|
||||
lineCount: 0,
|
||||
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
|
||||
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
|
||||
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
|
||||
}
|
||||
return create(mb, ib, gb, sb, eb, 0, lines)
|
||||
}
|
||||
|
||||
export function fromMesh(mesh: Mesh, lines?: Lines) {
|
||||
@@ -81,16 +89,67 @@ export namespace Lines {
|
||||
return builder.getLines();
|
||||
}
|
||||
|
||||
export function transformImmediate(line: Lines, t: Mat4) {
|
||||
transformRangeImmediate(line, t, 0, line.lineCount)
|
||||
function hashCode(lines: Lines) {
|
||||
return hashFnv32a([
|
||||
lines.lineCount, lines.mappingBuffer.ref.version, lines.indexBuffer.ref.version,
|
||||
lines.groupBuffer.ref.version, lines.startBuffer.ref.version, lines.startBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(lines: Lines, t: Mat4, offset: number, count: number) {
|
||||
function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number): Lines {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const lines = {
|
||||
kind: 'lines' as const,
|
||||
lineCount,
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
startBuffer: ValueCell.create(starts),
|
||||
endBuffer: ValueCell.create(ends),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(lines)
|
||||
if (newHash !== currentHash) {
|
||||
const s = calculateInvariantBoundingSphere(lines.startBuffer.ref.value, lines.lineCount * 4, 4)
|
||||
const e = calculateInvariantBoundingSphere(lines.endBuffer.ref.value, lines.lineCount * 4, 4)
|
||||
|
||||
Sphere3D.expandBySphere(boundingSphere, s, e)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (lines.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(lines.groupBuffer.ref.value, lines.lineCount, 4)
|
||||
currentGroup = lines.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines: Lines) {
|
||||
lines.lineCount = lineCount
|
||||
ValueCell.update(lines.mappingBuffer, mappings)
|
||||
ValueCell.update(lines.indexBuffer, indices)
|
||||
ValueCell.update(lines.groupBuffer, groups)
|
||||
ValueCell.update(lines.startBuffer, starts)
|
||||
ValueCell.update(lines.endBuffer, ends)
|
||||
return lines
|
||||
}
|
||||
|
||||
export function transform(lines: Lines, t: Mat4) {
|
||||
const start = lines.startBuffer.ref.value
|
||||
transformPositionArray(t, start, offset, count * 4)
|
||||
transformPositionArray(t, start, 0, lines.lineCount * 4)
|
||||
ValueCell.update(lines.startBuffer, start);
|
||||
const end = lines.endBuffer.ref.value
|
||||
transformPositionArray(t, end, offset, count * 4)
|
||||
transformPositionArray(t, end, 0, lines.lineCount * 4)
|
||||
ValueCell.update(lines.endBuffer, end);
|
||||
}
|
||||
|
||||
@@ -124,8 +183,8 @@ export namespace Lines {
|
||||
|
||||
const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(lines.startBuffer.ref.value, lines.endBuffer.ref.value, lines.lineCount,
|
||||
transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aMapping: lines.mappingBuffer,
|
||||
@@ -163,10 +222,9 @@ export namespace Lines {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: LinesValues, lines: Lines) {
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(
|
||||
values.aStart.ref.value, values.aEnd.ref.value, lines.lineCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -174,13 +232,4 @@ export namespace Lines {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
34
src/mol-geo/geometry/mesh/builder/axes.ts
Normal file
34
src/mol-geo/geometry/mesh/builder/axes.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { Axes3D } from '../../../../mol-math/geometry';
|
||||
import { createCage } from '../../../primitive/cage';
|
||||
|
||||
const tmpVec = Vec3()
|
||||
const tmpMatrix = Mat4.identity()
|
||||
|
||||
const tmpVertices = new Float32Array(6 * 3)
|
||||
const tmpEdges = new Uint8Array([0, 1, 2, 3, 4, 5])
|
||||
|
||||
export function addAxes(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
|
||||
const { origin, dirA, dirB, dirC } = axes
|
||||
|
||||
Vec3.add(tmpVec, origin, dirA)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirA), tmpVertices, 0)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirA), tmpVertices, 3)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirB), tmpVertices, 6)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirB), tmpVertices, 9)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirC), tmpVertices, 12)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirC), tmpVertices, 15)
|
||||
|
||||
const cage = createCage(tmpVertices, tmpEdges)
|
||||
const volume = Axes3D.volume(axes)
|
||||
const radius = (Math.cbrt(volume) / 300) * radiusScale
|
||||
|
||||
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Box3D } from '../../../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { Box3D, Axes3D } from '../../../../mol-math/geometry';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { CylinderProps } from '../../../primitive/cylinder';
|
||||
import { addCylinder } from './cylinder';
|
||||
import { addSphere } from './sphere';
|
||||
import { createCage } from '../../../primitive/cage';
|
||||
|
||||
const tmpStart = Vec3.zero()
|
||||
const tmpEnd = Vec3.zero()
|
||||
@@ -62,4 +63,49 @@ export function addBoundingBox(state: MeshBuilder.State, box: Box3D, radius: num
|
||||
Vec3.set(tmpEnd, min[0], max[1], max[2])
|
||||
addSphere(state, tmpEnd, radius, detail)
|
||||
addCylinder(state, tmpStart, tmpEnd, 1, cylinderProps)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const tmpBoxVecCorner = Vec3()
|
||||
const tmpBoxVecA = Vec3()
|
||||
const tmpBoxVecB = Vec3()
|
||||
const tmpBoxVecC = Vec3()
|
||||
const tmpMatrix = Mat4.identity()
|
||||
|
||||
const tmpVertices = new Float32Array(8 * 3)
|
||||
const tmpEdges = new Uint8Array([
|
||||
0, 1, 0, 3, 0, 6, 1, 2, 1, 7, 2, 3,
|
||||
2, 4, 3, 5, 4, 5, 4, 7, 5, 6, 6, 7
|
||||
])
|
||||
|
||||
export function addOrientedBox(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
|
||||
const { origin, dirA, dirB, dirC } = axes
|
||||
const negDirA = Vec3.negate(tmpBoxVecA, dirA)
|
||||
const negDirB = Vec3.negate(tmpBoxVecB, dirB)
|
||||
const negDirC = Vec3.negate(tmpBoxVecC, dirC)
|
||||
|
||||
let offset = 0
|
||||
const addCornerHelper = function (v1: Vec3, v2: Vec3, v3: Vec3) {
|
||||
Vec3.copy(tmpBoxVecCorner, origin)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v1)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v2)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v3)
|
||||
Vec3.toArray(tmpBoxVecCorner, tmpVertices, offset)
|
||||
offset += 3
|
||||
}
|
||||
addCornerHelper(dirA, dirB, dirC)
|
||||
addCornerHelper(dirA, dirB, negDirC)
|
||||
addCornerHelper(dirA, negDirB, negDirC)
|
||||
addCornerHelper(dirA, negDirB, dirC)
|
||||
addCornerHelper(negDirA, negDirB, negDirC)
|
||||
addCornerHelper(negDirA, negDirB, dirC)
|
||||
addCornerHelper(negDirA, dirB, dirC)
|
||||
addCornerHelper(negDirA, dirB, negDirC)
|
||||
|
||||
const cage = createCage(tmpVertices, tmpEdges)
|
||||
const volume = Axes3D.volume(axes)
|
||||
const radius = (Math.cbrt(volume) / 300) * radiusScale
|
||||
|
||||
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
|
||||
}
|
||||
@@ -26,8 +26,7 @@ function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) {
|
||||
Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir)
|
||||
// ensure the direction used to create the rotation is always pointing in the same
|
||||
// direction so the triangles of adjacent cylinder will line up
|
||||
Vec3.copy(tmpUp, up)
|
||||
if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1)
|
||||
Vec3.matchDirection(tmpUp, up, tmpCylinderMatDir)
|
||||
Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir)
|
||||
return Mat4.setTranslation(m, tmpCylinderCenter)
|
||||
}
|
||||
|
||||
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { getSphere } from './sphere';
|
||||
|
||||
const tmpEllipsoidMat = Mat4.identity()
|
||||
const tmpVec = Vec3()
|
||||
|
||||
function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
|
||||
Vec3.add(tmpVec, center, dirMajor)
|
||||
Mat4.targetTo(m, center, tmpVec, dirMinor)
|
||||
Mat4.setTranslation(m, center)
|
||||
return Mat4.scale(m, m, radiusScale)
|
||||
}
|
||||
|
||||
export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
|
||||
MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
|
||||
}
|
||||
@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) {
|
||||
return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
|
||||
}
|
||||
|
||||
function getSphere(detail: number) {
|
||||
export function getSphere(detail: number) {
|
||||
let sphere = sphereMap.get(detail)
|
||||
if (sphere === undefined) {
|
||||
sphere = Sphere(detail)
|
||||
|
||||
90
src/mol-geo/geometry/mesh/laplacian-smoothing.ts
Normal file
90
src/mol-geo/geometry/mesh/laplacian-smoothing.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
// TODO
|
||||
|
||||
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
|
||||
// dst[3 * j] += src[3 * i];
|
||||
// dst[3 * j + 1] += src[3 * i + 1];
|
||||
// dst[3 * j + 2] += src[3 * i + 2];
|
||||
// }
|
||||
|
||||
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
|
||||
// const triCount = surface.triangleIndices.length,
|
||||
// src = surface.vertices;
|
||||
|
||||
// const triangleIndices = surface.triangleIndices;
|
||||
|
||||
// for (let i = 0; i < triCount; i += 3) {
|
||||
// const a = triangleIndices[i],
|
||||
// b = triangleIndices[i + 1],
|
||||
// c = triangleIndices[i + 2];
|
||||
|
||||
// addVertex(src, b, vs, a);
|
||||
// addVertex(src, c, vs, a);
|
||||
|
||||
// addVertex(src, a, vs, b);
|
||||
// addVertex(src, c, vs, b);
|
||||
|
||||
// addVertex(src, a, vs, c);
|
||||
// addVertex(src, b, vs, c);
|
||||
// }
|
||||
|
||||
// const vw = 2 * vertexWeight;
|
||||
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
|
||||
// const n = vertexCounts[i] + vw;
|
||||
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
|
||||
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
|
||||
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
|
||||
// const vertexCounts = new Int32Array(surface.vertexCount),
|
||||
// triCount = surface.triangleIndices.length;
|
||||
|
||||
// const tris = surface.triangleIndices;
|
||||
// for (let i = 0; i < triCount; i++) {
|
||||
// // in a triangle 2 edges touch each vertex, hence the constant.
|
||||
// vertexCounts[tris[i]] += 2;
|
||||
// }
|
||||
|
||||
// let vs = new Float32Array(surface.vertices.length);
|
||||
// let started = Utils.PerformanceMonitor.currentTime();
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
// for (let i = 0; i < iterCount; i++) {
|
||||
// if (i > 0) {
|
||||
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
|
||||
// }
|
||||
// surface.normals = void 0;
|
||||
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
|
||||
// const t = surface.vertices;
|
||||
// surface.vertices = <any>vs;
|
||||
// vs = <any>t;
|
||||
|
||||
// const time = Utils.PerformanceMonitor.currentTime();
|
||||
// if (time - started > Computation.UpdateProgressDelta) {
|
||||
// started = time;
|
||||
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
|
||||
// }
|
||||
// }
|
||||
// return surface;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// * Smooths the vertices by averaging the neighborhood.
|
||||
// *
|
||||
// * Resets normals. Might replace vertex array.
|
||||
// */
|
||||
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
|
||||
|
||||
// if (iterCount < 1) iterCount = 0;
|
||||
// if (iterCount === 0) return Computation.resolve(surface);
|
||||
|
||||
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
|
||||
// }
|
||||
@@ -1,14 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Mesh } from './mesh';
|
||||
import { getNormalMatrix } from '../../util';
|
||||
import { Primitive } from '../../primitive/primitive';
|
||||
import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
import { addSphere } from './builder/sphere';
|
||||
@@ -85,7 +83,7 @@ export namespace MeshBuilder {
|
||||
const { vertices: va, normals: na, indices: ia } = primitive
|
||||
const { vertices, normals, indices, groups, currentGroup } = state
|
||||
const offset = vertices.elementCount
|
||||
const n = getNormalMatrix(tmpMat3, t)
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = va.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
|
||||
@@ -101,9 +99,30 @@ export namespace MeshBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number) {
|
||||
/** Flips triangle normals and winding order */
|
||||
export function addPrimitiveFlipped(state: State, t: Mat4, primitive: Primitive) {
|
||||
const { vertices: va, normals: na, indices: ia } = primitive
|
||||
const { vertices, normals, indices, groups, currentGroup } = state
|
||||
const offset = vertices.elementCount
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = va.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
|
||||
ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
|
||||
// normal
|
||||
Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, na, i), n)
|
||||
ChunkedArray.add3(normals, -tmpV[0], -tmpV[1], -tmpV[2]);
|
||||
// group
|
||||
ChunkedArray.add(groups, currentGroup);
|
||||
}
|
||||
for (let i = 0, il = ia.length; i < il; i += 3) {
|
||||
ChunkedArray.add3(indices, ia[i + 2] + offset, ia[i + 1] + offset, ia[i] + offset);
|
||||
}
|
||||
}
|
||||
|
||||
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number, radialSegments: number) {
|
||||
const { vertices: va, edges: ea } = cage
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius }
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments }
|
||||
for (let i = 0, il = ea.length; i < il; i += 2) {
|
||||
Vec3.fromArray(tmpVecA, va, ea[i] * 3)
|
||||
Vec3.fromArray(tmpVecB, va, ea[i + 1] * 3)
|
||||
@@ -121,15 +140,6 @@ export namespace MeshBuilder {
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const nb = ChunkedArray.compact(normals, true) as Float32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount: state.vertices.elementCount,
|
||||
triangleCount: state.indices.elementCount,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
normalsComputed: true,
|
||||
}
|
||||
return Mesh.create(vb, ib, nb, gb, state.vertices.elementCount, state.indices.elementCount, mesh)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 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>
|
||||
*/
|
||||
|
||||
import { Task } from '../../../mol-task'
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Vec3, Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra'
|
||||
import { Sphere3D } from '../../../mol-math/geometry'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { LocationIterator } from '../../util/location-iterator';
|
||||
import { createColors } from '../color-data';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { ChunkedArray, hashFnv32a } from '../../../mol-data/util';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
@@ -41,85 +41,96 @@ export interface Mesh {
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Flag indicating if normals are computed for the current set of vertices */
|
||||
normalsComputed: boolean,
|
||||
|
||||
/** Bounding sphere of the mesh */
|
||||
boundingSphere?: Sphere3D
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to vertex indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Mesh {
|
||||
export function create(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh?: Mesh): Mesh {
|
||||
return mesh ?
|
||||
update(vertices, indices, normals, groups, vertexCount, triangleCount, mesh) :
|
||||
fromArrays(vertices, indices, normals, groups, vertexCount, triangleCount)
|
||||
}
|
||||
|
||||
export function createEmpty(mesh?: Mesh): Mesh {
|
||||
const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0)
|
||||
const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0)
|
||||
const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount: 0,
|
||||
triangleCount: 0,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
normalsComputed: true,
|
||||
}
|
||||
return create(vb, ib, nb, gb, 0, 0, mesh)
|
||||
}
|
||||
|
||||
export function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, normalsComputed: boolean): Mesh {
|
||||
return {
|
||||
kind: 'mesh',
|
||||
function hashCode(mesh: Mesh) {
|
||||
return hashFnv32a([
|
||||
mesh.vertexCount, mesh.triangleCount,
|
||||
mesh.vertexBuffer.ref.version, mesh.indexBuffer.ref.version,
|
||||
mesh.normalBuffer.ref.version, mesh.groupBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number): Mesh {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const mesh = {
|
||||
kind: 'mesh' as const,
|
||||
vertexCount,
|
||||
triangleCount,
|
||||
vertexBuffer: ValueCell.create(vertices),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
normalBuffer: ValueCell.create(normals),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
normalsComputed,
|
||||
}
|
||||
}
|
||||
|
||||
export function computeNormalsImmediate(mesh: Mesh) {
|
||||
if (mesh.normalsComputed) return;
|
||||
|
||||
const normals = mesh.normalBuffer.ref.value.length >= mesh.vertexCount * 3
|
||||
? mesh.normalBuffer.ref.value : new Float32Array(mesh.vertexBuffer.ref.value.length);
|
||||
|
||||
const v = mesh.vertexBuffer.ref.value, triangles = mesh.indexBuffer.ref.value;
|
||||
|
||||
if (normals === mesh.normalBuffer.ref.value) {
|
||||
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
|
||||
normals[i] = 0; normals[i + 1] = 0; normals[i + 2] = 0;
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(mesh)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(mesh.vertexBuffer.ref.value, mesh.vertexCount, 1)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (mesh.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(mesh.groupBuffer.ref.value, mesh.vertexCount)
|
||||
currentGroup = mesh.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
|
||||
const x = Vec3.zero(), y = Vec3.zero(), z = Vec3.zero(), d1 = Vec3.zero(), d2 = Vec3.zero(), n = Vec3.zero();
|
||||
for (let i = 0, ii = 3 * mesh.triangleCount; i < ii; i += 3) {
|
||||
const a = 3 * triangles[i], b = 3 * triangles[i + 1], c = 3 * triangles[i + 2];
|
||||
function update(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh: Mesh) {
|
||||
mesh.vertexCount = vertexCount
|
||||
mesh.triangleCount = triangleCount
|
||||
ValueCell.update(mesh.vertexBuffer, vertices)
|
||||
ValueCell.update(mesh.indexBuffer, indices)
|
||||
ValueCell.update(mesh.normalBuffer, normals)
|
||||
ValueCell.update(mesh.groupBuffer, groups)
|
||||
return mesh
|
||||
}
|
||||
|
||||
Vec3.fromArray(x, v, a);
|
||||
Vec3.fromArray(y, v, b);
|
||||
Vec3.fromArray(z, v, c);
|
||||
Vec3.sub(d1, z, y);
|
||||
Vec3.sub(d2, x, y);
|
||||
Vec3.cross(n, d1, d2);
|
||||
export function computeNormals(mesh: Mesh) {
|
||||
const { vertexCount, triangleCount } = mesh
|
||||
const vertices = mesh.vertexBuffer.ref.value
|
||||
const indices = mesh.indexBuffer.ref.value
|
||||
|
||||
normals[a] += n[0]; normals[a + 1] += n[1]; normals[a + 2] += n[2];
|
||||
normals[b] += n[0]; normals[b + 1] += n[1]; normals[b + 2] += n[2];
|
||||
normals[c] += n[0]; normals[c + 1] += n[1]; normals[c + 2] += n[2];
|
||||
const normals = mesh.normalBuffer.ref.value.length >= vertexCount * 3
|
||||
? mesh.normalBuffer.ref.value
|
||||
: new Float32Array(vertexCount * 3);
|
||||
|
||||
if (normals === mesh.normalBuffer.ref.value) {
|
||||
normals.fill(0, 0, vertexCount * 3)
|
||||
}
|
||||
|
||||
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
|
||||
const nx = normals[i];
|
||||
const ny = normals[i + 1];
|
||||
const nz = normals[i + 2];
|
||||
const f = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
normals[i] *= f; normals[i + 1] *= f; normals[i + 2] *= f;
|
||||
|
||||
// console.log([normals[i], normals[i + 1], normals[i + 2]], [v[i], v[i + 1], v[i + 2]])
|
||||
}
|
||||
computeIndexedVertexNormals(vertices, indices, normals, vertexCount, triangleCount)
|
||||
ValueCell.update(mesh.normalBuffer, normals);
|
||||
mesh.normalsComputed = true;
|
||||
}
|
||||
|
||||
export function checkForDuplicateVertices(mesh: Mesh, fractionDigits = 3) {
|
||||
@@ -129,7 +140,7 @@ export namespace Mesh {
|
||||
const hash = (v: Vec3, d: number) => `${v[0].toFixed(d)}|${v[1].toFixed(d)}|${v[2].toFixed(d)}`
|
||||
let duplicates = 0
|
||||
|
||||
const a = Vec3.zero()
|
||||
const a = Vec3()
|
||||
for (let i = 0, il = mesh.vertexCount; i < il; ++i) {
|
||||
Vec3.fromArray(a, v, i * 3)
|
||||
const k = hash(a, fractionDigits)
|
||||
@@ -144,63 +155,15 @@ export namespace Mesh {
|
||||
return duplicates
|
||||
}
|
||||
|
||||
export function computeNormals(surface: Mesh): Task<Mesh> {
|
||||
return Task.create<Mesh>('Surface (Compute Normals)', async ctx => {
|
||||
if (surface.normalsComputed) return surface;
|
||||
|
||||
await ctx.update('Computing normals...');
|
||||
computeNormalsImmediate(surface);
|
||||
return surface;
|
||||
});
|
||||
}
|
||||
|
||||
export function transformImmediate(mesh: Mesh, t: Mat4) {
|
||||
transformRangeImmediate(mesh, t, 0, mesh.vertexCount)
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(mesh: Mesh, t: Mat4, offset: number, count: number) {
|
||||
const tmpMat3 = Mat3()
|
||||
export function transform(mesh: Mesh, t: Mat4) {
|
||||
const v = mesh.vertexBuffer.ref.value
|
||||
transformPositionArray(t, v, offset, count)
|
||||
// TODO normals transformation does not work for an unknown reason, ASR
|
||||
// if (mesh.normalBuffer.ref.value) {
|
||||
// const n = getNormalMatrix(Mat3.zero(), t)
|
||||
// transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count)
|
||||
// mesh.normalsComputed = true;
|
||||
// }
|
||||
transformPositionArray(t, v, 0, mesh.vertexCount)
|
||||
if (!Mat4.isTranslationAndUniformScaling(t)) {
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
transformDirectionArray(n, mesh.normalBuffer.ref.value, 0, mesh.vertexCount)
|
||||
}
|
||||
ValueCell.update(mesh.vertexBuffer, v);
|
||||
mesh.normalsComputed = false;
|
||||
}
|
||||
|
||||
export function computeBoundingSphere(mesh: Mesh): Task<Mesh> {
|
||||
return Task.create<Mesh>('Mesh (Compute Bounding Sphere)', async ctx => {
|
||||
if (mesh.boundingSphere) {
|
||||
return mesh;
|
||||
}
|
||||
await ctx.update('Computing bounding sphere...');
|
||||
|
||||
const vertices = mesh.vertexBuffer.ref.value;
|
||||
let x = 0, y = 0, z = 0;
|
||||
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
|
||||
x += vertices[i];
|
||||
y += vertices[i + 1];
|
||||
z += vertices[i + 2];
|
||||
}
|
||||
x /= mesh.vertexCount;
|
||||
y /= mesh.vertexCount;
|
||||
z /= mesh.vertexCount;
|
||||
let r = 0;
|
||||
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
|
||||
const dx = x - vertices[i];
|
||||
const dy = y - vertices[i + 1];
|
||||
const dz = z - vertices[i + 2];
|
||||
r = Math.max(r, dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
mesh.boundingSphere = {
|
||||
center: Vec3.create(x, y, z),
|
||||
radius: Math.sqrt(r)
|
||||
}
|
||||
return mesh;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,12 +191,12 @@ export namespace Mesh {
|
||||
group.currentIndex = vertexCount
|
||||
group.elementCount = vertexCount
|
||||
|
||||
const vi = Vec3.zero()
|
||||
const vj = Vec3.zero()
|
||||
const vk = Vec3.zero()
|
||||
const ni = Vec3.zero()
|
||||
const nj = Vec3.zero()
|
||||
const nk = Vec3.zero()
|
||||
const vi = Vec3()
|
||||
const vj = Vec3()
|
||||
const vk = Vec3()
|
||||
const ni = Vec3()
|
||||
const nj = Vec3()
|
||||
const nk = Vec3()
|
||||
|
||||
function add(i: number) {
|
||||
Vec3.fromArray(vi, vb, i * 3)
|
||||
@@ -361,6 +324,7 @@ export namespace Mesh {
|
||||
doubleSided: PD.Boolean(false),
|
||||
flipSided: PD.Boolean(false),
|
||||
flatShaded: PD.Boolean(false),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -388,10 +352,8 @@ export namespace Mesh {
|
||||
|
||||
const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
mesh.vertexBuffer.ref.value, mesh.vertexCount,
|
||||
transform.aTransform.ref.value, instanceCount
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: mesh.vertexBuffer,
|
||||
@@ -410,6 +372,7 @@ export namespace Mesh {
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dFlatShaded: ValueCell.create(props.flatShaded),
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,13 +387,13 @@ export namespace Mesh {
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, mesh.vertexCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -439,86 +402,3 @@ export namespace Mesh {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
|
||||
// dst[3 * j] += src[3 * i];
|
||||
// dst[3 * j + 1] += src[3 * i + 1];
|
||||
// dst[3 * j + 2] += src[3 * i + 2];
|
||||
// }
|
||||
|
||||
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
|
||||
// const triCount = surface.triangleIndices.length,
|
||||
// src = surface.vertices;
|
||||
|
||||
// const triangleIndices = surface.triangleIndices;
|
||||
|
||||
// for (let i = 0; i < triCount; i += 3) {
|
||||
// const a = triangleIndices[i],
|
||||
// b = triangleIndices[i + 1],
|
||||
// c = triangleIndices[i + 2];
|
||||
|
||||
// addVertex(src, b, vs, a);
|
||||
// addVertex(src, c, vs, a);
|
||||
|
||||
// addVertex(src, a, vs, b);
|
||||
// addVertex(src, c, vs, b);
|
||||
|
||||
// addVertex(src, a, vs, c);
|
||||
// addVertex(src, b, vs, c);
|
||||
// }
|
||||
|
||||
// const vw = 2 * vertexWeight;
|
||||
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
|
||||
// const n = vertexCounts[i] + vw;
|
||||
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
|
||||
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
|
||||
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
|
||||
// const vertexCounts = new Int32Array(surface.vertexCount),
|
||||
// triCount = surface.triangleIndices.length;
|
||||
|
||||
// const tris = surface.triangleIndices;
|
||||
// for (let i = 0; i < triCount; i++) {
|
||||
// // in a triangle 2 edges touch each vertex, hence the constant.
|
||||
// vertexCounts[tris[i]] += 2;
|
||||
// }
|
||||
|
||||
// let vs = new Float32Array(surface.vertices.length);
|
||||
// let started = Utils.PerformanceMonitor.currentTime();
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
// for (let i = 0; i < iterCount; i++) {
|
||||
// if (i > 0) {
|
||||
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
|
||||
// }
|
||||
// surface.normals = void 0;
|
||||
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
|
||||
// const t = surface.vertices;
|
||||
// surface.vertices = <any>vs;
|
||||
// vs = <any>t;
|
||||
|
||||
// const time = Utils.PerformanceMonitor.currentTime();
|
||||
// if (time - started > Computation.UpdateProgressDelta) {
|
||||
// started = time;
|
||||
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
|
||||
// }
|
||||
// }
|
||||
// return surface;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// * Smooths the vertices by averaging the neighborhood.
|
||||
// *
|
||||
// * Resets normals. Might replace vertex array.
|
||||
// */
|
||||
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
|
||||
|
||||
// if (iterCount < 1) iterCount = 0;
|
||||
// if (iterCount === 0) return Computation.resolve(surface);
|
||||
|
||||
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
|
||||
// }
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Points } from './points';
|
||||
|
||||
@@ -26,12 +25,7 @@ export namespace PointsBuilder {
|
||||
getPoints: () => {
|
||||
const cb = ChunkedArray.compact(centers, true) as Float32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'points',
|
||||
pointCount: centers.elementCount,
|
||||
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
|
||||
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
|
||||
}
|
||||
return Points.create(cb, gb, centers.elementCount, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
|
||||
GroupMapping,
|
||||
createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -14,7 +16,7 @@ import { createSizes } from '../size-data';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { LocationIterator } from '../../util/location-iterator';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { PointsValues } from '../../../mol-gl/renderable/points';
|
||||
@@ -23,37 +25,88 @@ import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
/** Point cloud */
|
||||
export interface Points {
|
||||
readonly kind: 'points',
|
||||
|
||||
/** Number of vertices in the point cloud */
|
||||
pointCount: number,
|
||||
|
||||
/** Center buffer as array of xyz values wrapped in a value cell */
|
||||
readonly centerBuffer: ValueCell<Float32Array>,
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the points */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to point indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Points {
|
||||
export function create(centers: Float32Array, groups: Float32Array, pointCount: number, points?: Points): Points {
|
||||
return points ?
|
||||
update(centers, groups, pointCount, points) :
|
||||
fromArrays(centers, groups, pointCount)
|
||||
}
|
||||
|
||||
export function createEmpty(points?: Points): Points {
|
||||
const cb = points ? points.centerBuffer.ref.value : new Float32Array(0)
|
||||
const gb = points ? points.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'points',
|
||||
pointCount: 0,
|
||||
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
|
||||
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
|
||||
return create(cb, gb, 0, points)
|
||||
}
|
||||
|
||||
function hashCode(points: Points) {
|
||||
return hashFnv32a([
|
||||
points.pointCount, points.centerBuffer.ref.version, points.groupBuffer.ref.version,
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(centers: Float32Array, groups: Float32Array, pointCount: number): Points {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const points = {
|
||||
kind: 'points' as const,
|
||||
pointCount,
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(points)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(points.centerBuffer.ref.value, points.pointCount, 1)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (points.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(points.groupBuffer.ref.value, points.pointCount)
|
||||
currentGroup = points.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
export function transformImmediate(points: Points, t: Mat4) {
|
||||
transformRangeImmediate(points, t, 0, points.pointCount)
|
||||
function update(centers: Float32Array, groups: Float32Array, pointCount: number, points: Points) {
|
||||
points.pointCount = pointCount
|
||||
ValueCell.update(points.centerBuffer, centers)
|
||||
ValueCell.update(points.groupBuffer, groups)
|
||||
return points
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(points: Points, t: Mat4, offset: number, count: number) {
|
||||
export function transform(points: Points, t: Mat4) {
|
||||
const c = points.centerBuffer.ref.value
|
||||
transformPositionArray(t, c, offset, count)
|
||||
transformPositionArray(t, c, 0, points.pointCount)
|
||||
ValueCell.update(points.centerBuffer, c);
|
||||
}
|
||||
|
||||
@@ -89,10 +142,8 @@ export namespace Points {
|
||||
|
||||
const counts = { drawCount: points.pointCount, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
points.centerBuffer.ref.value, points.pointCount,
|
||||
transform.aTransform.ref.value, transform.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: points.centerBuffer,
|
||||
@@ -129,10 +180,9 @@ export namespace Points {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: PointsValues, points: Points) {
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, points.pointCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Spheres } from './spheres';
|
||||
|
||||
@@ -50,14 +49,7 @@ export namespace SpheresBuilder {
|
||||
const mb = ChunkedArray.compact(mappings, true) as Float32Array
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'spheres',
|
||||
sphereCount: centers.elementCount / 4,
|
||||
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb),
|
||||
}
|
||||
return Spheres.create(cb, mb, ib, gb, centers.elementCount / 4, spheres)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,15 +13,16 @@ import { Theme } from '../../../mol-theme/theme';
|
||||
import { SpheresValues } from '../../../mol-gl/renderable/spheres';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { createSizes, getMaxSize } from '../size-data';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
|
||||
/** Spheres */
|
||||
export interface Spheres {
|
||||
readonly kind: 'spheres',
|
||||
|
||||
@@ -36,28 +37,85 @@ export interface Spheres {
|
||||
readonly indexBuffer: ValueCell<Uint32Array>,
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the spheres */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to sphere indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Spheres {
|
||||
export function create(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres?: Spheres): Spheres {
|
||||
return spheres ?
|
||||
update(centers, mappings, indices, groups, sphereCount, spheres) :
|
||||
fromArrays(centers, mappings, indices, groups, sphereCount)
|
||||
}
|
||||
|
||||
export function createEmpty(spheres?: Spheres): Spheres {
|
||||
const cb = spheres ? spheres.centerBuffer.ref.value : new Float32Array(0)
|
||||
const mb = spheres ? spheres.mappingBuffer.ref.value : new Float32Array(0)
|
||||
const ib = spheres ? spheres.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = spheres ? spheres.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'spheres',
|
||||
sphereCount: 0,
|
||||
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb)
|
||||
return create(cb, mb, ib, gb, 0, spheres)
|
||||
}
|
||||
|
||||
function hashCode(spheres: Spheres) {
|
||||
return hashFnv32a([
|
||||
spheres.sphereCount,
|
||||
spheres.centerBuffer.ref.version, spheres.mappingBuffer.ref.version,
|
||||
spheres.indexBuffer.ref.version, spheres.groupBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number): Spheres {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const spheres = {
|
||||
kind: 'spheres' as const,
|
||||
sphereCount,
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(spheres)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(spheres.centerBuffer.ref.value, spheres.sphereCount * 4, 4)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (spheres.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(spheres.groupBuffer.ref.value, spheres.sphereCount, 4)
|
||||
currentGroup = spheres.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return spheres
|
||||
}
|
||||
|
||||
function update(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres: Spheres) {
|
||||
spheres.sphereCount = sphereCount
|
||||
ValueCell.update(spheres.centerBuffer, centers)
|
||||
ValueCell.update(spheres.mappingBuffer, mappings)
|
||||
ValueCell.update(spheres.indexBuffer, indices)
|
||||
ValueCell.update(spheres.groupBuffer, groups)
|
||||
return spheres
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
|
||||
doubleSided: PD.Boolean(false),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -87,10 +145,8 @@ export namespace Spheres {
|
||||
const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const padding = getMaxSize(size)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: spheres.centerBuffer,
|
||||
@@ -111,6 +167,7 @@ export namespace Spheres {
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
uSizeFactor: ValueCell.create(props.sizeFactor),
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,14 +181,14 @@ export namespace Spheres {
|
||||
BaseGeometry.updateValues(values, props)
|
||||
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
|
||||
const padding = getMaxSize(values)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, spheres.sphereCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ export type FontVariant = 'normal' | 'small-caps'
|
||||
export type FontWeight = 'normal' | 'bold'
|
||||
|
||||
export const FontAtlasParams = {
|
||||
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
|
||||
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
|
||||
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
|
||||
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
|
||||
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
|
||||
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
|
||||
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
|
||||
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
|
||||
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
|
||||
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
|
||||
}
|
||||
export type FontAtlasParams = typeof FontAtlasParams
|
||||
export type FontAtlasProps = PD.Values<FontAtlasParams>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Text } from './text';
|
||||
import { getFontAtlas } from './font-atlas';
|
||||
@@ -16,7 +15,7 @@ const quadIndices = new Uint16Array([
|
||||
])
|
||||
|
||||
export interface TextBuilder {
|
||||
add(str: string, x: number, y: number, z: number, depth: number, group: number): void
|
||||
add(str: string, x: number, y: number, z: number, depth: number, scale: number, group: number): void
|
||||
getText(): Text
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ export namespace TextBuilder {
|
||||
}
|
||||
|
||||
return {
|
||||
add: (str: string, x: number, y: number, z: number, depth: number, group: number) => {
|
||||
add: (str: string, x: number, y: number, z: number, depth: number, scale: number, group: number) => {
|
||||
let bWidth = 0
|
||||
const nChar = str.length
|
||||
|
||||
@@ -111,10 +110,10 @@ export namespace TextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
const xLeft = -xShift - margin - 0.1
|
||||
const xRight = bWidth - xShift + margin + 0.1
|
||||
const yTop = bHeight - yShift + margin
|
||||
const yBottom = -yShift - margin
|
||||
const xLeft = (-xShift - margin - 0.1) * scale
|
||||
const xRight = (bWidth - xShift + margin + 0.1) * scale
|
||||
const yTop = (bHeight - yShift + margin) * scale
|
||||
const yBottom = (-yShift - margin) * scale
|
||||
|
||||
// background
|
||||
if (background) {
|
||||
@@ -137,45 +136,49 @@ export namespace TextBuilder {
|
||||
let xBaseA: number, yBaseA: number
|
||||
let xBaseB: number, yBaseB: number
|
||||
let xBaseCenter: number, yBaseCenter: number
|
||||
|
||||
const scaledTetherLength = tetherLength * scale
|
||||
const scaledTetherBaseWidth = tetherBaseWidth * scale
|
||||
|
||||
switch (attachment) {
|
||||
case 'bottom-left':
|
||||
xTip = xLeft - tetherLength / 2
|
||||
xBaseA = xLeft + tetherBaseWidth / 2
|
||||
xTip = xLeft - scaledTetherLength / 2
|
||||
xBaseA = xLeft + scaledTetherBaseWidth / 2
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = yBottom - tetherLength / 2
|
||||
yTip = yBottom - scaledTetherLength / 2
|
||||
yBaseA = yBottom
|
||||
yBaseB = yBottom + tetherBaseWidth / 2
|
||||
yBaseB = yBottom + scaledTetherBaseWidth / 2
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'bottom-center':
|
||||
xTip = 0
|
||||
xBaseA = tetherBaseWidth / 2
|
||||
xBaseB = -tetherBaseWidth / 2
|
||||
xBaseA = scaledTetherBaseWidth / 2
|
||||
xBaseB = -scaledTetherBaseWidth / 2
|
||||
xBaseCenter = 0
|
||||
yTip = yBottom - tetherLength
|
||||
yTip = yBottom - scaledTetherLength
|
||||
yBaseA = yBottom
|
||||
yBaseB = yBottom
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'bottom-right':
|
||||
xTip = xRight + tetherLength / 2
|
||||
xTip = xRight + scaledTetherLength / 2
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight - tetherBaseWidth / 2
|
||||
xBaseB = xRight - scaledTetherBaseWidth / 2
|
||||
xBaseCenter = xRight
|
||||
yTip = yBottom - tetherLength / 2
|
||||
yBaseA = yBottom + tetherBaseWidth / 2
|
||||
yTip = yBottom - scaledTetherLength / 2
|
||||
yBaseA = yBottom + scaledTetherBaseWidth / 2
|
||||
yBaseB = yBottom
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'middle-left':
|
||||
xTip = xLeft - tetherLength
|
||||
xTip = xLeft - scaledTetherLength
|
||||
xBaseA = xLeft
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = 0
|
||||
yBaseA = -tetherBaseWidth / 2
|
||||
yBaseB = tetherBaseWidth / 2
|
||||
yBaseA = -scaledTetherBaseWidth / 2
|
||||
yBaseB = scaledTetherBaseWidth / 2
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'middle-center':
|
||||
@@ -189,42 +192,42 @@ export namespace TextBuilder {
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'middle-right':
|
||||
xTip = xRight + tetherLength
|
||||
xTip = xRight + scaledTetherLength
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight
|
||||
xBaseCenter = xRight
|
||||
yTip = 0
|
||||
yBaseA = tetherBaseWidth / 2
|
||||
yBaseB = -tetherBaseWidth / 2
|
||||
yBaseA = scaledTetherBaseWidth / 2
|
||||
yBaseB = -scaledTetherBaseWidth / 2
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'top-left':
|
||||
xTip = xLeft - tetherLength / 2
|
||||
xBaseA = xLeft + tetherBaseWidth / 2
|
||||
xTip = xLeft - scaledTetherLength / 2
|
||||
xBaseA = xLeft + scaledTetherBaseWidth / 2
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = yTop + tetherLength / 2
|
||||
yTip = yTop + scaledTetherLength / 2
|
||||
yBaseA = yTop
|
||||
yBaseB = yTop - tetherBaseWidth / 2
|
||||
yBaseB = yTop - scaledTetherBaseWidth / 2
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
case 'top-center':
|
||||
xTip = 0
|
||||
xBaseA = tetherBaseWidth / 2
|
||||
xBaseB = -tetherBaseWidth / 2
|
||||
xBaseA = scaledTetherBaseWidth / 2
|
||||
xBaseB = -scaledTetherBaseWidth / 2
|
||||
xBaseCenter = 0
|
||||
yTip = yTop + tetherLength
|
||||
yTip = yTop + scaledTetherLength
|
||||
yBaseA = yTop
|
||||
yBaseB = yTop
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
case 'top-right':
|
||||
xTip = xRight + tetherLength / 2
|
||||
xTip = xRight + scaledTetherLength / 2
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight - tetherBaseWidth / 2
|
||||
xBaseB = xRight - scaledTetherBaseWidth / 2
|
||||
xBaseCenter = xRight
|
||||
yTip = yTop + tetherLength / 2
|
||||
yBaseA = yTop - tetherBaseWidth / 2
|
||||
yTip = yTop + scaledTetherLength / 2
|
||||
yBaseA = yTop - scaledTetherBaseWidth / 2
|
||||
yBaseB = yTop
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
@@ -252,10 +255,15 @@ export namespace TextBuilder {
|
||||
for (let iChar = 0; iChar < nChar; ++iChar) {
|
||||
const c = fontAtlas.get(str[iChar])
|
||||
|
||||
ChunkedArray.add2(mappings, xadvance - xShift, c.nh - yShift) // top left
|
||||
ChunkedArray.add2(mappings, xadvance - xShift, -yShift) // bottom left
|
||||
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, c.nh - yShift) // top right
|
||||
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, -yShift) // bottom right
|
||||
const left = (xadvance - xShift) * scale
|
||||
const right = (xadvance + c.nw - xShift) * scale
|
||||
const top = (c.nh - yShift) * scale
|
||||
const bottom = (-yShift) * scale
|
||||
|
||||
ChunkedArray.add2(mappings, left, top)
|
||||
ChunkedArray.add2(mappings, left, bottom)
|
||||
ChunkedArray.add2(mappings, right, top)
|
||||
ChunkedArray.add2(mappings, right, bottom)
|
||||
|
||||
const texWidth = fontAtlas.texture.width
|
||||
const texHeight = fontAtlas.texture.height
|
||||
@@ -281,17 +289,7 @@ export namespace TextBuilder {
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
const tb = ChunkedArray.compact(tcoords, true) as Float32Array
|
||||
return {
|
||||
kind: 'text',
|
||||
charCount: indices.elementCount / 2,
|
||||
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
|
||||
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
|
||||
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
|
||||
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb),
|
||||
}
|
||||
return Text.create(ft, cb,mb, db, ib, gb, tb, indices.elementCount / 2, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -15,7 +15,7 @@ import { createSizes, getMaxSize } from '../size-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { calculateBoundingSphere, TextureImage, createTextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { TextureImage, createTextureImage, calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { TextValues } from '../../../mol-gl/renderable/text';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
@@ -26,6 +26,8 @@ import { createRenderObject as _createRenderObject } from '../../../mol-gl/rende
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
|
||||
type TextAttachment = (
|
||||
'bottom-left' | 'bottom-center' | 'bottom-right' |
|
||||
@@ -38,7 +40,8 @@ export interface Text {
|
||||
readonly kind: 'text',
|
||||
|
||||
/** Number of characters in the text */
|
||||
readonly charCount: number,
|
||||
charCount: number,
|
||||
|
||||
/** Font Atlas */
|
||||
readonly fontTexture: ValueCell<TextureImage<Uint8Array>>,
|
||||
|
||||
@@ -54,9 +57,20 @@ export interface Text {
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
/** Texture coordinates buffer as array of uv values wrapped in a value cell */
|
||||
readonly tcoordBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the text */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to text indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Text {
|
||||
export function create(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text?: Text): Text {
|
||||
return text ?
|
||||
update(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount, text) :
|
||||
fromData(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount)
|
||||
}
|
||||
|
||||
export function createEmpty(text?: Text): Text {
|
||||
const ft = text ? text.fontTexture.ref.value : createTextureImage(0, 1, Uint8Array)
|
||||
const cb = text ? text.centerBuffer.ref.value : new Float32Array(0)
|
||||
@@ -65,17 +79,66 @@ export namespace Text {
|
||||
const ib = text ? text.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = text ? text.groupBuffer.ref.value : new Float32Array(0)
|
||||
const tb = text ? text.tcoordBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'text',
|
||||
charCount: 0,
|
||||
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
|
||||
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
|
||||
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
|
||||
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb)
|
||||
return create(ft, cb, mb, db, ib, gb, tb, 0, text)
|
||||
}
|
||||
|
||||
function hashCode(text: Text) {
|
||||
return hashFnv32a([
|
||||
text.charCount, text.fontTexture.ref.version,
|
||||
text.centerBuffer.ref.version, text.mappingBuffer.ref.version,
|
||||
text.depthBuffer.ref.version, text.indexBuffer.ref.version,
|
||||
text.groupBuffer.ref.version, text.tcoordBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromData(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number): Text {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const text = {
|
||||
kind: 'text' as const,
|
||||
charCount,
|
||||
fontTexture: ValueCell.create(fontTexture),
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
depthBuffer: ValueCell.create(depths),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
tcoordBuffer: ValueCell.create(tcoords),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(text)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(text.centerBuffer.ref.value, text.charCount * 4, 4)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (text.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(text.groupBuffer.ref.value, text.charCount, 4)
|
||||
currentGroup = text.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
function update(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text: Text) {
|
||||
text.charCount = charCount
|
||||
ValueCell.update(text.fontTexture, fontTexture)
|
||||
ValueCell.update(text.centerBuffer, centers)
|
||||
ValueCell.update(text.mappingBuffer, mappings)
|
||||
ValueCell.update(text.depthBuffer, depths)
|
||||
ValueCell.update(text.indexBuffer, indices)
|
||||
ValueCell.update(text.groupBuffer, groups)
|
||||
ValueCell.update(text.tcoordBuffer, tcoords)
|
||||
return text
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
@@ -130,10 +193,8 @@ export namespace Text {
|
||||
const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const padding = getPadding(text.mappingBuffer.ref.value, text.depthBuffer.ref.value, text.charCount, getMaxSize(size))
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
text.centerBuffer.ref.value, text.charCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: text.centerBuffer,
|
||||
@@ -194,10 +255,9 @@ export namespace Text {
|
||||
|
||||
function updateBoundingSphere(values: TextValues, text: Text) {
|
||||
const padding = getPadding(values.aMapping.ref.value, values.aDepth.ref.value, text.charCount, getMaxSize(values))
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, text.charCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -215,6 +275,7 @@ export namespace Text {
|
||||
|
||||
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
BaseGeometry.updateRenderableState(state, props)
|
||||
state.pickable = false
|
||||
state.opaque = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,38 +27,39 @@ export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
|
||||
/** Number of vertices in the texture-mesh */
|
||||
readonly vertexCount: ValueCell<number>,
|
||||
vertexCount: number,
|
||||
/** Number of groups in the texture-mesh */
|
||||
readonly groupCount: ValueCell<number>,
|
||||
groupCount: number,
|
||||
|
||||
readonly geoTextureDim: ValueCell<Vec2>,
|
||||
/** texture has vertex positions in XYZ and group id in W */
|
||||
readonly vertexGroupTexture: ValueCell<Texture>,
|
||||
readonly normalTexture: ValueCell<Texture>,
|
||||
|
||||
readonly boundingSphere: ValueCell<Sphere3D>,
|
||||
readonly boundingSphere: Sphere3D
|
||||
}
|
||||
|
||||
export namespace TextureMesh {
|
||||
export function create(vertexCount: number, groupCount: number, vertexGroupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh {
|
||||
const { width, height } = vertexGroupTexture
|
||||
const width = vertexGroupTexture.getWidth()
|
||||
const height = vertexGroupTexture.getHeight()
|
||||
if (textureMesh) {
|
||||
ValueCell.update(textureMesh.vertexCount, vertexCount)
|
||||
ValueCell.update(textureMesh.groupCount, groupCount)
|
||||
textureMesh.vertexCount = vertexCount
|
||||
textureMesh.groupCount = groupCount
|
||||
ValueCell.update(textureMesh.geoTextureDim, Vec2.set(textureMesh.geoTextureDim.ref.value, width, height))
|
||||
ValueCell.update(textureMesh.vertexGroupTexture, vertexGroupTexture)
|
||||
ValueCell.update(textureMesh.normalTexture, normalTexture)
|
||||
ValueCell.update(textureMesh.boundingSphere, boundingSphere)
|
||||
Sphere3D.copy(textureMesh.boundingSphere, boundingSphere)
|
||||
return textureMesh
|
||||
} else {
|
||||
return {
|
||||
kind: 'texture-mesh',
|
||||
vertexCount: ValueCell.create(vertexCount),
|
||||
groupCount: ValueCell.create(groupCount),
|
||||
vertexCount,
|
||||
groupCount,
|
||||
geoTextureDim: ValueCell.create(Vec2.create(width, height)),
|
||||
vertexGroupTexture: ValueCell.create(vertexGroupTexture),
|
||||
normalTexture: ValueCell.create(normalTexture),
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
boundingSphere: Sphere3D.clone(boundingSphere),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,9 +94,9 @@ export namespace TextureMesh {
|
||||
const overpaint = createEmptyOverpaint()
|
||||
const transparency = createEmptyTransparency()
|
||||
|
||||
const counts = { drawCount: textureMesh.vertexCount.ref.value, groupCount, instanceCount }
|
||||
const counts = { drawCount: textureMesh.vertexCount, groupCount, instanceCount }
|
||||
|
||||
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
|
||||
return {
|
||||
uGeoTexDim: textureMesh.geoTextureDim,
|
||||
@@ -103,9 +104,9 @@ export namespace TextureMesh {
|
||||
tNormal: textureMesh.normalTexture,
|
||||
|
||||
// aGroup is used as a vertex index here and the group id is retirieved from tPositionGroup
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount.ref.value))),
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))),
|
||||
boundingSphere: ValueCell.create(transformBoundingSphere),
|
||||
invariantBoundingSphere: textureMesh.boundingSphere,
|
||||
invariantBoundingSphere: ValueCell.create(Sphere3D.clone(textureMesh.boundingSphere)),
|
||||
|
||||
...color,
|
||||
...marker,
|
||||
@@ -128,14 +129,7 @@ export namespace TextureMesh {
|
||||
}
|
||||
|
||||
function updateValues(values: TextureMeshValues, props: PD.Values<Params>) {
|
||||
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
|
||||
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
|
||||
}
|
||||
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
|
||||
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
|
||||
}
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
|
||||
@@ -148,7 +142,7 @@ export namespace TextureMesh {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {
|
||||
const invariantBoundingSphere = textureMesh.boundingSphere.ref.value
|
||||
const invariantBoundingSphere = textureMesh.boundingSphere
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
|
||||
@@ -61,23 +61,20 @@ export function PerforatedBox() {
|
||||
let boxCage: Cage
|
||||
export function BoxCage() {
|
||||
if (!boxCage) {
|
||||
boxCage = createCage(
|
||||
[
|
||||
0.5, 0.5, -0.5, // bottom
|
||||
-0.5, 0.5, -0.5,
|
||||
-0.5, -0.5, -0.5,
|
||||
0.5, -0.5, -0.5,
|
||||
0.5, 0.5, 0.5, // top
|
||||
-0.5, 0.5, 0.5,
|
||||
-0.5, -0.5, 0.5,
|
||||
0.5, -0.5, 0.5
|
||||
],
|
||||
[
|
||||
0, 4, 1, 5, 2, 6, 3, 7, // sides
|
||||
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
|
||||
4, 5, 5, 6, 6, 7, 7, 4 // top base
|
||||
]
|
||||
)
|
||||
boxCage = createCage([
|
||||
0.5, 0.5, -0.5, // bottom
|
||||
-0.5, 0.5, -0.5,
|
||||
-0.5, -0.5, -0.5,
|
||||
0.5, -0.5, -0.5,
|
||||
0.5, 0.5, 0.5, // top
|
||||
-0.5, 0.5, 0.5,
|
||||
-0.5, -0.5, 0.5,
|
||||
0.5, -0.5, 0.5
|
||||
], [
|
||||
0, 4, 1, 5, 2, 6, 3, 7, // sides
|
||||
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
|
||||
4, 5, 5, 6, 6, 7, 7, 4 // top base
|
||||
])
|
||||
}
|
||||
return boxCage
|
||||
}
|
||||
@@ -4,6 +4,9 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3 } from '../../mol-math/linear-algebra'
|
||||
import { NumberArray } from '../../mol-util/type-helpers'
|
||||
|
||||
export interface Cage {
|
||||
readonly vertices: ArrayLike<number>
|
||||
readonly edges: ArrayLike<number>
|
||||
@@ -11,4 +14,24 @@ export interface Cage {
|
||||
|
||||
export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>): Cage {
|
||||
return { vertices, edges }
|
||||
}
|
||||
|
||||
export function cloneCage(cage: Cage): Cage {
|
||||
return {
|
||||
vertices: new Float32Array(cage.vertices),
|
||||
edges: new Uint32Array(cage.edges)
|
||||
}
|
||||
}
|
||||
|
||||
const tmpV = Vec3.zero()
|
||||
|
||||
/** Transform primitive in-place */
|
||||
export function transformCage(cage: Cage, t: Mat4) {
|
||||
const { vertices } = cage
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
|
||||
Vec3.toArray(tmpV, vertices as NumberArray, i)
|
||||
}
|
||||
return cage
|
||||
}
|
||||
69
src/mol-geo/primitive/circle.ts
Normal file
69
src/mol-geo/primitive/circle.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Primitive } from './primitive';
|
||||
|
||||
export const DefaultCircleProps = {
|
||||
radius: 1,
|
||||
segments: 36,
|
||||
thetaStart: 0,
|
||||
thetaLength: Math.PI * 2
|
||||
}
|
||||
export type CirclerProps = Partial<typeof DefaultCircleProps>
|
||||
|
||||
export function Circle(props?: CirclerProps): Primitive {
|
||||
const { radius, segments, thetaStart, thetaLength } = { ...DefaultCircleProps, ...props }
|
||||
|
||||
const isFull = thetaLength === Math.PI * 2
|
||||
const count = isFull ? segments + 1 : segments + 2
|
||||
|
||||
const vertices = new Float32Array(count * 3)
|
||||
const normals = new Float32Array(count * 3)
|
||||
const indices = new Uint32Array(segments * 3)
|
||||
|
||||
// center
|
||||
vertices[0] = 0; vertices[1] = 0; vertices[2] = 0;
|
||||
normals[0] = 0; normals[1] = 1; normals[2] = 0;
|
||||
|
||||
// vertices & normals
|
||||
for (let s = 0, i = 3; s < segments; ++s, i += 3) {
|
||||
const segment = thetaStart + s / segments * thetaLength;
|
||||
|
||||
vertices[i] = radius * Math.sin(segment)
|
||||
vertices[i + 1] = 0
|
||||
vertices[i + 2] = radius * Math.cos(segment)
|
||||
|
||||
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
|
||||
}
|
||||
|
||||
// indices
|
||||
for (let s = 1, i = 0; s < segments; ++s, i += 3) {
|
||||
indices[i] = s; indices[i + 1] = s + 1; indices[i + 2] = 0;
|
||||
}
|
||||
|
||||
if (isFull) {
|
||||
const j = (segments - 1) * 3
|
||||
indices[j] = segments
|
||||
indices[j + 1] = 1
|
||||
indices[j + 2] = 0
|
||||
} else {
|
||||
const segment = thetaStart + thetaLength;
|
||||
const i = (segments + 1) * 3
|
||||
|
||||
vertices[i] = radius * Math.sin(segment)
|
||||
vertices[i + 1] = 0
|
||||
vertices[i + 2] = radius * Math.cos(segment)
|
||||
|
||||
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
|
||||
|
||||
const j = (segments - 1) * 3
|
||||
indices[j] = segments
|
||||
indices[j + 1] = segments + 1
|
||||
indices[j + 2] = 0
|
||||
}
|
||||
|
||||
return { vertices, normals, indices }
|
||||
}
|
||||
@@ -14,46 +14,46 @@ const b = 1 / t;
|
||||
const c = 2 - t;
|
||||
|
||||
export const dodecahedronVertices: ReadonlyArray<number> = [
|
||||
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
|
||||
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
|
||||
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
|
||||
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
|
||||
-b, -b, -b, 0, -a, -c, b, -b, -b, b, b, -b, 0, a, -c,
|
||||
-b, b, -b, a, c, 0, -a, c, 0, -a, -c, 0, a, -c, 0
|
||||
];
|
||||
|
||||
/** indices of pentagonal faces, groups of five */
|
||||
export const dodecahedronFaces: ReadonlyArray<number> = [
|
||||
4, 3, 2, 1, 0,
|
||||
7, 6, 5, 0, 1,
|
||||
4, 3, 2, 1, 0,
|
||||
7, 6, 5, 0, 1,
|
||||
12, 11, 10, 9, 8,
|
||||
15, 14, 13, 8, 9,
|
||||
14, 3, 4, 16, 13,
|
||||
3, 14, 15, 17, 2,
|
||||
3, 14, 15, 17, 2,
|
||||
11, 6, 7, 18, 10,
|
||||
6, 11, 12, 19, 5,
|
||||
4, 0, 5, 19, 16,
|
||||
6, 11, 12, 19, 5,
|
||||
4, 0, 5, 19, 16,
|
||||
12, 8, 13, 16, 19,
|
||||
15, 9, 10, 18, 17,
|
||||
7, 1, 2, 17, 18
|
||||
7, 1, 2, 17, 18
|
||||
];
|
||||
|
||||
const dodecahedronIndices: ReadonlyArray<number> = [ // pentagonal faces
|
||||
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
|
||||
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
|
||||
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
|
||||
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
|
||||
12, 11, 10, 10, 9, 8, 12, 10, 8, // 12, 11, 10, 9, 8
|
||||
15, 14, 13, 13, 8, 9, 15, 13, 9, // 15, 14, 13, 8, 9
|
||||
14, 3, 4, 4, 16, 13, 14, 4, 13, // 14, 3, 4, 16, 13
|
||||
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
|
||||
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
|
||||
11, 6, 7, 7, 18, 10, 11, 7, 10, // 11, 6, 7, 18, 10
|
||||
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
|
||||
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
|
||||
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
|
||||
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
|
||||
12, 8, 13, 13, 16, 19, 12, 13, 19, // 12, 8, 13, 16, 19
|
||||
15, 9, 10, 10, 18, 17, 15, 10, 17, // 15, 9, 10, 18, 17
|
||||
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
|
||||
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
|
||||
];
|
||||
|
||||
const dodecahedronEdges: ReadonlyArray<number> = [
|
||||
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
|
||||
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
|
||||
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
|
||||
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
|
||||
10, 11, 10, 18, 11, 12, 12, 19, 13, 14, 13, 16, 14, 15, 15, 17, 16, 19, 17, 18,
|
||||
]
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ const t = (1 + Math.sqrt(5)) / 2;
|
||||
|
||||
const icosahedronVertices: ReadonlyArray<number> = [
|
||||
-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
|
||||
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
|
||||
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
|
||||
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
|
||||
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
|
||||
];
|
||||
|
||||
const icosahedronIndices: ReadonlyArray<number> = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -28,7 +28,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
appplyRadius(vertices, radius);
|
||||
|
||||
const normals = new Float32Array(vertices.length);
|
||||
computeIndexedVertexNormals(vertices, indices, normals)
|
||||
computeIndexedVertexNormals(vertices, indices, normals, vertices.length / 3, indices.length / 3)
|
||||
|
||||
return {
|
||||
vertices: new Float32Array(vertices),
|
||||
@@ -39,9 +39,9 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
// helper functions
|
||||
|
||||
function subdivide(detail: number) {
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const a = Vec3()
|
||||
const b = Vec3()
|
||||
const c = Vec3()
|
||||
|
||||
// iterate over all faces and apply a subdivison with the given detail value
|
||||
for (let i = 0; i < _indices.length; i += 3) {
|
||||
@@ -66,10 +66,10 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
for (let i = 0; i <= cols; ++i) {
|
||||
v[i] = []
|
||||
|
||||
const aj = Vec3.zero()
|
||||
const aj = Vec3()
|
||||
Vec3.lerp(aj, a, c, i / cols)
|
||||
|
||||
const bj = Vec3.zero()
|
||||
const bj = Vec3()
|
||||
Vec3.lerp(bj, b, c, i / cols)
|
||||
|
||||
const rows = cols - i
|
||||
@@ -77,7 +77,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
if (j === 0 && i === cols) {
|
||||
v[i][j] = aj
|
||||
} else {
|
||||
const abj = Vec3.zero()
|
||||
const abj = Vec3()
|
||||
Vec3.lerp(abj, aj, bj, j / rows)
|
||||
|
||||
v[i][j] = abj
|
||||
@@ -90,7 +90,6 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
for (let j = 0; j < 2 * (cols - i) - 1; ++j) {
|
||||
const k = Math.floor(j / 2)
|
||||
if (j % 2 === 0) {
|
||||
builder.add
|
||||
builder.add(v[i][k + 1], v[i + 1][k], v[i][k])
|
||||
} else {
|
||||
builder.add(v[i][k + 1], v[i + 1][k + 1], v[i + 1][k])
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra';
|
||||
import { getNormalMatrix } from '../util';
|
||||
import { NumberArray } from '../../mol-util/type-helpers';
|
||||
|
||||
export interface Primitive {
|
||||
@@ -30,6 +29,14 @@ export function createPrimitive(vertices: ArrayLike<number>, indices: ArrayLike<
|
||||
return builder.getPrimitive()
|
||||
}
|
||||
|
||||
export function copyPrimitive(primitive: Primitive): Primitive {
|
||||
return {
|
||||
vertices: new Float32Array(primitive.vertices),
|
||||
normals: new Float32Array(primitive.normals),
|
||||
indices: new Uint32Array(primitive.indices)
|
||||
}
|
||||
}
|
||||
|
||||
export interface PrimitiveBuilder {
|
||||
add(a: Vec3, b: Vec3, c: Vec3): void
|
||||
getPrimitive(): Primitive
|
||||
@@ -66,7 +73,7 @@ const tmpMat3 = Mat3.zero()
|
||||
/** Transform primitive in-place */
|
||||
export function transformPrimitive(primitive: Primitive, t: Mat4) {
|
||||
const { vertices, normals } = primitive
|
||||
const n = getNormalMatrix(tmpMat3, t)
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
|
||||
|
||||
@@ -78,10 +78,22 @@ export function PentagonalPrism() {
|
||||
|
||||
let hexagonalPrism: Primitive
|
||||
export function HexagonalPrism() {
|
||||
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, true))
|
||||
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, false))
|
||||
return hexagonalPrism
|
||||
}
|
||||
|
||||
let shiftedHexagonalPrism: Primitive
|
||||
export function ShiftedHexagonalPrism() {
|
||||
if (!shiftedHexagonalPrism) shiftedHexagonalPrism = Prism(polygon(6, true))
|
||||
return shiftedHexagonalPrism
|
||||
}
|
||||
|
||||
let heptagonalPrism: Primitive
|
||||
export function HeptagonalPrism() {
|
||||
if (!heptagonalPrism) heptagonalPrism = Prism(polygon(7, false))
|
||||
return heptagonalPrism
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
@@ -133,6 +145,6 @@ export function PentagonalPrismCage() {
|
||||
|
||||
let hexagonalPrismCage: Cage
|
||||
export function HexagonalPrismCage() {
|
||||
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, true))
|
||||
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, false))
|
||||
return hexagonalPrismCage
|
||||
}
|
||||
@@ -54,6 +54,12 @@ export function Pyramid(points: ArrayLike<number>): Primitive {
|
||||
return builder.getPrimitive()
|
||||
}
|
||||
|
||||
let triangularPyramid: Primitive
|
||||
export function TriangularPyramid() {
|
||||
if (!triangularPyramid) triangularPyramid = Pyramid(polygon(3, true))
|
||||
return triangularPyramid
|
||||
}
|
||||
|
||||
let octagonalPyramid: Primitive
|
||||
export function OctagonalPyramid() {
|
||||
if (!octagonalPyramid) octagonalPyramid = Pyramid(polygon(8, true))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,20 +8,16 @@ import { createPrimitive, Primitive } from './primitive';
|
||||
import { createCage, Cage } from './cage';
|
||||
|
||||
export const tetrahedronVertices: ReadonlyArray<number> = [
|
||||
0.7071, 0, 0, -0.3535, 0.6123, 0, -0.3535, -0.6123, 0,
|
||||
0, 0, 0.7071, 0, 0, -0.7071
|
||||
|
||||
0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5
|
||||
];
|
||||
|
||||
export const tetrahedronIndices: ReadonlyArray<number> = [
|
||||
4, 1, 0, 4, 2, 1, 4, 0, 2,
|
||||
0, 1, 3, 1, 2, 3, 2, 0, 3,
|
||||
2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
|
||||
];
|
||||
|
||||
const tetrahedronEdges: ReadonlyArray<number> = [
|
||||
0, 1, 1, 2, 2, 0,
|
||||
0, 3, 1, 3, 2, 3,
|
||||
0, 4, 1, 4, 2, 4,
|
||||
]
|
||||
|
||||
let tetrahedron: Primitive
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4, Mat3 } from '../mol-math/linear-algebra'
|
||||
import { NumberArray } from '../mol-util/type-helpers';
|
||||
import { arrayMax } from '../mol-util/array';
|
||||
|
||||
export function normalizeVec3Array<T extends NumberArray> (a: T) {
|
||||
const n = a.length
|
||||
for (let i = 0; i < n; i += 3) {
|
||||
const x = a[ i ]
|
||||
const y = a[ i + 1 ]
|
||||
const z = a[ i + 2 ]
|
||||
export function normalizeVec3Array<T extends NumberArray> (a: T, count: number) {
|
||||
for (let i = 0, il = count * 3; i < il; i += 3) {
|
||||
const x = a[i]
|
||||
const y = a[i + 1]
|
||||
const z = a[i + 2]
|
||||
const s = 1 / Math.sqrt(x * x + y * y + z * z)
|
||||
a[ i ] = x * s
|
||||
a[ i + 1 ] = y * s
|
||||
a[ i + 2 ] = z * s
|
||||
a[i] = x * s
|
||||
a[i + 1] = y * s
|
||||
a[i + 2] = z * s
|
||||
}
|
||||
}
|
||||
|
||||
export function getNormalMatrix(out: Mat3, t: Mat4) {
|
||||
Mat3.fromMat4(out, t)
|
||||
Mat3.invert(out, out)
|
||||
Mat3.transpose(out, out)
|
||||
return out
|
||||
return a
|
||||
}
|
||||
|
||||
const tmpV3 = Vec3.zero()
|
||||
@@ -45,38 +39,33 @@ export function transformDirectionArray (n: Mat3, array: NumberArray, offset: nu
|
||||
}
|
||||
}
|
||||
|
||||
export function setArrayZero(array: NumberArray) {
|
||||
const n = array.length
|
||||
for (let i = 0; i < n; ++i) array[i] = 0
|
||||
}
|
||||
|
||||
/** iterate over the entire buffer and apply the radius to each vertex */
|
||||
/** iterate over the entire array and apply the radius to each vertex */
|
||||
export function appplyRadius(vertices: NumberArray, radius: number) {
|
||||
const v = Vec3.zero()
|
||||
const n = vertices.length
|
||||
for (let i = 0; i < n; i += 3) {
|
||||
Vec3.fromArray(v, vertices, i)
|
||||
Vec3.normalize(v, v)
|
||||
Vec3.scale(v, v, radius)
|
||||
Vec3.toArray(v, vertices, i)
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
Vec3.fromArray(tmpV3, vertices, i)
|
||||
Vec3.normalize(tmpV3, tmpV3)
|
||||
Vec3.scale(tmpV3, tmpV3, radius)
|
||||
Vec3.toArray(tmpV3, vertices, i)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* indexed vertex normals weighted by triangle areas http://www.iquilezles.org/www/articles/normals/normals.htm
|
||||
* normal array must contain only zeros
|
||||
*/
|
||||
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T) {
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const cb = Vec3.zero()
|
||||
const ab = Vec3.zero()
|
||||
const a = Vec3()
|
||||
const b = Vec3()
|
||||
const c = Vec3()
|
||||
const cb = Vec3()
|
||||
const ab = Vec3()
|
||||
|
||||
for (let i = 0, il = indices.length; i < il; i += 3) {
|
||||
const ai = indices[ i ] * 3
|
||||
const bi = indices[ i + 1 ] * 3
|
||||
const ci = indices[ i + 2 ] * 3
|
||||
/**
|
||||
* indexed vertex normals weighted by triangle areas
|
||||
* http://www.iquilezles.org/www/articles/normals/normals.htm
|
||||
* - normals array must contain only zeros
|
||||
*/
|
||||
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T, vertexCount: number, triangleCount: number) {
|
||||
|
||||
for (let i = 0, il = triangleCount * 3; i < il; i += 3) {
|
||||
const ai = indices[i] * 3
|
||||
const bi = indices[i + 1] * 3
|
||||
const ci = indices[i + 2] * 3
|
||||
|
||||
Vec3.fromArray(a, vertices, ai)
|
||||
Vec3.fromArray(b, vertices, bi)
|
||||
@@ -86,34 +75,28 @@ export function computeIndexedVertexNormals<T extends NumberArray> (vertices: Nu
|
||||
Vec3.sub(ab, a, b)
|
||||
Vec3.cross(cb, cb, ab)
|
||||
|
||||
normals[ ai ] += cb[ 0 ]
|
||||
normals[ ai + 1 ] += cb[ 1 ]
|
||||
normals[ ai + 2 ] += cb[ 2 ]
|
||||
normals[ai] += cb[0]
|
||||
normals[ai + 1] += cb[1]
|
||||
normals[ai + 2] += cb[2]
|
||||
|
||||
normals[ bi ] += cb[ 0 ]
|
||||
normals[ bi + 1 ] += cb[ 1 ]
|
||||
normals[ bi + 2 ] += cb[ 2 ]
|
||||
normals[bi] += cb[0]
|
||||
normals[bi + 1] += cb[1]
|
||||
normals[bi + 2] += cb[2]
|
||||
|
||||
normals[ ci ] += cb[ 0 ]
|
||||
normals[ ci + 1 ] += cb[ 1 ]
|
||||
normals[ ci + 2 ] += cb[ 2 ]
|
||||
normals[ci] += cb[0]
|
||||
normals[ci + 1] += cb[1]
|
||||
normals[ci + 2] += cb[2]
|
||||
}
|
||||
|
||||
normalizeVec3Array(normals)
|
||||
return normals
|
||||
return normalizeVec3Array(normals, vertexCount)
|
||||
}
|
||||
|
||||
/** vertex normals for unindexed triangle soup, normal array must contain only zeros */
|
||||
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T) {
|
||||
setArrayZero(normals)
|
||||
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const cb = Vec3.zero()
|
||||
const ab = Vec3.zero()
|
||||
|
||||
for (let i = 0, il = vertices.length; i < il; i += 9) {
|
||||
/**
|
||||
* vertex normals for unindexed triangle soup
|
||||
* - normals array must contain only zeros
|
||||
*/
|
||||
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T, vertexCount: number) {
|
||||
for (let i = 0, il = vertexCount * 3; i < il; i += 9) {
|
||||
Vec3.fromArray(a, vertices, i)
|
||||
Vec3.fromArray(b, vertices, i + 3)
|
||||
Vec3.fromArray(c, vertices, i + 6)
|
||||
@@ -122,19 +105,58 @@ export function computeVertexNormals<T extends NumberArray> (vertices: NumberArr
|
||||
Vec3.sub(ab, a, b)
|
||||
Vec3.cross(cb, cb, ab)
|
||||
|
||||
normals[ i ] = cb[ 0 ]
|
||||
normals[ i + 1 ] = cb[ 1 ]
|
||||
normals[ i + 2 ] = cb[ 2 ]
|
||||
normals[i] = cb[0]
|
||||
normals[i + 1] = cb[1]
|
||||
normals[i + 2] = cb[2]
|
||||
|
||||
normals[ i + 3 ] = cb[ 0 ]
|
||||
normals[ i + 4 ] = cb[ 1 ]
|
||||
normals[ i + 5 ] = cb[ 2 ]
|
||||
normals[i + 3] = cb[0]
|
||||
normals[i + 4] = cb[1]
|
||||
normals[i + 5] = cb[2]
|
||||
|
||||
normals[ i + 6 ] = cb[ 0 ]
|
||||
normals[ i + 7 ] = cb[ 1 ]
|
||||
normals[ i + 8 ] = cb[ 2 ]
|
||||
normals[i + 6] = cb[0]
|
||||
normals[i + 7] = cb[1]
|
||||
normals[i + 8] = cb[2]
|
||||
}
|
||||
|
||||
normalizeVec3Array(normals)
|
||||
return normals
|
||||
}
|
||||
return normalizeVec3Array(normals, vertexCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps groups to data, range for group i is offsets[i] to offsets[i + 1]
|
||||
*/
|
||||
export type GroupMapping = {
|
||||
/** data indices */
|
||||
readonly indices: ArrayLike<number>
|
||||
/** range for group i is offsets[i] to offsets[i + 1] */
|
||||
readonly offsets: ArrayLike<number>
|
||||
}
|
||||
|
||||
/**
|
||||
* The `step` parameter allows to skip over repeated values in `groups`
|
||||
*/
|
||||
export function createGroupMapping(groups: ArrayLike<number>, dataCount: number, step = 1): GroupMapping {
|
||||
const maxId = arrayMax(groups)
|
||||
|
||||
const offsets = new Int32Array(maxId + 2)
|
||||
const bucketFill = new Int32Array(dataCount)
|
||||
const bucketSizes = new Int32Array(dataCount)
|
||||
|
||||
for (let i = 0, il = dataCount * step; i < il; i += step) ++bucketSizes[groups[i]]
|
||||
|
||||
let offset = 0
|
||||
for (let i = 0; i < dataCount; i++) {
|
||||
offsets[i] = offset
|
||||
offset += bucketSizes[i]
|
||||
}
|
||||
offsets[dataCount] = offset
|
||||
|
||||
const indices = new Int32Array(offset)
|
||||
for (let i = 0, il = dataCount * step; i < il; i += step) {
|
||||
const g = groups[i]
|
||||
const og = offsets[g] + bucketFill[g]
|
||||
indices[og] = i
|
||||
++bucketFill[g]
|
||||
}
|
||||
|
||||
return { indices, offsets }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 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>
|
||||
@@ -7,13 +7,13 @@
|
||||
*/
|
||||
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { ValueCell, noop } from '../../../mol-util';
|
||||
import { noop } from '../../../mol-util';
|
||||
import { Mesh } from '../../geometry/mesh/mesh';
|
||||
import { AllowedContours } from './tables';
|
||||
import { LinesBuilder } from '../../geometry/lines/lines-builder';
|
||||
import { Lines } from '../../geometry/lines/lines';
|
||||
|
||||
export interface MarchinCubesBuilder<T> {
|
||||
export interface MarchinCubesBuilder<T> {
|
||||
addVertex(x: number, y: number, z: number): number
|
||||
addNormal(x: number, y: number, z: number): void
|
||||
addGroup(group: number): void
|
||||
@@ -52,17 +52,7 @@ export function MarchinCubesMeshBuilder(vertexChunkSize: number, mesh?: Mesh): M
|
||||
const nb = ChunkedArray.compact(normals, true) as Float32Array;
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array;
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array;
|
||||
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount,
|
||||
triangleCount,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
normalsComputed: true
|
||||
}
|
||||
return Mesh.create(vb, ib, nb, gb, vertexCount, triangleCount, mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -28,11 +28,9 @@ import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-dat
|
||||
function createRenderer(gl: WebGLRenderingContext) {
|
||||
const ctx = createContext(gl)
|
||||
const camera = new Camera({
|
||||
near: 0.01,
|
||||
far: 10000,
|
||||
position: Vec3.create(0, 0, 50)
|
||||
})
|
||||
const renderer = Renderer.create(ctx, camera)
|
||||
const renderer = Renderer.create(ctx)
|
||||
return { ctx, camera, renderer }
|
||||
}
|
||||
|
||||
@@ -67,8 +65,6 @@ function createPoints() {
|
||||
...transparency,
|
||||
|
||||
uAlpha: ValueCell.create(1.0),
|
||||
uHighlightColor: ValueCell.create(Vec3.create(1.0, 0.4, 0.6)),
|
||||
uSelectColor: ValueCell.create(Vec3.create(0.2, 1.0, 0.1)),
|
||||
uInstanceCount: ValueCell.create(1),
|
||||
uGroupCount: ValueCell.create(3),
|
||||
|
||||
@@ -85,7 +81,6 @@ function createPoints() {
|
||||
dPointSizeAttenuation: ValueCell.create(true),
|
||||
dPointFilledCircle: ValueCell.create(false),
|
||||
uPointEdgeBleach: ValueCell.create(0.5),
|
||||
dUseFog: ValueCell.create(true),
|
||||
}
|
||||
const state: RenderableState = {
|
||||
visible: true,
|
||||
@@ -106,11 +101,11 @@ describe('renderer', () => {
|
||||
expect(ctx.gl.canvas.width).toBe(32)
|
||||
expect(ctx.gl.canvas.height).toBe(32)
|
||||
|
||||
expect(ctx.stats.bufferCount).toBe(0);
|
||||
expect(ctx.stats.textureCount).toBe(0);
|
||||
expect(ctx.stats.vaoCount).toBe(0);
|
||||
expect(ctx.programCache.count).toBe(0);
|
||||
expect(ctx.shaderCache.count).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(0);
|
||||
|
||||
renderer.setViewport(0, 0, 64, 48)
|
||||
expect(ctx.gl.getParameter(ctx.gl.VIEWPORT)[2]).toBe(64)
|
||||
@@ -126,25 +121,23 @@ describe('renderer', () => {
|
||||
const points = createPoints()
|
||||
|
||||
scene.add(points)
|
||||
await scene.commit().run()
|
||||
expect(ctx.stats.bufferCount).toBe(4);
|
||||
expect(ctx.stats.textureCount).toBe(5);
|
||||
expect(ctx.stats.vaoCount).toBe(5);
|
||||
expect(ctx.programCache.count).toBe(5);
|
||||
expect(ctx.shaderCache.count).toBe(10);
|
||||
scene.commit()
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(4);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
|
||||
scene.remove(points)
|
||||
await scene.commit().run()
|
||||
expect(ctx.stats.bufferCount).toBe(0);
|
||||
expect(ctx.stats.textureCount).toBe(0);
|
||||
expect(ctx.stats.vaoCount).toBe(0);
|
||||
expect(ctx.programCache.count).toBe(5);
|
||||
expect(ctx.shaderCache.count).toBe(10);
|
||||
scene.commit()
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
|
||||
ctx.programCache.dispose()
|
||||
expect(ctx.programCache.count).toBe(0);
|
||||
|
||||
ctx.shaderCache.clear()
|
||||
expect(ctx.shaderCache.count).toBe(0);
|
||||
ctx.resources.destroy()
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(0);
|
||||
})
|
||||
})
|
||||
55
src/mol-gl/commit-queue.ts
Normal file
55
src/mol-gl/commit-queue.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { LinkedList } from '../mol-data/generic'
|
||||
import { GraphicsRenderObject } from './render-object'
|
||||
|
||||
type N = LinkedList.Node<GraphicsRenderObject>
|
||||
|
||||
export class CommitQueue {
|
||||
private removeList = LinkedList<GraphicsRenderObject>();
|
||||
private removeMap = new Map<GraphicsRenderObject, N>();
|
||||
private addList = LinkedList<GraphicsRenderObject>();
|
||||
private addMap = new Map<GraphicsRenderObject, N>();
|
||||
|
||||
get isEmpty() {
|
||||
return this.removeList.count === 0 && this.addList.count === 0;
|
||||
}
|
||||
|
||||
add(o: GraphicsRenderObject) {
|
||||
if (this.removeMap.has(o)) {
|
||||
const a = this.removeMap.get(o)!;
|
||||
this.removeMap.delete(o);
|
||||
this.removeList.remove(a);
|
||||
}
|
||||
if (this.addMap.has(o)) return;
|
||||
const b = this.addList.addLast(o);
|
||||
this.addMap.set(o, b);
|
||||
}
|
||||
|
||||
remove(o: GraphicsRenderObject) {
|
||||
if (this.addMap.has(o)) {
|
||||
const a = this.addMap.get(o)!;
|
||||
this.addMap.delete(o);
|
||||
this.addList.remove(a);
|
||||
}
|
||||
if (this.removeMap.has(o)) return;
|
||||
const b = this.removeList.addLast(o);
|
||||
this.removeMap.set(o, b);
|
||||
}
|
||||
|
||||
tryGetRemove() {
|
||||
const o = this.removeList.removeFirst();
|
||||
if (o) this.removeMap.delete(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
tryGetAdd() {
|
||||
const o = this.addList.removeFirst();
|
||||
if (o) this.addMap.delete(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,13 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
|
||||
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { QuadSchema, QuadValues } from '../util';
|
||||
import { Vec2 } from '../../../mol-math/linear-algebra';
|
||||
import { getHistopyramidSum } from './sum';
|
||||
import { Framebuffer, createFramebuffer } from '../../../mol-gl/webgl/framebuffer';
|
||||
import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
|
||||
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'
|
||||
@@ -55,8 +55,8 @@ function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) {
|
||||
let textureFramebuffer = LevelTexturesFramebuffers[level]
|
||||
const size = Math.pow(2, level)
|
||||
if (textureFramebuffer === undefined) {
|
||||
const texture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const framebuffer = createFramebuffer(ctx.gl, ctx.stats)
|
||||
const texture = ctx.resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
const framebuffer = ctx.resources.framebuffer()
|
||||
texture.attachFramebuffer(framebuffer, 0)
|
||||
textureFramebuffer = { texture, framebuffer }
|
||||
textureFramebuffer.texture.define(size, size)
|
||||
@@ -85,22 +85,22 @@ export interface HistogramPyramid {
|
||||
}
|
||||
|
||||
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2): HistogramPyramid {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
|
||||
// printTexture(ctx, inputTexture, 2)
|
||||
if (inputTexture.width !== inputTexture.height || !isPowerOfTwo(inputTexture.width)) {
|
||||
if (inputTexture.getWidth() !== inputTexture.getHeight() || !isPowerOfTwo(inputTexture.getWidth())) {
|
||||
throw new Error('inputTexture must be of square power-of-two size')
|
||||
}
|
||||
|
||||
// This part set the levels
|
||||
const levels = Math.ceil(Math.log(inputTexture.width) / Math.log(2))
|
||||
const levels = Math.ceil(Math.log(inputTexture.getWidth()) / Math.log(2))
|
||||
const maxSize = Math.pow(2, levels)
|
||||
// console.log('levels', levels, 'maxSize', maxSize)
|
||||
|
||||
const pyramidTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const pyramidTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
pyramidTexture.define(maxSize, maxSize)
|
||||
|
||||
const framebuffer = framebufferCache.get('reduction').value
|
||||
const framebuffer = resources.framebuffer()
|
||||
pyramidTexture.attachFramebuffer(framebuffer, 0)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user