mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
826 Commits
v0.3.2
...
v0.6.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f2085d8b4 | ||
|
|
b5a48ad201 | ||
|
|
14b900791f | ||
|
|
de80799e68 | ||
|
|
53eca387fc | ||
|
|
fc3005f271 | ||
|
|
c95fdff00c | ||
|
|
3cd9042c72 | ||
|
|
4d3914426e | ||
|
|
5c77eec184 | ||
|
|
a931ed7c01 | ||
|
|
94cd2b618c | ||
|
|
9c97fc258d | ||
|
|
0ac1cfe555 | ||
|
|
3942f1bc33 | ||
|
|
a66e38a901 | ||
|
|
f65f4f4aeb | ||
|
|
f28b13bf87 | ||
|
|
8f211a0785 | ||
|
|
ba1dfb2851 | ||
|
|
964d56752e | ||
|
|
cb6a66eba5 | ||
|
|
94adf7259d | ||
|
|
3c831b549a | ||
|
|
7ccf36a0fa | ||
|
|
b0ee640c12 | ||
|
|
cd6d41a035 | ||
|
|
9c8f1f3e11 | ||
|
|
7c50a5a456 | ||
|
|
f3b6703fd4 | ||
|
|
60f2716b5a | ||
|
|
d7007ef99d | ||
|
|
4aca41cdbb | ||
|
|
7e31fac99a | ||
|
|
3bcf1cb6b5 | ||
|
|
a29cc74304 | ||
|
|
95c43d0864 | ||
|
|
369b958335 | ||
|
|
9a17da8f65 | ||
|
|
0c4ba20d6e | ||
|
|
22936ff6c9 | ||
|
|
3369ad0bdc | ||
|
|
20853ef60b | ||
|
|
da0c66bd8e | ||
|
|
44664917ff | ||
|
|
ce26e002c7 | ||
|
|
345b9f546c | ||
|
|
f4ed5e301d | ||
|
|
4818f851b3 | ||
|
|
6f2c47c0ca | ||
|
|
5e6bc0f0df | ||
|
|
083daa0b76 | ||
|
|
139a10ba8c | ||
|
|
0b512487f5 | ||
|
|
a85adfdf1f | ||
|
|
0171b785af | ||
|
|
4c1c484af9 | ||
|
|
db7585b6b6 | ||
|
|
d263f6d929 | ||
|
|
034c28e487 | ||
|
|
9a88f57ce6 | ||
|
|
28415646a2 | ||
|
|
b03295ef81 | ||
|
|
7d3e849ff3 | ||
|
|
926f20a6a4 | ||
|
|
c12d577ce6 | ||
|
|
aa8d3a9841 | ||
|
|
f11e03eb85 | ||
|
|
ab996b5189 | ||
|
|
4c91e730a2 | ||
|
|
5ca5f44c61 | ||
|
|
f8a89b1c0d | ||
|
|
99dacef747 | ||
|
|
24fc37105e | ||
|
|
cbd493d834 | ||
|
|
53dbd1aedf | ||
|
|
6ab1af54aa | ||
|
|
615d9758bb | ||
|
|
341f9f8c91 | ||
|
|
ba7d4a5215 | ||
|
|
428187ad81 | ||
|
|
fe96172fcd | ||
|
|
8b13be698c | ||
|
|
5edc924e4f | ||
|
|
78c50a4339 | ||
|
|
607284585c | ||
|
|
f7af352f03 | ||
|
|
008ec2c88c | ||
|
|
c91074ad91 | ||
|
|
51fbb619e5 | ||
|
|
062c4b335b | ||
|
|
d4107e273d | ||
|
|
4dad67fc2e | ||
|
|
64b0713742 | ||
|
|
22c45e788e | ||
|
|
75de544669 | ||
|
|
768ec10809 | ||
|
|
4d5d9be399 | ||
|
|
7f4afc111e | ||
|
|
0365f883dd | ||
|
|
6f98549f31 | ||
|
|
77920818d9 | ||
|
|
0682d54ee2 | ||
|
|
fbcc3b2fa2 | ||
|
|
c3f47b2ecb | ||
|
|
8b9f59ac5a | ||
|
|
362dcabe5c | ||
|
|
6e205661bd | ||
|
|
c1a8627702 | ||
|
|
2c5253943c | ||
|
|
03668216fa | ||
|
|
37ef234803 | ||
|
|
d25aa97fca | ||
|
|
17dc973305 | ||
|
|
eed4e4a134 | ||
|
|
5fd28c6cad | ||
|
|
0f69ea197d | ||
|
|
e1a214e1a8 | ||
|
|
e7e14750cb | ||
|
|
cb83013967 | ||
|
|
0a84e1bb7b | ||
|
|
aee7f4988a | ||
|
|
e762c402fa | ||
|
|
4375cae70d | ||
|
|
90268a9041 | ||
|
|
a3ebc4df45 | ||
|
|
b2743c76e0 | ||
|
|
969a70d571 | ||
|
|
e6a538dbd7 | ||
|
|
9cffce55c9 | ||
|
|
7bc91d7e99 | ||
|
|
943f5feb59 | ||
|
|
dc72dbbc97 | ||
|
|
1513a1e2d2 | ||
|
|
82989cae57 | ||
|
|
74f8a5053e | ||
|
|
79f7ea209a | ||
|
|
ba65e35c51 | ||
|
|
5bc4e3ce3b | ||
|
|
1c7ac60c11 | ||
|
|
00cd54b69c | ||
|
|
90e3a3f0c7 | ||
|
|
5eef3fc42a | ||
|
|
83a8474731 | ||
|
|
a5e13c5152 | ||
|
|
b19f53d380 | ||
|
|
abadef1d2e | ||
|
|
a3d976c5b8 | ||
|
|
414b20f06d | ||
|
|
d03a442b6c | ||
|
|
e5acce03e5 | ||
|
|
fe714d4515 | ||
|
|
7959344bca | ||
|
|
b9582f171d | ||
|
|
410123d933 | ||
|
|
ae484443d8 | ||
|
|
9d97e56f03 | ||
|
|
01269ec1ef | ||
|
|
a204264bcc | ||
|
|
eb0a048926 | ||
|
|
ba1509b37a | ||
|
|
ad379e7a32 | ||
|
|
9d45beea3b | ||
|
|
b2d134aeb4 | ||
|
|
d500b8ea19 | ||
|
|
8b3df4b373 | ||
|
|
f01fc7c8ba | ||
|
|
0e6da0bffa | ||
|
|
eab8b1c2bf | ||
|
|
8b42a0fede | ||
|
|
4bbe078230 | ||
|
|
aabb931d27 | ||
|
|
b1c1b21975 | ||
|
|
7b5dc3ef96 | ||
|
|
1f831f90e0 | ||
|
|
ef2c1b51e9 | ||
|
|
ace73c041b | ||
|
|
3c70fe5303 | ||
|
|
137e23c025 | ||
|
|
969a19d515 | ||
|
|
7536abe96c | ||
|
|
8e20e163f9 | ||
|
|
adbe8e1f67 | ||
|
|
5db134f34f | ||
|
|
80cf7c1dd2 | ||
|
|
9e5cc184ed | ||
|
|
a5185b456c | ||
|
|
0615198ad2 | ||
|
|
6cd422f5b3 | ||
|
|
4416178d6f | ||
|
|
b8cef99dc1 | ||
|
|
4dcb68af33 | ||
|
|
b4c70ab14a | ||
|
|
8436e17af9 | ||
|
|
bda04cbdc7 | ||
|
|
7287947a55 | ||
|
|
fb5eb1e19c | ||
|
|
d735dcdc3e | ||
|
|
83d8a61400 | ||
|
|
b72f57c040 | ||
|
|
8a7ef1c704 | ||
|
|
8731bc13d1 | ||
|
|
d103cda0c8 | ||
|
|
ca4e864e46 | ||
|
|
0e25950d51 | ||
|
|
2c29a90b5e | ||
|
|
2900836208 | ||
|
|
928a1df525 | ||
|
|
16e0dff551 | ||
|
|
7e443d5c9b | ||
|
|
172ae17966 | ||
|
|
8cbf7b5d0a | ||
|
|
2c40b85880 | ||
|
|
ad388f23ae | ||
|
|
27b559a2d8 | ||
|
|
c6d19e14c5 | ||
|
|
b807dca2d8 | ||
|
|
2cae6e3f59 | ||
|
|
69c73f3dcd | ||
|
|
4456ab2cd5 | ||
|
|
bdda18de23 | ||
|
|
1b2c2f3d41 | ||
|
|
fd19d29ef6 | ||
|
|
1f0c0fd756 | ||
|
|
83698cc52d | ||
|
|
70b94deb20 | ||
|
|
2da3df6e4d | ||
|
|
509e633a69 | ||
|
|
17fac2b82a | ||
|
|
c543c4e10a | ||
|
|
10073800dc | ||
|
|
04c38250b4 | ||
|
|
5ccb329af1 | ||
|
|
3cecb53bc5 | ||
|
|
0daf431d68 | ||
|
|
3e0c4242ad | ||
|
|
17b775c377 | ||
|
|
4525b98288 | ||
|
|
755699d479 | ||
|
|
a84309b800 | ||
|
|
16863535b8 | ||
|
|
dd9773d72e | ||
|
|
ec45f6c0ee | ||
|
|
59235630bb | ||
|
|
11ed0ca89b | ||
|
|
188ea6e8e2 | ||
|
|
293b464d9f | ||
|
|
0e91aa521e | ||
|
|
5272593cc3 | ||
|
|
c5f336b0e4 | ||
|
|
b1a6fa3ffc | ||
|
|
fe47134934 | ||
|
|
131cc606f0 | ||
|
|
3681f01fad | ||
|
|
e966c112ab | ||
|
|
d7b232b00b | ||
|
|
7986509ad3 | ||
|
|
73dcf970f3 | ||
|
|
9377aa2d05 | ||
|
|
686fa5a5ed | ||
|
|
c946ae6eab | ||
|
|
9b2f1d9415 | ||
|
|
f6c28aa8e2 | ||
|
|
64c72aa6f5 | ||
|
|
0a22917773 | ||
|
|
3c4888e52b | ||
|
|
9d31f1ba07 | ||
|
|
d722d17a02 | ||
|
|
522d929f5a | ||
|
|
42037074cb | ||
|
|
1e3f17efdf | ||
|
|
7389e0075d | ||
|
|
a080114690 | ||
|
|
1293848d9b | ||
|
|
16b2d9e873 | ||
|
|
7a64334261 | ||
|
|
009b20c95a | ||
|
|
296bea1b88 | ||
|
|
e0775607cc | ||
|
|
a1fb4b8bf3 | ||
|
|
29ae78c193 | ||
|
|
46cfb42cce | ||
|
|
94ef9f4dbe | ||
|
|
b864e992ef | ||
|
|
57ed9a4226 | ||
|
|
b2c93cbeda | ||
|
|
60540dadee | ||
|
|
c0236650f0 | ||
|
|
fb3017cfff | ||
|
|
2c327cfdf6 | ||
|
|
307f2efc97 | ||
|
|
929c91a48c | ||
|
|
bfd9595c1c | ||
|
|
c091f9a8ae | ||
|
|
d5b71b302b | ||
|
|
67103232be | ||
|
|
f471fc3d2b | ||
|
|
2a0783d005 | ||
|
|
7234ad261b | ||
|
|
001d1fb8d3 | ||
|
|
35a56bf37c | ||
|
|
9715ff061e | ||
|
|
9866db9ced | ||
|
|
fc2288a583 | ||
|
|
11d1bbe222 | ||
|
|
bc07256d8e | ||
|
|
3826bdb0c3 | ||
|
|
9609dddd47 | ||
|
|
64c51f0d94 | ||
|
|
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 | ||
|
|
7a0b4c4d23 | ||
|
|
f45edbc4f0 | ||
|
|
65d3355b18 | ||
|
|
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 |
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"
|
||||
}
|
||||
}
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -4,8 +4,8 @@
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
|
||||
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/*"],
|
||||
}
|
||||
}
|
||||
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@@ -9,6 +9,20 @@
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build-tsc",
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
10
README.md
10
README.md
@@ -31,12 +31,15 @@ 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-state` State transformations, builders, and managers.
|
||||
- `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).
|
||||
|
||||
@@ -59,9 +62,13 @@ This project builds on experience from previous solutions:
|
||||
### Build automatically on file save:
|
||||
npm run watch
|
||||
|
||||
If working on just the viewer, ``npm run watch-viewer`` will provide shorter compile times.
|
||||
|
||||
### 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 +94,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**
|
||||
|
||||
@@ -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.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)
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -6,133 +6,64 @@ Model Server is a tool for preprocessing and querying macromolecular structure d
|
||||
Installing and Running
|
||||
=====================
|
||||
|
||||
Getting the code (use node 8+):
|
||||
Requires nodejs 8+.
|
||||
|
||||
## From GitHub
|
||||
|
||||
```
|
||||
git clone https://github.com/molstar/molstar
|
||||
npm install
|
||||
```
|
||||
|
||||
Customize configuration at ``src/server/model/config.ts`` to point to your data and which custom properties to include (see the [Custom Properties](#custom-properties) section). Alternatively, the config can be edited in the compiled version in ``build/node_modules/servers/model/config.js``.
|
||||
|
||||
Afterwards, build the project:
|
||||
Afterwards, build the project source:
|
||||
|
||||
```
|
||||
npm run build
|
||||
npm run build-tsc
|
||||
```
|
||||
|
||||
(or run watch mode for automatic rebuilds: ``npm run watch``)
|
||||
and run the server by
|
||||
|
||||
Running the server locally for testing:
|
||||
```
|
||||
npm run model-server
|
||||
```
|
||||
or
|
||||
```
|
||||
node build/node_modules/servers/model/server
|
||||
node lib/servers/model/server/server
|
||||
```
|
||||
|
||||
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
|
||||
## From NPM
|
||||
|
||||
```
|
||||
npm install --production molstar
|
||||
./model-server
|
||||
```
|
||||
|
||||
(or ``node node_modules\.bin\model-server`` in Windows).
|
||||
|
||||
The NPM package contains all the tools mentioned here as "binaries":
|
||||
|
||||
- ``model-server``
|
||||
- ``model-server-query``
|
||||
- ``model-server-preprocess``
|
||||
|
||||
|
||||
## Memory issues
|
||||
### Production use
|
||||
|
||||
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
|
||||
|
||||
|
||||
### Memory issues
|
||||
|
||||
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
|
||||
|
||||
Preprocessor
|
||||
============
|
||||
## Preprocessor
|
||||
|
||||
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. See the [Custom Properties](#custom-properties) section for providing custom properties.
|
||||
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
|
||||
|
||||
## Usage
|
||||
|
||||
The app works in two modes: single files and folders.
|
||||
## Local Mode
|
||||
|
||||
Single files:
|
||||
|
||||
```
|
||||
node build\node_modules\servers\model\preprocess -i input.cif [-oc output.cif] [-ob output.bcif] [--cfg config.json]
|
||||
```
|
||||
|
||||
Folder:
|
||||
```
|
||||
node build\node_modules\servers\model\preprocess -fin input_folder [-foc output_cif_folder] [-fob output_bcif_folder] [--cfg config.json]
|
||||
```
|
||||
|
||||
## Config
|
||||
|
||||
The config speficies the maximum number of processes to use (in case of folder processing) and defines sources and parameters for custom properties.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"numProcesses": 4,
|
||||
"customProperties": {
|
||||
"sources": [
|
||||
"./properties/pdbe"
|
||||
],
|
||||
"params": {
|
||||
"PDBe": {
|
||||
"UseFileSource": false,
|
||||
"API": {
|
||||
"residuewise_outlier_summary": "https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry",
|
||||
"preferred_assembly": "https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary",
|
||||
"struct_ref_domain": "https://www.ebi.ac.uk/pdbe/api/mappings/sequence_domains"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
The server can be run in local/file based mode using ``node lib/servers/model/query`` (``model-server-query`` binary from the NPM package).
|
||||
|
||||
Custom Properties
|
||||
=================
|
||||
|
||||
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.
|
||||
This feature is still in development.
|
||||
|
||||
Local Mode
|
||||
==========
|
||||
|
||||
The server can be run in local/file based mode:
|
||||
|
||||
```
|
||||
node build/node_modules/servers/model/server jobs.json
|
||||
```
|
||||
|
||||
where ``jobs.json`` is an array of
|
||||
|
||||
```ts
|
||||
type LocalInput = {
|
||||
input: string,
|
||||
output: string,
|
||||
query: QueryName,
|
||||
modelNums?: number[],
|
||||
params?: any,
|
||||
binary?: boolean
|
||||
}[]
|
||||
```
|
||||
|
||||
For example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"input": "c:/test/quick/1tqn.cif",
|
||||
"output": "c:/test/quick/localapi/1tqn_full.cif",
|
||||
"query": "full"
|
||||
},
|
||||
{
|
||||
"input": "c:/test/quick/1tqn.cif",
|
||||
"output": "c:/test/quick/localapi/1tqn_full.bcif",
|
||||
"query": "full",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"input": "c:/test/quick/1cbs_updated.cif",
|
||||
"output": "c:/test/quick/localapi/1cbs_ligint.cif",
|
||||
"query": "residueInteraction",
|
||||
"params": {
|
||||
"atom_site": { "label_comp_id": "REA" }
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.
|
||||
@@ -7,107 +7,64 @@ It uses the text based CIF and BinaryCIF formats to deliver the data to the clie
|
||||
|
||||
For quick info about the benefits of using the server, check out the [examples](examples.md).
|
||||
|
||||
Installing the Server
|
||||
Installing and Running
|
||||
=====================
|
||||
|
||||
- Install [Node.js](https://nodejs.org/en/) (tested on Node 6.* and 7.*; x64 version is strongly preferred).
|
||||
- Get the code.
|
||||
- Prepare the data.
|
||||
- Run the server.
|
||||
Requires nodejs 8+.
|
||||
|
||||
Preparing the Data
|
||||
------------------
|
||||
## From GitHub
|
||||
|
||||
```
|
||||
git clone https://github.com/molstar/molstar
|
||||
npm install
|
||||
```
|
||||
|
||||
Afterwards, build the project source:
|
||||
|
||||
```
|
||||
npm run build-tsc
|
||||
```
|
||||
|
||||
and run the server by
|
||||
|
||||
```
|
||||
node lib/servers/volume/server
|
||||
```
|
||||
|
||||
## From NPM
|
||||
|
||||
```
|
||||
npm install --production molstar
|
||||
./volume-server
|
||||
```
|
||||
|
||||
(or ``node node_modules\.bin\volume-server`` in Windows).
|
||||
|
||||
The NPM package contains all the tools mentioned here as "binaries":
|
||||
|
||||
- ``volume-server``
|
||||
- ``volume-server-pack``
|
||||
- ``volume-server-query``
|
||||
|
||||
|
||||
### Production use
|
||||
|
||||
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
|
||||
|
||||
|
||||
### Memory issues
|
||||
|
||||
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
|
||||
|
||||
|
||||
## Preparing the Data
|
||||
|
||||
For the server to work, CCP4/MAP (models 0, 1, 2 are supported) input data need to be converted into a custom block format.
|
||||
To achieve this, use the ``pack`` application.
|
||||
To achieve this, use the ``pack`` application (``node lib/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
|
||||
|
||||
- To prepare data from x-ray based methods, use:
|
||||
## Local Mode
|
||||
|
||||
```
|
||||
node pack -xray main.ccp4 diff.ccp4 out.mdb
|
||||
```
|
||||
|
||||
- For EM data, use:
|
||||
|
||||
```
|
||||
node pack -em em.map out.mdb
|
||||
```
|
||||
|
||||
Running the Server
|
||||
------------------
|
||||
|
||||
- Install production dependencies:
|
||||
|
||||
```
|
||||
npm install --only=production
|
||||
```
|
||||
|
||||
- Update ``server-config.js`` to link to your data and optionally tweak the other parameters.
|
||||
|
||||
- Run it:
|
||||
|
||||
```
|
||||
node server
|
||||
```
|
||||
|
||||
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
|
||||
|
||||
### Local Mode
|
||||
|
||||
The program ``local`` in the build folder can be used to query the data without running a http server.
|
||||
|
||||
- ``node local`` prints the program usage.
|
||||
- ``node local jobs.json`` takes a list of jobs to execute in JSON format. A job entry is defined by this interface:
|
||||
|
||||
```TypeScript
|
||||
interface JobEntry {
|
||||
source: {
|
||||
filename: string,
|
||||
name: string,
|
||||
id: string
|
||||
},
|
||||
query: {
|
||||
kind: 'box' | 'cell',
|
||||
space?: 'fractional' | 'cartesian',
|
||||
bottomLeft?: number[],
|
||||
topRight?: number[],
|
||||
}
|
||||
params: {
|
||||
/** Determines the detail level as specified in server-config */
|
||||
detail?: number,
|
||||
/**
|
||||
* Determines the sampling level:
|
||||
* 1: Original data
|
||||
* 2: Downsampled by factor 1/2
|
||||
* ...
|
||||
* N: downsampled 1/2^(N-1)
|
||||
*/
|
||||
forcedSamplingLevel?: number,
|
||||
asBinary: boolean,
|
||||
},
|
||||
outputFolder: string
|
||||
}
|
||||
```
|
||||
|
||||
Example ``jobs.json`` file content:
|
||||
|
||||
```TypeScript
|
||||
[{
|
||||
source: {
|
||||
filename: `g:/test/mdb/emd-8116.mdb`,
|
||||
name: 'em',
|
||||
id: '8116',
|
||||
},
|
||||
query: {
|
||||
kind: 'cell'
|
||||
},
|
||||
params: {
|
||||
detail: 4,
|
||||
asBinary: true
|
||||
},
|
||||
outputFolder: 'g:/test/local-test'
|
||||
}]
|
||||
```
|
||||
The program ``lib/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
|
||||
|
||||
## Navigating the Source Code
|
||||
|
||||
@@ -122,8 +79,8 @@ The source code is split into 2 mains parts: ``pack`` and ``server``:
|
||||
Consuming the Data
|
||||
==================
|
||||
|
||||
The data can be consumed in any (modern) browser using the [CIFTools.js library](https://github.com/dsehnal/CIFTools.js) (or any other piece of code that can read text or binary CIF).
|
||||
The data can be consumed in any (modern) browser using the [ciftools library](https://github.com/molstar/ciftools) (or any other piece of code that can read text or binary CIF).
|
||||
|
||||
The [Data Format](DataFormat.md) document gives a detailed description of the server response format.
|
||||
|
||||
As a reference/example of the server usage, please see the implementation in [LiteMol](https://github.com/dsehnal/LiteMol) ([CIF.ts + Data.ts](https://github.com/dsehnal/LiteMol/tree/master/src/lib/Core/Formats/Density), [UI](https://github.com/dsehnal/LiteMol/tree/master/src/Viewer/Extensions/DensityStreaming)) or in Mol*.
|
||||
As a reference/example of the server usage is available in Mol* ``mol-plugin`` module.
|
||||
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
|
||||
|
||||
|
||||
|
||||
16438
package-lock.json
generated
16438
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
110
package.json
110
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.3.2",
|
||||
"version": "0.6.0-dev.4",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -11,20 +11,23 @@
|
||||
"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-tsc": "tsc --incremental",
|
||||
"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": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
"watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
|
||||
"watch-tsc": "tsc --watch --incremental",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --display minimal",
|
||||
"watch-webpack": "webpack -w --mode development --display errors-only --info-verbosity verbose",
|
||||
"watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js",
|
||||
"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",
|
||||
"volume-server-test": "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"
|
||||
@@ -32,6 +35,14 @@
|
||||
"files": [
|
||||
"lib/"
|
||||
],
|
||||
"bin": {
|
||||
"model-server": "lib/servers/model/server.js",
|
||||
"model-server-query": "lib/servers/model/local.js",
|
||||
"model-server-preprocess": "lib/servers/model/preprocess.js",
|
||||
"volume-server": "lib/servers/volume/server.js",
|
||||
"volume-server-query": "lib/servers/volume/query.js",
|
||||
"volume-server-pack": "lib/servers/volume/pack.js"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignoreRoot": [
|
||||
"./node_modules",
|
||||
@@ -63,56 +74,67 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^1.13.1",
|
||||
"@graphql-codegen/cli": "^1.13.1",
|
||||
"@graphql-codegen/time": "^1.13.1",
|
||||
"@graphql-codegen/typescript": "^1.13.1",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.13.1",
|
||||
"@graphql-codegen/typescript-operations": "^1.13.1",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^2.24.0",
|
||||
"@typescript-eslint/parser": "^2.24.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^5.0.0",
|
||||
"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": "^6.0.0",
|
||||
"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": "^8.0.0",
|
||||
"simple-git": "^1.126.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"ts-jest": "^24.1.0",
|
||||
"tslint": "^5.20.0",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.0",
|
||||
"webpack-cli": "^3.3.9"
|
||||
"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.132.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"ts-jest": "^25.2.1",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
},
|
||||
"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.12",
|
||||
"@types/node-fetch": "^2.5.2",
|
||||
"@types/react": "^16.9.5",
|
||||
"@types/react-dom": "^16.9.1",
|
||||
"@types/swagger-ui-dist": "3.0.3",
|
||||
"@types/webgl2": "0.0.5",
|
||||
"@types/compression": "1.7.0",
|
||||
"@types/express": "^4.17.3",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/node": "^13.9.2",
|
||||
"@types/node-fetch": "^2.5.5",
|
||||
"@types/react": "^16.9.23",
|
||||
"@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.5.8",
|
||||
"graphql": "^14.6.0",
|
||||
"immer": "^6.0.2",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.10.2",
|
||||
"react-dom": "^16.10.2",
|
||||
"rxjs": "^6.5.3",
|
||||
"swagger-ui-dist": "^3.23.11",
|
||||
"util.promisify": "^1.0.0",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"rxjs": "^6.5.4",
|
||||
"swagger-ui-dist": "^3.25.0",
|
||||
"tslib": "^1.11.1",
|
||||
"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 {
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin/state/transforms';
|
||||
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateBuilder } from '../../mol-state';
|
||||
import Expression from '../../mol-script/language/expression';
|
||||
import { BuiltInColorThemeName } from '../../mol-theme/color';
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
|
||||
export namespace StateHelper {
|
||||
@@ -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) {
|
||||
@@ -67,27 +67,30 @@ export namespace StateHelper {
|
||||
}
|
||||
|
||||
export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
|
||||
return b.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: id || 'deposited' }, { tags: 'asm' })
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: id || 'deposited' }
|
||||
}
|
||||
}
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, props, { tags: 'asm' })
|
||||
}
|
||||
|
||||
export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'), { tags: 'seq-visual' });
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'), { tags: 'het-visual' });
|
||||
// visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }), { tags: 'water-visual' });
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, coloring?: BuiltInColorThemeName) {
|
||||
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
|
||||
visualRoot
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, coloring), { tags: 'het-visual' });
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,10 @@
|
||||
|
||||
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());
|
||||
|
||||
@@ -7,19 +7,21 @@
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { StateTransforms } from '../../mol-plugin/state/transforms';
|
||||
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
|
||||
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';
|
||||
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
|
||||
import { CustomToastMessage } from './controls';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { Script } from '../../mol-script/script';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -39,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.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
|
||||
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
@@ -56,25 +61,31 @@ class BasicWrapper {
|
||||
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
|
||||
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
|
||||
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
}
|
||||
}
|
||||
return parsed
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [StripedResidues.Descriptor.name] }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { 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' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 }), { ref: 'water-visual' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill'), { ref: 'ihm-visual' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
@@ -82,7 +93,7 @@ class BasicWrapper {
|
||||
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.dataState;
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
|
||||
loadType = 'full';
|
||||
@@ -92,29 +103,38 @@ class BasicWrapper {
|
||||
|
||||
let tree: StateBuilder.Root;
|
||||
if (loadType === 'full') {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
|
||||
tree = state.build();
|
||||
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
|
||||
} else {
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
}
|
||||
}
|
||||
|
||||
tree = state.build();
|
||||
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
|
||||
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
const renderer = this.plugin.canvas3d.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
|
||||
const renderer = this.plugin.canvas3d!.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings(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 } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
animate = {
|
||||
@@ -130,52 +150,68 @@ class BasicWrapper {
|
||||
|
||||
coloring = {
|
||||
applyStripes: async () => {
|
||||
const state = this.plugin.state.dataState;
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
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.representation.structure.themes.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
for (const v of visuals) {
|
||||
tree.to(v).update(old => ({ ...old, colorTheme }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
}
|
||||
}
|
||||
|
||||
interactivity = {
|
||||
highlightOn: () => {
|
||||
const seq_id = 7;
|
||||
const data = (this.plugin.state.data.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.managers.interactivity.lociHighlights.highlightOnly({ loci });
|
||||
},
|
||||
clearHighlight: () => {
|
||||
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
|
||||
}
|
||||
}
|
||||
|
||||
tests = {
|
||||
staticSuperposition: async () => {
|
||||
const state = this.plugin.state.dataState;
|
||||
const state = this.plugin.state.data;
|
||||
const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: StateTransform.RootRef });
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
},
|
||||
dynamicSuperposition: async () => {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state: this.plugin.state.data, ref: StateTransform.RootRef });
|
||||
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
|
||||
},
|
||||
toggleValidationTooltip: async () => {
|
||||
const state = this.plugin.state.behaviorState;
|
||||
const state = this.plugin.state.behaviors;
|
||||
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
},
|
||||
showToasts: () => {
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Toast 1',
|
||||
message: 'This is an example text, timeout 3s',
|
||||
key: 'toast-1',
|
||||
timeoutMs: 3000
|
||||
});
|
||||
PluginCommands.Toast.Show.dispatch(this.plugin, {
|
||||
PluginCommands.Toast.Show(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' });
|
||||
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
|
||||
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { StateHelper } from './helpers';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateSelection, StateBuilder } from '../../mol-state';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
import { StructureSelection, QueryContext } from '../../mol-model/structure';
|
||||
@@ -32,7 +32,7 @@ export type SuperpositionTestInput = {
|
||||
// }
|
||||
|
||||
export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) {
|
||||
const b = ctx.state.dataState.build().toRoot();
|
||||
const b = ctx.state.data.build().toRoot();
|
||||
for (const s of src) {
|
||||
StateHelper.visual(ctx,
|
||||
StateHelper.transform(
|
||||
@@ -50,20 +50,20 @@ 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) {
|
||||
const state = ctx.state.dataState;
|
||||
const state = ctx.state.data;
|
||||
|
||||
const structures = state.build().toRoot();
|
||||
for (const s of src) {
|
||||
@@ -71,7 +71,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
|
||||
StateHelper.getModel(StateHelper.download(structures, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`), 'cif'));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(ctx, { state, tree: structures });
|
||||
await PluginCommands.State.Update(ctx, { state, tree: structures });
|
||||
|
||||
const pivot = MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
@@ -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();
|
||||
@@ -99,7 +99,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
|
||||
pivot, rest);
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(ctx, { state, tree: visuals });
|
||||
await PluginCommands.State.Update(ctx, { state, tree: visuals });
|
||||
}
|
||||
|
||||
function siteVisual(ctx: PluginContext, b: StateBuilder.To<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
|
||||
|
||||
@@ -144,9 +144,9 @@ async function createBonds() {
|
||||
const comp_id: string[] = []
|
||||
const atom_id_1: string[] = []
|
||||
const atom_id_2: string[] = []
|
||||
const value_order: string[] = []
|
||||
const pdbx_aromatic_flag: string[] = []
|
||||
const pdbx_stereo_config: string[] = []
|
||||
const value_order: typeof mmCIF_chemCompBond_schema['value_order']['T'][] = []
|
||||
const pdbx_aromatic_flag: typeof mmCIF_chemCompBond_schema['pdbx_aromatic_flag']['T'][] = []
|
||||
const pdbx_stereo_config: typeof mmCIF_chemCompBond_schema['pdbx_stereo_config']['T'][] = []
|
||||
const molstar_protonation_variant: string[] = []
|
||||
|
||||
function addBonds(compId: string, ccb: CCB, protonationVariant: boolean) {
|
||||
@@ -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.'
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
|
||||
import './index.html'
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../../mol-plugin/command';
|
||||
import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
|
||||
import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
|
||||
import { PluginCommands } from '../../../mol-plugin/commands';
|
||||
import { StateTransforms } from '../../../mol-plugin-state/transforms';
|
||||
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')
|
||||
import { createStructureRepresentationParams } from '../../../mol-plugin-state/helpers/structure-representation-params';
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -94,18 +94,18 @@ class LightingDemo {
|
||||
|
||||
setPreset(preset: Canvas3DPreset) {
|
||||
const props = getPreset(preset)
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
|
||||
PluginCommands.Canvas3D.SetSettings(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
|
||||
},
|
||||
}});
|
||||
@@ -120,18 +120,24 @@ class LightingDemo {
|
||||
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
|
||||
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
|
||||
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
}
|
||||
}
|
||||
return parsed
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
|
||||
}
|
||||
|
||||
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill', {}, 'illustrative'), { ref: 'seq-visual' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
@@ -139,7 +145,7 @@ class LightingDemo {
|
||||
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.dataState;
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
|
||||
loadType = 'full';
|
||||
@@ -149,17 +155,23 @@ class LightingDemo {
|
||||
|
||||
let tree: StateBuilder.Root;
|
||||
if (loadType === 'full') {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
|
||||
tree = state.build();
|
||||
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
|
||||
} else {
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
}
|
||||
}
|
||||
tree = state.build();
|
||||
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
|
||||
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as _ from '../../mol-plugin/state/transforms'
|
||||
import * as _ from '../../mol-plugin-state/transforms'
|
||||
import { StateTransformer, StateObject } from '../../mol-state';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@@ -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] | readonly [string, string, string])[]) {
|
||||
return options.map(o => `'${o[0]}'`).join(', ');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ 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) {
|
||||
@@ -50,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) {
|
||||
@@ -65,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;
|
||||
@@ -86,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])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,18 +123,6 @@ export function printSequence(model: Model) {
|
||||
console.log();
|
||||
}
|
||||
|
||||
export function printModRes(model: Model) {
|
||||
console.log('\nModified Residues\n=============');
|
||||
const map = model.properties.modifiedResidues.parentId;
|
||||
const { label_comp_id, _rowCount } = model.atomicHierarchy.residues;
|
||||
for (let i = 0; i < _rowCount; i++) {
|
||||
const comp_id = label_comp_id.value(i);
|
||||
if (!map.has(comp_id)) continue;
|
||||
console.log(`[${i}] ${map.get(comp_id)} -> ${comp_id}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
export function printRings(structure: Structure) {
|
||||
console.log('\nRings\n=============');
|
||||
for (const unit of structure.units) {
|
||||
@@ -148,7 +140,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;
|
||||
@@ -179,7 +171,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(', ')}`);
|
||||
@@ -214,9 +207,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.mod) printModRes(models[0]);
|
||||
if (args.intraBonds) printBonds(structure, true, false);
|
||||
if (args.interBonds) printBonds(structure, false, true);
|
||||
if (args.sec) printSecStructure(models[0]);
|
||||
}
|
||||
|
||||
@@ -242,8 +234,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 {
|
||||
@@ -256,8 +248,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,23 +1,22 @@
|
||||
/**
|
||||
* 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 { StateAction } from '../../../../mol-state';
|
||||
import { PluginContext } from '../../../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
|
||||
import { PluginStateObject as PSO } from '../../../../mol-plugin-state/objects';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
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';
|
||||
import { Task } from '../../../../mol-task';
|
||||
import { StructureRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation';
|
||||
import { StateTransforms } from '../../../../mol-plugin/state/transforms';
|
||||
import { StateTransforms } from '../../../../mol-plugin-state/transforms';
|
||||
import { distinctColors } from '../../../../mol-util/color/distinct';
|
||||
import { ModelIndexColorThemeProvider } from '../../../../mol-theme/color/model-index';
|
||||
import { Hcl } from '../../../../mol-util/color/spaces/hcl';
|
||||
@@ -26,13 +25,11 @@ import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder'
|
||||
import { getMatFromResamplePoints } from './curve';
|
||||
import { compile } from '../../../../mol-script/runtime/query/compiler';
|
||||
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
|
||||
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
|
||||
import { ColorTheme } from '../../../../mol-theme/color';
|
||||
import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
|
||||
import { ModelFormat } from '../../../../mol-model-formats/structure/format';
|
||||
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../../../mol-data/db';
|
||||
import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
|
||||
import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`
|
||||
@@ -124,7 +121,10 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const d = model.sourceData.data.atom_site
|
||||
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
|
||||
|
||||
@@ -201,8 +201,8 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
}
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
|
||||
entity: CifCategory.ofTable('entity', db.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
}
|
||||
|
||||
@@ -217,8 +217,8 @@ async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model)
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = ModelFormat.mmCIF(cif)
|
||||
const models = await _parse_mmCif(format, ctx)
|
||||
const format = MmcifFormat.fromFrame(cif)
|
||||
const models = await createModels(format.data.db, format, ctx)
|
||||
return models[0]
|
||||
})
|
||||
|
||||
@@ -268,7 +268,7 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
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
|
||||
}
|
||||
@@ -279,6 +279,9 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
})
|
||||
}
|
||||
|
||||
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: {
|
||||
@@ -289,15 +292,11 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['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'],
|
||||
] as ['spacefill' | 'gaussian-surface' | 'point', string][])
|
||||
representation: PD.Select('spacefill', RepresentationOptions)
|
||||
}, { isExpanded: true })
|
||||
},
|
||||
from: PSO.Root
|
||||
@@ -385,21 +384,21 @@ 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),
|
||||
color: getColorParams(hue)
|
||||
createStructureRepresentationParams(ctx, Structure.Empty, {
|
||||
...getReprParams(ctx, params.preset),
|
||||
...getColorParams(hue)
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -413,8 +412,8 @@ export const LoadCellPackModel = StateAction.build({
|
||||
.apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
|
||||
repr: getReprParams(ctx, params.preset),
|
||||
createStructureRepresentationParams(ctx, Structure.Empty, {
|
||||
...getReprParams(ctx, params.preset),
|
||||
color: UniformColorThemeProvider
|
||||
})
|
||||
)
|
||||
@@ -425,50 +424,46 @@ export const LoadCellPackModel = StateAction.build({
|
||||
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':
|
||||
return traceOnly
|
||||
? [
|
||||
ctx.structureRepresentation.registry.get('spacefill'),
|
||||
() => ({ sizeFactor: 2, ignoreHydrogens: true })
|
||||
] as [any, any]
|
||||
: [
|
||||
ctx.structureRepresentation.registry.get('spacefill'),
|
||||
() => ({ ignoreHydrogens: true })
|
||||
] as [any, any]
|
||||
? {
|
||||
type: ctx.representation.structure.registry.get('spacefill'),
|
||||
typeParams: { sizeFactor: 2, ignoreHydrogens: true }
|
||||
} : {
|
||||
type: ctx.representation.structure.registry.get('spacefill'),
|
||||
typeParams: { ignoreHydrogens: true }
|
||||
}
|
||||
case 'gaussian-surface':
|
||||
return [
|
||||
ctx.structureRepresentation.registry.get('gaussian-surface'),
|
||||
() => ({
|
||||
return {
|
||||
type: ctx.representation.structure.registry.get('gaussian-surface'),
|
||||
typeParams: {
|
||||
quality: 'custom', resolution: 10, radiusOffset: 2,
|
||||
alpha: 1.0, flatShaded: false, doubleSided: false,
|
||||
ignoreHydrogens: true
|
||||
})
|
||||
] as [any, any]
|
||||
}
|
||||
}
|
||||
case 'point':
|
||||
return [
|
||||
ctx.structureRepresentation.registry.get('point'),
|
||||
() => ({ ignoreHydrogens: true })
|
||||
] as [any, any]
|
||||
return { type: ctx.representation.structure.registry.get('point') }
|
||||
case 'ellipsoid':
|
||||
return { type: ctx.representation.structure.registry.get('orientation') }
|
||||
}
|
||||
}
|
||||
|
||||
function getColorParams(hue: [number, number]) {
|
||||
return [
|
||||
ModelIndexColorThemeProvider,
|
||||
(c: ColorTheme.Provider<any>, ctx: ThemeRegistryContext) => {
|
||||
return {
|
||||
palette: {
|
||||
name: 'generate',
|
||||
params: {
|
||||
hue, chroma: [30, 80], luminance: [15, 85],
|
||||
clusteringStepCount: 50, minSampleCount: 800,
|
||||
maxCount: 75
|
||||
}
|
||||
function getColorParams(hue: [number, number]): any {
|
||||
return {
|
||||
color: ModelIndexColorThemeProvider,
|
||||
colorParams: {
|
||||
palette: {
|
||||
name: 'generate',
|
||||
params: {
|
||||
hue, chroma: [30, 80], luminance: [15, 85],
|
||||
clusteringStepCount: 50, minSampleCount: 800,
|
||||
maxCount: 75
|
||||
}
|
||||
}
|
||||
}
|
||||
] as [any, any]
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin/state/objects';
|
||||
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin-state/objects';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { Task } from '../../../../mol-task';
|
||||
import { CellPack as _CellPack, Cell, CellPacking } from './data';
|
||||
|
||||
@@ -1,167 +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 { createStructureRepresentation3dParamss } 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(), e.pos, e.in);
|
||||
Vec3.normalize(direction, direction);
|
||||
const up = Vec3.sub(Vec3(), 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,
|
||||
// createStructureRepresentation3dParamss.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,
|
||||
// createStructureRepresentation3dParamss.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',
|
||||
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;
|
||||
}
|
||||
// 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
|
||||
// });
|
||||
// }
|
||||
@@ -5,16 +5,17 @@
|
||||
* @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 { PluginCommands } from '../../mol-plugin/commands';
|
||||
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')
|
||||
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
function getParam(name: string, regex: string): string {
|
||||
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
|
||||
@@ -27,7 +28,7 @@ function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [
|
||||
...DefaultPluginSpec.actions,
|
||||
PluginSpec.Action(CreateJoleculeState),
|
||||
// PluginSpec.Action(CreateJoleculeState),
|
||||
PluginSpec.Action(LoadCellPackModel),
|
||||
PluginSpec.Action(StructureFromCellpack),
|
||||
],
|
||||
@@ -42,10 +43,12 @@ function init() {
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
|
||||
}
|
||||
}
|
||||
},
|
||||
config: DefaultPluginSpec.config
|
||||
};
|
||||
const plugin = createPlugin(document.getElementById('app')!, spec);
|
||||
trySetSnapshot(plugin);
|
||||
tryLoadFromUrl(plugin);
|
||||
}
|
||||
|
||||
async function trySetSnapshot(ctx: PluginContext) {
|
||||
@@ -57,11 +60,42 @@ async function trySetSnapshot(ctx: PluginContext) {
|
||||
const url = snapshotId
|
||||
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
|
||||
: snapshotUrl;
|
||||
await PluginCommands.State.Snapshots.Fetch.dispatch(ctx, { url })
|
||||
await PluginCommands.State.Snapshots.Fetch(ctx, { url })
|
||||
} catch (e) {
|
||||
ctx.log.error('Failed to load snapshot.');
|
||||
console.warn('Failed to load snapshot', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function tryLoadFromUrl(ctx: PluginContext) {
|
||||
const url = getParam('loadFromURL', '[^&]+').trim();
|
||||
try {
|
||||
if (!url) return;
|
||||
|
||||
let format = 'cif', isBinary = false;
|
||||
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
|
||||
case 'pdb': format = 'pdb'; break;
|
||||
case 'mmbcif': isBinary = true; break;
|
||||
}
|
||||
|
||||
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
|
||||
|
||||
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
url,
|
||||
format: format as any,
|
||||
isBinary,
|
||||
options: params.source.params.options,
|
||||
structure: params.source.params.structure,
|
||||
}
|
||||
}
|
||||
}));
|
||||
} catch (e) {
|
||||
ctx.log.error(`Failed to load from URL (${url})`);
|
||||
console.warn(`Failed to load from URL (${url})`, e);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -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) {
|
||||
label: 'Evolutionary Conservation',
|
||||
type: 'static',
|
||||
async getData(model: Model, ctx: CustomProperty.Context) {
|
||||
const id = model.entryId.toLowerCase();
|
||||
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await req.json();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
@@ -94,13 +94,13 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
}
|
||||
}
|
||||
|
||||
const ProteopediaCustomColorThemeProvider: ColorTheme.Provider<ProteopediaCustomColorThemeParams> = {
|
||||
return {
|
||||
name: 'proteopedia-custom',
|
||||
label: 'Proteopedia Custom',
|
||||
category: 'Custom',
|
||||
factory: ProteopediaCustomColorTheme,
|
||||
getParams: getChainIdColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
|
||||
}
|
||||
|
||||
return ProteopediaCustomColorThemeProvider;
|
||||
}
|
||||
@@ -5,10 +5,11 @@
|
||||
*/
|
||||
|
||||
import { ResidueIndex, Model } from '../../mol-model/structure';
|
||||
import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/registry';
|
||||
import { BuiltInColorThemeName } from '../../mol-theme/color';
|
||||
import { AminoAcidNames } from '../../mol-model/structure/model/types';
|
||||
import { StructureRepresentationRegistry } from '../../mol-repr/structure/registry';
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { PolymerType } 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[] }[],
|
||||
@@ -53,15 +54,14 @@ export namespace ModelInfo {
|
||||
const hetMap = new Map<string, ModelInfo['hetResidues'][0]>();
|
||||
|
||||
for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
|
||||
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
|
||||
if (AminoAcidNames.has(comp_id)) continue;
|
||||
const mod_parent = model.properties.modifiedResidues.parentId.get(comp_id);
|
||||
if (mod_parent && AminoAcidNames.has(mod_parent)) continue;
|
||||
if (model.atomicHierarchy.derived.residue.polymerType[rI] !== PolymerType.NA) continue;
|
||||
|
||||
const cI = chainIndex[residueOffsets[rI]];
|
||||
const eI = model.atomicHierarchy.index.getEntityFromChain(cI);
|
||||
if (model.entities.data.type.value(eI) === 'water') continue;
|
||||
|
||||
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
|
||||
|
||||
let lig = hetMap.get(comp_id);
|
||||
if (!lig) {
|
||||
lig = { name: comp_id, indices: [] };
|
||||
@@ -72,10 +72,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
|
||||
};
|
||||
}
|
||||
@@ -97,7 +98,7 @@ export interface RepresentationStyle {
|
||||
}
|
||||
|
||||
export namespace RepresentationStyle {
|
||||
export type Entry = { hide?: boolean, kind?: BuiltInStructureRepresentationsName, coloring?: BuiltInColorThemeName }
|
||||
export type Entry = { hide?: boolean, kind?: StructureRepresentationRegistry.BuiltIn, coloring?: ColorTheme.BuiltIn }
|
||||
}
|
||||
|
||||
export enum StateElements {
|
||||
|
||||
@@ -8,36 +8,32 @@ import * as ReactDOM from 'react-dom';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { StateTransforms } from '../../mol-plugin/state/transforms';
|
||||
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
|
||||
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { Scheduler } from '../../mol-task';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
|
||||
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 { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
// 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 = 4;
|
||||
static VERSION_MAJOR = 4;
|
||||
static VERSION_MINOR = 0;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -59,23 +55,23 @@ class MolStarProteopediaWrapper {
|
||||
initial: {
|
||||
isExpanded: false,
|
||||
showControls: false
|
||||
},
|
||||
controls: {
|
||||
right: ControlsWrapper
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
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.representation.structure.themes.colorThemeRegistry.add(customColoring);
|
||||
this.plugin.representation.structure.themes.colorThemeRegistry.add(EvolutionaryConservation.colorThemeProvider!);
|
||||
this.plugin.managers.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
|
||||
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this.plugin.state.dataState;
|
||||
return this.plugin.state.data;
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
@@ -93,10 +89,16 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
private structure(assemblyId: string) {
|
||||
const model = this.state.build().to(StateElements.Model);
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
}
|
||||
}
|
||||
|
||||
const s = model
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [EvolutionaryConservation.propertyProvider.descriptor.name], properties: {} }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { ref: StateElements.Assembly });
|
||||
|
||||
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
|
||||
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: StateElements.Het });
|
||||
@@ -119,9 +121,10 @@ class MolStarProteopediaWrapper {
|
||||
root.delete(StateElements.SequenceVisual);
|
||||
} else {
|
||||
root.applyOrUpdate(StateElements.SequenceVisual, StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.sequence && style.sequence.kind) || 'cartoon',
|
||||
(style.sequence && style.sequence.coloring) || 'unit-index', structure));
|
||||
createStructureRepresentationParams(this.plugin, structure, {
|
||||
type: (style.sequence && style.sequence.kind) || 'cartoon',
|
||||
color: (style.sequence && style.sequence.coloring) || 'unit-index'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +137,10 @@ class MolStarProteopediaWrapper {
|
||||
root.delete(StateElements.HetVisual);
|
||||
} else {
|
||||
root.applyOrUpdate(StateElements.HetVisual, StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
|
||||
(style.hetGroups && style.hetGroups.coloring), structure));
|
||||
createStructureRepresentationParams(this.plugin, structure, {
|
||||
type: (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
|
||||
color: style.hetGroups && style.hetGroups.coloring
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +154,7 @@ class MolStarProteopediaWrapper {
|
||||
root.delete(StateElements.Het3DSNFG);
|
||||
} else {
|
||||
root.applyOrUpdate(StateElements.Het3DSNFG, StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin, 'carbohydrate', void 0, structure));
|
||||
createStructureRepresentationParams(this.plugin, structure, { type: 'carbohydrate' }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,9 +165,11 @@ 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 }));
|
||||
createStructureRepresentationParams(this.plugin, structure, {
|
||||
type: (style.water && style.water.kind) || 'ball-and-stick',
|
||||
typeParams: { alpha: 0.51 },
|
||||
color: style.water && style.water.coloring
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,14 +193,14 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
private applyState(tree: StateBuilder) {
|
||||
return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
|
||||
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
}
|
||||
|
||||
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
|
||||
async load({ url, format = 'cif', assemblyId = 'deposited', representationStyle }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.dataState;
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
|
||||
loadType = 'full';
|
||||
@@ -203,7 +209,7 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
if (loadType === 'full') {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
|
||||
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
|
||||
await this.applyState(modelTree);
|
||||
const info = await this.doInfo(true);
|
||||
@@ -214,37 +220,45 @@ class MolStarProteopediaWrapper {
|
||||
const tree = state.build();
|
||||
const info = await this.doInfo(true);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: asmId }));
|
||||
const props = {
|
||||
type: {
|
||||
name: 'assembly' as const,
|
||||
params: { id: asmId || 'deposited' }
|
||||
}
|
||||
}
|
||||
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
|
||||
await this.applyState(tree);
|
||||
}
|
||||
|
||||
await this.updateStyle(representationStyle);
|
||||
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }));
|
||||
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
|
||||
}
|
||||
|
||||
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
|
||||
const tree = this.visual(style, partial);
|
||||
if (!tree) return;
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
}
|
||||
|
||||
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) } } });
|
||||
PluginCommands.Canvas3D.SetSettings(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 } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
viewport = {
|
||||
setSettings: (settings?: Canvas3DProps) => {
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
settings: settings || DefaultCanvas3DParams
|
||||
});
|
||||
}
|
||||
@@ -252,10 +266,10 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
camera = {
|
||||
toggleSpin: () => this.toggleSpin(),
|
||||
resetPosition: () => PluginCommands.Camera.Reset.dispatch(this.plugin, { }),
|
||||
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { }),
|
||||
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
|
||||
// if (!options) {
|
||||
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
|
||||
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
// settings: {
|
||||
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
|
||||
// clip: DefaultCanvas3DParams.clip
|
||||
@@ -268,7 +282,7 @@ class MolStarProteopediaWrapper {
|
||||
// const props = this.plugin.canvas3d.props;
|
||||
// const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
|
||||
// const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
|
||||
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
|
||||
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
|
||||
// });
|
||||
// }
|
||||
@@ -298,7 +312,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.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
if (!params || !!params.sequence) {
|
||||
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
@@ -307,7 +321,7 @@ class MolStarProteopediaWrapper {
|
||||
tree.to(StateElements.HetVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,9 +329,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);
|
||||
@@ -325,7 +341,7 @@ class MolStarProteopediaWrapper {
|
||||
remove: () => {
|
||||
const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
|
||||
if (!r) return;
|
||||
PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
|
||||
PluginCommands.State.RemoveObject(this.plugin, { state: this.state, ref: r.transform.ref });
|
||||
if (this.experimentalDataElement) {
|
||||
ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
|
||||
this.experimentalDataElement = void 0;
|
||||
@@ -336,12 +352,12 @@ class MolStarProteopediaWrapper {
|
||||
hetGroups = {
|
||||
reset: () => {
|
||||
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, { });
|
||||
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
},
|
||||
focusFirst: async (compId: string) => {
|
||||
if (!this.state.transforms.has(StateElements.Assembly)) return;
|
||||
await PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
await PluginCommands.Camera.Reset(this.plugin, { });
|
||||
|
||||
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
|
||||
|
||||
@@ -374,7 +390,7 @@ class MolStarProteopediaWrapper {
|
||||
// }
|
||||
// });
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
|
||||
|
||||
const focus = (this.state.select(StateElements.HetGroupFocus)[0].obj as PluginStateObject.Molecule.Structure).data;
|
||||
const sphere = focus.boundary.sphere;
|
||||
@@ -382,26 +398,25 @@ 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));
|
||||
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
|
||||
const radius = Math.max(sphere.radius, 5)
|
||||
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius);
|
||||
PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: 250 });
|
||||
}
|
||||
}
|
||||
|
||||
private createSurVisualParams() {
|
||||
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
|
||||
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
|
||||
repr: BuiltInStructureRepresentations['ball-and-stick'],
|
||||
color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
|
||||
size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
|
||||
return createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'ball-and-stick',
|
||||
color: 'uniform', colorParams: { value: ColorNames.gray },
|
||||
size: 'uniform', sizeParams: { value: 0.33 }
|
||||
});
|
||||
}
|
||||
|
||||
private createCoreVisualParams() {
|
||||
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
|
||||
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
|
||||
repr: BuiltInStructureRepresentations['ball-and-stick'],
|
||||
// color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
|
||||
// size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
|
||||
return createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'ball-and-stick'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -414,8 +429,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,24 +6,11 @@
|
||||
|
||||
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 { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
|
||||
import { StateElements } from '../helpers';
|
||||
|
||||
export class ControlsWrapper extends PluginUIComponent {
|
||||
render() {
|
||||
return <div className='msp-scrollable-container msp-right-controls'>
|
||||
<CurrentObject />
|
||||
<AnimationControls />
|
||||
<CameraSnapshots />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
|
||||
ReactDOM.render(<PluginContextContainer plugin={plugin}>
|
||||
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
|
||||
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
export { Camera }
|
||||
|
||||
@@ -33,6 +34,7 @@ class Camera {
|
||||
zoom = 1
|
||||
|
||||
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
|
||||
readonly stateChanged = new BehaviorSubject<Partial<Camera.Snapshot>>(this.state);
|
||||
|
||||
get position() { return this.state.position; }
|
||||
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
|
||||
@@ -76,35 +78,39 @@ class Camera {
|
||||
|
||||
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
|
||||
this.transition.apply(snapshot, durationMs);
|
||||
this.stateChanged.next(snapshot);
|
||||
}
|
||||
|
||||
getSnapshot() {
|
||||
const ret = Camera.createDefaultSnapshot();
|
||||
Camera.copySnapshot(ret, this.state);
|
||||
return ret;
|
||||
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
|
||||
}
|
||||
|
||||
getFocus(target: Vec3, radius: number): Partial<Camera.Snapshot> {
|
||||
const fov = this.state.fov
|
||||
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
const r = Math.max(radius, 0.01)
|
||||
const { fov } = this.state
|
||||
const { width, height } = this.viewport
|
||||
const aspect = width / height
|
||||
const aspectFactor = (height < width ? 1 : aspect)
|
||||
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
|
||||
const targetDistance = Math.abs((r / aspectFactor) / Math.sin(fov / 2))
|
||||
|
||||
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)
|
||||
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
|
||||
state.target = Vec3.clone(target)
|
||||
state.radius = radius
|
||||
state.radius = r
|
||||
state.position = Vec3.clone(this.newPosition)
|
||||
if (up) Vec3.matchDirection(state.up, up, state.up)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
focus(target: Vec3, radius: number, durationMs?: number) {
|
||||
if (radius > 0) this.setState(this.getFocus(target, radius), durationMs);
|
||||
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
|
||||
if (radius > 0) {
|
||||
this.setState(this.getFocus(target, radius, up, dir), durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
project(out: Vec4, point: Vec3) {
|
||||
@@ -159,7 +165,9 @@ namespace Camera {
|
||||
target: Vec3.create(0, 0, 0),
|
||||
|
||||
radius: 10,
|
||||
radiusMax: 10,
|
||||
fog: 50,
|
||||
clipFar: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -172,7 +180,9 @@ namespace Camera {
|
||||
target: Vec3
|
||||
|
||||
radius: number
|
||||
radiusMax: number
|
||||
fog: number
|
||||
clipFar: boolean
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
@@ -186,7 +196,9 @@ namespace Camera {
|
||||
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
|
||||
|
||||
if (typeof source.radius !== 'undefined') out.radius = source.radius;
|
||||
if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
|
||||
if (typeof source.fog !== 'undefined') out.fog = source.fog;
|
||||
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -253,17 +265,17 @@ function updatePers(camera: Camera) {
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
const { radius, mode, fog } = camera.state
|
||||
let { radius, radiusMax, mode, fog, clipFar } = camera.state
|
||||
if (radius < 0.01) radius = 0.01
|
||||
|
||||
const cDist = Vec3.distance(camera.position, camera.target)
|
||||
const bRadius = Math.max(1, radius)
|
||||
|
||||
let near = cDist - bRadius
|
||||
let far = cDist + bRadius
|
||||
const normalizedFar = clipFar ? radius : radiusMax
|
||||
const cameraDistance = Vec3.distance(camera.position, camera.target)
|
||||
let near = cameraDistance - radius
|
||||
let far = cameraDistance + normalizedFar
|
||||
|
||||
const fogNearFactor = -(50 - fog) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + bRadius
|
||||
let fogNear = cameraDistance - (normalizedFar * fogNearFactor)
|
||||
let fogFar = far
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
|
||||
@@ -17,19 +17,26 @@ class CameraTransitionManager {
|
||||
private start = 0;
|
||||
inTransition = false;
|
||||
private durationMs = 0;
|
||||
private source: Camera.Snapshot = Camera.createDefaultSnapshot();
|
||||
private target: Camera.Snapshot = Camera.createDefaultSnapshot();
|
||||
private current = Camera.createDefaultSnapshot();
|
||||
private _source: Camera.Snapshot = Camera.createDefaultSnapshot();
|
||||
private _target: Camera.Snapshot = Camera.createDefaultSnapshot();
|
||||
private _current = Camera.createDefaultSnapshot();
|
||||
|
||||
get source(): Readonly<Camera.Snapshot> { return this._source }
|
||||
get target(): Readonly<Camera.Snapshot> { return this._target }
|
||||
|
||||
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
|
||||
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
|
||||
this.finish(to);
|
||||
return;
|
||||
Camera.copySnapshot(this._source, this.camera.state);
|
||||
Camera.copySnapshot(this._target, this.camera.state);
|
||||
Camera.copySnapshot(this._target, to);
|
||||
|
||||
if (this._target.radius > this._target.radiusMax) {
|
||||
this._target.radius = this._target.radiusMax
|
||||
}
|
||||
|
||||
Camera.copySnapshot(this.source, this.camera.state);
|
||||
Camera.copySnapshot(this.target, this.camera.state);
|
||||
Camera.copySnapshot(this.target, to);
|
||||
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
|
||||
this.finish(this._target);
|
||||
return;
|
||||
}
|
||||
|
||||
this.inTransition = true;
|
||||
this.func = transition || CameraTransitionManager.defaultTransition;
|
||||
@@ -52,12 +59,12 @@ class CameraTransitionManager {
|
||||
|
||||
const normalized = Math.min((this.t - this.start) / this.durationMs, 1);
|
||||
if (normalized === 1) {
|
||||
this.finish(this.target!);
|
||||
this.finish(this._target!);
|
||||
return;
|
||||
}
|
||||
|
||||
this.func(this.current, normalized, this.source, this.target);
|
||||
Camera.copySnapshot(this.camera.state, this.current);
|
||||
this.func(this._current, normalized, this._source, this._target);
|
||||
Camera.copySnapshot(this.camera.state, this._current);
|
||||
}
|
||||
|
||||
constructor(private camera: Camera) {
|
||||
@@ -80,6 +87,8 @@ namespace CameraTransitionManager {
|
||||
Vec3.lerp(out.target, source.target, target.target, t);
|
||||
Vec3.lerp(out.position, source.position, target.position, t);
|
||||
out.radius = lerp(source.radius, target.radius, t);
|
||||
// TODO take change of `clipFar` into account
|
||||
out.radiusMax = lerp(source.radiusMax, target.radiusMax, t);
|
||||
|
||||
// Lerp fov & fog
|
||||
out.fov = lerp(source.fov, target.fov, t);
|
||||
|
||||
@@ -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,18 +27,30 @@ 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 = {
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
|
||||
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']] as const),
|
||||
cameraFog: PD.MappedStatic('on', {
|
||||
on: PD.Group({
|
||||
intensity: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Show fog in the distance' }),
|
||||
cameraClipping: PD.Group({
|
||||
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
|
||||
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
|
||||
}, { pivot: 'radius' }),
|
||||
|
||||
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),
|
||||
@@ -53,29 +66,34 @@ 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(isSynchronous?: boolean): void
|
||||
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
|
||||
clear(): void
|
||||
syncVisibility(): 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(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
|
||||
readonly camera: Camera
|
||||
downloadScreenshot: () => void
|
||||
getPixelData: (variant: GraphicsRenderVariant) => PixelData
|
||||
setProps: (props: Partial<Canvas3DProps>) => void
|
||||
getImagePass: () => ImagePass
|
||||
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>
|
||||
@@ -83,15 +101,15 @@ 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 HoverEvent { current: Representation.Loci, buttons: ButtonsType, 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, {
|
||||
@@ -103,10 +121,45 @@ namespace Canvas3D {
|
||||
})
|
||||
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,7 +169,7 @@ namespace Canvas3D {
|
||||
const startTime = now()
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
|
||||
|
||||
const webgl = createContext(gl)
|
||||
const { gl, contextRestored } = webgl
|
||||
|
||||
let width = gl.drawingBufferWidth
|
||||
let height = gl.drawingBufferHeight
|
||||
@@ -126,7 +179,8 @@ namespace Canvas3D {
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode,
|
||||
fog: p.cameraFog
|
||||
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
|
||||
clipFar: p.cameraClipping.far
|
||||
})
|
||||
|
||||
const controls = TrackballControls.create(input, camera, p.trackball)
|
||||
@@ -139,8 +193,15 @@ namespace Canvas3D {
|
||||
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 = false
|
||||
let nextCameraResetDuration: number | undefined = void 0
|
||||
let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0
|
||||
|
||||
function getLoci(pickingId: PickingId) {
|
||||
let loci: Loci = EmptyLoci
|
||||
@@ -174,8 +235,8 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -184,32 +245,25 @@ namespace Canvas3D {
|
||||
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(true)
|
||||
} 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;
|
||||
@@ -224,45 +278,99 @@ 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(isSynchronous: boolean = false) {
|
||||
const allCommited = commitScene(isSynchronous);
|
||||
// Only reset the camera after the full scene has been commited.
|
||||
if (allCommited) resolveCameraReset();
|
||||
}
|
||||
|
||||
runTask(scene.commit()).then(() => {
|
||||
if (cameraResetRequested && !scene.isCommiting) {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
|
||||
cameraResetRequested = false
|
||||
}
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
})
|
||||
function resolveCameraReset() {
|
||||
if (!cameraResetRequested) return;
|
||||
const { center, radius } = scene.boundingSphere;
|
||||
if (radius > 0) {
|
||||
const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration
|
||||
const focus = camera.getFocus(center, radius);
|
||||
const snapshot = nextCameraResetSnapshot ? { ...focus, ...nextCameraResetSnapshot } : focus;
|
||||
camera.setState(snapshot, duration);
|
||||
}
|
||||
|
||||
nextCameraResetDuration = void 0;
|
||||
nextCameraResetSnapshot = void 0;
|
||||
cameraResetRequested = false;
|
||||
}
|
||||
|
||||
const sceneCommitTimeoutMs = 250;
|
||||
function commitScene(isSynchronous: boolean) {
|
||||
if (!scene.needsCommit) return true;
|
||||
|
||||
if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false;
|
||||
|
||||
if (debugHelper.isEnabled) debugHelper.update();
|
||||
if (reprCount.value === 0 || camera.state.radiusMax === 0) cameraResetRequested = true;
|
||||
camera.setState({ radiusMax: scene.boundingSphere.radius })
|
||||
reprCount.next(reprRenderObjects.size);
|
||||
return true;
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -270,24 +378,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;
|
||||
@@ -297,12 +390,19 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
clear: () => {
|
||||
reprUpdatedSubscriptions.forEach(v => v.unsubscribe())
|
||||
reprUpdatedSubscriptions.clear()
|
||||
reprRenderObjects.clear()
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
},
|
||||
syncVisibility: () => {
|
||||
if (scene.syncVisibility()) {
|
||||
camera.setState({ radiusMax: scene.boundingSphere.radius })
|
||||
}
|
||||
},
|
||||
|
||||
// draw,
|
||||
requestDraw,
|
||||
@@ -312,15 +412,13 @@ namespace Canvas3D {
|
||||
getLoci,
|
||||
|
||||
handleResize,
|
||||
resetCamera: () => {
|
||||
if (scene.isCommiting) {
|
||||
cameraResetRequested = true
|
||||
} else {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
|
||||
requestDraw(true);
|
||||
}
|
||||
requestCameraReset: options => {
|
||||
nextCameraResetDuration = options?.durationMs;
|
||||
nextCameraResetSnapshot = options?.snapshot;
|
||||
cameraResetRequested = true;
|
||||
},
|
||||
camera,
|
||||
boundingSphere: scene.boundingSphere,
|
||||
downloadScreenshot: () => {
|
||||
// TODO
|
||||
},
|
||||
@@ -336,19 +434,37 @@ namespace Canvas3D {
|
||||
didDraw,
|
||||
reprCount,
|
||||
setProps: (props: Partial<Canvas3DProps>) => {
|
||||
const cameraState: Partial<Camera.Snapshot> = Object.create(null)
|
||||
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
|
||||
camera.setState({ mode: props.cameraMode })
|
||||
cameraState.mode = props.cameraMode
|
||||
}
|
||||
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
|
||||
camera.setState({ fog: props.cameraFog })
|
||||
if (props.cameraFog !== undefined) {
|
||||
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0
|
||||
if (newFog !== camera.state.fog) cameraState.fog = newFog
|
||||
}
|
||||
if (props.cameraClipping !== undefined) {
|
||||
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
|
||||
cameraState.clipFar = props.cameraClipping.far
|
||||
}
|
||||
if (props.cameraClipping.radius !== undefined) {
|
||||
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius)
|
||||
if (radius > 0 && radius !== cameraState.radius) {
|
||||
// if radius = 0, NaNs happen
|
||||
cameraState.radius = Math.max(radius, 0.01)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(cameraState).length > 0) camera.setState(cameraState)
|
||||
|
||||
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)
|
||||
if (props.renderer) renderer.setProps(props.renderer)
|
||||
if (props.trackball) controls.setProps(props.trackball)
|
||||
if (props.debug) debugHelper.setProps(props.debug)
|
||||
|
||||
requestDraw(true)
|
||||
},
|
||||
getImagePass: (props: Partial<ImageProps> = {}) => {
|
||||
@@ -356,10 +472,18 @@ namespace Canvas3D {
|
||||
},
|
||||
|
||||
get props() {
|
||||
const radius = scene.boundingSphere.radius > 0
|
||||
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
|
||||
: 0
|
||||
|
||||
return {
|
||||
cameraMode: camera.state.mode,
|
||||
cameraFog: camera.state.fog,
|
||||
cameraFog: camera.state.fog > 0
|
||||
? { name: 'on' as const, params: { intensity: camera.state.fog } }
|
||||
: { name: 'off' as const, params: {} },
|
||||
cameraClipping: { far: camera.state.clipFar, radius },
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
transparentBackground: p.transparentBackground,
|
||||
|
||||
postprocessing: { ...postprocessing.props },
|
||||
multiSample: { ...multiSample.props },
|
||||
@@ -378,6 +502,8 @@ namespace Canvas3D {
|
||||
return interactionHelper.events
|
||||
},
|
||||
dispose: () => {
|
||||
contextRestoredSub.unsubscribe()
|
||||
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
input.dispose()
|
||||
|
||||
@@ -21,15 +21,15 @@ const M = ModifiersKeys
|
||||
const Trigger = Binding.Trigger
|
||||
|
||||
export const DefaultTrackballBindings = {
|
||||
dragRotate: Binding(Trigger(B.Flag.Primary, M.create()), 'Rotate the 3D scene by dragging using ${trigger}'),
|
||||
dragRotateZ: Binding(Trigger(B.Flag.Primary, M.create({ shift: true })), 'Rotate the 3D scene around the z-axis by dragging using ${trigger}'),
|
||||
dragPan: Binding(Trigger(B.Flag.Secondary, M.create()), 'Pan the 3D scene by dragging using ${trigger}'),
|
||||
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate', 'Drag using ${triggers}'),
|
||||
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate around z-axis', 'Drag using ${triggers}'),
|
||||
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan', 'Drag using ${triggers}'),
|
||||
dragZoom: Binding.Empty,
|
||||
dragFocus: Binding(Trigger(B.Flag.Forth, M.create()), 'Focus the 3D scene by dragging using ${trigger}'),
|
||||
dragFocusZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Focus and zoom the 3D scene by dragging using ${trigger}'),
|
||||
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus', 'Drag using ${triggers}'),
|
||||
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom', 'Drag using ${triggers}'),
|
||||
|
||||
scrollZoom: Binding(Trigger(B.Flag.Auxilary, M.create()), 'Zoom the 3D scene by scrolling using ${trigger}'),
|
||||
scrollFocus: Binding(Trigger(B.Flag.Auxilary, M.create({ shift: true })), 'Focus the 3D scene by scrolling using ${trigger}'),
|
||||
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom', 'Scroll using ${triggers}'),
|
||||
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'),
|
||||
scrollFocusZoom: Binding.Empty,
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export const TrackballControlsParams = {
|
||||
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
|
||||
|
||||
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 }),
|
||||
spinSpeed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
|
||||
|
||||
staticMoving: PD.Boolean(true, { isHidden: true }),
|
||||
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -45,27 +45,25 @@ export class BoundingSphereHelper {
|
||||
}
|
||||
|
||||
update() {
|
||||
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.grey)
|
||||
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.grey, sceneMaterialId)
|
||||
if (newSceneData) this.sceneData = newSceneData
|
||||
|
||||
this.parent.forEach((r, ro) => {
|
||||
const objectData = this.objectsData.get(ro)
|
||||
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato)
|
||||
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato, objectMaterialId)
|
||||
if (newObjectData) this.objectsData.set(ro, newObjectData)
|
||||
|
||||
if (ro.type === 'mesh' || ro.type === 'lines' || ro.type === 'points') {
|
||||
const instanceData = this.instancesData.get(ro)
|
||||
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, {
|
||||
aTransform: ro.values.aTransform,
|
||||
matrix: ro.values.matrix,
|
||||
transform: ro.values.transform,
|
||||
extraTransform: ro.values.extraTransform,
|
||||
uInstanceCount: ro.values.uInstanceCount,
|
||||
instanceCount: ro.values.instanceCount,
|
||||
aInstance: ro.values.aInstance,
|
||||
})
|
||||
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
|
||||
}
|
||||
const instanceData = this.instancesData.get(ro)
|
||||
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, instanceMaterialId, {
|
||||
aTransform: ro.values.aTransform,
|
||||
matrix: ro.values.matrix,
|
||||
transform: ro.values.transform,
|
||||
extraTransform: ro.values.extraTransform,
|
||||
uInstanceCount: ro.values.uInstanceCount,
|
||||
instanceCount: ro.values.instanceCount,
|
||||
aInstance: ro.values.aInstance,
|
||||
})
|
||||
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
|
||||
})
|
||||
|
||||
this.objectsData.forEach((objectData, ro) => {
|
||||
@@ -82,7 +80,7 @@ export class BoundingSphereHelper {
|
||||
})
|
||||
|
||||
this.scene.update(void 0, false)
|
||||
this.scene.syncCommit()
|
||||
this.scene.commit()
|
||||
}
|
||||
|
||||
syncVisibility() {
|
||||
@@ -116,10 +114,10 @@ export class BoundingSphereHelper {
|
||||
}
|
||||
}
|
||||
|
||||
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, transform?: TransformData) {
|
||||
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, materialId: number, transform?: TransformData) {
|
||||
if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) {
|
||||
const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh)
|
||||
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, transform)
|
||||
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, materialId, transform)
|
||||
if (data) {
|
||||
ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(mesh))
|
||||
} else {
|
||||
@@ -133,12 +131,20 @@ function createBoundingSphereMesh(boundingSphere: Sphere3D, mesh?: Mesh) {
|
||||
const detail = 2
|
||||
const vertexCount = sphereVertexCount(detail)
|
||||
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
|
||||
if (boundingSphere.radius) addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
|
||||
if (boundingSphere.radius) {
|
||||
addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
|
||||
if (Sphere3D.hasExtrema(boundingSphere)) {
|
||||
for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0)
|
||||
}
|
||||
}
|
||||
return MeshBuilder.getMesh(builderState)
|
||||
}
|
||||
|
||||
const boundingSphereHelberMaterialId = getNextMaterialId()
|
||||
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, transform?: TransformData) {
|
||||
const sceneMaterialId = getNextMaterialId()
|
||||
const objectMaterialId = getNextMaterialId()
|
||||
const instanceMaterialId = getNextMaterialId()
|
||||
|
||||
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
|
||||
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
|
||||
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, boundingSphereHelberMaterialId)
|
||||
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, materialId)
|
||||
}
|
||||
@@ -38,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) {
|
||||
@@ -50,7 +51,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ export class Canvas3dInteractionHelper {
|
||||
const loci = this.getLoci(this.id);
|
||||
// only broadcast the latest hover
|
||||
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
}
|
||||
@@ -78,22 +79,24 @@ export class Canvas3dInteractionHelper {
|
||||
this.inside = false;
|
||||
if (this.prevLoci.loci !== EmptyLoci) {
|
||||
this.prevLoci = Representation.Loci.Empty;
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
}
|
||||
|
||||
move(x: number, y: number, buttons: ButtonsType, 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);
|
||||
}
|
||||
@@ -101,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.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -109,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 }) => {
|
||||
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
|
||||
if (!inside) return;
|
||||
this.move(x, y, buttons, modifiers);
|
||||
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,11 @@
|
||||
*/
|
||||
|
||||
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 {
|
||||
@@ -20,13 +20,13 @@ export class DrawPass {
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions } = webgl
|
||||
const { gl, extensions, resources } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
this.colorTarget = createRenderTarget(webgl, width, height)
|
||||
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')
|
||||
@@ -42,29 +42,33 @@ export class DrawPass {
|
||||
}
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
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, colorTarget.width, colorTarget.height)
|
||||
renderer.render(scene, camera, '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, camera, '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, camera, 'depth', true)
|
||||
renderer.render(scene, camera, 'depth', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false)
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
}
|
||||
@@ -26,6 +27,7 @@ export class ImagePass {
|
||||
private _width = 1024
|
||||
private _height = 768
|
||||
private _camera = new Camera()
|
||||
private _transparentBackground = false
|
||||
|
||||
private _colorTarget: RenderTarget
|
||||
get colorTarget() { return this._colorTarget }
|
||||
@@ -40,6 +42,8 @@ export class ImagePass {
|
||||
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)
|
||||
@@ -48,6 +52,8 @@ export class ImagePass {
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
if (width === this._width && height === this._height) return
|
||||
|
||||
this._width = width
|
||||
this._height = height
|
||||
|
||||
@@ -57,6 +63,7 @@ export class ImagePass {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -69,10 +76,10 @@ export class ImagePass {
|
||||
this.renderer.setViewport(0, 0, this._width, this._height);
|
||||
|
||||
if (this.multiSample.enabled) {
|
||||
this.multiSample.render(false)
|
||||
this.multiSample.render(false, this._transparentBackground)
|
||||
this._colorTarget = this.multiSample.colorTarget
|
||||
} else {
|
||||
this.drawPass.render(false)
|
||||
this.drawPass.render(false, this._transparentBackground)
|
||||
if (this.postprocessing.enabled) {
|
||||
this.postprocessing.render(false)
|
||||
this._colorTarget = this.postprocessing.target
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -66,9 +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.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
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 }
|
||||
}
|
||||
@@ -105,15 +105,15 @@ export class MultiSamplePass {
|
||||
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
if (this.props.mode === 'temporal') {
|
||||
this.renderTemporalMultiSample(toDrawingBuffer)
|
||||
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
|
||||
} else {
|
||||
this.renderMultiSample(toDrawingBuffer)
|
||||
this.renderMultiSample(toDrawingBuffer, transparentBackground)
|
||||
}
|
||||
}
|
||||
|
||||
private renderMultiSample(toDrawingBuffer: boolean) {
|
||||
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -131,7 +131,8 @@ 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.
|
||||
@@ -148,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
|
||||
@@ -184,7 +185,7 @@ export class MultiSamplePass {
|
||||
camera.update()
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean) {
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -204,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)
|
||||
@@ -222,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.
|
||||
@@ -233,7 +235,7 @@ export class MultiSamplePass {
|
||||
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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
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';
|
||||
@@ -36,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()
|
||||
}
|
||||
@@ -68,11 +68,11 @@ export class PickPass {
|
||||
const { renderer, scene, camera } = this
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickObject', true);
|
||||
renderer.render(scene, camera, 'pickObject', true, false);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickInstance', true);
|
||||
renderer.render(scene, camera, 'pickInstance', true, false);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickGroup', true);
|
||||
renderer.render(scene, camera, 'pickGroup', true, false);
|
||||
|
||||
this.pickDirty = false
|
||||
}
|
||||
@@ -97,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()
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -14,9 +14,10 @@ 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';
|
||||
import { produce } from 'immer';
|
||||
|
||||
import quad_vert from '../../mol-gl/shader/quad.vert'
|
||||
import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag'
|
||||
@@ -27,7 +28,6 @@ const PostprocessingSchema = {
|
||||
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
dOrthographic: DefineSpec('number'),
|
||||
uNear: UniformSpec('f'),
|
||||
uFar: UniformSpec('f'),
|
||||
@@ -48,16 +48,21 @@ const PostprocessingSchema = {
|
||||
}
|
||||
|
||||
export const PostprocessingParams = {
|
||||
occlusionEnable: PD.Boolean(false),
|
||||
occlusionKernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
|
||||
occlusionBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
|
||||
occlusionRadius: PD.Numeric(32, { min: 0, max: 256, step: 1 }),
|
||||
|
||||
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),
|
||||
occlusion: PD.MappedStatic('off', {
|
||||
on: PD.Group({
|
||||
kernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
|
||||
bias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
|
||||
radius: PD.Numeric(64, { min: 0, max: 256, step: 1 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
|
||||
outline: PD.MappedStatic('off', {
|
||||
on: PD.Group({
|
||||
scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
|
||||
threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
|
||||
}),
|
||||
off: PD.Group({})
|
||||
}, { cycle: true, description: 'Draw outline around 3D objects' })
|
||||
}
|
||||
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
|
||||
|
||||
@@ -69,9 +74,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),
|
||||
@@ -79,14 +83,14 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
uFogFar: ValueCell.create(10000),
|
||||
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
|
||||
|
||||
dOcclusionEnable: ValueCell.create(p.occlusionEnable),
|
||||
dOcclusionKernelSize: ValueCell.create(p.occlusionKernelSize),
|
||||
uOcclusionBias: ValueCell.create(p.occlusionBias),
|
||||
uOcclusionRadius: ValueCell.create(p.occlusionRadius),
|
||||
dOcclusionEnable: ValueCell.create(p.occlusion.name === 'on'),
|
||||
dOcclusionKernelSize: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.kernelSize : 4),
|
||||
uOcclusionBias: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.bias : 0.5),
|
||||
uOcclusionRadius: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.radius : 64),
|
||||
|
||||
dOutlineEnable: ValueCell.create(p.outlineEnable),
|
||||
uOutlineScale: ValueCell.create(p.outlineScale * ctx.pixelRatio),
|
||||
uOutlineThreshold: ValueCell.create(p.outlineThreshold),
|
||||
dOutlineEnable: ValueCell.create(p.outline.name === 'on'),
|
||||
uOutlineScale: ValueCell.create((p.outline.name === 'on' ? p.outline.params.scale : 1) * ctx.pixelRatio),
|
||||
uOutlineThreshold: ValueCell.create(p.outline.name === 'on' ? p.outline.params.threshold : 0.8),
|
||||
|
||||
dPackedDepth: ValueCell.create(packedDepth),
|
||||
}
|
||||
@@ -105,14 +109,14 @@ 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)
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
return this.props.occlusionEnable || this.props.outlineEnable
|
||||
return this.props.occlusion.name === 'on' || this.props.outline.name === 'on'
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
@@ -121,40 +125,28 @@ export class PostprocessingPass {
|
||||
}
|
||||
|
||||
setProps(props: Partial<PostprocessingProps>) {
|
||||
if (props.occlusionEnable !== undefined) {
|
||||
this.props.occlusionEnable = props.occlusionEnable
|
||||
ValueCell.update(this.renderable.values.dOcclusionEnable, props.occlusionEnable)
|
||||
}
|
||||
if (props.occlusionKernelSize !== undefined) {
|
||||
this.props.occlusionKernelSize = props.occlusionKernelSize
|
||||
ValueCell.update(this.renderable.values.dOcclusionKernelSize, props.occlusionKernelSize)
|
||||
}
|
||||
if (props.occlusionBias !== undefined) {
|
||||
this.props.occlusionBias = props.occlusionBias
|
||||
ValueCell.update(this.renderable.values.uOcclusionBias, props.occlusionBias)
|
||||
}
|
||||
if (props.occlusionRadius !== undefined) {
|
||||
this.props.occlusionRadius = props.occlusionRadius
|
||||
ValueCell.update(this.renderable.values.uOcclusionRadius, props.occlusionRadius)
|
||||
}
|
||||
this.props = produce(this.props, p => {
|
||||
if (props.occlusion !== undefined) {
|
||||
p.occlusion.name = props.occlusion.name
|
||||
ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, props.occlusion.name === 'on')
|
||||
if (props.occlusion.name === 'on') {
|
||||
p.occlusion.params = { ...props.occlusion.params }
|
||||
ValueCell.updateIfChanged(this.renderable.values.dOcclusionKernelSize, props.occlusion.params.kernelSize)
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOcclusionBias, props.occlusion.params.bias)
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOcclusionRadius, props.occlusion.params.radius)
|
||||
}
|
||||
}
|
||||
|
||||
if (props.outlineEnable !== undefined) {
|
||||
this.props.outlineEnable = props.outlineEnable
|
||||
ValueCell.update(this.renderable.values.dOutlineEnable, props.outlineEnable)
|
||||
}
|
||||
if (props.outlineScale !== undefined) {
|
||||
this.props.outlineScale = props.outlineScale
|
||||
ValueCell.update(this.renderable.values.uOutlineScale, props.outlineScale * this.webgl.pixelRatio)
|
||||
}
|
||||
if (props.outlineThreshold !== undefined) {
|
||||
this.props.outlineThreshold = props.outlineThreshold
|
||||
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)
|
||||
}
|
||||
if (props.outline !== undefined) {
|
||||
p.outline.name = props.outline.name
|
||||
ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, props.outline.name === 'on')
|
||||
if (props.outline.name === 'on') {
|
||||
p.outline.params = { ...props.outline.params }
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOutlineScale, props.outline.params.scale)
|
||||
ValueCell.updateIfChanged(this.renderable.values.uOutlineThreshold, props.outline.params.threshold)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.renderable.update()
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
/** Set canvas size taking `devicePixelRatio` into account */
|
||||
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
|
||||
canvas.width = window.devicePixelRatio * width
|
||||
canvas.height = window.devicePixelRatio * height
|
||||
canvas.width = Math.round(window.devicePixelRatio * width)
|
||||
canvas.height = Math.round(window.devicePixelRatio * height)
|
||||
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?:
|
||||
const len = bin.length
|
||||
const len32 = len >> 2
|
||||
const a8 = new Uint8Array(len)
|
||||
const a32 = new Uint32Array( a8.buffer, 0, len32 )
|
||||
const a32 = new Uint32Array(a8.buffer, 0, len32)
|
||||
|
||||
let j = 0
|
||||
for (let i = 0; i < len32; ++i) {
|
||||
|
||||
@@ -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' };
|
||||
@@ -92,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);
|
||||
@@ -131,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({
|
||||
@@ -290,14 +304,14 @@ 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]
|
||||
}
|
||||
}
|
||||
@@ -311,7 +325,8 @@ function windowColumn<T>(column: Column<T>, start: number, end: number): Column<
|
||||
|
||||
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> {
|
||||
@@ -343,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);
|
||||
}
|
||||
|
||||
@@ -353,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> {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { sortArray } from '../util/sort'
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
|
||||
/** A collection of columns */
|
||||
type Table<Schema extends Table.Schema> = {
|
||||
type Table<Schema extends Table.Schema = any> = {
|
||||
readonly _rowCount: number,
|
||||
readonly _columns: ReadonlyArray<string>,
|
||||
readonly _schema: Schema
|
||||
@@ -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);
|
||||
@@ -59,7 +73,7 @@ namespace Table {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Partial<Row<S>>>): R {
|
||||
export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rows: ArrayLike<Partial<Row<S>>>): R {
|
||||
const ret = Object.create(null);
|
||||
const rowCount = rows.length;
|
||||
const columns = Object.keys(schema);
|
||||
@@ -77,14 +91,19 @@ namespace Table {
|
||||
return ret as R;
|
||||
}
|
||||
|
||||
export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R {
|
||||
export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: S, arrays: Partial<Arrays<S>>): R {
|
||||
const ret = Object.create(null);
|
||||
const columns = Object.keys(schema);
|
||||
ret._rowCount = arrays[columns[0]].length;
|
||||
ret._rowCount = 0;
|
||||
ret._columns = columns;
|
||||
ret._schema = schema;
|
||||
for (const k of columns) {
|
||||
(ret as any)[k] = typeof arrays[k] !== 'undefined' ? Column.ofArray({ array: arrays[k], schema: schema[k] }) : Column.Undefined(ret._rowCount, schema[k]);
|
||||
if (typeof arrays[k] !== 'undefined') {
|
||||
(ret as any)[k] = Column.ofArray({ array: arrays[k]!, schema: schema[k] });
|
||||
ret._rowCount = arrays[k]?.length;
|
||||
} else {
|
||||
(ret as any)[k] = Column.Undefined(ret._rowCount, schema[k]);
|
||||
}
|
||||
}
|
||||
return ret as R;
|
||||
}
|
||||
@@ -153,7 +172,7 @@ namespace Table {
|
||||
}
|
||||
|
||||
/** Sort and return a new table */
|
||||
export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) {
|
||||
export function sort<T extends Table>(table: T, cmp: (i: number, j: number) => number) {
|
||||
const indices = new Int32Array(table._rowCount);
|
||||
for (let i = 0, _i = indices.length; i < _i; i++) indices[i] = i;
|
||||
sortArray(indices, (_, i, j) => cmp(i, j));
|
||||
@@ -177,7 +196,7 @@ namespace Table {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function areEqual<T extends Table<Schema>>(a: T, b: T) {
|
||||
export function areEqual<T extends Table<any>>(a: T, b: T) {
|
||||
if (a._rowCount !== b._rowCount) return false;
|
||||
if (a._columns.length !== b._columns.length) return false;
|
||||
for (const c of a._columns) {
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
});
|
||||
@@ -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];
|
||||
|
||||
@@ -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';
|
||||
@@ -30,22 +28,24 @@ export const VisualQualityInfo = {
|
||||
'lowest': {},
|
||||
}
|
||||
export type VisualQuality = keyof typeof VisualQualityInfo
|
||||
export const VisualQualityNames = Object.keys(VisualQualityInfo)
|
||||
export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string])
|
||||
export const VisualQualityNames = Object.keys(VisualQualityInfo) as VisualQuality[]
|
||||
export const VisualQualityOptions = PD.arrayToOptions(VisualQualityNames)
|
||||
|
||||
//
|
||||
|
||||
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),
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
export const ShadingCategory: PD.Info = { category: 'Shading' };
|
||||
export const CustomQualityParamInfo: PD.Info = {
|
||||
category: 'Custom Quality',
|
||||
hideIf: (params: PD.Values<Params>) => typeof params.quality !== 'undefined' && params.quality !== 'custom'
|
||||
};
|
||||
|
||||
export type Counts = { drawCount: number, groupCount: number, instanceCount: number }
|
||||
|
||||
export function createSimple(colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData) {
|
||||
@@ -62,24 +62,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,12 @@
|
||||
/**
|
||||
* 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, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -17,19 +17,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 +43,29 @@ 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
|
||||
|
||||
setBoundingSphere(boundingSphere: Sphere3D): void
|
||||
}
|
||||
|
||||
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,71 @@ 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
|
||||
},
|
||||
setBoundingSphere(sphere: Sphere3D) {
|
||||
Sphere3D.copy(boundingSphere, sphere)
|
||||
currentHash = hashCode(lines)
|
||||
}
|
||||
}
|
||||
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 +187,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 +226,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 +236,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, 0, 4)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount, 0, 4)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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,6 +99,27 @@ export namespace MeshBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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, radialSegments }
|
||||
@@ -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,102 @@ 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
|
||||
|
||||
setBoundingSphere(boundingSphere: Sphere3D): void
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
setBoundingSphere(sphere: Sphere3D) {
|
||||
Sphere3D.copy(boundingSphere, sphere)
|
||||
currentHash = hashCode(mesh)
|
||||
}
|
||||
}
|
||||
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 +146,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 +161,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 +197,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)
|
||||
@@ -358,9 +327,10 @@ export namespace Mesh {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
doubleSided: PD.Boolean(false),
|
||||
flipSided: PD.Boolean(false),
|
||||
flatShaded: PD.Boolean(false),
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -388,10 +358,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 +378,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 +393,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 +408,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,12 @@
|
||||
/**
|
||||
* 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, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -14,7 +14,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 +23,94 @@ 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
|
||||
|
||||
setBoundingSphere(boundingSphere: Sphere3D): void
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
setBoundingSphere(sphere: Sphere3D) {
|
||||
Sphere3D.copy(boundingSphere, sphere)
|
||||
currentHash = hashCode(points)
|
||||
}
|
||||
}
|
||||
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 +146,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 +184,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,91 @@ 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
|
||||
|
||||
setBoundingSphere(boundingSphere: Sphere3D): void
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
setBoundingSphere(sphere: Sphere3D) {
|
||||
Sphere3D.copy(boundingSphere, sphere)
|
||||
currentHash = hashCode(spheres)
|
||||
}
|
||||
}
|
||||
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),
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -87,10 +151,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, 4
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: spheres.centerBuffer,
|
||||
@@ -111,6 +173,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 +187,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, 4
|
||||
)
|
||||
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,22 @@ 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
|
||||
|
||||
setBoundingSphere(boundingSphere: Sphere3D): void
|
||||
}
|
||||
|
||||
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 +81,70 @@ 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
|
||||
},
|
||||
setBoundingSphere(sphere: Sphere3D) {
|
||||
Sphere3D.copy(boundingSphere, sphere)
|
||||
currentHash = hashCode(text)
|
||||
}
|
||||
}
|
||||
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 +199,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 +261,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 +281,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,9 +70,9 @@ export namespace TextureMesh {
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
doubleSided: PD.Boolean(false),
|
||||
flipSided: PD.Boolean(false),
|
||||
flatShaded: PD.Boolean(false),
|
||||
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
||||
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>
|
||||
return { vertices, edges }
|
||||
}
|
||||
|
||||
export function copyCage(cage: Cage): Cage {
|
||||
export function cloneCage(cage: Cage): Cage {
|
||||
return {
|
||||
vertices: new Float32Array(cage.vertices),
|
||||
edges: new Uint32Array(cage.edges)
|
||||
|
||||
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 {
|
||||
@@ -74,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)
|
||||
|
||||
@@ -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-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 David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -166,6 +166,19 @@ class MarchingCubesState {
|
||||
const v1 = sfg(sf, hi, hj, hk);
|
||||
const t = (this.isoLevel - v0) / (v0 - v1);
|
||||
|
||||
if (this.idField) {
|
||||
const u = this.idFieldGet!(this.idField, li, lj, lk);
|
||||
const v = this.idFieldGet!(this.idField, hi, hj, hk)
|
||||
let a = t < 0.5 ? u : v;
|
||||
// -1 means 'no id', check if the other cell has an id
|
||||
if (a === -1) a = t < 0.5 ? v : u;
|
||||
// -2 means 'ignore this cell'
|
||||
if (a === -2) return -1
|
||||
this.builder.addGroup(a);
|
||||
} else {
|
||||
this.builder.addGroup(0);
|
||||
}
|
||||
|
||||
const id = this.builder.addVertex(
|
||||
li + t * (li - hi),
|
||||
lj + t * (lj - hj),
|
||||
@@ -189,16 +202,6 @@ class MarchingCubesState {
|
||||
n0z + t * (n0z - n1z)
|
||||
)
|
||||
|
||||
if (this.idField) {
|
||||
const u = this.idFieldGet!(this.idField, li, lj, lk);
|
||||
const v = this.idFieldGet!(this.idField, hi, hj, hk)
|
||||
let a = t < 0.5 ? u : v;
|
||||
if (a < 0) a = t < 0.5 ? v : u;
|
||||
this.builder.addGroup(a);
|
||||
} else {
|
||||
this.builder.addGroup(0);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -44,25 +44,20 @@ export function MarchinCubesMeshBuilder(vertexChunkSize: number, mesh?: Mesh): M
|
||||
ChunkedArray.add(groups, group);
|
||||
},
|
||||
addTriangle: (vertList: number[], a: number, b: number, c: number) => {
|
||||
++triangleCount
|
||||
ChunkedArray.add3(indices, vertList[a], vertList[b], vertList[c]);
|
||||
const i = vertList[a], j = vertList[b], k = vertList[c]
|
||||
// vertex indices <0 mean that the vertex was ignored and is not available
|
||||
// and hence we don't add a triangle when this occurs
|
||||
if (i >= 0 && j >= 0 && k >= 0) {
|
||||
++triangleCount
|
||||
ChunkedArray.add3(indices, i, j, k)
|
||||
}
|
||||
},
|
||||
get: () => {
|
||||
const vb = ChunkedArray.compact(vertices, true) as Float32Array;
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,17 +78,22 @@ export function MarchinCubesLinesBuilder(vertexChunkSize: number, lines?: Lines)
|
||||
ChunkedArray.add(groups, group);
|
||||
},
|
||||
addTriangle: (vertList: number[], a: number, b: number, c: number, edgeFilter: number) => {
|
||||
if (AllowedContours[a][b] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[a], vertList[b])
|
||||
}
|
||||
if (AllowedContours[b][c] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[b], vertList[c])
|
||||
}
|
||||
if (AllowedContours[a][c] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[a], vertList[c])
|
||||
const i = vertList[a], j = vertList[b], k = vertList[c]
|
||||
// vertex indices <0 mean that the vertex was ignored and is not available
|
||||
// and hence we don't add a triangle when this occurs
|
||||
if (i >= 0 && j >= 0 && k >= 0) {
|
||||
if (AllowedContours[a][b] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[a], vertList[b])
|
||||
}
|
||||
if (AllowedContours[b][c] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[b], vertList[c])
|
||||
}
|
||||
if (AllowedContours[a][c] & edgeFilter) {
|
||||
++linesCount
|
||||
ChunkedArray.add2(indices, vertList[a], vertList[c])
|
||||
}
|
||||
}
|
||||
},
|
||||
get: () => {
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -10,7 +10,8 @@ describe('renderable', () => {
|
||||
it('calculateBoundingSphere', () => {
|
||||
const position = new Float32Array([
|
||||
0, 0, 0,
|
||||
1, 0, 0
|
||||
1, 0, 0,
|
||||
-1, 0, 0,
|
||||
])
|
||||
const transform = new Float32Array([
|
||||
1, 0, 0, 0,
|
||||
@@ -26,15 +27,18 @@ describe('renderable', () => {
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
2, 0, 0, 0
|
||||
-1, 0, 0, 0
|
||||
])
|
||||
|
||||
const { boundingSphere } = calculateBoundingSphere(
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
position, position.length / 3,
|
||||
transform, transform.length / 16
|
||||
)
|
||||
|
||||
expect(boundingSphere.radius).toBe(1.5)
|
||||
expect(boundingSphere.center).toEqual([1.5, 0.0, 0.0])
|
||||
expect(invariantBoundingSphere.extrema).toEqual([[0, 0, 0], [1, 0, 0], [-1, 0, 0]])
|
||||
expect(invariantBoundingSphere.radius).toBe(1)
|
||||
expect(invariantBoundingSphere.center).toEqual([0, 0, 0])
|
||||
expect(boundingSphere.radius).toBe(2)
|
||||
expect(boundingSphere.center).toEqual([0, 0, 0])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -65,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),
|
||||
|
||||
@@ -83,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,
|
||||
@@ -104,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)
|
||||
@@ -124,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)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec } 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 { decodeFloatRGB } from '../../../mol-util/float-packing';
|
||||
@@ -45,14 +45,11 @@ function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
|
||||
let SumTexture: Texture
|
||||
function getSumTexture(ctx: WebGLContext) {
|
||||
if (SumTexture) return SumTexture
|
||||
SumTexture = createTexture(ctx, 'image-uint8', 'rgba', 'ubyte', 'nearest')
|
||||
SumTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest')
|
||||
SumTexture.define(1, 1)
|
||||
return SumTexture
|
||||
}
|
||||
|
||||
/** name for shared framebuffer used for histogram-pyramid operations */
|
||||
const FramebufferName = 'histogram-pyramid-sum'
|
||||
|
||||
function setRenderingDefaults(ctx: WebGLContext) {
|
||||
const { gl, state } = ctx
|
||||
state.disable(gl.CULL_FACE)
|
||||
@@ -66,12 +63,12 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
|
||||
const sumArray = new Uint8Array(4)
|
||||
export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
|
||||
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture)
|
||||
ctx.state.currentRenderItemId = -1
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
const sumTexture = getSumTexture(ctx)
|
||||
sumTexture.attachFramebuffer(framebuffer, 0)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable } 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 { Vec3, Vec2 } from '../../../mol-math/linear-algebra';
|
||||
@@ -17,9 +17,6 @@ import { getTriCount } from './tables';
|
||||
import quad_vert from '../../../mol-gl/shader/quad.vert'
|
||||
import active_voxels_frag from '../../../mol-gl/shader/marching-cubes/active-voxels.frag'
|
||||
|
||||
/** name for shared framebuffer used for gpu marching cubes operations */
|
||||
const FramebufferName = 'marching-cubes-active-voxels'
|
||||
|
||||
const ActiveVoxelsSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
@@ -67,13 +64,14 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { width, height } = volumeData
|
||||
const { gl, resources } = ctx
|
||||
const width = volumeData.getWidth()
|
||||
const height = volumeData.getHeight()
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
framebuffer.bind()
|
||||
|
||||
const activeVoxelsTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const activeVoxelsTex = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
activeVoxelsTex.define(width, height)
|
||||
|
||||
const renderable = getActiveVoxelsRenderable(ctx, volumeData, gridDim, gridTexDim, isoValue, gridScale)
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable } 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 { Vec3, Vec2, Mat4 } from '../../../mol-math/linear-algebra';
|
||||
@@ -18,9 +18,6 @@ import { getTriIndices } from './tables';
|
||||
import quad_vert from '../../../mol-gl/shader/quad.vert'
|
||||
import isosurface_frag from '../../../mol-gl/shader/marching-cubes/isosurface.frag'
|
||||
|
||||
/** name for shared framebuffer used for gpu marching cubes operations */
|
||||
const FramebufferName = 'marching-cubes-isosurface'
|
||||
|
||||
const IsosurfaceSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
@@ -83,30 +80,30 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
const { pyramidTex, height, levels, scale, count } = histogramPyramid
|
||||
|
||||
// console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim)
|
||||
// console.log('iso volumeData', volumeData)
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
|
||||
let needsClear = false
|
||||
|
||||
if (!vertexGroupTexture) {
|
||||
vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
} else if (vertexGroupTexture.width !== pyramidTex.width || vertexGroupTexture.height !== pyramidTex.height) {
|
||||
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else if (vertexGroupTexture.getWidth() !== pyramidTex.getWidth() || vertexGroupTexture.getHeight() !== pyramidTex.getHeight()) {
|
||||
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else {
|
||||
needsClear = true
|
||||
}
|
||||
|
||||
if (!normalTexture) {
|
||||
normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
normalTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
} else if (normalTexture.width !== pyramidTex.width || normalTexture.height !== pyramidTex.height) {
|
||||
normalTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else if (normalTexture.getWidth() !== pyramidTex.getWidth() || normalTexture.getHeight() !== pyramidTex.getHeight()) {
|
||||
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else {
|
||||
needsClear = true
|
||||
}
|
||||
@@ -150,7 +147,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
])
|
||||
|
||||
setRenderingDefaults(ctx)
|
||||
gl.viewport(0, 0, pyramidTex.width, pyramidTex.height)
|
||||
gl.viewport(0, 0, pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT)
|
||||
renderable.render()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,7 +13,7 @@ import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { GLRenderingContext } from '../../mol-gl/webgl/compat';
|
||||
|
||||
export const QuadPositions = new Float32Array([
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0 // Second triangle
|
||||
])
|
||||
|
||||
@@ -42,11 +42,11 @@ function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: numb
|
||||
}
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
width = defaults(width, texture.width)
|
||||
height = defaults(height, texture.height)
|
||||
const { gl, resources } = ctx
|
||||
width = defaults(width, texture.getWidth())
|
||||
height = defaults(height, texture.getHeight())
|
||||
const size = width * height * 4
|
||||
const framebuffer = framebufferCache.get('read-texture').value
|
||||
const framebuffer = resources.framebuffer()
|
||||
const array = getArrayForTexture(gl, texture, size)
|
||||
framebuffer.bind()
|
||||
texture.attachFramebuffer(framebuffer, 0)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/**
|
||||
* 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 { RenderableState, Renderable } from './renderable'
|
||||
import { RenderableValues } from './renderable/schema';
|
||||
import { idFactory } from '../mol-util/id-factory';
|
||||
import { WebGLContext } from './webgl/context';
|
||||
import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume';
|
||||
@@ -20,53 +19,40 @@ const getNextId = idFactory(0, 0x7FFFFFFF)
|
||||
|
||||
export const getNextMaterialId = idFactory(0, 0x7FFFFFFF)
|
||||
|
||||
export interface BaseRenderObject<T extends RenderableValues> { id: number, type: string, values: T, state: RenderableState, materialId: number }
|
||||
export interface MeshRenderObject extends BaseRenderObject<MeshValues> { type: 'mesh' }
|
||||
export interface PointsRenderObject extends BaseRenderObject<PointsValues> { type: 'points' }
|
||||
export interface SpheresRenderObject extends BaseRenderObject<SpheresValues> { type: 'spheres' }
|
||||
export interface TextRenderObject extends BaseRenderObject<TextValues> { type: 'text' }
|
||||
export interface LinesRenderObject extends BaseRenderObject<LinesValues> { type: 'lines' }
|
||||
export interface DirectVolumeRenderObject extends BaseRenderObject<DirectVolumeValues> { type: 'direct-volume' }
|
||||
export interface TextureMeshRenderObject extends BaseRenderObject<TextureMeshValues> { type: 'texture-mesh' }
|
||||
export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectType> {
|
||||
readonly id: number,
|
||||
readonly type: T,
|
||||
readonly values: RenderObjectValues<T>,
|
||||
readonly state: RenderableState,
|
||||
readonly materialId: number
|
||||
}
|
||||
|
||||
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
|
||||
|
||||
export type RenderObjectValues<T extends RenderObjectType> =
|
||||
T extends 'mesh' ? MeshValues :
|
||||
T extends 'points' ? PointsValues :
|
||||
T extends 'spheres' ? SpheresValues :
|
||||
T extends 'text' ? TextValues :
|
||||
T extends 'lines' ? LinesValues :
|
||||
T extends 'direct-volume' ? DirectVolumeValues :
|
||||
T extends 'texture-mesh' ? TextureMeshValues : never
|
||||
|
||||
//
|
||||
|
||||
export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject | TextureMeshRenderObject
|
||||
|
||||
export type RenderObjectKindType = {
|
||||
'mesh': MeshRenderObject
|
||||
'points': PointsRenderObject
|
||||
'spheres': SpheresRenderObject
|
||||
'text': TextRenderObject
|
||||
'lines': LinesRenderObject
|
||||
'direct-volume': DirectVolumeRenderObject
|
||||
'texture-mesh': TextureMeshRenderObject
|
||||
}
|
||||
export type RenderObjectValuesType = {
|
||||
'mesh': MeshValues
|
||||
'points': PointsValues
|
||||
'spheres': SpheresValues
|
||||
'text': TextValues
|
||||
'lines': LinesValues
|
||||
'direct-volume': DirectVolumeValues
|
||||
'texture-mesh': TextureMeshValues
|
||||
}
|
||||
export type RenderObjectType = keyof RenderObjectKindType
|
||||
|
||||
//
|
||||
|
||||
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValuesType[T], state: RenderableState, materialId: number): RenderObjectKindType[T] {
|
||||
return { id: getNextId(), type, values, state, materialId } as RenderObjectKindType[T]
|
||||
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValues<T>, state: RenderableState, materialId: number): GraphicsRenderObject<T> {
|
||||
return { id: getNextId(), type, values, state, materialId } as GraphicsRenderObject<T>
|
||||
}
|
||||
|
||||
export function createRenderable(ctx: WebGLContext, o: GraphicsRenderObject): Renderable<any> {
|
||||
export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>): Renderable<any> {
|
||||
switch (o.type) {
|
||||
case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'points': return PointsRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'spheres': return SpheresRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'text': return TextRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId)
|
||||
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId)
|
||||
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId)
|
||||
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId)
|
||||
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId)
|
||||
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId)
|
||||
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId)
|
||||
}
|
||||
}
|
||||
throw new Error('unsupported type')
|
||||
}
|
||||
|
||||
@@ -51,9 +51,6 @@ export const DirectVolumeSchema = {
|
||||
elements: ElementsSpec('uint32'),
|
||||
|
||||
uAlpha: UniformSpec('f'),
|
||||
uHighlightColor: UniformSpec('v3'),
|
||||
uSelectColor: UniformSpec('v3'),
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
|
||||
uIsoValue: UniformSpec('f'),
|
||||
uBboxMin: UniformSpec('v3'),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user