mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
1240 Commits
v0.2.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 | ||
|
|
944d370c14 | ||
|
|
74f9aa6af6 | ||
|
|
c20c9c9917 | ||
|
|
4801435d72 | ||
|
|
33fd105ef7 | ||
|
|
3ea3fb8984 | ||
|
|
b4bbc544ca | ||
|
|
5f880e920b | ||
|
|
bcce801dd7 | ||
|
|
00f9dcee4a | ||
|
|
505af2bc96 | ||
|
|
c217aab5fc | ||
|
|
5d5fd0028f | ||
|
|
c88693dfdd | ||
|
|
0a16ec1bd2 | ||
|
|
6f36a3c724 | ||
|
|
71496bd1ef | ||
|
|
c5a99a7c12 | ||
|
|
cfaf33d696 | ||
|
|
e24c76d2bf | ||
|
|
b6273205a2 | ||
|
|
b38193aa19 | ||
|
|
f9cfacae23 | ||
|
|
ac46317dc6 | ||
|
|
2b492a5a61 | ||
|
|
a2133657f0 | ||
|
|
e8de45789f | ||
|
|
3d2bd167ca | ||
|
|
504c8626dc | ||
|
|
f4b29dc7e0 | ||
|
|
b34c5c743b | ||
|
|
c57311d6c0 | ||
|
|
4d786dc697 | ||
|
|
062e3e055a | ||
|
|
1465174a45 | ||
|
|
cc00ada5a3 | ||
|
|
cbf312b62d | ||
|
|
2be3144086 | ||
|
|
da3acd9d19 | ||
|
|
34b048479b | ||
|
|
ca92931bf2 | ||
|
|
0d3daeb823 | ||
|
|
58b1d7e0eb | ||
|
|
c5997ed056 | ||
|
|
da1deee7f3 | ||
|
|
a4eaff3175 | ||
|
|
211cfc0bd3 | ||
|
|
c0f85b691d | ||
|
|
6b93d58ea6 | ||
|
|
5bce423b49 | ||
|
|
01b0dde503 | ||
|
|
ed1bc8cb7d | ||
|
|
abe559261b | ||
|
|
b0cdf22cb8 | ||
|
|
a61ba71f1e | ||
|
|
7061d57559 | ||
|
|
83a1e6c87c | ||
|
|
c18888b8de | ||
|
|
2d80935e00 | ||
|
|
4287d158b6 | ||
|
|
4e9b569178 | ||
|
|
5d626d291b | ||
|
|
afa4a01c44 | ||
|
|
3c4a23c5a3 | ||
|
|
8d92c976d9 | ||
|
|
1a9adfad29 | ||
|
|
a9e9a5974d | ||
|
|
27160aa8fe | ||
|
|
6a71af00cf | ||
|
|
6c0938db50 | ||
|
|
b76173c82f | ||
|
|
0bcf2b1ff4 | ||
|
|
d074415a26 | ||
|
|
42f3e38026 | ||
|
|
74c16ee7ba | ||
|
|
f6ef22b917 | ||
|
|
c3f3d7efda | ||
|
|
61d44efdc4 | ||
|
|
ad200c86ec | ||
|
|
4aecf4e0b4 | ||
|
|
d81f37c78b | ||
|
|
c2979ce5ab | ||
|
|
09c7edce88 | ||
|
|
beefb79258 | ||
|
|
529c6ac81c | ||
|
|
84b988ea96 | ||
|
|
35e978efc9 | ||
|
|
19559d01f7 | ||
|
|
56cec343e2 | ||
|
|
20c9bd0130 | ||
|
|
1336997c58 | ||
|
|
b178fdefdc | ||
|
|
453d60060a | ||
|
|
901fac97a0 | ||
|
|
29b6a88343 | ||
|
|
a2217c7fc6 | ||
|
|
3eec30aa42 | ||
|
|
f352e19e90 | ||
|
|
be65ef89bc | ||
|
|
cfc46073c0 | ||
|
|
1819a2bbda | ||
|
|
aa3a42f94e | ||
|
|
4bff55b612 | ||
|
|
dbc4e09909 | ||
|
|
0a074c8a66 | ||
|
|
8b314ebb75 | ||
|
|
4dc0791aeb | ||
|
|
e3483f11b1 | ||
|
|
244a678ab7 | ||
|
|
8e1d44fabc | ||
|
|
954a5b58e0 | ||
|
|
53223bc72b | ||
|
|
a11a1fa07e | ||
|
|
9ab4001544 | ||
|
|
3e3b71d230 | ||
|
|
186929269b | ||
|
|
afa7a04af0 | ||
|
|
2861d12f04 | ||
|
|
65e1212b2f | ||
|
|
47136c8b71 | ||
|
|
375b829562 | ||
|
|
2718d42b01 | ||
|
|
93d4118c0a | ||
|
|
3a2a47af12 | ||
|
|
0eacdfca85 | ||
|
|
a7388be25f | ||
|
|
5fcdcb1275 | ||
|
|
4635bdffb0 | ||
|
|
72b1c36111 | ||
|
|
fc5ff601a8 | ||
|
|
8dd142fc9f | ||
|
|
97f24293e2 | ||
|
|
f4beba5215 | ||
|
|
e2abe0f52a | ||
|
|
5d18643374 | ||
|
|
769f2a30c2 | ||
|
|
0e040d7744 | ||
|
|
7600d0a44e | ||
|
|
9113d6d189 | ||
|
|
fd9dac86b9 | ||
|
|
fe1f3bd4bb | ||
|
|
d950718110 | ||
|
|
4be903b9d5 | ||
|
|
8050644869 | ||
|
|
347c1986df | ||
|
|
eac4a8988b | ||
|
|
25137f29d2 | ||
|
|
4601fe74bb | ||
|
|
f0b54e9cbf | ||
|
|
fdb45c3624 | ||
|
|
f3b1ec0ed8 | ||
|
|
79510614b9 | ||
|
|
f8c9bc6812 | ||
|
|
85d35aab90 | ||
|
|
f5b09dbd10 | ||
|
|
57ea322fd6 | ||
|
|
c31aab5594 | ||
|
|
a5556e8c41 | ||
|
|
0ce2966c47 | ||
|
|
f2c04a13af | ||
|
|
60ba0de219 | ||
|
|
99e515604e | ||
|
|
84dfa60fc2 | ||
|
|
a753095f92 | ||
|
|
ce0ff2fed8 | ||
|
|
1c1deb5ee7 | ||
|
|
e28749b794 | ||
|
|
7566cc89ca | ||
|
|
8b76c09559 | ||
|
|
c60d7d3faf | ||
|
|
3eb3d1e27e | ||
|
|
3083c7d9e0 | ||
|
|
9184219976 | ||
|
|
90a3d302f3 | ||
|
|
02dd5f9d11 | ||
|
|
3685c92b52 | ||
|
|
e1a04c8b0b | ||
|
|
5a704b7974 | ||
|
|
2376d0a9c6 | ||
|
|
87e0c05ec0 | ||
|
|
8140b75086 | ||
|
|
1f32d5469e | ||
|
|
b09549439f | ||
|
|
e1b7a5b267 | ||
|
|
1ed420aeb7 | ||
|
|
301a23b1e0 | ||
|
|
e53bb51f15 | ||
|
|
4e6e8fed82 | ||
|
|
b2809ad631 | ||
|
|
37648b41ea | ||
|
|
fac183c2ee | ||
|
|
b113448e01 | ||
|
|
c585ba7791 | ||
|
|
afb6c65c48 | ||
|
|
527e91c9eb | ||
|
|
9e2ef87611 | ||
|
|
f2342c3273 | ||
|
|
347981792e | ||
|
|
032d2f2784 | ||
|
|
ddc1119a80 | ||
|
|
0fe6774f04 | ||
|
|
2a71e44ae8 | ||
|
|
1257f1ce85 | ||
|
|
07cd9f4b16 | ||
|
|
ed5ff1c9ce | ||
|
|
7ffc2db76e | ||
|
|
03067ca6d6 | ||
|
|
a69f1337d7 | ||
|
|
5e83c3350a | ||
|
|
f051d2d01e | ||
|
|
257340283b | ||
|
|
e769d77ec8 | ||
|
|
1923535918 | ||
|
|
dd3fc5620b | ||
|
|
7165258431 | ||
|
|
df9b367e0b | ||
|
|
ed1ae71f71 | ||
|
|
1c58bca454 | ||
|
|
c01be0644e | ||
|
|
66b9f6104c | ||
|
|
af4d2c4003 | ||
|
|
3b1a2f19a4 | ||
|
|
6b874786a8 | ||
|
|
f9d2560468 | ||
|
|
961034584a | ||
|
|
f6d11a59a3 | ||
|
|
53ee758378 | ||
|
|
c80c630810 | ||
|
|
13cd6e82ba | ||
|
|
f2966032d9 | ||
|
|
76503b52f5 | ||
|
|
aa24be8e9b | ||
|
|
5d7bb894d4 | ||
|
|
fcf559fa6b | ||
|
|
27963b5aed | ||
|
|
40d539c4aa | ||
|
|
d1433aaf7b | ||
|
|
8f4bf9a314 | ||
|
|
cca0f407f6 | ||
|
|
1e9e41754a | ||
|
|
38dc2d6e26 | ||
|
|
ea46b1c8c8 | ||
|
|
5846d7c4b4 | ||
|
|
214176ce2e | ||
|
|
ee4cb214e1 | ||
|
|
01cca9e8a6 | ||
|
|
7a23493e19 | ||
|
|
e109f069a8 | ||
|
|
82e667e402 | ||
|
|
c28feb2d1c | ||
|
|
8447a2d4f2 | ||
|
|
f9ebf1c399 | ||
|
|
286b27720a | ||
|
|
dc0e54c275 | ||
|
|
b710291d5e | ||
|
|
d84d5f38f5 | ||
|
|
9bea13438f | ||
|
|
2fc28f6005 | ||
|
|
bb07d6ec56 | ||
|
|
3cdfd04048 | ||
|
|
2120a258f9 | ||
|
|
2b5e49d215 | ||
|
|
b88bf9bdf2 | ||
|
|
df0f15d132 | ||
|
|
a6319bfb3d | ||
|
|
d6278cb3eb | ||
|
|
560da38687 | ||
|
|
83ba9d8776 | ||
|
|
ee776e6e3e | ||
|
|
3e52496b4e | ||
|
|
2659b96008 | ||
|
|
a8be84701b | ||
|
|
0c79aa1709 | ||
|
|
e57a19857f | ||
|
|
469dd05cd9 | ||
|
|
fb72db61bd | ||
|
|
c285e30ee0 | ||
|
|
789a327322 | ||
|
|
b8d2021599 | ||
|
|
36eae744af | ||
|
|
00df6ae52a | ||
|
|
023b65572e | ||
|
|
904e9b869c | ||
|
|
6f204b960d | ||
|
|
6217a51fa5 | ||
|
|
f1edb05c5c | ||
|
|
55bd27bb97 | ||
|
|
e2c9b601a6 | ||
|
|
238191660e | ||
|
|
0b175acc25 | ||
|
|
02865cbece | ||
|
|
a3e14bf579 | ||
|
|
1d502cbb54 | ||
|
|
4894b110b9 | ||
|
|
cefd0440a0 | ||
|
|
296bfb343e | ||
|
|
bc91f0d3ff | ||
|
|
aaa8215a6d | ||
|
|
bf0b37895d | ||
|
|
0810ed411d | ||
|
|
ff8fec542c | ||
|
|
8fb7308572 | ||
|
|
c9b7049532 | ||
|
|
da71332de1 | ||
|
|
a0de8dd9f9 | ||
|
|
e226f27041 | ||
|
|
804a04d9f8 | ||
|
|
3be06bb3b6 | ||
|
|
c7b618c246 | ||
|
|
55d990962f | ||
|
|
53e0a36539 | ||
|
|
ed7a5219bf | ||
|
|
8b49ccdc08 | ||
|
|
04fd3ade5f | ||
|
|
48985cd49d | ||
|
|
dd0707a8a5 | ||
|
|
b41ebcbbc8 | ||
|
|
991d2e3a57 | ||
|
|
7f4ac6782f | ||
|
|
7e7e30a82e | ||
|
|
08e92f12d3 | ||
|
|
d713ea6a76 | ||
|
|
0924020f24 | ||
|
|
9f10af3ba6 | ||
|
|
f754026cc5 | ||
|
|
321d98f4c1 | ||
|
|
58a49a8512 | ||
|
|
98bb9575b6 | ||
|
|
f10a135dea | ||
|
|
f300e524d1 | ||
|
|
87028c0a0b | ||
|
|
8ea23e6965 | ||
|
|
f9d8942814 | ||
|
|
b40df2f1e3 | ||
|
|
884cb0d9a4 | ||
|
|
ef1ccd4286 | ||
|
|
898abda373 | ||
|
|
e42c664a8c | ||
|
|
987bf47827 | ||
|
|
6201dd1d74 | ||
|
|
5ed17ce4e5 | ||
|
|
e301eca9c2 | ||
|
|
8a4ef015a2 | ||
|
|
67f3f3fdbb | ||
|
|
adc5b559cd | ||
|
|
bbaa637118 | ||
|
|
a3094b4d19 | ||
|
|
c3f937e113 | ||
|
|
04df327939 | ||
|
|
b1a0f46ade | ||
|
|
389e249862 | ||
|
|
cfcf9f6818 | ||
|
|
bcb8419f37 | ||
|
|
7d24bcf1dc | ||
|
|
8d0f7a2dc7 | ||
|
|
a7cb7beaa8 | ||
|
|
3c9b82dc04 | ||
|
|
9dba6d5371 | ||
|
|
a65bba0969 | ||
|
|
175e009152 | ||
|
|
897d17c8ed | ||
|
|
ca866cfa3a | ||
|
|
f6b2c0b2ba | ||
|
|
ea419c68ae | ||
|
|
40cf348d40 | ||
|
|
93ea759a71 | ||
|
|
3e50377eb8 | ||
|
|
3fbd1f8dc4 | ||
|
|
f03ce68513 | ||
|
|
50e2d542df | ||
|
|
e53e739d18 | ||
|
|
dfb7f7811f | ||
|
|
115824bbcf | ||
|
|
438de5760d | ||
|
|
3f765aedec | ||
|
|
96144fb10f | ||
|
|
cc34425712 | ||
|
|
9d68838893 | ||
|
|
6c68cebca0 | ||
|
|
b3e784262d | ||
|
|
2fdc22de71 | ||
|
|
77f0b0033f | ||
|
|
69da5abb88 | ||
|
|
a827e9a449 | ||
|
|
e74a29ae6a | ||
|
|
9b3d2f396e | ||
|
|
14cf7cc101 | ||
|
|
e5293c4d36 | ||
|
|
3afe21a4c3 | ||
|
|
bb50b69bb4 | ||
|
|
af7c030338 | ||
|
|
34b1eee876 | ||
|
|
c9cd1075d3 | ||
|
|
4fa04f0ff8 | ||
|
|
75f6466fd7 | ||
|
|
503ffd80fd | ||
|
|
c8ac64a571 | ||
|
|
bf81b902bd | ||
|
|
b636cdf9cc | ||
|
|
2d3b85825a | ||
|
|
a5a34f39e0 | ||
|
|
fe7e04f61b | ||
|
|
1a14720e35 | ||
|
|
18bf743ed2 | ||
|
|
f8d085a034 | ||
|
|
94bf3a136c | ||
|
|
9e8a8f3e71 | ||
|
|
45f9d93f3a | ||
|
|
44a566fdf3 | ||
|
|
d7f7770b7c | ||
|
|
df6b163505 | ||
|
|
51f88fff71 | ||
|
|
6b8db5abc6 | ||
|
|
37a0b07d56 |
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"
|
||||
}
|
||||
}
|
||||
18
.vscode/extensions.json
vendored
Normal file
18
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
"wayou.vscode-todo-highlight"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
]
|
||||
}
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Mol* Viewer",
|
||||
"url": "http://localhost:1338/build/viewer/index.html",
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -6,4 +6,7 @@
|
||||
"*.vert.ts": "glsl",
|
||||
"*.gql.ts": "graphql"
|
||||
},
|
||||
"eslint.options": {
|
||||
"ignorePattern": ["webpack.config.js", "scripts/*"],
|
||||
}
|
||||
}
|
||||
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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
23
README.md
23
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
|
||||
|
||||
@@ -85,17 +92,18 @@ and navigate to `build/viewer`
|
||||
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/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**
|
||||
|
||||
export NODE_PATH="lib"; node --max-old-space-size=8192 build/src/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
|
||||
export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
|
||||
|
||||
**Test model server**
|
||||
|
||||
@@ -133,14 +141,19 @@ To get syntax highlighting for shader and graphql files add the following to Vis
|
||||
|
||||
## Publish
|
||||
|
||||
## Prerelease
|
||||
### Prerelease
|
||||
npm version prerelease # asumes the current version ends with '-dev.X'
|
||||
npm publish --tag next
|
||||
|
||||
## Release
|
||||
### Release
|
||||
npm version 0.X.0 # provide valid semver string
|
||||
npm publish
|
||||
|
||||
## Deploy
|
||||
npm run test
|
||||
npm run build
|
||||
node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
|
||||
|
||||
## Contributing
|
||||
Just open an issue or make a pull request. All contributions are welcome.
|
||||
|
||||
|
||||
@@ -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-dev.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
|
||||
|
||||
|
||||
|
||||
17272
package-lock.json
generated
17272
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
116
package.json
116
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.2",
|
||||
"version": "0.6.0-dev.4",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -11,18 +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-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/",
|
||||
"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-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --display minimal",
|
||||
"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 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-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"
|
||||
@@ -30,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",
|
||||
@@ -61,54 +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.0.2",
|
||||
"concurrently": "^4.1.0",
|
||||
"cpx": "^1.5.0",
|
||||
"css-loader": "^2.1.1",
|
||||
"circular-dependency-plugin": "^5.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": "^3.0.1",
|
||||
"graphql-code-generator": "^0.18.2",
|
||||
"graphql-codegen-time": "^0.18.2",
|
||||
"graphql-codegen-typescript-template": "^0.18.2",
|
||||
"jest": "^24.8.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"http-server": "^0.12.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"raw-loader": "^2.0.0",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint": "^5.17.0",
|
||||
"typescript": "^3.5.1",
|
||||
"uglify-js": "^3.6.0",
|
||||
"util.promisify": "^1.0.0",
|
||||
"webpack": "^4.32.2",
|
||||
"webpack-cli": "^3.3.2"
|
||||
"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": "0.0.36",
|
||||
"@types/express": "^4.16.1",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/node": "^12.0.4",
|
||||
"@types/node-fetch": "^2.3.4",
|
||||
"@types/react": "^16.8.19",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/swagger-ui-dist": "3.0.0",
|
||||
"@types/webgl2": "0.0.4",
|
||||
"@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.3.1",
|
||||
"graphql": "^14.6.0",
|
||||
"immer": "^6.0.2",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"rxjs": "^6.5.2",
|
||||
"swagger-ui-dist": "^3.22.2",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
59
scripts/deploy.js
Normal file
59
scripts/deploy.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
const git = require('simple-git')
|
||||
const path = require('path')
|
||||
const fs = require("fs")
|
||||
const fse = require("fs-extra")
|
||||
|
||||
const remoteUrl = "https://github.com/molstar/molstar.github.io.git"
|
||||
const buildDir = path.resolve(__dirname, '../build/')
|
||||
const deployDir = path.resolve(buildDir, 'deploy/')
|
||||
const localPath = path.resolve(deployDir, 'molstar.github.io/')
|
||||
|
||||
function log(command, stdout, stderr) {
|
||||
if (command) {
|
||||
console.log('\n###', command)
|
||||
stdout.pipe(process.stdout)
|
||||
stderr.pipe(process.stderr)
|
||||
}
|
||||
}
|
||||
|
||||
function copyViewer() {
|
||||
console.log('\n###', 'copy viewer files')
|
||||
const viewerBuildPath = path.resolve(buildDir, '../build/viewer/')
|
||||
const viewerDeployPath = path.resolve(localPath, 'viewer/')
|
||||
fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true })
|
||||
}
|
||||
|
||||
if (!fs.existsSync(localPath)) {
|
||||
console.log('\n###', 'create localPath')
|
||||
fs.mkdirSync(localPath, { recursive: true })
|
||||
}
|
||||
|
||||
process.chdir(localPath);
|
||||
|
||||
if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
|
||||
console.log('\n###', 'clone repository')
|
||||
git()
|
||||
.outputHandler(log)
|
||||
.clone(remoteUrl, localPath)
|
||||
.fetch(['--all'])
|
||||
.exec(copyViewer)
|
||||
.add(['-A'])
|
||||
.commit('updated viewer')
|
||||
.push()
|
||||
} else {
|
||||
console.log('\n###', 'update repository')
|
||||
git()
|
||||
.outputHandler(log)
|
||||
.fetch(['--all'])
|
||||
.reset(['--hard', 'origin/master'])
|
||||
.exec(copyViewer)
|
||||
.add(['-A'])
|
||||
.commit('updated viewer')
|
||||
.push()
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
|
||||
@@ -9,9 +10,8 @@ import { Model, ElementIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export const StripedResidues = CustomElementProperty.create<number>({
|
||||
isStatic: true,
|
||||
label: 'Residue Stripes',
|
||||
name: 'basic-wrapper-residue-striping',
|
||||
display: 'Residue Stripes',
|
||||
getData(model: Model) {
|
||||
const map = new Map<ElementIndex, number>();
|
||||
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
|
||||
@@ -24,7 +24,7 @@ export const StripedResidues = CustomElementProperty.create<number>({
|
||||
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
|
||||
defaultColor: Color(0x777777)
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
return e === 0 ? 'Odd stripe' : 'Even stripe'
|
||||
}
|
||||
})
|
||||
@@ -4,9 +4,9 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin/ui/base';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin/ui/state/update-transform';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
|
||||
|
||||
export class BasicWrapperControls extends PluginUIComponent {
|
||||
|
||||
@@ -19,4 +19,12 @@ export class BasicWrapperControls extends PluginUIComponent {
|
||||
<TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomToastMessage extends PluginUIComponent {
|
||||
render() {
|
||||
return <>
|
||||
Custom <i>Toast</i> content. No timeout.
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -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,22 +29,22 @@ 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) {
|
||||
const query = MS.struct.generator.atomGroups({
|
||||
const expression = MS.struct.generator.atomGroups({
|
||||
'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
|
||||
})
|
||||
return b.apply(StateTransforms.Model.StructureSelection, { query, label: `Chain ${auth_asym_id}` });
|
||||
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Chain ${auth_asym_id}` });
|
||||
}
|
||||
|
||||
export function select(b: StateBuilder.To<PSO.Molecule.Structure>, query: Expression) {
|
||||
return b.apply(StateTransforms.Model.StructureSelection, { query });
|
||||
export function select(b: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression) {
|
||||
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression });
|
||||
}
|
||||
|
||||
export function selectSurroundingsOfFirstResidue(b: StateBuilder.To<PSO.Molecule.Structure>, comp_id: string, radius: number) {
|
||||
const query = MS.struct.modifier.includeSurroundings({
|
||||
const expression = MS.struct.modifier.includeSurroundings({
|
||||
0: MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
|
||||
@@ -53,7 +53,7 @@ export namespace StateHelper {
|
||||
]),
|
||||
radius
|
||||
})
|
||||
return b.apply(StateTransforms.Model.StructureSelection, { query, label: `Surr. ${comp_id} (${radius} ang)` });
|
||||
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Surr. ${comp_id} (${radius} ang)` });
|
||||
}
|
||||
|
||||
export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) {
|
||||
@@ -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>, query: Expression, coloring?: BuiltInColorThemeName) {
|
||||
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
|
||||
visualRoot
|
||||
.apply(StateTransforms.Model.StructureSelection, { query })
|
||||
.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,10 +105,18 @@
|
||||
|
||||
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
|
||||
|
||||
addHeader('Interactivity');
|
||||
addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());
|
||||
addControl('Clear Highlight', () => BasicMolStarWrapper.interactivity.clearHighlight());
|
||||
|
||||
addHeader('Tests');
|
||||
|
||||
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
|
||||
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
|
||||
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
|
||||
|
||||
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
|
||||
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -7,17 +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';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
|
||||
import { CustomToastMessage } from './controls';
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { Script } from '../../mol-script/script';
|
||||
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 }
|
||||
@@ -37,12 +41,15 @@ class BasicWrapper {
|
||||
// left: 'none',
|
||||
// right: BasicWrapperControls
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.Descriptor.name, StripedResidues.colorTheme!);
|
||||
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider);
|
||||
this.plugin.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) {
|
||||
@@ -54,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;
|
||||
}
|
||||
|
||||
@@ -80,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';
|
||||
@@ -90,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 = {
|
||||
@@ -128,30 +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.behaviors;
|
||||
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
},
|
||||
showToasts: () => {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Toast 1',
|
||||
message: 'This is an example text, timeout 3s',
|
||||
key: 'toast-1',
|
||||
timeoutMs: 3000
|
||||
});
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Toast 2',
|
||||
message: CustomToastMessage,
|
||||
key: 'toast-2'
|
||||
});
|
||||
},
|
||||
hideToasts: () => {
|
||||
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) {
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -15,12 +15,13 @@ const readFile = util.promisify(fs.readFile)
|
||||
const writeFile = util.promisify(fs.writeFile)
|
||||
|
||||
import { Progress } from '../../mol-task'
|
||||
import { Database, Table, DatabaseCollection, Column } from '../../mol-data/db'
|
||||
import { Database, Table, DatabaseCollection } from '../../mol-data/db'
|
||||
import { CIF } from '../../mol-io/reader/cif'
|
||||
import { CifWriter } from '../../mol-io/writer/cif'
|
||||
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd'
|
||||
import { SetUtils } from '../../mol-util/set'
|
||||
import { DefaultMap } from '../../mol-util/map'
|
||||
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
|
||||
|
||||
export async function ensureAvailable(path: string, url: string) {
|
||||
if (FORCE_DOWNLOAD || !fs.existsSync(path)) {
|
||||
@@ -74,16 +75,6 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
|
||||
type CCB = Table<CCD_Schema['chem_comp_bond']>
|
||||
type CCA = Table<CCD_Schema['chem_comp_atom']>
|
||||
|
||||
const ChemCompBond_Schema = {
|
||||
comp_id: CCD_Schema['chem_comp_bond'].comp_id,
|
||||
atom_id_1: CCD_Schema['chem_comp_bond'].atom_id_1,
|
||||
atom_id_2: CCD_Schema['chem_comp_bond'].atom_id_2,
|
||||
value_order: CCD_Schema['chem_comp_bond'].value_order,
|
||||
pdbx_aromatic_flag: CCD_Schema['chem_comp_bond'].pdbx_aromatic_flag,
|
||||
pdbx_stereo_config: CCD_Schema['chem_comp_bond'].pdbx_stereo_config,
|
||||
molstar_protonation_variant: Column.Schema.Str()
|
||||
}
|
||||
|
||||
function ccbKey(compId: string, atomId1: string, atomId2: string) {
|
||||
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`
|
||||
}
|
||||
@@ -153,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) {
|
||||
@@ -202,14 +193,14 @@ async function createBonds() {
|
||||
}
|
||||
}
|
||||
|
||||
const bondTable = Table.ofArrays(ChemCompBond_Schema, {
|
||||
const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, {
|
||||
comp_id, atom_id_1, atom_id_2, value_order,
|
||||
pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
|
||||
})
|
||||
|
||||
const bondDatabase = Database.ofTables(
|
||||
TABLE_NAME,
|
||||
{ chem_comp_bond: ChemCompBond_Schema },
|
||||
{ chem_comp_bond: mmCIF_chemCompBond_schema },
|
||||
{ chem_comp_bond: bondTable }
|
||||
)
|
||||
|
||||
@@ -220,20 +211,23 @@ async function run(out: string, binary = false) {
|
||||
const bonds = await createBonds()
|
||||
|
||||
const cif = getEncodedCif(TABLE_NAME, bonds, binary)
|
||||
if (!fs.existsSync(path.dirname(out))) {
|
||||
fs.mkdirSync(path.dirname(out));
|
||||
}
|
||||
writeFile(out, cif)
|
||||
}
|
||||
|
||||
const TABLE_NAME = 'CHEM_COMP_BONDS'
|
||||
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'data')
|
||||
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data')
|
||||
const CCD_PATH = path.join(DATA_DIR, 'components.cif')
|
||||
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif')
|
||||
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.'
|
||||
|
||||
87
src/apps/demos/lighting/index.html
Normal file
87
src/apps/demos/lighting/index.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<title>Mol* Lighting Demo</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 160px;
|
||||
top: 100px;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
top: 100px;
|
||||
left: 780px;
|
||||
}
|
||||
|
||||
#controls > button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
#controls > input, #controls > select {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='controls'></div>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
LightingDemo.init('app')
|
||||
LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
|
||||
|
||||
addHeader('Example PDB IDs');
|
||||
addControl('1M07', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' }));
|
||||
addControl('6HY0', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6HY0.cif', assemblyId: '1' }));
|
||||
addControl('6QVK', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6QVK.cif', assemblyId: '1' }));
|
||||
addControl('1RB8', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1RB8.cif', assemblyId: '1' }));
|
||||
|
||||
addSeparator()
|
||||
|
||||
addHeader('Lighting Presets');
|
||||
addControl('Illustrative', () => LightingDemo.setPreset('illustrative'));
|
||||
addControl('Standard', () => LightingDemo.setPreset('standard'));
|
||||
addControl('Ambient Occlusion', () => LightingDemo.setPreset('occlusion'));
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
function addControl(label, action) {
|
||||
var btn = document.createElement('button');
|
||||
btn.onclick = action;
|
||||
btn.innerText = label;
|
||||
$('controls').appendChild(btn);
|
||||
}
|
||||
|
||||
function addSeparator() {
|
||||
var hr = document.createElement('br');
|
||||
$('controls').appendChild(hr);
|
||||
}
|
||||
|
||||
function addHeader(header) {
|
||||
var h = document.createElement('h3');
|
||||
h.innerText = header;
|
||||
$('controls').appendChild(h);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
178
src/apps/demos/lighting/index.ts
Normal file
178
src/apps/demos/lighting/index.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
|
||||
import './index.html'
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
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';
|
||||
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 }
|
||||
|
||||
const Canvas3DPresets = {
|
||||
illustrative: {
|
||||
multiSample: {
|
||||
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusionEnable: true,
|
||||
occlusionBias: 0.8,
|
||||
occlusionKernelSize: 6,
|
||||
outlineEnable: true,
|
||||
},
|
||||
renderer: {
|
||||
ambientIntensity: 1,
|
||||
lightIntensity: 0,
|
||||
}
|
||||
},
|
||||
occlusion: {
|
||||
multiSample: {
|
||||
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusionEnable: true,
|
||||
occlusionBias: 0.8,
|
||||
occlusionKernelSize: 6,
|
||||
outlineEnable: false,
|
||||
},
|
||||
renderer: {
|
||||
ambientIntensity: 0.4,
|
||||
lightIntensity: 0.6,
|
||||
}
|
||||
},
|
||||
standard: {
|
||||
multiSample: {
|
||||
mode: 'off' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusionEnable: false,
|
||||
outlineEnable: false,
|
||||
},
|
||||
renderer: {
|
||||
ambientIntensity: 0.4,
|
||||
lightIntensity: 0.6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Canvas3DPreset = keyof typeof Canvas3DPresets
|
||||
|
||||
function getPreset(preset: Canvas3DPreset) {
|
||||
switch (preset) {
|
||||
case 'illustrative': return Canvas3DPresets['illustrative']
|
||||
case 'standard': return Canvas3DPresets['standard']
|
||||
case 'occlusion': return Canvas3DPresets['occlusion']
|
||||
}
|
||||
}
|
||||
|
||||
class LightingDemo {
|
||||
plugin: PluginContext;
|
||||
|
||||
init(target: string | HTMLElement) {
|
||||
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
|
||||
...DefaultPluginSpec,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: false,
|
||||
showControls: false
|
||||
},
|
||||
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
|
||||
}
|
||||
});
|
||||
|
||||
this.setPreset('illustrative');
|
||||
}
|
||||
|
||||
setPreset(preset: Canvas3DPreset) {
|
||||
const props = getPreset(preset)
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
|
||||
...props,
|
||||
multiSample: {
|
||||
...this.plugin.canvas3d!.props.multiSample,
|
||||
...props.multiSample
|
||||
},
|
||||
renderer: {
|
||||
...this.plugin.canvas3d!.props.renderer,
|
||||
...props.renderer
|
||||
},
|
||||
postprocessing: {
|
||||
...this.plugin.canvas3d!.props.postprocessing,
|
||||
...props.postprocessing
|
||||
},
|
||||
}});
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
|
||||
}
|
||||
|
||||
private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
|
||||
const parsed = format === 'cif'
|
||||
? 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.StructureFromModel, props, { ref: 'asm' });
|
||||
}
|
||||
|
||||
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
|
||||
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
|
||||
loadType = 'full';
|
||||
} else if (this.loadedParams.url === url) {
|
||||
if (state.select('asm').length > 0) loadType = 'update';
|
||||
}
|
||||
|
||||
let tree: StateBuilder.Root;
|
||||
if (loadType === 'full') {
|
||||
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.StructureFromModel, p => ({ ...p, ...props }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).LightingDemo = new LightingDemo();
|
||||
@@ -112,7 +112,7 @@ const state: State = {
|
||||
|
||||
function formatParams(def: QueryDefinition) {
|
||||
const prms = Object.create(null);
|
||||
for (const p of def.params) {
|
||||
for (const p of def.jsonParams) {
|
||||
prms[p.name] = p.exampleValues ? p.exampleValues[0] : void 0;
|
||||
}
|
||||
return JSON.stringify(prms, void 0, 2);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -19,9 +19,10 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
case 'conditioned': return getParams(param.conditionParams, offset);
|
||||
case 'multi-select': return `Array of ${oToS(param.options)}`;
|
||||
case 'color': return 'Color as 0xrrggbb';
|
||||
case 'color-scale': return `One of ${oToS(param.options)}`;
|
||||
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]`;
|
||||
@@ -30,7 +31,7 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
case 'line-graph': return `A list of 2d vectors [xi, yi][]`;
|
||||
case 'object-list': return `Array of\n${paramInfo(PD.Group(param.element), offset + 2)}`;
|
||||
// TODO: support more languages
|
||||
case 'script-expression': return `An expression in the specified language { language: 'mol-script', expressiong: string }`;
|
||||
case 'script': return `An expression in the specified language { language: 'mol-script', expressiong: string }`;
|
||||
default:
|
||||
const _: never = param;
|
||||
console.warn(`${_} has no associated UI component`);
|
||||
@@ -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(', ');
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import { OrderedSet } from '../../mol-data/int';
|
||||
import { openCif, downloadCif } from './helpers';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
|
||||
import { Sequence } from '../../mol-model/sequence';
|
||||
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
|
||||
async function downloadFromPdb(pdb: string) {
|
||||
@@ -49,8 +52,10 @@ export function residueLabel(model: Model, rI: number) {
|
||||
export function printSecStructure(model: Model) {
|
||||
console.log('\nSecondary Structure\n=============');
|
||||
const { residues } = model.atomicHierarchy;
|
||||
const { key, elements } = model.properties.secondaryStructure;
|
||||
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
|
||||
if (!secondaryStructure) return
|
||||
|
||||
const { key, elements } = secondaryStructure
|
||||
const count = residues._rowCount;
|
||||
let rI = 0;
|
||||
while (rI < count) {
|
||||
@@ -64,14 +69,14 @@ export function printSecStructure(model: Model) {
|
||||
}
|
||||
}
|
||||
|
||||
export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
export function printBonds(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
if (showIntra) {
|
||||
console.log('\nIntra Unit Links\n=============');
|
||||
console.log('\nIntra Unit Bonds\n=============');
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
const elements = unit.elements;
|
||||
const { a, b, edgeCount } = unit.links;
|
||||
const { a, b, edgeCount } = unit.bonds;
|
||||
const { model } = unit;
|
||||
|
||||
if (!edgeCount) continue;
|
||||
@@ -85,20 +90,20 @@ export function printLinks(structure: Structure, showIntra: boolean, showInter:
|
||||
}
|
||||
|
||||
if (showInter) {
|
||||
console.log('\nInter Unit Links\n=============');
|
||||
const links = structure.links;
|
||||
console.log('\nInter Unit Bonds\n=============');
|
||||
const bonds = structure.interUnitBonds;
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
for (const pairLinks of links.getLinkedUnits(unit)) {
|
||||
if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
|
||||
for (const pairBonds of bonds.getConnectedUnits(unit)) {
|
||||
if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
|
||||
|
||||
const { unitA, unitB } = pairLinks;
|
||||
console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
|
||||
const { unitA, unitB } = pairBonds;
|
||||
console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
|
||||
|
||||
for (const aI of pairLinks.linkedElementIndices) {
|
||||
for (const link of pairLinks.getBonds(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
|
||||
for (const aI of pairBonds.connectedIndices) {
|
||||
for (const bond of pairBonds.getEdges(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[bond.indexB])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,21 +115,10 @@ export function printSequence(model: Model) {
|
||||
console.log('\nSequence\n=============');
|
||||
const { byEntityKey } = model.sequence;
|
||||
for (const key of Object.keys(byEntityKey)) {
|
||||
const seq = byEntityKey[+key];
|
||||
console.log(`${seq.entityId} (${seq.sequence.kind} ${seq.num.value(0)} (offset ${seq.sequence.offset}), ${seq.num.value(seq.num.rowCount - 1)}) (${seq.compId.value(0)}, ${seq.compId.value(seq.compId.rowCount - 1)})`);
|
||||
console.log(`${seq.sequence.sequence}`);
|
||||
}
|
||||
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}`);
|
||||
const { sequence, entityId } = byEntityKey[+key];
|
||||
const { seqId, compId } = sequence
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${Sequence.getSequenceString(sequence)}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
@@ -146,7 +140,7 @@ export function printRings(structure: Structure) {
|
||||
|
||||
export function printUnits(structure: Structure) {
|
||||
console.log('\nUnits\n=============');
|
||||
const l = StructureElement.create();
|
||||
const l = StructureElement.Location.create(structure);
|
||||
|
||||
for (const unit of structure.units) {
|
||||
l.unit = unit;
|
||||
@@ -159,14 +153,14 @@ export function printUnits(structure: Structure) {
|
||||
console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`);
|
||||
|
||||
const props = StructureProperties.coarse;
|
||||
const seq = l.unit.model.sequence;
|
||||
const modelSeq = l.unit.model.sequence;
|
||||
|
||||
for (let j = 0, _j = Math.min(size, 3); j < _j; j++) {
|
||||
l.element = OrderedSet.getAt(elements, j);
|
||||
|
||||
const residues: string[] = [];
|
||||
const start = props.seq_id_begin(l), end = props.seq_id_end(l);
|
||||
const compId = seq.byEntityKey[props.entityKey(l)].compId.value;
|
||||
const compId = modelSeq.byEntityKey[props.entityKey(l)].sequence.compId.value;
|
||||
for (let e = start; e <= end; e++) residues.push(compId(e));
|
||||
console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`);
|
||||
}
|
||||
@@ -177,7 +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(', ')}`);
|
||||
@@ -212,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]);
|
||||
}
|
||||
|
||||
@@ -240,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 {
|
||||
@@ -254,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',
|
||||
|
||||
228
src/apps/viewer/extensions/cellpack/curve.ts
Normal file
228
src/apps/viewer/extensions/cellpack/curve.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Ludovic Autin <autin@scripps.edu>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Quat, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { NumberArray } from '../../../../mol-util/type-helpers';
|
||||
|
||||
interface Frame {
|
||||
t: Vec3,
|
||||
r: Vec3,
|
||||
s: Vec3,
|
||||
}
|
||||
|
||||
const a0Tmp = Vec3()
|
||||
const a1Tmp = Vec3()
|
||||
const a2Tmp = Vec3()
|
||||
const a3Tmp = Vec3()
|
||||
function CubicInterpolate(out: Vec3, y0: Vec3, y1: Vec3, y2: Vec3, y3: Vec3, mu: number): Vec3 {
|
||||
const mu2 = mu * mu;
|
||||
Vec3.sub(a0Tmp, y3, y2)
|
||||
Vec3.sub(a0Tmp, a0Tmp, y0)
|
||||
Vec3.add(a0Tmp, a0Tmp, y1)
|
||||
|
||||
Vec3.sub(a1Tmp, y0, y1)
|
||||
Vec3.sub(a1Tmp, a1Tmp, a0Tmp)
|
||||
|
||||
Vec3.sub(a2Tmp, y2, y0)
|
||||
|
||||
Vec3.copy(a3Tmp, y1)
|
||||
|
||||
out[0] = a0Tmp[0] * mu * mu2 + a1Tmp[0] * mu2 + a2Tmp[0] * mu + a3Tmp[0]
|
||||
out[1] = a0Tmp[1] * mu * mu2 + a1Tmp[1] * mu2 + a2Tmp[1] * mu + a3Tmp[1]
|
||||
out[2] = a0Tmp[2] * mu * mu2 + a1Tmp[2] * mu2 + a2Tmp[2] * mu + a3Tmp[2]
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
const cp0 = Vec3()
|
||||
const cp1 = Vec3()
|
||||
const cp2 = Vec3()
|
||||
const cp3 = Vec3()
|
||||
const currentPosition = Vec3()
|
||||
function ResampleControlPoints(points: NumberArray, segmentLength: number) {
|
||||
const nP = points.length / 3
|
||||
// insert a point at the end and at the begining
|
||||
// controlPoints.Insert(0, controlPoints[0] + (controlPoints[0] - controlPoints[1]) / 2.0f);
|
||||
// controlPoints.Add(controlPoints[nP - 1] + (controlPoints[nP - 1] - controlPoints[nP - 2]) / 2.0f);
|
||||
|
||||
let resampledControlPoints: Vec3[] = []
|
||||
// resampledControlPoints.Add(controlPoints[0]);
|
||||
// resampledControlPoints.Add(controlPoints[1]);
|
||||
|
||||
let idx = 1
|
||||
// const currentPosition = Vec3.create(points[idx * 3], points[idx * 3 + 1], points[idx * 3 + 2])
|
||||
Vec3.fromArray(currentPosition, points, idx * 3)
|
||||
|
||||
let lerpValue = 0.0
|
||||
|
||||
// Normalize the distance between control points
|
||||
while (true) {
|
||||
if (idx + 2 >= nP) break
|
||||
Vec3.fromArray(cp0, points, (idx - 1) * 3)
|
||||
Vec3.fromArray(cp1, points, idx * 3)
|
||||
Vec3.fromArray(cp2, points, (idx + 1) * 3)
|
||||
Vec3.fromArray(cp3, points, (idx + 2) * 3)
|
||||
// const cp0 = Vec3.create(points[(idx-1)*3], points[(idx-1)*3+1], points[(idx-1)*3+2]) // controlPoints[currentPointId - 1];
|
||||
// const cp1 = Vec3.create(points[idx*3], points[idx*3+1], points[idx*3+2]) // controlPoints[currentPointId];
|
||||
// const cp2 = Vec3.create(points[(idx+1)*3], points[(idx+1)*3+1], points[(idx+1)*3+2]) // controlPoints[currentPointId + 1];
|
||||
// const cp3 = Vec3.create(points[(idx+2)*3], points[(idx+2)*3+1], points[(idx+2)*3+2]); // controlPoints[currentPointId + 2];
|
||||
let found = false
|
||||
for (; lerpValue <= 1; lerpValue += 0.01) {
|
||||
// lerp?slerp
|
||||
// let candidate:Vec3 = Vec3.lerp(Vec3.zero(), cp0, cp1, lerpValue);
|
||||
// const candidate:Vec3 = Vec3.bezier(Vec3.zero(), cp0, cp1, cp2, cp3, lerpValue);
|
||||
const candidate = CubicInterpolate(Vec3(), cp0, cp1, cp2, cp3, lerpValue)
|
||||
const d = Vec3.distance(currentPosition, candidate);
|
||||
if (d > segmentLength) {
|
||||
resampledControlPoints.push(candidate)
|
||||
Vec3.copy(currentPosition, candidate)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
lerpValue = 0
|
||||
idx += 1
|
||||
}
|
||||
}
|
||||
return resampledControlPoints
|
||||
}
|
||||
|
||||
|
||||
const prevV = Vec3()
|
||||
const tmpV1 = Vec3()
|
||||
const tmpV2 = Vec3()
|
||||
const tmpV3 = Vec3()
|
||||
|
||||
// easier to align to theses normals
|
||||
function GetSmoothNormals(points: Vec3[]) {
|
||||
const nP: number = points.length;
|
||||
const smoothNormals: Vec3[] = []
|
||||
if (points.length < 3) {
|
||||
for (let i = 0; i < points.length; ++i)
|
||||
smoothNormals.push(Vec3.normalize(Vec3(), points[i]))
|
||||
return smoothNormals;
|
||||
}
|
||||
let p0 = points[0]
|
||||
let p1 = points[1]
|
||||
let p2 = points[2]
|
||||
const p21 = Vec3.sub(tmpV1, p2, p1)
|
||||
const p01 = Vec3.sub(tmpV2, p0, p1)
|
||||
const p0121 = Vec3.cross(tmpV3, p01, p21)
|
||||
Vec3.normalize(prevV, p0121)
|
||||
smoothNormals.push(Vec3.clone(prevV))
|
||||
for (let i = 1; i < points.length - 1; ++i) {
|
||||
p0 = points[i - 1]
|
||||
p1 = points[i]
|
||||
p2 = points[i + 1]
|
||||
const t = Vec3.normalize(tmpV1, Vec3.sub(tmpV1, p2 , p0))
|
||||
const b = Vec3.normalize(tmpV2, Vec3.cross(tmpV2, t, prevV))
|
||||
const n = Vec3.normalize(Vec3(), Vec3.cross(tmpV3, t, b))
|
||||
Vec3.negate(n, n)
|
||||
Vec3.copy(prevV, n)
|
||||
smoothNormals.push(n)
|
||||
}
|
||||
const last = Vec3()
|
||||
Vec3.normalize(last, Vec3.cross(last,
|
||||
Vec3.sub(tmpV1, points[nP - 3], points[nP-2]),
|
||||
Vec3.sub(tmpV2, points[nP-2] , points[nP-1]))
|
||||
)
|
||||
smoothNormals.push(last)
|
||||
return smoothNormals;
|
||||
}
|
||||
|
||||
const frameTmpV1 = Vec3()
|
||||
const frameTmpV2 = Vec3()
|
||||
const frameTmpV3 = Vec3()
|
||||
|
||||
function getFrame(reference: Vec3, tangent: Vec3) {
|
||||
const t = Vec3.normalize(Vec3(), tangent);
|
||||
// make reference vector orthogonal to tangent
|
||||
const proj_r_to_t = Vec3.scale(
|
||||
frameTmpV1, tangent, Vec3.dot(reference, tangent) / Vec3.dot(tangent, tangent)
|
||||
)
|
||||
const r = Vec3.normalize(Vec3(), Vec3.sub(frameTmpV2, reference, proj_r_to_t))
|
||||
// make bitangent vector orthogonal to the others
|
||||
const s = Vec3.normalize(Vec3(), Vec3.cross(frameTmpV3, t, r))
|
||||
return { t, r, s }
|
||||
}
|
||||
|
||||
const mfTmpV1 = Vec3()
|
||||
const mfTmpV2 = Vec3()
|
||||
const mfTmpV3 = Vec3()
|
||||
const mfTmpV4 = Vec3()
|
||||
const mfTmpV5 = Vec3()
|
||||
const mfTmpV6 = Vec3()
|
||||
const mfTmpV7 = Vec3()
|
||||
const mfTmpV8 = Vec3()
|
||||
const mfTmpV9 = Vec3()
|
||||
|
||||
// easier to align to theses normals
|
||||
// https://github.com/bzamecnik/gpg/blob/master/rotation-minimizing-frame/rmf.py
|
||||
function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
|
||||
const frames: Frame[] = [];
|
||||
const t0 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[1], points[0]))
|
||||
frames.push(getFrame(normals[0], t0))
|
||||
|
||||
for (let i = 0; i< points.length-2; ++i) {
|
||||
const t2 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[i+2], points[i+1]))
|
||||
const v1 = Vec3.sub(mfTmpV2, points[i + 1], points[i]) // this is tangeant
|
||||
const c1 = Vec3.dot(v1, v1)
|
||||
// compute r_i^L = R_1 * r_i
|
||||
const v1r = Vec3.scale(mfTmpV3, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].r))
|
||||
const ref_L_i = Vec3.sub(mfTmpV4, frames[i].r, v1r)
|
||||
// compute t_i^L = R_1 * t_i
|
||||
const v1t = Vec3.scale(mfTmpV5, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].t))
|
||||
const tan_L_i = Vec3.sub(mfTmpV6, frames[i].t, v1t)
|
||||
// # compute reflection vector of R_2
|
||||
const v2 = Vec3.sub(mfTmpV7, t2 , tan_L_i)
|
||||
const c2 = Vec3.dot(v2, v2)
|
||||
// compute r_(i+1) = R_2 * r_i^L
|
||||
const v2l = Vec3.scale(mfTmpV8, v1, (2.0/c2) * Vec3.dot(v2, ref_L_i))
|
||||
const ref_next = Vec3.sub(mfTmpV9, ref_L_i, v2l) // ref_L_i - (2 / c2) * v2.dot(ref_L_i) * v2
|
||||
frames.push(getFrame(ref_next, t2)) // frames.append(Frame(ref_next, tangents[i+1]))
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
const rpTmpVec1 = Vec3()
|
||||
|
||||
export function getMatFromResamplePoints(points: NumberArray) {
|
||||
const segmentLength = 3.4
|
||||
const new_points = ResampleControlPoints(points, 3.4)
|
||||
const npoints = new_points.length
|
||||
const new_normal = GetSmoothNormals(new_points)
|
||||
const frames = GetMiniFrame(new_points, new_normal)
|
||||
const limit = npoints
|
||||
const transforms: Mat4[] = []
|
||||
const pti = Vec3.copy(rpTmpVec1, new_points[0]);
|
||||
// console.log(new_points.length)
|
||||
// console.log(points.length/3)
|
||||
// console.log(limit)
|
||||
// console.log(segmentLength)
|
||||
for (let i = 0; i<npoints-2; ++i) {
|
||||
const pti1: Vec3 = new_points[i+1] // Vec3.create(points[(i+1)*3],points[(i+1)*3+1],points[(i+1)*3+2]);
|
||||
const d = Vec3.distance(pti, pti1)
|
||||
if (d >= segmentLength) {
|
||||
// use twist or random?
|
||||
const quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t) // Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),new_normal[i]);//Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),direction);new_normal
|
||||
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random()*3.60 ) // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
|
||||
const m = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat)) // Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),quat1,quat2));//Mat4.fromQuat(Mat4.zero(),quat);//Mat4.identity();//Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),rq,quat));
|
||||
// let pos:Vec3 = Vec3.add(Vec3.zero(),pti1,pti)
|
||||
// pos = Vec3.scale(pos,pos,1.0/2.0);
|
||||
// Vec3.makeRotation(Mat4.zero(),Vec3.create(0,0,1),frames[i].t);//
|
||||
Mat4.setTranslation(m, pti1)
|
||||
// let m2:Mat4 = GetTubePropertiesMatrix(pti,pti1);
|
||||
// let q:Quat = Quat.rotationTo(Quat.zero(), Vec3.create(0,1,0),Vec3.create(0,0,1))
|
||||
// m2=Mat4.mul(Mat4.identity(),Mat4.fromQuat(Mat4.zero(),q),m2);
|
||||
transforms.push(m)
|
||||
Vec3.copy(pti, pti1)
|
||||
}
|
||||
if (transforms.length >= limit) break
|
||||
}
|
||||
return transforms
|
||||
}
|
||||
62
src/apps/viewer/extensions/cellpack/data.ts
Normal file
62
src/apps/viewer/extensions/cellpack/data.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Quat } from '../../../../mol-math/linear-algebra';
|
||||
|
||||
export interface CellPack {
|
||||
cell: Cell
|
||||
packings: CellPacking[]
|
||||
}
|
||||
|
||||
export interface CellPacking {
|
||||
name: string,
|
||||
location: 'surface' | 'interior' | 'cytoplasme',
|
||||
ingredients: Packing['ingredients']
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export interface Cell {
|
||||
recipe: Recipe
|
||||
cytoplasme?: Packing
|
||||
compartments?: { [key: string]: Compartment }
|
||||
}
|
||||
|
||||
export interface Recipe {
|
||||
setupfile: string
|
||||
/** First entry is name, secound is path: [name: string, path: string][] */
|
||||
paths: [string, string][]
|
||||
version: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface Compartment {
|
||||
surface?: Packing
|
||||
interior?: Packing
|
||||
}
|
||||
|
||||
export interface Packing {
|
||||
ingredients: { [key: string]: Ingredient }
|
||||
}
|
||||
|
||||
export interface Ingredient {
|
||||
source: IngredientSource
|
||||
results: [Vec3, Quat][]
|
||||
name: string
|
||||
positions?: [Vec3[]] // why wrapped in an extra array?
|
||||
radii?: [number[]] // why wrapped in an extra array?
|
||||
|
||||
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
|
||||
nbCurve?: number
|
||||
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
|
||||
[curveX: string]: unknown
|
||||
}
|
||||
|
||||
export interface IngredientSource {
|
||||
pdb: string
|
||||
transform: { center: boolean, translate?: Vec3 }
|
||||
biomt?: boolean
|
||||
}
|
||||
469
src/apps/viewer/extensions/cellpack/model.ts
Normal file
469
src/apps/viewer/extensions/cellpack/model.ts
Normal file
@@ -0,0 +1,469 @@
|
||||
/**
|
||||
* 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 { 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, 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 { 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';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
|
||||
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 { 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}`
|
||||
}
|
||||
|
||||
async function getModel(id: string, baseUrl: string) {
|
||||
let model: Model;
|
||||
if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
// return
|
||||
const cif = await getFromPdb(id)
|
||||
model = (await trajectoryFromMmCIF(cif).run())[0]
|
||||
} else {
|
||||
const pdb = await getFromCellPackDB(id, baseUrl)
|
||||
model = (await trajectoryFromPDB(pdb).run())[0]
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
async function getStructure(model: Model, props: { assembly?: string } = {}) {
|
||||
let structure = Structure.ofModel(model)
|
||||
const { assembly } = props
|
||||
|
||||
if (assembly) {
|
||||
structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
|
||||
}
|
||||
|
||||
const query = MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
|
||||
})
|
||||
])
|
||||
const compiled = compile<StructureSelection>(query)
|
||||
const result = compiled(new QueryContext(structure))
|
||||
structure = StructureSelection.unionStructure(result)
|
||||
|
||||
return structure
|
||||
}
|
||||
|
||||
function getTransform(trans: Vec3, rot: Quat) {
|
||||
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
|
||||
Mat4.transpose(m, m)
|
||||
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0))
|
||||
Mat4.setTranslation(m, trans)
|
||||
return m
|
||||
}
|
||||
|
||||
function getResultTransforms(results: Ingredient['results']) {
|
||||
return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
|
||||
}
|
||||
|
||||
function getCurveTransforms(ingredient: Ingredient) {
|
||||
const n = ingredient.nbCurve || 0
|
||||
const instances: Mat4[] = []
|
||||
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const cname = `curve${i}`
|
||||
if (!(cname in ingredient)) {
|
||||
// console.warn(`Expected '${cname}' in ingredient`)
|
||||
continue
|
||||
}
|
||||
const _points = ingredient[cname] as Vec3[]
|
||||
if (_points.length <= 2) {
|
||||
// TODO handle curve with 2 or less points
|
||||
continue
|
||||
}
|
||||
const points = new Float32Array(_points.length * 3)
|
||||
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
|
||||
const newInstances = getMatFromResamplePoints(points)
|
||||
instances.push(...newInstances)
|
||||
}
|
||||
|
||||
return instances
|
||||
}
|
||||
|
||||
function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
const builder = Structure.Builder()
|
||||
const { units } = structure;
|
||||
|
||||
for (let i = 0, il = transforms.length; i < il; ++i) {
|
||||
const id = `${i + 1}`
|
||||
const op = SymmetryOperator.create(id, transforms[i], { id, operList: [ id ] })
|
||||
for (const unit of units) {
|
||||
builder.addWithOperator(unit, op)
|
||||
}
|
||||
}
|
||||
|
||||
return builder.getStructure();
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
|
||||
|
||||
const { db } = model.sourceData.data
|
||||
const d = db.atom_site
|
||||
const n = d._rowCount
|
||||
const rowCount = n * transforms.length
|
||||
|
||||
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
|
||||
|
||||
const x = d.Cartn_x.toArray()
|
||||
const y = d.Cartn_y.toArray()
|
||||
const z = d.Cartn_z.toArray()
|
||||
|
||||
const Cartn_x = new Float32Array(rowCount)
|
||||
const Cartn_y = new Float32Array(rowCount)
|
||||
const Cartn_z = new Float32Array(rowCount)
|
||||
const map = new Uint32Array(rowCount)
|
||||
const seq = new Int32Array(rowCount)
|
||||
let offset = 0
|
||||
for (let c = 0; c < count; ++c) {
|
||||
const cStart = offsets[c]
|
||||
const cEnd = offsets[c + 1]
|
||||
const cLength = cEnd - cStart
|
||||
for (let t = 0, tl = transforms.length; t < tl; ++t) {
|
||||
const m = transforms[t]
|
||||
for (let j = cStart; j < cEnd; ++j) {
|
||||
const i = offset + j - cStart
|
||||
const xj = x[j], yj = y[j], zj = z[j]
|
||||
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
|
||||
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
|
||||
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
|
||||
map[i] = j
|
||||
seq[i] = t + 1
|
||||
}
|
||||
offset += cLength
|
||||
}
|
||||
}
|
||||
|
||||
function multColumn<T>(column: Column<T>) {
|
||||
const array = column.toArray()
|
||||
return Column.ofLambda({
|
||||
value: row => array[map[row]],
|
||||
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
|
||||
rowCount, schema: column.schema
|
||||
})
|
||||
}
|
||||
|
||||
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
|
||||
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
|
||||
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
|
||||
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
|
||||
auth_seq_id: CifField.ofNumbers(seq),
|
||||
|
||||
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
|
||||
Cartn_x: CifField.ofNumbers(Cartn_x),
|
||||
Cartn_y: CifField.ofNumbers(Cartn_y),
|
||||
Cartn_z: CifField.ofNumbers(Cartn_z),
|
||||
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
|
||||
id: CifField.ofColumn(Column.ofLambda({
|
||||
value: row => row,
|
||||
areValuesEqual: (rowA, rowB) => rowA === rowB,
|
||||
rowCount, schema: d.id.schema,
|
||||
})),
|
||||
|
||||
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
|
||||
|
||||
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
|
||||
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
|
||||
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
|
||||
label_seq_id: CifField.ofNumbers(seq),
|
||||
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
|
||||
|
||||
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
|
||||
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
|
||||
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
|
||||
}
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', db.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
}
|
||||
|
||||
return {
|
||||
header: name,
|
||||
categoryNames: Object.keys(categories),
|
||||
categories
|
||||
};
|
||||
}
|
||||
|
||||
async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model)
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = MmcifFormat.fromFrame(cif)
|
||||
const models = await createModels(format.data.db, format, ctx)
|
||||
return models[0]
|
||||
})
|
||||
|
||||
const curveModel = await curveModelTask.run()
|
||||
return getStructure(curveModel)
|
||||
}
|
||||
|
||||
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const { name, source, results, nbCurve } = ingredient
|
||||
|
||||
// TODO can these be added to the library?
|
||||
if (name === 'HIV1_CAhex_0_1_0') return
|
||||
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
|
||||
if (name === 'iLDL') return
|
||||
if (name === 'peptides') return
|
||||
if (name === 'lypoglycane') return
|
||||
|
||||
if (source.pdb === 'None') return
|
||||
|
||||
const model = await getModel(source.pdb || name, baseUrl)
|
||||
if (!model) return
|
||||
|
||||
if (nbCurve) {
|
||||
return getCurve(name, getCurveTransforms(ingredient), model)
|
||||
} else {
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
return getAssembly(getResultTransforms(results), structure)
|
||||
}
|
||||
}
|
||||
|
||||
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
|
||||
return Task.create('Create Packing Structure', async ctx => {
|
||||
const { ingredients, name } = packing
|
||||
const structures: Structure[] = []
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) await ctx.update(iName)
|
||||
const s = await getIngredientStructure(ingredients[iName], baseUrl)
|
||||
if (s) structures.push(s)
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
|
||||
const builder = Structure.Builder({ label: name })
|
||||
let offsetInvariantId = 0
|
||||
for (const s of structures) {
|
||||
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
|
||||
let maxInvariantId = 0
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId
|
||||
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId
|
||||
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId)
|
||||
}
|
||||
offsetInvariantId += maxInvariantId
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
|
||||
const s = builder.getStructure()
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
const RepresentationOptions = PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'] as const)
|
||||
type RepresentationName = (typeof RepresentationOptions)[0][0]
|
||||
|
||||
export const LoadCellPackModel = StateAction.build({
|
||||
display: { name: 'Load CellPack Model' },
|
||||
params: {
|
||||
id: PD.Select('influenza_model1.json', [
|
||||
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
|
||||
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
['curveTest', 'Curve Test'],
|
||||
] as const),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
preset: PD.Group({
|
||||
traceOnly: PD.Boolean(false),
|
||||
representation: PD.Select('spacefill', RepresentationOptions)
|
||||
}, { isExpanded: true })
|
||||
},
|
||||
from: PSO.Root
|
||||
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
|
||||
const url = getCellPackModelUrl(params.id, params.baseUrl)
|
||||
|
||||
const root = state.build().toRoot();
|
||||
|
||||
let cellPackBuilder: any
|
||||
|
||||
if (params.id === 'curveTest') {
|
||||
const url = `${params.baseUrl}/extras/rna_allpoints.json`
|
||||
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
|
||||
const { points } = await (new Response(data)).json() as { points: number[] }
|
||||
const curve0: Vec3[] = []
|
||||
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j))
|
||||
}
|
||||
const cell: Cell = {
|
||||
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
|
||||
compartments: {
|
||||
'CurveCompartment': {
|
||||
interior: {
|
||||
ingredients: {
|
||||
'CurveIngredient': {
|
||||
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
|
||||
results: [],
|
||||
name: 'RNA',
|
||||
nbCurve: 1,
|
||||
curve0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
} else {
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
|
||||
const { packings } = cellPackObject.data
|
||||
const tree = state.build().to(cellPackBuilder.ref);
|
||||
|
||||
const isHiv = (
|
||||
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
|
||||
params.id === 'HIV-1_0.1.6-8_mixed_radii_pdb.cpr'
|
||||
)
|
||||
|
||||
if (isHiv) {
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
|
||||
const url = `${params.baseUrl}/extras/rna_allpoints.json`
|
||||
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
|
||||
const { points } = await (new Response(data)).json() as { points: number[] }
|
||||
|
||||
const curve0: Vec3[] = []
|
||||
for (let j = 0, jl = points.length; j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j))
|
||||
}
|
||||
packings[i].ingredients['RNA'] = {
|
||||
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
|
||||
results: [],
|
||||
name: 'RNA',
|
||||
nbCurve: 1,
|
||||
curve0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const colors = distinctColors(packings.length)
|
||||
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
const hcl = Hcl.fromColor(Hcl(), colors[i])
|
||||
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number]
|
||||
const p = { packing: i, baseUrl: params.baseUrl }
|
||||
|
||||
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'])
|
||||
])
|
||||
})
|
||||
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } }) as any
|
||||
}
|
||||
cellpackTree
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(ctx, Structure.Empty, {
|
||||
...getReprParams(ctx, params.preset),
|
||||
...getColorParams(hue)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (isHiv) {
|
||||
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
|
||||
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(ctx, Structure.Empty, {
|
||||
...getReprParams(ctx, params.preset),
|
||||
color: UniformColorThemeProvider
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
console.time('cellpack')
|
||||
await state.updateTree(tree).runInContext(taskCtx);
|
||||
console.timeEnd('cellpack')
|
||||
}));
|
||||
|
||||
function getReprParams(ctx: PluginContext, params: { representation: RepresentationName, traceOnly: boolean }) {
|
||||
const { representation, traceOnly } = params
|
||||
switch (representation) {
|
||||
case 'spacefill':
|
||||
return traceOnly
|
||||
? {
|
||||
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 {
|
||||
type: ctx.representation.structure.registry.get('gaussian-surface'),
|
||||
typeParams: {
|
||||
quality: 'custom', resolution: 10, radiusOffset: 2,
|
||||
alpha: 1.0, flatShaded: false, doubleSided: false,
|
||||
ignoreHydrogens: true
|
||||
}
|
||||
}
|
||||
case 'point':
|
||||
return { type: ctx.representation.structure.registry.get('point') }
|
||||
case 'ellipsoid':
|
||||
return { type: ctx.representation.structure.registry.get('orientation') }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/apps/viewer/extensions/cellpack/state.ts
Normal file
74
src/apps/viewer/extensions/cellpack/state.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
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';
|
||||
import { createStructureFromCellPack } from './model';
|
||||
|
||||
// export const DefaultCellPackBaseUrl = 'https://cdn.jsdelivr.net/gh/mesoscope/cellPACK_data@master/cellPACK_database_1.1.0/'
|
||||
export const DefaultCellPackBaseUrl = 'https://mgldev.scripps.edu/projects/autoPACK/web/cellpackproject/'
|
||||
|
||||
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
|
||||
|
||||
export { ParseCellPack }
|
||||
type ParseCellPack = typeof ParseCellPack
|
||||
const ParseCellPack = PluginStateTransform.BuiltIn({
|
||||
name: 'parse-cellpack',
|
||||
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
|
||||
from: PSO.Format.Json,
|
||||
to: CellPack
|
||||
})({
|
||||
apply({ a }) {
|
||||
return Task.create('Parse CellPack', async ctx => {
|
||||
const cell = a.data as Cell
|
||||
|
||||
const packings: CellPacking[] = []
|
||||
const { compartments, cytoplasme } = cell
|
||||
if (compartments) {
|
||||
for (const name in compartments) {
|
||||
const { surface, interior } = compartments[name]
|
||||
if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients })
|
||||
if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients })
|
||||
}
|
||||
}
|
||||
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients })
|
||||
|
||||
return new CellPack({ cell, packings });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export { StructureFromCellpack }
|
||||
type StructureFromCellpack = typeof ParseCellPack
|
||||
const StructureFromCellpack = PluginStateTransform.BuiltIn({
|
||||
name: 'structure-from-cellpack',
|
||||
display: { name: 'Structure from CellPack', description: 'Create Structure from CellPack Packing' },
|
||||
from: CellPack,
|
||||
to: PSO.Molecule.Structure,
|
||||
params: a => {
|
||||
if (!a) {
|
||||
return {
|
||||
packing: PD.Numeric(0, {}, { description: 'Packing Index' }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl)
|
||||
};
|
||||
}
|
||||
const options = a.data.packings.map((d, i) => [i, d.name] as [number, string])
|
||||
return {
|
||||
packing: PD.Select(0, options),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl)
|
||||
}
|
||||
}
|
||||
})({
|
||||
apply({ a, params }) {
|
||||
return Task.create('Structure from CellPack', async ctx => {
|
||||
const packing = a.data.packings[params.packing]
|
||||
const structure = await createStructureFromCellPack(packing, params.baseUrl).runInContext(ctx)
|
||||
return new PSO.Molecule.Structure(structure, { label: packing.name })
|
||||
});
|
||||
}
|
||||
});
|
||||
48
src/apps/viewer/extensions/cellpack/util.ts
Normal file
48
src/apps/viewer/extensions/cellpack/util.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CIF } from '../../../../mol-io/reader/cif'
|
||||
import { parsePDB } from '../../../../mol-io/reader/pdb/parser';
|
||||
|
||||
async function parseCif(data: string|Uint8Array) {
|
||||
const comp = CIF.parse(data);
|
||||
const parsed = await comp.run();
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function parsePDBfile(data: string, id: string) {
|
||||
const comp = parsePDB(data, id);
|
||||
const parsed = await comp.run();
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function downloadCif(url: string, isBinary: boolean) {
|
||||
const data = await fetch(url);
|
||||
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
|
||||
}
|
||||
|
||||
async function downloadPDB(url: string, id: string) {
|
||||
const data = await fetch(url);
|
||||
return parsePDBfile(await data.text(), id);
|
||||
}
|
||||
|
||||
export async function getFromPdb(id: string) {
|
||||
const parsed = await downloadCif(`https://files.rcsb.org/download/${id}.cif`, false);
|
||||
return parsed.blocks[0];
|
||||
}
|
||||
|
||||
function getCellPackDataUrl(id: string, baseUrl: string) {
|
||||
const url = `${baseUrl}/other/${id}`
|
||||
return url.endsWith('.pdb') ? url : `${url}.pdb`
|
||||
}
|
||||
|
||||
export async function getFromCellPackDB(id: string, baseUrl: string) {
|
||||
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
|
||||
return parsed;
|
||||
}
|
||||
@@ -1,171 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
// /**
|
||||
// * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
// *
|
||||
// * @author David Sehnal <david.sehnal@gmail.com>
|
||||
// */
|
||||
|
||||
import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
|
||||
import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { PluginStateObject } from '../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition } from '../../../mol-util/param-definition';
|
||||
import { PluginCommands } from '../../../mol-plugin/command';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
|
||||
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
import { UUID } from '../../../mol-util';
|
||||
import { ColorNames } from '../../../mol-util/color/tables';
|
||||
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 query = createQuery([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.StructureSelection, { query, 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.StructureSelection, { query: MS.struct.modifier.wholeResidues([query]), 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.StructureSelection, { query: createQuery(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.StructureSelection, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l. || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
// }
|
||||
return PluginStateSnapshotManager.Entry({
|
||||
id: UUID.create22(),
|
||||
data: { tree: StateTree.toJSON(b.getTree()) },
|
||||
camera: {
|
||||
current: getCameraSnapshot(params.e.camera),
|
||||
transitionStyle: 'animate',
|
||||
transitionDurationInMs: 350
|
||||
}
|
||||
}, {
|
||||
name: params.e.text
|
||||
});
|
||||
}
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
|
||||
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
const direction = Vec3.sub(Vec3.zero(), e.pos, e.in);
|
||||
Vec3.normalize(direction, direction);
|
||||
const up = Vec3.sub(Vec3.zero(), e.pos, e.up);
|
||||
Vec3.normalize(up, up);
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// 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',
|
||||
position: Vec3.scaleAndAdd(Vec3.zero(), e.pos, direction, e.slab.zoom),
|
||||
target: e.pos,
|
||||
direction,
|
||||
up,
|
||||
near: e.slab.zoom + e.slab.z_front,
|
||||
far: e.slab.zoom + e.slab.z_back,
|
||||
fogNear: e.slab.zoom + e.slab.z_front,
|
||||
fogFar: e.slab.zoom + e.slab.z_back,
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1
|
||||
};
|
||||
return s;
|
||||
}
|
||||
// function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
// const direction = Vec3.sub(Vec3(), e.pos, e.in);
|
||||
// Vec3.normalize(direction, direction);
|
||||
// const up = Vec3.sub(Vec3(), e.pos, e.up);
|
||||
// Vec3.normalize(up, up);
|
||||
|
||||
function createQuery(atomIndices: number[]) {
|
||||
if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
// const s: Camera.Snapshot = {
|
||||
// mode: 'perspective',
|
||||
// fov: Math.PI / 4,
|
||||
// position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
// target: e.pos,
|
||||
// radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
// fog: 50,
|
||||
// up,
|
||||
// };
|
||||
// return s;
|
||||
// }
|
||||
|
||||
return MS.struct.generator.atomGroups({
|
||||
'atom-test': atomIndices.length === 1
|
||||
? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
: MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
'group-by': 0
|
||||
});
|
||||
}
|
||||
// function createExpression(atomIndices: number[]) {
|
||||
// if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
|
||||
// return MS.struct.generator.atomGroups({
|
||||
// 'atom-test': atomIndices.length === 1
|
||||
// ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
// : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
// 'group-by': 0
|
||||
// });
|
||||
// }
|
||||
BIN
src/apps/viewer/favicon.ico
Executable file
BIN
src/apps/viewer/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>Mol* Viewer</title>
|
||||
<style>
|
||||
* {
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import '../../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';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
import { LoadCellPackModel } from './extensions/cellpack/model';
|
||||
import { StructureFromCellpack } from './extensions/cellpack/state';
|
||||
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');
|
||||
@@ -21,7 +26,12 @@ const hideControls = getParam('hide-controls', `[^&]+`) === '1';
|
||||
|
||||
function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [...DefaultPluginSpec.actions, PluginSpec.Action(CreateJoleculeState)],
|
||||
actions: [
|
||||
...DefaultPluginSpec.actions,
|
||||
// PluginSpec.Action(CreateJoleculeState),
|
||||
PluginSpec.Action(LoadCellPackModel),
|
||||
PluginSpec.Action(StructureFromCellpack),
|
||||
],
|
||||
behaviors: [...DefaultPluginSpec.behaviors],
|
||||
animations: [...DefaultPluginSpec.animations || []],
|
||||
customParamEditors: DefaultPluginSpec.customParamEditors,
|
||||
@@ -29,11 +39,16 @@ function init() {
|
||||
initial: {
|
||||
isExpanded: true,
|
||||
showControls: !hideControls
|
||||
},
|
||||
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) {
|
||||
@@ -45,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) {
|
||||
const id = model.label.toLowerCase();
|
||||
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await req.json();
|
||||
label: 'Evolutionary Conservation',
|
||||
type: 'static',
|
||||
async getData(model: Model, ctx: CustomProperty.Context) {
|
||||
const id = model.entryId.toLowerCase();
|
||||
const url = `https://proteopedia.org/cgi-bin/cnsrf?${id}`
|
||||
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
|
||||
const annotations = (json && json.residueAnnotations) || [];
|
||||
|
||||
const conservationMap = new Map<string, number>();
|
||||
@@ -65,7 +67,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
},
|
||||
defaultColor: EvolutionaryConservationDefaultColor
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
|
||||
return e ? `Evolutionary Conservation: ${e}` : void 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
== v3.4 ==
|
||||
|
||||
* Fixed HET group reset.
|
||||
* Updated core.
|
||||
* Removed Camera Cliping.
|
||||
|
||||
== v3.3 ==
|
||||
|
||||
* Camera Clipping.
|
||||
|
||||
== v3.2 ==
|
||||
|
||||
* Fixed assembly loading.
|
||||
* Better HET group focus.
|
||||
|
||||
== v3.0 ==
|
||||
|
||||
* Fixed initial camera zoom.
|
||||
|
||||
@@ -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.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) {
|
||||
@@ -67,11 +67,11 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
if (StructureElement.isLocation(location)) {
|
||||
if (StructureElement.Location.is(location)) {
|
||||
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[] }[],
|
||||
@@ -18,9 +19,9 @@ export interface ModelInfo {
|
||||
|
||||
export namespace ModelInfo {
|
||||
async function getPreferredAssembly(ctx: PluginContext, model: Model) {
|
||||
if (model.label.length <= 3) return void 0;
|
||||
if (model.entryId.length <= 3) return void 0;
|
||||
try {
|
||||
const id = model.label.toLowerCase();
|
||||
const id = model.entryId.toLowerCase();
|
||||
const src = await ctx.runTask(ctx.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${id}` })) as string;
|
||||
const json = JSON.parse(src);
|
||||
const data = json && json[id];
|
||||
@@ -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 {
|
||||
@@ -105,6 +106,8 @@ export enum StateElements {
|
||||
ModelProps = 'model-props',
|
||||
Assembly = 'assembly',
|
||||
|
||||
VolumeStreaming = 'volume-streaming',
|
||||
|
||||
Sequence = 'sequence',
|
||||
SequenceVisual = 'sequence-visual',
|
||||
Het = 'het',
|
||||
@@ -113,5 +116,6 @@ export enum StateElements {
|
||||
Water = 'water',
|
||||
WaterVisual = 'water-visual',
|
||||
|
||||
HetGroupFocus = 'het-group-focus'
|
||||
HetGroupFocus = 'het-group-focus',
|
||||
HetGroupFocusGroup = 'het-group-focus-group'
|
||||
}
|
||||
@@ -40,6 +40,13 @@
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#volume-streaming-wrapper {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 780px;
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
@@ -55,6 +62,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<div id="volume-streaming-wrapper"></div>
|
||||
<script>
|
||||
// it might be a good idea to define these colors in a separate script file
|
||||
var CustomColors = [0x00ff00, 0x0000ff];
|
||||
@@ -66,7 +74,7 @@
|
||||
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
var pdbId = '1eve', assemblyId= 'preferred';
|
||||
var pdbId = '1cbs', assemblyId= 'preferred';
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
|
||||
var format = 'cif';
|
||||
|
||||
@@ -122,7 +130,11 @@
|
||||
addSeparator();
|
||||
|
||||
addHeader('Camera');
|
||||
addControl('Toggle Spin', () => PluginWrapper.toggleSpin());
|
||||
addControl('Reset Position', () => PluginWrapper.camera.resetPosition());
|
||||
addControl('Toggle Spin', () => PluginWrapper.camera.toggleSpin());
|
||||
// Same as "wheel icon" and Viewport options
|
||||
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
|
||||
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
@@ -141,7 +153,8 @@
|
||||
addSeparator();
|
||||
addHeader('Misc');
|
||||
|
||||
addControl('Apply Evo Cons', () => PluginWrapper.coloring.evolutionaryConservation());
|
||||
addControl('Apply Evo Cons Style', () => PluginWrapper.coloring.evolutionaryConservation());
|
||||
addControl('Apply Evo Cons Colors', () => PluginWrapper.coloring.evolutionaryConservation({ sequence: true, het: false, keepStyle: true }));
|
||||
addControl('Default Visuals', () => PluginWrapper.updateStyle());
|
||||
|
||||
addSeparator();
|
||||
@@ -151,6 +164,11 @@
|
||||
addHetGroupsContainer();
|
||||
|
||||
addSeparator();
|
||||
addHeader('Exp. Data');
|
||||
addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
|
||||
addControl('Remove', () => PluginWrapper.experimentalData.remove());
|
||||
|
||||
addSeparator();
|
||||
addHeader('State');
|
||||
|
||||
var snapshot;
|
||||
|
||||
@@ -4,36 +4,36 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
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 { StateBuilder, StateObject } from '../../mol-state';
|
||||
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 } 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/tables';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
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 = 1;
|
||||
static VERSION_MAJOR = 4;
|
||||
static VERSION_MINOR = 0;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -55,30 +55,30 @@ 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) {
|
||||
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
|
||||
}
|
||||
|
||||
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
|
||||
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats) {
|
||||
const parsed = format === 'cif'
|
||||
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
|
||||
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
|
||||
@@ -89,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 });
|
||||
@@ -115,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'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,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
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,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' }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,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
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,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 = '', representationStyle }: LoadParams) {
|
||||
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';
|
||||
@@ -199,40 +209,83 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
if (loadType === 'full') {
|
||||
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
|
||||
const modelTree = this.model(this.download(state.build().toRoot(), url), format, assemblyId);
|
||||
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);
|
||||
const structureTree = this.structure((assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
const structureTree = this.structure(asmId);
|
||||
await this.applyState(structureTree);
|
||||
} else {
|
||||
const tree = state.build();
|
||||
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
|
||||
const info = await this.doInfo(true);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
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(this.plugin, {
|
||||
settings: settings || DefaultCanvas3DParams
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
camera = {
|
||||
toggleSpin: () => this.toggleSpin(),
|
||||
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { }),
|
||||
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
|
||||
// if (!options) {
|
||||
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
// settings: {
|
||||
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
|
||||
// clip: DefaultCanvas3DParams.clip
|
||||
// }
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// options = options || { };
|
||||
// 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(this.plugin, {
|
||||
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
animate = {
|
||||
@@ -247,53 +300,85 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
coloring = {
|
||||
evolutionaryConservation: async () => {
|
||||
await this.updateStyle({ sequence: { kind: 'spacefill' } }, true);
|
||||
evolutionaryConservation: async (params?: { sequence?: boolean, het?: boolean, keepStyle?: boolean }) => {
|
||||
if (!params || !params.keepStyle) {
|
||||
await this.updateStyle({ sequence: { kind: 'spacefill' } }, true);
|
||||
}
|
||||
|
||||
const state = this.state;
|
||||
|
||||
// const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
|
||||
|
||||
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
// for (const v of visuals) {
|
||||
// }
|
||||
|
||||
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
|
||||
const tree = state.build();
|
||||
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 }));
|
||||
}
|
||||
if (params && !!params.het) {
|
||||
tree.to(StateElements.HetVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
}
|
||||
}
|
||||
|
||||
private experimentalDataElement?: Element = void 0;
|
||||
experimentalData = {
|
||||
init: async (parent: Element) => {
|
||||
const asm = this.state.select(StateElements.Assembly)[0].obj!;
|
||||
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);
|
||||
},
|
||||
remove: () => {
|
||||
const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
|
||||
if (!r) return;
|
||||
PluginCommands.State.RemoveObject(this.plugin, { state: this.state, ref: r.transform.ref });
|
||||
if (this.experimentalDataElement) {
|
||||
ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
|
||||
this.experimentalDataElement = void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hetGroups = {
|
||||
reset: () => {
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocus);
|
||||
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
|
||||
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
|
||||
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
},
|
||||
focusFirst: async (resn: string) => {
|
||||
focusFirst: async (compId: string) => {
|
||||
if (!this.state.transforms.has(StateElements.Assembly)) return;
|
||||
await PluginCommands.Camera.Reset(this.plugin, { });
|
||||
|
||||
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
|
||||
|
||||
const update = this.state.build();
|
||||
|
||||
update.delete(StateElements.HetGroupFocus);
|
||||
update.delete(StateElements.HetGroupFocusGroup);
|
||||
|
||||
const surroundings = MS.struct.modifier.includeSurroundings({
|
||||
0: MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), resn]),
|
||||
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
|
||||
})
|
||||
]),
|
||||
radius: 5,
|
||||
'as-whole-residues': true
|
||||
});
|
||||
const core = MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), compId]),
|
||||
'group-by': MS.core.str.concat([MS.struct.atomProperty.core.operatorName(), MS.struct.atomProperty.macromolecular.residueKey()])
|
||||
})
|
||||
]);
|
||||
const surroundings = MS.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true });
|
||||
|
||||
const sel = update.to(StateElements.Assembly)
|
||||
.apply(StateTransforms.Model.StructureSelection, { label: resn, query: surroundings }, { ref: StateElements.HetGroupFocus });
|
||||
const group = update.to(StateElements.Assembly).group(StateTransforms.Misc.CreateGroup, { label: compId }, { ref: StateElements.HetGroupFocusGroup });
|
||||
|
||||
sel.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
|
||||
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Core', expression: core }, { ref: StateElements.HetGroupFocus })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createCoreVisualParams());
|
||||
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Surroundings', expression: surroundings })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
|
||||
// sel.apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'residues', params: { } },
|
||||
// options: {
|
||||
@@ -305,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;
|
||||
@@ -313,17 +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, 0.75 * sphere.radius);
|
||||
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 createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'ball-and-stick'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -336,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);
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from '../../../mol-plugin/ui/base';
|
||||
import { CurrentObject } from '../../../mol-plugin/ui/plugin';
|
||||
import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
|
||||
import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
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} />
|
||||
</PluginContextContainer>,
|
||||
parent);
|
||||
}
|
||||
@@ -28,9 +28,9 @@ function messageTree(root: Progress.Node, prefix = ''): string {
|
||||
|
||||
function createTask<T>(delayMs: number, r: T): Task<T> {
|
||||
return Task.create('delayed value ' + r, async ctx => {
|
||||
ctx.update('Processing delayed... ' + r, true);
|
||||
ctx.update(`Processing delayed ${r} after ${delayMs}ms`, true);
|
||||
await Scheduler.delay(delayMs);
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'hello from delayed... ' });
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: `hello from delayed ${r} ${delayMs}` });
|
||||
return r;
|
||||
}, () => console.log('On abort called ' + r));
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export function testTree() {
|
||||
const r = await c1 + await c2 + await c3;
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'Almost done...' });
|
||||
return r + 1;
|
||||
});
|
||||
}, () => console.log('On abort O'));
|
||||
}
|
||||
|
||||
export type ChunkedState = { i: number, current: number, total: number }
|
||||
@@ -115,7 +115,7 @@ async function test() {
|
||||
// const r = await Run(testTree(), abortingObserver, 250);
|
||||
// console.log(r);
|
||||
|
||||
const m = await ms({ i: 10 }).run(logP);
|
||||
const m = await testTree().run(abortingObserver, 50);
|
||||
console.log(m);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export interface ColorThemeComponentProps {
|
||||
colorTheme: ColorTheme<any>
|
||||
}
|
||||
|
||||
export interface ColorThemeComponentState {
|
||||
|
||||
}
|
||||
|
||||
export class ColorThemeComponent extends React.Component<ColorThemeComponentProps, ColorThemeComponentState> {
|
||||
state = {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const ct = this.props.colorTheme
|
||||
return <div>
|
||||
<span>Color Theme Info </span>
|
||||
|
||||
{ct.description ? <div><i>{ct.description}</i></div> : ''}
|
||||
{
|
||||
ct.legend && ct.legend.kind === 'scale-legend'
|
||||
? <div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '30px',
|
||||
background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`
|
||||
}}
|
||||
>
|
||||
<span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span>
|
||||
<span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span>
|
||||
</div>
|
||||
: ct.legend && ct.legend.kind === 'table-legend'
|
||||
? <div>
|
||||
{ct.legend.table.map((value, i) => {
|
||||
const [name, color] = value
|
||||
return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}>
|
||||
<div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div>
|
||||
{name}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export interface BooleanParamComponentProps {
|
||||
label: string
|
||||
param: PD.Boolean
|
||||
value: boolean
|
||||
onChange(v: boolean): void
|
||||
}
|
||||
|
||||
export interface BooleanParamComponentState {
|
||||
value: boolean
|
||||
}
|
||||
|
||||
export class BooleanParamComponent extends React.Component<BooleanParamComponentProps, BooleanParamComponentState> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(value: boolean) {
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<button onClick={e => this.onChange(!this.state.value) }>
|
||||
{this.state.value ? 'Off' : 'On'}
|
||||
</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { ColorNames } from '../../../mol-util/color/tables';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
|
||||
export interface ColorParamComponentProps {
|
||||
label: string
|
||||
param: PD.Color
|
||||
value: Color
|
||||
onChange(v: Color): void
|
||||
}
|
||||
|
||||
export interface ColorParamComponentState {
|
||||
value: Color
|
||||
}
|
||||
|
||||
export class ColorParamComponent extends React.Component<ColorParamComponentProps, ColorParamComponentState> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(value: Color) {
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<select value={this.state.value} onChange={e => this.onChange(Color(parseInt(e.target.value))) }>
|
||||
{Object.keys(ColorNames).map(name => {
|
||||
return <option key={name} value={(ColorNames as { [k: string]: Color})[name]}>{name}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export interface MultiSelectParamComponentProps<T extends string> {
|
||||
label: string
|
||||
param: PD.MultiSelect<T>
|
||||
value: T[]
|
||||
onChange(v: T[]): void
|
||||
}
|
||||
|
||||
export interface MultiSelectParamComponentState<T extends string> {
|
||||
value: T[]
|
||||
}
|
||||
|
||||
export class MultiSelectParamComponent<T extends string> extends React.Component<MultiSelectParamComponentProps<T>, MultiSelectParamComponentState<T>> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(value: T[]) {
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<select multiple value={this.state.value} onChange={e => {
|
||||
const value = Array.from(e.target.options).filter(option => option.selected).map(option => option.value)
|
||||
this.onChange(value as T[])
|
||||
}}>
|
||||
{this.props.param.options.map(v => {
|
||||
const [value, label] = v
|
||||
return <option key={label} value={value}>{label}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export interface NumberParamComponentProps {
|
||||
label: string
|
||||
param: PD.Numeric
|
||||
value: number
|
||||
onChange(v: number): void
|
||||
}
|
||||
|
||||
export interface NumberParamComponentState {
|
||||
value: number
|
||||
}
|
||||
|
||||
export class NumberParamComponent extends React.Component<NumberParamComponentProps, NumberParamComponentState> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(valueStr: string) {
|
||||
const value = this.props.param.step && Number.isInteger(this.props.param.step) ? parseInt(valueStr) : parseFloat(valueStr)
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<input type='range'
|
||||
value={this.state.value}
|
||||
min={this.props.param.min}
|
||||
max={this.props.param.max}
|
||||
step={this.props.param.step}
|
||||
onChange={e => this.onChange(e.currentTarget.value)}
|
||||
>
|
||||
</input>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export interface SelectParamComponentProps<T extends string> {
|
||||
label: string
|
||||
param: PD.Select<T>
|
||||
value: T
|
||||
onChange(v: T): void
|
||||
}
|
||||
|
||||
export interface SelectParamComponentState<T extends string> {
|
||||
value: T
|
||||
}
|
||||
|
||||
export class SelectParamComponent<T extends string> extends React.Component<SelectParamComponentProps<T>, SelectParamComponentState<T>> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(value: T) {
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<select value={this.state.value} onChange={e => this.onChange(e.target.value as T) }>
|
||||
{this.props.param.options.map(v => {
|
||||
const [value, label] = v
|
||||
return <option key={label} value={value}>{label}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export interface TextParamComponentProps {
|
||||
label: string
|
||||
param: PD.Text
|
||||
value: string
|
||||
onChange(v: string): void
|
||||
}
|
||||
|
||||
export interface TextParamComponentState {
|
||||
value: string
|
||||
}
|
||||
|
||||
export class TextParamComponent extends React.Component<TextParamComponentProps, TextParamComponentState> {
|
||||
state = {
|
||||
value: this.props.value
|
||||
}
|
||||
|
||||
onChange(value: string) {
|
||||
this.setState({ value })
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<span>{this.props.label} </span>
|
||||
<input type='text'
|
||||
value={this.state.value}
|
||||
onChange={e => this.onChange(e.currentTarget.value)}
|
||||
>
|
||||
</input>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { BooleanParamComponent } from './parameter/boolean';
|
||||
import { NumberParamComponent } from './parameter/number';
|
||||
import { SelectParamComponent } from './parameter/select';
|
||||
import { MultiSelectParamComponent } from './parameter/multi-select';
|
||||
import { TextParamComponent } from './parameter/text';
|
||||
import { ColorParamComponent } from './parameter/color';
|
||||
import { camelCaseToWords } from '../../mol-util/string';
|
||||
|
||||
interface ParametersProps<P extends PD.Params> {
|
||||
params: P
|
||||
values: { [k in keyof P]: P[k]['defaultValue'] }
|
||||
onChange<K extends keyof P>(k: K, v: P[K]['defaultValue']): void
|
||||
}
|
||||
|
||||
type ParametersState = {}
|
||||
|
||||
function getParamComponent<P extends PD.Any>(label: string, param: PD.Any, value: P['defaultValue'], onChange: (v: P['defaultValue']) => void) {
|
||||
switch (param.type) {
|
||||
case 'boolean':
|
||||
return <BooleanParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
case 'number':
|
||||
return <NumberParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
case 'select':
|
||||
return <SelectParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
case 'multi-select':
|
||||
return <MultiSelectParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
case 'text':
|
||||
return <TextParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
case 'color':
|
||||
return <ColorParamComponent label={label} param={param} value={value} onChange={onChange} />
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function getLabel(name: string, param: PD.Base<any>) {
|
||||
return param.label === undefined ? camelCaseToWords(name) : param.label
|
||||
}
|
||||
|
||||
export class ParametersComponent<P extends PD.Params> extends React.Component<ParametersProps<P>, ParametersState> {
|
||||
onChange(k: string, value: any) {
|
||||
this.props.onChange(k, value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div style={{ width: '100%' }}>
|
||||
{ Object.keys(this.props.params).map(k => {
|
||||
const param = this.props.params[k]
|
||||
const value = this.props.values[k]
|
||||
const label = getLabel(k, param)
|
||||
return <div key={k}>
|
||||
{getParamComponent(label, param, value, v => this.onChange(k, v))}
|
||||
</div>
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// /**
|
||||
// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
// *
|
||||
// * @author David Sehnal <david.sehnal@gmail.com>
|
||||
// */
|
||||
|
||||
// import * as React from 'react'
|
||||
// import { Structure, StructureSequence, Queries, StructureSelection, StructureProperties, StructureQuery } from 'mol-model/structure';
|
||||
// import { EmptyLoci } from 'mol-model/loci';
|
||||
|
||||
// export class SequenceView extends View<SequenceViewController, {}, {}> {
|
||||
// render() {
|
||||
// const s = this.controller.latestState.structure;
|
||||
// if (!s) return <div className='molstar-sequence-view-wrap'>No structure available.</div>;
|
||||
|
||||
// const seqs = s.models[0].sequence.sequences;
|
||||
// return <div className='molstar-sequence-view-wrap'>
|
||||
// {seqs.map((seq, i) => <EntitySequence key={i} ctx={this.controller.context} seq={seq} structure={s} /> )}
|
||||
// </div>;
|
||||
// }
|
||||
// }
|
||||
|
||||
// function createQuery(entityId: string, label_seq_id: number) {
|
||||
// return Queries.generators.atoms({
|
||||
// entityTest: ctx => StructureProperties.entity.id(ctx.element) === entityId,
|
||||
// residueTest: ctx => StructureProperties.residue.label_seq_id(ctx.element) === label_seq_id
|
||||
// });
|
||||
// }
|
||||
|
||||
// // TODO: this is really ineffective and should be done using a canvas.
|
||||
// class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> {
|
||||
|
||||
// raiseInteractityEvent(seqId?: number) {
|
||||
// if (typeof seqId === 'undefined') {
|
||||
// InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const query = createQuery(this.props.seq.entityId, seqId);
|
||||
// const loci = StructureSelection.toLoci(StructureQuery.run(query, this.props.structure));
|
||||
// if (loci.elements.length === 0) InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci);
|
||||
// else InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, loci);
|
||||
// }
|
||||
|
||||
|
||||
// render() {
|
||||
// const { ctx, seq } = this.props;
|
||||
// const { offset, sequence } = seq.sequence;
|
||||
|
||||
// const elems: JSX.Element[] = [];
|
||||
// for (let i = 0, _i = sequence.length; i < _i; i++) {
|
||||
// elems[elems.length] = <ResidueView ctx={ctx} seqId={offset + i} letter={sequence[i]} parent={this} key={i} />;
|
||||
// }
|
||||
|
||||
// return <div style={{ wordWrap: 'break-word' }}>
|
||||
// <span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset} </span>
|
||||
// {elems}
|
||||
// </div>;
|
||||
// }
|
||||
// }
|
||||
|
||||
// class ResidueView extends React.Component<{ ctx: Context, seqId: number, letter: string, parent: EntitySequence }, { isHighlighted: boolean }> {
|
||||
// state = { isHighlighted: false }
|
||||
|
||||
// mouseEnter = () => {
|
||||
// this.setState({ isHighlighted: true });
|
||||
// this.props.parent.raiseInteractityEvent(this.props.seqId);
|
||||
// }
|
||||
|
||||
// mouseLeave = () => {
|
||||
// this.setState({ isHighlighted: false });
|
||||
// this.props.parent.raiseInteractityEvent();
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// return <span onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave}
|
||||
// style={{ cursor: 'pointer', backgroundColor: this.state.isHighlighted ? 'yellow' : void 0 }}>
|
||||
// {this.props.letter}
|
||||
// </span>;
|
||||
// }
|
||||
// }
|
||||
@@ -7,17 +7,12 @@
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
|
||||
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
|
||||
import { Object3D } from '../mol-gl/object3d';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CameraTransitionManager } from './camera/transition';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
export { Camera }
|
||||
|
||||
// TODO: slab controls that modify near/far planes?
|
||||
|
||||
class Camera implements Object3D {
|
||||
readonly updatedViewProjection = new BehaviorSubject<Camera>(this);
|
||||
|
||||
class Camera {
|
||||
readonly view: Mat4 = Mat4.identity();
|
||||
readonly projection: Mat4 = Mat4.identity();
|
||||
readonly projectionView: Mat4 = Mat4.identity();
|
||||
@@ -32,14 +27,18 @@ class Camera implements Object3D {
|
||||
width: 1, height: 1
|
||||
}
|
||||
|
||||
near = 1
|
||||
far = 10000
|
||||
fogNear = 5000
|
||||
fogFar = 10000
|
||||
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); }
|
||||
|
||||
get direction() { return this.state.direction; }
|
||||
set direction(v: Vec3) { Vec3.copy(this.state.direction, v); }
|
||||
|
||||
get up() { return this.state.up; }
|
||||
set up(v: Vec3) { Vec3.copy(this.state.up, v); }
|
||||
|
||||
@@ -51,10 +50,12 @@ class Camera implements Object3D {
|
||||
private deltaDirection = Vec3.zero();
|
||||
private newPosition = Vec3.zero();
|
||||
|
||||
updateMatrices() {
|
||||
update() {
|
||||
const snapshot = this.state as Camera.Snapshot;
|
||||
const height = 2 * Math.tan(snapshot.fov / 2) * Vec3.distance(snapshot.position, snapshot.target);
|
||||
snapshot.zoom = this.viewport.height / height;
|
||||
this.zoom = this.viewport.height / height;
|
||||
|
||||
updateClip(this);
|
||||
|
||||
switch (this.state.mode) {
|
||||
case 'orthographic': updateOrtho(this); break;
|
||||
@@ -62,11 +63,7 @@ class Camera implements Object3D {
|
||||
default: throw new Error('unknown camera mode');
|
||||
}
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON.Value) || !Mat4.areEqual(this.view, this.prevView, EPSILON.Value);
|
||||
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
Mat4.invert(this.inverseProjectionView, this.projectionView)
|
||||
|
||||
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
|
||||
|
||||
if (changed) {
|
||||
Mat4.mul(this.projectionView, this.projection, this.view)
|
||||
@@ -74,52 +71,48 @@ class Camera implements Object3D {
|
||||
|
||||
Mat4.copy(this.prevView, this.view);
|
||||
Mat4.copy(this.prevProjection, this.projection);
|
||||
this.updatedViewProjection.next(this);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
setState(snapshot: Partial<Camera.Snapshot>) {
|
||||
this.transition.apply(snapshot);
|
||||
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
|
||||
this.transition.apply(snapshot, durationMs);
|
||||
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 currentDistance = Vec3.distance(this.state.position, target)
|
||||
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
|
||||
const deltaDistance = Math.abs(currentDistance - targetDistance)
|
||||
const targetDistance = Math.abs((r / aspectFactor) / Math.sin(fov / 2))
|
||||
|
||||
Vec3.sub(this.deltaDirection, this.state.position, target)
|
||||
Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
|
||||
if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
|
||||
Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
|
||||
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)
|
||||
|
||||
return { target, position: Vec3.clone(this.newPosition) };
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
|
||||
state.target = Vec3.clone(target)
|
||||
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) {
|
||||
this.setState(this.getFocus(target, radius));
|
||||
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
|
||||
if (radius > 0) {
|
||||
this.setState(this.getFocus(target, radius, up, dir), durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
// lookAt(target: Vec3) {
|
||||
// cameraLookAt(this.position, this.up, this.direction, target);
|
||||
// }
|
||||
|
||||
// translate(v: Vec3) {
|
||||
// Vec3.add(this.position, this.position, v);
|
||||
// cameraLookAt(this.position, this.up, this.direction, this.target);
|
||||
// }
|
||||
|
||||
project(out: Vec4, point: Vec3) {
|
||||
return cameraProject(out, point, this.viewport, this.projectionView)
|
||||
}
|
||||
@@ -128,10 +121,6 @@ class Camera implements Object3D {
|
||||
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.updatedViewProjection.complete();
|
||||
}
|
||||
|
||||
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) {
|
||||
this.viewport = viewport;
|
||||
Camera.copySnapshot(this.state, state);
|
||||
@@ -142,13 +131,6 @@ class Camera implements Object3D {
|
||||
namespace Camera {
|
||||
export type Mode = 'perspective' | 'orthographic'
|
||||
|
||||
export interface ClippingInfo {
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an offseted view in a larger frustum. This is useful for
|
||||
* - multi-window or multi-monitor/multi-machine setups
|
||||
@@ -176,64 +158,54 @@ namespace Camera {
|
||||
export function createDefaultSnapshot(): Snapshot {
|
||||
return {
|
||||
mode: 'perspective',
|
||||
fov: Math.PI / 4,
|
||||
|
||||
position: Vec3.zero(),
|
||||
direction: Vec3.create(0, 0, -1),
|
||||
position: Vec3.create(0, 0, 100),
|
||||
up: Vec3.create(0, 1, 0),
|
||||
|
||||
target: Vec3.create(0, 0, 0),
|
||||
|
||||
near: 1,
|
||||
far: 10000,
|
||||
fogNear: 1,
|
||||
fogFar: 10000,
|
||||
|
||||
fov: Math.PI / 4,
|
||||
zoom: 1,
|
||||
radius: 10,
|
||||
radiusMax: 10,
|
||||
fog: 50,
|
||||
clipFar: true
|
||||
};
|
||||
}
|
||||
|
||||
export interface Snapshot {
|
||||
mode: Mode,
|
||||
mode: Mode
|
||||
fov: number
|
||||
|
||||
position: Vec3,
|
||||
// Normalized camera direction
|
||||
direction: Vec3,
|
||||
up: Vec3,
|
||||
target: Vec3,
|
||||
position: Vec3
|
||||
up: Vec3
|
||||
target: Vec3
|
||||
|
||||
near: number,
|
||||
far: number,
|
||||
fogNear: number,
|
||||
fogFar: number,
|
||||
|
||||
fov: number,
|
||||
zoom: number,
|
||||
radius: number
|
||||
radiusMax: number
|
||||
fog: number
|
||||
clipFar: boolean
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
if (!source) return;
|
||||
if (!source) return out;
|
||||
|
||||
if (typeof source.mode !== 'undefined') out.mode = source.mode;
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
|
||||
if (typeof source.position !== 'undefined') Vec3.copy(out.position, source.position);
|
||||
if (typeof source.direction !== 'undefined') Vec3.copy(out.direction, source.direction);
|
||||
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
|
||||
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
|
||||
|
||||
if (typeof source.near !== 'undefined') out.near = source.near;
|
||||
if (typeof source.far !== 'undefined') out.far = source.far;
|
||||
if (typeof source.fogNear !== 'undefined') out.fogNear = source.fogNear;
|
||||
if (typeof source.fogFar !== 'undefined') out.fogFar = source.fogFar;
|
||||
if (typeof source.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;
|
||||
|
||||
if (typeof source.fov !== 'undefined') out.fov = source.fov;
|
||||
if (typeof source.zoom !== 'undefined') out.zoom = source.zoom;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
const _center = Vec3.zero();
|
||||
function updateOrtho(camera: Camera) {
|
||||
const { viewport, state: { zoom, near, far }, viewOffset } = camera
|
||||
const { viewport, zoom, near, far, viewOffset } = camera
|
||||
|
||||
const fullLeft = -(viewport.width - viewport.x) / 2
|
||||
const fullRight = (viewport.width - viewport.x) / 2
|
||||
@@ -250,7 +222,7 @@ function updateOrtho(camera: Camera) {
|
||||
let top = cy + dy
|
||||
let bottom = cy - dy
|
||||
|
||||
if (viewOffset && viewOffset.enabled) {
|
||||
if (viewOffset.enabled) {
|
||||
const zoomW = zoom / (viewOffset.width / viewOffset.fullWidth)
|
||||
const zoomH = zoom / (viewOffset.height / viewOffset.fullHeight)
|
||||
const scaleW = (fullRight - fullLeft) / viewOffset.width
|
||||
@@ -265,21 +237,20 @@ function updateOrtho(camera: Camera) {
|
||||
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updatePers(camera: Camera) {
|
||||
const aspect = camera.viewport.width / camera.viewport.height
|
||||
|
||||
const { state: { fov, near, far }, viewOffset } = camera
|
||||
const { near, far, viewOffset } = camera
|
||||
|
||||
let top = near * Math.tan(0.5 * fov)
|
||||
let top = near * Math.tan(0.5 * camera.state.fov)
|
||||
let height = 2 * top
|
||||
let width = aspect * height
|
||||
let left = -0.5 * width
|
||||
|
||||
if (viewOffset && viewOffset.enabled) {
|
||||
if (viewOffset.enabled) {
|
||||
left += viewOffset.offsetX * width / viewOffset.fullWidth
|
||||
top -= viewOffset.offsetY * height / viewOffset.fullHeight
|
||||
width *= viewOffset.width / viewOffset.fullWidth
|
||||
@@ -290,6 +261,33 @@ function updatePers(camera: Camera) {
|
||||
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far)
|
||||
|
||||
// build view matrix
|
||||
Vec3.add(_center, camera.position, camera.direction)
|
||||
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
|
||||
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
let { radius, radiusMax, mode, fog, clipFar } = camera.state
|
||||
if (radius < 0.01) radius = 0.01
|
||||
|
||||
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 = cameraDistance - (normalizedFar * fogNearFactor)
|
||||
let fogFar = far
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
near = Math.max(5, near)
|
||||
far = Math.max(5, far)
|
||||
} else {
|
||||
near = Math.max(0, near)
|
||||
far = Math.max(0, far)
|
||||
}
|
||||
|
||||
camera.near = near;
|
||||
camera.far = far;
|
||||
camera.fogNear = fogNear;
|
||||
camera.fogFar = fogFar;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -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 || 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) {
|
||||
@@ -68,33 +75,23 @@ class CameraTransitionManager {
|
||||
namespace CameraTransitionManager {
|
||||
export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void
|
||||
|
||||
const _rot = Quat.identity();
|
||||
export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void {
|
||||
Camera.copySnapshot(out, target);
|
||||
|
||||
// Rotate direction
|
||||
const rot = Quat.identity();
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.direction, target.direction), t);
|
||||
Vec3.transformQuat(out.direction, source.direction, rot);
|
||||
|
||||
// Rotate up
|
||||
Quat.setIdentity(rot);
|
||||
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, rot);
|
||||
Quat.slerp(_rot, Quat.Identity, Quat.rotationTo(_rot, source.up, target.up), t);
|
||||
Vec3.transformQuat(out.up, source.up, _rot);
|
||||
|
||||
// Lerp target
|
||||
// Lerp target, position & radius
|
||||
Vec3.lerp(out.target, source.target, target.target, t);
|
||||
Vec3.lerp(out.position, source.position, target.position, t);
|
||||
out.radius = lerp(source.radius, target.radius, t);
|
||||
// TODO take change of `clipFar` into account
|
||||
out.radiusMax = lerp(source.radiusMax, target.radiusMax, t);
|
||||
|
||||
// Update position
|
||||
const dist = -lerp(Vec3.distance(source.position, source.target), Vec3.distance(target.position, target.target), t);
|
||||
Vec3.scale(out.position, out.direction, dist);
|
||||
Vec3.add(out.position, out.position, out.target);
|
||||
|
||||
// Lerp other props
|
||||
out.zoom = lerp(source.zoom, target.zoom, t);
|
||||
// Lerp fov & fog
|
||||
out.fov = lerp(source.fov, target.fov, t);
|
||||
out.near = lerp(source.near, target.near, t);
|
||||
out.far = lerp(source.far, target.far, t);
|
||||
out.fogNear = lerp(source.fogNear, target.fogNear, t);
|
||||
out.fogFar = lerp(source.fogFar, target.fogFar, t);
|
||||
out.fog = lerp(source.fog, target.fog, t);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
|
||||
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra'
|
||||
|
||||
export { Viewport }
|
||||
|
||||
@@ -55,31 +55,6 @@ namespace Viewport {
|
||||
|
||||
//
|
||||
|
||||
const tmpVec3 = Vec3()
|
||||
|
||||
/** Modifies the direction & up vectors in place, both are normalized */
|
||||
export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
|
||||
Vec3.sub(tmpVec3, target, position)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
|
||||
if (!Vec3.isZero(tmpVec3)) {
|
||||
// change direction vector to look at target
|
||||
const d = Vec3.dot(tmpVec3, up)
|
||||
if (Math.abs(d - 1) < EPSILON.Value) { // parallel
|
||||
Vec3.scale(up, direction, -1)
|
||||
} else if (Math.abs(d + 1) < EPSILON.Value) { // anti parallel
|
||||
Vec3.copy(up, direction)
|
||||
}
|
||||
Vec3.copy(direction, tmpVec3)
|
||||
|
||||
// normalize up vector
|
||||
Vec3.cross(tmpVec3, direction, up)
|
||||
Vec3.normalize(tmpVec3, tmpVec3)
|
||||
Vec3.cross(up, tmpVec3, direction)
|
||||
Vec3.normalize(up, up)
|
||||
}
|
||||
}
|
||||
|
||||
const NEAR_RANGE = 0
|
||||
const FAR_RANGE = 1
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
@@ -17,7 +18,7 @@ import { Representation } from '../mol-repr/representation';
|
||||
import Scene from '../mol-gl/scene';
|
||||
import { GraphicsRenderVariant } from '../mol-gl/webgl/render-item';
|
||||
import { PickingId } from '../mol-geo/geometry/picking';
|
||||
import { MarkerAction } from '../mol-geo/geometry/marker-data';
|
||||
import { MarkerAction } from '../mol-util/marker-action';
|
||||
import { Loci, EmptyLoci, isEmptyLoci } from '../mol-model/loci';
|
||||
import { Camera } from './camera';
|
||||
import { ParamDefinition as PD } from '../mol-util/param-definition';
|
||||
@@ -26,19 +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 = {
|
||||
// TODO: FPS cap?
|
||||
// maxFps: PD.Numeric(30),
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
|
||||
cameraClipDistance: PD.Numeric(0, { min: 0.0, max: 50.0, step: 0.1 }, { description: 'The distance between camera and scene at which to clip regardless of near clipping plane.' }),
|
||||
clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
|
||||
fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
|
||||
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),
|
||||
@@ -46,6 +58,7 @@ export const Canvas3DParams = {
|
||||
trackball: PD.Group(TrackballControlsParams),
|
||||
debug: PD.Group(DebugHelperParams)
|
||||
}
|
||||
export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
|
||||
export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
|
||||
|
||||
export { Canvas3D }
|
||||
@@ -53,27 +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
|
||||
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>
|
||||
@@ -81,29 +101,66 @@ interface Canvas3D {
|
||||
readonly stats: RendererStats
|
||||
readonly interaction: Canvas3dInteractionHelper['events']
|
||||
|
||||
dispose: () => void
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
|
||||
const DefaultRunTask = (task: Task<unknown>) => task.run()
|
||||
|
||||
namespace Canvas3D {
|
||||
export interface HighlightEvent { current: Representation.Loci, prev: Representation.Loci, modifiers?: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}) {
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
|
||||
const gl = getGLContext(canvas, {
|
||||
alpha: false,
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
depth: true,
|
||||
preserveDrawingBuffer: true
|
||||
preserveDrawingBuffer: true,
|
||||
premultipliedAlpha: false,
|
||||
})
|
||||
if (gl === null) throw new Error('Could not create a WebGL rendering context')
|
||||
const input = InputObserver.fromElement(canvas)
|
||||
return Canvas3D.create(gl, input, props)
|
||||
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> = {}): Canvas3D {
|
||||
const p = { ...PD.getDefaultValues(Canvas3DParams), ...props }
|
||||
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>>()
|
||||
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>()
|
||||
@@ -112,31 +169,39 @@ namespace Canvas3D {
|
||||
const startTime = now()
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
|
||||
|
||||
const camera = new Camera({
|
||||
near: 0.1,
|
||||
far: 10000,
|
||||
position: Vec3.create(0, 0, 10),
|
||||
mode: p.cameraMode
|
||||
})
|
||||
|
||||
const webgl = createContext(gl)
|
||||
const { gl, contextRestored } = webgl
|
||||
|
||||
let width = gl.drawingBufferWidth
|
||||
let height = gl.drawingBufferHeight
|
||||
|
||||
const scene = Scene.create(webgl)
|
||||
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode,
|
||||
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
|
||||
clipFar: p.cameraClipping.far
|
||||
})
|
||||
|
||||
const controls = TrackballControls.create(input, camera, p.trackball)
|
||||
const renderer = Renderer.create(webgl, camera, p.renderer)
|
||||
const renderer = Renderer.create(webgl, p.renderer)
|
||||
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
|
||||
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
|
||||
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, 0.5)
|
||||
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper)
|
||||
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
|
||||
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
|
||||
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
|
||||
|
||||
let isUpdating = false
|
||||
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
|
||||
@@ -144,7 +209,9 @@ namespace Canvas3D {
|
||||
reprRenderObjects.forEach((_, _repr) => {
|
||||
const _loci = _repr.getLoci(pickingId)
|
||||
if (!isEmptyLoci(_loci)) {
|
||||
if (!isEmptyLoci(loci)) console.warn('found another loci')
|
||||
if (!isEmptyLoci(loci)) {
|
||||
console.warn('found another loci, this should not happen')
|
||||
}
|
||||
loci = _loci
|
||||
repr = _repr
|
||||
}
|
||||
@@ -168,75 +235,35 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
let currentNear = -1, currentFar = -1, currentFogNear = -1, currentFogFar = -1
|
||||
function setClipping() {
|
||||
const cDist = Vec3.distance(camera.state.position, camera.state.target)
|
||||
const bRadius = Math.max(10, scene.boundingSphere.radius)
|
||||
|
||||
const nearFactor = (50 - p.clip[0]) / 50
|
||||
const farFactor = -(50 - p.clip[1]) / 50
|
||||
let near = cDist - (bRadius * nearFactor)
|
||||
let far = cDist + (bRadius * farFactor)
|
||||
|
||||
const fogNearFactor = (50 - p.fog[0]) / 50
|
||||
const fogFarFactor = -(50 - p.fog[1]) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + (bRadius * fogFarFactor)
|
||||
|
||||
if (camera.state.mode === 'perspective') {
|
||||
near = Math.max(1, p.cameraClipDistance, near)
|
||||
far = Math.max(1, far)
|
||||
fogNear = Math.max(1, fogNear)
|
||||
fogFar = Math.max(1, fogFar)
|
||||
} else if (camera.state.mode === 'orthographic') {
|
||||
if (p.cameraClipDistance > 0) {
|
||||
near = Math.max(p.cameraClipDistance, near)
|
||||
}
|
||||
}
|
||||
|
||||
if (near !== currentNear || far !== currentFar || fogNear !== currentFogNear || fogFar !== currentFogFar) {
|
||||
camera.setState({ near, far, fogNear, fogFar })
|
||||
currentNear = near, currentFar = far, currentFogNear = fogNear, currentFogFar = fogFar
|
||||
}
|
||||
}
|
||||
|
||||
function render(variant: 'pick' | 'draw', force: boolean) {
|
||||
if (isUpdating) return false
|
||||
function render(force: boolean) {
|
||||
if (webgl.isContextLost) return false
|
||||
|
||||
let didRender = false
|
||||
controls.update(currentTime);
|
||||
// TODO: is this a good fix? Also, setClipping does not work if the user has manually set a clipping plane.
|
||||
if (!camera.transition.inTransition) setClipping();
|
||||
const cameraChanged = camera.updateMatrices();
|
||||
controls.update(currentTime)
|
||||
Viewport.set(camera.viewport, 0, 0, width, height)
|
||||
const cameraChanged = camera.update()
|
||||
multiSample.update(force || cameraChanged, currentTime)
|
||||
|
||||
if (force || cameraChanged || multiSample.enabled) {
|
||||
switch (variant) {
|
||||
case 'pick':
|
||||
pickPass.render()
|
||||
break;
|
||||
case 'draw':
|
||||
renderer.setViewport(0, 0, width, height);
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render()
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
break;
|
||||
renderer.setViewport(0, 0, width, height)
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render(true, p.transparentBackground)
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled, p.transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
didRender = true
|
||||
}
|
||||
|
||||
return didRender && cameraChanged;
|
||||
return didRender;
|
||||
}
|
||||
|
||||
let forceNextDraw = false;
|
||||
let currentTime = 0;
|
||||
|
||||
function draw(force?: boolean) {
|
||||
if (render('draw', !!force || forceNextDraw)) {
|
||||
if (render(!!force || forceNextDraw)) {
|
||||
didDraw.next(now() - startTime as now.Timestamp)
|
||||
}
|
||||
forceNextDraw = false;
|
||||
@@ -251,36 +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(isSynchronous: boolean = false) {
|
||||
const allCommited = commitScene(isSynchronous);
|
||||
// Only reset the camera after the full scene has been commited.
|
||||
if (allCommited) resolveCameraReset();
|
||||
}
|
||||
|
||||
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) {
|
||||
isUpdating = true
|
||||
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)
|
||||
|
||||
scene.update(repr.renderObjects, false)
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
isUpdating = false
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -288,29 +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) {
|
||||
isUpdating = true
|
||||
renderObjects.forEach(o => scene.remove(o))
|
||||
reprRenderObjects.delete(repr)
|
||||
scene.update(void 0, false)
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
isUpdating = false
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
}
|
||||
},
|
||||
add,
|
||||
remove,
|
||||
commit,
|
||||
update: (repr, keepSphere) => {
|
||||
if (repr) {
|
||||
if (!reprRenderObjects.has(repr)) return;
|
||||
@@ -320,9 +390,18 @@ 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,
|
||||
@@ -333,11 +412,13 @@ namespace Canvas3D {
|
||||
getLoci,
|
||||
|
||||
handleResize,
|
||||
resetCamera: () => {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
|
||||
requestDraw(true);
|
||||
requestCameraReset: options => {
|
||||
nextCameraResetDuration = options?.durationMs;
|
||||
nextCameraResetSnapshot = options?.snapshot;
|
||||
cameraResetRequested = true;
|
||||
},
|
||||
camera,
|
||||
boundingSphere: scene.boundingSphere,
|
||||
downloadScreenshot: () => {
|
||||
// TODO
|
||||
},
|
||||
@@ -351,28 +432,58 @@ 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.cameraClipDistance !== undefined) p.cameraClipDistance = props.cameraClipDistance
|
||||
if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]]
|
||||
if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]]
|
||||
if (props.cameraFog !== undefined) {
|
||||
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> = {}) => {
|
||||
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
|
||||
},
|
||||
|
||||
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,
|
||||
cameraClipDistance: p.cameraClipDistance,
|
||||
clip: p.clip,
|
||||
fog: p.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 },
|
||||
@@ -391,12 +502,13 @@ namespace Canvas3D {
|
||||
return interactionHelper.events
|
||||
},
|
||||
dispose: () => {
|
||||
contextRestoredSub.unsubscribe()
|
||||
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
input.dispose()
|
||||
controls.dispose()
|
||||
renderer.dispose()
|
||||
camera.dispose()
|
||||
interactionHelper.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,26 +9,47 @@
|
||||
*/
|
||||
|
||||
import { Quat, Vec2, Vec3, EPSILON } from '../../mol-math/linear-algebra';
|
||||
import { cameraLookAt, Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, ButtonsType, PinchInput } from '../../mol-util/input/input-observer';
|
||||
import { Object3D } from '../../mol-gl/object3d';
|
||||
import { Viewport } from '../camera/util';
|
||||
import InputObserver, { DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Camera } from '../camera';
|
||||
import { absMax } from '../../mol-math/misc';
|
||||
import { Binding } from '../../mol-util/binding';
|
||||
|
||||
const B = ButtonsType
|
||||
const M = ModifiersKeys
|
||||
const Trigger = Binding.Trigger
|
||||
|
||||
export const DefaultTrackballBindings = {
|
||||
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate', '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', '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', 'Scroll using ${triggers}'),
|
||||
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'),
|
||||
scrollFocusZoom: Binding.Empty,
|
||||
}
|
||||
|
||||
export const TrackballControlsParams = {
|
||||
noScroll: PD.Boolean(true, { isHidden: true }),
|
||||
|
||||
rotateSpeed: PD.Numeric(5.0, { min: 0.1, max: 10, step: 0.1 }),
|
||||
rotateSpeed: PD.Numeric(3.0, { min: 0.1, max: 10, step: 0.1 }),
|
||||
zoomSpeed: PD.Numeric(6.0, { min: 0.1, max: 10, step: 0.1 }),
|
||||
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
|
||||
|
||||
spin: PD.Boolean(false),
|
||||
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
|
||||
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
|
||||
spinSpeed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
|
||||
|
||||
staticMoving: PD.Boolean(true, { isHidden: true }),
|
||||
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
|
||||
|
||||
minDistance: PD.Numeric(0.01, {}, { isHidden: true }),
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true })
|
||||
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
|
||||
|
||||
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true })
|
||||
}
|
||||
export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
|
||||
|
||||
@@ -44,11 +65,10 @@ interface TrackballControls {
|
||||
dispose: () => void
|
||||
}
|
||||
namespace TrackballControls {
|
||||
export function create(input: InputObserver, object: Object3D & { target: Vec3 }, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
export function create(input: InputObserver, camera: Camera, props: Partial<TrackballControlsProps> = {}): TrackballControls {
|
||||
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props }
|
||||
|
||||
const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
|
||||
const target: Vec3 = object.target
|
||||
const viewport = Viewport()
|
||||
|
||||
let disposed = false
|
||||
|
||||
@@ -60,91 +80,116 @@ namespace TrackballControls {
|
||||
let _isInteracting = false;
|
||||
|
||||
// For internal use
|
||||
const lastPosition = Vec3.zero()
|
||||
const lastPosition = Vec3()
|
||||
|
||||
const _eye = Vec3.zero()
|
||||
const _eye = Vec3()
|
||||
|
||||
const _movePrev = Vec2.zero()
|
||||
const _moveCurr = Vec2.zero()
|
||||
const _rotPrev = Vec2()
|
||||
const _rotCurr = Vec2()
|
||||
const _rotLastAxis = Vec3()
|
||||
let _rotLastAngle = 0
|
||||
|
||||
const _lastAxis = Vec3.zero()
|
||||
let _lastAngle = 0
|
||||
const _zRotPrev = Vec2()
|
||||
const _zRotCurr = Vec2()
|
||||
let _zRotLastAngle = 0
|
||||
|
||||
const _zoomStart = Vec2.zero()
|
||||
const _zoomEnd = Vec2.zero()
|
||||
const _zoomStart = Vec2()
|
||||
const _zoomEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2.zero()
|
||||
const _panEnd = Vec2.zero()
|
||||
const _focusStart = Vec2()
|
||||
const _focusEnd = Vec2()
|
||||
|
||||
const _panStart = Vec2()
|
||||
const _panEnd = Vec2()
|
||||
|
||||
// Initial values for reseting
|
||||
const target0 = Vec3.clone(target)
|
||||
const position0 = Vec3.clone(object.position)
|
||||
const up0 = Vec3.clone(object.up)
|
||||
const target0 = Vec3.clone(camera.target)
|
||||
const position0 = Vec3.clone(camera.position)
|
||||
const up0 = Vec3.clone(camera.up)
|
||||
|
||||
const mouseOnScreenVec2 = Vec2.zero()
|
||||
const mouseOnScreenVec2 = Vec2()
|
||||
function getMouseOnScreen(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnScreenVec2,
|
||||
(pageX - viewport.x) / viewport.width,
|
||||
(pageY - viewport.y) / viewport.height
|
||||
);
|
||||
return mouseOnScreenVec2;
|
||||
}
|
||||
|
||||
const mouseOnCircleVec2 = Vec2.zero()
|
||||
const mouseOnCircleVec2 = Vec2()
|
||||
function getMouseOnCircle(pageX: number, pageY: number) {
|
||||
Vec2.set(
|
||||
return Vec2.set(
|
||||
mouseOnCircleVec2,
|
||||
((pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5)),
|
||||
((viewport.height + 2 * (viewport.y - pageY)) / viewport.width) // screen.width intentional
|
||||
(pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5),
|
||||
(viewport.height + 2 * (viewport.y - pageY)) / viewport.width // screen.width intentional
|
||||
);
|
||||
return mouseOnCircleVec2;
|
||||
}
|
||||
|
||||
const rotAxis = Vec3.zero()
|
||||
const rotQuat = Quat.zero()
|
||||
const rotEyeDir = Vec3.zero()
|
||||
const rotObjUpDir = Vec3.zero()
|
||||
const rotObjSideDir = Vec3.zero()
|
||||
const rotMoveDir = Vec3.zero()
|
||||
const rotAxis = Vec3()
|
||||
const rotQuat = Quat()
|
||||
const rotEyeDir = Vec3()
|
||||
const rotObjUpDir = Vec3()
|
||||
const rotObjSideDir = Vec3()
|
||||
const rotMoveDir = Vec3()
|
||||
|
||||
function rotateCamera() {
|
||||
Vec3.set(rotMoveDir, _moveCurr[0] - _movePrev[0], _moveCurr[1] - _movePrev[1], 0);
|
||||
let angle = Vec3.magnitude(rotMoveDir);
|
||||
const dx = _rotCurr[0] - _rotPrev[0]
|
||||
const dy = _rotCurr[1] - _rotPrev[1]
|
||||
Vec3.set(rotMoveDir, dx, dy, 0);
|
||||
|
||||
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed;
|
||||
|
||||
if (angle) {
|
||||
Vec3.copy(_eye, object.position)
|
||||
Vec3.sub(_eye, _eye, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
Vec3.normalize(rotEyeDir, Vec3.copy(rotEyeDir, _eye))
|
||||
Vec3.normalize(rotObjUpDir, Vec3.copy(rotObjUpDir, object.up))
|
||||
Vec3.normalize(rotEyeDir, _eye)
|
||||
Vec3.normalize(rotObjUpDir, camera.up)
|
||||
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir))
|
||||
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, _moveCurr[1] - _movePrev[1])
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, _moveCurr[0] - _movePrev[0])
|
||||
|
||||
Vec3.add(rotMoveDir, Vec3.copy(rotMoveDir, rotObjUpDir), rotObjSideDir)
|
||||
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy)
|
||||
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx)
|
||||
|
||||
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir)
|
||||
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye))
|
||||
|
||||
angle *= p.rotateSpeed;
|
||||
Quat.setAxisAngle(rotQuat, rotAxis, angle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
|
||||
Vec3.copy(_lastAxis, rotAxis)
|
||||
_lastAngle = angle;
|
||||
} else if (!p.staticMoving && _lastAngle) {
|
||||
_lastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, Vec3.copy(_eye, object.position), target)
|
||||
Quat.setAxisAngle(rotQuat, _lastAxis, _lastAngle)
|
||||
Vec3.copy(_rotLastAxis, rotAxis)
|
||||
_rotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _rotLastAngle) {
|
||||
_rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle)
|
||||
|
||||
Vec3.transformQuat(_eye, _eye, rotQuat)
|
||||
Vec3.transformQuat(object.up, object.up, rotQuat)
|
||||
Vec3.transformQuat(camera.up, camera.up, rotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
|
||||
const zRotQuat = Quat()
|
||||
|
||||
function zRotateCamera() {
|
||||
const dx = _zRotCurr[0] - _zRotPrev[0]
|
||||
const dy = _zRotCurr[1] - _zRotPrev[1]
|
||||
const angle = p.rotateSpeed * (-dx + dy) * -0.05
|
||||
|
||||
if (angle) {
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, angle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
_zRotLastAngle = angle;
|
||||
} else if (!p.staticMoving && _zRotLastAngle) {
|
||||
_zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle)
|
||||
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
|
||||
}
|
||||
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
|
||||
function zoomCamera() {
|
||||
@@ -160,9 +205,23 @@ namespace TrackballControls {
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2.zero()
|
||||
const panObjUp = Vec3.zero()
|
||||
const panOffset = Vec3.zero()
|
||||
function focusCamera() {
|
||||
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
|
||||
if (factor !== 0.0) {
|
||||
const radius = Math.max(1, camera.state.radius + 10 * factor)
|
||||
camera.setState({ radius })
|
||||
}
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
} else {
|
||||
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor
|
||||
}
|
||||
}
|
||||
|
||||
const panMouseChange = Vec2()
|
||||
const panObjUp = Vec3()
|
||||
const panOffset = Vec3()
|
||||
|
||||
function panCamera() {
|
||||
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart)
|
||||
@@ -170,14 +229,14 @@ namespace TrackballControls {
|
||||
if (Vec2.squaredMagnitude(panMouseChange)) {
|
||||
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed)
|
||||
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), object.up)
|
||||
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up)
|
||||
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0])
|
||||
|
||||
Vec3.setMagnitude(panObjUp, object.up, panMouseChange[1])
|
||||
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1])
|
||||
Vec3.add(panOffset, panOffset, panObjUp)
|
||||
|
||||
Vec3.add(object.position, object.position, panOffset)
|
||||
Vec3.add(target, target, panOffset)
|
||||
Vec3.add(camera.position, camera.position, panOffset)
|
||||
Vec3.add(camera.target, camera.target, panOffset)
|
||||
|
||||
if (p.staticMoving) {
|
||||
Vec2.copy(_panStart, _panEnd)
|
||||
@@ -193,14 +252,16 @@ namespace TrackballControls {
|
||||
function checkDistances() {
|
||||
if (Vec3.squaredMagnitude(_eye) > p.maxDistance * p.maxDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.maxDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
|
||||
if (Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) {
|
||||
Vec3.setMagnitude(_eye, _eye, p.minDistance)
|
||||
Vec3.add(object.position, target, _eye)
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
Vec2.copy(_zoomStart, _zoomEnd)
|
||||
Vec2.copy(_focusStart, _focusEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,20 +271,19 @@ namespace TrackballControls {
|
||||
if (lastUpdated === t) return;
|
||||
if (p.spin) spin(t - lastUpdated);
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
|
||||
rotateCamera()
|
||||
zRotateCamera()
|
||||
zoomCamera()
|
||||
focusCamera()
|
||||
panCamera()
|
||||
|
||||
Vec3.add(object.position, target, _eye)
|
||||
|
||||
Vec3.add(camera.position, camera.target, _eye)
|
||||
checkDistances()
|
||||
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
|
||||
if (Vec3.squaredDistance(lastPosition, object.position) > EPSILON.Value) {
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
if (Vec3.squaredDistance(lastPosition, camera.position) > EPSILON) {
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
lastUpdated = t;
|
||||
@@ -231,54 +291,82 @@ namespace TrackballControls {
|
||||
|
||||
/** Reset object's vectors and the target vector to their initial values */
|
||||
function reset() {
|
||||
Vec3.copy(target, target0)
|
||||
Vec3.copy(object.position, position0)
|
||||
Vec3.copy(object.up, up0)
|
||||
Vec3.copy(camera.target, target0)
|
||||
Vec3.copy(camera.position, position0)
|
||||
Vec3.copy(camera.up, up0)
|
||||
|
||||
Vec3.sub(_eye, object.position, target)
|
||||
cameraLookAt(object.position, object.up, object.direction, target)
|
||||
Vec3.copy(lastPosition, object.position)
|
||||
Vec3.sub(_eye, camera.position, camera.target)
|
||||
Vec3.copy(lastPosition, camera.position)
|
||||
}
|
||||
|
||||
// listeners
|
||||
|
||||
function onDrag({ pageX, pageY, buttons, isStart }: DragInput) {
|
||||
function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
|
||||
_isInteracting = true;
|
||||
|
||||
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers)
|
||||
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers)
|
||||
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers)
|
||||
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers)
|
||||
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers)
|
||||
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers)
|
||||
|
||||
getMouseOnCircle(pageX, pageY)
|
||||
getMouseOnScreen(pageX, pageY)
|
||||
|
||||
if (isStart) {
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) {
|
||||
Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_rotPrev, _rotCurr)
|
||||
}
|
||||
if (dragRotateZ) {
|
||||
Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
Vec2.copy(_zRotPrev, _zRotCurr)
|
||||
}
|
||||
if (dragZoom || dragFocusZoom) {
|
||||
Vec2.copy(_zoomStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_zoomEnd, _zoomStart)
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY))
|
||||
}
|
||||
if (dragFocus) {
|
||||
Vec2.copy(_focusStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_focusEnd, _focusStart)
|
||||
}
|
||||
if (dragPan) {
|
||||
Vec2.copy(_panStart, mouseOnScreenVec2)
|
||||
Vec2.copy(_panEnd, _panStart)
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons === ButtonsType.Flag.Primary) {
|
||||
Vec2.copy(_movePrev, _moveCurr)
|
||||
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Auxilary) {
|
||||
Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY))
|
||||
} else if (buttons === ButtonsType.Flag.Secondary) {
|
||||
Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY))
|
||||
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2)
|
||||
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2)
|
||||
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2)
|
||||
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
|
||||
if (dragFocusZoom) {
|
||||
const dist = Vec3.distance(camera.state.position, camera.state.target);
|
||||
camera.setState({ radius: dist / 5 })
|
||||
}
|
||||
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
|
||||
}
|
||||
|
||||
function onInteractionEnd() {
|
||||
_isInteracting = false;
|
||||
}
|
||||
|
||||
function onWheel({ dy }: WheelInput) {
|
||||
_zoomStart[1] -= dy * 0.0001
|
||||
function onWheel({ dx, dy, dz, buttons, modifiers }: WheelInput) {
|
||||
const delta = absMax(dx, dy, dz)
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_zoomEnd[1] += delta * 0.0001
|
||||
}
|
||||
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
|
||||
_focusEnd[1] += delta * 0.0001
|
||||
}
|
||||
}
|
||||
|
||||
function onPinch({ fraction }: PinchInput) {
|
||||
_isInteracting = true;
|
||||
_zoomStart[1] -= (fraction - 1) * 0.1
|
||||
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
|
||||
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
|
||||
_isInteracting = true;
|
||||
_zoomEnd[1] += (fraction - 1) * 0.1
|
||||
}
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
@@ -295,7 +383,7 @@ namespace TrackballControls {
|
||||
function spin(deltaT: number) {
|
||||
const frameSpeed = (p.spinSpeed || 0) / 1000;
|
||||
_spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
|
||||
if (!_isInteracting) Vec2.add(_moveCurr, _movePrev, _spinSpeed);
|
||||
if (!_isInteracting) Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
|
||||
}
|
||||
|
||||
// force an update at start
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -13,7 +13,7 @@ import Scene from '../../mol-gl/scene';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/tables';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { TransformData } from '../../mol-geo/geometry/transform-data';
|
||||
import { sphereVertexCount } from '../../mol-geo/primitive/sphere';
|
||||
import { ValueCell } from '../../mol-util';
|
||||
@@ -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) => {
|
||||
@@ -81,7 +79,8 @@ export class BoundingSphereHelper {
|
||||
}
|
||||
})
|
||||
|
||||
this.scene.update(void 0, false);
|
||||
this.scene.update(void 0, false)
|
||||
this.scene.commit()
|
||||
}
|
||||
|
||||
syncVisibility() {
|
||||
@@ -115,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 {
|
||||
@@ -132,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)
|
||||
}
|
||||
@@ -11,13 +11,15 @@ import InputObserver, { ModifiersKeys, ButtonsType } from '../../mol-util/input/
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
|
||||
type Canvas3D = import('../canvas3d').Canvas3D
|
||||
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
|
||||
type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
|
||||
|
||||
export class Canvas3dInteractionHelper {
|
||||
private ev = RxEventHelper.create();
|
||||
|
||||
readonly events = {
|
||||
highlight: this.ev<import('../canvas3d').Canvas3D.HighlightEvent>(),
|
||||
click: this.ev<import('../canvas3d').Canvas3D.ClickEvent>(),
|
||||
hover: this.ev<HoverEvent>(),
|
||||
click: this.ev<ClickEvent>(),
|
||||
};
|
||||
|
||||
private cX = -1;
|
||||
@@ -36,6 +38,7 @@ export class Canvas3dInteractionHelper {
|
||||
private inside = false;
|
||||
|
||||
private buttons: ButtonsType = ButtonsType.create(0);
|
||||
private button: ButtonsType.Flag = ButtonsType.create(0);
|
||||
private modifiers: ModifiersKeys = ModifiersKeys.None;
|
||||
|
||||
private identify(isClick: boolean, t: number) {
|
||||
@@ -48,18 +51,18 @@ export class Canvas3dInteractionHelper {
|
||||
if (!this.id) return;
|
||||
|
||||
if (isClick) {
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
return;
|
||||
}
|
||||
|
||||
// only highlight the latest
|
||||
if (!this.inside || this.currentIdentifyT !== t) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loci = this.getLoci(this.id);
|
||||
// only broadcast the latest hover
|
||||
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
|
||||
this.events.highlight.next({ current: loci, prev: this.prevLoci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
}
|
||||
@@ -75,23 +78,25 @@ export class Canvas3dInteractionHelper {
|
||||
leave() {
|
||||
this.inside = false;
|
||||
if (this.prevLoci.loci !== EmptyLoci) {
|
||||
const prev = this.prevLoci;
|
||||
this.prevLoci = Representation.Loci.Empty;
|
||||
this.events.highlight.next({ current: this.prevLoci, prev });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
}
|
||||
|
||||
move(x: number, y: number, modifiers: ModifiersKeys) {
|
||||
move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.inside = true;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
}
|
||||
|
||||
select(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
|
||||
select(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.identify(true, 0);
|
||||
}
|
||||
@@ -99,7 +104,7 @@ export class Canvas3dInteractionHelper {
|
||||
modify(modifiers: ModifiersKeys) {
|
||||
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
|
||||
this.modifiers = modifiers;
|
||||
this.events.highlight.next({ current: this.prevLoci, prev: this.prevLoci, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -107,17 +112,17 @@ export class Canvas3dInteractionHelper {
|
||||
}
|
||||
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
|
||||
input.move.subscribe(({x, y, inside, buttons, modifiers }) => {
|
||||
if (!inside || buttons) { return; }
|
||||
this.move(x, y, modifiers);
|
||||
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
|
||||
if (!inside) return;
|
||||
this.move(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.leave.subscribe(() => {
|
||||
this.leave();
|
||||
});
|
||||
|
||||
input.click.subscribe(({x, y, buttons, modifiers }) => {
|
||||
this.select(x, y, buttons, modifiers);
|
||||
input.click.subscribe(({x, y, buttons, button, modifiers }) => {
|
||||
this.select(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.modifiers.subscribe(modifiers => this.modify(modifiers));
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { createTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class DrawPass {
|
||||
colorTarget: RenderTarget
|
||||
@@ -18,14 +19,14 @@ export class DrawPass {
|
||||
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions } = webgl
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions, resources } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = webgl.createRenderTarget(width, height)
|
||||
this.packedDepth = !extensions.depthTexture
|
||||
this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
|
||||
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest')
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexture.define(width, height)
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
@@ -41,30 +42,33 @@ export class DrawPass {
|
||||
}
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
const { webgl, renderer, scene, debugHelper, colorTarget, depthTarget } = this
|
||||
const { gl } = webgl
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
} else {
|
||||
colorTarget.bind()
|
||||
if (!this.packedDepth) {
|
||||
// TODO unlcear why it is not enough to call `attachFramebuffer` in `Texture.reset`
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
}
|
||||
}
|
||||
renderer.setViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
renderer.render(scene, 'color', true)
|
||||
|
||||
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight())
|
||||
renderer.render(scene, camera, 'color', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'color', false)
|
||||
renderer.render(debugHelper.scene, camera, 'color', false, transparentBackground)
|
||||
}
|
||||
|
||||
// do a depth pass if not rendering to drawing buffer and
|
||||
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (!toDrawingBuffer && depthTarget) {
|
||||
depthTarget.bind()
|
||||
renderer.render(scene, 'depth', true)
|
||||
renderer.render(scene, camera, 'depth', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, 'depth', false)
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
src/mol-canvas3d/passes/image.ts
Normal file
98
src/mol-canvas3d/passes/image.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { DrawPass } from './draw'
|
||||
import { PostprocessingPass, PostprocessingParams } from './postprocessing'
|
||||
import { MultiSamplePass, MultiSampleParams } from './multi-sample'
|
||||
import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
}
|
||||
export type ImageProps = PD.Values<typeof ImageParams>
|
||||
|
||||
export class ImagePass {
|
||||
private _width = 1024
|
||||
private _height = 768
|
||||
private _camera = new Camera()
|
||||
private _transparentBackground = false
|
||||
|
||||
private _colorTarget: RenderTarget
|
||||
get colorTarget() { return this._colorTarget }
|
||||
|
||||
readonly drawPass: DrawPass
|
||||
private readonly postprocessing: PostprocessingPass
|
||||
private readonly multiSample: MultiSamplePass
|
||||
|
||||
get width() { return this._width }
|
||||
get height() { return this._height }
|
||||
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
|
||||
const p = { ...PD.getDefaultValues(ImageParams), ...props }
|
||||
|
||||
this._transparentBackground = p.transparentBackground
|
||||
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
|
||||
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
|
||||
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
|
||||
|
||||
this.setSize(this._width, this._height)
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
if (width === this._width && height === this._height) return
|
||||
|
||||
this._width = width
|
||||
this._height = height
|
||||
|
||||
this.drawPass.setSize(width, height)
|
||||
this.postprocessing.setSize(width, height)
|
||||
this.multiSample.setSize(width, height)
|
||||
}
|
||||
|
||||
setProps(props: Partial<ImageProps> = {}) {
|
||||
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
|
||||
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) this.multiSample.setProps(props.multiSample)
|
||||
}
|
||||
|
||||
render() {
|
||||
Camera.copySnapshot(this._camera.state, this.camera.state)
|
||||
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
|
||||
this._camera.update()
|
||||
|
||||
this.renderer.setViewport(0, 0, this._width, this._height);
|
||||
|
||||
if (this.multiSample.enabled) {
|
||||
this.multiSample.render(false, this._transparentBackground)
|
||||
this._colorTarget = this.multiSample.colorTarget
|
||||
} else {
|
||||
this.drawPass.render(false, this._transparentBackground)
|
||||
if (this.postprocessing.enabled) {
|
||||
this.postprocessing.render(false)
|
||||
this._colorTarget = this.postprocessing.target
|
||||
} else {
|
||||
this._colorTarget = this.drawPass.colorTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getImageData(width: number, height: number) {
|
||||
this.setSize(width, height)
|
||||
this.render()
|
||||
const pd = this.colorTarget.getPixelData()
|
||||
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { RenderTarget, createRenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
import { PostprocessingPass } from './postprocessing';
|
||||
import { DrawPass } from './draw';
|
||||
@@ -35,7 +35,7 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
|
||||
const values: Values<typeof ComposeSchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
uWeight: ValueCell.create(1.0),
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
|
||||
|
||||
export class MultiSamplePass {
|
||||
props: MultiSampleProps
|
||||
colorTarget: RenderTarget
|
||||
|
||||
private composeTarget: RenderTarget
|
||||
private holdTarget: RenderTarget
|
||||
@@ -65,8 +66,9 @@ export class MultiSamplePass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
|
||||
const { gl } = webgl
|
||||
this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
|
||||
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props }
|
||||
}
|
||||
@@ -92,6 +94,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this.colorTarget.setSize(width, height)
|
||||
this.composeTarget.setSize(width, height)
|
||||
this.holdTarget.setSize(width, height)
|
||||
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
|
||||
@@ -102,15 +105,15 @@ export class MultiSamplePass {
|
||||
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
|
||||
}
|
||||
|
||||
render() {
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
if (this.props.mode === 'temporal') {
|
||||
this.renderTemporalMultiSample()
|
||||
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
|
||||
} else {
|
||||
this.renderMultiSample()
|
||||
this.renderMultiSample(toDrawingBuffer, transparentBackground)
|
||||
}
|
||||
}
|
||||
|
||||
private renderMultiSample() {
|
||||
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -128,14 +131,15 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
for (let i = 0; i < offsetList.length; ++i) {
|
||||
const offset = offsetList[i]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// the theory is that equal weights for each sample lead to an accumulation of rounding
|
||||
// errors. The following equation varies the sampleWeight per sample so that it is uniformly
|
||||
@@ -145,7 +149,7 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -168,16 +172,20 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample() {
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -197,7 +205,7 @@ export class MultiSamplePass {
|
||||
const i = this.sampleIndex
|
||||
|
||||
if (i === 0) {
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
@@ -215,7 +223,8 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
@@ -223,10 +232,10 @@ export class MultiSamplePass {
|
||||
for (let i = 0; i < numSamplesPerFrame; ++i) {
|
||||
const offset = offsetList[this.sampleIndex]
|
||||
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -252,7 +261,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, composeTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
state.disable(gl.BLEND)
|
||||
compose.render()
|
||||
@@ -261,7 +274,11 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
|
||||
ValueCell.update(compose.values.tColor, holdTarget.texture)
|
||||
compose.update()
|
||||
webgl.unbindFramebuffer()
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
this.colorTarget.bind()
|
||||
}
|
||||
gl.viewport(0, 0, width, height)
|
||||
if (accumulationWeight === 0) state.disable(gl.BLEND)
|
||||
else state.enable(gl.BLEND)
|
||||
@@ -269,7 +286,7 @@ export class MultiSamplePass {
|
||||
}
|
||||
|
||||
camera.viewOffset.enabled = false
|
||||
camera.updateMatrices()
|
||||
camera.update()
|
||||
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,25 +5,29 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
|
||||
const readBuffer = new Uint8Array(4)
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class PickPass {
|
||||
pickDirty = true
|
||||
|
||||
objectPickTarget: RenderTarget
|
||||
instancePickTarget: RenderTarget
|
||||
groupPickTarget: RenderTarget
|
||||
|
||||
private objectBuffer: Uint8Array
|
||||
private instanceBuffer: Uint8Array
|
||||
private groupBuffer: Uint8Array
|
||||
|
||||
private pickScale: number
|
||||
private pickWidth: number
|
||||
private pickHeight: number
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) {
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
|
||||
const { gl } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
@@ -31,35 +35,75 @@ export class PickPass {
|
||||
this.pickScale = pickBaseScale / webgl.pixelRatio
|
||||
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()
|
||||
}
|
||||
|
||||
private setupBuffers() {
|
||||
const bufferSize = this.pickWidth * this.pickHeight * 4
|
||||
if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
|
||||
this.objectBuffer = new Uint8Array(bufferSize)
|
||||
this.instanceBuffer = new Uint8Array(bufferSize)
|
||||
this.groupBuffer = new Uint8Array(bufferSize)
|
||||
}
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this.pickScale = this.pickBaseScale / this.webgl.pixelRatio
|
||||
this.pickWidth = Math.round(width * this.pickScale)
|
||||
this.pickHeight = Math.round(height * this.pickScale)
|
||||
|
||||
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight)
|
||||
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight)
|
||||
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight)
|
||||
|
||||
this.setupBuffers()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { renderer, scene } = this
|
||||
const { renderer, scene, camera } = this
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, 'pickObject', true);
|
||||
renderer.render(scene, camera, 'pickObject', true, false);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, 'pickInstance', true);
|
||||
renderer.render(scene, camera, 'pickInstance', true, false);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, 'pickGroup', true);
|
||||
renderer.render(scene, camera, 'pickGroup', true, false);
|
||||
|
||||
this.pickDirty = false
|
||||
}
|
||||
|
||||
private syncBuffers() {
|
||||
const { webgl } = this
|
||||
|
||||
this.objectPickTarget.bind()
|
||||
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer)
|
||||
|
||||
this.instancePickTarget.bind()
|
||||
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer)
|
||||
|
||||
this.groupPickTarget.bind()
|
||||
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer)
|
||||
}
|
||||
|
||||
private getId(x: number, y: number, buffer: Uint8Array) {
|
||||
const idx = (y * this.pickWidth + x) * 4
|
||||
return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2])
|
||||
}
|
||||
|
||||
identify(x: number, y: number): PickingId | undefined {
|
||||
const { webgl, pickScale } = this
|
||||
if (webgl.isContextLost) return
|
||||
|
||||
const { gl } = webgl
|
||||
if (this.pickDirty) this.render()
|
||||
if (this.pickDirty) {
|
||||
this.render()
|
||||
this.syncBuffers()
|
||||
}
|
||||
|
||||
x *= webgl.pixelRatio
|
||||
y *= webgl.pixelRatio
|
||||
@@ -68,19 +112,13 @@ export class PickPass {
|
||||
const xp = Math.round(x * pickScale)
|
||||
const yp = Math.round(y * pickScale)
|
||||
|
||||
this.objectPickTarget.bind()
|
||||
webgl.readPixels(xp, yp, 1, 1, readBuffer)
|
||||
const objectId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
|
||||
const objectId = this.getId(xp, yp, this.objectBuffer)
|
||||
if (objectId === -1) return
|
||||
|
||||
this.instancePickTarget.bind()
|
||||
webgl.readPixels(xp, yp, 1, 1, readBuffer)
|
||||
const instanceId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
|
||||
const instanceId = this.getId(xp, yp, this.instanceBuffer)
|
||||
if (instanceId === -1) return
|
||||
|
||||
this.groupPickTarget.bind()
|
||||
webgl.readPixels(xp, yp, 1, 1, readBuffer)
|
||||
const groupId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
|
||||
const groupId = this.getId(xp, yp, this.groupBuffer)
|
||||
if (groupId === -1) return
|
||||
|
||||
return { objectId, instanceId, groupId }
|
||||
|
||||
@@ -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(64, { 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,49 +125,37 @@ 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()
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.state.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.state.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.state.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.state.fogNear)
|
||||
ValueCell.update(this.renderable.values.uFar, this.camera.far)
|
||||
ValueCell.update(this.renderable.values.uNear, this.camera.near)
|
||||
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
|
||||
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
|
||||
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
|
||||
|
||||
const { gl, state } = this.webgl
|
||||
|
||||
@@ -4,16 +4,57 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
/** resize canvas to container element */
|
||||
/** Set canvas size taking `devicePixelRatio` into account */
|
||||
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
|
||||
canvas.width = Math.round(window.devicePixelRatio * width)
|
||||
canvas.height = Math.round(window.devicePixelRatio * height)
|
||||
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
|
||||
}
|
||||
|
||||
/** Resize canvas to container element taking `devicePixelRatio` into account */
|
||||
export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
|
||||
let w = window.innerWidth
|
||||
let h = window.innerHeight
|
||||
let width = window.innerWidth
|
||||
let height = window.innerHeight
|
||||
if (container !== document.body) {
|
||||
let bounds = container.getBoundingClientRect()
|
||||
w = bounds.right - bounds.left
|
||||
h = bounds.bottom - bounds.top
|
||||
width = bounds.right - bounds.left
|
||||
height = bounds.bottom - bounds.top
|
||||
}
|
||||
canvas.width = window.devicePixelRatio * w
|
||||
canvas.height = window.devicePixelRatio * h
|
||||
Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
|
||||
setCanvasSize(canvas, width, height)
|
||||
}
|
||||
|
||||
function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?: string, quality?: any) {
|
||||
const bin = atob(canvas.toDataURL(type, quality).split(',')[1])
|
||||
const len = bin.length
|
||||
const len32 = len >> 2
|
||||
const a8 = new Uint8Array(len)
|
||||
const a32 = new Uint32Array(a8.buffer, 0, len32)
|
||||
|
||||
let j = 0
|
||||
for (let i = 0; i < len32; ++i) {
|
||||
a32[i] = bin.charCodeAt(j++) |
|
||||
bin.charCodeAt(j++) << 8 |
|
||||
bin.charCodeAt(j++) << 16 |
|
||||
bin.charCodeAt(j++) << 24
|
||||
}
|
||||
|
||||
let tailLength = len & 3;
|
||||
while (tailLength--) a8[j] = bin.charCodeAt(j++)
|
||||
|
||||
callback(new Blob([a8], { type: type || 'image/png' }));
|
||||
}
|
||||
|
||||
export async function canvasToBlob(canvas: HTMLCanvasElement, type?: string, quality?: any): Promise<Blob> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (blob: Blob | null) => {
|
||||
if (blob) resolve(blob)
|
||||
else reject('no blob returned')
|
||||
}
|
||||
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
_canvasToBlob(canvas, callback, type, quality)
|
||||
} else {
|
||||
canvas.toBlob(callback, type, quality)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
import * as ColumnHelpers from './column-helpers'
|
||||
import { Tensor as Tensors } from '../../mol-math/linear-algebra'
|
||||
import { Tokens } from '../../mol-io/reader/common/text/tokenizer';
|
||||
import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../mol-io/reader/common/text/number-parser';
|
||||
|
||||
interface Column<T> {
|
||||
readonly schema: Column.Schema,
|
||||
@@ -35,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' };
|
||||
@@ -71,6 +73,7 @@ namespace Column {
|
||||
rowCount: number,
|
||||
schema: T,
|
||||
valueKind?: (row: number) => ValueKind,
|
||||
areValuesEqual?: (rowA: number, rowB: number) => boolean
|
||||
}
|
||||
|
||||
export interface ArraySpec<T extends Schema> {
|
||||
@@ -89,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);
|
||||
@@ -128,6 +137,45 @@ 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({
|
||||
value: (row: number) => fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0,
|
||||
rowCount: count,
|
||||
schema: Schema.int,
|
||||
});
|
||||
}
|
||||
|
||||
export function ofFloatTokens(tokens: Tokens) {
|
||||
const { count, data, indices } = tokens
|
||||
return lambdaColumn({
|
||||
value: (row: number) => fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0,
|
||||
rowCount: count,
|
||||
schema: Schema.float,
|
||||
});
|
||||
}
|
||||
|
||||
export function ofStringTokens(tokens: Tokens) {
|
||||
const { count, data, indices } = tokens
|
||||
return lambdaColumn({
|
||||
value: (row: number) => {
|
||||
const ret = data.substring(indices[2 * row], indices[2 * row + 1]);
|
||||
if (ret === '.' || ret === '?') return '';
|
||||
return ret;
|
||||
},
|
||||
rowCount: count,
|
||||
schema: Schema.str,
|
||||
});
|
||||
}
|
||||
|
||||
export function window<T>(column: Column<T>, start: number, end: number) {
|
||||
return windowColumn(column, start, end);
|
||||
}
|
||||
@@ -214,7 +262,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
|
||||
}
|
||||
}
|
||||
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqual, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
return {
|
||||
schema: schema,
|
||||
__array: void 0,
|
||||
@@ -227,7 +275,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, sch
|
||||
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
|
||||
return array;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
|
||||
areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,19 +304,19 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
return ret;
|
||||
}
|
||||
: isTyped
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB]
|
||||
}
|
||||
}
|
||||
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number) {
|
||||
function windowColumn<T>(column: Column<T>, start: number, end: number): Column<T> {
|
||||
if (!column.isDefined) return Column.Undefined(end - start, column.schema);
|
||||
if (start === 0 && end === column.rowCount) return column;
|
||||
if (!!column.__array && ColumnHelpers.isTypedArray(column.__array)) return windowTyped(column, start, end);
|
||||
@@ -277,7 +325,8 @@ function windowColumn<T>(column: Column<T>, start: number, end: number) {
|
||||
|
||||
function windowTyped<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
const array = ColumnHelpers.typedArrayWindow(c.__array, { start, end });
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: c.valueKind }) as any;
|
||||
const vk = c.valueKind;
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: row => vk(start + row) }) as any;
|
||||
}
|
||||
|
||||
function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
@@ -309,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);
|
||||
}
|
||||
|
||||
@@ -319,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) {
|
||||
@@ -218,6 +237,16 @@ namespace Table {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function toArrays<S extends Schema>(table: Table<S>) {
|
||||
const arrays: { [k: string]: ArrayLike<any> } = {}
|
||||
const { _columns } = table;
|
||||
for (let i = 0; i < _columns.length; i++) {
|
||||
const c = _columns[i]
|
||||
arrays[c] = table[c].toArray();
|
||||
}
|
||||
return arrays as { [k in keyof S]: ArrayLike<S[k]['T']> }
|
||||
}
|
||||
|
||||
export function formatToString<S extends Schema>(table: Table<S>) {
|
||||
const sb = StringBuilder.create();
|
||||
|
||||
|
||||
@@ -90,14 +90,14 @@ class LinkedListImpl<T> implements LinkedList<T> {
|
||||
if (node.previous !== null) {
|
||||
node.previous.next = node.next;
|
||||
}
|
||||
else if (/*first == item*/ node.previous === null) {
|
||||
else if (/* first == item*/ node.previous === null) {
|
||||
this.first = node.next;
|
||||
}
|
||||
|
||||
if (node.next !== null) {
|
||||
node.next.previous = node.previous;
|
||||
}
|
||||
else if (/*last == item*/ node.next === null) {
|
||||
else if (/* last == item*/ node.next === null) {
|
||||
this.last = node.previous;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace UniqueArray {
|
||||
array[array.length] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function has<K, T>({ keys }: UniqueArray<K, T>, key: K) {
|
||||
return keys.has(key);
|
||||
}
|
||||
}
|
||||
|
||||
export { UniqueArray }
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import OrderedSet from '../ordered-set'
|
||||
import Interval from '../interval'
|
||||
import SortedArray from '../sorted-array';
|
||||
|
||||
describe('ordered set', () => {
|
||||
function ordSetToArray(set: OrderedSet) {
|
||||
@@ -81,6 +82,13 @@ describe('ordered set', () => {
|
||||
expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([12, 13, 16]))).toBe(false);
|
||||
});
|
||||
|
||||
it('isSubsetIS', () => {
|
||||
expect(OrderedSet.isSubset(
|
||||
Interval.ofRange(1271, 1295),
|
||||
OrderedSet.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
|
||||
)).toBe(true);
|
||||
});
|
||||
|
||||
it('access/membership', () => {
|
||||
expect(OrderedSet.has(empty, 10)).toBe(false);
|
||||
expect(OrderedSet.indexOf(empty, 10)).toBe(-1);
|
||||
@@ -156,6 +164,15 @@ describe('ordered set', () => {
|
||||
testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
|
||||
it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
|
||||
|
||||
testEq('idxIntersect 1', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([1, 2, 4]),
|
||||
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6]),
|
||||
SortedArray.ofSortedArray([2, 4, 5, 8])), [0, 2]);
|
||||
testEq('idxIntersect 2', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([0, 1]),
|
||||
SortedArray.ofSortedArray([1, 2]),
|
||||
SortedArray.ofSortedArray([1, 2])), [0, 1]);
|
||||
|
||||
testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []);
|
||||
testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []);
|
||||
testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -63,8 +63,102 @@ describe('sortedArray', () => {
|
||||
compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
|
||||
})
|
||||
|
||||
it('indicesOf 2', () => {
|
||||
compareArrays(
|
||||
SortedArray.indicesOf(
|
||||
SortedArray.ofSortedArray([0, 1, 2, 3, 4, 8, 9, 10]),
|
||||
SortedArray.ofSortedArray([1, 3, 4, 9, 10])
|
||||
),
|
||||
[1, 3, 4, 6, 7]
|
||||
);
|
||||
})
|
||||
|
||||
test('intersectionSize', SortedArray.intersectionSize(a1234, a2468), 2);
|
||||
|
||||
// console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
|
||||
// console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
|
||||
it('union1', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 836, 837, 838, 839, 840, 841, 842, 843]),
|
||||
SortedArray.ofSortedArray([835])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union2', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 832, 833]),
|
||||
SortedArray.ofSortedArray([831])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833])
|
||||
)
|
||||
})
|
||||
|
||||
it('union3ab', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835]),
|
||||
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union3ba', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843]),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835])
|
||||
),
|
||||
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
|
||||
)
|
||||
})
|
||||
|
||||
it('union4', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([1, 3, 5, 7, 9]),
|
||||
SortedArray.ofSortedArray([2, 4, 6, 8])
|
||||
),
|
||||
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
)
|
||||
})
|
||||
|
||||
it('union5', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([2, 3, 4, 20, 21, 22]),
|
||||
SortedArray.ofSortedArray([10, 11, 12])
|
||||
),
|
||||
SortedArray.ofSortedArray([2, 3, 4, 10, 11, 12, 20, 21, 22])
|
||||
)
|
||||
})
|
||||
|
||||
it('union6', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819]),
|
||||
SortedArray.ofSortedArray([1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758])
|
||||
),
|
||||
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819])
|
||||
)
|
||||
})
|
||||
|
||||
it('union7', () => {
|
||||
compareArrays(
|
||||
SortedArray.union(
|
||||
SortedArray.ofSortedArray([3766, 3767, 3768, 3770, 3773, 3780, 3783, 3787, 3797]),
|
||||
SortedArray.ofSortedArray([3769, 3790, 3794])
|
||||
),
|
||||
SortedArray.ofSortedArray([3766, 3767, 3768, 3769, 3770, 3773, 3780, 3783, 3787, 3790, 3794, 3797])
|
||||
)
|
||||
})
|
||||
|
||||
it('isSubset', () => {
|
||||
expect(SortedArray.isSubset(
|
||||
SortedArray.ofSortedArray([1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295]),
|
||||
SortedArray.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
|
||||
)).toBe(true);
|
||||
})
|
||||
});
|
||||
@@ -17,7 +17,7 @@ describe('rangesArray', () => {
|
||||
it(`iterator, ${name}`, () => {
|
||||
const rangesIt = SortedRanges.transientSegments(ranges, set)
|
||||
const { index, start, end } = expectedValues
|
||||
|
||||
|
||||
let i = 0
|
||||
while (rangesIt.hasNext) {
|
||||
const segment = rangesIt.move()
|
||||
@@ -41,7 +41,7 @@ describe('rangesArray', () => {
|
||||
|
||||
testIterator('two ranges',
|
||||
SortedRanges.ofSortedRanges([1, 2, 3, 4]),
|
||||
OrderedSet.ofBounds(1, 4),
|
||||
OrderedSet.ofBounds(1, 5),
|
||||
{ index: [0, 1], start: [0, 2], end: [2, 4] }
|
||||
)
|
||||
testIterator('first range',
|
||||
@@ -62,7 +62,7 @@ describe('rangesArray', () => {
|
||||
testIterator('set in second range and beyond',
|
||||
SortedRanges.ofSortedRanges([1, 2, 3, 4]),
|
||||
SortedArray.ofSortedArray([3, 10]),
|
||||
{ index: [1], start: [0], end: [2] }
|
||||
{ index: [1], start: [0], end: [1] }
|
||||
)
|
||||
testIterator('length 1 range',
|
||||
SortedRanges.ofSortedRanges([1, 1, 3, 4]),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
@@ -9,6 +9,7 @@ import Tuple from '../tuple'
|
||||
export const Empty = Tuple.Zero;
|
||||
export function ofRange(min: number, max: number) { return max < min ? Tuple.create(min, min) : Tuple.create(min, max + 1); }
|
||||
export function ofBounds(start: number, end: number) { return end <= start ? Tuple.create(start, start) : Tuple.create(start, end); }
|
||||
export function ofLength(length: number) { return length < 0 ? Tuple.create(0, 0) : Tuple.create(0, length); }
|
||||
export const is = Tuple.is;
|
||||
|
||||
export const start = Tuple.fst;
|
||||
@@ -17,6 +18,7 @@ export const min = Tuple.fst;
|
||||
export function max(i: Tuple) { return Tuple.snd(i) - 1; }
|
||||
export function size(i: Tuple) { return Tuple.snd(i) - Tuple.fst(i); }
|
||||
export const hashCode = Tuple.hashCode;
|
||||
export const toString = Tuple.toString;
|
||||
|
||||
export function has(int: Tuple, v: number) { return Tuple.fst(int) <= v && v < Tuple.snd(int); }
|
||||
/** Returns the index of `x` in `set` or -1 if not found. */
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -36,6 +36,8 @@ export function end(set: OrderedSetImpl) { return I.is(set) ? I.end(set) : S.end
|
||||
export function hashCode(set: OrderedSetImpl) { return I.is(set) ? I.hashCode(set) : S.hashCode(set); }
|
||||
// TODO: possibly add more hash functions to allow for multilevel hashing.
|
||||
|
||||
export function toString(set: OrderedSetImpl) { return I.is(set) ? I.toString(set) : S.toString(set); }
|
||||
|
||||
export function areEqual(a: OrderedSetImpl, b: OrderedSetImpl) {
|
||||
if (I.is(a)) {
|
||||
if (I.is(b)) return I.areEqual(a, b);
|
||||
@@ -124,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) {
|
||||
@@ -284,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);
|
||||
}
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -35,6 +35,11 @@ export function hashCode(xs: Nums) {
|
||||
if (s > 2) return hash4(s, xs[0], xs[s - 1], xs[s >> 1]);
|
||||
return hash3(s, xs[0], xs[s - 1]);
|
||||
}
|
||||
export function toString(xs: Nums) {
|
||||
const s = xs.length;
|
||||
if (s > 5) return `[${xs[0]}, ${xs[1]}, ..., ${xs[s - 1]}], length ${s}`;
|
||||
return `[${(xs as number[]).join(', ')}]`;
|
||||
}
|
||||
|
||||
/** Returns the index of `x` in `set` or -1 if not found. */
|
||||
export function indexOf(xs: Nums, v: number) {
|
||||
@@ -42,8 +47,10 @@ export function indexOf(xs: Nums, v: number) {
|
||||
return l === 0 ? -1 : xs[0] <= v && v <= xs[l - 1] ? binarySearchRange(xs, v, 0, l) : -1;
|
||||
}
|
||||
export function indexOfInInterval(xs: Nums, v: number, bounds: Interval) {
|
||||
return indexOfInRange(xs, v, Interval.start(bounds), Interval.end(bounds))
|
||||
}
|
||||
export function indexOfInRange(xs: Nums, v: number, s: number, e: number) {
|
||||
const l = xs.length;
|
||||
const s = Interval.start(bounds), e = Interval.end(bounds);
|
||||
return l === 0 || e <= s ? -1 : xs[s] <= v && v <= xs[e - 1] ? binarySearchRange(xs, v, s, e) : -1;
|
||||
}
|
||||
export function has(xs: Nums, v: number) { return indexOf(xs, v) >= 0; }
|
||||
@@ -60,6 +67,11 @@ export function areEqual(a: Nums, b: Nums) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if `v` is smaller or equal the first element of `xs`
|
||||
* Returns length of `xs` if `v` is bigger than the last element of `xs`
|
||||
* Otherwise returns the first index where the value of `xs` is equal or bigger than `v`
|
||||
*/
|
||||
export function findPredecessorIndex(xs: Nums, v: number) {
|
||||
const len = xs.length;
|
||||
if (v <= xs[0]) return 0;
|
||||
@@ -159,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++; }
|
||||
@@ -188,6 +202,10 @@ export function union(a: Nums, b: Nums) {
|
||||
else { indices[offset++] = x; i++; j++; }
|
||||
}
|
||||
|
||||
// insert the remaining common part
|
||||
for (; i < endI; i++) indices[offset++] = a[i];
|
||||
for (; j < endJ; j++) indices[offset++] = b[j];
|
||||
|
||||
// insert the "tail"
|
||||
for (; i < lenA; i++) indices[offset++] = a[i];
|
||||
for (; j < lenB; j++) indices[offset++] = b[j];
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Interval {
|
||||
export const ofRange: <T extends number = number>(min: T, max: T) => Interval<T> = Impl.ofRange as any;
|
||||
/** Create interval from bounds [start, end), i.e. [start, end - 1] */
|
||||
export const ofBounds: <T extends number = number>(start: T, end: T) => Interval<T> = Impl.ofBounds as any;
|
||||
/** Create interval from length [0, length), i.e. [0, length - 1] */
|
||||
export const ofLength: <T extends number = number>(length: T) => Interval<T> = Impl.ofLength as any;
|
||||
export const is: <T extends number = number>(v: any) => v is Interval<T> = Impl.is as any;
|
||||
|
||||
/** Test if a value is within the bounds of the interval */
|
||||
@@ -34,6 +36,8 @@ namespace Interval {
|
||||
export const size: <T extends number = number>(interval: Interval<T>) => number = Impl.size as any;
|
||||
/** Hash code describing the interval */
|
||||
export const hashCode: <T extends number = number>(interval: Interval<T>) => number = Impl.hashCode as any;
|
||||
/** String representation of the interval */
|
||||
export const toString: <T extends number = number>(interval: Interval<T>) => string = Impl.toString as any;
|
||||
|
||||
/** Test if two intervals are identical */
|
||||
export const areEqual: <T extends number = number>(a: Interval<T>, b: Interval<T>) => boolean = Impl.areEqual as any;
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -8,7 +8,6 @@ import * as Base from './impl/ordered-set'
|
||||
import Interval from './interval'
|
||||
import SortedArray from './sorted-array';
|
||||
|
||||
/** test */
|
||||
namespace OrderedSet {
|
||||
export const Empty: OrderedSet = Base.Empty as any;
|
||||
export const ofSingleton: <T extends number = number>(value: T) => OrderedSet<T> = Base.ofSingleton as any;
|
||||
@@ -22,12 +21,14 @@ namespace OrderedSet {
|
||||
export const has: <T extends number = number>(set: OrderedSet<T>, x: T) => boolean = Base.has as any;
|
||||
/** Returns the index of `x` in `set` or -1 if not found. */
|
||||
export const indexOf: <T extends number = number>(set: OrderedSet<T>, x: T) => number = Base.indexOf as any;
|
||||
/** Returns the value in `set` at index `i`. */
|
||||
export const getAt: <T extends number = number>(set: OrderedSet<T>, i: number) => T = Base.getAt as any;
|
||||
|
||||
export const min: <T extends number = number>(set: OrderedSet<T>) => T = Base.min as any;
|
||||
export const max: <T extends number = number>(set: OrderedSet<T>) => T = Base.max as any;
|
||||
export const start: <T extends number = number>(set: OrderedSet<T>) => T = Base.start as any;
|
||||
export const end: <T extends number = number>(set: OrderedSet<T>) => T = Base.end as any;
|
||||
/** Number of elements in the OrderedSet */
|
||||
export const size: <T extends number = number>(set: OrderedSet<T>) => number = Base.size as any;
|
||||
export const hashCode: <T extends number = number>(set: OrderedSet<T>) => number = Base.hashCode as any;
|
||||
|
||||
@@ -38,8 +39,15 @@ 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;
|
||||
|
||||
/**
|
||||
* Returns 0 if `x` is smaller or equal the first element of `set`
|
||||
* Returns length of `set` if `x` is bigger than the last element of `set`
|
||||
* Otherwise returns the first index where the value of `set` is equal or bigger than `x`
|
||||
*/
|
||||
export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any;
|
||||
export const findPredecessorIndexInInterval: <T extends number = number>(set: OrderedSet<T>, x: T, range: Interval) => number = Base.findPredecessorIndexInInterval as any;
|
||||
export const findRange: <T extends number = number>(set: OrderedSet<T>, min: T, max: T) => Interval = Base.findRange as any;
|
||||
@@ -49,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);
|
||||
}
|
||||
@@ -62,10 +74,12 @@ namespace OrderedSet {
|
||||
OrderedSet.forEach(set, v => array.push(v))
|
||||
return array
|
||||
}
|
||||
|
||||
export function toString<T extends number = number>(set: OrderedSet<T>): string {
|
||||
return Base.toString(set)
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents bla */
|
||||
type OrderedSet<T extends number = number> = SortedArray<T> | Interval<T>
|
||||
|
||||
|
||||
export default OrderedSet
|
||||
@@ -18,7 +18,7 @@ namespace Segmentation {
|
||||
export const getSegment: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, value: T) => number = Impl.getSegment as any;
|
||||
export const projectValue: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any;
|
||||
|
||||
// Segment iterator that mutates a single segment object to mark all the segments.
|
||||
/** Segment iterator that mutates a single segment object to mark all the segments. */
|
||||
export const transientSegments: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, segment?: Segment) => Impl.SegmentIterator<I> = Impl.segments as any;
|
||||
|
||||
export type SegmentIterator<I extends number = number> = Impl.SegmentIterator<I>
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -12,9 +12,9 @@ namespace SortedArray {
|
||||
export const ofUnsortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofUnsortedArray as any;
|
||||
export const ofSingleton: <T extends number = number>(v: number) => SortedArray<T> = Impl.ofSingleton as any;
|
||||
export const ofSortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofSortedArray as any;
|
||||
// create sorted array [min, max] (it DOES contain the max value)
|
||||
/** create sorted array [min, max] (it DOES contain the max value) */
|
||||
export const ofRange: <T extends number = number>(min: T, max: T) => SortedArray<T> = Impl.ofRange as any;
|
||||
// create sorted array [min, max) (it does NOT contain the max value)
|
||||
/** create sorted array [min, max) (it does NOT contain the max value) */
|
||||
export const ofBounds: <T extends number = number>(min: T, max: T) => SortedArray<T> = (min, max) => Impl.ofRange(min, max - 1) as any;
|
||||
export const is: <T extends number = number>(v: any) => v is SortedArray<T> = Impl.is as any;
|
||||
|
||||
@@ -22,15 +22,17 @@ namespace SortedArray {
|
||||
/** Returns the index of `x` in `set` or -1 if not found. */
|
||||
export const indexOf: <T extends number = number>(array: SortedArray<T>, x: T) => number = Impl.indexOf as any;
|
||||
export const indexOfInInterval: <T extends number = number>(array: SortedArray<T>, x: number, bounds: Interval) => number = Impl.indexOfInInterval as any;
|
||||
export const indexOfInRange: <T extends number = number>(array: SortedArray<T>, x: number, start: number, end: number) => number = Impl.indexOfInRange as any;
|
||||
|
||||
// array[0]
|
||||
/** Returns `array[0]` */
|
||||
export const start: <T extends number = number>(array: SortedArray<T>) => T = Impl.start as any;
|
||||
// array[array.length - 1] + 1
|
||||
/** Returns `array[array.length - 1] + 1` */
|
||||
export const end: <T extends number = number>(array: SortedArray<T>) => T = Impl.end as any;
|
||||
export const min: <T extends number = number>(array: SortedArray<T>) => T = Impl.min as any;
|
||||
export const max: <T extends number = number>(array: SortedArray<T>) => T = Impl.max as any;
|
||||
export const size: <T extends number = number>(array: SortedArray<T>) => number = Impl.size as any;
|
||||
export const hashCode: <T extends number = number>(array: SortedArray<T>) => number = Impl.hashCode as any;
|
||||
export const toString: <T extends number = number>(array: SortedArray<T>) => string = Impl.toString as any;
|
||||
|
||||
export const areEqual: <T extends number = number>(a: SortedArray<T>, b: SortedArray<T>) => boolean = Impl.areEqual as any;
|
||||
export const areIntersecting: <T extends number = number>(a: SortedArray<T>, b: SortedArray<T>) => boolean = Impl.areIntersecting as any;
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -23,6 +23,64 @@ namespace SortedRanges {
|
||||
}
|
||||
return size
|
||||
}
|
||||
export function count<T extends number = number>(ranges: SortedRanges<T>) { return ranges.length / 2 }
|
||||
|
||||
export function startAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
|
||||
return ranges[index * 2]
|
||||
}
|
||||
export function endAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
|
||||
return ranges[index * 2 + 1] + 1
|
||||
}
|
||||
|
||||
export function minAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
|
||||
return ranges[index * 2]
|
||||
}
|
||||
export function maxAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
|
||||
return ranges[index * 2 + 1]
|
||||
}
|
||||
|
||||
export function areEqual<T extends number = number>(a: SortedRanges<T>, b: SortedRanges<T>) {
|
||||
if (a.length !== b.length) return false
|
||||
for (let i = 0, il = a.length; i < il; ++i) {
|
||||
if (a[i] !== b[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function forEach<T extends number = number>(ranges: SortedRanges<T>, f: (value: T, i: number) => void) {
|
||||
let k = 0
|
||||
for (let i = 0, il = ranges.length; i < il; i += 2) {
|
||||
for (let j = ranges[i], jl = ranges[i + 1]; j <= jl; ++j) {
|
||||
f(j, k);
|
||||
++k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns if a value of `set` is included in `ranges` */
|
||||
export function has<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
|
||||
return firstIntersectionIndex(ranges, set) !== -1
|
||||
}
|
||||
|
||||
/** Returns if a value of `set` is included in `ranges` from given index */
|
||||
export function hasFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number) {
|
||||
return firstIntersectionIndexFrom(ranges, set, from) !== -1
|
||||
}
|
||||
|
||||
export function firstIntersectionIndex<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>): number {
|
||||
return firstIntersectionIndexFrom(ranges, set, 0)
|
||||
}
|
||||
|
||||
export function firstIntersectionIndexFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number): number {
|
||||
if (minAt(ranges, from) > OrderedSet.max(set) || max(ranges) < OrderedSet.min(set)) return -1
|
||||
|
||||
for (let i = from, il = count(ranges); i < il; ++i) {
|
||||
const interval = Interval.ofRange(minAt(ranges, i), maxAt(ranges, i))
|
||||
if (OrderedSet.areIntersecting(interval, set)) return i
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
|
||||
return new Iterator<T, I>(ranges, set)
|
||||
@@ -32,53 +90,27 @@ namespace SortedRanges {
|
||||
private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T }
|
||||
|
||||
private curIndex = 0
|
||||
private maxIndex = 0
|
||||
private interval: Interval<T>
|
||||
private curMin: T = 0 as T
|
||||
|
||||
hasNext: boolean = false;
|
||||
|
||||
updateInterval() {
|
||||
this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1])
|
||||
}
|
||||
|
||||
updateValue() {
|
||||
this.value.index = this.curIndex / 2 as I
|
||||
this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex])
|
||||
this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1
|
||||
private updateValue() {
|
||||
this.value.index = this.curIndex as I
|
||||
this.value.start = OrderedSet.findPredecessorIndex(this.set, startAt(this.ranges, this.curIndex))
|
||||
this.value.end = OrderedSet.findPredecessorIndex(this.set, endAt(this.ranges, this.curIndex))
|
||||
}
|
||||
|
||||
move() {
|
||||
if (this.hasNext) {
|
||||
this.updateValue()
|
||||
while (this.curIndex <= this.maxIndex) {
|
||||
this.curIndex += 2
|
||||
this.curMin = Interval.end(this.interval)
|
||||
this.updateInterval()
|
||||
if (Interval.min(this.interval) >= this.curMin && OrderedSet.areIntersecting(this.interval, this.set)) break
|
||||
}
|
||||
this.hasNext = this.curIndex <= this.maxIndex
|
||||
this.curIndex = firstIntersectionIndexFrom(this.ranges, this.set, this.curIndex + 1)
|
||||
this.hasNext = this.curIndex !== -1
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
getRangeIndex(value: number) {
|
||||
const index = SortedArray.findPredecessorIndex(this.ranges, value)
|
||||
return (index % 2 === 1) ? index - 1 : index
|
||||
}
|
||||
|
||||
constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) {
|
||||
// TODO cleanup, refactor to make it clearer
|
||||
const min = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.min(set))
|
||||
const max = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.max(set) + 1)
|
||||
if (ranges.length && min !== max) {
|
||||
this.curIndex = this.getRangeIndex(OrderedSet.min(set))
|
||||
this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set)))
|
||||
this.curMin = this.ranges[this.curIndex]
|
||||
this.updateInterval()
|
||||
}
|
||||
|
||||
this.hasNext = ranges.length > 0 && min !== max && this.curIndex <= this.maxIndex
|
||||
this.curIndex = firstIntersectionIndex(ranges, set)
|
||||
this.hasNext = this.curIndex !== -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -73,6 +73,11 @@ namespace IntTuple {
|
||||
_float64[0] = t as any;
|
||||
return hash2(_int32[0], _int32[1]);
|
||||
}
|
||||
|
||||
export function toString(t: IntTuple) {
|
||||
_float64[0] = t as any;
|
||||
return `(${_int32[0]}, ${_int32[1]})`;
|
||||
}
|
||||
}
|
||||
|
||||
export default IntTuple
|
||||
@@ -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
|
||||
|
||||
@@ -73,7 +73,7 @@ export function sortedCantorPairing(a: number, b: number) {
|
||||
/**
|
||||
* 32 bit FNV-1a hash, see http://isthe.com/chongo/tech/comp/fnv/
|
||||
*/
|
||||
export function hashFnv32a(array: number[]) {
|
||||
export function hashFnv32a(array: ArrayLike<number>) {
|
||||
let hval = 0x811c9dc5;
|
||||
for (let i = 0, il = array.length; i < il; ++i) {
|
||||
hval ^= array[i];
|
||||
|
||||
@@ -9,11 +9,9 @@ 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/tables';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { NullLocation } from '../../mol-model/location';
|
||||
import { UniformColorTheme } from '../../mol-theme/color/uniform';
|
||||
import { UniformSizeTheme } from '../../mol-theme/size/uniform';
|
||||
@@ -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,20 +18,22 @@ 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/scale';
|
||||
import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
|
||||
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 {
|
||||
@@ -81,7 +117,7 @@ export namespace DirectVolume {
|
||||
Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
|
||||
Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
|
||||
]),
|
||||
list: PD.ColorScale<ColorListName>('RedYellowBlue', ColorListOptions),
|
||||
list: PD.ColorList<ColorListName>('red-yellow-blue', ColorListOptions),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { spline } from '../../../mol-math/interpolate';
|
||||
import { ColorScale, Color } from '../../../mol-util/color';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Vec2 } from '../../../mol-math/linear-algebra';
|
||||
import { ColorListName } from '../../../mol-util/color/scale';
|
||||
import { ColorListName } from '../../../mol-util/color/lists';
|
||||
|
||||
export interface ControlPoint { x: number, alpha: number }
|
||||
|
||||
|
||||
@@ -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)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
@@ -13,57 +13,6 @@ export type MarkerData = {
|
||||
uMarkerTexDim: ValueCell<Vec2>
|
||||
}
|
||||
|
||||
export enum MarkerAction {
|
||||
Highlight,
|
||||
RemoveHighlight,
|
||||
Select,
|
||||
Deselect,
|
||||
Toggle,
|
||||
Clear
|
||||
}
|
||||
|
||||
export function applyMarkerAction(array: Uint8Array, start: number, end: number, action: MarkerAction) {
|
||||
let changed = false
|
||||
for (let i = start; i < end; ++i) {
|
||||
let v = array[i]
|
||||
switch (action) {
|
||||
case MarkerAction.Highlight:
|
||||
if (v % 2 === 0) {
|
||||
v += 1
|
||||
}
|
||||
break
|
||||
case MarkerAction.RemoveHighlight:
|
||||
if (v % 2 !== 0) {
|
||||
v -= 1
|
||||
}
|
||||
break
|
||||
case MarkerAction.Select:
|
||||
if (v < 2) v += 2
|
||||
// v += 2
|
||||
break
|
||||
case MarkerAction.Deselect:
|
||||
// if (v >= 2) {
|
||||
// v -= 2
|
||||
// }
|
||||
v = v % 2
|
||||
break
|
||||
case MarkerAction.Toggle:
|
||||
if (v >= 2) {
|
||||
v -= 2
|
||||
} else {
|
||||
v += 2
|
||||
}
|
||||
break
|
||||
case MarkerAction.Clear:
|
||||
v = 0
|
||||
break
|
||||
}
|
||||
changed = array[i] !== v || changed
|
||||
array[i] = v
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
export function createMarkers(count: number, markerData?: MarkerData): MarkerData {
|
||||
const markers = createTextureImage(Math.max(1, count), 1, Uint8Array, markerData && markerData.tMarker.ref.value.array)
|
||||
if (markerData) {
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
/**
|
||||
* 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, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { Primitive } from '../../../primitive/primitive';
|
||||
import { Primitive, transformPrimitive } from '../../../primitive/primitive';
|
||||
import { Cylinder, CylinderProps } from '../../../primitive/cylinder';
|
||||
import { Prism } from '../../../primitive/prism';
|
||||
import { polygon } from '../../../primitive/polygon';
|
||||
|
||||
const cylinderMap = new Map<string, Primitive>()
|
||||
const up = Vec3.create(0, 1, 0)
|
||||
@@ -24,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)
|
||||
}
|
||||
@@ -34,7 +35,12 @@ function getCylinder(props: CylinderProps) {
|
||||
const key = JSON.stringify(props)
|
||||
let cylinder = cylinderMap.get(key)
|
||||
if (cylinder === undefined) {
|
||||
cylinder = Cylinder(props)
|
||||
if (props.radialSegments && props.radialSegments <= 4) {
|
||||
const box = Prism(polygon(4, true, props.radiusTop), props)
|
||||
cylinder = transformPrimitive(box, Mat4.rotX90)
|
||||
} else {
|
||||
cylinder = Cylinder(props)
|
||||
}
|
||||
cylinderMap.set(key, cylinder)
|
||||
}
|
||||
return cylinder
|
||||
|
||||
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
23
src/mol-geo/geometry/mesh/builder/ellipsoid.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { getSphere } from './sphere';
|
||||
|
||||
const tmpEllipsoidMat = Mat4.identity()
|
||||
const tmpVec = Vec3()
|
||||
|
||||
function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
|
||||
Vec3.add(tmpVec, center, dirMajor)
|
||||
Mat4.targetTo(m, center, tmpVec, dirMinor)
|
||||
Mat4.setTranslation(m, center)
|
||||
return Mat4.scale(m, m, radiusScale)
|
||||
}
|
||||
|
||||
export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
|
||||
MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
|
||||
}
|
||||
108
src/mol-geo/geometry/mesh/builder/ribbon.ts
Normal file
108
src/mol-geo/geometry/mesh/builder/ribbon.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 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 { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { ChunkedArray } from '../../../../mol-data/util';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
|
||||
const tA = Vec3.zero()
|
||||
const tB = Vec3.zero()
|
||||
const tV = Vec3.zero()
|
||||
|
||||
const horizontalVector = Vec3.zero()
|
||||
const verticalVector = Vec3.zero()
|
||||
const normalOffset = Vec3.zero()
|
||||
const positionVector = Vec3.zero()
|
||||
const normalVector = Vec3.zero()
|
||||
const torsionVector = Vec3.zero()
|
||||
|
||||
/** set arrowHeight = 0 for no arrow */
|
||||
export function addRibbon(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number) {
|
||||
const { currentGroup, vertices, normals, indices, groups } = state
|
||||
|
||||
let vertexCount = vertices.elementCount
|
||||
let offsetLength = 0
|
||||
|
||||
if (arrowHeight > 0) {
|
||||
Vec3.fromArray(tA, controlPoints, 0)
|
||||
Vec3.fromArray(tB, controlPoints, linearSegments * 3)
|
||||
offsetLength = arrowHeight / Vec3.magnitude(Vec3.sub(tV, tB, tA))
|
||||
}
|
||||
|
||||
for (let i = 0; i <= linearSegments; ++i) {
|
||||
const width = widthValues[i]
|
||||
const height = heightValues[i]
|
||||
|
||||
const actualHeight = arrowHeight === 0 ? height : arrowHeight * (1 - i / linearSegments);
|
||||
const i3 = i * 3
|
||||
|
||||
Vec3.fromArray(verticalVector, normalVectors, i3)
|
||||
Vec3.scale(verticalVector, verticalVector, actualHeight);
|
||||
|
||||
Vec3.fromArray(horizontalVector, binormalVectors, i3)
|
||||
Vec3.scale(horizontalVector, horizontalVector, width);
|
||||
|
||||
if (arrowHeight > 0) {
|
||||
Vec3.fromArray(tA, normalVectors, i3)
|
||||
Vec3.fromArray(tB, binormalVectors, i3)
|
||||
Vec3.scale(normalOffset, Vec3.cross(normalOffset, tA, tB), offsetLength)
|
||||
}
|
||||
|
||||
Vec3.fromArray(positionVector, controlPoints, i3)
|
||||
Vec3.fromArray(normalVector, normalVectors, i3)
|
||||
Vec3.fromArray(torsionVector, binormalVectors, i3)
|
||||
|
||||
Vec3.add(tA, positionVector, verticalVector)
|
||||
Vec3.negate(tB, torsionVector)
|
||||
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
|
||||
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
|
||||
|
||||
Vec3.sub(tA, positionVector, verticalVector)
|
||||
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
|
||||
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
|
||||
|
||||
Vec3.add(tA, positionVector, verticalVector)
|
||||
Vec3.copy(tB, torsionVector)
|
||||
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
|
||||
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
|
||||
|
||||
Vec3.sub(tA, positionVector, verticalVector)
|
||||
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
|
||||
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
|
||||
}
|
||||
|
||||
for (let i = 0; i < linearSegments; ++i) {
|
||||
ChunkedArray.add3(
|
||||
indices,
|
||||
vertexCount + i * 4,
|
||||
vertexCount + (i + 1) * 4 + 1,
|
||||
vertexCount + i * 4 + 1
|
||||
);
|
||||
ChunkedArray.add3(
|
||||
indices,
|
||||
vertexCount + i * 4,
|
||||
vertexCount + (i + 1) * 4,
|
||||
vertexCount + (i + 1) * 4 + 1
|
||||
);
|
||||
|
||||
ChunkedArray.add3(
|
||||
indices,
|
||||
vertexCount + i * 4 + 2 + 1,
|
||||
vertexCount + (i + 1) * 4 + 2 + 1,
|
||||
vertexCount + i * 4 + 2
|
||||
);
|
||||
ChunkedArray.add3(
|
||||
indices,
|
||||
vertexCount + i * 4 + 2,
|
||||
vertexCount + (i + 1) * 4 + 2 + 1,
|
||||
vertexCount + (i + 1) * 4 + 2
|
||||
);
|
||||
}
|
||||
|
||||
const addedVertexCount = (linearSegments + 1) * 4
|
||||
for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user