mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
1316 Commits
v0.6.0-dev
...
v2.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80323d8122 | ||
|
|
cbd6aa0b6b | ||
|
|
3831bd9941 | ||
|
|
3d3e2c3a86 | ||
|
|
bc5d796653 | ||
|
|
82dd0496c2 | ||
|
|
056742ac74 | ||
|
|
29d4cfbcca | ||
|
|
376449f7c8 | ||
|
|
bc37fad007 | ||
|
|
2e561a8de7 | ||
|
|
e6c8c69d0c | ||
|
|
d121a11e28 | ||
|
|
5484a2a72c | ||
|
|
d527609b6d | ||
|
|
e628f580a7 | ||
|
|
b662179b4d | ||
|
|
fa2b8542bf | ||
|
|
901522f500 | ||
|
|
62b63c1aa5 | ||
|
|
24b36f41da | ||
|
|
c9c890782c | ||
|
|
f2c539ebd8 | ||
|
|
feb922ca91 | ||
|
|
25127bb84b | ||
|
|
8fb01d2157 | ||
|
|
c09357ea75 | ||
|
|
9f2513dae0 | ||
|
|
11a52c0390 | ||
|
|
e955dc7e94 | ||
|
|
c8107272f6 | ||
|
|
fb08fe7545 | ||
|
|
b6f054ea28 | ||
|
|
dc7e85133c | ||
|
|
90cddf4e41 | ||
|
|
2cddbb72a6 | ||
|
|
a16faaac4e | ||
|
|
6c5224f33e | ||
|
|
77d013b775 | ||
|
|
02a466e8b9 | ||
|
|
3cb65cbe3d | ||
|
|
fe8838542c | ||
|
|
78b5c9aac4 | ||
|
|
021fa7b79b | ||
|
|
0443589b09 | ||
|
|
415288de9f | ||
|
|
ecbafb086a | ||
|
|
e5dae6c0dd | ||
|
|
16f4524bdb | ||
|
|
6b33021f43 | ||
|
|
fdf37100c2 | ||
|
|
e28674d0dc | ||
|
|
fb7456286a | ||
|
|
9d240f8928 | ||
|
|
48ef5efb21 | ||
|
|
52b2e7c144 | ||
|
|
f2d1d60f6b | ||
|
|
5a176a378a | ||
|
|
60151c2c24 | ||
|
|
a5db6350a2 | ||
|
|
0618eb18ba | ||
|
|
bffdff6aad | ||
|
|
7753a6ec56 | ||
|
|
b8aafa1d78 | ||
|
|
672875187b | ||
|
|
547d60d573 | ||
|
|
99471d2a7b | ||
|
|
45d249b71a | ||
|
|
1382edd81c | ||
|
|
89a6102f8d | ||
|
|
163929477e | ||
|
|
c10a8369e8 | ||
|
|
8fbba52de8 | ||
|
|
ca3174b2c3 | ||
|
|
b9864fba80 | ||
|
|
f8e9bc1e7f | ||
|
|
f79f1507f7 | ||
|
|
61ab205a5d | ||
|
|
2c65260a4f | ||
|
|
0597a1ef24 | ||
|
|
8d6557e51c | ||
|
|
5cff0dff3d | ||
|
|
93206e76d7 | ||
|
|
40933a8539 | ||
|
|
989800783b | ||
|
|
d83b0d2c4d | ||
|
|
5e5d5a63dc | ||
|
|
b1755604e2 | ||
|
|
e58da9b574 | ||
|
|
f5d6498601 | ||
|
|
07f351888f | ||
|
|
4588fdd5d5 | ||
|
|
c3b32baf6a | ||
|
|
b8d60cea9b | ||
|
|
25b8956712 | ||
|
|
7015309db6 | ||
|
|
aad861db37 | ||
|
|
ae7811705d | ||
|
|
7e26dac50b | ||
|
|
75f43d038c | ||
|
|
b9ba940510 | ||
|
|
35603baaaa | ||
|
|
19dc32c491 | ||
|
|
95997e6a61 | ||
|
|
03e19a2ad7 | ||
|
|
765b133369 | ||
|
|
703e729514 | ||
|
|
b0216c4ce6 | ||
|
|
6796fc1cd4 | ||
|
|
87c504f9a8 | ||
|
|
2e770cb733 | ||
|
|
9f440f68e0 | ||
|
|
40028b27ba | ||
|
|
4676ad8738 | ||
|
|
e1c7833826 | ||
|
|
dd1bca0fee | ||
|
|
c38ab2c638 | ||
|
|
459c5aa5a7 | ||
|
|
b8bf07d393 | ||
|
|
ea87ac2094 | ||
|
|
e1b830a59d | ||
|
|
41e1ac76c0 | ||
|
|
98b118fd1e | ||
|
|
5f691913e4 | ||
|
|
26e2516097 | ||
|
|
3d2e4115ed | ||
|
|
dbce1ccb3d | ||
|
|
03aa2be978 | ||
|
|
8dfc52e1ab | ||
|
|
6058179f10 | ||
|
|
ea9e25b03c | ||
|
|
d60c3ddce3 | ||
|
|
724e79bddf | ||
|
|
2de61215c4 | ||
|
|
e783d9a9f1 | ||
|
|
e9e971d4f3 | ||
|
|
96dea14cb1 | ||
|
|
04fc157340 | ||
|
|
cfc24fa99e | ||
|
|
19c1088209 | ||
|
|
ee6c2e0841 | ||
|
|
20ee659b00 | ||
|
|
b6514a4a50 | ||
|
|
07b8bdb951 | ||
|
|
afd18cabd4 | ||
|
|
1117ce05d5 | ||
|
|
fc15e952bf | ||
|
|
249e5a3e0b | ||
|
|
4bfe3f6bde | ||
|
|
75b7e0b4d9 | ||
|
|
ee4ce2fd7a | ||
|
|
db0aa12e75 | ||
|
|
6d2578d3d0 | ||
|
|
99d61f48b4 | ||
|
|
146022dc12 | ||
|
|
92730cad01 | ||
|
|
d6b68b06da | ||
|
|
b174fbf0c6 | ||
|
|
fde1557955 | ||
|
|
24a0753881 | ||
|
|
5664e1d8be | ||
|
|
4881a41256 | ||
|
|
235e41ee03 | ||
|
|
94d293a4d3 | ||
|
|
40f1ca207f | ||
|
|
926fb38c1e | ||
|
|
5a14fcabc5 | ||
|
|
560e40773f | ||
|
|
6561732f57 | ||
|
|
b45cf206fd | ||
|
|
70e07be64d | ||
|
|
f3013f0e46 | ||
|
|
2e7041bd78 | ||
|
|
5d0447c9bb | ||
|
|
9eba0b91a8 | ||
|
|
58bc6722a9 | ||
|
|
1acfed3233 | ||
|
|
8147b3aa34 | ||
|
|
b21552ff36 | ||
|
|
c683cbe962 | ||
|
|
bd270e4428 | ||
|
|
23d942d8a5 | ||
|
|
cbcd6b99d2 | ||
|
|
ee5c098a9f | ||
|
|
070a15d679 | ||
|
|
befa5174f8 | ||
|
|
d6c4366f40 | ||
|
|
181cfefa63 | ||
|
|
0e7c885961 | ||
|
|
d58e90d93f | ||
|
|
cd872b47e6 | ||
|
|
2683c5b318 | ||
|
|
c71f60a164 | ||
|
|
881cbc1947 | ||
|
|
f3e7febbd1 | ||
|
|
e68ad13031 | ||
|
|
7fbbe1e63a | ||
|
|
a5ca72af3c | ||
|
|
1ce6641eb3 | ||
|
|
5dc413ab8c | ||
|
|
50b615e86c | ||
|
|
5b4c6743e7 | ||
|
|
99a3906978 | ||
|
|
981db34736 | ||
|
|
c079a8c5a8 | ||
|
|
ad70adf6ce | ||
|
|
89110b52bd | ||
|
|
8a69f050a6 | ||
|
|
9e38a44406 | ||
|
|
3514ab23c3 | ||
|
|
b59e3c383d | ||
|
|
eeba565d78 | ||
|
|
687e54cc87 | ||
|
|
ac73939440 | ||
|
|
7a3eb8d03f | ||
|
|
3d26904e0b | ||
|
|
468e14bc35 | ||
|
|
e2dc61212e | ||
|
|
aa911ad4bc | ||
|
|
bb5494264c | ||
|
|
c0116a3baa | ||
|
|
9c7497b447 | ||
|
|
fa3a79fdeb | ||
|
|
2987240df4 | ||
|
|
17a1640da5 | ||
|
|
a86da8ee11 | ||
|
|
20e373115d | ||
|
|
531260fbc5 | ||
|
|
47a3dfcef9 | ||
|
|
c5ca51fd80 | ||
|
|
2f2e44c032 | ||
|
|
26acb37098 | ||
|
|
466308cde8 | ||
|
|
dc9af9d8b0 | ||
|
|
c17bfd65e7 | ||
|
|
6de07ab8c2 | ||
|
|
0b8aab802c | ||
|
|
13f28fbe33 | ||
|
|
2eda679966 | ||
|
|
35c778b644 | ||
|
|
96f8ba5a80 | ||
|
|
0daaa94958 | ||
|
|
e672503fda | ||
|
|
4d86c9e0ae | ||
|
|
80fbc474f6 | ||
|
|
dacdc6abfc | ||
|
|
58bc8b58de | ||
|
|
07ead670dd | ||
|
|
00fd760f71 | ||
|
|
a71186905d | ||
|
|
a7e0524d01 | ||
|
|
7d7c1241d4 | ||
|
|
1f6e928d78 | ||
|
|
9bc256bdab | ||
|
|
734096260d | ||
|
|
1b4b6f9435 | ||
|
|
54fe5c85d6 | ||
|
|
f336891bf3 | ||
|
|
d2a3c9c61f | ||
|
|
6968959fe2 | ||
|
|
7749fe5000 | ||
|
|
bf45d2df5d | ||
|
|
b2222844ae | ||
|
|
3d21f1ecc6 | ||
|
|
cde280de60 | ||
|
|
9b415ddff2 | ||
|
|
906c3ac2b6 | ||
|
|
498611d4d4 | ||
|
|
a11bc73d68 | ||
|
|
9616ae5d63 | ||
|
|
c81476d2a7 | ||
|
|
397f001352 | ||
|
|
7edf274477 | ||
|
|
3c1a26c4f5 | ||
|
|
1c695846d5 | ||
|
|
a4c6d1e0e6 | ||
|
|
e51fe83800 | ||
|
|
316076d81e | ||
|
|
4073055d8d | ||
|
|
c6e0ec1c06 | ||
|
|
49aaa48e6e | ||
|
|
0eb882883e | ||
|
|
a6c25551dd | ||
|
|
0a3f73860a | ||
|
|
1de159d65c | ||
|
|
e2c411fefe | ||
|
|
3cf1c64e12 | ||
|
|
b159752b72 | ||
|
|
0d7db59c9e | ||
|
|
a8bf90a68b | ||
|
|
96aff39272 | ||
|
|
a9ae08fc1f | ||
|
|
a24f989c01 | ||
|
|
41ff45d14c | ||
|
|
6ad80bf66b | ||
|
|
eeed48a1f7 | ||
|
|
232bc0d076 | ||
|
|
ac6b87add4 | ||
|
|
2e3bff7d48 | ||
|
|
bd223b4c39 | ||
|
|
a75dc11427 | ||
|
|
30acaffb72 | ||
|
|
2818102b8b | ||
|
|
519e5a6f92 | ||
|
|
071740e7c1 | ||
|
|
7aafb2f4c3 | ||
|
|
3c72988d77 | ||
|
|
3c01dfbd42 | ||
|
|
3a5829aa3e | ||
|
|
ffeeddb37a | ||
|
|
50945493c1 | ||
|
|
fa80c4797a | ||
|
|
650e8bf703 | ||
|
|
13d57737ae | ||
|
|
a6d1a3dfdd | ||
|
|
afffdc06e5 | ||
|
|
80f1b1c795 | ||
|
|
06111e2731 | ||
|
|
adb49371bb | ||
|
|
7b726ded20 | ||
|
|
9f85a0c840 | ||
|
|
f92755c920 | ||
|
|
d141c27765 | ||
|
|
062ac65f0f | ||
|
|
bb420d0806 | ||
|
|
0018032423 | ||
|
|
3dd48ac73c | ||
|
|
4632a6f305 | ||
|
|
eda570d4f1 | ||
|
|
b0127d746d | ||
|
|
5a66ca69c4 | ||
|
|
1c17277f03 | ||
|
|
d771bdc8ff | ||
|
|
eace3f4259 | ||
|
|
8f88da70a6 | ||
|
|
b797be9642 | ||
|
|
2395b7a10a | ||
|
|
0764795c08 | ||
|
|
ea8b7a1d56 | ||
|
|
7c6827f5f5 | ||
|
|
5a6f16ef8d | ||
|
|
8dfdcdd0b7 | ||
|
|
67a2594108 | ||
|
|
871f9635e3 | ||
|
|
25251f3546 | ||
|
|
98824f477e | ||
|
|
aae9a117e8 | ||
|
|
452639c3ce | ||
|
|
6bd45e0a9b | ||
|
|
be100a3ac6 | ||
|
|
96a8cd789c | ||
|
|
d195e1dbf5 | ||
|
|
e6a8e788f5 | ||
|
|
a755ed441e | ||
|
|
de8f294329 | ||
|
|
021171c07d | ||
|
|
013ddb72ed | ||
|
|
207c226f66 | ||
|
|
b4ff98499b | ||
|
|
8471d337a2 | ||
|
|
2f84b94227 | ||
|
|
8dcd6063b7 | ||
|
|
caefe7ba67 | ||
|
|
ad6cebc59b | ||
|
|
e8d2e6d806 | ||
|
|
39352c40d1 | ||
|
|
9994262abc | ||
|
|
5bcf923381 | ||
|
|
ab5dd0b733 | ||
|
|
5a8a6310f8 | ||
|
|
a634c7a587 | ||
|
|
353c5d6d95 | ||
|
|
92698c486c | ||
|
|
898dd1161d | ||
|
|
361f289d0e | ||
|
|
b49d036fcd | ||
|
|
cfee9d86c0 | ||
|
|
92622dfbd7 | ||
|
|
80c2876350 | ||
|
|
fb99f6db8a | ||
|
|
862f8193ef | ||
|
|
490c6679eb | ||
|
|
dd278ca964 | ||
|
|
0892bb24d0 | ||
|
|
83ce5e9422 | ||
|
|
c4ba92c7cb | ||
|
|
846bdf10b0 | ||
|
|
91a46ea7df | ||
|
|
e4ec68a86c | ||
|
|
410655052f | ||
|
|
17162e967a | ||
|
|
c119a1bc21 | ||
|
|
7f2e98f714 | ||
|
|
82f5d8be21 | ||
|
|
73fa675346 | ||
|
|
1f2812b2e3 | ||
|
|
6f05179db8 | ||
|
|
fc8848e97c | ||
|
|
b991102bfa | ||
|
|
e3768805a6 | ||
|
|
cdb65665a6 | ||
|
|
3765bc410c | ||
|
|
69bbd76f33 | ||
|
|
b61b3e1115 | ||
|
|
835f717e47 | ||
|
|
6a35a3ece0 | ||
|
|
518621a1bd | ||
|
|
51acfa1dce | ||
|
|
5be6c9176a | ||
|
|
dfa83c94f7 | ||
|
|
67aedd4770 | ||
|
|
346eb59da9 | ||
|
|
3195594ef3 | ||
|
|
489b412308 | ||
|
|
2d0e8d4ca0 | ||
|
|
27f94c81a2 | ||
|
|
1e865ecacc | ||
|
|
f293a02485 | ||
|
|
ddf00600c6 | ||
|
|
88cd639493 | ||
|
|
0a30ed45f9 | ||
|
|
b5b282c141 | ||
|
|
c4c60cb263 | ||
|
|
e3cf4e928e | ||
|
|
d8a08ef900 | ||
|
|
8b8f3bf492 | ||
|
|
3983073d6c | ||
|
|
82b22fa3f2 | ||
|
|
8a38f73771 | ||
|
|
37da82b138 | ||
|
|
dd17cb23d9 | ||
|
|
8f3afd9f7c | ||
|
|
6e39188f0b | ||
|
|
667cacea12 | ||
|
|
49f0ec981c | ||
|
|
a60d6e9223 | ||
|
|
2d111c1e25 | ||
|
|
874cde4f72 | ||
|
|
826318760e | ||
|
|
de790051aa | ||
|
|
00bf75839e | ||
|
|
b9d4501dcc | ||
|
|
46fb1789b0 | ||
|
|
a1e8bf841b | ||
|
|
6662dbfdd6 | ||
|
|
39b9710d16 | ||
|
|
4fe303da72 | ||
|
|
0662506d35 | ||
|
|
ea987f5601 | ||
|
|
bcae586122 | ||
|
|
e56f188a12 | ||
|
|
8fda9beb7b | ||
|
|
0f50a6682b | ||
|
|
524d38c450 | ||
|
|
68a2e52355 | ||
|
|
447d1f940f | ||
|
|
1cbb59b5d0 | ||
|
|
6f5bcdef90 | ||
|
|
f6f1c5a350 | ||
|
|
f6545c38be | ||
|
|
53fe73d3ee | ||
|
|
3bf5ab1ef7 | ||
|
|
b4813ff866 | ||
|
|
845269e9a5 | ||
|
|
59968d92ab | ||
|
|
d5b7cd370b | ||
|
|
b4434cce17 | ||
|
|
e73227519b | ||
|
|
f8e6d5cbfb | ||
|
|
70bde8c899 | ||
|
|
361dce2b96 | ||
|
|
c83ce28bf4 | ||
|
|
b9a3620a4c | ||
|
|
a939a57811 | ||
|
|
8388ee8f1e | ||
|
|
befa40f5a2 | ||
|
|
36257e2b0f | ||
|
|
769022e88c | ||
|
|
51c180a8f4 | ||
|
|
2ffc5dc5c0 | ||
|
|
431ba01117 | ||
|
|
c6a4350b81 | ||
|
|
9ef8d0c9f8 | ||
|
|
e7606477c2 | ||
|
|
de093b5472 | ||
|
|
88cd9184d8 | ||
|
|
055c169c1f | ||
|
|
1988275695 | ||
|
|
82451bff00 | ||
|
|
e6fd0202a6 | ||
|
|
c21d84dd62 | ||
|
|
f392ac21cd | ||
|
|
d4e5473b86 | ||
|
|
760298c6bf | ||
|
|
d949b99629 | ||
|
|
fff9719e48 | ||
|
|
5858a6eb19 | ||
|
|
46acc1b95e | ||
|
|
da4654b859 | ||
|
|
32869a9a45 | ||
|
|
4cfbccc5d6 | ||
|
|
6301196e67 | ||
|
|
0c61c2badd | ||
|
|
1207526161 | ||
|
|
405d9d524f | ||
|
|
f724717821 | ||
|
|
6247efa8b6 | ||
|
|
7045545419 | ||
|
|
17a0a2be6f | ||
|
|
0e92cfa007 | ||
|
|
04e17872d0 | ||
|
|
59255c720d | ||
|
|
eb71e2c606 | ||
|
|
db2905ba9f | ||
|
|
d097a4abd2 | ||
|
|
459cfd7ab8 | ||
|
|
317229afee | ||
|
|
1e2f16d6b3 | ||
|
|
be07c1668f | ||
|
|
90ddb3dc34 | ||
|
|
f968e86387 | ||
|
|
56639f0bda | ||
|
|
8ae40bfd7c | ||
|
|
a7901c53ce | ||
|
|
76f856fa4f | ||
|
|
63a4cda442 | ||
|
|
603aa89609 | ||
|
|
51dd388912 | ||
|
|
c4708f0260 | ||
|
|
dcfe2e3072 | ||
|
|
9d536fefff | ||
|
|
ed69d15ee1 | ||
|
|
4cd7f0575e | ||
|
|
ad1507dadf | ||
|
|
c358259437 | ||
|
|
6b961c532f | ||
|
|
bb8f872a13 | ||
|
|
93df8a65cd | ||
|
|
de9fd2fcd7 | ||
|
|
24ae8dfda8 | ||
|
|
909e4b3a9f | ||
|
|
09c46447d9 | ||
|
|
239a7cc072 | ||
|
|
f7ccff61e0 | ||
|
|
f8a7483467 | ||
|
|
0a7b3fa396 | ||
|
|
e5cf8bcc04 | ||
|
|
f93230ce44 | ||
|
|
bdde2cea31 | ||
|
|
d44bb6c908 | ||
|
|
052e30b739 | ||
|
|
48a7ac80f5 | ||
|
|
c9717c2332 | ||
|
|
aaed0a9a63 | ||
|
|
c29648ac9b | ||
|
|
cb1f52a8f4 | ||
|
|
5f759edc1b | ||
|
|
8897261836 | ||
|
|
23b1426567 | ||
|
|
329658ff54 | ||
|
|
482059cc9b | ||
|
|
4e509fc479 | ||
|
|
6b1edd9d10 | ||
|
|
d0c692fe03 | ||
|
|
36ee1089a8 | ||
|
|
723bf3e657 | ||
|
|
dc08e524b3 | ||
|
|
3ecd305adf | ||
|
|
60eb42391e | ||
|
|
26f5210518 | ||
|
|
7b41d800c0 | ||
|
|
3aa9ef7595 | ||
|
|
fad5a40ec4 | ||
|
|
58e0ba016e | ||
|
|
b8f168ebf5 | ||
|
|
560c26c73f | ||
|
|
73ada6b1f1 | ||
|
|
4f2cee3b56 | ||
|
|
ad6975c99d | ||
|
|
bea462b8b2 | ||
|
|
5652fa55db | ||
|
|
d2208a0814 | ||
|
|
62a456ce82 | ||
|
|
bef1142a31 | ||
|
|
8e07de62dc | ||
|
|
347b9ead6b | ||
|
|
6d9a09620c | ||
|
|
17349b8529 | ||
|
|
3a200fe2d7 | ||
|
|
10bd513680 | ||
|
|
9214c54e7d | ||
|
|
64da492d63 | ||
|
|
cbb65aaaac | ||
|
|
78c9dda257 | ||
|
|
20450e352f | ||
|
|
9559c22858 | ||
|
|
0d39dc69f1 | ||
|
|
1a29159dfd | ||
|
|
8c21d3b9d9 | ||
|
|
24143d7078 | ||
|
|
22ead527f2 | ||
|
|
104666a13e | ||
|
|
f9ea48fd7b | ||
|
|
80598dc102 | ||
|
|
fbaaa57ca2 | ||
|
|
b0113d6189 | ||
|
|
da1fa03a5f | ||
|
|
48d0418f0e | ||
|
|
cf0122ce23 | ||
|
|
7216a25877 | ||
|
|
89eae0807e | ||
|
|
bfc52fbc6b | ||
|
|
94cd5d3395 | ||
|
|
30d34584bf | ||
|
|
c81166d04b | ||
|
|
3384a8630b | ||
|
|
e39c7c4e98 | ||
|
|
38e838a352 | ||
|
|
cea22c0ea1 | ||
|
|
6b73361963 | ||
|
|
096a4ee63e | ||
|
|
28c8d6bef9 | ||
|
|
37fdbfe12a | ||
|
|
295cb84cc8 | ||
|
|
b5252516e3 | ||
|
|
8e2c0327d6 | ||
|
|
eaa92b75a3 | ||
|
|
ab81c89a9a | ||
|
|
e49af151c1 | ||
|
|
78ca5cbb43 | ||
|
|
48c242d59d | ||
|
|
8b4603d5a1 | ||
|
|
cc6dce8845 | ||
|
|
b2eac8092c | ||
|
|
a22362bac8 | ||
|
|
298b283686 | ||
|
|
14f4de2e3f | ||
|
|
9bde4c40b0 | ||
|
|
61390cb64f | ||
|
|
1b67bc41f5 | ||
|
|
5b698b816e | ||
|
|
bf9303ea80 | ||
|
|
017676c148 | ||
|
|
526f7700b2 | ||
|
|
220e01106f | ||
|
|
240de5b24d | ||
|
|
17a001427b | ||
|
|
a423970b9c | ||
|
|
56f4c8775f | ||
|
|
5ed4aa0fae | ||
|
|
4f4245b895 | ||
|
|
bc6d2112e2 | ||
|
|
3b2b87d264 | ||
|
|
181646f052 | ||
|
|
ea5a945810 | ||
|
|
7cfdc8ab1b | ||
|
|
52e011ade9 | ||
|
|
895e2ede2d | ||
|
|
3a25769a94 | ||
|
|
5db4b48a86 | ||
|
|
aa2899bfbd | ||
|
|
40bbd87c4e | ||
|
|
fb7dd66600 | ||
|
|
e12dc2b089 | ||
|
|
78242b18c3 | ||
|
|
9043e4c8e1 | ||
|
|
54b5e3a0cc | ||
|
|
b6719a2f57 | ||
|
|
087d5fbb68 | ||
|
|
4f60c91256 | ||
|
|
99415ef290 | ||
|
|
d446a2d047 | ||
|
|
b476f738bc | ||
|
|
4c0d1383b2 | ||
|
|
6a924bf732 | ||
|
|
5e1f1220af | ||
|
|
d13ee0a2cc | ||
|
|
826127672a | ||
|
|
df8dd7278a | ||
|
|
cbdc4a3e9d | ||
|
|
75266ad257 | ||
|
|
0e93374b2d | ||
|
|
8e350617f2 | ||
|
|
f6acf0a60a | ||
|
|
7982f25a45 | ||
|
|
ddafa7aac1 | ||
|
|
65946c3045 | ||
|
|
63d699d620 | ||
|
|
e1170c0552 | ||
|
|
97612bf044 | ||
|
|
012ac33bd5 | ||
|
|
272e208fd4 | ||
|
|
714f0623bb | ||
|
|
5327962409 | ||
|
|
7c3529ae30 | ||
|
|
61c63df9e9 | ||
|
|
9b6fcaeb79 | ||
|
|
ef3f035f27 | ||
|
|
e9bc67fbf4 | ||
|
|
745f2aecf8 | ||
|
|
eff80ad5ff | ||
|
|
27ef44b833 | ||
|
|
9d0190c11c | ||
|
|
0069687233 | ||
|
|
478033d405 | ||
|
|
58f57d5ad2 | ||
|
|
3f80230f4f | ||
|
|
da35f0ea16 | ||
|
|
72bdd5bc05 | ||
|
|
255cc620a5 | ||
|
|
4753271a6d | ||
|
|
11c7024edd | ||
|
|
47ba54199f | ||
|
|
cd7643e79b | ||
|
|
9a797e39e5 | ||
|
|
7898840003 | ||
|
|
8e91cb6d54 | ||
|
|
893401f7c4 | ||
|
|
021e6ffeb5 | ||
|
|
4080c1e005 | ||
|
|
5063e99761 | ||
|
|
a59f6546c5 | ||
|
|
3a737099ad | ||
|
|
d1f76fd48e | ||
|
|
437d52e484 | ||
|
|
610977cc08 | ||
|
|
6c78adb353 | ||
|
|
abef75bfa2 | ||
|
|
8137d4acdb | ||
|
|
899a186808 | ||
|
|
bb7d3e075c | ||
|
|
78ba9df263 | ||
|
|
9126416389 | ||
|
|
eef944b617 | ||
|
|
208cc2e48e | ||
|
|
1522bf4ae4 | ||
|
|
422f4567f1 | ||
|
|
6c8ae32ff9 | ||
|
|
e67c610b84 | ||
|
|
96aa003702 | ||
|
|
f6262f4be5 | ||
|
|
efc5bf6a45 | ||
|
|
cbc9801477 | ||
|
|
94e21e8a3a | ||
|
|
2ed118604c | ||
|
|
219d4f4d33 | ||
|
|
a937e3c57d | ||
|
|
9fe16e321e | ||
|
|
697e9986b4 | ||
|
|
613cdc3145 | ||
|
|
a2bf489017 | ||
|
|
aca49b9ba5 | ||
|
|
11d5e301f3 | ||
|
|
2797b451fb | ||
|
|
e82918db6a | ||
|
|
e0b98f70f0 | ||
|
|
297b9bd3ff | ||
|
|
013a59857d | ||
|
|
0b14381255 | ||
|
|
e1e0b0f2da | ||
|
|
2142290300 | ||
|
|
aeb7c7033d | ||
|
|
0f8540e7fc | ||
|
|
f14b57fe30 | ||
|
|
abfb1d5992 | ||
|
|
35c53b27fe | ||
|
|
a7f144e810 | ||
|
|
f4cb3aeed7 | ||
|
|
bceb044552 | ||
|
|
5e41e959f8 | ||
|
|
c95d54f9cd | ||
|
|
8149a25ad4 | ||
|
|
d500393501 | ||
|
|
9d2fa3e749 | ||
|
|
ee886244fc | ||
|
|
f2557fe80a | ||
|
|
bf904a5b32 | ||
|
|
76a5ce8f14 | ||
|
|
1ff83d9648 | ||
|
|
d121ed8b6c | ||
|
|
6c27deed74 | ||
|
|
7e2d15f329 | ||
|
|
10bd7853f3 | ||
|
|
4a7bfe953c | ||
|
|
3598d13a3d | ||
|
|
96ac561279 | ||
|
|
2c81267ca7 | ||
|
|
395fa5dad1 | ||
|
|
d15340e62e | ||
|
|
eab4c08836 | ||
|
|
69c5bf0094 | ||
|
|
6c112f83e8 | ||
|
|
f49c34c551 | ||
|
|
3ab4458cb2 | ||
|
|
ad3c07c634 | ||
|
|
68525c2109 | ||
|
|
9e9851472d | ||
|
|
c0edb27323 | ||
|
|
0a70783b5e | ||
|
|
a3d101cdf9 | ||
|
|
7255e08ecf | ||
|
|
b1bdb8e66b | ||
|
|
49c8c7f396 | ||
|
|
d3b4280589 | ||
|
|
bb9acaaa9c | ||
|
|
fc10b9bf7b | ||
|
|
5fcb495d24 | ||
|
|
b2fdcba674 | ||
|
|
0d01948ba9 | ||
|
|
c4370670cb | ||
|
|
1b19136c18 | ||
|
|
217e983da8 | ||
|
|
57338bdad1 | ||
|
|
0e84bf9513 | ||
|
|
0be28cacdf | ||
|
|
017c608439 | ||
|
|
20ee9496e3 | ||
|
|
6fd81d0961 | ||
|
|
823a68f9bf | ||
|
|
deab18e805 | ||
|
|
19016b6730 | ||
|
|
4319ae251c | ||
|
|
e5920e29b4 | ||
|
|
c376ddfc9d | ||
|
|
8fe2d3f724 | ||
|
|
4d7a128528 | ||
|
|
663ec9695e | ||
|
|
5e6eb7ed49 | ||
|
|
dfaa4dacdb | ||
|
|
f7adb8b589 | ||
|
|
cb6b1bf19d | ||
|
|
27a4e1d7d9 | ||
|
|
0cba88ad8c | ||
|
|
e535c4efa8 | ||
|
|
31f58ee110 | ||
|
|
2a9f6c88a0 | ||
|
|
feb167dcf8 | ||
|
|
1b53ea846b | ||
|
|
88b9be5fd1 | ||
|
|
89486ea9e2 | ||
|
|
86c09ead98 | ||
|
|
1f60d887a8 | ||
|
|
a672115505 | ||
|
|
8f54ea137d | ||
|
|
4171008c3f | ||
|
|
3a9c3780ac | ||
|
|
fe55f33bd1 | ||
|
|
71bc88c041 | ||
|
|
a5aadfef0e | ||
|
|
0b368ef804 | ||
|
|
f398993d33 | ||
|
|
b6f59ca9c3 | ||
|
|
c857c17bb4 | ||
|
|
3415fe0847 | ||
|
|
1569958a29 | ||
|
|
3543faa0c2 | ||
|
|
251dbf3877 | ||
|
|
32d35efef0 | ||
|
|
8b6428a61d | ||
|
|
dda43370cf | ||
|
|
92c1e979c0 | ||
|
|
ad38a33943 | ||
|
|
88c276a4c7 | ||
|
|
0a3d19235d | ||
|
|
0d90fd1f06 | ||
|
|
02d3274e83 | ||
|
|
2531af2b94 | ||
|
|
850328be4e | ||
|
|
f8ce9cbb65 | ||
|
|
2af9d1cabf | ||
|
|
e8d1737d40 | ||
|
|
0328e93518 | ||
|
|
8a4ab9bdb9 | ||
|
|
410cdb193d | ||
|
|
a278337b4c | ||
|
|
b1308de0b9 | ||
|
|
9705078970 | ||
|
|
b1ca98e945 | ||
|
|
35054eaca9 | ||
|
|
2747c743c9 | ||
|
|
031d08a8d4 | ||
|
|
7cc6c4a9c8 | ||
|
|
ff27098514 | ||
|
|
545cd65066 | ||
|
|
84bfc6e7a9 | ||
|
|
2f71c4c5e4 | ||
|
|
1448f7aeb6 | ||
|
|
79d66a5cfc | ||
|
|
2ec19ac04c | ||
|
|
f62a6d4512 | ||
|
|
4fbcee3953 | ||
|
|
12bb283b97 | ||
|
|
13d776c7cb | ||
|
|
f45b48c6e1 | ||
|
|
ff14c94a90 | ||
|
|
0a0ef35b74 | ||
|
|
e3dc10c085 | ||
|
|
46113bf3d4 | ||
|
|
0f3ef61f7d | ||
|
|
86aae08257 | ||
|
|
06bf2c39a1 | ||
|
|
66a23bc2a2 | ||
|
|
51e86f1e43 | ||
|
|
78c70b3f5b | ||
|
|
8f52ffe061 | ||
|
|
e95b91ab84 | ||
|
|
f4dbd66496 | ||
|
|
5895df0499 | ||
|
|
6d0d88f3be | ||
|
|
7e71428cc3 | ||
|
|
2e215440f7 | ||
|
|
c04fa56c6c | ||
|
|
6c70b5e38f | ||
|
|
ad9160a4a3 | ||
|
|
c747d3928e | ||
|
|
68e8d67054 | ||
|
|
9e8fc76d28 | ||
|
|
d8970305de | ||
|
|
824675a658 | ||
|
|
090ad613cc | ||
|
|
ea35e09c96 | ||
|
|
d8b9a1a560 | ||
|
|
c259f58e63 | ||
|
|
9d4c2a1147 | ||
|
|
f13c3fe38b | ||
|
|
60409df145 | ||
|
|
1d7321cd6f | ||
|
|
eb68ccbf6b | ||
|
|
b1ece44c49 | ||
|
|
37ae274fb6 | ||
|
|
c900045fcd | ||
|
|
50d95ccf6a | ||
|
|
c9171444eb | ||
|
|
56ea62af71 | ||
|
|
9e81626928 | ||
|
|
84fda6e35d | ||
|
|
0f758cf554 | ||
|
|
a6605052db | ||
|
|
8514175da2 | ||
|
|
6a49427fc0 | ||
|
|
7c18e5eb86 | ||
|
|
2a7d258715 | ||
|
|
54fb9beeee | ||
|
|
27ebbc50d5 | ||
|
|
2a1b6e52b2 | ||
|
|
3110e82d92 | ||
|
|
4be999ce32 | ||
|
|
f0d7a4ed2a | ||
|
|
2dacfcb485 | ||
|
|
6218cc5371 | ||
|
|
b4808f2909 | ||
|
|
1a2e9eaa84 | ||
|
|
056ce42097 | ||
|
|
b14b5ca626 | ||
|
|
ffbaa944f2 | ||
|
|
e2ba96174a | ||
|
|
8c5d99bb54 | ||
|
|
b18b3be070 | ||
|
|
2e69b7c419 | ||
|
|
5007f5fb72 | ||
|
|
6fe83a9a70 | ||
|
|
20af084127 | ||
|
|
d6501170e6 | ||
|
|
5f33364514 | ||
|
|
7924c008fa | ||
|
|
2d2a53f28e | ||
|
|
1f7ffabef9 | ||
|
|
16d5c07224 | ||
|
|
2392bfb579 | ||
|
|
b4036f576c | ||
|
|
7e3cca5780 | ||
|
|
690d6812dc | ||
|
|
a44aa02f13 | ||
|
|
65ddd6d68a | ||
|
|
754025b3b1 | ||
|
|
f0649c5aa3 | ||
|
|
6df045211c | ||
|
|
0b9371527e | ||
|
|
8a00540de0 | ||
|
|
0d78905686 | ||
|
|
6edab203c2 | ||
|
|
0abfdb5ee3 | ||
|
|
88369158c9 | ||
|
|
8926575283 | ||
|
|
15b0288ce4 | ||
|
|
28853ec19d | ||
|
|
e3480a076a | ||
|
|
abf6452124 | ||
|
|
2cdd811dd3 | ||
|
|
be6fea39bf | ||
|
|
ed1bc2cd07 | ||
|
|
28d3d5861a | ||
|
|
95d3ef491f | ||
|
|
5c37ddfc6d | ||
|
|
4d9e2d9c91 | ||
|
|
0b1c18913d | ||
|
|
7aee2d805d | ||
|
|
06f03f399a | ||
|
|
c8c2355d3e | ||
|
|
3d1366024d | ||
|
|
f6964d2a66 | ||
|
|
de60f70af5 | ||
|
|
8f2e619162 | ||
|
|
fbcef01c55 | ||
|
|
641e0639d4 | ||
|
|
5048573976 | ||
|
|
78e4d8536d | ||
|
|
a01d088205 | ||
|
|
4b1d1a045d | ||
|
|
e2857d00b4 | ||
|
|
a55a71d31a | ||
|
|
acf793f112 | ||
|
|
2d58ea28ea | ||
|
|
b21de78eb5 | ||
|
|
376d4b4ee1 | ||
|
|
e39304c7cf | ||
|
|
7cba9cda0c | ||
|
|
04c690e8f9 | ||
|
|
170d0fbc9d | ||
|
|
40d632a7b1 | ||
|
|
2e754d23f4 | ||
|
|
5639a4b37c | ||
|
|
8c959f8a60 | ||
|
|
d42c9a6e15 | ||
|
|
da4dabc3f5 | ||
|
|
92217905f8 | ||
|
|
598441a727 | ||
|
|
f707cb19a4 | ||
|
|
63fc408be6 | ||
|
|
69dedd8c22 | ||
|
|
5480805754 | ||
|
|
655ae65b8d | ||
|
|
1a23cb672e | ||
|
|
19e18b4089 | ||
|
|
3d909d5012 | ||
|
|
ad521948b6 | ||
|
|
2f3b6a28c1 | ||
|
|
c779da674c | ||
|
|
d9b140f9f2 | ||
|
|
052648023e | ||
|
|
f5d12d440e | ||
|
|
99d7a90863 | ||
|
|
901d5c86e6 | ||
|
|
df9efd05e6 | ||
|
|
26b8adaec4 | ||
|
|
fc6f5a0336 | ||
|
|
f823a887b7 | ||
|
|
b346d4d85d | ||
|
|
70bd035898 | ||
|
|
7e5cdd8e06 | ||
|
|
a21dac60e0 | ||
|
|
9bd2e0d96e | ||
|
|
dd15a000e1 | ||
|
|
ebcfa44f22 | ||
|
|
43845adb71 | ||
|
|
9e3fff65a7 | ||
|
|
05c35a3a3a | ||
|
|
6f46965344 | ||
|
|
27ee576340 | ||
|
|
58492328df | ||
|
|
fd102bede1 | ||
|
|
3d8c47eefa | ||
|
|
c4e43228a2 | ||
|
|
6526090b8b | ||
|
|
094a018b5b | ||
|
|
a0a9c994b2 | ||
|
|
524ed90e3f | ||
|
|
7abba750b4 | ||
|
|
ecff1641d2 | ||
|
|
47ecf04386 | ||
|
|
c4697a9f45 | ||
|
|
53d5414492 | ||
|
|
311f5c09f5 | ||
|
|
be4439451f | ||
|
|
09c83e83ba | ||
|
|
bea4c64d85 | ||
|
|
0387dcd444 | ||
|
|
a89f21dafd | ||
|
|
7921c05d55 | ||
|
|
dafa946937 | ||
|
|
95673b0131 | ||
|
|
0118136869 | ||
|
|
573c2a7ad6 | ||
|
|
41977ea758 | ||
|
|
75ccded612 | ||
|
|
2429111a59 | ||
|
|
c475cb292e | ||
|
|
04d34b369a | ||
|
|
cfe4c6c559 | ||
|
|
4d13f99d22 | ||
|
|
62ebd4d8a9 | ||
|
|
026c25621f | ||
|
|
c096ae299d | ||
|
|
ae306d1761 | ||
|
|
4caae32933 | ||
|
|
6bbf20980e | ||
|
|
0fd00ecab8 | ||
|
|
70de73a95b | ||
|
|
578ad69c36 | ||
|
|
af263b8c88 | ||
|
|
bef6775de5 | ||
|
|
3c27450e82 | ||
|
|
9400f27f82 | ||
|
|
c176313f7b | ||
|
|
77461d5a23 | ||
|
|
a5c039bf11 | ||
|
|
e3c08f23bf | ||
|
|
67a26e93e4 | ||
|
|
a1d261b7c8 | ||
|
|
3826394940 | ||
|
|
791a54aeec | ||
|
|
48f1bb7755 | ||
|
|
53e028325f | ||
|
|
02eda0a1e2 | ||
|
|
92600160ff | ||
|
|
6ec67a7faf | ||
|
|
a4386744a2 | ||
|
|
09210279e1 | ||
|
|
e78cf18a38 | ||
|
|
9d1d7bbf72 | ||
|
|
60d5e85b4c | ||
|
|
2c263e216f | ||
|
|
e1f671a5b3 | ||
|
|
7ffaea48cb | ||
|
|
0e4527613c | ||
|
|
f363621de3 | ||
|
|
4901ec3b5d | ||
|
|
c592b8a53f | ||
|
|
325e4e44dc | ||
|
|
a81f7f6cfd | ||
|
|
31b83e9675 | ||
|
|
dc89f33cd0 | ||
|
|
f5f9237a78 | ||
|
|
4429ff0ac1 | ||
|
|
a0d38e6b10 | ||
|
|
c060664f84 | ||
|
|
10f7f15f70 | ||
|
|
d40ff29337 | ||
|
|
7ca666ae98 | ||
|
|
dcaea9d40b | ||
|
|
b878273d81 | ||
|
|
c15d29f2da | ||
|
|
3e3a13070a | ||
|
|
11bcd84e66 | ||
|
|
44b63e5953 | ||
|
|
fb0634a0f4 | ||
|
|
0b651db35b | ||
|
|
b24062b575 | ||
|
|
35c5c4cfc0 | ||
|
|
4b8bf57d1a | ||
|
|
a81f0d911a | ||
|
|
28e2227989 | ||
|
|
4f97b6836a | ||
|
|
5ab0dfaba8 | ||
|
|
66751fa006 | ||
|
|
f1d78e4805 | ||
|
|
715c78a04f | ||
|
|
0cd8b4ee8c | ||
|
|
c6efa475a5 | ||
|
|
e846e1fdd7 | ||
|
|
4221067f8f | ||
|
|
36951d6f19 | ||
|
|
ac45500e35 | ||
|
|
0f22eab8b9 | ||
|
|
5a8fd6d518 | ||
|
|
60ee04b9b9 | ||
|
|
d99b5bd505 | ||
|
|
6312c1f99b | ||
|
|
ef870a510f | ||
|
|
394ff05626 | ||
|
|
e90ccfdd20 | ||
|
|
92a86e324b | ||
|
|
e5d6816392 | ||
|
|
a1b2de16a3 | ||
|
|
f605021cfb | ||
|
|
619a2ccb3a | ||
|
|
0cc077c346 | ||
|
|
e34aad991b | ||
|
|
2a925cdd6a | ||
|
|
36c5e444ca | ||
|
|
c7f6041aa4 | ||
|
|
68401b3556 | ||
|
|
3843064eac | ||
|
|
d0de9073e7 | ||
|
|
35bd0ec5d8 | ||
|
|
373a69abbd | ||
|
|
9fa56bf76c | ||
|
|
3ad42fc7e0 | ||
|
|
7dd6efafb2 | ||
|
|
61e26df637 | ||
|
|
a47283bf5b | ||
|
|
d7e5385b04 | ||
|
|
8eaac83238 | ||
|
|
9fbe224f00 | ||
|
|
26dc7b2f65 | ||
|
|
bb6b6f9690 | ||
|
|
0fabfc9873 | ||
|
|
9c3e057410 | ||
|
|
f4cbb1ec73 | ||
|
|
1175f589eb | ||
|
|
8c7563d7f2 | ||
|
|
33533cbb09 | ||
|
|
0ec37a7b95 | ||
|
|
f3db0c171b | ||
|
|
cd10d23371 | ||
|
|
d60c88c506 | ||
|
|
7266aab4be | ||
|
|
54c2ef7a0b | ||
|
|
b882a72d77 | ||
|
|
1eab4dac96 | ||
|
|
b2376aea70 | ||
|
|
1641cfbd03 | ||
|
|
f27540ca02 | ||
|
|
b4320de291 | ||
|
|
1b26aa4b36 | ||
|
|
1ca91f35e2 | ||
|
|
1c5ac56ecf | ||
|
|
ee85e13206 | ||
|
|
440474d53b | ||
|
|
d9c251b2ce | ||
|
|
975b4aee2a | ||
|
|
4a7413a8d9 | ||
|
|
593b34bfe3 | ||
|
|
691b1721b2 | ||
|
|
b8133b4300 | ||
|
|
0c459b98db | ||
|
|
060725642f | ||
|
|
eaf28bf016 | ||
|
|
ed6084c40b | ||
|
|
e56063b065 | ||
|
|
f8ddfb1638 | ||
|
|
900f2e1f76 | ||
|
|
00c9e05a65 | ||
|
|
ecaab51315 | ||
|
|
2a4d45714c | ||
|
|
6e13ef5cbc | ||
|
|
f48e2ab238 | ||
|
|
0e34d976c0 | ||
|
|
2d6cd4c6da | ||
|
|
4a4b3ef5b4 | ||
|
|
67e6167b55 | ||
|
|
79b33bfbd8 | ||
|
|
f5fc96ee3b | ||
|
|
691571ec39 | ||
|
|
f2b20a646e | ||
|
|
bef9ff86d2 | ||
|
|
ff3da5f2db | ||
|
|
129727d5d1 | ||
|
|
6e26b4d5f9 | ||
|
|
371bb11ce8 | ||
|
|
556196ae3f | ||
|
|
f5667411d7 | ||
|
|
774f419a53 | ||
|
|
9fa5d40306 | ||
|
|
ba89d5ec1e | ||
|
|
b7fa577d9b | ||
|
|
949425d14d | ||
|
|
2cb0d63750 | ||
|
|
038aa47578 | ||
|
|
c833598c26 | ||
|
|
489c52361a | ||
|
|
9edf41a2f2 | ||
|
|
5bcca99f60 | ||
|
|
1b0a310fc7 | ||
|
|
e3e7fa3040 | ||
|
|
5297dd6f11 | ||
|
|
c557e93255 | ||
|
|
ad5f0c987a | ||
|
|
30aa6d035d | ||
|
|
b198289fc4 | ||
|
|
51e45085f1 | ||
|
|
97ebb2dccc | ||
|
|
29d28bd3f4 | ||
|
|
09c5c040f2 | ||
|
|
6e60a2f9dc | ||
|
|
5d36108113 | ||
|
|
f4a423ebe5 | ||
|
|
9942fbe549 | ||
|
|
dbde7521e4 | ||
|
|
6eee3e8368 | ||
|
|
cc0ccd7830 | ||
|
|
7ad25249a9 | ||
|
|
687c7d54ff | ||
|
|
d6fff1ffdf | ||
|
|
20a8b8892e | ||
|
|
5c09ecc98d | ||
|
|
19539a2c9d | ||
|
|
5c093c7f22 | ||
|
|
c43ed90607 | ||
|
|
a85242d9c5 | ||
|
|
722d2a9c6c | ||
|
|
f7d65ff52c | ||
|
|
7bee2be12d | ||
|
|
326649d4f5 | ||
|
|
6aba5df55d | ||
|
|
57bbcf9425 | ||
|
|
08ba34d607 | ||
|
|
4c122227a7 | ||
|
|
e14afb4dad | ||
|
|
472def49f6 | ||
|
|
90549893e3 | ||
|
|
73a8e45202 | ||
|
|
35df55cb4f | ||
|
|
c2028d20a8 | ||
|
|
eaffdc6a98 | ||
|
|
0b1e6100a9 | ||
|
|
2168905c11 | ||
|
|
f955e6a299 | ||
|
|
98f3981e12 | ||
|
|
82f94d20ea | ||
|
|
fbc6d47117 | ||
|
|
6a2e4cf813 | ||
|
|
4aabef7683 | ||
|
|
74ae91ee1b | ||
|
|
8b62766474 | ||
|
|
2995504916 |
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/*
|
||||
build/*
|
||||
lib/*
|
||||
119
.eslintrc.json
119
.eslintrc.json
@@ -3,63 +3,94 @@
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"indent": "off",
|
||||
"arrow-parens": [
|
||||
"off",
|
||||
"as-needed"
|
||||
],
|
||||
"brace-style": "off",
|
||||
"comma-spacing": "off",
|
||||
"space-infix-ops": "error",
|
||||
"comma-dangle": "off",
|
||||
"eqeqeq": [
|
||||
"warn",
|
||||
"error",
|
||||
"smart"
|
||||
],
|
||||
"import/order": "off",
|
||||
"no-eval": "warn",
|
||||
"no-new-wrappers": "warn",
|
||||
"no-trailing-spaces": "warn",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-unsafe-finally": "warn",
|
||||
"no-var": "warn",
|
||||
"spaced-comment": "warn"
|
||||
}
|
||||
"no-var": "error",
|
||||
"spaced-comment": "error",
|
||||
"semi": "warn",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"selector": "ExportDefaultDeclaration",
|
||||
"message": "Default exports are not allowed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": ["tsconfig.json", "tsconfig.commonjs.json"],
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/class-name-casing": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
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": [
|
||||
"error",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"off",
|
||||
null
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/brace-style": [
|
||||
"error",
|
||||
"1tbs", { "allowSingleLine": true }
|
||||
],
|
||||
"@typescript-eslint/comma-spacing": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.github/workflows/lint.yml
vendored
Normal file
18
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
name: eslint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install node v12
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- name: yarn install
|
||||
run: yarn install
|
||||
- name: eslint
|
||||
uses: icrawl/action-eslint@v1
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@ node_modules/
|
||||
debug.log
|
||||
npm-debug.log
|
||||
tsconfig.tsbuildinfo
|
||||
tsconfig.commonjs.tsbuildinfo
|
||||
|
||||
*.sublime-workspace
|
||||
.idea
|
||||
|
||||
1
.npmignore
Normal file
1
.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
tsconfig.commonjs.buildinfo
|
||||
@@ -14,6 +14,5 @@ before_install:
|
||||
node_js:
|
||||
- "12"
|
||||
- "10"
|
||||
- "8"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start
|
||||
57
README.md
57
README.md
@@ -5,7 +5,7 @@
|
||||
|
||||
# Mol*
|
||||
|
||||
The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will serve as basis for the next-generation data delivery and analysis tools for macromolecular structure data. This is a collaboration between PDBe and RCSB PDB teams and the development will be open source and available to anyone who wants to use it for developing visualisation tools for macromolecular structure data available from [PDB](https://www.wwpdb.org/) and other institutions.
|
||||
The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will serve as a basis for the next-generation data delivery and analysis tools for macromolecular structure data. This is a collaboration between PDBe and RCSB PDB teams and the development will be open-source and available to anyone who wants to use it for developing visualization tools for macromolecular structure data available from [PDB](https://www.wwpdb.org/) and other institutions.
|
||||
|
||||
This particular project is the implementation of this technology (still under development).
|
||||
|
||||
@@ -16,32 +16,32 @@ This particular project is the implementation of this technology (still under de
|
||||
The core of Mol* currently consists of these modules (see under `src/`):
|
||||
|
||||
- `mol-task` Computation abstraction with progress tracking and cancellation support.
|
||||
- `mol-data` Collections (integer based sets, interface to columns/tables, etc.)
|
||||
- `mol-data` Collections (integer-based sets, interface to columns/tables, etc.)
|
||||
- `mol-math` Math related (loosely) algorithms and data structures.
|
||||
- `mol-io` Parsing library. Each format is parsed into an interface that corresponds to the data stored by it. Support for common coordinate, experimental/map, and annotation data formats.
|
||||
- `mol-model` Data structures and algorithms (such as querying) for representing molecular data (including coordinate, experimental/map, and annotation data).
|
||||
- `mol-model-formats` Data format parsers for `mol-model`.
|
||||
- `mol-model-props` Common "custom properties".
|
||||
- `mol-script` A scriting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)).
|
||||
- `mol-script` A scripting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)).
|
||||
- `mol-geo` Creating (molecular) geometries.
|
||||
- `mol-theme` Theming for structure, volume and shape representations.
|
||||
- `mol-repr` Molecular representations for structures, volumes and shapes.
|
||||
- `mol-gl` A wrapper around WebGL.
|
||||
- `mol-canvas3d` A low level 3d view component. Uses `mol-geo` to generate geometries.
|
||||
- `mol-canvas3d` A low-level 3d view component. Uses `mol-geo` to generate geometries.
|
||||
- `mol-state` State representation tree with state saving and automatic updates.
|
||||
- `mol-app` Components for builduing UIs.
|
||||
- `mol-app` Components for building 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-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 into 3rd party solutions.
|
||||
- `mol-util` Useful things that do not fit elsewhere.
|
||||
|
||||
Moreover, the project contains the imlementation of `servers`, including
|
||||
Moreover, the project contains the implementation 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).
|
||||
The project also contains performance tests (`perf-tests`), `examples`, and basic proof of concept `cli` apps (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
|
||||
|
||||
## Previous Work
|
||||
This project builds on experience from previous solutions:
|
||||
@@ -89,21 +89,29 @@ and navigate to `build/viewer`
|
||||
|
||||
### Code generation
|
||||
**CIF schemas**
|
||||
Install CIFTools `npm install ciftools -g`
|
||||
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
|
||||
node ./lib/commonjs/cli/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
|
||||
node ./lib/commonjs/cli/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
|
||||
node ./lib/commonjs/cli/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
|
||||
node ./lib/commonjs/cli/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
|
||||
|
||||
**Lipid names**
|
||||
|
||||
node lib/commonjs/cli/lipid-params -o src/mol-model/structure/model/types/lipids.ts
|
||||
|
||||
**Ion names**
|
||||
|
||||
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-ions.js src/mol-model/structure/model/types/ions.ts
|
||||
|
||||
|
||||
**GraphQL schemas**
|
||||
|
||||
./node_modules/.bin/graphql-codegen -c ./data/rcsb-graphql/codegen.yml
|
||||
node node_modules//@graphql-codegen/cli/bin -c src/extensions/rcsb/graphql/codegen.yml
|
||||
|
||||
### Other scripts
|
||||
**Create chem comp bond table**
|
||||
|
||||
export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
|
||||
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-table.js build/data/ccb.bcif -b
|
||||
|
||||
**Test model server**
|
||||
|
||||
@@ -115,22 +123,26 @@ Install CIFTools `npm install ciftools -g`
|
||||
|
||||
**Convert any CIF to BinaryCIF**
|
||||
|
||||
node build/model-server/preprocess -i file.cif -ob file.bcif
|
||||
node lib/servers/model/preprocess -i file.cif -ob file.bcif
|
||||
|
||||
To see all available commands, use ``node build/model-server/preprocess -h``.
|
||||
To see all available commands, use ``node lib/servers/model/preprocess -h``.
|
||||
|
||||
Or
|
||||
|
||||
node lib/commonjs/cli/cif2bcif
|
||||
|
||||
## Development
|
||||
|
||||
### Intallation
|
||||
### Installation
|
||||
|
||||
If node complains about a missine acorn peer dependency, run the following commands
|
||||
If node complains about a missing acorn peer dependency, run the following commands
|
||||
|
||||
npm update acorn --depth 20
|
||||
npm dedupe
|
||||
|
||||
### Editor
|
||||
|
||||
To get syntax highlighting for shader and graphql files add the following to Visual Code's settings files and make sure relevant extanesions are installed in the editor.
|
||||
To get syntax highlighting for shader and graphql files add the following to Visual Code's settings files and make sure relevant extensions are installed in the editor.
|
||||
|
||||
"files.associations": {
|
||||
"*.glsl.ts": "glsl",
|
||||
@@ -142,7 +154,7 @@ To get syntax highlighting for shader and graphql files add the following to Vis
|
||||
## Publish
|
||||
|
||||
### Prerelease
|
||||
npm version prerelease # asumes the current version ends with '-dev.X'
|
||||
npm version prerelease # assumes the current version ends with '-dev.X'
|
||||
npm publish --tag next
|
||||
|
||||
### Release
|
||||
@@ -164,4 +176,5 @@ Continually develop this prototype project. As individual modules become stable,
|
||||
Funding sources include but are not limited to:
|
||||
* [RCSB PDB](https://www.rcsb.org) funding by a grant [DBI-1338415; PI: SK Burley] from the NSF, the NIH, and the US DoE
|
||||
* [PDBe, EMBL-EBI](https://pdbe.org)
|
||||
* [CEITEC](https://www.ceitec.eu/)
|
||||
* [CEITEC](https://www.ceitec.eu/)
|
||||
* [EntosAI](https://www.entos.ai)
|
||||
|
||||
88
data/cif-field-names/bird-field-names.csv
Normal file
88
data/cif-field-names/bird-field-names.csv
Normal file
@@ -0,0 +1,88 @@
|
||||
pdbx_reference_molecule.prd_id
|
||||
pdbx_reference_molecule.name
|
||||
pdbx_reference_molecule.represent_as
|
||||
pdbx_reference_molecule.type
|
||||
pdbx_reference_molecule.type_evidence_code
|
||||
pdbx_reference_molecule.class
|
||||
pdbx_reference_molecule.class_evidence_code
|
||||
pdbx_reference_molecule.formula
|
||||
pdbx_reference_molecule.chem_comp_id
|
||||
pdbx_reference_molecule.formula_weight
|
||||
pdbx_reference_molecule.release_status
|
||||
pdbx_reference_molecule.replaces
|
||||
pdbx_reference_molecule.replaced_by
|
||||
pdbx_reference_molecule.compound_details
|
||||
pdbx_reference_molecule.description
|
||||
pdbx_reference_molecule.representative_PDB_id_code
|
||||
|
||||
pdbx_reference_entity_list.prd_id
|
||||
pdbx_reference_entity_list.ref_entity_id
|
||||
pdbx_reference_entity_list.component_id
|
||||
pdbx_reference_entity_list.type
|
||||
pdbx_reference_entity_list.details
|
||||
|
||||
pdbx_reference_entity_nonpoly.prd_id
|
||||
pdbx_reference_entity_nonpoly.ref_entity_id
|
||||
pdbx_reference_entity_nonpoly.name
|
||||
pdbx_reference_entity_nonpoly.chem_comp_id
|
||||
|
||||
pdbx_reference_entity_link.prd_id
|
||||
pdbx_reference_entity_link.link_id
|
||||
pdbx_reference_entity_link.link_class
|
||||
pdbx_reference_entity_link.ref_entity_id_1
|
||||
pdbx_reference_entity_link.entity_seq_num_1
|
||||
pdbx_reference_entity_link.comp_id_1
|
||||
pdbx_reference_entity_link.atom_id_1
|
||||
pdbx_reference_entity_link.ref_entity_id_2
|
||||
pdbx_reference_entity_link.entity_seq_num_2
|
||||
pdbx_reference_entity_link.comp_id_2
|
||||
pdbx_reference_entity_link.atom_id_2
|
||||
pdbx_reference_entity_link.value_order
|
||||
pdbx_reference_entity_link.component_1
|
||||
pdbx_reference_entity_link.component_2
|
||||
pdbx_reference_entity_link.details
|
||||
|
||||
pdbx_reference_entity_poly_link.prd_id
|
||||
pdbx_reference_entity_poly_link.ref_entity_id
|
||||
pdbx_reference_entity_poly_link.link_id
|
||||
pdbx_reference_entity_poly_link.atom_id_1
|
||||
pdbx_reference_entity_poly_link.comp_id_1
|
||||
pdbx_reference_entity_poly_link.entity_seq_num_1
|
||||
pdbx_reference_entity_poly_link.atom_id_2
|
||||
pdbx_reference_entity_poly_link.comp_id_2
|
||||
pdbx_reference_entity_poly_link.entity_seq_num_2
|
||||
pdbx_reference_entity_poly_link.value_order
|
||||
pdbx_reference_entity_poly_link.component_id
|
||||
|
||||
pdbx_reference_entity_poly.prd_id
|
||||
pdbx_reference_entity_poly.ref_entity_id
|
||||
pdbx_reference_entity_poly.db_code
|
||||
pdbx_reference_entity_poly.db_name
|
||||
pdbx_reference_entity_poly.type
|
||||
|
||||
pdbx_reference_entity_sequence.prd_id
|
||||
pdbx_reference_entity_sequence.ref_entity_id
|
||||
pdbx_reference_entity_sequence.type
|
||||
pdbx_reference_entity_sequence.NRP_flag
|
||||
pdbx_reference_entity_sequence.one_letter_codes
|
||||
|
||||
pdbx_reference_entity_poly_seq.prd_id
|
||||
pdbx_reference_entity_poly_seq.ref_entity_id
|
||||
pdbx_reference_entity_poly_seq.num
|
||||
pdbx_reference_entity_poly_seq.mon_id
|
||||
pdbx_reference_entity_poly_seq.parent_mon_id
|
||||
pdbx_reference_entity_poly_seq.hetero
|
||||
pdbx_reference_entity_poly_seq.observed
|
||||
|
||||
pdbx_reference_entity_src_nat.prd_id
|
||||
pdbx_reference_entity_src_nat.ref_entity_id
|
||||
pdbx_reference_entity_src_nat.ordinal
|
||||
pdbx_reference_entity_src_nat.taxid
|
||||
pdbx_reference_entity_src_nat.organism_scientific
|
||||
pdbx_reference_entity_src_nat.db_code
|
||||
pdbx_reference_entity_src_nat.db_name
|
||||
|
||||
pdbx_prd_audit.prd_id
|
||||
pdbx_prd_audit.date
|
||||
pdbx_prd_audit.processing_site
|
||||
pdbx_prd_audit.action_type
|
||||
|
60
data/cif-field-names/ccd-field-names.csv
Normal file
60
data/cif-field-names/ccd-field-names.csv
Normal file
@@ -0,0 +1,60 @@
|
||||
chem_comp.id
|
||||
chem_comp.name
|
||||
chem_comp.type
|
||||
chem_comp.pdbx_type
|
||||
chem_comp.formula
|
||||
chem_comp.mon_nstd_parent_comp_id
|
||||
chem_comp.pdbx_synonyms
|
||||
chem_comp.pdbx_formal_charge
|
||||
chem_comp.pdbx_initial_date
|
||||
chem_comp.pdbx_modified_date
|
||||
chem_comp.pdbx_ambiguous_flag
|
||||
chem_comp.pdbx_release_status
|
||||
chem_comp.pdbx_replaced_by
|
||||
chem_comp.pdbx_replaces
|
||||
chem_comp.formula_weight
|
||||
chem_comp.one_letter_code
|
||||
chem_comp.three_letter_code
|
||||
chem_comp.pdbx_model_coordinates_details
|
||||
chem_comp.pdbx_model_coordinates_missing_flag
|
||||
chem_comp.pdbx_ideal_coordinates_details
|
||||
chem_comp.pdbx_ideal_coordinates_missing_flag
|
||||
chem_comp.pdbx_model_coordinates_db_code
|
||||
chem_comp.pdbx_processing_site
|
||||
|
||||
chem_comp_atom.comp_id
|
||||
chem_comp_atom.atom_id
|
||||
chem_comp_atom.alt_atom_id
|
||||
chem_comp_atom.type_symbol
|
||||
chem_comp_atom.charge
|
||||
chem_comp_atom.pdbx_align
|
||||
chem_comp_atom.pdbx_aromatic_flag
|
||||
chem_comp_atom.pdbx_leaving_atom_flag
|
||||
chem_comp_atom.pdbx_stereo_config
|
||||
chem_comp_atom.model_Cartn_x
|
||||
chem_comp_atom.model_Cartn_y
|
||||
chem_comp_atom.model_Cartn_z
|
||||
chem_comp_atom.pdbx_model_Cartn_x_ideal
|
||||
chem_comp_atom.pdbx_model_Cartn_y_ideal
|
||||
chem_comp_atom.pdbx_model_Cartn_z_ideal
|
||||
chem_comp_atom.pdbx_ordinal
|
||||
|
||||
chem_comp_bond.comp_id
|
||||
chem_comp_bond.atom_id_1
|
||||
chem_comp_bond.atom_id_2
|
||||
chem_comp_bond.value_order
|
||||
chem_comp_bond.pdbx_aromatic_flag
|
||||
chem_comp_bond.pdbx_stereo_config
|
||||
chem_comp_bond.pdbx_ordinal
|
||||
|
||||
pdbx_chem_comp_descriptor.comp_id
|
||||
pdbx_chem_comp_descriptor.type
|
||||
pdbx_chem_comp_descriptor.program
|
||||
pdbx_chem_comp_descriptor.program_version
|
||||
pdbx_chem_comp_descriptor.descriptor
|
||||
|
||||
pdbx_chem_comp_identifier.comp_id
|
||||
pdbx_chem_comp_identifier.type
|
||||
pdbx_chem_comp_identifier.program
|
||||
pdbx_chem_comp_identifier.program_version
|
||||
pdbx_chem_comp_identifier.identifier
|
||||
|
76
data/cif-field-names/cif-core-field-names.csv
Normal file
76
data/cif-field-names/cif-core-field-names.csv
Normal file
@@ -0,0 +1,76 @@
|
||||
audit.block_doi
|
||||
|
||||
database_code.depnum_ccdc_archive
|
||||
database_code.depnum_ccdc_fiz
|
||||
database_code.ICSD
|
||||
database_code.MDF
|
||||
database_code.NBS
|
||||
database_code.CSD
|
||||
database_code.COD
|
||||
|
||||
chemical.name_systematic
|
||||
chemical.name_common
|
||||
chemical.melting_point
|
||||
|
||||
chemical_formula.moiety
|
||||
chemical_formula.sum
|
||||
chemical_formula.weight
|
||||
|
||||
atom_type.symbol
|
||||
atom_type.description
|
||||
|
||||
atom_type_scat.dispersion_real
|
||||
atom_type_scat.dispersion_imag
|
||||
atom_type_scat.source
|
||||
|
||||
space_group.crystal_system
|
||||
space_group.name_H-M_full
|
||||
space_group.IT_number
|
||||
space_group_symop.operation_xyz
|
||||
|
||||
cell.length_a
|
||||
cell.length_b
|
||||
cell.length_c
|
||||
cell.angle_alpha
|
||||
cell.angle_beta
|
||||
cell.angle_gamma
|
||||
cell.volume
|
||||
cell.formula_units_Z
|
||||
|
||||
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.calc_flag
|
||||
atom_site.refinement_flags
|
||||
atom_site.disorder_assembly
|
||||
atom_site.disorder_group
|
||||
atom_site.site_symmetry_multiplicity
|
||||
|
||||
atom_site_aniso.label
|
||||
atom_site_aniso.U
|
||||
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
|
||||
atom_site_aniso.U_su
|
||||
atom_site_aniso.U_11_su
|
||||
atom_site_aniso.U_22_su
|
||||
atom_site_aniso.U_33_su
|
||||
atom_site_aniso.U_23_su
|
||||
atom_site_aniso.U_13_su
|
||||
atom_site_aniso.U_12_su
|
||||
|
||||
geom_bond.atom_site_label_1
|
||||
geom_bond.atom_site_label_2
|
||||
geom_bond.distance
|
||||
geom_bond.site_symmetry_1
|
||||
geom_bond.site_symmetry_2
|
||||
geom_bond.publ_flag
|
||||
geom_bond.valence
|
||||
|
805
data/cif-field-names/mmcif-field-names.csv
Normal file
805
data/cif-field-names/mmcif-field-names.csv
Normal file
@@ -0,0 +1,805 @@
|
||||
atom_sites.entry_id
|
||||
atom_sites.fract_transf_matrix
|
||||
atom_sites.fract_transf_vector
|
||||
|
||||
atom_site.group_PDB
|
||||
atom_site.id
|
||||
atom_site.type_symbol
|
||||
atom_site.label_atom_id
|
||||
atom_site.label_alt_id
|
||||
atom_site.label_comp_id
|
||||
atom_site.label_asym_id
|
||||
atom_site.label_entity_id
|
||||
atom_site.label_seq_id
|
||||
atom_site.pdbx_PDB_ins_code
|
||||
atom_site.pdbx_formal_charge
|
||||
atom_site.Cartn_x
|
||||
atom_site.Cartn_y
|
||||
atom_site.Cartn_z
|
||||
atom_site.occupancy
|
||||
atom_site.B_iso_or_equiv
|
||||
atom_site.auth_atom_id
|
||||
atom_site.auth_comp_id
|
||||
atom_site.auth_asym_id
|
||||
atom_site.auth_seq_id
|
||||
atom_site.pdbx_PDB_model_num
|
||||
atom_site.ihm_model_id
|
||||
|
||||
atom_site_anisotrop.id
|
||||
atom_site_anisotrop.U
|
||||
atom_site_anisotrop.U_esd
|
||||
atom_site_anisotrop.pdbx_PDB_ins_code
|
||||
atom_site_anisotrop.pdbx_auth_asym_id
|
||||
atom_site_anisotrop.pdbx_auth_atom_id
|
||||
atom_site_anisotrop.pdbx_auth_comp_id
|
||||
atom_site_anisotrop.pdbx_auth_seq_id
|
||||
atom_site_anisotrop.pdbx_label_alt_id
|
||||
atom_site_anisotrop.pdbx_label_asym_id
|
||||
atom_site_anisotrop.pdbx_label_atom_id
|
||||
atom_site_anisotrop.pdbx_label_comp_id
|
||||
atom_site_anisotrop.pdbx_label_seq_id
|
||||
atom_site_anisotrop.type_symbol
|
||||
|
||||
chem_comp.id
|
||||
chem_comp.type
|
||||
chem_comp.mon_nstd_flag
|
||||
chem_comp.name
|
||||
chem_comp.pdbx_synonyms
|
||||
chem_comp.formula
|
||||
chem_comp.formula_weight
|
||||
|
||||
chem_comp_bond.comp_id
|
||||
chem_comp_bond.pdbx_stereo_config
|
||||
chem_comp_bond.pdbx_ordinal
|
||||
chem_comp_bond.pdbx_aromatic_flag
|
||||
chem_comp_bond.atom_id_1
|
||||
chem_comp_bond.atom_id_2
|
||||
chem_comp_bond.value_order
|
||||
|
||||
pdbx_chem_comp_identifier.comp_id
|
||||
pdbx_chem_comp_identifier.type
|
||||
pdbx_chem_comp_identifier.program
|
||||
pdbx_chem_comp_identifier.program_version
|
||||
pdbx_chem_comp_identifier.identifier
|
||||
|
||||
pdbx_chem_comp_related.comp_id
|
||||
pdbx_chem_comp_related.related_comp_id
|
||||
pdbx_chem_comp_related.relationship_type
|
||||
pdbx_chem_comp_related.details
|
||||
|
||||
pdbx_chem_comp_synonyms.comp_id
|
||||
pdbx_chem_comp_synonyms.name
|
||||
pdbx_chem_comp_synonyms.provenance
|
||||
|
||||
cell.entry_id
|
||||
cell.length_a
|
||||
cell.length_b
|
||||
cell.length_c
|
||||
cell.angle_alpha
|
||||
cell.angle_beta
|
||||
cell.angle_gamma
|
||||
cell.Z_PDB
|
||||
cell.pdbx_unique_axis
|
||||
|
||||
pdbx_database_related.db_name
|
||||
pdbx_database_related.details
|
||||
pdbx_database_related.db_id
|
||||
pdbx_database_related.content_type
|
||||
|
||||
pdbx_database_status.status_code
|
||||
pdbx_database_status.status_code_sf
|
||||
pdbx_database_status.status_code_mr
|
||||
pdbx_database_status.entry_id
|
||||
pdbx_database_status.recvd_initial_deposition_date
|
||||
pdbx_database_status.SG_entry
|
||||
pdbx_database_status.deposit_site
|
||||
pdbx_database_status.process_site
|
||||
pdbx_database_status.status_code_cs
|
||||
pdbx_database_status.methods_development_category
|
||||
pdbx_database_status.pdb_format_compatible
|
||||
|
||||
entity.id
|
||||
entity.type
|
||||
entity.src_method
|
||||
entity.pdbx_description
|
||||
entity.formula_weight
|
||||
entity.pdbx_number_of_molecules
|
||||
entity.details
|
||||
entity.pdbx_mutation
|
||||
entity.pdbx_fragment
|
||||
entity.pdbx_ec
|
||||
|
||||
entity_poly.entity_id
|
||||
entity_poly.type
|
||||
entity_poly.nstd_linkage
|
||||
entity_poly.nstd_monomer
|
||||
entity_poly.pdbx_seq_one_letter_code
|
||||
entity_poly.pdbx_seq_one_letter_code_can
|
||||
entity_poly.pdbx_strand_id
|
||||
entity_poly.pdbx_target_identifier
|
||||
|
||||
entity_poly_seq.entity_id
|
||||
entity_poly_seq.num
|
||||
entity_poly_seq.mon_id
|
||||
entity_poly_seq.hetero
|
||||
|
||||
entity_src_gen.entity_id
|
||||
entity_src_gen.pdbx_src_id
|
||||
entity_src_gen.pdbx_beg_seq_num
|
||||
entity_src_gen.pdbx_end_seq_num
|
||||
entity_src_gen.pdbx_gene_src_gene
|
||||
entity_src_gen.pdbx_gene_src_scientific_name
|
||||
entity_src_gen.plasmid_name
|
||||
|
||||
entity_src_nat.entity_id
|
||||
entity_src_nat.pdbx_src_id
|
||||
entity_src_nat.pdbx_beg_seq_num
|
||||
entity_src_nat.pdbx_end_seq_num
|
||||
entity_src_nat.pdbx_organism_scientific
|
||||
entity_src_nat.pdbx_plasmid_name
|
||||
|
||||
pdbx_entity_instance_feature.ordinal
|
||||
pdbx_entity_instance_feature.feature_type
|
||||
pdbx_entity_instance_feature.details
|
||||
pdbx_entity_instance_feature.asym_id
|
||||
pdbx_entity_instance_feature.comp_id
|
||||
pdbx_entity_instance_feature.seq_num
|
||||
pdbx_entity_instance_feature.auth_asym_id
|
||||
pdbx_entity_instance_feature.auth_comp_id
|
||||
pdbx_entity_instance_feature.auth_seq_num
|
||||
|
||||
pdbx_entity_src_syn.entity_id
|
||||
pdbx_entity_src_syn.pdbx_src_id
|
||||
pdbx_entity_src_syn.pdbx_beg_seq_num
|
||||
pdbx_entity_src_syn.pdbx_end_seq_num
|
||||
pdbx_entity_src_syn.organism_scientific
|
||||
|
||||
pdbx_entity_branch.entity_id
|
||||
pdbx_entity_branch.type
|
||||
|
||||
pdbx_entity_branch_list.entity_id
|
||||
pdbx_entity_branch_list.comp_id
|
||||
pdbx_entity_branch_list.num
|
||||
pdbx_entity_branch_list.hetero
|
||||
|
||||
pdbx_entity_branch_link.link_id
|
||||
pdbx_entity_branch_link.entity_id
|
||||
pdbx_entity_branch_link.entity_branch_list_num_1
|
||||
pdbx_entity_branch_link.comp_id_1
|
||||
pdbx_entity_branch_link.atom_id_1
|
||||
pdbx_entity_branch_link.leaving_atom_id_1
|
||||
pdbx_entity_branch_link.atom_stereo_config_1
|
||||
pdbx_entity_branch_link.entity_branch_list_num_2
|
||||
pdbx_entity_branch_link.comp_id_2
|
||||
pdbx_entity_branch_link.atom_id_2
|
||||
pdbx_entity_branch_link.leaving_atom_id_2
|
||||
pdbx_entity_branch_link.atom_stereo_config_2
|
||||
pdbx_entity_branch_link.value_order
|
||||
pdbx_entity_branch_link.details
|
||||
|
||||
pdbx_branch_scheme.asym_id
|
||||
pdbx_branch_scheme.entity_id
|
||||
pdbx_branch_scheme.mon_id
|
||||
pdbx_branch_scheme.num
|
||||
pdbx_branch_scheme.auth_asym_id
|
||||
pdbx_branch_scheme.auth_mon_id
|
||||
pdbx_branch_scheme.auth_seq_num
|
||||
pdbx_branch_scheme.hetero
|
||||
pdbx_branch_scheme.pdb_mon_id
|
||||
pdbx_branch_scheme.pdb_asym_id
|
||||
pdbx_branch_scheme.pdb_seq_num
|
||||
|
||||
pdbx_entity_branch_descriptor.ordinal
|
||||
pdbx_entity_branch_descriptor.entity_id
|
||||
pdbx_entity_branch_descriptor.descriptor
|
||||
pdbx_entity_branch_descriptor.type
|
||||
pdbx_entity_branch_descriptor.program
|
||||
pdbx_entity_branch_descriptor.program_version
|
||||
|
||||
pdbx_entity_nonpoly.entity_id
|
||||
pdbx_entity_nonpoly.name
|
||||
pdbx_entity_nonpoly.comp_id
|
||||
|
||||
pdbx_nonpoly_scheme.asym_id
|
||||
pdbx_nonpoly_scheme.entity_id
|
||||
pdbx_nonpoly_scheme.mon_id
|
||||
pdbx_nonpoly_scheme.ndb_seq_num
|
||||
pdbx_nonpoly_scheme.pdb_seq_num
|
||||
pdbx_nonpoly_scheme.auth_seq_num
|
||||
pdbx_nonpoly_scheme.pdb_mon_id
|
||||
pdbx_nonpoly_scheme.auth_mon_id
|
||||
pdbx_nonpoly_scheme.pdb_strand_id
|
||||
pdbx_nonpoly_scheme.pdb_ins_code
|
||||
|
||||
entry.id
|
||||
|
||||
audit_conform.dict_name
|
||||
audit_conform.dict_version
|
||||
audit_conform.dict_location
|
||||
|
||||
database_2.database_id
|
||||
database_2.database_code
|
||||
|
||||
audit_author.name
|
||||
audit_author.pdbx_ordinal
|
||||
audit_author.identifier_ORCID
|
||||
|
||||
citation.id
|
||||
citation.title
|
||||
citation.journal_abbrev
|
||||
citation.journal_volume
|
||||
citation.page_first
|
||||
citation.page_last
|
||||
citation.year
|
||||
citation.journal_id_ASTM
|
||||
citation.country
|
||||
citation.journal_id_ISSN
|
||||
citation.journal_id_CSD
|
||||
citation.book_publisher
|
||||
citation.pdbx_database_id_PubMed
|
||||
citation.pdbx_database_id_DOI
|
||||
|
||||
citation_author.citation_id
|
||||
citation_author.name
|
||||
citation_author.ordinal
|
||||
|
||||
exptl.entry_id
|
||||
exptl.method
|
||||
|
||||
struct.entry_id
|
||||
struct.title
|
||||
struct.pdbx_descriptor
|
||||
|
||||
struct_asym.id
|
||||
struct_asym.pdbx_blank_PDB_chainid_flag
|
||||
struct_asym.pdbx_modified
|
||||
struct_asym.entity_id
|
||||
struct_asym.details
|
||||
|
||||
struct_conf.conf_type_id
|
||||
struct_conf.id
|
||||
struct_conf.pdbx_PDB_helix_id
|
||||
struct_conf.beg_label_comp_id
|
||||
struct_conf.beg_label_asym_id
|
||||
struct_conf.beg_label_seq_id
|
||||
struct_conf.pdbx_beg_PDB_ins_code
|
||||
struct_conf.end_label_comp_id
|
||||
struct_conf.end_label_asym_id
|
||||
struct_conf.end_label_seq_id
|
||||
struct_conf.pdbx_end_PDB_ins_code
|
||||
struct_conf.beg_auth_comp_id
|
||||
struct_conf.beg_auth_asym_id
|
||||
struct_conf.beg_auth_seq_id
|
||||
struct_conf.end_auth_comp_id
|
||||
struct_conf.end_auth_asym_id
|
||||
struct_conf.end_auth_seq_id
|
||||
struct_conf.pdbx_PDB_helix_class
|
||||
struct_conf.details
|
||||
struct_conf.pdbx_PDB_helix_length
|
||||
|
||||
struct_conn.id
|
||||
struct_conn.conn_type_id
|
||||
struct_conn.pdbx_PDB_id
|
||||
struct_conn.ptnr1_label_asym_id
|
||||
struct_conn.ptnr1_label_comp_id
|
||||
struct_conn.ptnr1_label_seq_id
|
||||
struct_conn.ptnr1_label_atom_id
|
||||
struct_conn.pdbx_ptnr1_label_alt_id
|
||||
struct_conn.pdbx_ptnr1_PDB_ins_code
|
||||
struct_conn.pdbx_ptnr1_standard_comp_id
|
||||
struct_conn.ptnr1_symmetry
|
||||
struct_conn.ptnr2_label_asym_id
|
||||
struct_conn.ptnr2_label_comp_id
|
||||
struct_conn.ptnr2_label_seq_id
|
||||
struct_conn.ptnr2_label_atom_id
|
||||
struct_conn.pdbx_ptnr2_label_alt_id
|
||||
struct_conn.pdbx_ptnr2_PDB_ins_code
|
||||
struct_conn.ptnr1_auth_asym_id
|
||||
struct_conn.ptnr1_auth_comp_id
|
||||
struct_conn.ptnr1_auth_seq_id
|
||||
struct_conn.ptnr2_auth_asym_id
|
||||
struct_conn.ptnr2_auth_comp_id
|
||||
struct_conn.ptnr2_auth_seq_id
|
||||
struct_conn.ptnr2_symmetry
|
||||
struct_conn.pdbx_ptnr3_label_atom_id
|
||||
struct_conn.pdbx_ptnr3_label_seq_id
|
||||
struct_conn.pdbx_ptnr3_label_comp_id
|
||||
struct_conn.pdbx_ptnr3_label_asym_id
|
||||
struct_conn.pdbx_ptnr3_label_alt_id
|
||||
struct_conn.pdbx_ptnr3_PDB_ins_code
|
||||
struct_conn.details
|
||||
struct_conn.pdbx_dist_value
|
||||
struct_conn.pdbx_value_order
|
||||
|
||||
struct_conn_type.id
|
||||
struct_conn_type.criteria
|
||||
struct_conn_type.reference
|
||||
|
||||
struct_keywords.entry_id
|
||||
struct_keywords.pdbx_keywords
|
||||
struct_keywords.text
|
||||
|
||||
struct_ncs_oper.id
|
||||
struct_ncs_oper.code
|
||||
struct_ncs_oper.matrix
|
||||
struct_ncs_oper.vector
|
||||
struct_ncs_oper.details
|
||||
|
||||
struct_sheet_range.sheet_id
|
||||
struct_sheet_range.id
|
||||
struct_sheet_range.beg_label_comp_id
|
||||
struct_sheet_range.beg_label_asym_id
|
||||
struct_sheet_range.beg_label_seq_id
|
||||
struct_sheet_range.pdbx_beg_PDB_ins_code
|
||||
struct_sheet_range.end_label_comp_id
|
||||
struct_sheet_range.end_label_asym_id
|
||||
struct_sheet_range.end_label_seq_id
|
||||
struct_sheet_range.pdbx_end_PDB_ins_code
|
||||
struct_sheet_range.beg_auth_comp_id
|
||||
struct_sheet_range.beg_auth_asym_id
|
||||
struct_sheet_range.beg_auth_seq_id
|
||||
struct_sheet_range.end_auth_comp_id
|
||||
struct_sheet_range.end_auth_asym_id
|
||||
struct_sheet_range.end_auth_seq_id
|
||||
|
||||
struct_site.id
|
||||
struct_site.pdbx_evidence_code
|
||||
struct_site.pdbx_auth_asym_id
|
||||
struct_site.pdbx_auth_comp_id
|
||||
struct_site.pdbx_auth_seq_id
|
||||
struct_site.pdbx_auth_ins_code
|
||||
struct_site.pdbx_num_residues
|
||||
struct_site.details
|
||||
|
||||
struct_site_gen.id
|
||||
struct_site_gen.site_id
|
||||
struct_site_gen.pdbx_num_res
|
||||
struct_site_gen.label_comp_id
|
||||
struct_site_gen.label_asym_id
|
||||
struct_site_gen.label_seq_id
|
||||
struct_site_gen.pdbx_auth_ins_code
|
||||
struct_site_gen.auth_comp_id
|
||||
struct_site_gen.auth_asym_id
|
||||
struct_site_gen.auth_seq_id
|
||||
struct_site_gen.label_atom_id
|
||||
struct_site_gen.label_alt_id
|
||||
struct_site_gen.symmetry
|
||||
struct_site_gen.details
|
||||
|
||||
symmetry.entry_id
|
||||
symmetry.cell_setting
|
||||
symmetry.Int_Tables_number
|
||||
symmetry.space_group_name_Hall
|
||||
symmetry.space_group_name_H-M
|
||||
|
||||
pdbx_molecule.instance_id
|
||||
pdbx_molecule.prd_id
|
||||
pdbx_molecule.asym_id
|
||||
|
||||
pdbx_molecule_features.prd_id
|
||||
pdbx_molecule_features.name
|
||||
pdbx_molecule_features.type
|
||||
pdbx_molecule_features.class
|
||||
pdbx_molecule_features.details
|
||||
|
||||
pdbx_reference_entity_link.prd_id
|
||||
pdbx_reference_entity_link.link_id
|
||||
pdbx_reference_entity_link.link_class
|
||||
pdbx_reference_entity_link.ref_entity_id_1
|
||||
pdbx_reference_entity_link.entity_seq_num_1
|
||||
pdbx_reference_entity_link.comp_id_1
|
||||
pdbx_reference_entity_link.atom_id_1
|
||||
pdbx_reference_entity_link.ref_entity_id_2
|
||||
pdbx_reference_entity_link.entity_seq_num_2
|
||||
pdbx_reference_entity_link.comp_id_2
|
||||
pdbx_reference_entity_link.atom_id_2
|
||||
pdbx_reference_entity_link.value_order
|
||||
pdbx_reference_entity_link.component_1
|
||||
pdbx_reference_entity_link.component_2
|
||||
pdbx_reference_entity_link.details
|
||||
|
||||
pdbx_reference_entity_list.prd_id
|
||||
pdbx_reference_entity_list.ref_entity_id
|
||||
pdbx_reference_entity_list.component_id
|
||||
pdbx_reference_entity_list.type
|
||||
pdbx_reference_entity_list.details
|
||||
|
||||
pdbx_reference_entity_poly_link.prd_id
|
||||
pdbx_reference_entity_poly_link.ref_entity_id
|
||||
pdbx_reference_entity_poly_link.link_id
|
||||
pdbx_reference_entity_poly_link.atom_id_1
|
||||
pdbx_reference_entity_poly_link.comp_id_1
|
||||
pdbx_reference_entity_poly_link.entity_seq_num_1
|
||||
pdbx_reference_entity_poly_link.atom_id_2
|
||||
pdbx_reference_entity_poly_link.comp_id_2
|
||||
pdbx_reference_entity_poly_link.entity_seq_num_2
|
||||
pdbx_reference_entity_poly_link.value_order
|
||||
pdbx_reference_entity_poly_link.component_id
|
||||
|
||||
pdbx_struct_assembly.id
|
||||
pdbx_struct_assembly.details
|
||||
pdbx_struct_assembly.method_details
|
||||
pdbx_struct_assembly.oligomeric_details
|
||||
pdbx_struct_assembly.oligomeric_count
|
||||
|
||||
pdbx_struct_assembly_gen.assembly_id
|
||||
pdbx_struct_assembly_gen.oper_expression
|
||||
pdbx_struct_assembly_gen.asym_id_list
|
||||
|
||||
pdbx_struct_oper_list.id
|
||||
pdbx_struct_oper_list.type
|
||||
pdbx_struct_oper_list.name
|
||||
pdbx_struct_oper_list.symmetry_operation
|
||||
pdbx_struct_oper_list.matrix
|
||||
pdbx_struct_oper_list.vector
|
||||
|
||||
pdbx_struct_mod_residue.id
|
||||
pdbx_struct_mod_residue.label_asym_id
|
||||
pdbx_struct_mod_residue.label_seq_id
|
||||
pdbx_struct_mod_residue.label_comp_id
|
||||
pdbx_struct_mod_residue.auth_asym_id
|
||||
pdbx_struct_mod_residue.auth_seq_id
|
||||
pdbx_struct_mod_residue.auth_comp_id
|
||||
pdbx_struct_mod_residue.PDB_ins_code
|
||||
pdbx_struct_mod_residue.parent_comp_id
|
||||
pdbx_struct_mod_residue.details
|
||||
|
||||
pdbx_unobs_or_zero_occ_residues.id
|
||||
pdbx_unobs_or_zero_occ_residues.PDB_model_num
|
||||
pdbx_unobs_or_zero_occ_residues.polymer_flag
|
||||
pdbx_unobs_or_zero_occ_residues.occupancy_flag
|
||||
pdbx_unobs_or_zero_occ_residues.auth_asym_id
|
||||
pdbx_unobs_or_zero_occ_residues.auth_comp_id
|
||||
pdbx_unobs_or_zero_occ_residues.auth_seq_id
|
||||
pdbx_unobs_or_zero_occ_residues.PDB_ins_code
|
||||
pdbx_unobs_or_zero_occ_residues.label_asym_id
|
||||
pdbx_unobs_or_zero_occ_residues.label_comp_id
|
||||
pdbx_unobs_or_zero_occ_residues.label_seq_id
|
||||
|
||||
ihm_struct_assembly.id
|
||||
ihm_struct_assembly.name
|
||||
ihm_struct_assembly.description
|
||||
|
||||
ihm_struct_assembly_details.id
|
||||
ihm_struct_assembly_details.assembly_id
|
||||
ihm_struct_assembly_details.parent_assembly_id
|
||||
ihm_struct_assembly_details.entity_description
|
||||
ihm_struct_assembly_details.entity_id
|
||||
ihm_struct_assembly_details.asym_id
|
||||
ihm_struct_assembly_details.entity_poly_segment_id
|
||||
|
||||
ihm_model_representation.id
|
||||
ihm_model_representation.name
|
||||
ihm_model_representation.details
|
||||
|
||||
ihm_model_representation_details.id
|
||||
ihm_model_representation_details.representation_id
|
||||
ihm_model_representation_details.entity_id
|
||||
ihm_model_representation_details.entity_description
|
||||
ihm_model_representation_details.entity_asym_id
|
||||
ihm_model_representation_details.entity_poly_segment_id
|
||||
ihm_model_representation_details.model_object_primitive
|
||||
ihm_model_representation_details.starting_model_id
|
||||
ihm_model_representation_details.model_mode
|
||||
ihm_model_representation_details.model_granularity
|
||||
ihm_model_representation_details.model_object_count
|
||||
|
||||
ihm_external_reference_info.reference_id
|
||||
ihm_external_reference_info.reference_provider
|
||||
ihm_external_reference_info.reference_type
|
||||
ihm_external_reference_info.reference
|
||||
ihm_external_reference_info.refers_to
|
||||
ihm_external_reference_info.associated_url
|
||||
|
||||
ihm_external_files.id
|
||||
ihm_external_files.reference_id
|
||||
ihm_external_files.file_path
|
||||
ihm_external_files.content_type
|
||||
ihm_external_files.file_size_bytes
|
||||
ihm_external_files.details
|
||||
|
||||
ihm_dataset_list.id
|
||||
ihm_dataset_list.data_type
|
||||
ihm_dataset_list.database_hosted
|
||||
|
||||
ihm_dataset_group.id
|
||||
ihm_dataset_group.name
|
||||
ihm_dataset_group.application
|
||||
ihm_dataset_group.details
|
||||
|
||||
ihm_dataset_group_link.group_id
|
||||
ihm_dataset_group_link.dataset_list_id
|
||||
|
||||
ihm_dataset_external_reference.id
|
||||
ihm_dataset_external_reference.dataset_list_id
|
||||
ihm_dataset_external_reference.file_id
|
||||
|
||||
ihm_dataset_related_db_reference.id
|
||||
ihm_dataset_related_db_reference.dataset_list_id
|
||||
ihm_dataset_related_db_reference.db_name
|
||||
ihm_dataset_related_db_reference.accession_code
|
||||
ihm_dataset_related_db_reference.version
|
||||
ihm_dataset_related_db_reference.details
|
||||
|
||||
ihm_related_datasets.dataset_list_id_derived
|
||||
ihm_related_datasets.dataset_list_id_primary
|
||||
|
||||
ihm_poly_residue_feature.ordinal_id
|
||||
ihm_poly_residue_feature.feature_id
|
||||
ihm_poly_residue_feature.entity_id
|
||||
ihm_poly_residue_feature.asym_id
|
||||
ihm_poly_residue_feature.seq_id_begin
|
||||
ihm_poly_residue_feature.comp_id_begin
|
||||
ihm_poly_residue_feature.seq_id_end
|
||||
ihm_poly_residue_feature.comp_id_end
|
||||
|
||||
ihm_feature_list.feature_id
|
||||
ihm_feature_list.feature_type
|
||||
ihm_feature_list.entity_type
|
||||
|
||||
ihm_cross_link_list.id
|
||||
ihm_cross_link_list.group_id
|
||||
ihm_cross_link_list.entity_description_1
|
||||
ihm_cross_link_list.entity_id_1
|
||||
ihm_cross_link_list.seq_id_1
|
||||
ihm_cross_link_list.comp_id_1
|
||||
ihm_cross_link_list.entity_description_2
|
||||
ihm_cross_link_list.entity_id_2
|
||||
ihm_cross_link_list.seq_id_2
|
||||
ihm_cross_link_list.comp_id_2
|
||||
ihm_cross_link_list.linker_type
|
||||
ihm_cross_link_list.dataset_list_id
|
||||
|
||||
ihm_cross_link_restraint.id
|
||||
ihm_cross_link_restraint.group_id
|
||||
ihm_cross_link_restraint.entity_id_1
|
||||
ihm_cross_link_restraint.asym_id_1
|
||||
ihm_cross_link_restraint.seq_id_1
|
||||
ihm_cross_link_restraint.atom_id_1
|
||||
ihm_cross_link_restraint.comp_id_1
|
||||
ihm_cross_link_restraint.entity_id_2
|
||||
ihm_cross_link_restraint.asym_id_2
|
||||
ihm_cross_link_restraint.seq_id_2
|
||||
ihm_cross_link_restraint.atom_id_2
|
||||
ihm_cross_link_restraint.comp_id_2
|
||||
ihm_cross_link_restraint.restraint_type
|
||||
ihm_cross_link_restraint.conditional_crosslink_flag
|
||||
ihm_cross_link_restraint.model_granularity
|
||||
ihm_cross_link_restraint.distance_threshold
|
||||
ihm_cross_link_restraint.psi
|
||||
ihm_cross_link_restraint.sigma_1
|
||||
ihm_cross_link_restraint.sigma_2
|
||||
|
||||
ihm_cross_link_result_parameters.id
|
||||
ihm_cross_link_result_parameters.restraint_id
|
||||
ihm_cross_link_result_parameters.model_id
|
||||
ihm_cross_link_result_parameters.psi
|
||||
ihm_cross_link_result_parameters.sigma_1
|
||||
ihm_cross_link_result_parameters.sigma_2
|
||||
|
||||
ihm_sas_restraint.id
|
||||
ihm_sas_restraint.dataset_list_id
|
||||
ihm_sas_restraint.model_id
|
||||
ihm_sas_restraint.struct_assembly_id
|
||||
ihm_sas_restraint.profile_segment_flag
|
||||
ihm_sas_restraint.fitting_atom_type
|
||||
ihm_sas_restraint.fitting_method
|
||||
ihm_sas_restraint.fitting_state
|
||||
ihm_sas_restraint.radius_of_gyration
|
||||
ihm_sas_restraint.chi_value
|
||||
ihm_sas_restraint.details
|
||||
|
||||
ihm_derived_distance_restraint.id
|
||||
ihm_derived_distance_restraint.group_id
|
||||
ihm_derived_distance_restraint.feature_id_1
|
||||
ihm_derived_distance_restraint.feature_id_2
|
||||
ihm_derived_distance_restraint.group_conditionality
|
||||
ihm_derived_distance_restraint.restraint_type
|
||||
ihm_derived_distance_restraint.distance_upper_limit
|
||||
ihm_derived_distance_restraint.random_exclusion_fraction
|
||||
ihm_derived_distance_restraint.dataset_list_id
|
||||
|
||||
ihm_2dem_class_average_restraint.id
|
||||
ihm_2dem_class_average_restraint.dataset_list_id
|
||||
ihm_2dem_class_average_restraint.number_raw_micrographs
|
||||
ihm_2dem_class_average_restraint.pixel_size_width
|
||||
ihm_2dem_class_average_restraint.pixel_size_height
|
||||
ihm_2dem_class_average_restraint.image_resolution
|
||||
ihm_2dem_class_average_restraint.image_segment_flag
|
||||
ihm_2dem_class_average_restraint.number_of_projections
|
||||
ihm_2dem_class_average_restraint.struct_assembly_id
|
||||
ihm_2dem_class_average_restraint.details
|
||||
|
||||
ihm_2dem_class_average_fitting.id
|
||||
ihm_2dem_class_average_fitting.restraint_id
|
||||
ihm_2dem_class_average_fitting.model_id
|
||||
ihm_2dem_class_average_fitting.cross_correlation_coefficient
|
||||
ihm_2dem_class_average_fitting.rot_matrix
|
||||
ihm_2dem_class_average_fitting.tr_vector
|
||||
|
||||
ihm_3dem_restraint.id
|
||||
ihm_3dem_restraint.dataset_list_id
|
||||
ihm_3dem_restraint.fitting_method
|
||||
ihm_3dem_restraint.struct_assembly_id
|
||||
ihm_3dem_restraint.number_of_gaussians
|
||||
ihm_3dem_restraint.model_id
|
||||
ihm_3dem_restraint.cross_correlation_coefficient
|
||||
|
||||
ihm_predicted_contact_restraint.id
|
||||
ihm_predicted_contact_restraint.group_id
|
||||
ihm_predicted_contact_restraint.entity_id_1
|
||||
ihm_predicted_contact_restraint.asym_id_1
|
||||
ihm_predicted_contact_restraint.seq_id_1
|
||||
ihm_predicted_contact_restraint.comp_id_1
|
||||
ihm_predicted_contact_restraint.rep_atom_1
|
||||
ihm_predicted_contact_restraint.entity_id_2
|
||||
ihm_predicted_contact_restraint.asym_id_2
|
||||
ihm_predicted_contact_restraint.seq_id_2
|
||||
ihm_predicted_contact_restraint.comp_id_2
|
||||
ihm_predicted_contact_restraint.rep_atom_2
|
||||
ihm_predicted_contact_restraint.restraint_type
|
||||
ihm_predicted_contact_restraint.distance_lower_limit
|
||||
ihm_predicted_contact_restraint.distance_upper_limit
|
||||
ihm_predicted_contact_restraint.probability
|
||||
ihm_predicted_contact_restraint.model_granularity
|
||||
ihm_predicted_contact_restraint.dataset_list_id
|
||||
ihm_predicted_contact_restraint.software_id
|
||||
|
||||
ihm_starting_model_details.starting_model_id
|
||||
ihm_starting_model_details.entity_id
|
||||
ihm_starting_model_details.entity_description
|
||||
ihm_starting_model_details.asym_id
|
||||
ihm_starting_model_details.entity_poly_segment_id
|
||||
ihm_starting_model_details.starting_model_source
|
||||
ihm_starting_model_details.starting_model_auth_asym_id
|
||||
ihm_starting_model_details.starting_model_sequence_offset
|
||||
ihm_starting_model_details.dataset_list_id
|
||||
|
||||
ihm_starting_comparative_models.id
|
||||
ihm_starting_comparative_models.starting_model_id
|
||||
ihm_starting_comparative_models.starting_model_auth_asym_id
|
||||
ihm_starting_comparative_models.starting_model_seq_id_begin
|
||||
ihm_starting_comparative_models.starting_model_seq_id_end
|
||||
ihm_starting_comparative_models.template_auth_asym_id
|
||||
ihm_starting_comparative_models.template_seq_id_begin
|
||||
ihm_starting_comparative_models.template_seq_id_end
|
||||
ihm_starting_comparative_models.template_sequence_identity
|
||||
ihm_starting_comparative_models.template_sequence_identity_denominator
|
||||
ihm_starting_comparative_models.template_dataset_list_id
|
||||
ihm_starting_comparative_models.alignment_file_id
|
||||
|
||||
ihm_starting_model_coord.starting_model_id
|
||||
ihm_starting_model_coord.group_PDB
|
||||
ihm_starting_model_coord.id
|
||||
ihm_starting_model_coord.type_symbol
|
||||
ihm_starting_model_coord.atom_id
|
||||
ihm_starting_model_coord.comp_id
|
||||
ihm_starting_model_coord.entity_id
|
||||
ihm_starting_model_coord.asym_id
|
||||
ihm_starting_model_coord.seq_id
|
||||
ihm_starting_model_coord.Cartn_x
|
||||
ihm_starting_model_coord.Cartn_y
|
||||
ihm_starting_model_coord.Cartn_z
|
||||
ihm_starting_model_coord.B_iso_or_equiv
|
||||
ihm_starting_model_coord.ordinal_id
|
||||
|
||||
ihm_starting_model_seq_dif.id
|
||||
ihm_starting_model_seq_dif.entity_id
|
||||
ihm_starting_model_seq_dif.asym_id
|
||||
ihm_starting_model_seq_dif.seq_id
|
||||
ihm_starting_model_seq_dif.comp_id
|
||||
ihm_starting_model_seq_dif.starting_model_id
|
||||
ihm_starting_model_seq_dif.db_asym_id
|
||||
ihm_starting_model_seq_dif.db_seq_id
|
||||
ihm_starting_model_seq_dif.db_comp_id
|
||||
ihm_starting_model_seq_dif.details
|
||||
|
||||
ihm_modeling_protocol.id
|
||||
ihm_modeling_protocol.protocol_name
|
||||
ihm_modeling_protocol.num_steps
|
||||
|
||||
ihm_modeling_protocol_details.id
|
||||
ihm_modeling_protocol_details.protocol_id
|
||||
ihm_modeling_protocol_details.step_id
|
||||
ihm_modeling_protocol_details.struct_assembly_id
|
||||
ihm_modeling_protocol_details.dataset_group_id
|
||||
ihm_modeling_protocol_details.struct_assembly_description
|
||||
ihm_modeling_protocol_details.step_name
|
||||
ihm_modeling_protocol_details.step_method
|
||||
ihm_modeling_protocol_details.num_models_begin
|
||||
ihm_modeling_protocol_details.num_models_end
|
||||
ihm_modeling_protocol_details.multi_scale_flag
|
||||
ihm_modeling_protocol_details.multi_state_flag
|
||||
ihm_modeling_protocol_details.ordered_flag
|
||||
ihm_modeling_protocol_details.software_id
|
||||
ihm_modeling_protocol_details.script_file_id
|
||||
|
||||
ihm_modeling_post_process.id
|
||||
ihm_modeling_post_process.protocol_id
|
||||
ihm_modeling_post_process.analysis_id
|
||||
ihm_modeling_post_process.step_id
|
||||
ihm_modeling_post_process.type
|
||||
ihm_modeling_post_process.feature
|
||||
ihm_modeling_post_process.num_models_begin
|
||||
ihm_modeling_post_process.num_models_end
|
||||
|
||||
ihm_ensemble_info.ensemble_id
|
||||
ihm_ensemble_info.ensemble_name
|
||||
ihm_ensemble_info.post_process_id
|
||||
ihm_ensemble_info.model_group_id
|
||||
ihm_ensemble_info.ensemble_clustering_method
|
||||
ihm_ensemble_info.ensemble_clustering_feature
|
||||
ihm_ensemble_info.num_ensemble_models
|
||||
ihm_ensemble_info.num_ensemble_models_deposited
|
||||
ihm_ensemble_info.ensemble_precision_value
|
||||
ihm_ensemble_info.ensemble_file_id
|
||||
|
||||
ihm_localization_density_files.id
|
||||
ihm_localization_density_files.file_id
|
||||
ihm_localization_density_files.ensemble_id
|
||||
ihm_localization_density_files.entity_id
|
||||
ihm_localization_density_files.asym_id
|
||||
ihm_localization_density_files.entity_poly_segment_id
|
||||
|
||||
ihm_model_list.model_id
|
||||
ihm_model_list.model_name
|
||||
ihm_model_list.assembly_id
|
||||
ihm_model_list.protocol_id
|
||||
ihm_model_list.representation_id
|
||||
|
||||
ihm_model_group.id
|
||||
ihm_model_group.name
|
||||
ihm_model_group.details
|
||||
|
||||
ihm_model_group_link.group_id
|
||||
ihm_model_group_link.model_id
|
||||
|
||||
ihm_model_representative.id
|
||||
ihm_model_representative.model_group_id
|
||||
ihm_model_representative.model_id
|
||||
ihm_model_representative.selection_criteria
|
||||
|
||||
ihm_sphere_obj_site.id
|
||||
ihm_sphere_obj_site.entity_id
|
||||
ihm_sphere_obj_site.seq_id_begin
|
||||
ihm_sphere_obj_site.seq_id_end
|
||||
ihm_sphere_obj_site.asym_id
|
||||
ihm_sphere_obj_site.Cartn_x
|
||||
ihm_sphere_obj_site.Cartn_y
|
||||
ihm_sphere_obj_site.Cartn_z
|
||||
ihm_sphere_obj_site.object_radius
|
||||
ihm_sphere_obj_site.rmsf
|
||||
ihm_sphere_obj_site.model_id
|
||||
|
||||
ihm_gaussian_obj_site.id
|
||||
ihm_gaussian_obj_site.entity_id
|
||||
ihm_gaussian_obj_site.seq_id_begin
|
||||
ihm_gaussian_obj_site.seq_id_end
|
||||
ihm_gaussian_obj_site.asym_id
|
||||
ihm_gaussian_obj_site.mean_Cartn_x
|
||||
ihm_gaussian_obj_site.mean_Cartn_y
|
||||
ihm_gaussian_obj_site.mean_Cartn_z
|
||||
ihm_gaussian_obj_site.weight
|
||||
ihm_gaussian_obj_site.covariance_matrix
|
||||
ihm_gaussian_obj_site.model_id
|
||||
|
||||
ihm_gaussian_obj_ensemble.id
|
||||
ihm_gaussian_obj_ensemble.entity_id
|
||||
ihm_gaussian_obj_ensemble.seq_id_begin
|
||||
ihm_gaussian_obj_ensemble.seq_id_end
|
||||
ihm_gaussian_obj_ensemble.asym_id
|
||||
ihm_gaussian_obj_ensemble.mean_Cartn_x
|
||||
ihm_gaussian_obj_ensemble.mean_Cartn_y
|
||||
ihm_gaussian_obj_ensemble.mean_Cartn_z
|
||||
ihm_gaussian_obj_ensemble.weight
|
||||
ihm_gaussian_obj_ensemble.covariance_matrix
|
||||
ihm_gaussian_obj_ensemble.ensemble_id
|
||||
|
||||
ihm_multi_state_modeling.state_id
|
||||
ihm_multi_state_modeling.state_group_id
|
||||
ihm_multi_state_modeling.population_fraction
|
||||
ihm_multi_state_modeling.population_fraction_sd
|
||||
ihm_multi_state_modeling.state_type
|
||||
ihm_multi_state_modeling.state_name
|
||||
ihm_multi_state_modeling.experiment_type
|
||||
ihm_multi_state_modeling.details
|
||||
|
76
data/cif-field-names/mmtf-filter.csv
Normal file
76
data/cif-field-names/mmtf-filter.csv
Normal file
@@ -0,0 +1,76 @@
|
||||
cell.length_a
|
||||
cell.length_b
|
||||
cell.length_c
|
||||
cell.angle_alpha
|
||||
cell.angle_beta
|
||||
cell.angle_gamma
|
||||
|
||||
symmetry.space_group_name_H-M
|
||||
|
||||
entry.id
|
||||
|
||||
struct.title
|
||||
|
||||
pdbx_database_status.recvd_initial_deposition_date
|
||||
|
||||
pdbx_audit_revision_history.revision_date
|
||||
|
||||
struct_ncs_oper
|
||||
|
||||
pdbx_struct_assembly_gen
|
||||
|
||||
pdbx_struct_oper_list
|
||||
|
||||
entity.id
|
||||
entity.type
|
||||
entity.pdbx_description
|
||||
|
||||
entity_poly.entity_id
|
||||
entity_poly.pdbx_seq_one_letter_code
|
||||
entity_poly.pdbx_strand_id
|
||||
|
||||
exptl.method
|
||||
|
||||
refine.ls_d_res_low
|
||||
refine.ls_R_factor_R_free
|
||||
refine.ls_R_factor_R_work
|
||||
|
||||
atom_site.pdbx_formal_charge
|
||||
atom_site.label_atom_id
|
||||
atom_site.type_symbol
|
||||
|
||||
chem_comp.id
|
||||
chem_comp.type
|
||||
chem_comp.name
|
||||
|
||||
chem_comp_bond
|
||||
|
||||
atom_site.Cartn_x
|
||||
atom_site.Cartn_y
|
||||
atom_site.Cartn_z
|
||||
atom_site.B_iso_or_equiv
|
||||
atom_site.id
|
||||
atom_site.label_alt_id
|
||||
atom_site.occupancy
|
||||
atom_site.label_seq_id
|
||||
atom_site.label_comp_id
|
||||
|
||||
struct_sheet_range.id
|
||||
struct_sheet_range.beg_label_asym_id
|
||||
struct_sheet_range.beg_label_seq_id
|
||||
struct_sheet_range.pdbx_beg_PDB_ins_code
|
||||
struct_sheet_range.end_label_asym_id
|
||||
struct_sheet_range.end_label_seq_id
|
||||
struct_sheet_range.pdbx_end_PDB_ins_code
|
||||
struct_conf.conf_type_id
|
||||
struct_conf.id
|
||||
struct_conf.beg_label_asym_id
|
||||
struct_conf.beg_label_seq_id
|
||||
struct_conf.pdbx_beg_PDB_ins_code
|
||||
struct_conf.end_label_asym_id
|
||||
struct_conf.end_label_seq_id
|
||||
struct_conf.pdbx_end_PDB_ins_code
|
||||
|
||||
atom_site.pdbx_PDB_ins_code
|
||||
atom_site.label_asym_id
|
||||
atom_site.auth_asym_id
|
||||
|
29
docs/interesting-pdb-entries.md
Normal file
29
docs/interesting-pdb-entries.md
Normal file
@@ -0,0 +1,29 @@
|
||||
* Cyclic polymers (1sfi, 6dny, 1HVZ)
|
||||
* B-DNA (1bna)
|
||||
* Missing carbonyl oxygen (1gfl)
|
||||
* Mono-saccharides with alt locs (1B5F)
|
||||
* Microheterogeneity
|
||||
* Protein (1EJG, 3NIR)
|
||||
* DNA (3VOK)
|
||||
* PNA: peptide nucleic acid (5eme, 1xj9)
|
||||
* Peptide derived residues
|
||||
* GFP chromophores (5Z6Y)
|
||||
* Nucleotides that don’t have a parent base set, i.e. detect purine/pyrimidine from geometry (THX in 1AUL, OMC in e.g. 5D3G)
|
||||
* Bases with modified ring atoms
|
||||
* DZ has C1 instead of N1 (e.g. 6I4N)
|
||||
* DP has N5 instead of C5 and C7 instead of N7 (e.g. 6I4N)
|
||||
* Beta & Gamma peptides (e.g. 1GAC, 6PQF)
|
||||
* Mixed (heterogeneous) all-atom/trace-only RNA model (1JGQ)
|
||||
* Polymers with residues with missing trace atoms (e.g. 2QFJ)
|
||||
* Modified RNA bases (1y26, 5L4O)
|
||||
* Discontinuous chains, i.e. gaps in the sequence (3sn6)
|
||||
* Lots of sheets (1cbs)
|
||||
* DNA (2np2, 1d66)
|
||||
* C-alpha only (2rcj)
|
||||
* Not cyclic, but termini are backbone-only and within distance but seqIds are not compatible (6SW3)
|
||||
* Close backbone atoms but not linked (e.g. 4HIV)
|
||||
* Non-standard residues
|
||||
* Protein (1BRR, 5Z6Y)
|
||||
* DNA (5D3G)
|
||||
* Multiple models with different sets of ligands or missing ligands (1J6T, 1VRC, 2ICY, 1O2F)
|
||||
* Long linear sugar chain (4HG6)
|
||||
@@ -24,7 +24,7 @@ npm run build-tsc
|
||||
and run the server by
|
||||
|
||||
```
|
||||
node lib/servers/model/server/server
|
||||
node lib/commonjs/servers/model/server/server
|
||||
```
|
||||
|
||||
## From NPM
|
||||
@@ -54,12 +54,12 @@ Sometimes nodejs might run into problems with memory. This is usually resolved b
|
||||
|
||||
## Preprocessor
|
||||
|
||||
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.
|
||||
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/commonjs/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
|
||||
|
||||
|
||||
## Local Mode
|
||||
|
||||
The server can be run in local/file based mode using ``node lib/servers/model/query`` (``model-server-query`` binary from the NPM package).
|
||||
The server can be run in local/file based mode using ``node lib/commonjs/servers/model/query`` (``model-server-query`` binary from the NPM package).
|
||||
|
||||
Custom Properties
|
||||
=================
|
||||
|
||||
@@ -28,7 +28,7 @@ npm run build-tsc
|
||||
and run the server by
|
||||
|
||||
```
|
||||
node lib/servers/volume/server
|
||||
node lib/commonjs/servers/volume/server
|
||||
```
|
||||
|
||||
## From NPM
|
||||
@@ -60,11 +60,11 @@ Sometimes nodejs might run into problems with memory. This is usually resolved b
|
||||
## 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 (``node lib/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
|
||||
To achieve this, use the ``pack`` application (``node lib/commonjs/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
|
||||
|
||||
## Local Mode
|
||||
|
||||
The program ``lib/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
|
||||
The program ``lib/commonjs/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
|
||||
|
||||
|
||||
49870
package-lock.json
generated
49870
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
151
package.json
151
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.6.0-dev.8",
|
||||
"version": "2.0.0-dev.5",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -11,37 +11,46 @@
|
||||
"url": "https://github.com/molstar/molstar/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix",
|
||||
"test": "npm run lint && jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"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 -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
|
||||
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
|
||||
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
|
||||
"build-webpack": "webpack --mode production --config ./webpack.config.production.js",
|
||||
"build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
|
||||
"watch": "concurrently -c \"green,green,gray,gray\" --names \"tsc,srv,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-servers\" \"npm:watch-extra\" \"npm:watch-webpack\"",
|
||||
"watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
|
||||
"watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
|
||||
"watch-tsc": "tsc --watch --incremental",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
|
||||
"watch-servers": "tsc --build tsconfig.commonjs.json --watch --incremental",
|
||||
"watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
|
||||
"watch-webpack": "webpack -w --mode development --display minimal",
|
||||
"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",
|
||||
"watch-webpack-viewer-debug": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.debug.js",
|
||||
"serve": "http-server -p 1338 -g",
|
||||
"model-server": "node lib/commonjs/servers/model/server.js",
|
||||
"model-server-watch": "nodemon --watch lib lib/commonjs/servers/model/server.js",
|
||||
"volume-server-test": "node lib/commonjs/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/commonjs/servers/plugin-state/index.js --working-folder ./build/state --port 1339",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
"version": "npm run build",
|
||||
"postversion": "git push && git push --tags"
|
||||
},
|
||||
"files": [
|
||||
"lib/"
|
||||
"lib/",
|
||||
"build/viewer/"
|
||||
],
|
||||
"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"
|
||||
"cif2bcif": "lib/commonjs/cli/cif2bcif/index.js",
|
||||
"cifschema": "lib/commonjs/cli/cifschema/index.js",
|
||||
"model-server": "lib/commonjs/servers/model/server.js",
|
||||
"model-server-query": "lib/commonjs/servers/model/query.js",
|
||||
"model-server-preprocess": "lib/commonjs/servers/model/preprocess.js",
|
||||
"volume-server": "lib/commonjs/servers/volume/server.js",
|
||||
"volume-server-query": "lib/commonjs/servers/volume/query.js",
|
||||
"volume-server-pack": "lib/commonjs/servers/volume/pack.js"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignoreRoot": [
|
||||
@@ -70,70 +79,72 @@
|
||||
"contributors": [
|
||||
"Alexander Rose <alexander.rose@weirdbyte.de>",
|
||||
"David Sehnal <david.sehnal@gmail.com>",
|
||||
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>"
|
||||
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>",
|
||||
"Áron Samuel Kovács <aron.kovacs@mail.muni.cz>",
|
||||
"Ludovic Autin <autin@scripps.edu>",
|
||||
"Michal Malý <michal.maly@ibt.cas.cz>",
|
||||
"Jiří Černý <jiri.cerny@ibt.cas.cz>"
|
||||
],
|
||||
"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.26.0",
|
||||
"@typescript-eslint/parser": "^2.26.0",
|
||||
"@graphql-codegen/add": "^2.0.2",
|
||||
"@graphql-codegen/cli": "^1.19.4",
|
||||
"@graphql-codegen/time": "^2.0.2",
|
||||
"@graphql-codegen/typescript": "^1.19.0",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.18.1",
|
||||
"@graphql-codegen/typescript-graphql-request": "^2.0.3",
|
||||
"@graphql-codegen/typescript-operations": "^1.17.12",
|
||||
"@types/cors": "^2.8.8",
|
||||
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
||||
"@typescript-eslint/parser": "^4.9.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"cpx2": "^2.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"concurrently": "^5.3.0",
|
||||
"cpx2": "^3.0.0",
|
||||
"css-loader": "^5.0.1",
|
||||
"eslint": "^7.15.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"http-server": "^0.12.1",
|
||||
"jest": "^25.2.4",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"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.3.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"graphql": "^15.4.0",
|
||||
"http-server": "^0.12.3",
|
||||
"jest": "^26.6.3",
|
||||
"mini-css-extract-plugin": "^1.3.2",
|
||||
"node-sass": "^5.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^10.1.0",
|
||||
"simple-git": "^2.25.0",
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-jest": "^26.4.4",
|
||||
"typescript": "^4.2.3",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-version-file-plugin": "^0.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^1.0.38",
|
||||
"@types/benchmark": "^1.0.31",
|
||||
"@types/benchmark": "^2.1.0",
|
||||
"@types/compression": "1.7.0",
|
||||
"@types/express": "^4.17.3",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/node": "^13.9.5",
|
||||
"@types/node-fetch": "^2.5.5",
|
||||
"@types/react": "^16.9.27",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/swagger-ui-dist": "3.0.5",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/jest": "^26.0.18",
|
||||
"@types/node": "^14.14.11",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/swagger-ui-dist": "3.30.0",
|
||||
"argparse": "^1.0.10",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^14.6.0",
|
||||
"immer": "^6.0.2",
|
||||
"h264-mp4-encoder": "^1.0.12",
|
||||
"immer": "^8.0.1",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"rxjs": "^6.5.4",
|
||||
"swagger-ui-dist": "^3.25.0",
|
||||
"tslib": "^1.11.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"rxjs": "^6.6.6",
|
||||
"swagger-ui-dist": "^3.37.2",
|
||||
"tslib": "^2.1.0",
|
||||
"util.promisify": "^1.0.1",
|
||||
"xhr2": "^0.2.0"
|
||||
}
|
||||
|
||||
@@ -4,40 +4,40 @@
|
||||
* @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 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/')
|
||||
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)
|
||||
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 })
|
||||
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 })
|
||||
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')
|
||||
console.log('\n###', 'clone repository');
|
||||
git()
|
||||
.outputHandler(log)
|
||||
.clone(remoteUrl, localPath)
|
||||
@@ -45,9 +45,9 @@ if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
|
||||
.exec(copyViewer)
|
||||
.add(['-A'])
|
||||
.commit('updated viewer')
|
||||
.push()
|
||||
.push();
|
||||
} else {
|
||||
console.log('\n###', 'update repository')
|
||||
console.log('\n###', 'update repository');
|
||||
git()
|
||||
.outputHandler(log)
|
||||
.fetch(['--all'])
|
||||
@@ -55,5 +55,5 @@ if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
|
||||
.exec(copyViewer)
|
||||
.add(['-A'])
|
||||
.commit('updated viewer')
|
||||
.push()
|
||||
.push();
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
|
||||
|
||||
export class BasicWrapperControls extends PluginUIComponent {
|
||||
|
||||
render() {
|
||||
return <div style={{ overflowY: 'auto', display: 'block', height: '100%' }}>
|
||||
<TransformUpdaterControl nodeRef='asm' />
|
||||
<TransformUpdaterControl nodeRef='seq-visual' header={{ name: 'Sequence Visual' }} />
|
||||
<TransformUpdaterControl nodeRef='het-visual' header={{ name: 'HET Visual' }} />
|
||||
<TransformUpdaterControl nodeRef='water-visual' header={{ name: 'Water Visual' }} initiallyCollapsed={true} />
|
||||
<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.
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
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 { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateBuilder } from '../../mol-state';
|
||||
import Expression from '../../mol-script/language/expression';
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
|
||||
export namespace StateHelper {
|
||||
export function download(b: StateBuilder.To<PSO.Root>, url: string, ref?: string) {
|
||||
return b.apply(StateTransforms.Data.Download, { url, isBinary: false }, { ref });
|
||||
}
|
||||
|
||||
export function getModel(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, modelIndex = 0) {
|
||||
const parsed = format === 'cif'
|
||||
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
|
||||
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
|
||||
|
||||
return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex });
|
||||
}
|
||||
|
||||
export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
|
||||
};
|
||||
|
||||
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
|
||||
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.StructureSelectionFromExpression, { expression, label: `Chain ${auth_asym_id}` });
|
||||
}
|
||||
|
||||
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 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]),
|
||||
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
|
||||
})
|
||||
]),
|
||||
radius
|
||||
})
|
||||
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Surr. ${comp_id} (${radius} ang)` });
|
||||
}
|
||||
|
||||
export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) {
|
||||
return b.apply(StateTransforms.Model.TransformStructureConformation,
|
||||
{ transform: { name: 'components', params: { axis: Vec3.create(1, 0, 0), angle: 0, translation: Vec3.zero() } } },
|
||||
{ tags: 'transform' });
|
||||
}
|
||||
|
||||
export function transform(b: StateBuilder.To<PSO.Molecule.Structure>, matrix: Mat4) {
|
||||
return b.apply(StateTransforms.Model.TransformStructureConformation, {
|
||||
transform: { name: 'matrix', params: matrix }
|
||||
}, { tags: 'transform' });
|
||||
}
|
||||
|
||||
export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
|
||||
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,
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
|
||||
visualRoot
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
|
||||
return visualRoot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
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 { 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, StateTransform } from '../../mol-state';
|
||||
import { StripedResidues } from './coloring';
|
||||
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
|
||||
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 }
|
||||
|
||||
class BasicWrapper {
|
||||
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: BasicWrapperControls
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
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.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' }, { ref: 'seq' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { 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' });
|
||||
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
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,
|
||||
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-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, { });
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
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(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
animate = {
|
||||
modelIndex: {
|
||||
maxFPS: 8,
|
||||
onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
|
||||
onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
|
||||
palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
|
||||
loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
|
||||
stop: () => this.plugin.state.animation.stop()
|
||||
}
|
||||
}
|
||||
|
||||
coloring = {
|
||||
applyStripes: async () => {
|
||||
const state = this.plugin.state.data;
|
||||
|
||||
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
|
||||
const tree = state.build();
|
||||
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(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.data;
|
||||
const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
|
||||
await PluginCommands.State.Update(this.plugin, { state, tree });
|
||||
},
|
||||
dynamicSuperposition: async () => {
|
||||
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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).BasicMolStarWrapper = new BasicWrapper();
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
// TODO: move to an "example"
|
||||
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { StateHelper } from './helpers';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateSelection, StateBuilder } from '../../mol-state';
|
||||
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';
|
||||
import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
|
||||
import Expression from '../../mol-script/language/expression';
|
||||
|
||||
export type SuperpositionTestInput = {
|
||||
pdbId: string,
|
||||
auth_asym_id: string,
|
||||
matrix: Mat4
|
||||
}[];
|
||||
|
||||
// function getAxisAngleTranslation(m: Mat4) {
|
||||
// const translation = Mat4.getTranslation(Vec3.zero(), m);
|
||||
// const axis = Vec3.zero();
|
||||
// const angle = 180 / Math.PI * Quat.getAxisAngle(axis, Mat4.getRotation(Quat.zero(), m));
|
||||
// return { translation, axis, angle };
|
||||
// }
|
||||
|
||||
export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) {
|
||||
const b = ctx.state.data.build().toRoot();
|
||||
for (const s of src) {
|
||||
StateHelper.visual(ctx,
|
||||
StateHelper.transform(
|
||||
StateHelper.selectChain(
|
||||
StateHelper.structure(
|
||||
StateHelper.getModel(StateHelper.download(b, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`), 'cif')),
|
||||
s.auth_asym_id
|
||||
),
|
||||
s.matrix
|
||||
)
|
||||
);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
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]] )}
|
||||
];
|
||||
|
||||
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
|
||||
const state = ctx.state.data;
|
||||
|
||||
const structures = state.build().toRoot();
|
||||
for (const s of src) {
|
||||
StateHelper.structure(
|
||||
StateHelper.getModel(StateHelper.download(structures, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`), 'cif'));
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update(ctx, { state, tree: structures });
|
||||
|
||||
const pivot = MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
|
||||
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
|
||||
})
|
||||
]);
|
||||
const rest = MS.struct.modifier.exceptBy({
|
||||
0: MS.struct.generator.all(),
|
||||
by: pivot
|
||||
});
|
||||
|
||||
const query = compile<StructureSelection>(pivot);
|
||||
const xs = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
|
||||
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.obj!.data))));
|
||||
|
||||
const transforms = superposeStructures(selections);
|
||||
const visuals = state.build();
|
||||
|
||||
siteVisual(ctx, StateHelper.selectSurroundingsOfFirstResidue(visuals.to(xs[0].transform.ref), 'HEM', 7), pivot, rest);
|
||||
for (let i = 1; i < selections.length; i++) {
|
||||
const root = visuals.to(xs[i].transform.ref);
|
||||
siteVisual(ctx,
|
||||
StateHelper.transform(StateHelper.selectSurroundingsOfFirstResidue(root, 'HEM', 7), transforms[i - 1].bTransform),
|
||||
pivot, rest);
|
||||
}
|
||||
|
||||
await PluginCommands.State.Update(ctx, { state, tree: visuals });
|
||||
}
|
||||
|
||||
function siteVisual(ctx: PluginContext, b: StateBuilder.To<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
|
||||
StateHelper.ballsAndSticks(ctx, b, pivot, 'residue-name');
|
||||
StateHelper.ballsAndSticks(ctx, b, rest, 'uniform');
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse'
|
||||
import * as util from 'util'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import * as zlib from 'zlib'
|
||||
import fetch from 'node-fetch'
|
||||
require('util.promisify').shim()
|
||||
const readFile = util.promisify(fs.readFile)
|
||||
const writeFile = util.promisify(fs.writeFile)
|
||||
|
||||
import { Progress } from '../../mol-task'
|
||||
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)) {
|
||||
console.log(`downloading ${url}...`)
|
||||
const data = await fetch(url)
|
||||
if (!fs.existsSync(DATA_DIR)) {
|
||||
fs.mkdirSync(DATA_DIR);
|
||||
}
|
||||
if (url.endsWith('.gz')) {
|
||||
await writeFile(path, zlib.gunzipSync(await data.buffer()))
|
||||
} else {
|
||||
await writeFile(path, await data.text())
|
||||
}
|
||||
console.log(`done downloading ${url}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDataAvailable() {
|
||||
await ensureAvailable(CCD_PATH, CCD_URL)
|
||||
await ensureAvailable(PVCD_PATH, PVCD_URL)
|
||||
}
|
||||
|
||||
export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
|
||||
const parsed = await parseCif(await readFile(path, 'utf8'))
|
||||
return CIF.toDatabaseCollection(schema, parsed.result)
|
||||
}
|
||||
|
||||
export async function readCCD() {
|
||||
return readFileAsCollection(CCD_PATH, CCD_Schema)
|
||||
}
|
||||
|
||||
export async function readPVCD() {
|
||||
return readFileAsCollection(PVCD_PATH, CCD_Schema)
|
||||
}
|
||||
|
||||
async function parseCif(data: string | Uint8Array) {
|
||||
const comp = CIF.parse(data);
|
||||
console.time('parse cif');
|
||||
const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
|
||||
console.timeEnd('parse cif');
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed
|
||||
}
|
||||
|
||||
export function getEncodedCif(name: string, database: Database<Database.Schema>, binary = false) {
|
||||
const encoder = CifWriter.createEncoder({ binary, encoderName: 'mol*' });
|
||||
CifWriter.Encoder.writeDatabase(encoder, name, database)
|
||||
return encoder.getData();
|
||||
}
|
||||
|
||||
type CCB = Table<CCD_Schema['chem_comp_bond']>
|
||||
type CCA = Table<CCD_Schema['chem_comp_atom']>
|
||||
|
||||
function ccbKey(compId: string, atomId1: string, atomId2: string) {
|
||||
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`
|
||||
}
|
||||
|
||||
function addChemCompBondToSet(set: Set<string>, ccb: CCB) {
|
||||
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
|
||||
set.add(ccbKey(ccb.comp_id.value(i), ccb.atom_id_1.value(i), ccb.atom_id_2.value(i)))
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
function addChemCompAtomToSet(set: Set<string>, cca: CCA) {
|
||||
for (let i = 0, il = cca._rowCount; i < il; ++i) {
|
||||
set.add(cca.atom_id.value(i))
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
function checkAddingBondsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
|
||||
const ccbSetByParent = DefaultMap<string, Set<string>>(() => new Set())
|
||||
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_bond } = pvcd[k]
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
|
||||
if (parentIds.length === 0) {
|
||||
const set = ccbSetByParent.getDefault(chem_comp.id.value(0))
|
||||
addChemCompBondToSet(set, chem_comp_bond)
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
const parentId = parentIds[i]
|
||||
const set = ccbSetByParent.getDefault(parentId)
|
||||
addChemCompBondToSet(set, chem_comp_bond)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_atom, chem_comp_bond } = pvcd[k]
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
|
||||
if (parentIds.length > 0) {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
const entryBonds = addChemCompBondToSet(new Set<string>(), chem_comp_bond)
|
||||
const entryAtoms = addChemCompAtomToSet(new Set<string>(), chem_comp_atom)
|
||||
const extraBonds = SetUtils.difference(ccbSetByParent.get(parentIds[i])!, entryBonds)
|
||||
extraBonds.forEach(bk => {
|
||||
const [a1, a2] = bk.split('|')
|
||||
if (entryAtoms.has(a1) && entryAtoms.has(a2)) {
|
||||
console.error(`Adding all PVCD bonds would wrongly add bond ${bk} for ${k}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createBonds() {
|
||||
await ensureDataAvailable()
|
||||
const ccd = await readCCD()
|
||||
const pvcd = await readPVCD()
|
||||
|
||||
const ccbSet = new Set<string>()
|
||||
|
||||
const comp_id: string[] = []
|
||||
const atom_id_1: string[] = []
|
||||
const atom_id_2: 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) {
|
||||
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
|
||||
const atomId1 = ccb.atom_id_1.value(i)
|
||||
const atomId2 = ccb.atom_id_2.value(i)
|
||||
const k = ccbKey(compId, atomId1, atomId2)
|
||||
if (!ccbSet.has(k)) {
|
||||
atom_id_1.push(atomId1)
|
||||
atom_id_2.push(atomId2)
|
||||
comp_id.push(compId)
|
||||
value_order.push(ccb.value_order.value(i))
|
||||
pdbx_aromatic_flag.push(ccb.pdbx_aromatic_flag.value(i))
|
||||
pdbx_stereo_config.push(ccb.pdbx_stereo_config.value(i))
|
||||
molstar_protonation_variant.push(protonationVariant ? 'Y' : 'N')
|
||||
ccbSet.add(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check adding bonds from PVCD
|
||||
checkAddingBondsFromPVCD(pvcd)
|
||||
|
||||
// add bonds from PVCD
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_bond } = pvcd[k]
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0)
|
||||
if (parentIds.length === 0) {
|
||||
addBonds(chem_comp.id.value(0), chem_comp_bond, false)
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
addBonds(parentIds[i], chem_comp_bond, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add bonds from CCD
|
||||
for (const k in ccd) {
|
||||
const { chem_comp, chem_comp_bond } = ccd[k]
|
||||
if (chem_comp_bond._rowCount) {
|
||||
addBonds(chem_comp.id.value(0), chem_comp_bond, false)
|
||||
}
|
||||
}
|
||||
|
||||
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: mmCIF_chemCompBond_schema },
|
||||
{ chem_comp_bond: bondTable }
|
||||
)
|
||||
|
||||
return bondDatabase
|
||||
}
|
||||
|
||||
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, '..', '..', '..', '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.'
|
||||
});
|
||||
parser.addArgument('out', {
|
||||
help: 'Generated file output path.'
|
||||
});
|
||||
parser.addArgument([ '--forceDownload', '-f' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Force download of CCD and PVCD.'
|
||||
});
|
||||
parser.addArgument([ '--binary', '-b' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Output as BinaryCIF.'
|
||||
});
|
||||
interface Args {
|
||||
out: string
|
||||
forceDownload?: boolean
|
||||
binary?: boolean
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
const FORCE_DOWNLOAD = args.forceDownload
|
||||
|
||||
run(args.out, args.binary)
|
||||
@@ -1,178 +0,0 @@
|
||||
/**
|
||||
* 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();
|
||||
37
src/apps/docking-viewer/index.html
Normal file
37
src/apps/docking-viewer/index.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!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* Docking Viewer</title>
|
||||
<style>
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 100px;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./molstar.js"></script>
|
||||
<script type="text/javascript">
|
||||
var viewer = new DockingViewer('app', [0x33DD22, 0x1133EE], true);
|
||||
|
||||
function getParam(name, regex) {
|
||||
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
var pdbqt = getParam('pdbqt', '[^&]+').trim();
|
||||
var mol2 = getParam('mol2', '[^&]+').trim();
|
||||
|
||||
viewer.loadStructuresFromUrlsAndMerge([
|
||||
{ url: pdbqt, format: 'pdbqt' },
|
||||
{ url: mol2, format: 'mol2' }
|
||||
]);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
213
src/apps/docking-viewer/index.ts
Normal file
213
src/apps/docking-viewer/index.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import '../../mol-util/polyfill';
|
||||
import { createPlugin } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import './index.html';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { ObjectKeys } from '../../mol-util/type-helpers';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { PluginStateTransform, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Task } from '../../mol-task';
|
||||
import { StateObject } from '../../mol-state';
|
||||
import { ViewportComponent, StructurePreset, ShowButtons } from './viewport';
|
||||
import { PluginBehaviors } from '../../mol-plugin/behavior';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
|
||||
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
|
||||
|
||||
const DefaultViewerOptions = {
|
||||
extensions: ObjectKeys({}),
|
||||
layoutIsExpanded: true,
|
||||
layoutShowControls: true,
|
||||
layoutShowRemoteState: true,
|
||||
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
|
||||
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
|
||||
viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
|
||||
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
|
||||
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
|
||||
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
|
||||
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
|
||||
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
|
||||
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
|
||||
};
|
||||
|
||||
class Viewer {
|
||||
plugin: PluginContext
|
||||
|
||||
constructor(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
|
||||
const o = { ...DefaultViewerOptions, ...{
|
||||
layoutIsExpanded: false,
|
||||
layoutShowControls: false,
|
||||
layoutShowRemoteState: false,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: false,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: true,
|
||||
viewportShowControls: false,
|
||||
viewportShowSettings: false,
|
||||
viewportShowSelectionMode: false,
|
||||
viewportShowAnimation: false,
|
||||
} };
|
||||
const defaultSpec = DefaultPluginSpec();
|
||||
|
||||
const spec: PluginSpec = {
|
||||
actions: [...defaultSpec.actions],
|
||||
behaviors: [
|
||||
PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci, { mark: false }),
|
||||
PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider),
|
||||
PluginSpec.Behavior(PluginBehaviors.Camera.FocusLoci),
|
||||
|
||||
PluginSpec.Behavior(PluginBehaviors.CustomProps.StructureInfo),
|
||||
PluginSpec.Behavior(PluginBehaviors.CustomProps.Interactions),
|
||||
PluginSpec.Behavior(PluginBehaviors.CustomProps.SecondaryStructure),
|
||||
],
|
||||
animations: [...defaultSpec.animations || []],
|
||||
customParamEditors: defaultSpec.customParamEditors,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: o.layoutIsExpanded,
|
||||
showControls: o.layoutShowControls,
|
||||
controlsDisplay: o.layoutControlsDisplay,
|
||||
},
|
||||
controls: {
|
||||
...defaultSpec.layout && defaultSpec.layout.controls,
|
||||
top: o.layoutShowSequence ? undefined : 'none',
|
||||
bottom: o.layoutShowLog ? undefined : 'none',
|
||||
left: o.layoutShowLeftPanel ? undefined : 'none',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
...defaultSpec.components,
|
||||
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
|
||||
viewport: {
|
||||
view: ViewportComponent
|
||||
}
|
||||
},
|
||||
config: [
|
||||
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
|
||||
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
|
||||
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
|
||||
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
|
||||
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
|
||||
[PluginConfig.State.DefaultServer, o.pluginStateServer],
|
||||
[PluginConfig.State.CurrentServer, o.pluginStateServer],
|
||||
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
|
||||
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
|
||||
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider],
|
||||
[ShowButtons, showButtons]
|
||||
]
|
||||
};
|
||||
|
||||
const element = typeof elementOrId === 'string'
|
||||
? document.getElementById(elementOrId)
|
||||
: elementOrId;
|
||||
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
|
||||
this.plugin = createPlugin(element, spec);
|
||||
|
||||
(this.plugin.customState as any) = {
|
||||
colorPalette: {
|
||||
name: 'colors',
|
||||
params: { list: { colors } }
|
||||
}
|
||||
};
|
||||
|
||||
this.plugin.behaviors.canvas3d.initialized.subscribe(v => {
|
||||
if (v) {
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
|
||||
renderer: {
|
||||
...this.plugin.canvas3d!.props.renderer,
|
||||
backgroundColor: ColorNames.white,
|
||||
},
|
||||
camera: {
|
||||
...this.plugin.canvas3d!.props.camera,
|
||||
helper: { axes: { name: 'off', params: {} } }
|
||||
}
|
||||
} });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadStructuresFromUrlsAndMerge(sources: { url: string, format: BuiltInTrajectoryFormat, isBinary?: boolean }[]) {
|
||||
const structures: { ref: string }[] = [];
|
||||
for (const { url, format, isBinary } of sources) {
|
||||
const data = await this.plugin.builders.data.download({ url, isBinary });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
|
||||
const model = await this.plugin.builders.structure.createModel(trajectory);
|
||||
const modelProperties = await this.plugin.builders.structure.insertModelProperties(model);
|
||||
const structure = await this.plugin.builders.structure.createStructure(modelProperties || model);
|
||||
const structureProperties = await this.plugin.builders.structure.insertStructureProperties(structure);
|
||||
|
||||
structures.push({ ref: structureProperties?.ref || structure.ref });
|
||||
}
|
||||
|
||||
// remove current structuresfrom hierarchy as they will be merged
|
||||
// TODO only works with using loadStructuresFromUrlsAndMerge once
|
||||
// need some more API metho to work with the hierarchy
|
||||
this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove');
|
||||
|
||||
const dependsOn = structures.map(({ ref }) => ref);
|
||||
const data = this.plugin.state.data.build().toRoot().apply(MergeStructures, { structures }, { dependsOn });
|
||||
const structure = await data.commit();
|
||||
const structureProperties = await this.plugin.builders.structure.insertStructureProperties(structure);
|
||||
this.plugin.behaviors.canvas3d.initialized.subscribe(async v => {
|
||||
await this.plugin.builders.structure.representation.applyPreset(structureProperties || structure, StructurePreset);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type MergeStructures = typeof MergeStructures
|
||||
const MergeStructures = PluginStateTransform.BuiltIn({
|
||||
name: 'merge-structures',
|
||||
display: { name: 'Merge Structures', description: 'Merge Structure' },
|
||||
from: PSO.Root,
|
||||
to: PSO.Molecule.Structure,
|
||||
params: {
|
||||
structures: PD.ObjectList({
|
||||
ref: PD.Text('')
|
||||
}, ({ ref }) => ref, { isHidden: true })
|
||||
}
|
||||
})({
|
||||
apply({ params, dependencies }) {
|
||||
return Task.create('Merge Structures', async ctx => {
|
||||
if (params.structures.length === 0) return StateObject.Null;
|
||||
|
||||
const first = dependencies![params.structures[0].ref].data as Structure;
|
||||
const builder = Structure.Builder({ masterModel: first.models[0] });
|
||||
for (const { ref } of params.structures) {
|
||||
const s = dependencies![ref].data as Structure;
|
||||
for (const unit of s.units) {
|
||||
// TODO invariantId
|
||||
builder.addUnit(unit.kind, unit.model, unit.conformation.operator, unit.elements, unit.traits);
|
||||
}
|
||||
}
|
||||
|
||||
const structure = builder.getStructure();
|
||||
return new PSO.Molecule.Structure(structure, { label: 'Merged Structure' });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
(window as any).DockingViewer = Viewer;
|
||||
export { Viewer as DockingViewer };
|
||||
276
src/apps/docking-viewer/viewport.tsx
Normal file
276
src/apps/docking-viewer/viewport.tsx
Normal file
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
|
||||
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
|
||||
import { LociLabels } from '../../mol-plugin-ui/controls';
|
||||
import { Toasts } from '../../mol-plugin-ui/toast';
|
||||
import { Button } from '../../mol-plugin-ui/controls/common';
|
||||
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { InteractionsRepresentationProvider } from '../../mol-model-props/computed/representations/interactions';
|
||||
import { InteractionTypeColorThemeProvider } from '../../mol-model-props/computed/themes/interaction-type';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
|
||||
function shinyStyle(plugin: PluginContext) {
|
||||
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
style: { name: 'plastic', params: {} },
|
||||
},
|
||||
postprocessing: {
|
||||
...plugin.canvas3d!.props.postprocessing,
|
||||
occlusion: { name: 'off', params: {} },
|
||||
outline: { name: 'off', params: {} }
|
||||
}
|
||||
} });
|
||||
}
|
||||
|
||||
function occlusionStyle(plugin: PluginContext) {
|
||||
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
renderer: {
|
||||
...plugin.canvas3d!.props.renderer,
|
||||
style: { name: 'flat', params: {} }
|
||||
},
|
||||
postprocessing: {
|
||||
...plugin.canvas3d!.props.postprocessing,
|
||||
occlusion: { name: 'on', params: {
|
||||
samples: 64,
|
||||
radius: 8,
|
||||
bias: 1.0,
|
||||
blurKernelSize: 13
|
||||
} },
|
||||
outline: { name: 'on', params: {
|
||||
scale: 1.0,
|
||||
threshold: 0.33
|
||||
} }
|
||||
}
|
||||
} });
|
||||
}
|
||||
|
||||
const ligandPlusSurroundings = StructureSelectionQuery('Surrounding Residues (5 \u212B) of Ligand plus Ligand itself', MS.struct.modifier.union([
|
||||
MS.struct.modifier.includeSurroundings({
|
||||
0: StructureSelectionQueries.ligand.expression,
|
||||
radius: 5,
|
||||
'as-whole-residues': true
|
||||
})
|
||||
]));
|
||||
|
||||
const ligandSurroundings = StructureSelectionQuery('Surrounding Residues (5 \u212B) of Ligand', MS.struct.modifier.union([
|
||||
MS.struct.modifier.exceptBy({
|
||||
0: ligandPlusSurroundings.expression,
|
||||
by: StructureSelectionQueries.ligand.expression
|
||||
})
|
||||
]));
|
||||
|
||||
const PresetParams = {
|
||||
...StructureRepresentationPresetProvider.CommonParams,
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const StructurePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure',
|
||||
display: { name: 'Structure' },
|
||||
params: () => PresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
if (!structureCell) return {};
|
||||
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
await shinyStyle(plugin);
|
||||
plugin.managers.interactivity.setProps({ granularity: 'residue' });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
export const IllustrativePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-illustrative',
|
||||
display: { name: 'Illustrative' },
|
||||
params: () => PresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
if (!structureCell) return {};
|
||||
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'spacefill', typeParams: { ...typeParams }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'spacefill', typeParams: { ...typeParams }, color: 'illustrative', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
await occlusionStyle(plugin);
|
||||
plugin.managers.interactivity.setProps({ granularity: 'residue' });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
const SurfacePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-surface',
|
||||
display: { name: 'Surface' },
|
||||
params: () => PresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
await shinyStyle(plugin);
|
||||
plugin.managers.interactivity.setProps({ granularity: 'residue' });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
const PocketPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-pocket',
|
||||
display: { name: 'Pocket' },
|
||||
params: () => PresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
await shinyStyle(plugin);
|
||||
plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
const InteractionsPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-interactions',
|
||||
display: { name: 'Interactions' },
|
||||
params: () => PresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
const components = {
|
||||
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
|
||||
surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
|
||||
interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`)
|
||||
};
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
const representations = {
|
||||
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
|
||||
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
|
||||
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
|
||||
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
await shinyStyle(plugin);
|
||||
plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
export const ShowButtons = PluginConfig.item('showButtons', true);
|
||||
|
||||
export class ViewportComponent extends PluginUIComponent {
|
||||
async _set(structures: readonly StructureRef[], preset: StructureRepresentationPresetProvider) {
|
||||
await this.plugin.managers.structure.component.clear(structures);
|
||||
await this.plugin.managers.structure.component.applyPreset(structures, preset);
|
||||
}
|
||||
|
||||
set = async (preset: StructureRepresentationPresetProvider) => {
|
||||
await this._set(this.plugin.managers.structure.hierarchy.selection.structures, preset);
|
||||
}
|
||||
|
||||
structurePreset = () => this.set(StructurePreset);
|
||||
illustrativePreset = () => this.set(IllustrativePreset);
|
||||
surfacePreset = () => this.set(SurfacePreset);
|
||||
pocketPreset = () => this.set(PocketPreset);
|
||||
interactionsPreset = () => this.set(InteractionsPreset);
|
||||
|
||||
get showButtons () {
|
||||
return this.plugin.config.get(ShowButtons);
|
||||
}
|
||||
|
||||
render() {
|
||||
const VPControls = this.plugin.spec.components?.viewport?.controls || ViewportControls;
|
||||
|
||||
return <>
|
||||
<Viewport />
|
||||
{this.showButtons && <div className='msp-viewport-top-left-controls'>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<Button onClick={this.structurePreset} >Structure</Button>
|
||||
</div>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<Button onClick={this.illustrativePreset}>Illustrative</Button>
|
||||
</div>
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<Button onClick={this.surfacePreset}>Surface</Button>
|
||||
</div>
|
||||
{/* <div style={{ marginBottom: '4px' }}>
|
||||
<Button onClick={this.pocketPreset}>Pocket</Button>
|
||||
</div> */}
|
||||
<div style={{ marginBottom: '4px' }}>
|
||||
<Button onClick={this.interactionsPreset}>Interactions</Button>
|
||||
</div>
|
||||
</div>}
|
||||
<VPControls />
|
||||
<BackgroundTaskProgress />
|
||||
<div className='msp-highlight-toast-wrapper'>
|
||||
<LociLabels />
|
||||
<Toasts />
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
}
|
||||
43
src/apps/viewer/embedded.html
Normal file
43
src/apps/viewer/embedded.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<!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">
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
<title>Embedded Mol* Viewer</title>
|
||||
<style>
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 100px;
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./molstar.js"></script>
|
||||
<script type="text/javascript">
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
layoutIsExpanded: false,
|
||||
layoutShowControls: false,
|
||||
layoutShowRemoteState: false,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: false,
|
||||
layoutShowLeftPanel: true,
|
||||
|
||||
viewportShowExpand: true,
|
||||
viewportShowSelectionMode: false,
|
||||
viewportShowAnimation: false,
|
||||
|
||||
pdbProvider: 'rcsb',
|
||||
emdbProvider: 'rcsb',
|
||||
});
|
||||
viewer.loadPdb('7bv2');
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,228 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
/**
|
||||
* 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], { assembly: { id, operId: i, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 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://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/'
|
||||
|
||||
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 })
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* 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,168 +0,0 @@
|
||||
// /**
|
||||
// * 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 } 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[];
|
||||
|
||||
// data.sort((a, b) => a.order - b.order);
|
||||
|
||||
// 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);
|
||||
// }
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
||||
// 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 = {
|
||||
// // ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// // sizeFactor: 1,
|
||||
// // offsetX: 0,
|
||||
// // offsetY: 0,
|
||||
// // offsetZ: 10,
|
||||
// // background: true,
|
||||
// // backgroundMargin: 0.2,
|
||||
// // backgroundColor: ColorNames.snow,
|
||||
// // 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);
|
||||
|
||||
// let i = 0;
|
||||
// for (const l of params.e.labels) {
|
||||
// const expression = createExpression([l.i_atom]);
|
||||
// const group = b.to(template.structure)
|
||||
// .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
|
||||
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
|
||||
// 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
|
||||
// });
|
||||
// }
|
||||
|
||||
// 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);
|
||||
|
||||
// const s: Camera.Snapshot = {
|
||||
// mode: 'perspective',
|
||||
// fov: Math.PI / 4,
|
||||
// position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
// target: e.pos,
|
||||
// radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
// fog: 50,
|
||||
// up,
|
||||
// };
|
||||
// return s;
|
||||
// }
|
||||
|
||||
// function 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
|
||||
// });
|
||||
// }
|
||||
@@ -34,10 +34,56 @@
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
<script type="text/javascript" src="./molstar.js"></script>
|
||||
<script type="text/javascript">
|
||||
function getParam(name, regex) {
|
||||
var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
|
||||
var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
|
||||
if (debugMode) molstar.setDebugMode(debugMode, debugMode);
|
||||
|
||||
var disableAntialiasing = getParam('disable-antialiasing', '[^&]+').trim() === '1';
|
||||
var pixelScale = parseFloat(getParam('pixel-scale', '[^&]+').trim() || '1');
|
||||
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
|
||||
var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
|
||||
var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
|
||||
var viewer = new molstar.Viewer('app', {
|
||||
disableAntialiasing: disableAntialiasing,
|
||||
pixelScale: pixelScale,
|
||||
enableWboit: !disableWboit,
|
||||
layoutShowControls: !hideControls,
|
||||
viewportShowExpand: false,
|
||||
pdbProvider: pdbProvider || 'pdbe',
|
||||
emdbProvider: emdbProvider || 'pdbe',
|
||||
});
|
||||
|
||||
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
|
||||
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
|
||||
|
||||
var snapshotUrl = getParam('snapshot-url', '[^&]+').trim();
|
||||
var snapshotUrlType = getParam('snapshot-url-type', '[^&]+').toLowerCase().trim() || 'molj';
|
||||
if (snapshotUrl && snapshotUrlType) viewer.loadSnapshotFromUrl(snapshotUrl, snapshotUrlType);
|
||||
|
||||
var structureUrl = getParam('structure-url', '[^&]+').trim();
|
||||
var structureUrlFormat = getParam('structure-url-format', '[a-z]+').toLowerCase().trim();
|
||||
var structureUrlIsBinary = getParam('structure-url-is-binary', '[^&]+').trim() === '1';
|
||||
if (structureUrl) viewer.loadStructureFromUrl(structureUrl, structureUrlFormat, structureUrlIsBinary);
|
||||
|
||||
var pdb = getParam('pdb', '[^&]+').trim();
|
||||
if (pdb) viewer.loadPdb(pdb);
|
||||
|
||||
var pdbDev = getParam('pdb-dev', '[^&]+').trim();
|
||||
if (pdbDev) viewer.loadPdbDev(pdbDev);
|
||||
|
||||
var emdb = getParam('emdb', '[^&]+').trim();
|
||||
if (emdb) viewer.loadEmdb(emdb);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,102 +1,279 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import '../../mol-util/polyfill';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import './favicon.ico'
|
||||
import { createPlugin } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import './index.html';
|
||||
import './embedded.html';
|
||||
import './favicon.ico';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { LoadCellPackModel } from './extensions/cellpack/model';
|
||||
import { StructureFromCellpack } from './extensions/cellpack/state';
|
||||
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
import { CellPack } from '../../extensions/cellpack';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { ObjectKeys } from '../../mol-util/type-helpers';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
|
||||
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
|
||||
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
|
||||
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
|
||||
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { StateObjectSelector } from '../../mol-state';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
|
||||
function getParam(name: string, regex: string): string {
|
||||
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
|
||||
return decodeURIComponent(((window.location.search || '').match(r) || [])[1] || '');
|
||||
}
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
const hideControls = getParam('hide-controls', `[^&]+`) === '1';
|
||||
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
|
||||
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
|
||||
|
||||
function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [
|
||||
...DefaultPluginSpec.actions,
|
||||
// PluginSpec.Action(CreateJoleculeState),
|
||||
PluginSpec.Action(LoadCellPackModel),
|
||||
PluginSpec.Action(StructureFromCellpack),
|
||||
],
|
||||
behaviors: [...DefaultPluginSpec.behaviors],
|
||||
animations: [...DefaultPluginSpec.animations || []],
|
||||
customParamEditors: DefaultPluginSpec.customParamEditors,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: true,
|
||||
showControls: !hideControls
|
||||
const CustomFormats = [
|
||||
['g3d', G3dProvider] as const
|
||||
];
|
||||
|
||||
const Extensions = {
|
||||
'cellpack': PluginSpec.Behavior(CellPack),
|
||||
'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
|
||||
'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
|
||||
'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
|
||||
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
|
||||
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
|
||||
'g3d': PluginSpec.Behavior(G3DFormat),
|
||||
'mp4-export': PluginSpec.Behavior(Mp4Export)
|
||||
};
|
||||
|
||||
const DefaultViewerOptions = {
|
||||
customFormats: CustomFormats as [string, DataFormatProvider][],
|
||||
extensions: ObjectKeys(Extensions),
|
||||
layoutIsExpanded: true,
|
||||
layoutShowControls: true,
|
||||
layoutShowRemoteState: true,
|
||||
layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
|
||||
layoutShowSequence: true,
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
disableAntialiasing: false,
|
||||
pixelScale: 1,
|
||||
enableWboit: true,
|
||||
|
||||
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
|
||||
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
|
||||
viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
|
||||
viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
|
||||
viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
|
||||
pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
|
||||
volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
|
||||
volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
|
||||
pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
|
||||
emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
|
||||
};
|
||||
type ViewerOptions = typeof DefaultViewerOptions;
|
||||
|
||||
export class Viewer {
|
||||
plugin: PluginContext
|
||||
|
||||
constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
|
||||
const o = { ...DefaultViewerOptions, ...options };
|
||||
const defaultSpec = DefaultPluginSpec();
|
||||
|
||||
const spec: PluginSpec = {
|
||||
actions: [...defaultSpec.actions],
|
||||
behaviors: [
|
||||
...defaultSpec.behaviors,
|
||||
...o.extensions.map(e => Extensions[e]),
|
||||
],
|
||||
animations: [...defaultSpec.animations || []],
|
||||
customParamEditors: defaultSpec.customParamEditors,
|
||||
customFormats: o?.customFormats,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: o.layoutIsExpanded,
|
||||
showControls: o.layoutShowControls,
|
||||
controlsDisplay: o.layoutControlsDisplay,
|
||||
},
|
||||
controls: {
|
||||
...defaultSpec.layout && defaultSpec.layout.controls,
|
||||
top: o.layoutShowSequence ? undefined : 'none',
|
||||
bottom: o.layoutShowLog ? undefined : 'none',
|
||||
left: o.layoutShowLeftPanel ? undefined : 'none',
|
||||
}
|
||||
},
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
|
||||
}
|
||||
},
|
||||
config: DefaultPluginSpec.config
|
||||
};
|
||||
spec.config?.set(PluginConfig.Viewport.ShowExpand, false);
|
||||
const plugin = createPlugin(document.getElementById('app')!, spec);
|
||||
trySetSnapshot(plugin);
|
||||
tryLoadFromUrl(plugin);
|
||||
}
|
||||
components: {
|
||||
...defaultSpec.components,
|
||||
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
|
||||
},
|
||||
config: [
|
||||
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
|
||||
[PluginConfig.General.PixelScale, o.pixelScale],
|
||||
[PluginConfig.General.EnableWboit, o.enableWboit],
|
||||
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
|
||||
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
|
||||
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
|
||||
[PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
|
||||
[PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
|
||||
[PluginConfig.State.DefaultServer, o.pluginStateServer],
|
||||
[PluginConfig.State.CurrentServer, o.pluginStateServer],
|
||||
[PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
|
||||
[PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
|
||||
[PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
|
||||
[PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
|
||||
]
|
||||
};
|
||||
|
||||
async function trySetSnapshot(ctx: PluginContext) {
|
||||
try {
|
||||
const snapshotUrl = getParam('snapshot-url', `[^&]+`);
|
||||
const snapshotId = getParam('snapshot-id', `[^&]+`);
|
||||
if (!snapshotUrl && !snapshotId) return;
|
||||
// TODO parametrize the server
|
||||
const url = snapshotId
|
||||
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
|
||||
: snapshotUrl;
|
||||
await PluginCommands.State.Snapshots.Fetch(ctx, { url })
|
||||
} catch (e) {
|
||||
ctx.log.error('Failed to load snapshot.');
|
||||
console.warn('Failed to load snapshot', e);
|
||||
const element = typeof elementOrId === 'string'
|
||||
? document.getElementById(elementOrId)
|
||||
: elementOrId;
|
||||
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
|
||||
this.plugin = createPlugin(element, spec);
|
||||
}
|
||||
}
|
||||
|
||||
async function tryLoadFromUrl(ctx: PluginContext) {
|
||||
const url = getParam('loadFromURL', '[^&]+').trim();
|
||||
try {
|
||||
if (!url) return;
|
||||
setRemoteSnapshot(id: string) {
|
||||
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
|
||||
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
|
||||
}
|
||||
|
||||
let format = 'cif', isBinary = false;
|
||||
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
|
||||
case 'pdb': format = 'pdb'; break;
|
||||
case 'mmbcif': isBinary = true; break;
|
||||
}
|
||||
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
|
||||
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
|
||||
}
|
||||
|
||||
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
|
||||
|
||||
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
|
||||
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'url',
|
||||
params: {
|
||||
url,
|
||||
url: Asset.Url(url),
|
||||
format: format as any,
|
||||
isBinary,
|
||||
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async loadAllModelsOrAssemblyFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
|
||||
const plugin = this.plugin;
|
||||
|
||||
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
|
||||
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
|
||||
|
||||
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'all-models', { useDefaultIfSingleModel: true, representationPresetParams: options?.representationParams });
|
||||
}
|
||||
|
||||
async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
|
||||
const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
|
||||
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
|
||||
}
|
||||
|
||||
loadPdb(pdb: string, options?: LoadStructureOptions) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'pdb' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: pdb,
|
||||
server: {
|
||||
name: provider,
|
||||
params: PdbDownloadProvider[provider].defaultValue as any
|
||||
}
|
||||
},
|
||||
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
loadPdbDev(pdbDev: string) {
|
||||
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
|
||||
source: {
|
||||
name: 'pdb-dev' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: pdbDev,
|
||||
encoding: 'bcif',
|
||||
},
|
||||
options: params.source.params.options,
|
||||
}
|
||||
}
|
||||
}));
|
||||
} catch (e) {
|
||||
ctx.log.error(`Failed to load from URL (${url})`);
|
||||
console.warn(`Failed to load from URL (${url})`, e);
|
||||
}
|
||||
|
||||
loadEmdb(emdb: string, options?: { detail?: number }) {
|
||||
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
|
||||
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
|
||||
source: {
|
||||
name: 'pdb-emd-ds' as const,
|
||||
params: {
|
||||
provider: {
|
||||
id: emdb,
|
||||
server: provider,
|
||||
},
|
||||
detail: options?.detail ?? 3,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async loadVolumeFromUrl(url: string, format: BuildInVolumeFormat, isBinary: boolean, isovalues: VolumeIsovalueInfo[], entryId?: string) {
|
||||
const plugin = this.plugin;
|
||||
|
||||
if (!plugin.dataFormats.get(format)) {
|
||||
throw new Error(`Unknown density format: ${format}`);
|
||||
}
|
||||
|
||||
return plugin.dataTransaction(async () => {
|
||||
const data = await plugin.builders.data.download({ url, isBinary, label: entryId }, { state: { isGhost: true } });
|
||||
|
||||
const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId });
|
||||
const volume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
|
||||
if (!volume?.isOk) throw new Error('Failed to parse any volume.');
|
||||
|
||||
const repr = plugin.build().to(volume);
|
||||
for (const iso of isovalues) {
|
||||
repr.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, volume.data!, {
|
||||
type: 'isosurface',
|
||||
typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
|
||||
color: 'uniform',
|
||||
colorParams: { value: iso.color }
|
||||
}));
|
||||
}
|
||||
|
||||
await repr.commit();
|
||||
});
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
this.plugin.layout.events.updated.next();
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
export interface LoadStructureOptions {
|
||||
representationParams?: StructureRepresentationPresetProvider.CommonParams
|
||||
}
|
||||
|
||||
export interface VolumeIsovalueInfo {
|
||||
type: 'absolute' | 'relative',
|
||||
value: number,
|
||||
color: Color,
|
||||
alpha?: number
|
||||
}
|
||||
73
src/cli/chem-comp-dict/create-ions.ts
Normal file
73
src/cli/chem-comp-dict/create-ions.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Josh McMenemy <josh.mcmenemy@gmail.com>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as path from 'path';
|
||||
import util from 'util';
|
||||
import fs from 'fs';
|
||||
require('util.promisify').shim();
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
import { DatabaseCollection } from '../../mol-data/db';
|
||||
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
|
||||
import { ensureDataAvailable, readCCD } from './util';
|
||||
|
||||
function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
|
||||
const ionNames: string[] = [];
|
||||
for (const k in ccd) {
|
||||
const {chem_comp} = ccd[k];
|
||||
if (chem_comp.name.value(0).toUpperCase().includes(' ION')) {
|
||||
ionNames.push(chem_comp.id.value(0));
|
||||
}
|
||||
}
|
||||
// these are extra ions that don't have ION in their name
|
||||
ionNames.push('NCO', 'OHX');
|
||||
return ionNames;
|
||||
}
|
||||
|
||||
function writeIonNamesFile(filePath: string, ionNames: string[]) {
|
||||
const output = `/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated ion names params file. Names extracted from CCD components.
|
||||
*
|
||||
* @author molstar/chem-comp-dict/create-table cli
|
||||
*/
|
||||
|
||||
export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});
|
||||
`;
|
||||
writeFile(filePath, output);
|
||||
}
|
||||
|
||||
async function run(out: string, forceDownload = false) {
|
||||
await ensureDataAvailable(forceDownload);
|
||||
const ccd = await readCCD();
|
||||
const ionNames = extractIonNames(ccd);
|
||||
if (!fs.existsSync(path.dirname(out))) {
|
||||
fs.mkdirSync(path.dirname(out));
|
||||
}
|
||||
writeIonNamesFile(out, ionNames);
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Extract and save IonNames from CCD.'
|
||||
});
|
||||
parser.addArgument('out', {
|
||||
help: 'Generated file output path.'
|
||||
});
|
||||
parser.addArgument([ '--forceDownload', '-f' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Force download of CCD and PVCD.'
|
||||
});
|
||||
interface Args {
|
||||
out: string,
|
||||
forceDownload?: boolean,
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
run(args.out, args.forceDownload);
|
||||
294
src/cli/chem-comp-dict/create-table.ts
Normal file
294
src/cli/chem-comp-dict/create-table.ts
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as util from 'util';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
require('util.promisify').shim();
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
import { Database, Table, DatabaseCollection } from '../../mol-data/db';
|
||||
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';
|
||||
import { ccd_chemCompAtom_schema } from '../../mol-io/reader/cif/schema/ccd-extras';
|
||||
import { ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
|
||||
|
||||
type CCB = Table<CCD_Schema['chem_comp_bond']>
|
||||
type CCA = Table<CCD_Schema['chem_comp_atom']>
|
||||
|
||||
function ccbKey(compId: string, atomId1: string, atomId2: string) {
|
||||
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`;
|
||||
}
|
||||
|
||||
function ccaKey(compId: string, atomId: string) {
|
||||
return `${compId}:${atomId}`;
|
||||
}
|
||||
|
||||
function addChemCompBondToSet(set: Set<string>, ccb: CCB) {
|
||||
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
|
||||
set.add(ccbKey(ccb.comp_id.value(i), ccb.atom_id_1.value(i), ccb.atom_id_2.value(i)));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
function addChemCompAtomToSet(set: Set<string>, cca: CCA) {
|
||||
for (let i = 0, il = cca._rowCount; i < il; ++i) {
|
||||
set.add(ccaKey(cca.comp_id.value(i), cca.atom_id.value(i)));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
function checkAddingBondsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
|
||||
const ccbSetByParent = DefaultMap<string, Set<string>>(() => new Set());
|
||||
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_bond } = pvcd[k];
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
|
||||
if (parentIds.length === 0) {
|
||||
const set = ccbSetByParent.getDefault(chem_comp.id.value(0));
|
||||
addChemCompBondToSet(set, chem_comp_bond);
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
const parentId = parentIds[i];
|
||||
const set = ccbSetByParent.getDefault(parentId);
|
||||
addChemCompBondToSet(set, chem_comp_bond);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_atom, chem_comp_bond } = pvcd[k];
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
|
||||
if (parentIds.length > 0) {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
const entryBonds = addChemCompBondToSet(new Set<string>(), chem_comp_bond);
|
||||
const entryAtoms = addChemCompAtomToSet(new Set<string>(), chem_comp_atom);
|
||||
const extraBonds = SetUtils.difference(ccbSetByParent.get(parentIds[i])!, entryBonds);
|
||||
extraBonds.forEach(bk => {
|
||||
const [a1, a2] = bk.split('|');
|
||||
if (entryAtoms.has(a1) && entryAtoms.has(a2)) {
|
||||
console.error(`Adding all PVCD bonds would wrongly add bond ${bk} for ${k}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkAddingAtomsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
|
||||
const ccaSetByParent = DefaultMap<string, Set<string>>(() => new Set());
|
||||
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_atom } = pvcd[k];
|
||||
if (chem_comp_atom._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
|
||||
if (parentIds.length === 0) {
|
||||
const set = ccaSetByParent.getDefault(chem_comp.id.value(0));
|
||||
addChemCompAtomToSet(set, chem_comp_atom);
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
const parentId = parentIds[i];
|
||||
const set = ccaSetByParent.getDefault(parentId);
|
||||
addChemCompAtomToSet(set, chem_comp_atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createBonds(
|
||||
ccd: DatabaseCollection<CCD_Schema>,
|
||||
pvcd: DatabaseCollection<CCD_Schema>,
|
||||
atomsRequested: boolean
|
||||
) {
|
||||
const ccbSet = new Set<string>();
|
||||
|
||||
const comp_id: string[] = [];
|
||||
const atom_id_1: string[] = [];
|
||||
const atom_id_2: 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) {
|
||||
for (let i = 0, il = ccb._rowCount; i < il; ++i) {
|
||||
const atomId1 = ccb.atom_id_1.value(i);
|
||||
const atomId2 = ccb.atom_id_2.value(i);
|
||||
const k = ccbKey(compId, atomId1, atomId2);
|
||||
if (!ccbSet.has(k)) {
|
||||
atom_id_1.push(atomId1);
|
||||
atom_id_2.push(atomId2);
|
||||
comp_id.push(compId);
|
||||
value_order.push(ccb.value_order.value(i));
|
||||
pdbx_aromatic_flag.push(ccb.pdbx_aromatic_flag.value(i));
|
||||
pdbx_stereo_config.push(ccb.pdbx_stereo_config.value(i));
|
||||
molstar_protonation_variant.push(protonationVariant ? 'Y' : 'N');
|
||||
ccbSet.add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check adding bonds from PVCD
|
||||
checkAddingBondsFromPVCD(pvcd);
|
||||
|
||||
// add bonds from PVCD
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_bond } = pvcd[k];
|
||||
if (chem_comp_bond._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
|
||||
if (parentIds.length === 0) {
|
||||
addBonds(chem_comp.id.value(0), chem_comp_bond, false);
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
addBonds(parentIds[i], chem_comp_bond, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add bonds from CCD
|
||||
for (const k in ccd) {
|
||||
const { chem_comp, chem_comp_bond } = ccd[k];
|
||||
if (chem_comp_bond._rowCount) {
|
||||
addBonds(chem_comp.id.value(0), chem_comp_bond, false);
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
CCB_TABLE_NAME,
|
||||
{ chem_comp_bond: mmCIF_chemCompBond_schema },
|
||||
{ chem_comp_bond: bondTable }
|
||||
);
|
||||
|
||||
return { bonds: bondDatabase, atoms: atomsRequested ? createAtoms(ccd, pvcd) : void 0 };
|
||||
}
|
||||
|
||||
function createAtoms(ccd: DatabaseCollection<CCD_Schema>, pvcd: DatabaseCollection<CCD_Schema>) {
|
||||
const ccaSet = new Set<string>();
|
||||
|
||||
const comp_id: string[] = [];
|
||||
const atom_id: string[] = [];
|
||||
const charge: number[] = [];
|
||||
const pdbx_stereo_config: typeof CCD_Schema.chem_comp_atom['pdbx_stereo_config']['T'][] = [];
|
||||
|
||||
function addAtoms(compId: string, cca: CCA) {
|
||||
for (let i = 0, il = cca._rowCount; i < il; ++i) {
|
||||
const atomId = cca.atom_id.value(i);
|
||||
const k = ccaKey(compId, atomId);
|
||||
if (!ccaSet.has(k)) {
|
||||
atom_id.push(atomId);
|
||||
comp_id.push(compId);
|
||||
charge.push(cca.charge.value(i));
|
||||
pdbx_stereo_config.push(cca.pdbx_stereo_config.value(i));
|
||||
ccaSet.add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check adding atoms from PVCD
|
||||
checkAddingAtomsFromPVCD(pvcd);
|
||||
|
||||
// add atoms from PVCD
|
||||
for (const k in pvcd) {
|
||||
const { chem_comp, chem_comp_atom } = pvcd[k];
|
||||
if (chem_comp_atom._rowCount) {
|
||||
const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
|
||||
if (parentIds.length === 0) {
|
||||
addAtoms(chem_comp.id.value(0), chem_comp_atom);
|
||||
} else {
|
||||
for (let i = 0, il = parentIds.length; i < il; ++i) {
|
||||
addAtoms(parentIds[i], chem_comp_atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add atoms from CCD
|
||||
for (const k in ccd) {
|
||||
const { chem_comp, chem_comp_atom } = ccd[k];
|
||||
if (chem_comp_atom._rowCount) {
|
||||
addAtoms(chem_comp.id.value(0), chem_comp_atom);
|
||||
}
|
||||
}
|
||||
|
||||
const atomTable = Table.ofArrays(ccd_chemCompAtom_schema, {
|
||||
comp_id, atom_id, charge, pdbx_stereo_config
|
||||
});
|
||||
|
||||
return Database.ofTables(
|
||||
CCA_TABLE_NAME,
|
||||
{ chem_comp_atom: ccd_chemCompAtom_schema },
|
||||
{ chem_comp_atom: atomTable }
|
||||
);
|
||||
}
|
||||
|
||||
async function run(out: string, binary = false, forceDownload = false, ccaOut?: string) {
|
||||
await ensureDataAvailable(forceDownload);
|
||||
const ccd = await readCCD();
|
||||
const pvcd = await readPVCD();
|
||||
|
||||
const { bonds, atoms } = await createBonds(ccd, pvcd, !!ccaOut);
|
||||
|
||||
const ccbCif = getEncodedCif(CCB_TABLE_NAME, bonds, binary);
|
||||
if (!fs.existsSync(path.dirname(out))) {
|
||||
fs.mkdirSync(path.dirname(out));
|
||||
}
|
||||
writeFile(out, ccbCif);
|
||||
|
||||
if (!!ccaOut) {
|
||||
const ccaCif = getEncodedCif(CCA_TABLE_NAME, atoms, binary);
|
||||
if (!fs.existsSync(path.dirname(ccaOut))) {
|
||||
fs.mkdirSync(path.dirname(ccaOut));
|
||||
}
|
||||
writeFile(ccaOut, ccaCif);
|
||||
}
|
||||
}
|
||||
|
||||
const CCB_TABLE_NAME = 'CHEM_COMP_BONDS';
|
||||
const CCA_TABLE_NAME = 'CHEM_COMP_ATOMS';
|
||||
|
||||
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.'
|
||||
});
|
||||
parser.addArgument('out', {
|
||||
help: 'Generated file output path.'
|
||||
});
|
||||
parser.addArgument([ '--forceDownload', '-f' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Force download of CCD and PVCD.'
|
||||
});
|
||||
parser.addArgument([ '--binary', '-b' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Output as BinaryCIF.'
|
||||
});
|
||||
parser.addArgument(['--ccaOut', '-a'], {
|
||||
help: 'Optional generated file output path for chem_comp_atom data.',
|
||||
required: false
|
||||
});
|
||||
interface Args {
|
||||
out: string,
|
||||
forceDownload?: boolean,
|
||||
binary?: boolean,
|
||||
ccaOut?: string
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
run(args.out, args.binary, args.forceDownload, args.ccaOut);
|
||||
75
src/cli/chem-comp-dict/util.ts
Normal file
75
src/cli/chem-comp-dict/util.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as util from 'util';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as zlib from 'zlib';
|
||||
import fetch from 'node-fetch';
|
||||
require('util.promisify').shim();
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
import { Progress } from '../../mol-task';
|
||||
import { Database } 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';
|
||||
|
||||
export async function ensureAvailable(path: string, url: string, forceDownload = false) {
|
||||
if (forceDownload || !fs.existsSync(path)) {
|
||||
console.log(`downloading ${url}...`);
|
||||
const data = await fetch(url);
|
||||
if (!fs.existsSync(DATA_DIR)) {
|
||||
fs.mkdirSync(DATA_DIR);
|
||||
}
|
||||
if (url.endsWith('.gz')) {
|
||||
await writeFile(path, zlib.gunzipSync(await data.buffer()));
|
||||
} else {
|
||||
await writeFile(path, await data.text());
|
||||
}
|
||||
console.log(`done downloading ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDataAvailable(forceDownload = false) {
|
||||
await ensureAvailable(CCD_PATH, CCD_URL, forceDownload);
|
||||
await ensureAvailable(PVCD_PATH, PVCD_URL, forceDownload);
|
||||
}
|
||||
|
||||
export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
|
||||
const parsed = await parseCif(await readFile(path, 'utf8'));
|
||||
return CIF.toDatabaseCollection(schema, parsed.result);
|
||||
}
|
||||
|
||||
export async function readCCD() {
|
||||
return readFileAsCollection(CCD_PATH, CCD_Schema);
|
||||
}
|
||||
|
||||
export async function readPVCD() {
|
||||
return readFileAsCollection(PVCD_PATH, CCD_Schema);
|
||||
}
|
||||
|
||||
async function parseCif(data: string | Uint8Array) {
|
||||
const comp = CIF.parse(data);
|
||||
console.time('parse cif');
|
||||
const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
|
||||
console.timeEnd('parse cif');
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function getEncodedCif(name: string, database: Database<Database.Schema>, binary = false) {
|
||||
const encoder = CifWriter.createEncoder({ binary, encoderName: 'mol*' });
|
||||
CifWriter.Encoder.writeDatabase(encoder, name, database);
|
||||
return encoder.getData();
|
||||
}
|
||||
|
||||
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';
|
||||
119
src/cli/cif2bcif/converter.ts
Normal file
119
src/cli/cif2bcif/converter.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
*/
|
||||
|
||||
import { CIF, CifCategory, getCifFieldType, CifField, CifFile } from '../../mol-io/reader/cif';
|
||||
import { CifWriter, EncodingStrategyHint } from '../../mol-io/writer/cif';
|
||||
import * as util from 'util';
|
||||
import * as fs from 'fs';
|
||||
import * as zlib from 'zlib';
|
||||
import { Progress, Task, RuntimeContext } from '../../mol-task';
|
||||
import { classifyFloatArray, classifyIntArray } from '../../mol-io/common/binary-cif';
|
||||
import { BinaryEncodingProvider } from '../../mol-io/writer/cif/encoder/binary';
|
||||
import { Category } from '../../mol-io/writer/cif/encoder';
|
||||
import { ReaderResult } from '../../mol-io/reader/result';
|
||||
|
||||
function showProgress(p: Progress) {
|
||||
process.stdout.write(`\r${new Array(80).join(' ')}`);
|
||||
process.stdout.write(`\r${Progress.format(p)}`);
|
||||
}
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
const unzipAsync = util.promisify<zlib.InputType, Buffer>(zlib.unzip);
|
||||
|
||||
async function readFile(ctx: RuntimeContext, filename: string): Promise<ReaderResult<CifFile>> {
|
||||
const isGz = /\.gz$/i.test(filename);
|
||||
if (filename.match(/\.bcif/)) {
|
||||
let input = await readFileAsync(filename);
|
||||
if (isGz) input = await unzipAsync(input);
|
||||
return await CIF.parseBinary(new Uint8Array(input)).runInContext(ctx);
|
||||
} else {
|
||||
let str: string;
|
||||
if (isGz) {
|
||||
const data = await unzipAsync(await readFileAsync(filename));
|
||||
str = data.toString('utf8');
|
||||
} else {
|
||||
str = await readFileAsync(filename, 'utf8');
|
||||
}
|
||||
return await CIF.parseText(str).runInContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function getCIF(ctx: RuntimeContext, filename: string) {
|
||||
const parsed = await readFile(ctx, filename);
|
||||
if (parsed.isError) {
|
||||
throw new Error(parsed.toString());
|
||||
}
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
|
||||
return {
|
||||
name: cat.name,
|
||||
instance: () => CifWriter.categoryInstance(fields, { data: cat, rowCount: cat.rowCount })
|
||||
};
|
||||
}
|
||||
|
||||
function classify(name: string, field: CifField): CifWriter.Field {
|
||||
const type = getCifFieldType(field);
|
||||
if (type['@type'] === 'str') {
|
||||
return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
|
||||
} else if (type['@type'] === 'float') {
|
||||
const encoder = classifyFloatArray(field.toFloatArray({ array: Float64Array }));
|
||||
return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, encoder, typedArray: Float64Array });
|
||||
} else {
|
||||
const encoder = classifyIntArray(field.toIntArray({ array: Int32Array }));
|
||||
return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, encoder, typedArray: Int32Array });
|
||||
}
|
||||
}
|
||||
|
||||
export function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
|
||||
return Task.create<Uint8Array>('BinaryCIF', async ctx => {
|
||||
const encodingProvider: BinaryEncodingProvider = hints
|
||||
? CifWriter.createEncodingProviderFromJsonConfig(hints)
|
||||
: { get: (c, f) => void 0 };
|
||||
const cif = await getCIF(ctx, path);
|
||||
|
||||
const encoder = CifWriter.createEncoder({
|
||||
binary: !asText,
|
||||
encoderName: 'mol*/ciftools cif2bcif',
|
||||
binaryAutoClassifyEncoding: true,
|
||||
binaryEncodingPovider: encodingProvider
|
||||
});
|
||||
|
||||
if (filter) {
|
||||
encoder.setFilter(Category.filterOf(filter));
|
||||
}
|
||||
|
||||
let maxProgress = 0;
|
||||
for (const b of cif.blocks) {
|
||||
maxProgress += b.categoryNames.length;
|
||||
for (const c of b.categoryNames) maxProgress += b.categories[c].fieldNames.length;
|
||||
}
|
||||
|
||||
let current = 0;
|
||||
for (const b of cif.blocks) {
|
||||
encoder.startDataBlock(b.header);
|
||||
for (const c of b.categoryNames) {
|
||||
const cat = b.categories[c];
|
||||
const fields: CifWriter.Field[] = [];
|
||||
for (const f of cat.fieldNames) {
|
||||
fields.push(classify(f, cat.getField(f)!));
|
||||
current++;
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
|
||||
}
|
||||
|
||||
encoder.writeCategory(getCategoryInstanceProvider(b.categories[c], fields));
|
||||
current++;
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
|
||||
}
|
||||
}
|
||||
await ctx.update('Exporting...');
|
||||
const ret = encoder.getData() as Uint8Array;
|
||||
await ctx.update('Done.\n');
|
||||
return ret;
|
||||
}).run(showProgress, 250);
|
||||
}
|
||||
68
src/cli/cif2bcif/index.ts
Normal file
68
src/cli/cif2bcif/index.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as util from 'util';
|
||||
import * as fs from 'fs';
|
||||
import * as zlib from 'zlib';
|
||||
import { convert } from './converter';
|
||||
|
||||
require('util.promisify').shim();
|
||||
|
||||
async function process(srcPath: string, outPath: string, configPath?: string, filterPath?: string) {
|
||||
const config = configPath ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : void 0;
|
||||
const filter = filterPath ? fs.readFileSync(filterPath, 'utf8') : void 0;
|
||||
|
||||
const res = await convert(srcPath, false, config, filter);
|
||||
await write(outPath, res);
|
||||
}
|
||||
|
||||
const zipAsync = util.promisify<zlib.InputType, Buffer>(zlib.gzip);
|
||||
|
||||
async function write(outPath: string, res: Uint8Array) {
|
||||
const isGz = /\.gz$/i.test(outPath);
|
||||
if (isGz) {
|
||||
res = await zipAsync(res);
|
||||
}
|
||||
fs.writeFileSync(outPath, res);
|
||||
}
|
||||
|
||||
function run(args: Args) {
|
||||
process(args.src, args.out, args.config, args.filter);
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Convert any CIF file to a BCIF file'
|
||||
});
|
||||
parser.addArgument([ 'src' ], {
|
||||
help: 'Source CIF path'
|
||||
});
|
||||
parser.addArgument([ 'out' ], {
|
||||
help: 'Output BCIF path'
|
||||
});
|
||||
parser.addArgument([ '-c', '--config' ], {
|
||||
help: 'Optional encoding strategy/precision config path',
|
||||
required: false
|
||||
});
|
||||
parser.addArgument([ '-f', '--filter' ], {
|
||||
help: 'Optional filter whitelist/blacklist path',
|
||||
required: false
|
||||
});
|
||||
|
||||
interface Args {
|
||||
src: string
|
||||
out: string
|
||||
config?: string
|
||||
filter?: string
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
if (args) {
|
||||
run(args);
|
||||
}
|
||||
275
src/cli/cifschema/index.ts
Normal file
275
src/cli/cifschema/index.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import { parseCsv } from '../../mol-io/reader/csv/parser';
|
||||
import { CifFrame, CifBlock } from '../../mol-io/reader/cif';
|
||||
import { parseCifText } from '../../mol-io/reader/cif/text/parser';
|
||||
import { generateSchema } from './util/cif-dic';
|
||||
import { generate } from './util/generate';
|
||||
import { Filter, Database } from './util/schema';
|
||||
import { parseImportGet } from './util/helper';
|
||||
|
||||
function getDicVersion(block: CifBlock) {
|
||||
return block.categories.dictionary.getField('version')!.str(0);
|
||||
}
|
||||
|
||||
function getDicNamespace(block: CifBlock) {
|
||||
return block.categories.dictionary.getField('namespace')!.str(0);
|
||||
}
|
||||
|
||||
async function runGenerateSchemaMmcif(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
|
||||
await ensureMmcifDicAvailable();
|
||||
const mmcifDic = await parseCifText(fs.readFileSync(MMCIF_DIC_PATH, 'utf8')).run();
|
||||
if (mmcifDic.isError) throw mmcifDic;
|
||||
|
||||
await ensureIhmDicAvailable();
|
||||
const ihmDic = await parseCifText(fs.readFileSync(IHM_DIC_PATH, 'utf8')).run();
|
||||
if (ihmDic.isError) throw ihmDic;
|
||||
|
||||
await ensureCarbBranchDicAvailable();
|
||||
const carbBranchDic = await parseCifText(fs.readFileSync(CARB_BRANCH_DIC_PATH, 'utf8')).run();
|
||||
if (carbBranchDic.isError) throw carbBranchDic;
|
||||
|
||||
await ensureCarbCompDicAvailable();
|
||||
const carbCompDic = await parseCifText(fs.readFileSync(CARB_COMP_DIC_PATH, 'utf8')).run();
|
||||
if (carbCompDic.isError) throw carbCompDic;
|
||||
|
||||
const mmcifDicVersion = getDicVersion(mmcifDic.result.blocks[0]);
|
||||
const ihmDicVersion = getDicVersion(ihmDic.result.blocks[0]);
|
||||
const carbDicVersion = 'draft';
|
||||
const version = `Dictionary versions: mmCIF ${mmcifDicVersion}, IHM ${ihmDicVersion}, CARB ${carbDicVersion}.`;
|
||||
|
||||
const frames: CifFrame[] = [...mmcifDic.result.blocks[0].saveFrames, ...ihmDic.result.blocks[0].saveFrames, ...carbBranchDic.result.blocks[0].saveFrames, ...carbCompDic.result.blocks[0].saveFrames];
|
||||
const schema = generateSchema(frames);
|
||||
|
||||
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
|
||||
}
|
||||
|
||||
async function runGenerateSchemaCifCore(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
|
||||
await ensureCifCoreDicAvailable();
|
||||
const cifCoreDic = await parseCifText(fs.readFileSync(CIF_CORE_DIC_PATH, 'utf8')).run();
|
||||
if (cifCoreDic.isError) throw cifCoreDic;
|
||||
|
||||
const cifCoreDicVersion = getDicVersion(cifCoreDic.result.blocks[0]);
|
||||
const version = `Dictionary versions: CifCore ${cifCoreDicVersion}.`;
|
||||
|
||||
const frames: CifFrame[] = [...cifCoreDic.result.blocks[0].saveFrames];
|
||||
const imports = await resolveImports(frames, DIC_DIR);
|
||||
const schema = generateSchema(frames, imports);
|
||||
|
||||
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
|
||||
}
|
||||
|
||||
async function resolveImports(frames: CifFrame[], baseDir: string): Promise<Map<string, CifFrame[]>> {
|
||||
const imports = new Map<string, CifFrame[]>();
|
||||
|
||||
for (const d of frames) {
|
||||
if ('import' in d.categories) {
|
||||
const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0));
|
||||
for (const g of importGet) {
|
||||
const { file } = g;
|
||||
if (!file) continue;
|
||||
if (imports.has(file)) continue;
|
||||
|
||||
const dic = await parseCifText(fs.readFileSync(path.join(baseDir, file), 'utf8')).run();
|
||||
if (dic.isError) throw dic;
|
||||
|
||||
imports.set(file, [...dic.result.blocks[0].saveFrames]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
async function runGenerateSchemaDic(name: string, dicPath: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
|
||||
const dic = await parseCifText(fs.readFileSync(dicPath, 'utf8')).run();
|
||||
if (dic.isError) throw dic;
|
||||
|
||||
const dicVersion = getDicVersion(dic.result.blocks[0]);
|
||||
const dicName = getDicNamespace(dic.result.blocks[0]);
|
||||
const version = `Dictionary versions: ${dicName} ${dicVersion}.`;
|
||||
|
||||
const frames: CifFrame[] = [...dic.result.blocks[0].saveFrames];
|
||||
const imports = await resolveImports(frames, path.dirname(dicPath));
|
||||
const schema = generateSchema(frames, imports);
|
||||
|
||||
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
|
||||
}
|
||||
|
||||
async function runGenerateSchema(name: string, version: string, schema: Database, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
|
||||
const filter = fieldNamesPath ? await getFieldNamesFilter(fieldNamesPath) : undefined;
|
||||
const output = typescript ? generate(name, version, schema, filter, moldbImportPath, addAliases) : JSON.stringify(schema, undefined, 4);
|
||||
|
||||
if (out) {
|
||||
fs.writeFileSync(out, output);
|
||||
} else {
|
||||
console.log(output);
|
||||
}
|
||||
}
|
||||
|
||||
async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
|
||||
const fieldNamesStr = fs.readFileSync(fieldNamesPath, 'utf8');
|
||||
const parsed = await parseCsv(fieldNamesStr, { noColumnNames: true }).run();
|
||||
if (parsed.isError) throw parser.error;
|
||||
const csvFile = parsed.result;
|
||||
|
||||
const fieldNamesCol = csvFile.table.getColumn('0');
|
||||
if (!fieldNamesCol) throw 'error getting fields columns';
|
||||
const fieldNames = fieldNamesCol.toStringArray();
|
||||
|
||||
const filter: Filter = {};
|
||||
fieldNames.forEach((name, i) => {
|
||||
const [ category, field ] = name.split('.');
|
||||
// console.log(category, field)
|
||||
if (!filter[ category ]) filter[ category ] = {};
|
||||
filter[ category ][ field ] = true;
|
||||
});
|
||||
return filter;
|
||||
}
|
||||
|
||||
async function ensureMmcifDicAvailable() { await ensureDicAvailable(MMCIF_DIC_PATH, MMCIF_DIC_URL); }
|
||||
async function ensureIhmDicAvailable() { await ensureDicAvailable(IHM_DIC_PATH, IHM_DIC_URL); }
|
||||
async function ensureCarbBranchDicAvailable() { await ensureDicAvailable(CARB_BRANCH_DIC_PATH, CARB_BRANCH_DIC_URL); }
|
||||
async function ensureCarbCompDicAvailable() { await ensureDicAvailable(CARB_COMP_DIC_PATH, CARB_COMP_DIC_URL); }
|
||||
async function ensureCifCoreDicAvailable() {
|
||||
await ensureDicAvailable(CIF_CORE_DIC_PATH, CIF_CORE_DIC_URL);
|
||||
await ensureDicAvailable(CIF_CORE_ENUM_PATH, CIF_CORE_ENUM_URL);
|
||||
await ensureDicAvailable(CIF_CORE_ATTR_PATH, CIF_CORE_ATTR_URL);
|
||||
}
|
||||
|
||||
async function ensureDicAvailable(dicPath: string, dicUrl: string) {
|
||||
if (FORCE_DIC_DOWNLOAD || !fs.existsSync(dicPath)) {
|
||||
const name = dicUrl.substr(dicUrl.lastIndexOf('/') + 1);
|
||||
console.log(`downloading ${name}...`);
|
||||
const data = await fetch(dicUrl);
|
||||
if (!fs.existsSync(DIC_DIR)) {
|
||||
fs.mkdirSync(DIC_DIR);
|
||||
}
|
||||
fs.writeFileSync(dicPath, await data.text());
|
||||
console.log(`done downloading ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const DIC_DIR = path.resolve(__dirname, '../../../../build/dics/');
|
||||
const MMCIF_DIC_PATH = `${DIC_DIR}/mmcif_pdbx_v50.dic`;
|
||||
const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic';
|
||||
const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`;
|
||||
const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic';
|
||||
const CARB_BRANCH_DIC_PATH = `${DIC_DIR}/entity_branch-extension.dic`;
|
||||
const CARB_BRANCH_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/entity_branch-extension.dic';
|
||||
const CARB_COMP_DIC_PATH = `${DIC_DIR}/chem_comp-extension.dic`;
|
||||
const CARB_COMP_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/chem_comp-extension.dic';
|
||||
|
||||
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
|
||||
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';
|
||||
const CIF_CORE_ENUM_PATH = `${DIC_DIR}/templ_enum.cif`;
|
||||
const CIF_CORE_ENUM_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_enum.cif';
|
||||
const CIF_CORE_ATTR_PATH = `${DIC_DIR}/templ_attr.cif`;
|
||||
const CIF_CORE_ATTR_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_attr.cif';
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Create schema from mmcif dictionary (v50 plus IHM and entity_branch extensions, downloaded from wwPDB)'
|
||||
});
|
||||
parser.addArgument([ '--preset', '-p' ], {
|
||||
defaultValue: '',
|
||||
choices: ['', 'mmCIF', 'CCD', 'BIRD', 'CifCore'],
|
||||
help: 'Preset name'
|
||||
});
|
||||
parser.addArgument([ '--name', '-n' ], {
|
||||
defaultValue: '',
|
||||
help: 'Schema name'
|
||||
});
|
||||
parser.addArgument([ '--out', '-o' ], {
|
||||
help: 'Generated schema output path, if not given printed to stdout'
|
||||
});
|
||||
parser.addArgument([ '--targetFormat', '-tf' ], {
|
||||
defaultValue: 'typescript-molstar',
|
||||
choices: ['typescript-molstar', 'json-internal'],
|
||||
help: 'Target format'
|
||||
});
|
||||
parser.addArgument([ '--dicPath', '-d' ], {
|
||||
defaultValue: '',
|
||||
help: 'Path to dictionary'
|
||||
});
|
||||
parser.addArgument([ '--fieldNamesPath', '-fn' ], {
|
||||
defaultValue: '',
|
||||
help: 'Field names to include'
|
||||
});
|
||||
parser.addArgument([ '--forceDicDownload', '-f' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Force download of dictionaries'
|
||||
});
|
||||
parser.addArgument([ '--moldataImportPath', '-mip' ], {
|
||||
defaultValue: 'molstar/lib/mol-data',
|
||||
help: 'mol-data import path (for typescript target only)'
|
||||
});
|
||||
parser.addArgument([ '--addAliases', '-aa' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Add field name/path aliases'
|
||||
});
|
||||
interface Args {
|
||||
name: string
|
||||
preset: '' | 'mmCIF' | 'CCD' | 'BIRD' | 'CifCore'
|
||||
forceDicDownload: boolean
|
||||
dic: '' | 'mmCIF' | 'CifCore'
|
||||
dicPath: string,
|
||||
fieldNamesPath: string
|
||||
targetFormat: 'typescript-molstar' | 'json-internal'
|
||||
out: string,
|
||||
moldataImportPath: string
|
||||
addAliases: boolean
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
const FORCE_DIC_DOWNLOAD = args.forceDicDownload;
|
||||
|
||||
switch (args.preset) {
|
||||
case 'mmCIF':
|
||||
args.name = 'mmCIF';
|
||||
args.dic = 'mmCIF';
|
||||
args.fieldNamesPath = path.resolve(__dirname, '../../../../data/cif-field-names/mmcif-field-names.csv');
|
||||
break;
|
||||
case 'CCD':
|
||||
args.name = 'CCD';
|
||||
args.dic = 'mmCIF';
|
||||
args.fieldNamesPath = path.resolve(__dirname, '../../../../data/cif-field-names/ccd-field-names.csv');
|
||||
break;
|
||||
case 'BIRD':
|
||||
args.name = 'BIRD';
|
||||
args.dic = 'mmCIF';
|
||||
args.fieldNamesPath = path.resolve(__dirname, '../../../../data/cif-field-names/bird-field-names.csv');
|
||||
break;
|
||||
case 'CifCore':
|
||||
args.name = 'CifCore';
|
||||
args.dic = 'CifCore';
|
||||
args.fieldNamesPath = path.resolve(__dirname, '../../../../data/cif-field-names/cif-core-field-names.csv');
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.name) {
|
||||
const typescript = args.targetFormat === 'typescript-molstar';
|
||||
if (args.dicPath) {
|
||||
runGenerateSchemaDic(args.name, args.dicPath, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
} else if (args.dic === 'mmCIF') {
|
||||
runGenerateSchemaMmcif(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
} else if (args.dic === 'CifCore') {
|
||||
runGenerateSchemaCifCore(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
475
src/cli/cifschema/util/cif-dic.ts
Normal file
475
src/cli/cifschema/util/cif-dic.ts
Normal file
@@ -0,0 +1,475 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Database, Column, EnumCol, StrCol, IntCol, ListCol, FloatCol, CoordCol, MatrixCol, VectorCol } from './schema';
|
||||
import { parseImportGet } from './helper';
|
||||
import * as Data from '../../../mol-io/reader/cif/data-model';
|
||||
import { CifFrame } from '../../../mol-io/reader/cif/data-model';
|
||||
|
||||
export function getFieldType(type: string, description: string, values?: string[], container?: string): Column {
|
||||
switch (type) {
|
||||
// mmCIF
|
||||
case 'code':
|
||||
case 'ucode':
|
||||
case 'line':
|
||||
case 'uline':
|
||||
case 'text':
|
||||
case 'char':
|
||||
case 'uchar3':
|
||||
case 'uchar1':
|
||||
case 'boolean':
|
||||
return values && values.length ? EnumCol(values, 'str', description) : StrCol(description);
|
||||
case 'aliasname':
|
||||
case 'name':
|
||||
case 'idname':
|
||||
case 'any':
|
||||
case 'atcode':
|
||||
case 'fax':
|
||||
case 'phone':
|
||||
case 'email':
|
||||
case 'code30':
|
||||
case 'seq-one-letter-code':
|
||||
case 'author':
|
||||
case 'orcid_id':
|
||||
case 'sequence_dep':
|
||||
case 'pdb_id':
|
||||
case 'emd_id':
|
||||
// todo, consider adding specialised fields
|
||||
case 'yyyy-mm-dd':
|
||||
case 'yyyy-mm-dd:hh:mm':
|
||||
case 'yyyy-mm-dd:hh:mm-flex':
|
||||
case 'int-range':
|
||||
case 'float-range':
|
||||
case 'binary':
|
||||
case 'operation_expression':
|
||||
case 'point_symmetry':
|
||||
case '4x3_matrix':
|
||||
case '3x4_matrices':
|
||||
case 'point_group':
|
||||
case 'point_group_helical':
|
||||
case 'symmetry_operation':
|
||||
case 'date_dep':
|
||||
case 'url':
|
||||
case 'symop':
|
||||
case 'exp_data_doi':
|
||||
case 'asym_id':
|
||||
return StrCol(description);
|
||||
case 'int':
|
||||
case 'non_negative_int':
|
||||
case 'positive_int':
|
||||
return values && values.length ? EnumCol(values, 'int', description) : IntCol(description);
|
||||
case 'float':
|
||||
return FloatCol(description);
|
||||
case 'ec-type':
|
||||
case 'ucode-alphanum-csv':
|
||||
case 'id_list':
|
||||
return ListCol('str', ',', description);
|
||||
case 'id_list_spc':
|
||||
return ListCol('str', ' ', description);
|
||||
|
||||
// cif
|
||||
case 'Text':
|
||||
case 'Code':
|
||||
case 'Complex':
|
||||
case 'Symop':
|
||||
case 'List':
|
||||
case 'List(Real,Real)':
|
||||
case 'List(Real,Real,Real,Real)':
|
||||
case 'Date':
|
||||
case 'Datetime':
|
||||
case 'Tag':
|
||||
case 'Implied':
|
||||
return wrapContainer('str', ',', description, container);
|
||||
case 'Real':
|
||||
return wrapContainer('float', ',', description, container);
|
||||
case 'Integer':
|
||||
return wrapContainer('int', ',', description, container);
|
||||
|
||||
}
|
||||
console.log(`unknown type '${type}'`);
|
||||
return StrCol(description);
|
||||
}
|
||||
|
||||
function ColFromType(type: 'int' | 'str' | 'float' | 'coord', description: string): Column {
|
||||
switch (type) {
|
||||
case 'int': return IntCol(description);
|
||||
case 'str': return StrCol(description);
|
||||
case 'float': return FloatCol(description);
|
||||
case 'coord': return CoordCol(description);
|
||||
}
|
||||
}
|
||||
|
||||
function wrapContainer(type: 'int' | 'str' | 'float' | 'coord', separator: string, description: string, container?: string) {
|
||||
return container && container === 'List' ? ListCol(type, separator, description) : ColFromType(type, description);
|
||||
}
|
||||
|
||||
type FrameCategories = { [category: string]: Data.CifFrame }
|
||||
type FrameLinks = { [k: string]: string }
|
||||
|
||||
interface FrameData {
|
||||
categories: FrameCategories
|
||||
links: FrameLinks
|
||||
}
|
||||
|
||||
type Imports = Map<string, CifFrame[]>
|
||||
|
||||
function getImportFrames(d: Data.CifFrame, imports: Imports) {
|
||||
const frames: Data.CifFrame[] = [];
|
||||
if (!('import' in d.categories)) return frames;
|
||||
|
||||
const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0));
|
||||
for (const g of importGet) {
|
||||
const { file, save } = g;
|
||||
if (!file || !save) {
|
||||
console.warn(`missing 'save' or 'file' for import in '${d.header}'`);
|
||||
continue;
|
||||
}
|
||||
const importFrames = imports.get(file);
|
||||
if (!importFrames) {
|
||||
console.warn(`missing '${file}' entry in imports`);
|
||||
continue;
|
||||
}
|
||||
const importSave = importFrames.find(id => id.header.toLowerCase() === save.toLowerCase());
|
||||
if (!importSave) {
|
||||
console.warn(`missing '${save}' save frame in '${file}'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
frames.push(importSave);
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** get field from given or linked category */
|
||||
function getField(category: string, field: string, d: Data.CifFrame, imports: Imports, ctx: FrameData): Data.CifField|undefined {
|
||||
const { categories, links } = ctx;
|
||||
const cat = d.categories[category];
|
||||
if (cat) {
|
||||
return cat.getField(field);
|
||||
} else if (d.header in links) {
|
||||
const linkName = links[d.header];
|
||||
if (linkName in categories) {
|
||||
return getField(category, field, categories[linkName], imports, ctx);
|
||||
} else {
|
||||
// console.log(`link '${linkName}' not found`)
|
||||
}
|
||||
} else {
|
||||
const importFrames = getImportFrames(d, imports);
|
||||
for (const idf of importFrames) {
|
||||
return getField(category, field, idf, imports, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEnums(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
|
||||
const value = getField('item_enumeration', 'value', d, imports, ctx);
|
||||
const enums: string[] = [];
|
||||
if (value) {
|
||||
for (let i = 0; i < value.rowCount; ++i) {
|
||||
enums.push(value.str(i));
|
||||
// console.log(value.str(i))
|
||||
}
|
||||
return enums;
|
||||
} else {
|
||||
// console.log(`item_enumeration.value not found for '${d.header}'`)
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
|
||||
const value = getField('type', 'container', d, imports, ctx);
|
||||
return value ? value.str(0) : undefined;
|
||||
}
|
||||
|
||||
function getCode(d: Data.CifFrame, imports: Imports, ctx: FrameData): [string, string[] | undefined, string | undefined ] | undefined {
|
||||
const code = getField('item_type', 'code', d, imports, ctx) || getField('type', 'contents', d, imports, ctx);
|
||||
if (code) {
|
||||
return [ code.str(0), getEnums(d, imports, ctx), getContainer(d, imports, ctx) ];
|
||||
} else {
|
||||
console.log(`item_type.code or type.contents not found for '${d.header}'`);
|
||||
}
|
||||
}
|
||||
|
||||
function getSubCategory(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
|
||||
const value = getField('item_sub_category', 'id', d, imports, ctx);
|
||||
if (value) {
|
||||
return value.str(0);
|
||||
}
|
||||
}
|
||||
|
||||
function getDescription(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
|
||||
const value = getField('item_description', 'description', d, imports, ctx) || getField('description', 'text', d, imports, ctx);
|
||||
if (value) {
|
||||
// trim (after newlines) and remove references to square brackets
|
||||
return value.str(0).trim()
|
||||
.replace(/(\r\n|\r|\n)([ \t]+)/g, '\n')
|
||||
.replace(/(\[[1-3]\])+ element/, 'elements')
|
||||
.replace(/(\[[1-3]\])+/, '');
|
||||
}
|
||||
}
|
||||
|
||||
function getAliases(d: Data.CifFrame, imports: Imports, ctx: FrameData): string[] | undefined {
|
||||
const value = getField('item_aliases', 'alias_name', d, imports, ctx) || getField('alias', 'definition_id', d, imports, ctx);
|
||||
return value ? value.toStringArray().map(v => v.substr(1)) : undefined;
|
||||
}
|
||||
|
||||
const reMatrixField = /\[[1-3]\]\[[1-3]\]/;
|
||||
const reVectorField = /\[[1-3]\]/;
|
||||
|
||||
const FORCE_INT_FIELDS = [
|
||||
'_atom_site.id',
|
||||
'_atom_site.auth_seq_id',
|
||||
'_atom_site_anisotrop.id',
|
||||
'_pdbx_struct_mod_residue.auth_seq_id',
|
||||
'_struct_conf.beg_auth_seq_id',
|
||||
'_struct_conf.end_auth_seq_id',
|
||||
'_struct_conn.ptnr1_auth_seq_id',
|
||||
'_struct_conn.ptnr2_auth_seq_id',
|
||||
'_struct_sheet_range.beg_auth_seq_id',
|
||||
'_struct_sheet_range.end_auth_seq_id',
|
||||
];
|
||||
|
||||
const FORCE_MATRIX_FIELDS_MAP: { [k: string]: string } = {
|
||||
'atom_site_aniso.U_11': 'U',
|
||||
'atom_site_aniso.U_22': 'U',
|
||||
'atom_site_aniso.U_33': 'U',
|
||||
'atom_site_aniso.U_23': 'U',
|
||||
'atom_site_aniso.U_13': 'U',
|
||||
'atom_site_aniso.U_12': 'U',
|
||||
'atom_site_aniso.U_11_su': 'U_su',
|
||||
'atom_site_aniso.U_22_su': 'U_su',
|
||||
'atom_site_aniso.U_33_su': 'U_su',
|
||||
'atom_site_aniso.U_23_su': 'U_su',
|
||||
'atom_site_aniso.U_13_su': 'U_su',
|
||||
'atom_site_aniso.U_12_su': 'U_su',
|
||||
};
|
||||
const FORCE_MATRIX_FIELDS = Object.keys(FORCE_MATRIX_FIELDS_MAP);
|
||||
|
||||
const EXTRA_ALIASES: Database['aliases'] = {
|
||||
'atom_site_aniso.U': [
|
||||
'atom_site_anisotrop_U'
|
||||
],
|
||||
'atom_site_aniso.U_su': [
|
||||
'atom_site_aniso_U_esd',
|
||||
'atom_site_anisotrop_U_esd',
|
||||
],
|
||||
};
|
||||
|
||||
const COMMA_SEPARATED_LIST_FIELDS = [
|
||||
'_atom_site.pdbx_struct_group_id',
|
||||
'_chem_comp.mon_nstd_parent_comp_id',
|
||||
'_diffrn_radiation.pdbx_wavelength_list',
|
||||
'_diffrn_source.pdbx_wavelength_list',
|
||||
'_em_diffraction.tilt_angle_list', // 20,40,50,55
|
||||
'_em_entity_assembly.entity_id_list',
|
||||
'_entity.pdbx_description', // Endolysin,Beta-2 adrenergic receptor
|
||||
'_entity.pdbx_ec',
|
||||
'_entity_poly.pdbx_strand_id', // A,B
|
||||
'_entity_src_gen.pdbx_gene_src_gene', // ADRB2, ADRB2R, B2AR
|
||||
'_pdbx_depui_entry_details.experimental_methods',
|
||||
'_pdbx_depui_entry_details.requested_accession_types',
|
||||
'_pdbx_soln_scatter_model.software_list', // INSIGHT II, HOMOLOGY, DISCOVERY, BIOPOLYMER, DELPHI
|
||||
'_pdbx_soln_scatter_model.software_author_list', // MSI
|
||||
'_pdbx_soln_scatter_model.entry_fitting_list', // Odd example: 'PDB CODE 1HFI, 1HCC, 1HFH, 1VCC'
|
||||
'_pdbx_struct_assembly_gen.entity_inst_id',
|
||||
'_pdbx_struct_assembly_gen.asym_id_list',
|
||||
'_pdbx_struct_assembly_gen.auth_asym_id_list',
|
||||
'_pdbx_struct_assembly_gen_depositor_info.asym_id_list',
|
||||
'_pdbx_struct_assembly_gen_depositor_info.chain_id_list',
|
||||
'_pdbx_struct_group_list.group_enumeration_type',
|
||||
'_reflns.pdbx_diffrn_id',
|
||||
'_refine.pdbx_diffrn_id',
|
||||
'_reflns_shell.pdbx_diffrn_id',
|
||||
'_struct_keywords.text',
|
||||
];
|
||||
|
||||
const SPACE_SEPARATED_LIST_FIELDS = [
|
||||
'_chem_comp.pdbx_subcomponent_list', // TSM DPH HIS CHF EMR
|
||||
'_pdbx_soln_scatter.data_reduction_software_list', // OTOKO
|
||||
'_pdbx_soln_scatter.data_analysis_software_list', // SCTPL5 GNOM
|
||||
];
|
||||
|
||||
const SEMICOLON_SEPARATED_LIST_FIELDS = [
|
||||
'_chem_comp.pdbx_synonyms' // GLYCERIN; PROPANE-1,2,3-TRIOL
|
||||
];
|
||||
|
||||
/**
|
||||
* Useful when a dictionary extension will add enum values to an existing dictionary.
|
||||
* By adding them here, the dictionary extension can be tested before the added enum
|
||||
* values are available in the existing dictionary.
|
||||
*/
|
||||
const EXTRA_ENUM_VALUES: { [k: string]: string[] } = {
|
||||
|
||||
};
|
||||
|
||||
export function generateSchema(frames: CifFrame[], imports: Imports = new Map()): Database {
|
||||
const tables: Database['tables'] = {};
|
||||
const aliases: Database['aliases'] = { ...EXTRA_ALIASES };
|
||||
|
||||
const categories: FrameCategories = {};
|
||||
const links: FrameLinks = {};
|
||||
const ctx = { categories, links };
|
||||
|
||||
// get category metadata
|
||||
frames.forEach(d => {
|
||||
// category definitions in mmCIF start with '_' and don't include a '.'
|
||||
// category definitions in cifCore don't include a '.'
|
||||
if (d.header[0] === '_' || d.header.includes('.')) return;
|
||||
const categoryName = d.header.toLowerCase();
|
||||
// console.log(d.header, d.categoryNames, d.categories)
|
||||
let descriptionField: Data.CifField | undefined;
|
||||
const categoryKeyNames = new Set<string>();
|
||||
|
||||
if ('category' in d.categories && 'category_key' in d.categories) {
|
||||
const category = d.categories['category'];
|
||||
const categoryKey = d.categories['category_key'];
|
||||
if (categoryKey) {
|
||||
const categoryKey_names = categoryKey.getField('name');
|
||||
if (categoryKey_names) {
|
||||
for (let i = 0, il = categoryKey_names.rowCount; i < il; ++i) {
|
||||
categoryKeyNames.add(categoryKey_names.str(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
descriptionField = category.getField('description');
|
||||
|
||||
if (categoryKeyNames.size === 0) {
|
||||
console.log(`no key given for category '${categoryName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if ('description' in d.categories) {
|
||||
descriptionField = d.categories['description'].getField('text');
|
||||
}
|
||||
|
||||
let description = '';
|
||||
if (descriptionField) {
|
||||
description = descriptionField.str(0).trim()
|
||||
.replace(/(\r\n|\r|\n)([ \t]+)/g, '\n'); // remove padding after newlines
|
||||
} else {
|
||||
console.log(`no description given for category '${categoryName}'`);
|
||||
}
|
||||
|
||||
tables[categoryName] = { description, key: categoryKeyNames, columns: {} };
|
||||
|
||||
// console.log('++++++++++++++++++++++++++++++++++++++++++')
|
||||
// console.log('name', categoryName)
|
||||
// console.log('desc', description)
|
||||
// console.log('key', categoryKeyNames)
|
||||
});
|
||||
|
||||
// build list of links between categories
|
||||
frames.forEach(d => {
|
||||
if (d.header[0] !== '_' && !d.header.includes('.')) return;
|
||||
categories[d.header] = d;
|
||||
const item_linked = d.categories['item_linked'];
|
||||
if (item_linked) {
|
||||
const child_name = item_linked.getField('child_name');
|
||||
const parent_name = item_linked.getField('parent_name');
|
||||
if (child_name && parent_name) {
|
||||
for (let i = 0; i < item_linked.rowCount; ++i) {
|
||||
const childName = child_name.str(i);
|
||||
const parentName = parent_name.str(i);
|
||||
if (childName in links && links[childName] !== parentName) {
|
||||
console.log(`${childName} linked to ${links[childName]}, ignoring link to ${parentName}`);
|
||||
}
|
||||
links[childName] = parentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// get field data
|
||||
Object.keys(categories).forEach(fullName => {
|
||||
const d = categories[fullName];
|
||||
if (!d) {
|
||||
console.log(`'${fullName}' not found, moving on`);
|
||||
return;
|
||||
}
|
||||
|
||||
const categoryName = d.header.substring(d.header[0] === '_' ? 1 : 0, d.header.indexOf('.'));
|
||||
const itemName = d.header.substring(d.header.indexOf('.') + 1);
|
||||
let fields: { [k: string]: Column };
|
||||
if (categoryName in tables) {
|
||||
fields = tables[categoryName].columns;
|
||||
tables[categoryName].key.add(itemName);
|
||||
} else if (categoryName.toLowerCase() in tables) {
|
||||
// take case from category name in 'field' data as it is better if data is from cif dictionaries
|
||||
tables[categoryName] = tables[categoryName.toLowerCase()];
|
||||
fields = tables[categoryName].columns;
|
||||
} else {
|
||||
console.log(`category '${categoryName}' has no metadata`);
|
||||
fields = {};
|
||||
tables[categoryName] = {
|
||||
description: '',
|
||||
key: new Set(),
|
||||
columns: fields
|
||||
};
|
||||
}
|
||||
|
||||
const itemAliases = getAliases(d, imports, ctx);
|
||||
if (itemAliases) aliases[`${categoryName}.${itemName}`] = itemAliases;
|
||||
|
||||
const description = getDescription(d, imports, ctx) || '';
|
||||
|
||||
// need to use regex to check for matrix or vector items
|
||||
// as sub_category assignment is missing for some entries
|
||||
const subCategory = getSubCategory(d, imports, ctx);
|
||||
if (subCategory === 'cartesian_coordinate' || subCategory === 'fractional_coordinate') {
|
||||
fields[itemName] = CoordCol(description);
|
||||
} else if (FORCE_INT_FIELDS.includes(d.header)) {
|
||||
fields[itemName] = IntCol(description);
|
||||
console.log(`forcing int: ${d.header}`);
|
||||
} else if (FORCE_MATRIX_FIELDS.includes(d.header)) {
|
||||
fields[itemName] = FloatCol(description);
|
||||
fields[FORCE_MATRIX_FIELDS_MAP[d.header]] = MatrixCol(3, 3, description);
|
||||
console.log(`forcing matrix: ${d.header}`);
|
||||
} else if (subCategory === 'matrix') {
|
||||
fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description);
|
||||
} else if (subCategory === 'vector') {
|
||||
fields[itemName.replace(reVectorField, '')] = VectorCol(3, description);
|
||||
} else {
|
||||
if (itemName.match(reMatrixField)) {
|
||||
fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description);
|
||||
console.log(`${d.header} should have 'matrix' _item_sub_category.id`);
|
||||
} else if (itemName.match(reVectorField)) {
|
||||
fields[itemName.replace(reVectorField, '')] = VectorCol(3, description);
|
||||
console.log(`${d.header} should have 'vector' _item_sub_category.id`);
|
||||
} else {
|
||||
const code = getCode(d, imports, ctx);
|
||||
if (code) {
|
||||
let fieldType = getFieldType(code[0], description, code[1], code[2]);
|
||||
if (fieldType.type === 'str') {
|
||||
if (COMMA_SEPARATED_LIST_FIELDS.includes(d.header)) {
|
||||
fieldType = ListCol('str', ',', description);
|
||||
console.log(`forcing comma separated: ${d.header}`);
|
||||
} else if (SPACE_SEPARATED_LIST_FIELDS.includes(d.header)) {
|
||||
fieldType = ListCol('str', ' ', description);
|
||||
console.log(`forcing space separated: ${d.header}`);
|
||||
} else if (SEMICOLON_SEPARATED_LIST_FIELDS.includes(d.header)) {
|
||||
fieldType = ListCol('str', ';', description);
|
||||
console.log(`forcing space separated: ${d.header}`);
|
||||
}
|
||||
}
|
||||
if (d.header in EXTRA_ENUM_VALUES) {
|
||||
if (fieldType.type === 'enum') {
|
||||
fieldType.values.push(...EXTRA_ENUM_VALUES[d.header]);
|
||||
} else {
|
||||
console.warn(`expected enum: ${d.header}`);
|
||||
}
|
||||
}
|
||||
fields[itemName] = fieldType;
|
||||
} else {
|
||||
fields[itemName] = StrCol(description);
|
||||
// console.log(`could not determine code for '${d.header}'`)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { tables, aliases };
|
||||
}
|
||||
151
src/cli/cifschema/util/generate.ts
Normal file
151
src/cli/cifschema/util/generate.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Database, Filter, Column } from './schema';
|
||||
import { indentString } from '../../../mol-util/string';
|
||||
import { FieldPath } from '../../../mol-io/reader/cif/schema';
|
||||
|
||||
function header (name: string, info: string, moldataImportPath: string) {
|
||||
return `/**
|
||||
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated '${name}' schema file. ${info}
|
||||
*
|
||||
* @author molstar/ciftools package
|
||||
*/
|
||||
|
||||
import { Database, Column } from '${moldataImportPath}/db';
|
||||
|
||||
import Schema = Column.Schema;`;
|
||||
}
|
||||
|
||||
function footer (name: string) {
|
||||
return `
|
||||
export type ${name}_Schema = typeof ${name}_Schema;
|
||||
export interface ${name}_Database extends Database<${name}_Schema> {};`;
|
||||
}
|
||||
|
||||
function getTypeShorthands(schema: Database, fields?: Filter) {
|
||||
const types = new Set<string>();
|
||||
Object.keys(schema.tables).forEach(table => {
|
||||
if (fields && !fields[table]) return;
|
||||
const { columns } = schema.tables[table];
|
||||
Object.keys(columns).forEach(columnName => {
|
||||
if (fields && !fields[table][columnName]) return;
|
||||
types.add(schema.tables[table].columns[columnName].type);
|
||||
});
|
||||
});
|
||||
const shorthands: string[] = [];
|
||||
types.forEach(type => {
|
||||
switch (type) {
|
||||
case 'str': shorthands.push('const str = Schema.str;'); break;
|
||||
case 'int': shorthands.push('const int = Schema.int;'); break;
|
||||
case 'float': shorthands.push('const float = Schema.float;'); break;
|
||||
case 'coord': shorthands.push('const coord = Schema.coord;'); break;
|
||||
case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break;
|
||||
case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break;
|
||||
case 'vector': shorthands.push('const Vector = Schema.Vector;'); break;
|
||||
case 'list': shorthands.push('const List = Schema.List;'); break;
|
||||
}
|
||||
});
|
||||
return shorthands.join('\n');
|
||||
}
|
||||
|
||||
function getTypeDef(c: Column): string {
|
||||
switch (c.type) {
|
||||
case 'str': return 'str';
|
||||
case 'int': return 'int';
|
||||
case 'float': return 'float';
|
||||
case 'coord': return 'coord';
|
||||
case 'enum':
|
||||
return `Aliased<'${c.values.map(v => v.replace(/'/g, '\\\'')).join(`' | '`)}'>(${c.subType})`;
|
||||
case 'matrix':
|
||||
return `Matrix(${c.rows}, ${c.columns})`;
|
||||
case 'vector':
|
||||
return `Vector(${c.length})`;
|
||||
case 'list':
|
||||
if (c.subType === 'int') {
|
||||
return `List('${c.separator}', x => parseInt(x, 10))`;
|
||||
} else if (c.subType === 'float' || c.subType === 'coord') {
|
||||
return `List('${c.separator}', x => parseFloat(x))`;
|
||||
} else {
|
||||
return `List('${c.separator}', x => x)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
||||
function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'`; }
|
||||
|
||||
function doc(description: string, spacesCount: number) {
|
||||
const spaces = ' '.repeat(spacesCount);
|
||||
return [
|
||||
`${spaces}/**`,
|
||||
`${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'),
|
||||
`${spaces} */`
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export function generate (name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
|
||||
const codeLines: string[] = [];
|
||||
|
||||
if (fields) {
|
||||
Object.keys(fields).forEach(table => {
|
||||
if (table in schema.tables) {
|
||||
const schemaTable = schema.tables[table];
|
||||
Object.keys(fields[table]).forEach(column => {
|
||||
if (!(column in schemaTable.columns)) {
|
||||
console.log(`filter field '${table}.${column}' not found in schema`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(`filter category '${table}' not found in schema`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
codeLines.push(`export const ${name}_Schema = {`);
|
||||
Object.keys(schema.tables).forEach(table => {
|
||||
if (fields && !fields[table]) return;
|
||||
const { description, columns } = schema.tables[table];
|
||||
if (description) codeLines.push(doc(description, 4));
|
||||
codeLines.push(` ${safePropertyString(table)}: {`);
|
||||
Object.keys(columns).forEach(columnName => {
|
||||
if (fields && !fields[table][columnName]) return;
|
||||
const c = columns[columnName];
|
||||
const typeDef = getTypeDef(c);
|
||||
if (c.description) codeLines.push(doc(c.description, 8));
|
||||
codeLines.push(` ${safePropertyString(columnName)}: ${typeDef},`);
|
||||
});
|
||||
codeLines.push(' },');
|
||||
});
|
||||
codeLines.push('};');
|
||||
|
||||
if (addAliases) {
|
||||
codeLines.push('');
|
||||
codeLines.push(`export const ${name}_Aliases = {`);
|
||||
Object.keys(schema.aliases).forEach(path => {
|
||||
const [ table, columnName ] = path.split('.');
|
||||
if (fields && !fields[table]) return;
|
||||
if (fields && !fields[table][columnName]) return;
|
||||
|
||||
const filteredAliases = new Set<string>();
|
||||
schema.aliases[path].forEach(p => {
|
||||
if (!FieldPath.equal(p, path)) filteredAliases.add(FieldPath.canonical(p));
|
||||
});
|
||||
|
||||
if (filteredAliases.size === 0) return;
|
||||
codeLines.push(` ${safePropertyString(path)}: [`);
|
||||
filteredAliases.forEach(alias => {
|
||||
codeLines.push(` '${alias}',`);
|
||||
});
|
||||
codeLines.push(' ],');
|
||||
});
|
||||
codeLines.push('};');
|
||||
}
|
||||
|
||||
return `${header(name, info, moldataImportPath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}`;
|
||||
}
|
||||
20
src/cli/cifschema/util/helper.ts
Normal file
20
src/cli/cifschema/util/helper.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
export type Import = { save?: string, file?: string }
|
||||
|
||||
export function parseImportGet(s: string): Import[] {
|
||||
// [{'save':hi_ang_Fox_coeffs 'file':templ_attr.cif} {'save':hi_ang_Fox_c0 'file':templ_enum.cif}]
|
||||
// [{"file":'templ_enum.cif' "save":'H_M_ref'}]
|
||||
return s.trim().substring(2, s.length - 2).split(/}[ \n\t]*{/g).map(s => {
|
||||
const save = s.match(/('save'|"save"):([^ \t\n]+)/);
|
||||
const file = s.match(/('file'|"file"):([^ \t\n]+)/);
|
||||
return {
|
||||
save: save ? save[0].substr(7).replace(/['"]/g, '') : undefined,
|
||||
file: file ? file[0].substr(7).replace(/['"]/g, '') : undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
77
src/cli/cifschema/util/schema.ts
Normal file
77
src/cli/cifschema/util/schema.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
export interface Database {
|
||||
tables: { [ tableName: string ]: Table }
|
||||
aliases: { [ path: string ]: string[] }
|
||||
}
|
||||
export interface Table {
|
||||
description: string
|
||||
key: Set<string>
|
||||
columns: { [ columnName: string ]: Column }
|
||||
}
|
||||
export type Column = IntCol | StrCol | FloatCol | CoordCol | EnumCol | VectorCol | MatrixCol | ListCol
|
||||
|
||||
type BaseCol = { description: string }
|
||||
|
||||
export type IntCol = { type: 'int' } & BaseCol
|
||||
export function IntCol(description: string): IntCol { return { type: 'int', description }; }
|
||||
|
||||
export type StrCol = { type: 'str' } & BaseCol
|
||||
export function StrCol(description: string): StrCol { return { type: 'str', description }; }
|
||||
|
||||
export type FloatCol = { type: 'float' } & BaseCol
|
||||
export function FloatCol(description: string): FloatCol { return { type: 'float', description }; }
|
||||
|
||||
export type CoordCol = { type: 'coord' } & BaseCol
|
||||
export function CoordCol(description: string): CoordCol { return { type: 'coord', description }; }
|
||||
|
||||
export type EnumCol = { type: 'enum', subType: 'int' | 'str', values: string[] } & BaseCol
|
||||
export function EnumCol(values: string[], subType: 'int' | 'str', description: string): EnumCol {
|
||||
return { type: 'enum', description, values, subType };
|
||||
}
|
||||
|
||||
export type VectorCol = { type: 'vector', length: number } & BaseCol
|
||||
export function VectorCol(length: number, description: string): VectorCol {
|
||||
return { type: 'vector', description, length };
|
||||
}
|
||||
|
||||
export type MatrixCol = { type: 'matrix', rows: number, columns: number } & BaseCol
|
||||
export function MatrixCol(columns: number, rows: number, description: string): MatrixCol {
|
||||
return { type: 'matrix', description, columns, rows };
|
||||
}
|
||||
|
||||
export type ListCol = { type: 'list', subType: 'int' | 'str' | 'float' | 'coord', separator: string } & BaseCol
|
||||
export function ListCol(subType: 'int' | 'str' | 'float' | 'coord', separator: string, description: string): ListCol {
|
||||
return { type: 'list', description, separator, subType };
|
||||
}
|
||||
|
||||
export type Filter = { [ table: string ]: { [ column: string ]: true } }
|
||||
|
||||
export function mergeFilters (...filters: Filter[]) {
|
||||
const n = filters.length;
|
||||
const mergedFilter: Filter = {};
|
||||
const fields: Map<string, number> = new Map();
|
||||
filters.forEach(filter => {
|
||||
Object.keys(filter).forEach(category => {
|
||||
Object.keys(filter[ category ]).forEach(field => {
|
||||
const key = `${category}.${field}`;
|
||||
const value = fields.get(key) || 0;
|
||||
fields.set(key, value + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
fields.forEach((v, k) => {
|
||||
if (v !== n) return;
|
||||
const [categoryName, fieldName] = k.split('.');
|
||||
if (categoryName in mergedFilter) {
|
||||
mergedFilter[categoryName][fieldName] = true;
|
||||
} else {
|
||||
mergedFilter[categoryName] = { fieldName: true };
|
||||
}
|
||||
});
|
||||
return mergedFilter;
|
||||
}
|
||||
93
src/cli/lipid-params/index.ts
Normal file
93
src/cli/lipid-params/index.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import { UniqueArray } from '../../mol-data/generic';
|
||||
|
||||
const LIPIDS_DIR = path.resolve(__dirname, '../../../../build/lipids/');
|
||||
|
||||
const MARTINI_LIPIDS_PATH = path.resolve(LIPIDS_DIR, 'martini_lipids.itp');
|
||||
const MARTINI_LIPIDS_URL = 'http://www.cgmartini.nl/images/parameters/lipids/Collections/martini_v2.0_lipids_all_201506.itp';
|
||||
|
||||
async function ensureAvailable(path: string, url: string) {
|
||||
if (FORCE_DOWNLOAD || !fs.existsSync(path)) {
|
||||
const name = url.substr(url.lastIndexOf('/') + 1);
|
||||
console.log(`downloading ${name}...`);
|
||||
const data = await fetch(url);
|
||||
if (!fs.existsSync(LIPIDS_DIR)) {
|
||||
fs.mkdirSync(LIPIDS_DIR);
|
||||
}
|
||||
fs.writeFileSync(path, await data.text());
|
||||
console.log(`done downloading ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureLipidsAvailable() { await ensureAvailable(MARTINI_LIPIDS_PATH, MARTINI_LIPIDS_URL); }
|
||||
|
||||
const extraLipids = ['DMPC'];
|
||||
|
||||
async function run(out: string) {
|
||||
await ensureLipidsAvailable();
|
||||
const lipidsItpStr = fs.readFileSync(MARTINI_LIPIDS_PATH, 'utf8');
|
||||
|
||||
const lipids = UniqueArray.create<string>();
|
||||
const reLipid = /\[moleculetype\]\n; molname nrexcl\n +([a-zA-Z]{3,5})/g;
|
||||
let m: RegExpExecArray | null;
|
||||
|
||||
while ((m = reLipid.exec(lipidsItpStr)) !== null) {
|
||||
const v = m[0].substr(m[0].lastIndexOf(' ') + 1);
|
||||
UniqueArray.add(lipids, v, v);
|
||||
}
|
||||
|
||||
for (const v of extraLipids) {
|
||||
UniqueArray.add(lipids, v, v);
|
||||
}
|
||||
|
||||
const lipidNames = JSON.stringify(lipids.array);
|
||||
|
||||
if (out) {
|
||||
const output = `/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Code-generated lipid params file. Names extracted from Martini FF lipids itp.
|
||||
*
|
||||
* @author molstar/lipid-params cli
|
||||
*/
|
||||
|
||||
export const LipidNames = new Set(${lipidNames.replace(/"/g, "'").replace(/,/g, ', ')});
|
||||
`;
|
||||
fs.writeFileSync(out, output);
|
||||
} else {
|
||||
console.log(lipidNames);
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Create lipid params (from martini lipids itp)'
|
||||
});
|
||||
parser.addArgument([ '--out', '-o' ], {
|
||||
help: 'Generated lipid params output path, if not given printed to stdout'
|
||||
});
|
||||
parser.addArgument([ '--forceDownload', '-f' ], {
|
||||
action: 'storeTrue',
|
||||
help: 'Force download of martini lipids itp'
|
||||
});
|
||||
interface Args {
|
||||
out: string
|
||||
forceDownload: boolean
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
const FORCE_DOWNLOAD = args.forceDownload;
|
||||
|
||||
run(args.out || '').catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @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';
|
||||
@@ -13,7 +14,7 @@ import { PluginContext } from '../../mol-plugin/context';
|
||||
import { ParamDefinition } from '../../mol-util/param-definition';
|
||||
|
||||
// force the transform to be evaluated
|
||||
_.StateTransforms.Data.Download.id
|
||||
_.StateTransforms.Data.Download.id;
|
||||
|
||||
// Empty plugin context
|
||||
const ctx = new PluginContext({
|
||||
@@ -32,7 +33,7 @@ function writeTransformer(t: StateTransformer) {
|
||||
StringBuilder.write(builder, `## <a name="${t.id.replace('.', '-')}"></a>${t.id} :: ${typeToString(t.definition.from)} -> ${typeToString(t.definition.to)}`);
|
||||
StringBuilder.newline(builder);
|
||||
if (t.definition.display.description) {
|
||||
StringBuilder.write(builder, `*${t.definition.display.description}*`)
|
||||
StringBuilder.write(builder, `*${t.definition.display.description}*`);
|
||||
StringBuilder.newline(builder);
|
||||
}
|
||||
StringBuilder.newline(builder);
|
||||
@@ -48,7 +49,7 @@ function writeTransformer(t: StateTransformer) {
|
||||
StringBuilder.write(builder, `\`\`\`js\n${JSON.stringify(ParamDefinition.getDefaultValues(params), null, 2)}\n\`\`\``);
|
||||
StringBuilder.newline(builder);
|
||||
}
|
||||
StringBuilder.write(builder, '----------------------------')
|
||||
StringBuilder.write(builder, '----------------------------');
|
||||
StringBuilder.newline(builder);
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ transformers.forEach(t => {
|
||||
StringBuilder.newline(builder);
|
||||
});
|
||||
StringBuilder.newline(builder);
|
||||
StringBuilder.write(builder, '----------------------------')
|
||||
StringBuilder.write(builder, '----------------------------');
|
||||
StringBuilder.newline(builder);
|
||||
transformers.forEach(t => writeTransformer(t));
|
||||
|
||||
@@ -21,9 +21,13 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
case 'color': return 'Color as 0xrrggbb';
|
||||
case 'color-list': return `A list of colors as 0xrrggbb`;
|
||||
case 'vec3': return `3D vector [x, y, z]`;
|
||||
case 'mat4': return `4x4 transformation matrix`;
|
||||
case 'url': return `URL couple with unique identifier`;
|
||||
case 'file': return `JavaScript File Handle`;
|
||||
case 'file-list': return `JavaScript FileList Handle`;
|
||||
case 'select': return `One of ${oToS(param.options)}`;
|
||||
case 'value-ref': return `Reference to a runtime defined value.`;
|
||||
case 'data-ref': return `Reference to a computed data value.`;
|
||||
case 'text': return 'String';
|
||||
case 'interval': return `Interval [min, max]`;
|
||||
case 'group': return `Object with:\n${getParams(param.params, offset + 2)}`;
|
||||
@@ -4,19 +4,19 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as util from 'util'
|
||||
import * as fs from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import * as util from 'util';
|
||||
import * as fs from 'fs';
|
||||
import fetch from 'node-fetch';
|
||||
require('util.promisify').shim();
|
||||
|
||||
import { CIF } from '../../mol-io/reader/cif'
|
||||
import { Progress } from '../../mol-task'
|
||||
import { CIF } from '../../mol-io/reader/cif';
|
||||
import { Progress } from '../../mol-task';
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
|
||||
async function readFile(path: string) {
|
||||
if (path.match(/\.bcif$/)) {
|
||||
const input = await readFileAsync(path)
|
||||
const input = await readFileAsync(path);
|
||||
const data = new Uint8Array(input.byteLength);
|
||||
for (let i = 0; i < input.byteLength; i++) data[i] = input[i];
|
||||
return data;
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
@@ -5,11 +6,11 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as argparse from 'argparse'
|
||||
import * as argparse from 'argparse';
|
||||
require('util.promisify').shim();
|
||||
|
||||
import { CifFrame } from '../../mol-io/reader/cif'
|
||||
import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing } from '../../mol-model/structure'
|
||||
import { CifFrame } from '../../mol-io/reader/cif';
|
||||
import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing, Trajectory } from '../../mol-model/structure';
|
||||
// import { Run, Progress } from '../../mol-task'
|
||||
import { OrderedSet } from '../../mol-data/int';
|
||||
import { openCif, downloadCif } from './helpers';
|
||||
@@ -18,6 +19,7 @@ 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';
|
||||
import { Task } from '../../mol-task';
|
||||
|
||||
|
||||
async function downloadFromPdb(pdb: string) {
|
||||
@@ -32,30 +34,32 @@ export async function readCifFile(path: string) {
|
||||
}
|
||||
|
||||
export function atomLabel(model: Model, aI: number) {
|
||||
const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
|
||||
const { label_atom_id } = atoms
|
||||
const { label_comp_id, label_seq_id } = residues
|
||||
const { label_asym_id } = chains
|
||||
const rI = residueAtomSegments.index[aI]
|
||||
const cI = chainAtomSegments.index[aI]
|
||||
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)} ${label_atom_id.value(aI)}`
|
||||
const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy;
|
||||
const { label_atom_id, label_comp_id } = atoms;
|
||||
const { label_seq_id } = residues;
|
||||
const { label_asym_id } = chains;
|
||||
const rI = residueAtomSegments.index[aI];
|
||||
const cI = chainAtomSegments.index[aI];
|
||||
return `${label_asym_id.value(cI)} ${label_comp_id.value(aI)} ${label_seq_id.value(rI)} ${label_atom_id.value(aI)}`;
|
||||
}
|
||||
|
||||
export function residueLabel(model: Model, rI: number) {
|
||||
const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
|
||||
const { label_comp_id, label_seq_id } = residues
|
||||
const { label_asym_id } = chains
|
||||
const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]]
|
||||
return `${label_asym_id.value(cI)} ${label_comp_id.value(rI)} ${label_seq_id.value(rI)}`
|
||||
const { atoms, residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy;
|
||||
const { label_comp_id } = atoms;
|
||||
const { label_seq_id } = residues;
|
||||
const { label_asym_id } = chains;
|
||||
const aI = residueAtomSegments.offsets[rI];
|
||||
const cI = chainAtomSegments.index[aI];
|
||||
return `${label_asym_id.value(cI)} ${label_comp_id.value(aI)} ${label_seq_id.value(rI)}`;
|
||||
}
|
||||
|
||||
export function printSecStructure(model: Model) {
|
||||
console.log('\nSecondary Structure\n=============');
|
||||
const { residues } = model.atomicHierarchy;
|
||||
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
|
||||
if (!secondaryStructure) return
|
||||
if (!secondaryStructure) return;
|
||||
|
||||
const { key, elements } = secondaryStructure
|
||||
const { key, elements } = secondaryStructure;
|
||||
const count = residues._rowCount;
|
||||
let rI = 0;
|
||||
while (rI < count) {
|
||||
@@ -95,15 +99,17 @@ export function printBonds(structure: Structure, showIntra: boolean, showInter:
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
for (const pairBonds of bonds.getConnectedUnits(unit)) {
|
||||
for (const pairBonds of bonds.getConnectedUnits(unit.id)) {
|
||||
if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
|
||||
|
||||
const { unitA, unitB } = pairBonds;
|
||||
console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
|
||||
const { unitA, unitB, edgeCount } = pairBonds;
|
||||
const uA = structure.unitMap.get(unitA);
|
||||
const uB = structure.unitMap.get(unitB);
|
||||
console.log(`${unitA} - ${unitB}: ${edgeCount} bond(s)`);
|
||||
|
||||
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])}`);
|
||||
console.log(`${atomLabel(uA.model, uA.elements[aI])} -- ${atomLabel(uB.model, uB.elements[bond.indexB])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,8 +122,8 @@ export function printSequence(model: Model) {
|
||||
const { byEntityKey } = model.sequence;
|
||||
for (const key of Object.keys(byEntityKey)) {
|
||||
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)})`);
|
||||
const { seqId, compId } = sequence;
|
||||
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)}, ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
|
||||
console.log(`${Sequence.getSequenceString(sequence)}`);
|
||||
}
|
||||
console.log();
|
||||
@@ -132,7 +138,7 @@ export function printRings(structure: Structure) {
|
||||
for (let i = 0, _i = Math.min(5, all.length); i < _i; i++) {
|
||||
fps[fps.length] = UnitRing.fingerprint(unit, all[i]);
|
||||
}
|
||||
if (all.length > 5) fps.push('...')
|
||||
if (all.length > 5) fps.push('...');
|
||||
console.log(`Unit ${unit.id}, ${all.length} ring(s), ${byFingerprint.size} different fingerprint(s).\n ${fps.join(', ')}`);
|
||||
}
|
||||
console.log();
|
||||
@@ -171,8 +177,8 @@ export function printUnits(structure: Structure) {
|
||||
|
||||
export function printSymmetryInfo(model: Model) {
|
||||
console.log('\nSymmetry Info\n=============');
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
if (!symmetry) return
|
||||
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(', ')}`);
|
||||
@@ -180,10 +186,11 @@ export function printSymmetryInfo(model: Model) {
|
||||
console.log(`NCS operators: ${symmetry.ncsOperators && symmetry.ncsOperators.map(a => a.name).join(', ')}`);
|
||||
}
|
||||
|
||||
export function printModelStats(models: ReadonlyArray<Model>) {
|
||||
export async function printModelStats(models: Trajectory) {
|
||||
console.log('\nModels\n=============');
|
||||
|
||||
for (const m of models) {
|
||||
for (let i = 0; i < models.frameCount; i++) {
|
||||
const m = await Task.resolveInContext(models.getFrameAtIndex(i));
|
||||
if (m.coarseHierarchy.isDefined) {
|
||||
console.log(`${m.label} ${m.modelNum}: ${m.atomicHierarchy.atoms._rowCount} atom(s), ${m.coarseHierarchy.spheres.count} sphere(s), ${m.coarseHierarchy.gaussians.count} gaussian(s)`);
|
||||
} else {
|
||||
@@ -195,7 +202,7 @@ export function printModelStats(models: ReadonlyArray<Model>) {
|
||||
|
||||
export async function getModelsAndStructure(frame: CifFrame) {
|
||||
const models = await trajectoryFromMmCIF(frame).run();
|
||||
const structure = Structure.ofModel(models[0]);
|
||||
const structure = Structure.ofModel(models.representative);
|
||||
return { models, structure };
|
||||
}
|
||||
|
||||
@@ -203,17 +210,17 @@ async function run(frame: CifFrame, args: Args) {
|
||||
const { models, structure } = await getModelsAndStructure(frame);
|
||||
|
||||
if (args.models) printModelStats(models);
|
||||
if (args.seq) printSequence(models[0]);
|
||||
if (args.seq) printSequence(models.representative);
|
||||
if (args.units) printUnits(structure);
|
||||
if (args.sym) printSymmetryInfo(models[0]);
|
||||
if (args.sym) printSymmetryInfo(models.representative);
|
||||
if (args.rings) printRings(structure);
|
||||
if (args.intraBonds) printBonds(structure, true, false);
|
||||
if (args.interBonds) printBonds(structure, false, true);
|
||||
if (args.sec) printSecStructure(models[0]);
|
||||
if (args.sec) printSecStructure(models.representative);
|
||||
}
|
||||
|
||||
async function runDL(pdb: string, args: Args) {
|
||||
const mmcif = await downloadFromPdb(pdb)
|
||||
const mmcif = await downloadFromPdb(pdb);
|
||||
run(mmcif, args);
|
||||
}
|
||||
|
||||
@@ -255,5 +262,5 @@ interface Args {
|
||||
}
|
||||
const args: Args = parser.parseArgs();
|
||||
|
||||
if (args.download) runDL(args.download, args)
|
||||
else if (args.file) runFile(args.file, args)
|
||||
if (args.download) runDL(args.download, args);
|
||||
else if (args.file) runFile(args.file, args);
|
||||
@@ -1,46 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as fs from 'fs'
|
||||
import * as argparse from 'argparse'
|
||||
import * as util from 'util'
|
||||
import * as fs from 'fs';
|
||||
import * as argparse from 'argparse';
|
||||
import * as util from 'util';
|
||||
|
||||
import { VolumeData, VolumeIsoValue } from '../../mol-model/volume'
|
||||
import { downloadCif } from './helpers'
|
||||
import { CIF } from '../../mol-io/reader/cif'
|
||||
import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { downloadCif } from './helpers';
|
||||
import { CIF } from '../../mol-io/reader/cif';
|
||||
import { Table } from '../../mol-data/db';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { Task } from '../../mol-task';
|
||||
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
|
||||
import { volumeFromDensityServerData, DscifFormat } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
require('util.promisify').shim();
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
|
||||
type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
|
||||
|
||||
async function getVolume(url: string): Promise<Volume> {
|
||||
const cif = await downloadCif(url, true);
|
||||
const data = CIF.schema.densityServer(cif.blocks[1]);
|
||||
return { source: data, volume: await volumeFromDensityServerData(data).run() };
|
||||
return await volumeFromDensityServerData(data).run();
|
||||
}
|
||||
|
||||
function print(data: Volume) {
|
||||
const { volume_data_3d_info } = data.source;
|
||||
function print(volume: Volume) {
|
||||
if (!DscifFormat.is(volume.sourceData)) return;
|
||||
const { volume_data_3d_info } = volume.sourceData.data;
|
||||
const row = Table.getRow(volume_data_3d_info, 0);
|
||||
console.log(row);
|
||||
console.log(data.volume.cell);
|
||||
console.log(data.volume.dataStats);
|
||||
console.log(data.volume.fractionalBox);
|
||||
console.log(volume.grid.transform);
|
||||
console.log(volume.grid.stats);
|
||||
}
|
||||
|
||||
async function doMesh(data: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
async function doMesh(volume: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
|
||||
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
|
||||
|
||||
// Export the mesh in OBJ format.
|
||||
25
src/examples/alpha-orbitals/controls.tsx
Normal file
25
src/examples/alpha-orbitals/controls.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { AlphaOrbitalsExample } from '.';
|
||||
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
|
||||
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
|
||||
import { PluginContextContainer } from '../../mol-plugin-ui/plugin';
|
||||
|
||||
export function mountControls(orbitals: AlphaOrbitalsExample, parent: Element) {
|
||||
ReactDOM.render(<PluginContextContainer plugin={orbitals.plugin}>
|
||||
<Controls orbitals={orbitals} />
|
||||
</PluginContextContainer>, parent);
|
||||
}
|
||||
|
||||
function Controls({ orbitals }: { orbitals: AlphaOrbitalsExample }) {
|
||||
const params = useBehavior(orbitals.params);
|
||||
const values = useBehavior(orbitals.state);
|
||||
|
||||
return <ParameterControls params={params as any} values={values} onChangeValues={(vs: any) => orbitals.state.next(vs)} />;
|
||||
}
|
||||
60420
src/examples/alpha-orbitals/example-data.ts
Normal file
60420
src/examples/alpha-orbitals/example-data.ts
Normal file
File diff suppressed because it is too large
Load Diff
61
src/examples/alpha-orbitals/index.html
Normal file
61
src/examples/alpha-orbitals/index.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!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* Alpha Orbitals Example</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
#controls {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
width: 300px;
|
||||
}
|
||||
#sponsor {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
bottom: 8px;
|
||||
font-family: "Helvetica Neue", "Segoe UI", Helvetica, "Source Sans Pro", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
#sponsor svg {
|
||||
fill: #128EA4;
|
||||
width: 100px;
|
||||
}
|
||||
#sponsor a {
|
||||
text-decoration: none;
|
||||
color: #128EA4;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id='controls'></div>
|
||||
<div id='sponsor'>
|
||||
<a href='https://www.entos.ai/envision' target="_blank" rel="noopener">
|
||||
<svg class="makeStyles-root-46" viewBox="0 0 190 36" xmlns="http://www.w3.org/2000/svg"><path d="M32.2591 28.6707C32.2591 32.3914 29.2421 35.407 25.5214 35.407C22.0752 35.407 19.2338 32.8206 18.8325 29.4831V29.4775C18.8143 29.3312 18.8018 29.1835 18.7934 29.0344C18.7934 29.0316 18.7921 29.0274 18.7921 29.0246V29.0177C18.7865 28.902 18.7837 28.7864 18.7837 28.6707C18.7837 26.2557 20.0532 24.1389 21.9609 22.9503C21.9623 22.9489 21.9651 22.9489 21.9665 22.9475C22.0933 22.8666 22.2243 22.7914 22.3581 22.7203C22.3581 22.7203 22.3595 22.7203 22.3595 22.7189C23.3029 22.2173 24.3787 21.933 25.5214 21.933C29.2421 21.933 32.2591 24.9486 32.2591 28.6707Z"></path><path d="M25.5214 14.0692C29.2421 14.0692 32.2591 11.0522 32.2591 7.33146C32.2591 3.61074 29.2421 0.59375 25.5214 0.59375C22.0529 0.59375 19.1962 3.21637 18.8255 6.58592C18.8185 6.67092 18.8116 6.75454 18.8018 6.83815C18.7893 7.00119 18.7837 7.16563 18.7837 7.33146C18.7837 9.73669 20.0434 11.8465 21.94 13.038C22.0891 13.116 22.2355 13.201 22.3776 13.2916C22.3783 13.2923 22.379 13.2926 22.3797 13.293C22.3804 13.2933 22.3811 13.2937 22.3818 13.2944C23.3196 13.7891 24.3871 14.0692 25.5214 14.0692Z"></path><path d="M19.3645 12.4113C20.2926 12.4113 21.1694 12.638 21.94 13.038C20.0434 11.8465 18.7837 9.73669 18.7837 7.33146C18.7837 7.16563 18.7893 7.00119 18.8018 6.83815C18.4688 9.76455 16.1385 12.0866 13.2065 12.4044C13.8545 13.1193 14.3785 13.9484 14.745 14.857C15.7497 13.3798 17.4443 12.4113 19.3645 12.4113Z"></path><path d="M14.7312 21.1249V21.1236C14.1279 20.2331 13.7767 19.1587 13.7767 18.0007V17.9728C13.7767 15.3084 12.2285 13.0035 9.9835 11.911C9.98141 11.9103 9.97967 11.9096 9.97793 11.9089C9.97619 11.9082 9.97444 11.9075 9.97235 11.9068C9.96817 11.904 9.96538 11.9026 9.9612 11.9012C9.95981 11.9012 9.95981 11.8998 9.95981 11.8998C9.9417 11.8915 9.92394 11.8831 9.90618 11.8747C9.8884 11.8664 9.87063 11.858 9.85251 11.8497C9.82046 11.8343 9.78701 11.819 9.75357 11.8051L9.74521 11.8009C8.91745 11.4372 8.0019 11.2351 7.03898 11.2351C3.31826 11.2351 0.30127 14.2521 0.30127 17.9728C0.30127 21.6935 3.31826 24.7105 7.03898 24.7105C7.98797 24.7105 8.89098 24.514 9.71037 24.1601C9.71246 24.1594 9.7142 24.1583 9.71594 24.1573C9.71768 24.1562 9.71943 24.1552 9.72152 24.1545C9.8107 24.1169 9.8985 24.0765 9.9849 24.0333L9.98629 24.0319C10.7625 23.6919 11.6181 23.5037 12.5197 23.5037C12.7524 23.5037 12.9824 23.5163 13.2081 23.54C13.2082 23.5399 13.2081 23.54 13.2081 23.54C15.0168 23.7365 16.5971 24.695 17.6185 26.0885C17.9195 25.1688 18.3752 24.3201 18.9563 23.5732C17.1964 23.4464 15.6635 22.5058 14.7312 21.1249Z"></path><g clip-path="url(#clip0)"><path d="M106.391 18.0021C106.391 11.3724 101.039 6 94.4389 6H88.4585C81.8581 6 76.5061 11.3724 76.5061 18.0021V30.0042H81.2845V18.0021C81.2845 14.0268 84.4941 10.8008 88.4585 10.8008H94.4347C98.395 10.8008 101.609 14.0226 101.609 18.0021V30.0042H106.391V18.0021Z"></path><path d="M149.432 6H142.258C135.653 6 130.301 11.3724 130.301 18.0021C130.301 24.6319 135.653 30.0042 142.258 30.0042H149.432C156.036 30.0042 161.388 24.6319 161.388 18.0021C161.388 11.3724 156.032 6 149.432 6ZM149.432 25.1992H142.258C138.297 25.1992 135.084 21.9774 135.084 17.9979C135.084 14.0183 138.293 10.7966 142.258 10.7966H149.432C153.392 10.7966 156.606 14.0183 156.606 17.9979C156.606 21.9774 153.392 25.1992 149.432 25.1992Z"></path><path d="M74.1151 25.1992H58.5736C55.4526 25.1992 52.804 23.1924 51.8171 20.3983H74.1151V17.9979C74.1151 17.1808 74.1868 16.3807 74.3175 15.5975H51.8171C52.804 12.8033 55.4526 10.7966 58.5736 10.7966H76.0383C77.1475 8.87458 78.6911 7.22773 80.5299 6H58.5736C51.969 6 46.6169 11.3724 46.6169 18.0021C46.6169 24.6276 51.969 30 58.5736 30H74.1151V25.1992Z"></path><path d="M120.74 6H115.958H102.369C104.212 7.22773 105.751 8.87458 106.861 10.8008H115.958V30H120.74V10.8008H129.838C130.947 8.87458 132.486 7.22773 134.329 6H120.74Z"></path><path d="M182.906 15.6017H169.756C168.436 15.6017 167.365 14.5264 167.365 13.2013C167.365 11.8762 168.436 10.8008 169.756 10.8008H188.882V6H169.756C165.796 6 162.582 9.22173 162.582 13.2013C162.582 17.1808 165.791 20.4025 169.756 20.4025H182.906C184.226 20.4025 185.297 21.4779 185.297 22.803C185.297 24.1281 184.226 25.2034 182.906 25.2034H161.852C160.743 27.1297 159.199 28.7765 157.361 30.0042H182.906C186.866 30.0042 190.08 26.7825 190.08 22.803C190.08 18.8234 186.866 15.6017 182.906 15.6017Z"></path></g><defs><clipPath id="clip0"><rect width="190" height="24" fill="white" transform="translate(0 6)"></rect></clipPath></defs></svg>
|
||||
<div>
|
||||
Entos Envision
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<script>
|
||||
AlphaOrbitalsExample.init('app')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
223
src/examples/alpha-orbitals/index.ts
Normal file
223
src/examples/alpha-orbitals/index.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { SphericalBasisOrder } from '../../extensions/alpha-orbitals/spherical-functions';
|
||||
import { BasisAndOrbitals, CreateOrbitalDensityVolume, CreateOrbitalRepresentation3D, CreateOrbitalVolume, StaticBasisAndOrbitals } from '../../extensions/alpha-orbitals/transforms';
|
||||
import { createPluginAsync } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { StateObjectSelector, StateTransformer } from '../../mol-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition } from '../../mol-util/param-definition';
|
||||
import { mountControls } from './controls';
|
||||
import { DemoMoleculeSDF, DemoOrbitals } from './example-data';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { debounceTime, skip } from 'rxjs/operators';
|
||||
import './index.html';
|
||||
import { Basis, AlphaOrbital } from '../../extensions/alpha-orbitals/data-model';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
interface DemoInput {
|
||||
moleculeSdf: string,
|
||||
basis: Basis,
|
||||
order: SphericalBasisOrder,
|
||||
orbitals: AlphaOrbital[]
|
||||
}
|
||||
|
||||
interface Params {
|
||||
show: { name: 'orbital', params: { index: number } } | { name: 'density', params: {} },
|
||||
isoValue: number,
|
||||
gpuSurface: boolean
|
||||
}
|
||||
|
||||
type Selectors = {
|
||||
type: 'orbital',
|
||||
volume: StateObjectSelector<PluginStateObject.Volume.Data, typeof CreateOrbitalVolume>,
|
||||
positive: StateObjectSelector<PluginStateObject.Volume.Representation3D, typeof CreateOrbitalRepresentation3D>
|
||||
negative: StateObjectSelector<PluginStateObject.Volume.Representation3D, typeof CreateOrbitalRepresentation3D>
|
||||
} | {
|
||||
type: 'density',
|
||||
volume: StateObjectSelector<PluginStateObject.Volume.Data, typeof CreateOrbitalDensityVolume>,
|
||||
positive: StateObjectSelector<PluginStateObject.Volume.Representation3D, typeof CreateOrbitalRepresentation3D>
|
||||
}
|
||||
|
||||
export class AlphaOrbitalsExample {
|
||||
plugin: PluginContext;
|
||||
|
||||
async init(target: string | HTMLElement) {
|
||||
const defaultSpec = DefaultPluginSpec();
|
||||
this.plugin = await createPluginAsync(typeof target === 'string' ? document.getElementById(target)! : target, {
|
||||
...defaultSpec,
|
||||
layout: {
|
||||
initial: {
|
||||
isExpanded: false,
|
||||
showControls: false
|
||||
},
|
||||
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' },
|
||||
},
|
||||
components: {
|
||||
viewport: {
|
||||
canvas3d: {
|
||||
camera: {
|
||||
helper: { axes: { name: 'off', params: { } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
config: [
|
||||
[PluginConfig.Viewport.ShowExpand, false],
|
||||
[PluginConfig.Viewport.ShowControls, false],
|
||||
[PluginConfig.Viewport.ShowSelectionMode, false],
|
||||
[PluginConfig.Viewport.ShowAnimation, false],
|
||||
]
|
||||
});
|
||||
|
||||
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
|
||||
|
||||
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
|
||||
PluginCommands.Toast.Show(this.plugin, {
|
||||
title: 'Error',
|
||||
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.load({
|
||||
moleculeSdf: DemoMoleculeSDF,
|
||||
...DemoOrbitals
|
||||
});
|
||||
|
||||
mountControls(this, document.getElementById('controls')!);
|
||||
}
|
||||
|
||||
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
|
||||
readonly state = new BehaviorSubject<Params>({ show: { name: 'orbital', params: { index: 32 } }, isoValue: 1, gpuSurface: false });
|
||||
|
||||
private selectors?: Selectors = void 0;
|
||||
private basis?: StateObjectSelector<BasisAndOrbitals> = void 0;
|
||||
|
||||
private currentParams: Params = { ...this.state.value };
|
||||
|
||||
private clearVolume() {
|
||||
if (!this.selectors) return;
|
||||
const v = this.selectors.volume;
|
||||
this.selectors = void 0;
|
||||
return this.plugin.build().delete(v).commit();
|
||||
}
|
||||
|
||||
private async syncVolume() {
|
||||
if (!this.basis?.isOk) return;
|
||||
|
||||
const state = this.state.value;
|
||||
|
||||
if (state.show.name !== this.selectors?.type) {
|
||||
await this.clearVolume();
|
||||
}
|
||||
|
||||
const update = this.plugin.build();
|
||||
if (state.show.name === 'orbital') {
|
||||
if (!this.selectors) {
|
||||
const volume = update
|
||||
.to(this.basis)
|
||||
.apply(CreateOrbitalVolume, { index: state.show.params.index });
|
||||
|
||||
const positive = volume.apply(CreateOrbitalRepresentation3D, this.volumeParams('positive', ColorNames.blue)).selector;
|
||||
const negative = volume.apply(CreateOrbitalRepresentation3D, this.volumeParams('negative', ColorNames.red)).selector;
|
||||
|
||||
this.selectors = { type: 'orbital', volume: volume.selector, positive, negative };
|
||||
} else {
|
||||
const index = state.show.params.index;
|
||||
update.to(this.selectors.volume).update(CreateOrbitalVolume, () => ({ index }));
|
||||
}
|
||||
} else {
|
||||
if (!this.selectors) {
|
||||
const volume = update
|
||||
.to(this.basis)
|
||||
.apply(CreateOrbitalDensityVolume);
|
||||
const positive = volume.apply(CreateOrbitalRepresentation3D, this.volumeParams('positive', ColorNames.blue)).selector;
|
||||
this.selectors = { type: 'density', volume: volume.selector, positive };
|
||||
}
|
||||
}
|
||||
|
||||
await update.commit();
|
||||
|
||||
if (this.currentParams.gpuSurface !== this.state.value.gpuSurface) {
|
||||
await this.setIsovalue();
|
||||
}
|
||||
|
||||
this.currentParams = this.state.value;
|
||||
}
|
||||
|
||||
private setIsovalue() {
|
||||
if (!this.selectors) return;
|
||||
|
||||
this.currentParams = this.state.value;
|
||||
const update = this.plugin.build();
|
||||
update.to(this.selectors.positive).update(this.volumeParams('positive', ColorNames.blue));
|
||||
if (this.selectors?.type === 'orbital') {
|
||||
update.to(this.selectors.negative).update(this.volumeParams('negative', ColorNames.red));
|
||||
}
|
||||
return update.commit();
|
||||
}
|
||||
|
||||
private volumeParams(kind: 'positive' | 'negative', color: Color): StateTransformer.Params<typeof CreateOrbitalRepresentation3D> {
|
||||
return {
|
||||
alpha: 0.85,
|
||||
color,
|
||||
directVolume: this.state.value.gpuSurface,
|
||||
kind,
|
||||
relativeIsovalue: this.state.value.isoValue,
|
||||
pickable: false,
|
||||
xrayShaded: true
|
||||
};
|
||||
}
|
||||
|
||||
async load(input: DemoInput) {
|
||||
await this.plugin.clear();
|
||||
|
||||
const data = await this.plugin.builders.data.rawData({ data: input.moleculeSdf }, { state: { isGhost: true } });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, 'mol');
|
||||
const model = await this.plugin.builders.structure.createModel(trajectory);
|
||||
const structure = await this.plugin.builders.structure.createStructure(model);
|
||||
|
||||
const all = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'all');
|
||||
if (all) await this.plugin.builders.structure.representation.addRepresentation(all, { type: 'ball-and-stick', color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } });
|
||||
|
||||
|
||||
this.basis = await this.plugin.build().toRoot()
|
||||
.apply(StaticBasisAndOrbitals, { basis: input.basis, order: input.order, orbitals: input.orbitals })
|
||||
.commit();
|
||||
|
||||
await this.syncVolume();
|
||||
|
||||
this.params.next({
|
||||
show: ParamDefinition.MappedStatic('orbital', {
|
||||
'orbital': ParamDefinition.Group({
|
||||
index: ParamDefinition.Numeric(32, { min: 0, max: input.orbitals.length - 1 }, { immediateUpdate: true, isEssential: true }),
|
||||
}),
|
||||
'density': ParamDefinition.EmptyGroup()
|
||||
}, { cycle: true }),
|
||||
isoValue: ParamDefinition.Numeric(this.currentParams.isoValue, { min: 0.5, max: 3, step: 0.1 }, { immediateUpdate: true, isEssential: false }),
|
||||
gpuSurface: ParamDefinition.Boolean(this.currentParams.gpuSurface, { isHidden: true })
|
||||
});
|
||||
|
||||
this.state.pipe(skip(1), debounceTime(1000 / 24)).subscribe(async params => {
|
||||
if (params.show.name !== this.currentParams.show.name
|
||||
|| (params.show.name === 'orbital' && this.currentParams.show.name === 'orbital' && params.show.params.index !== this.currentParams.show.params.index)) {
|
||||
this.syncVolume();
|
||||
} else if (params.isoValue !== this.currentParams.isoValue || params.gpuSurface !== this.currentParams.gpuSurface) {
|
||||
this.setIsovalue();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).AlphaOrbitalsExample = new AlphaOrbitalsExample();
|
||||
@@ -18,13 +18,13 @@ export const StripedResidues = CustomElementProperty.create<number>({
|
||||
for (let i = 0, _i = model.atomicHierarchy.atoms._rowCount; i < _i; i++) {
|
||||
map.set(i as ElementIndex, residueIndex[i] % 2);
|
||||
}
|
||||
return map;
|
||||
return { value: map };
|
||||
},
|
||||
coloring: {
|
||||
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
|
||||
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff); },
|
||||
defaultColor: Color(0x777777)
|
||||
},
|
||||
getLabel(e) {
|
||||
return e === 0 ? 'Odd stripe' : 'Even stripe'
|
||||
return e === 0 ? 'Odd stripe' : 'Even stripe';
|
||||
}
|
||||
})
|
||||
});
|
||||
16
src/examples/basic-wrapper/controls.tsx
Normal file
16
src/examples/basic-wrapper/controls.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
|
||||
export class CustomToastMessage extends PluginUIComponent {
|
||||
render() {
|
||||
return <>
|
||||
Custom <i>Toast</i> content. No timeout.
|
||||
</>;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -50,18 +50,18 @@
|
||||
<input type='text' id='url' placeholder='url' />
|
||||
<input type='text' id='assemblyId' placeholder='assembly id' />
|
||||
<select id='format'>
|
||||
<option value='cif' selected>CIF</option>
|
||||
<option value='mmcif' selected>mmCIF</option>
|
||||
<option value='pdb'>PDB</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
<script>
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
|
||||
var pdbId = '1grm', assemblyId= '1';
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
|
||||
var format = 'cif';
|
||||
|
||||
var format = 'mmcif';
|
||||
|
||||
$('url').value = url;
|
||||
$('url').onchange = function (e) { url = e.target.value; }
|
||||
$('assemblyId').value = assemblyId;
|
||||
@@ -69,15 +69,8 @@
|
||||
$('format').value = format;
|
||||
$('format').onchange = function (e) { format = e.target.value; }
|
||||
|
||||
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
|
||||
// var format = 'pdb';
|
||||
// var assemblyId = 'deposited';
|
||||
|
||||
BasicMolStarWrapper.init('app' /** or document.getElementById('app') */);
|
||||
BasicMolStarWrapper.setBackground(0xffffff);
|
||||
// BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId });
|
||||
// BasicMolStarWrapper.toggleSpin();
|
||||
|
||||
|
||||
addControl('Load Asym Unit', () => BasicMolStarWrapper.load({ url: url, format: format }));
|
||||
addControl('Load Assembly', () => BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
|
||||
@@ -86,14 +79,14 @@
|
||||
|
||||
addHeader('Camera');
|
||||
addControl('Toggle Spin', () => BasicMolStarWrapper.toggleSpin());
|
||||
|
||||
|
||||
addSeparator();
|
||||
|
||||
addHeader('Animation');
|
||||
|
||||
// adjust this number to make the animation faster or slower
|
||||
// requires to "restart" the animation if changed
|
||||
BasicMolStarWrapper.animate.modelIndex.maxFPS = 30;
|
||||
BasicMolStarWrapper.animate.modelIndex.targetFps = 30;
|
||||
|
||||
addControl('Play To End', () => BasicMolStarWrapper.animate.modelIndex.onceForward());
|
||||
addControl('Play To Start', () => BasicMolStarWrapper.animate.modelIndex.onceBackward());
|
||||
@@ -104,6 +97,7 @@
|
||||
addHeader('Misc');
|
||||
|
||||
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
|
||||
addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
|
||||
|
||||
addHeader('Interactivity');
|
||||
addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());
|
||||
@@ -114,7 +108,7 @@
|
||||
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());
|
||||
|
||||
168
src/examples/basic-wrapper/index.ts
Normal file
168
src/examples/basic-wrapper/index.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { createPlugin } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/model-index';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { Script } from '../../mol-script/script';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { StripedResidues } from './coloring';
|
||||
import { CustomToastMessage } from './controls';
|
||||
import './index.html';
|
||||
import { buildStaticSuperposition, dynamicSuperpositionTest, StaticSuperpositionTestData } from './superposition';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
|
||||
|
||||
class BasicWrapper {
|
||||
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'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
|
||||
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
|
||||
}
|
||||
|
||||
async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {
|
||||
await this.plugin.clear();
|
||||
|
||||
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
|
||||
|
||||
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
|
||||
structure: assemblyId ? {
|
||||
name: 'assembly',
|
||||
params: { id: assemblyId }
|
||||
} : {
|
||||
name: 'model',
|
||||
params: { }
|
||||
},
|
||||
showUnitcell: false,
|
||||
representationPreset: 'auto'
|
||||
});
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: props => { props.renderer.backgroundColor = Color(color); } });
|
||||
}
|
||||
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, {
|
||||
settings: props => {
|
||||
props.trackball.spin = !props.trackball.spin;
|
||||
}
|
||||
});
|
||||
if (!this.plugin.canvas3d.props.trackball.spin) PluginCommands.Camera.Reset(this.plugin, {});
|
||||
}
|
||||
|
||||
private animateModelIndexTargetFps() {
|
||||
return Math.max(1, this.animate.modelIndex.targetFps | 0);
|
||||
}
|
||||
|
||||
animate = {
|
||||
modelIndex: {
|
||||
targetFps: 8,
|
||||
onceForward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'once', params: { direction: 'forward' } } }); },
|
||||
onceBackward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'once', params: { direction: 'backward' } } }); },
|
||||
palindrome: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'palindrome', params: {} } }); },
|
||||
loop: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'loop', params: { direction: 'forward' } } }); },
|
||||
stop: () => this.plugin.managers.animation.stop()
|
||||
}
|
||||
}
|
||||
|
||||
coloring = {
|
||||
applyStripes: async () => {
|
||||
this.plugin.dataTransaction(async () => {
|
||||
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
|
||||
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: StripedResidues.propertyProvider.descriptor.name as any });
|
||||
}
|
||||
});
|
||||
},
|
||||
applyDefault: async () => {
|
||||
this.plugin.dataTransaction(async () => {
|
||||
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
|
||||
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: 'default' });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interactivity = {
|
||||
highlightOn: () => {
|
||||
const data = this.plugin.managers.structure.hierarchy.current.structures[0]?.cell.obj?.data;
|
||||
if (!data) return;
|
||||
|
||||
const seq_id = 7;
|
||||
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 () => {
|
||||
await this.plugin.clear();
|
||||
return buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
|
||||
},
|
||||
dynamicSuperposition: async () => {
|
||||
await this.plugin.clear();
|
||||
return dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
|
||||
},
|
||||
toggleValidationTooltip: () => {
|
||||
return this.plugin.state.updateBehavior(PDBeStructureQualityReport, params => { params.showTooltip = !params.showTooltip; });
|
||||
},
|
||||
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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).BasicMolStarWrapper = new BasicWrapper();
|
||||
119
src/examples/basic-wrapper/superposition.ts
Normal file
119
src/examples/basic-wrapper/superposition.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { QueryContext, StructureSelection } from '../../mol-model/structure';
|
||||
import { superpose } from '../../mol-model/structure/structure/util/superposition';
|
||||
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { Expression } from '../../mol-script/language/expression';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
|
||||
export type SuperpositionTestInput = {
|
||||
pdbId: string,
|
||||
auth_asym_id: string,
|
||||
matrix: Mat4
|
||||
}[];
|
||||
|
||||
export function buildStaticSuperposition(plugin: PluginContext, src: SuperpositionTestInput) {
|
||||
return plugin.dataTransaction(async () => {
|
||||
for (const s of src) {
|
||||
const { structure } = await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`, 'mmcif');
|
||||
await transform(plugin, structure, s.matrix);
|
||||
const chain = await plugin.builders.structure.tryCreateComponentFromExpression(structure, chainSelection(s.auth_asym_id), `Chain ${s.auth_asym_id}`);
|
||||
if (chain) await plugin.builders.structure.representation.addRepresentation(chain, { type: 'cartoon' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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]])
|
||||
}
|
||||
];
|
||||
|
||||
export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], comp_id: string) {
|
||||
return plugin.dataTransaction(async () => {
|
||||
for (const s of src) {
|
||||
await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`, 'mmcif');
|
||||
}
|
||||
|
||||
const pivot = MS.struct.filter.first([
|
||||
MS.struct.generator.atomGroups({
|
||||
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
|
||||
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
|
||||
})
|
||||
]);
|
||||
|
||||
const rest = MS.struct.modifier.exceptBy({
|
||||
0: MS.struct.modifier.includeSurroundings({
|
||||
0: pivot,
|
||||
radius: 5
|
||||
}),
|
||||
by: pivot
|
||||
});
|
||||
|
||||
const query = compile<StructureSelection>(pivot);
|
||||
const xs = plugin.managers.structure.hierarchy.current.structures;
|
||||
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.cell.obj!.data))));
|
||||
|
||||
const transforms = superpose(selections);
|
||||
|
||||
await siteVisual(plugin, xs[0].cell, pivot, rest);
|
||||
for (let i = 1; i < selections.length; i++) {
|
||||
await transform(plugin, xs[i].cell, transforms[i - 1].bTransform);
|
||||
await siteVisual(plugin, xs[i].cell, pivot, rest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function siteVisual(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
|
||||
const center = await plugin.builders.structure.tryCreateComponentFromExpression(s, pivot, 'pivot');
|
||||
if (center) await plugin.builders.structure.representation.addRepresentation(center, { type: 'ball-and-stick', color: 'residue-name' });
|
||||
|
||||
const surr = await plugin.builders.structure.tryCreateComponentFromExpression(s, rest, 'rest');
|
||||
if (surr) await plugin.builders.structure.representation.addRepresentation(surr, { type: 'ball-and-stick', color: 'uniform', size: 'uniform', sizeParams: { value: 0.33 } });
|
||||
}
|
||||
|
||||
async function loadStructure(plugin: PluginContext, url: string, format: BuiltInTrajectoryFormat, assemblyId?: string) {
|
||||
const data = await plugin.builders.data.download({ url: Asset.Url(url) });
|
||||
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
|
||||
const model = await plugin.builders.structure.createModel(trajectory);
|
||||
const structure = await plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : void 0);
|
||||
|
||||
return { data, trajectory, model, structure };
|
||||
}
|
||||
|
||||
function chainSelection(auth_asym_id: string) {
|
||||
return MS.struct.generator.atomGroups({
|
||||
'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
|
||||
});
|
||||
}
|
||||
|
||||
function transform(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, matrix: Mat4) {
|
||||
const b = plugin.state.data.build().to(s)
|
||||
.insert(StateTransforms.Model.TransformStructureConformation, { transform: { name: 'matrix', params: { data: matrix, transpose: false } } });
|
||||
return plugin.runTask(plugin.state.data.updateTree(b));
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Table } from '../../mol-data/db'
|
||||
import { CifWriter } from '../../mol-io/writer/cif'
|
||||
import * as S from './schemas'
|
||||
import { Table } from '../../mol-data/db';
|
||||
import { CifWriter } from '../../mol-io/writer/cif';
|
||||
import * as S from './schemas';
|
||||
// import { getCategoryInstanceProvider } from './utils'
|
||||
|
||||
export default function create(allData: any) {
|
||||
export function createMapping(allData: any) {
|
||||
const mols = Object.keys(allData);
|
||||
const enc = CifWriter.createEncoder();
|
||||
enc.startDataBlock(mols[0]);
|
||||
@@ -4,21 +4,21 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Column } from '../../mol-data/db'
|
||||
import { Column } from '../../mol-data/db';
|
||||
|
||||
import Type = Column.Schema
|
||||
|
||||
export const Sources = {
|
||||
id: Type.str,
|
||||
count: Type.int
|
||||
}
|
||||
};
|
||||
export type Sources = typeof Sources
|
||||
|
||||
export const Base = {
|
||||
id: Type.str,
|
||||
identifier: Type.str,
|
||||
mapping_group_id: Type.int
|
||||
}
|
||||
};
|
||||
export type Base = typeof Base
|
||||
|
||||
export const mapping = {
|
||||
@@ -36,17 +36,17 @@ export const mapping = {
|
||||
end_label_seq_id: Type.int,
|
||||
end_auth_seq_id: Type.int,
|
||||
pdbx_end_PDB_ins_code: Type.str
|
||||
}
|
||||
};
|
||||
export type mapping = typeof mapping
|
||||
|
||||
export const Pfam = {
|
||||
description: Type.str
|
||||
}
|
||||
};
|
||||
export type Pfam = typeof Pfam
|
||||
|
||||
export const InterPro = {
|
||||
name: Type.str
|
||||
}
|
||||
};
|
||||
export type InterPro = typeof InterPro
|
||||
|
||||
export const CATH = {
|
||||
@@ -56,32 +56,32 @@ export const CATH = {
|
||||
identifier: Type.str,
|
||||
class: Type.str,
|
||||
topology: Type.str,
|
||||
}
|
||||
};
|
||||
export type CATH = typeof CATH
|
||||
|
||||
export const EC = {
|
||||
accepted_name: Type.str,
|
||||
reaction: Type.str,
|
||||
systematic_name: Type.str
|
||||
}
|
||||
};
|
||||
export type EC = typeof EC
|
||||
|
||||
export const UniProt = {
|
||||
name: Type.str
|
||||
}
|
||||
};
|
||||
export type UniProt = typeof UniProt
|
||||
|
||||
export const SCOP = {
|
||||
sccs: Type.str,
|
||||
description: Type.str
|
||||
}
|
||||
};
|
||||
export type SCOP = typeof SCOP
|
||||
|
||||
export const GO = {
|
||||
category: Type.str,
|
||||
definition: Type.str,
|
||||
name: Type.str
|
||||
}
|
||||
};
|
||||
export type GO = typeof GO
|
||||
|
||||
export const categories = {
|
||||
@@ -92,4 +92,4 @@ export const categories = {
|
||||
UniProt,
|
||||
SCOP,
|
||||
GO
|
||||
}
|
||||
};
|
||||
@@ -4,9 +4,9 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as express from 'express'
|
||||
import fetch from 'node-fetch'
|
||||
import createMapping from './mapping'
|
||||
import express from 'express';
|
||||
import fetch from 'node-fetch';
|
||||
import { createMapping } from './mapping';
|
||||
|
||||
async function getMappings(id: string) {
|
||||
const data = await fetch(`https://www.ebi.ac.uk/pdbe/api/mappings/${id}`);
|
||||
@@ -19,7 +19,7 @@ let PORT = process.env.port || 1338;
|
||||
|
||||
const app = express();
|
||||
|
||||
const PREFIX = '/'
|
||||
const PREFIX = '/';
|
||||
|
||||
app.get(`${PREFIX}/:id`, async (req, res) => {
|
||||
try {
|
||||
@@ -41,7 +41,7 @@ app.get(`${PREFIX}/:id`, async (req, res) => {
|
||||
app.get(`${PREFIX}`, (req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
|
||||
res.end('Usage: /pdb_id, e.g. /1tqn');
|
||||
})
|
||||
});
|
||||
|
||||
app.listen(PORT);
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import fetch from 'node-fetch'
|
||||
import createMapping from './mapping'
|
||||
import fetch from 'node-fetch';
|
||||
import { createMapping } from './mapping';
|
||||
|
||||
(async function () {
|
||||
const data = await fetch('https://www.ebi.ac.uk/pdbe/api/mappings/1tqn?pretty=true');
|
||||
@@ -12,18 +12,18 @@
|
||||
}
|
||||
#app {
|
||||
position: absolute;
|
||||
left: 160px;
|
||||
top: 100px;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
top: 100px;
|
||||
left: 780px;
|
||||
bottom: 100px;
|
||||
right: 50px;
|
||||
z-index: 10;
|
||||
font-family: sans-serif;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
#controls > button {
|
||||
@@ -38,21 +38,21 @@
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='controls'></div>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
<script>
|
||||
LightingDemo.init('app')
|
||||
LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
|
||||
|
||||
LightingDemo.load({ url: 'https://models.rcsb.org/4KTC.bcif', assemblyId: '1' }, 5, 1.3)
|
||||
|
||||
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' }));
|
||||
addControl('4KTC', () => LightingDemo.load({ url: 'https://models.rcsb.org/4KTC.bcif', assemblyId: '1' }, 5, 1.3));
|
||||
addControl('5FJ5', () => LightingDemo.load({ url: 'https://models.rcsb.org/5FJ5.bcif', assemblyId: '1' }, 8, 1.8));
|
||||
addControl('1UPN', () => LightingDemo.load({ url: 'https://models.rcsb.org/1UPN.bcif', assemblyId: '1' }, 7, 1.6));
|
||||
addControl('1RB8', () => LightingDemo.load({ url: 'https://models.rcsb.org/1RB8.bcif', assemblyId: '1' }, 6, 1.3));
|
||||
|
||||
addSeparator()
|
||||
|
||||
129
src/examples/lighting/index.ts
Normal file
129
src/examples/lighting/index.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
import { createPlugin } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import './index.html';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
|
||||
type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
|
||||
|
||||
type _Preset = Pick<Canvas3DProps, 'multiSample' | 'postprocessing' | 'renderer'>
|
||||
type Preset = { [K in keyof _Preset]: Partial<_Preset[K]> }
|
||||
|
||||
const Canvas3DPresets = {
|
||||
illustrative: <Preset> {
|
||||
multiSample: {
|
||||
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
|
||||
outline: { name: 'on', params: { scale: 1, threshold: 0.1 } }
|
||||
},
|
||||
renderer: {
|
||||
style: { name: 'flat', params: {} }
|
||||
}
|
||||
},
|
||||
occlusion: <Preset> {
|
||||
multiSample: {
|
||||
mode: 'temporal' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
|
||||
outline: { name: 'off', params: { } }
|
||||
},
|
||||
renderer: {
|
||||
style: { name: 'matte', params: {} }
|
||||
}
|
||||
},
|
||||
standard: <Preset> {
|
||||
multiSample: {
|
||||
mode: 'off' as Canvas3DProps['multiSample']['mode']
|
||||
},
|
||||
postprocessing: {
|
||||
occlusion: { name: 'off', params: { } },
|
||||
outline: { name: 'off', params: { } }
|
||||
},
|
||||
renderer: {
|
||||
style: { name: 'matte', params: {} }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
type Canvas3DPreset = keyof typeof Canvas3DPresets
|
||||
|
||||
class LightingDemo {
|
||||
plugin: PluginContext;
|
||||
|
||||
private radius = 5;
|
||||
private bias = 1.1;
|
||||
private preset: Canvas3DPreset = 'illustrative';
|
||||
|
||||
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 = Canvas3DPresets[preset];
|
||||
if (props.postprocessing.occlusion?.name === 'on') {
|
||||
props.postprocessing.occlusion.params.radius = this.radius;
|
||||
props.postprocessing.occlusion.params.bias = this.bias;
|
||||
}
|
||||
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
|
||||
},
|
||||
}});
|
||||
}
|
||||
|
||||
async load({ url, format = 'mmcif', isBinary = true, assemblyId = '' }: LoadParams, radius: number, bias: number) {
|
||||
await this.plugin.clear();
|
||||
|
||||
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
|
||||
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
|
||||
const model = await this.plugin.builders.structure.createModel(trajectory);
|
||||
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'model', params: { } });
|
||||
|
||||
const polymer = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer');
|
||||
if (polymer) await this.plugin.builders.structure.representation.addRepresentation(polymer, { type: 'spacefill', color: 'illustrative' });
|
||||
|
||||
const ligand = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand');
|
||||
if (ligand) await this.plugin.builders.structure.representation.addRepresentation(ligand, { type: 'ball-and-stick', color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } });
|
||||
|
||||
this.radius = radius;
|
||||
this.bias = bias;
|
||||
this.setPreset(this.preset);
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).LightingDemo = new LightingDemo();
|
||||
@@ -9,6 +9,7 @@ import { CustomElementProperty } from '../../mol-model-props/common/custom-eleme
|
||||
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
|
||||
const EvolutionaryConservationPalette: Color[] = [
|
||||
[255, 255, 129], // insufficient
|
||||
@@ -30,9 +31,9 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
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 url = Asset.getUrlAsset(ctx.assetManager, `https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await ctx.assetManager.resolve(url, 'json').runInContext(ctx.runtime);
|
||||
const annotations = json.data?.residueAnnotations || [];
|
||||
|
||||
const conservationMap = new Map<string, number>();
|
||||
|
||||
@@ -58,7 +59,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
return { value: map, assets: [json] };
|
||||
},
|
||||
coloring: {
|
||||
getColor(e: number) {
|
||||
@@ -68,7 +69,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
defaultColor: EvolutionaryConservationDefaultColor
|
||||
},
|
||||
getLabel(e) {
|
||||
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
|
||||
if (e === 10) return `Evolutionary Conservation: Insufficient Data`;
|
||||
return e ? `Evolutionary Conservation: ${e}` : void 0;
|
||||
}
|
||||
});
|
||||
@@ -11,58 +11,58 @@ import { Unit, StructureProperties, StructureElement, Bond } from '../../mol-mod
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Location } from '../../mol-model/location';
|
||||
import { ColorTheme, LocationColor } from '../../mol-theme/color';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition'
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ThemeDataContext } from '../../mol-theme/theme';
|
||||
import { Column } from '../../mol-data/db';
|
||||
|
||||
const Description = 'Gives every chain a color from a list based on its `asym_id` value.'
|
||||
const Description = 'Gives every chain a color from a list based on its `asym_id` value.';
|
||||
|
||||
export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const ProteopediaCustomColorThemeParams = {
|
||||
colors: PD.ObjectList({ color: PD.Color(Color(0xffffff)) }, ({ color }) => Color.toHexString(color),
|
||||
{ defaultValue: colors.map(c => ({ color: Color(c) })) })
|
||||
}
|
||||
};
|
||||
type ProteopediaCustomColorThemeParams = typeof ProteopediaCustomColorThemeParams
|
||||
function getChainIdColorThemeParams(ctx: ThemeDataContext) {
|
||||
return ProteopediaCustomColorThemeParams // TODO return copy
|
||||
return ProteopediaCustomColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
function getAsymId(unit: Unit): StructureElement.Property<string> {
|
||||
switch (unit.kind) {
|
||||
case Unit.Kind.Atomic:
|
||||
return StructureProperties.chain.label_asym_id
|
||||
return StructureProperties.chain.label_asym_id;
|
||||
case Unit.Kind.Spheres:
|
||||
case Unit.Kind.Gaussians:
|
||||
return StructureProperties.coarse.asym_id
|
||||
return StructureProperties.coarse.asym_id;
|
||||
}
|
||||
}
|
||||
|
||||
function addAsymIds(map: Map<string, number>, data: Column<string>) {
|
||||
let j = map.size
|
||||
let j = map.size;
|
||||
for (let o = 0, ol = data.rowCount; o < ol; ++o) {
|
||||
const k = data.value(o)
|
||||
const k = data.value(o);
|
||||
if (!map.has(k)) {
|
||||
map.set(k, j)
|
||||
j += 1
|
||||
map.set(k, j);
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ProteopediaCustomColorTheme(ctx: ThemeDataContext, props: PD.Values<ProteopediaCustomColorThemeParams>): ColorTheme<ProteopediaCustomColorThemeParams> {
|
||||
let color: LocationColor
|
||||
let color: LocationColor;
|
||||
|
||||
const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
|
||||
|
||||
if (ctx.structure) {
|
||||
const l = StructureElement.Location.create(ctx.structure)
|
||||
const { models } = ctx.structure
|
||||
const asymIdSerialMap = new Map<string, number>()
|
||||
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) {
|
||||
const m = models[i]
|
||||
addAsymIds(asymIdSerialMap, m.atomicHierarchy.chains.label_asym_id)
|
||||
const m = models[i];
|
||||
addAsymIds(asymIdSerialMap, m.atomicHierarchy.chains.label_asym_id);
|
||||
if (m.coarseHierarchy.isDefined) {
|
||||
addAsymIds(asymIdSerialMap, m.coarseHierarchy.spheres.asym_id)
|
||||
addAsymIds(asymIdSerialMap, m.coarseHierarchy.gaussians.asym_id)
|
||||
addAsymIds(asymIdSerialMap, m.coarseHierarchy.spheres.asym_id);
|
||||
addAsymIds(asymIdSerialMap, m.coarseHierarchy.gaussians.asym_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,16 +72,16 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const o = asymIdSerialMap.get(asym_id(location)) || 0;
|
||||
return colors[o % colorCount].color;
|
||||
} else if (Bond.isLocation(location)) {
|
||||
const asym_id = getAsymId(location.aUnit)
|
||||
l.unit = location.aUnit
|
||||
l.element = location.aUnit.elements[location.aIndex]
|
||||
const asym_id = getAsymId(location.aUnit);
|
||||
l.unit = location.aUnit;
|
||||
l.element = location.aUnit.elements[location.aIndex];
|
||||
const o = asymIdSerialMap.get(asym_id(l)) || 0;
|
||||
return colors[o % colorCount].color;
|
||||
}
|
||||
return defaultColor
|
||||
}
|
||||
return defaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => defaultColor
|
||||
color = () => defaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -91,7 +91,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
props,
|
||||
description: Description,
|
||||
legend: undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -102,5 +102,5 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
getParams: getChainIdColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -4,12 +4,11 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { ResidueIndex, Model } from '../../mol-model/structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
import { Model, ResidueIndex } from '../../mol-model/structure';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
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[] }[],
|
||||
@@ -54,13 +53,12 @@ export namespace ModelInfo {
|
||||
const hetMap = new Map<string, ModelInfo['hetResidues'][0]>();
|
||||
|
||||
for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
|
||||
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 entityType = model.entities.data.type.value(eI);
|
||||
if (entityType !== 'non-polymer' && entityType !== 'branched') continue;
|
||||
|
||||
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
|
||||
const comp_id = model.atomicHierarchy.atoms.label_comp_id.value(residueOffsets[rI]);
|
||||
|
||||
let lig = hetMap.get(comp_id);
|
||||
if (!lig) {
|
||||
@@ -72,7 +70,7 @@ export namespace ModelInfo {
|
||||
}
|
||||
|
||||
const preferredAssemblyId = await pref;
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
const symmetry = ModelSymmetry.Provider.get(model);
|
||||
|
||||
return {
|
||||
hetResidues: hetResidues,
|
||||
@@ -86,6 +84,7 @@ export type SupportedFormats = 'cif' | 'pdb'
|
||||
export interface LoadParams {
|
||||
url: string,
|
||||
format?: SupportedFormats,
|
||||
isBinary?: boolean,
|
||||
assemblyId?: string,
|
||||
representationStyle?: RepresentationStyle
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
<link rel="stylesheet" type="text/css" href="molstar.css" />
|
||||
<script type="text/javascript" src="./index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -60,11 +60,12 @@
|
||||
<option value='cif' selected>CIF</option>
|
||||
<option value='pdb'>PDB</option>
|
||||
</select>
|
||||
<input type='checkbox' id='isBinary' style="display: inline-block; width: auto" /> <label for="isBinary"> Binary</label><br />
|
||||
</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
|
||||
// it might be a good idea to define these colors in a separate script file
|
||||
var CustomColors = [0x00ff00, 0x0000ff];
|
||||
|
||||
// create an instance of the plugin
|
||||
@@ -73,24 +74,28 @@
|
||||
console.log('Wrapper version', MolStarProteopediaWrapper.VERSION_MAJOR, MolStarProteopediaWrapper.VERSION_MINOR);
|
||||
|
||||
function $(id) { return document.getElementById(id); }
|
||||
|
||||
var pdbId = '1cbs', assemblyId= 'preferred';
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
|
||||
|
||||
var pdbId = '1cbs', assemblyId= 'preferred', isBinary = true;
|
||||
var url = 'https://www.ebi.ac.uk/pdbe/entry-files/download/' + pdbId + '.bcif'
|
||||
var format = 'cif';
|
||||
|
||||
|
||||
$('url').value = url;
|
||||
$('url').onchange = function (e) { url = e.target.value; }
|
||||
$('assemblyId').value = assemblyId;
|
||||
$('assemblyId').onchange = function (e) { assemblyId = e.target.value; }
|
||||
$('format').value = format;
|
||||
$('format').onchange = function (e) { format = e.target.value; }
|
||||
$('isBinary').checked = isBinary;
|
||||
$('isBinary').onchange = function (e) { isBinary = !!e.target.checked; };
|
||||
|
||||
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
|
||||
// var format = 'pdb';
|
||||
// var assemblyId = 'deposited';
|
||||
function loadAndSnapshot(params) {
|
||||
PluginWrapper.load(params).then(() => {
|
||||
setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);
|
||||
});
|
||||
}
|
||||
|
||||
var representationStyle = {
|
||||
sequence: { coloring: 'proteopedia-custom' }, // or just { }
|
||||
// sequence: { coloring: 'proteopedia-custom' }, // or just { }
|
||||
hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
|
||||
water: { hide: true },
|
||||
snfg3d: { hide: false }
|
||||
@@ -100,7 +105,7 @@
|
||||
customColorList: CustomColors
|
||||
});
|
||||
PluginWrapper.setBackground(0xffffff);
|
||||
PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId, representationStyle: representationStyle });
|
||||
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
|
||||
PluginWrapper.toggleSpin();
|
||||
|
||||
PluginWrapper.events.modelInfo.subscribe(function (info) {
|
||||
@@ -108,8 +113,8 @@
|
||||
listHetGroups(info);
|
||||
});
|
||||
|
||||
addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format }));
|
||||
addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
|
||||
addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
|
||||
addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
|
||||
|
||||
addSeparator();
|
||||
|
||||
@@ -135,14 +140,14 @@
|
||||
// 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();
|
||||
|
||||
addHeader('Animation');
|
||||
|
||||
// adjust this number to make the animation faster or slower
|
||||
// requires to "restart" the animation if changed
|
||||
PluginWrapper.animate.modelIndex.maxFPS = 30;
|
||||
PluginWrapper.animate.modelIndex.targetFps = 30;
|
||||
|
||||
addControl('Play To End', () => PluginWrapper.animate.modelIndex.onceForward());
|
||||
addControl('Play To Start', () => PluginWrapper.animate.modelIndex.onceBackward());
|
||||
@@ -168,21 +173,24 @@
|
||||
addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
|
||||
addControl('Remove', () => PluginWrapper.experimentalData.remove());
|
||||
|
||||
addSeparator();
|
||||
addSeparator();
|
||||
addHeader('State');
|
||||
|
||||
var snapshot;
|
||||
addControl('Set Snapshot', () => {
|
||||
snapshot = PluginWrapper.snapshot.get();
|
||||
// could use JSON.stringify(snapshot) and upload the data
|
||||
// const options = { data: true, behavior: false, animation: false, interactivity: false, canvas3d: false, camera: false, cameraTransition: false };
|
||||
snapshot = PluginWrapper.plugin.state.getSnapshot(/** options */);
|
||||
// console.log(JSON.stringify(snapshot, null, 2));
|
||||
});
|
||||
addControl('Restore Snapshot', () => {
|
||||
if (!snapshot) return;
|
||||
PluginWrapper.snapshot.set(snapshot);
|
||||
});
|
||||
addControl('Download Snapshot', () => {
|
||||
snapshot = PluginWrapper.snapshot.download();
|
||||
addControl('Download State', () => {
|
||||
PluginWrapper.snapshot.download('molj');
|
||||
});
|
||||
addControl('Download Session', () => {
|
||||
PluginWrapper.snapshot.download('molx');
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
@@ -218,7 +226,7 @@
|
||||
var l = document.createElement('button');
|
||||
l.innerText = r.name;
|
||||
l.onclick = function () {
|
||||
PluginWrapper.hetGroups.focusFirst(r.name);
|
||||
PluginWrapper.hetGroups.focusFirst(r.name, { doNotLabelWaters: true });
|
||||
};
|
||||
div.appendChild(l);
|
||||
});
|
||||
|
||||
@@ -5,34 +5,35 @@
|
||||
*/
|
||||
|
||||
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/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, 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 { 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 { 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 { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
|
||||
import { createPlugin } from '../../mol-plugin';
|
||||
import { DefaultPluginSpec } from '../../mol-plugin/spec';
|
||||
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/model-index';
|
||||
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { getFormattedTime } from '../../mol-util/date';
|
||||
require('../../mol-plugin-ui/skin/light.scss')
|
||||
import { download } from '../../mol-util/download';
|
||||
import { RxEventHelper } from '../../mol-util/rx-event-helper';
|
||||
import { EvolutionaryConservation } from './annotation';
|
||||
import { createProteopediaCustomTheme } from './coloring';
|
||||
import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
|
||||
import './index.html';
|
||||
import { volumeStreamingControls } from './ui/controls';
|
||||
require('../../mol-plugin-ui/skin/light.scss');
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
static VERSION_MAJOR = 5;
|
||||
static VERSION_MINOR = 0;
|
||||
static VERSION_MINOR = 5;
|
||||
|
||||
private _ev = RxEventHelper.create();
|
||||
|
||||
@@ -46,7 +47,7 @@ class MolStarProteopediaWrapper {
|
||||
customColorList?: number[]
|
||||
}) {
|
||||
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
|
||||
...DefaultPluginSpec,
|
||||
...DefaultPluginSpec(),
|
||||
animations: [
|
||||
AnimateModelIndex
|
||||
],
|
||||
@@ -73,8 +74,8 @@ class MolStarProteopediaWrapper {
|
||||
return this.plugin.state.data;
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string, isBinary: boolean) {
|
||||
return b.apply(StateTransforms.Data.Download, { url: Asset.Url(url), isBinary });
|
||||
}
|
||||
|
||||
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats) {
|
||||
@@ -89,11 +90,14 @@ class MolStarProteopediaWrapper {
|
||||
private structure(assemblyId: string) {
|
||||
const model = this.state.build().to(StateElements.Model);
|
||||
const props = {
|
||||
type: {
|
||||
type: assemblyId ? {
|
||||
name: 'assembly' as const,
|
||||
params: { id: assemblyId || 'deposited' }
|
||||
params: { id: assemblyId }
|
||||
} : {
|
||||
name: 'model' as const,
|
||||
params: { }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const s = model
|
||||
.apply(StateTransforms.Model.StructureFromModel, props, { ref: StateElements.Assembly });
|
||||
@@ -185,7 +189,7 @@ class MolStarProteopediaWrapper {
|
||||
const model = this.getObj<PluginStateObject.Molecule.Model>('model');
|
||||
if (!model) return;
|
||||
|
||||
const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly)
|
||||
const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly);
|
||||
this.events.modelInfo.next(info);
|
||||
return info;
|
||||
}
|
||||
@@ -194,8 +198,9 @@ class MolStarProteopediaWrapper {
|
||||
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
|
||||
}
|
||||
|
||||
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
|
||||
async load({ url, format = 'cif', assemblyId = 'deposited', representationStyle }: LoadParams) {
|
||||
private emptyLoadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
|
||||
private loadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
|
||||
async load({ url, format = 'cif', assemblyId = '', isBinary = false, representationStyle }: LoadParams) {
|
||||
let loadType: 'full' | 'update' = 'full';
|
||||
|
||||
const state = this.plugin.state.data;
|
||||
@@ -208,7 +213,7 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
if (loadType === 'full') {
|
||||
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
|
||||
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
|
||||
const modelTree = this.model(this.download(state.build().toRoot(), url, isBinary), format);
|
||||
await this.applyState(modelTree);
|
||||
const info = await this.doInfo(true);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
@@ -219,11 +224,14 @@ class MolStarProteopediaWrapper {
|
||||
const info = await this.doInfo(true);
|
||||
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
|
||||
const props = {
|
||||
type: {
|
||||
type: assemblyId ? {
|
||||
name: 'assembly' as const,
|
||||
params: { id: asmId || 'deposited' }
|
||||
params: { id: asmId }
|
||||
} : {
|
||||
name: 'model' as const,
|
||||
params: { }
|
||||
}
|
||||
}
|
||||
};
|
||||
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
|
||||
await this.applyState(tree);
|
||||
}
|
||||
@@ -231,7 +239,6 @@ class MolStarProteopediaWrapper {
|
||||
await this.updateStyle(representationStyle);
|
||||
|
||||
this.loadedParams = { url, format, assemblyId };
|
||||
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
|
||||
}
|
||||
|
||||
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
|
||||
@@ -249,9 +256,7 @@ class MolStarProteopediaWrapper {
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
|
||||
}
|
||||
|
||||
viewport = {
|
||||
@@ -264,36 +269,21 @@ class MolStarProteopediaWrapper {
|
||||
|
||||
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;
|
||||
// }
|
||||
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { })
|
||||
}
|
||||
|
||||
// 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] }
|
||||
// });
|
||||
// }
|
||||
private animateModelIndexTargetFps() {
|
||||
return Math.max(1, this.animate.modelIndex.targetFps | 0);
|
||||
}
|
||||
|
||||
animate = {
|
||||
modelIndex: {
|
||||
maxFPS: 8,
|
||||
onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
|
||||
onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
|
||||
palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
|
||||
loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
|
||||
stop: () => this.plugin.state.animation.stop()
|
||||
targetFps: 8,
|
||||
onceForward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'once', params: { direction: 'forward' } } }); },
|
||||
onceBackward: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'once', params: { direction: 'backward' } } }); },
|
||||
palindrome: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'palindrome', params: {} } }); },
|
||||
loop: () => { this.plugin.managers.animation.play(AnimateModelIndex, { duration: { name: 'computed', params: { targetFps: this.animateModelIndexTargetFps() } }, mode: { name: 'loop', params: { direction: 'forward' } } }); },
|
||||
stop: () => this.plugin.managers.animation.stop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,11 +294,6 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
const state = this.state;
|
||||
|
||||
// const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
|
||||
// for (const v of visuals) {
|
||||
// }
|
||||
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
@@ -353,12 +338,10 @@ class MolStarProteopediaWrapper {
|
||||
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
|
||||
PluginCommands.Camera.Reset(this.plugin, { });
|
||||
},
|
||||
focusFirst: async (compId: string) => {
|
||||
focusFirst: async (compId: string, options?: { hideLabels: boolean, doNotLabelWaters: boolean }) => {
|
||||
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.HetGroupFocusGroup);
|
||||
@@ -372,69 +355,66 @@ class MolStarProteopediaWrapper {
|
||||
const surroundings = MS.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true });
|
||||
|
||||
const group = update.to(StateElements.Assembly).group(StateTransforms.Misc.CreateGroup, { label: compId }, { ref: StateElements.HetGroupFocusGroup });
|
||||
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
|
||||
const coreSel = group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Core', expression: core }, { ref: StateElements.HetGroupFocus });
|
||||
|
||||
|
||||
coreSel.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'ball-and-stick'
|
||||
}));
|
||||
coreSel.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'label',
|
||||
typeParams: { level: 'element' }
|
||||
}), { tags: ['proteopedia-labels'] });
|
||||
|
||||
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: {
|
||||
// ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// background: true,
|
||||
// backgroundMargin: 0.2,
|
||||
// backgroundColor: ColorNames.snow,
|
||||
// backgroundOpacity: 0.9,
|
||||
// }
|
||||
// });
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'ball-and-stick',
|
||||
color: 'uniform', colorParams: { value: ColorNames.gray },
|
||||
size: 'uniform', sizeParams: { value: 0.33 }
|
||||
}));
|
||||
|
||||
if (!options?.hideLabels) {
|
||||
// Labels
|
||||
const waters = MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.entityType(), 'water']),
|
||||
});
|
||||
const exclude = options?.doNotLabelWaters ? MS.struct.combinator.merge([core, waters]) : core;
|
||||
const onlySurroundings = MS.struct.modifier.exceptBy({ 0: surroundings, by: exclude });
|
||||
|
||||
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Surroundings (only)', expression: onlySurroundings })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, asm.data, {
|
||||
type: 'label',
|
||||
typeParams: { level: 'residue' }
|
||||
}), { tags: ['proteopedia-labels'] }); // the tag can later be used to toggle the labels
|
||||
}
|
||||
|
||||
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;
|
||||
// const asmCenter = asm.boundary.sphere.center;
|
||||
// const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
|
||||
// Vec3.normalize(position, position);
|
||||
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
|
||||
const radius = Math.max(sphere.radius, 5)
|
||||
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 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'
|
||||
});
|
||||
}
|
||||
|
||||
snapshot = {
|
||||
get: () => {
|
||||
return this.plugin.state.getSnapshot();
|
||||
get: (params?: PluginState.SnapshotParams) => {
|
||||
return this.plugin.state.getSnapshot(params);
|
||||
},
|
||||
set: (snapshot: PluginState.Snapshot) => {
|
||||
return this.plugin.state.setSnapshot(snapshot);
|
||||
},
|
||||
download: () => {
|
||||
const json = JSON.stringify(this.plugin.state.getSnapshot(), null, 2);
|
||||
const blob = new Blob([json], {type : 'application/json;charset=utf-8'});
|
||||
download(blob, `mol-star_state_${(name || getFormattedTime())}.json`)
|
||||
download: async (type: 'molj' | 'molx' = 'molj', params?: PluginState.SnapshotParams) => {
|
||||
const data = await this.plugin.managers.snapshot.serialize({ type, params });
|
||||
download(data, `mol-star_state_${getFormattedTime()}.${type}`);
|
||||
},
|
||||
fetch: async (url: string) => {
|
||||
fetch: async (url: string, type: 'molj' | 'molx' = 'molj') => {
|
||||
try {
|
||||
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
|
||||
// TODO: is this OK to test for snapshots from server?
|
||||
await this.plugin.state.setSnapshot(snapshot?.data?.entries?.[0]?.snapshot || snapshot);
|
||||
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
|
||||
this.loadedParams = { ...this.emptyLoadedParams };
|
||||
return await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import { StateElements } from '../helpers';
|
||||
|
||||
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
|
||||
ReactDOM.render(<PluginContextContainer plugin={plugin}>
|
||||
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
|
||||
</PluginContextContainer>,
|
||||
parent);
|
||||
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
|
||||
</PluginContextContainer>, parent);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Task, Progress, Scheduler, MultistepTask, chunkedSubtask } from '../mol-task'
|
||||
import { Task, Progress, Scheduler, MultistepTask, chunkedSubtask } from '../mol-task';
|
||||
import { now } from '../mol-util/now';
|
||||
|
||||
export async function test1() {
|
||||
@@ -85,19 +85,19 @@ export const ms = MultistepTask('ms-task', ['step 1', 'step 2', 'step 3'], async
|
||||
await step(0);
|
||||
|
||||
const child = Task.create('chunked', async ctx => {
|
||||
const s = await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 125 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
|
||||
const s = await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 125 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p));
|
||||
return s.i;
|
||||
});
|
||||
|
||||
await child.runAsChild(ctx);
|
||||
await Scheduler.delay(250);
|
||||
await step(1);
|
||||
await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 80 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
|
||||
await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 80 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p));
|
||||
await Scheduler.delay(250);
|
||||
await step(2);
|
||||
await Scheduler.delay(250);
|
||||
return p.i + 3;
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
export function abortingObserver(p: Progress) {
|
||||
|
||||
214
src/extensions/alpha-orbitals/_spec/collocation.spec.ts
Normal file
214
src/extensions/alpha-orbitals/_spec/collocation.spec.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Box3D } from '../../../mol-math/geometry';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { RuntimeContext } from '../../../mol-task';
|
||||
import { sphericalCollocation } from '../collocation';
|
||||
import { Basis, CubeGridInfo } from '../data-model';
|
||||
|
||||
describe('alpha-orbitals-cubes', () => {
|
||||
it('water', async () => {
|
||||
const grid: CubeGridInfo = {
|
||||
params: {
|
||||
basis: _testBasis,
|
||||
cutoffThreshold: 0,
|
||||
sphericalOrder: 'cca-reverse',
|
||||
boxExpand: 0,
|
||||
gridSpacing: []
|
||||
},
|
||||
box: Box3D.create(Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)),
|
||||
delta: Vec3.create(2, 2, 2),
|
||||
dimensions: Vec3.create(2, 2, 2),
|
||||
npoints: 8,
|
||||
size: Vec3.create(2, 2, 2)
|
||||
};
|
||||
|
||||
const matrix = await sphericalCollocation(grid, {
|
||||
energy: 0,
|
||||
occupancy: 0,
|
||||
alpha: [-2.2623991420609075e-16, 0.6360205395000592, 0.6672122399886391, -0.3876927909355508, -1.6780131293332933e-16, 2.844782862661151e-16, 4.977960694176068e-19, -2.3945919908996803e-16]
|
||||
}, RuntimeContext.Synchronous);
|
||||
|
||||
const expected = [-0.1451730622877498, 0.06479453956039086, -0.2777738736440713, -0.057116584776260436, 0.05929916178822645, 0.2742903371231049, -0.07221698722165386, 0.15389180241391376];
|
||||
|
||||
expect(matrix.length).toBe(expected.length);
|
||||
|
||||
for (let i = 0; i < matrix.length; i++) {
|
||||
expect(Math.abs(matrix[i] - expected[i]) < 1e-6).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const _testBasis: Basis = {
|
||||
'atoms': [
|
||||
{
|
||||
'center': [
|
||||
0.025886090588624934,
|
||||
0.019164790004065606,
|
||||
-0.013539970104105408
|
||||
] as Vec3,
|
||||
'shells': [
|
||||
{
|
||||
'angularMomentum': [0],
|
||||
'coefficients': [
|
||||
[
|
||||
-0.004151277818987536,
|
||||
-0.02067024147993795,
|
||||
-0.05150303336984537,
|
||||
0.33462711739899537,
|
||||
0.5621061300983125,
|
||||
0.17129946969948573
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
152.28769660788095,
|
||||
27.928015215973073,
|
||||
7.848374792384515,
|
||||
1.1223350202705642,
|
||||
0.5093846587907856,
|
||||
0.24292266532510307
|
||||
]
|
||||
},
|
||||
{
|
||||
'angularMomentum': [1],
|
||||
'coefficients': [
|
||||
[
|
||||
0.007924233646294425,
|
||||
0.051441048251911314,
|
||||
0.18984000600705359,
|
||||
0.4049863191150474,
|
||||
0.40123628611490797,
|
||||
0.1051855189039082
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
27.203421487167727,
|
||||
7.09409912597673,
|
||||
2.5383362605345954,
|
||||
1.0610730767843852,
|
||||
0.4851948916410433,
|
||||
0.22938302550642545
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'center': [
|
||||
0.5082729578468134,
|
||||
1.6880351220025265,
|
||||
0.4963443067810461
|
||||
] as Vec3,
|
||||
'shells': [
|
||||
{
|
||||
'angularMomentum': [0],
|
||||
'coefficients': [
|
||||
[
|
||||
0.009163596280542963,
|
||||
0.04936149294292479,
|
||||
0.16853830490998634,
|
||||
0.37056279972195677,
|
||||
0.4164915298246781,
|
||||
0.13033408410772263
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
33.710073211949485,
|
||||
6.180705022740464,
|
||||
1.7291385346152253,
|
||||
0.5940057549921978,
|
||||
0.2306698170449518,
|
||||
0.09500256906284119
|
||||
]
|
||||
},
|
||||
{
|
||||
'angularMomentum': [0],
|
||||
'coefficients': [
|
||||
[
|
||||
-0.32279868167000036,
|
||||
3.209629817295221,
|
||||
2.4672629224617935,
|
||||
-0.048487066612842224,
|
||||
-0.2611850111200143,
|
||||
-0.8917817597810863,
|
||||
-1.9607480081275706,
|
||||
-2.203769342520311,
|
||||
-0.6896328935259993
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
10.256286070314905,
|
||||
0.6227965325875392,
|
||||
0.2391007667853915,
|
||||
33.710073211949485,
|
||||
6.180705022740464,
|
||||
1.7291385346152253,
|
||||
0.5940057549921978,
|
||||
0.2306698170449518,
|
||||
0.09500256906284119
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'center': [
|
||||
1.1367367844436005,
|
||||
-0.47018519422670163,
|
||||
-1.356802622574504
|
||||
] as Vec3,
|
||||
'shells': [
|
||||
{
|
||||
'angularMomentum': [0],
|
||||
'coefficients': [
|
||||
[
|
||||
0.009163596280542963,
|
||||
0.04936149294292479,
|
||||
0.16853830490998634,
|
||||
0.37056279972195677,
|
||||
0.4164915298246781,
|
||||
0.13033408410772263
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
33.710073211949485,
|
||||
6.180705022740464,
|
||||
1.7291385346152253,
|
||||
0.5940057549921978,
|
||||
0.2306698170449518,
|
||||
0.09500256906284119
|
||||
]
|
||||
},
|
||||
{
|
||||
'angularMomentum': [0],
|
||||
'coefficients': [
|
||||
[
|
||||
-0.32279868167000036,
|
||||
3.209629817295221,
|
||||
2.4672629224617935,
|
||||
-0.048487066612842224,
|
||||
-0.2611850111200143,
|
||||
-0.8917817597810863,
|
||||
-1.9607480081275706,
|
||||
-2.203769342520311,
|
||||
-0.6896328935259993
|
||||
]
|
||||
],
|
||||
'exponents': [
|
||||
10.256286070314905,
|
||||
0.6227965325875392,
|
||||
0.2391007667853915,
|
||||
33.710073211949485,
|
||||
6.180705022740464,
|
||||
1.7291385346152253,
|
||||
0.5940057549921978,
|
||||
0.2306698170449518,
|
||||
0.09500256906284119
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
162
src/extensions/alpha-orbitals/collocation.ts
Normal file
162
src/extensions/alpha-orbitals/collocation.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Inspired by https://github.com/dgasmith/gau2grid.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { arrayMin } from '../../mol-util/array';
|
||||
import { AlphaOrbital, CubeGridInfo } from './data-model';
|
||||
import { normalizeBasicOrder, SphericalFunctions } from './spherical-functions';
|
||||
|
||||
export async function sphericalCollocation(
|
||||
grid: CubeGridInfo,
|
||||
orbital: AlphaOrbital,
|
||||
taskCtx: RuntimeContext
|
||||
) {
|
||||
const { basis, sphericalOrder, cutoffThreshold } = grid.params;
|
||||
let baseCount = 0;
|
||||
|
||||
for (const atom of basis.atoms) {
|
||||
for (const shell of atom.shells) {
|
||||
for (const L of shell.angularMomentum) {
|
||||
if (L > 4) {
|
||||
// TODO: will L > 4 be required? Would need to precompute more functions in that case.
|
||||
throw new Error('Angular momentum L > 4 not supported.');
|
||||
}
|
||||
|
||||
baseCount += 2 * L + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const matrix = new Float32Array(grid.npoints);
|
||||
|
||||
let baseIndex = 0;
|
||||
for (const atom of basis.atoms) {
|
||||
for (const shell of atom.shells) {
|
||||
let amIndex = 0;
|
||||
for (const L of shell.angularMomentum) {
|
||||
const alpha = normalizeBasicOrder(
|
||||
L,
|
||||
orbital.alpha.slice(baseIndex, baseIndex + 2 * L + 1),
|
||||
sphericalOrder
|
||||
);
|
||||
baseIndex += 2 * L + 1;
|
||||
|
||||
collocationBasis(
|
||||
matrix,
|
||||
grid,
|
||||
L,
|
||||
shell.coefficients[amIndex++],
|
||||
shell.exponents,
|
||||
atom.center,
|
||||
cutoffThreshold,
|
||||
alpha
|
||||
);
|
||||
|
||||
if (taskCtx.shouldUpdate) {
|
||||
await taskCtx.update({
|
||||
message: 'Computing...',
|
||||
current: baseIndex,
|
||||
max: baseCount,
|
||||
isIndeterminate: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
function collocationBasis(
|
||||
matrix: Float32Array,
|
||||
grid: CubeGridInfo,
|
||||
L: number,
|
||||
coefficients: number[],
|
||||
exponents: number[],
|
||||
center: Vec3,
|
||||
cutoffThreshold: number,
|
||||
alpha: number[]
|
||||
) {
|
||||
const ncoeff = exponents.length;
|
||||
const sphericalFunc = SphericalFunctions[L];
|
||||
|
||||
const cx = center[0],
|
||||
cy = center[1],
|
||||
cz = center[2];
|
||||
const ny = grid.dimensions[1],
|
||||
nz = grid.dimensions[2];
|
||||
const gdx = grid.delta[0],
|
||||
gdy = grid.delta[1],
|
||||
gdz = grid.delta[2];
|
||||
const sx = grid.box.min[0],
|
||||
sy = grid.box.min[1],
|
||||
sz = grid.box.min[2];
|
||||
|
||||
const cutoffRadius =
|
||||
cutoffThreshold > 0
|
||||
? Math.sqrt(-Math.log(cutoffThreshold) / arrayMin(exponents))
|
||||
: 10000;
|
||||
const cutoffSquared = cutoffRadius * cutoffRadius;
|
||||
|
||||
const radiusBox = getRadiusBox(grid, center, cutoffRadius);
|
||||
const iMin = radiusBox[0][0],
|
||||
jMin = radiusBox[0][1],
|
||||
kMin = radiusBox[0][2];
|
||||
const iMax = radiusBox[1][0],
|
||||
jMax = radiusBox[1][1],
|
||||
kMax = radiusBox[1][2];
|
||||
|
||||
for (let i = iMin; i <= iMax; i++) {
|
||||
const x = sx + gdx * i - cx;
|
||||
const oX = i * ny * nz;
|
||||
|
||||
for (let j = jMin; j <= jMax; j++) {
|
||||
const y = sy + gdy * j - cy;
|
||||
const oY = oX + j * nz;
|
||||
for (let k = kMin; k <= kMax; k++) {
|
||||
const z = sz + gdz * k - cz;
|
||||
const R2 = x * x + y * y + z * z;
|
||||
|
||||
if (R2 > cutoffSquared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let gaussianSum = 0;
|
||||
for (let c = 0; c < ncoeff; c++) {
|
||||
gaussianSum +=
|
||||
coefficients[c] * Math.exp(-exponents[c] * R2);
|
||||
}
|
||||
|
||||
const sphericalSum = L === 0 ? alpha[0] : sphericalFunc(alpha, x, y, z);
|
||||
|
||||
|
||||
matrix[k + oY] += gaussianSum * sphericalSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRadiusBox(grid: CubeGridInfo, center: Vec3, radius: number) {
|
||||
const r = Vec3.create(radius, radius, radius);
|
||||
const min = Vec3.scaleAndAdd(Vec3(), center, r, -1);
|
||||
const max = Vec3.add(Vec3(), center, r);
|
||||
|
||||
Vec3.sub(min, min, grid.box.min);
|
||||
Vec3.sub(max, max, grid.box.min);
|
||||
|
||||
Vec3.div(min, min, grid.delta);
|
||||
Vec3.floor(min, min);
|
||||
Vec3.max(min, min, Vec3());
|
||||
|
||||
Vec3.div(max, max, grid.delta);
|
||||
Vec3.ceil(max, max);
|
||||
Vec3.min(max, max, Vec3.subScalar(Vec3(), grid.dimensions, 1));
|
||||
|
||||
return [min, max];
|
||||
}
|
||||
131
src/extensions/alpha-orbitals/data-model.ts
Normal file
131
src/extensions/alpha-orbitals/data-model.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Mat4, Tensor, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { Grid } from '../../mol-model/volume';
|
||||
import { SphericalBasisOrder } from './spherical-functions';
|
||||
import { Box3D, RegularGrid3d } from '../../mol-math/geometry';
|
||||
import { arrayMin, arrayMax, arrayRms, arrayMean } from '../../mol-util/array';
|
||||
|
||||
// Note: generally contracted gaussians are currently not supported.
|
||||
export interface SphericalElectronShell {
|
||||
exponents: number[];
|
||||
angularMomentum: number[];
|
||||
// number[] for each angular momentum
|
||||
coefficients: number[][];
|
||||
}
|
||||
|
||||
export interface Basis {
|
||||
atoms: {
|
||||
// in Bohr units!
|
||||
center: Vec3;
|
||||
shells: SphericalElectronShell[];
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface AlphaOrbital {
|
||||
energy: number;
|
||||
occupancy: number;
|
||||
alpha: number[];
|
||||
}
|
||||
|
||||
export interface CubeGridComputationParams {
|
||||
basis: Basis;
|
||||
/**
|
||||
* for each electron shell compute a cutoff radius as
|
||||
* const cutoffRadius = Math.sqrt(-Math.log(cutoffThreshold) / arrayMin(exponents));
|
||||
*/
|
||||
cutoffThreshold: number;
|
||||
sphericalOrder: SphericalBasisOrder;
|
||||
boxExpand: number;
|
||||
gridSpacing: number | [atomCountThreshold: number, spacing: number][];
|
||||
doNotComputeIsovalues?: boolean;
|
||||
}
|
||||
|
||||
export interface CubeGridInfo {
|
||||
params: CubeGridComputationParams;
|
||||
dimensions: Vec3;
|
||||
box: Box3D;
|
||||
size: Vec3;
|
||||
npoints: number;
|
||||
delta: Vec3;
|
||||
}
|
||||
|
||||
export interface CubeGrid {
|
||||
grid: Grid;
|
||||
isovalues?: { negative?: number; positive?: number };
|
||||
}
|
||||
|
||||
export function initCubeGrid(params: CubeGridComputationParams): CubeGridInfo {
|
||||
const geometry = params.basis.atoms.map(a => a.center);
|
||||
const { gridSpacing: spacing, boxExpand: expand } = params;
|
||||
|
||||
const count = geometry.length;
|
||||
const box = Box3D.expand(
|
||||
Box3D(),
|
||||
Box3D.fromVec3Array(Box3D(), geometry),
|
||||
Vec3.create(expand, expand, expand)
|
||||
);
|
||||
const size = Box3D.size(Vec3(), box);
|
||||
|
||||
const spacingThresholds =
|
||||
typeof spacing === 'number' ? [[0, spacing]] : [...spacing];
|
||||
spacingThresholds.sort((a, b) => b[0] - a[0]);
|
||||
|
||||
let s = 0.4;
|
||||
for (let i = 0; i <= spacingThresholds.length; i++) {
|
||||
s = spacingThresholds[i][1];
|
||||
if (spacingThresholds[i][0] <= count) break;
|
||||
}
|
||||
|
||||
const dimensions = Vec3.ceil(Vec3(), Vec3.scale(Vec3(), size, 1 / s));
|
||||
|
||||
return {
|
||||
params,
|
||||
box,
|
||||
dimensions,
|
||||
size,
|
||||
npoints: dimensions[0] * dimensions[1] * dimensions[2],
|
||||
delta: Vec3.div(Vec3(), size, Vec3.subScalar(Vec3(), dimensions, 1)),
|
||||
};
|
||||
}
|
||||
|
||||
const BohrToAngstromFactor = 0.529177210859;
|
||||
|
||||
export function createGrid(gridInfo: RegularGrid3d, values: Float32Array, axisOrder: number[]) {
|
||||
const boxSize = Box3D.size(Vec3(), gridInfo.box);
|
||||
const boxOrigin = Vec3.clone(gridInfo.box.min);
|
||||
|
||||
Vec3.scale(boxSize, boxSize, BohrToAngstromFactor);
|
||||
Vec3.scale(boxOrigin, boxOrigin, BohrToAngstromFactor);
|
||||
|
||||
const scale = Mat4.fromScaling(
|
||||
Mat4(),
|
||||
Vec3.div(
|
||||
Vec3(),
|
||||
boxSize,
|
||||
Vec3.sub(Vec3(), gridInfo.dimensions, Vec3.create(1, 1, 1))
|
||||
)
|
||||
);
|
||||
const translate = Mat4.fromTranslation(Mat4(), boxOrigin);
|
||||
const matrix = Mat4.mul(Mat4(), translate, scale);
|
||||
|
||||
const grid: Grid = {
|
||||
transform: { kind: 'matrix', matrix },
|
||||
cells: Tensor.create(
|
||||
Tensor.Space(gridInfo.dimensions, axisOrder, Float32Array),
|
||||
(values as any) as Tensor.Data
|
||||
),
|
||||
stats: {
|
||||
min: arrayMin(values),
|
||||
max: arrayMax(values),
|
||||
mean: arrayMean(values),
|
||||
sigma: arrayRms(values),
|
||||
},
|
||||
};
|
||||
|
||||
return grid;
|
||||
}
|
||||
124
src/extensions/alpha-orbitals/density.ts
Normal file
124
src/extensions/alpha-orbitals/density.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { sortArray } from '../../mol-data/util';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Task } from '../../mol-task';
|
||||
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
|
||||
import { gpuComputeAlphaOrbitalsDensityGridValues } from './gpu/compute';
|
||||
|
||||
export function createSphericalCollocationDensityGrid(
|
||||
params: CubeGridComputationParams, orbitals: AlphaOrbital[], webgl?: WebGLContext
|
||||
): Task<CubeGrid> {
|
||||
return Task.create('Spherical Collocation Grid', async (ctx) => {
|
||||
const cubeGrid = initCubeGrid(params);
|
||||
|
||||
let matrix: Float32Array;
|
||||
if (canComputeGrid3dOnGPU(webgl)) {
|
||||
// console.time('gpu');
|
||||
matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl!, cubeGrid, orbitals);
|
||||
// console.timeEnd('gpu');
|
||||
} else {
|
||||
throw new Error('Missing OES_texture_float WebGL extension.');
|
||||
}
|
||||
|
||||
const grid = createGrid(cubeGrid, matrix, [0, 1, 2]);
|
||||
let isovalues: { negative?: number, positive?: number } | undefined;
|
||||
|
||||
if (!params.doNotComputeIsovalues) {
|
||||
isovalues = computeDensityIsocontourValues(matrix, 0.85);
|
||||
}
|
||||
|
||||
return { grid, isovalues };
|
||||
});
|
||||
}
|
||||
|
||||
export function computeDensityIsocontourValues(input: Float32Array, cumulativeThreshold: number) {
|
||||
let weightSum = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = Math.abs(v);
|
||||
weightSum += w;
|
||||
}
|
||||
const avgWeight = weightSum / input.length;
|
||||
let minWeight = 3 * avgWeight;
|
||||
|
||||
// do not try to identify isovalues for degenerate data
|
||||
// e.g. all values are almost same
|
||||
if (Math.abs(avgWeight - input[0] * input[0]) < 1e-5) {
|
||||
return { negative: void 0, positive: void 0 };
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
while (true) {
|
||||
let csum = 0;
|
||||
size = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = Math.abs(v);
|
||||
if (w >= minWeight) {
|
||||
csum += w;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
if (csum / weightSum > cumulativeThreshold) {
|
||||
break;
|
||||
}
|
||||
|
||||
minWeight -= avgWeight;
|
||||
}
|
||||
|
||||
const values = new Float32Array(size);
|
||||
const weights = new Float32Array(size);
|
||||
const indices = new Int32Array(size);
|
||||
|
||||
let o = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = Math.abs(v);
|
||||
if (w >= minWeight) {
|
||||
values[o] = v;
|
||||
weights[o] = w;
|
||||
indices[o] = o;
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
sortArray(
|
||||
indices,
|
||||
(indices, i, j) => weights[indices[j]] - weights[indices[i]]
|
||||
);
|
||||
|
||||
let cweight = 0,
|
||||
cutoffIndex = 0;
|
||||
for (let i = 0; i < size; i++) {
|
||||
cweight += weights[indices[i]];
|
||||
|
||||
if (cweight / weightSum >= cumulativeThreshold) {
|
||||
cutoffIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let positive = Number.POSITIVE_INFINITY,
|
||||
negative = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < cutoffIndex; i++) {
|
||||
const v = values[indices[i]];
|
||||
if (v > 0) {
|
||||
if (v < positive) positive = v;
|
||||
} else if (v < 0) {
|
||||
if (v > negative) negative = v;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
negative: negative !== Number.NEGATIVE_INFINITY ? negative : void 0,
|
||||
positive: positive !== Number.POSITIVE_INFINITY ? positive : void 0,
|
||||
};
|
||||
}
|
||||
170
src/extensions/alpha-orbitals/gpu/compute.ts
Normal file
170
src/extensions/alpha-orbitals/gpu/compute.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { createGrid3dComputeRenderable } from '../../../mol-gl/compute/grid3d';
|
||||
import { TextureSpec, UnboxedValues, UniformSpec } from '../../../mol-gl/renderable/schema';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { RuntimeContext } from '../../../mol-task';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { arrayMin } from '../../../mol-util/array';
|
||||
import { AlphaOrbital, Basis, CubeGridInfo } from '../data-model';
|
||||
import { normalizeBasicOrder, SphericalBasisOrder } from '../spherical-functions';
|
||||
import { MAIN, UTILS } from './shader.frag';
|
||||
|
||||
const Schema = {
|
||||
tCenters: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
|
||||
tInfo: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
|
||||
tCoeff: TextureSpec('image-float32', 'rgb', 'float', 'nearest'),
|
||||
tAlpha: TextureSpec('image-float32', 'alpha', 'float', 'nearest'),
|
||||
uNCenters: UniformSpec('i'),
|
||||
uNAlpha: UniformSpec('i'),
|
||||
uNCoeff: UniformSpec('i'),
|
||||
uMaxCoeffs: UniformSpec('i'),
|
||||
};
|
||||
|
||||
const Orbitals = createGrid3dComputeRenderable({
|
||||
schema: Schema,
|
||||
loopBounds: ['uNCenters', 'uMaxCoeffs'],
|
||||
mainCode: MAIN,
|
||||
utilCode: UTILS,
|
||||
returnCode: 'v',
|
||||
values(params: { grid: CubeGridInfo, orbital: AlphaOrbital }) {
|
||||
return createTextureData(params.grid, params.orbital);
|
||||
}
|
||||
});
|
||||
|
||||
const Density = createGrid3dComputeRenderable({
|
||||
schema: {
|
||||
...Schema,
|
||||
uOccupancy: UniformSpec('f'),
|
||||
},
|
||||
loopBounds: ['uNCenters', 'uMaxCoeffs'],
|
||||
mainCode: MAIN,
|
||||
utilCode: UTILS,
|
||||
returnCode: 'current + uOccupancy * v * v',
|
||||
values(params: { grid: CubeGridInfo, orbitals: AlphaOrbital[] }) {
|
||||
return {
|
||||
...createTextureData(params.grid, params.orbitals[0]),
|
||||
uOccupancy: 0
|
||||
};
|
||||
},
|
||||
cumulative: {
|
||||
states(params: { grid: CubeGridInfo, orbitals: AlphaOrbital[] }) {
|
||||
return params.orbitals.filter(o => o.occupancy !== 0);
|
||||
},
|
||||
update({ grid }, state: AlphaOrbital, values) {
|
||||
const alpha = getNormalizedAlpha(grid.params.basis, state.alpha, grid.params.sphericalOrder);
|
||||
ValueCell.updateIfChanged(values.uOccupancy, state.occupancy);
|
||||
ValueCell.update(values.tAlpha, { width: alpha.length, height: 1, array: alpha });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function gpuComputeAlphaOrbitalsGridValues(ctx: RuntimeContext, webgl: WebGLContext, grid: CubeGridInfo, orbital: AlphaOrbital) {
|
||||
return Orbitals(ctx, webgl, grid, { grid, orbital });
|
||||
}
|
||||
|
||||
export function gpuComputeAlphaOrbitalsDensityGridValues(ctx: RuntimeContext, webgl: WebGLContext, grid: CubeGridInfo, orbitals: AlphaOrbital[]) {
|
||||
return Density(ctx, webgl, grid, { grid, orbitals });
|
||||
}
|
||||
|
||||
function getNormalizedAlpha(basis: Basis, alphaOrbitals: number[], sphericalOrder: SphericalBasisOrder) {
|
||||
const alpha = new Float32Array(alphaOrbitals.length);
|
||||
|
||||
let aO = 0;
|
||||
for (const atom of basis.atoms) {
|
||||
for (const shell of atom.shells) {
|
||||
for (const L of shell.angularMomentum) {
|
||||
const a0 = normalizeBasicOrder(L, alphaOrbitals.slice(aO, aO + 2 * L + 1), sphericalOrder);
|
||||
for (let i = 0; i < a0.length; i++) alpha[aO + i] = a0[i];
|
||||
aO += 2 * L + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
function createTextureData(grid: CubeGridInfo, orbital: AlphaOrbital): UnboxedValues<typeof Schema> {
|
||||
const { basis, sphericalOrder, cutoffThreshold } = grid.params;
|
||||
|
||||
let centerCount = 0;
|
||||
let baseCount = 0;
|
||||
let coeffCount = 0;
|
||||
for (const atom of basis.atoms) {
|
||||
for (const shell of atom.shells) {
|
||||
for (const L of shell.angularMomentum) {
|
||||
if (L > 4) {
|
||||
// TODO: will L > 4 be required? Would need to precompute more functions in that case.
|
||||
throw new Error('Angular momentum L > 4 not supported.');
|
||||
}
|
||||
|
||||
centerCount++;
|
||||
baseCount += 2 * L + 1;
|
||||
coeffCount += shell.exponents.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const centers = new Float32Array(4 * centerCount);
|
||||
// L, alpha_offset, coeff_offset_start, coeff_offset_end
|
||||
const info = new Float32Array(4 * centerCount);
|
||||
const alpha = new Float32Array(baseCount);
|
||||
const coeff = new Float32Array(3 * coeffCount);
|
||||
|
||||
let maxCoeffs = 0;
|
||||
let cO = 0, aO = 0, coeffO = 0;
|
||||
for (const atom of basis.atoms) {
|
||||
for (const shell of atom.shells) {
|
||||
|
||||
let amIndex = 0;
|
||||
for (const L of shell.angularMomentum) {
|
||||
const a0 = normalizeBasicOrder(L, orbital.alpha.slice(aO, aO + 2 * L + 1), sphericalOrder);
|
||||
|
||||
const cutoffRadius = cutoffThreshold > 0
|
||||
? Math.sqrt(-Math.log(cutoffThreshold) / arrayMin(shell.exponents))
|
||||
: 10000;
|
||||
|
||||
centers[4 * cO + 0] = atom.center[0];
|
||||
centers[4 * cO + 1] = atom.center[1];
|
||||
centers[4 * cO + 2] = atom.center[2];
|
||||
centers[4 * cO + 3] = cutoffRadius * cutoffRadius;
|
||||
|
||||
info[4 * cO + 0] = L;
|
||||
info[4 * cO + 1] = aO;
|
||||
info[4 * cO + 2] = coeffO;
|
||||
info[4 * cO + 3] = coeffO + shell.exponents.length;
|
||||
|
||||
for (let i = 0; i < a0.length; i++) alpha[aO + i] = a0[i];
|
||||
|
||||
const c0 = shell.coefficients[amIndex++];
|
||||
for (let i = 0; i < shell.exponents.length; i++) {
|
||||
coeff[3 * (coeffO + i) + 0] = c0[i];
|
||||
coeff[3 * (coeffO + i) + 1] = shell.exponents[i];
|
||||
}
|
||||
|
||||
if (c0.length > maxCoeffs) {
|
||||
maxCoeffs = c0.length;
|
||||
}
|
||||
|
||||
cO++;
|
||||
aO += 2 * L + 1;
|
||||
coeffO += shell.exponents.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uNCenters: centerCount,
|
||||
uNAlpha: baseCount,
|
||||
uNCoeff: coeffCount,
|
||||
uMaxCoeffs: maxCoeffs,
|
||||
tCenters: { width: centerCount, height: 1, array: centers },
|
||||
tInfo: { width: centerCount, height: 1, array: info },
|
||||
tCoeff: { width: coeffCount, height: 1, array: coeff },
|
||||
tAlpha: { width: baseCount, height: 1, array: alpha },
|
||||
};
|
||||
}
|
||||
145
src/extensions/alpha-orbitals/gpu/shader.frag.ts
Normal file
145
src/extensions/alpha-orbitals/gpu/shader.frag.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
export const UTILS = `
|
||||
float L1(vec3 p, float a0, float a1, float a2) {
|
||||
return a0 * p.z + a1 * p.x + a2 * p.y;
|
||||
}
|
||||
|
||||
float L2(vec3 p, float a0, float a1, float a2, float a3, float a4) {
|
||||
float x = p.x, y = p.y, z = p.z;
|
||||
float xx = x * x, yy = y * y, zz = z * z;
|
||||
return (
|
||||
a0 * (-0.5 * xx - 0.5 * yy + zz) +
|
||||
a1 * (1.7320508075688772 * x * z) +
|
||||
a2 * (1.7320508075688772 * y * z) +
|
||||
a3 * (0.8660254037844386 * xx - 0.8660254037844386 * yy) +
|
||||
a4 * (1.7320508075688772 * x * y)
|
||||
);
|
||||
}
|
||||
|
||||
float L3(vec3 p, float a0, float a1, float a2, float a3, float a4, float a5, float a6) {
|
||||
float x = p.x, y = p.y, z = p.z;
|
||||
float xx = x * x, yy = y * y, zz = z * z;
|
||||
float xxx = xx * x, yyy = yy * y, zzz = zz * z;
|
||||
return (
|
||||
a0 * (-1.5 * xx * z - 1.5 * yy * z + zzz) +
|
||||
a1 * (-0.6123724356957945 * xxx - 0.6123724356957945 * x * yy + 2.449489742783178 * x * zz) +
|
||||
a2 * (-0.6123724356957945 * xx * y - 0.6123724356957945 * yyy + 2.449489742783178 * y * zz) +
|
||||
a3 * (1.9364916731037085 * xx * z - 1.9364916731037085 * yy * z) +
|
||||
a4 * (3.872983346207417 * x * y * z) +
|
||||
a5 * (0.7905694150420949 * xxx - 2.3717082451262845 * x * yy) +
|
||||
a6 * (2.3717082451262845 * xx * y - 0.7905694150420949 * yyy)
|
||||
);
|
||||
}
|
||||
|
||||
float L4(vec3 p, float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8) {
|
||||
float x = p.x, y = p.y, z = p.z;
|
||||
float xx = x * x, yy = y * y, zz = z * z;
|
||||
float xxx = xx * x, yyy = yy * y, zzz = zz * z;
|
||||
float xxxx = xxx * x, yyyy = yyy * y, zzzz = zzz * z;
|
||||
return (
|
||||
a0 * (0.375 * xxxx + 0.75 * xx * yy + 0.375 * yyyy - 3.0 * xx * zz - 3.0 * yy * zz + zzzz) +
|
||||
a1 * (-2.3717082451262845 * xxx * z - 2.3717082451262845 * x * yy * z + 3.1622776601683795 * x * zzz) +
|
||||
a2 * (-2.3717082451262845 * xx * y * z - 2.3717082451262845 * yyy * z + 3.1622776601683795 * y * zzz) +
|
||||
a3 * (-0.5590169943749475 * xxxx + 0.5590169943749475 * yyyy + 3.3541019662496847 * xx * zz - 3.3541019662496847 * yy * zz) +
|
||||
a4 * (-1.118033988749895 * xxx * y - 1.118033988749895 * x * yyy + 6.708203932499369 * x * y * zz) +
|
||||
a5 * (2.091650066335189 * xxx * z + -6.274950199005566 * x * yy * z) +
|
||||
a6 * (6.274950199005566 * xx * y * z + -2.091650066335189 * yyy * z) +
|
||||
a7 * (0.739509972887452 * xxxx - 4.437059837324712 * xx * yy + 0.739509972887452 * yyyy) +
|
||||
a8 * (2.958039891549808 * xxx * y + -2.958039891549808 * x * yyy)
|
||||
);
|
||||
}
|
||||
|
||||
float alpha(float offset, float f) {
|
||||
#ifdef WEBGL1
|
||||
// in webgl1, the value is in the alpha channel!
|
||||
return texture2D(tAlpha, vec2(offset * f, 0.5)).a;
|
||||
#else
|
||||
return texture2D(tAlpha, vec2(offset * f, 0.5)).x;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Y(int L, vec3 X, float aO, float fA) {
|
||||
if (L == 0) {
|
||||
return alpha(aO, fA);
|
||||
} else if (L == 1) {
|
||||
return L1(X,
|
||||
alpha(aO, fA), alpha(aO + 1.0, fA), alpha(aO + 2.0, fA)
|
||||
);
|
||||
} else if (L == 2) {
|
||||
return L2(X,
|
||||
alpha(aO, fA), alpha(aO + 1.0, fA), alpha(aO + 2.0, fA), alpha(aO + 3.0, fA), alpha(aO + 4.0, fA)
|
||||
);
|
||||
} else if (L == 3) {
|
||||
return L3(X,
|
||||
alpha(aO, fA), alpha(aO + 1.0, fA), alpha(aO + 2.0, fA), alpha(aO + 3.0, fA), alpha(aO + 4.0, fA),
|
||||
alpha(aO + 5.0, fA), alpha(aO + 6.0, fA)
|
||||
);
|
||||
} else if (L == 4) {
|
||||
return L4(X,
|
||||
alpha(aO, fA), alpha(aO + 1.0, fA), alpha(aO + 2.0, fA), alpha(aO + 3.0, fA), alpha(aO + 4.0, fA),
|
||||
alpha(aO + 5.0, fA), alpha(aO + 6.0, fA), alpha(aO + 7.0, fA), alpha(aO + 8.0, fA)
|
||||
);
|
||||
}
|
||||
// TODO: do we need L > 4?
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#ifndef WEBGL1
|
||||
float R(float R2, int start, int end, float fCoeff) {
|
||||
float gauss = 0.0;
|
||||
for (int i = start; i < end; i++) {
|
||||
vec2 c = texture2D(tCoeff, vec2(float(i) * fCoeff, 0.5)).xy;
|
||||
gauss += c.x * exp(-c.y * R2);
|
||||
}
|
||||
return gauss;
|
||||
}
|
||||
#else
|
||||
float R(float R2, int start, int end, float fCoeff) {
|
||||
float gauss = 0.0;
|
||||
int o = start;
|
||||
for (int i = 0; i < uMaxCoeffs; i++) {
|
||||
if (o >= end) break;
|
||||
|
||||
vec2 c = texture2D(tCoeff, vec2(float(o) * fCoeff, 0.5)).xy;
|
||||
gauss += c.x * exp(-c.y * R2);
|
||||
o++;
|
||||
}
|
||||
return gauss;
|
||||
}
|
||||
#endif
|
||||
`;
|
||||
|
||||
export const MAIN = `
|
||||
float fCenter = 1.0 / float(uNCenters - 1);
|
||||
float fCoeff = 1.0 / float(uNCoeff - 1);
|
||||
float fA = 1.0 / float(uNAlpha - 1);
|
||||
|
||||
float v = 0.0;
|
||||
|
||||
for (int i = 0; i < uNCenters; i++) {
|
||||
vec2 cCoord = vec2(float(i) * fCenter, 0.5);
|
||||
|
||||
vec4 center = texture2D(tCenters, cCoord);
|
||||
vec3 X = xyz - center.xyz;
|
||||
float R2 = dot(X, X);
|
||||
|
||||
// center.w is squared cutoff radius
|
||||
if (R2 > center.w) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vec4 info = texture2D(tInfo, cCoord);
|
||||
|
||||
int L = int(info.x);
|
||||
float aO = info.y;
|
||||
int coeffStart = int(info.z);
|
||||
int coeffEnd = int(info.w);
|
||||
|
||||
v += R(R2, coeffStart, coeffEnd, fCoeff) * Y(L, X, aO, fA);
|
||||
}
|
||||
`;
|
||||
131
src/extensions/alpha-orbitals/orbitals.ts
Normal file
131
src/extensions/alpha-orbitals/orbitals.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Inspired by https://github.com/dgasmith/gau2grid.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { sortArray } from '../../mol-data/util';
|
||||
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { Task } from '../../mol-task';
|
||||
import { sphericalCollocation } from './collocation';
|
||||
import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
|
||||
import { gpuComputeAlphaOrbitalsGridValues } from './gpu/compute';
|
||||
|
||||
// setDebugMode(true);
|
||||
|
||||
export function createSphericalCollocationGrid(
|
||||
params: CubeGridComputationParams, orbital: AlphaOrbital, webgl?: WebGLContext
|
||||
): Task<CubeGrid> {
|
||||
return Task.create('Spherical Collocation Grid', async (ctx) => {
|
||||
const cubeGrid = initCubeGrid(params);
|
||||
|
||||
let matrix: Float32Array;
|
||||
if (canComputeGrid3dOnGPU(webgl)) {
|
||||
// console.time('gpu');
|
||||
matrix = await gpuComputeAlphaOrbitalsGridValues(ctx, webgl!, cubeGrid, orbital);
|
||||
// console.timeEnd('gpu');
|
||||
} else {
|
||||
// console.time('cpu');
|
||||
matrix = await sphericalCollocation(cubeGrid, orbital, ctx);
|
||||
// console.timeEnd('cpu');
|
||||
}
|
||||
|
||||
const grid = createGrid(cubeGrid, matrix, [0, 1, 2]);
|
||||
let isovalues: { negative?: number, positive?: number } | undefined;
|
||||
|
||||
if (!params.doNotComputeIsovalues) {
|
||||
isovalues = computeOrbitalIsocontourValues(matrix, 0.85);
|
||||
}
|
||||
|
||||
return { grid, isovalues };
|
||||
});
|
||||
}
|
||||
|
||||
export function computeOrbitalIsocontourValues(input: Float32Array, cumulativeThreshold: number) {
|
||||
let weightSum = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = v * v;
|
||||
weightSum += w;
|
||||
}
|
||||
const avgWeight = weightSum / input.length;
|
||||
let minWeight = 3 * avgWeight;
|
||||
|
||||
// do not try to identify isovalues for degenerate data
|
||||
// e.g. all values are almost same
|
||||
if (Math.abs(avgWeight - input[0] * input[0]) < 1e-5) {
|
||||
return { negative: void 0, positive: void 0 };
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
while (true) {
|
||||
let csum = 0;
|
||||
size = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = v * v;
|
||||
if (w >= minWeight) {
|
||||
csum += w;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
if (csum / weightSum > cumulativeThreshold) {
|
||||
break;
|
||||
}
|
||||
|
||||
minWeight -= avgWeight;
|
||||
}
|
||||
|
||||
const values = new Float32Array(size);
|
||||
const weights = new Float32Array(size);
|
||||
const indices = new Int32Array(size);
|
||||
|
||||
let o = 0;
|
||||
for (let i = 0, _i = input.length; i < _i; i++) {
|
||||
const v = input[i];
|
||||
const w = v * v;
|
||||
if (w >= minWeight) {
|
||||
values[o] = v;
|
||||
weights[o] = w;
|
||||
indices[o] = o;
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
sortArray(
|
||||
indices,
|
||||
(indices, i, j) => weights[indices[j]] - weights[indices[i]]
|
||||
);
|
||||
|
||||
let cweight = 0,
|
||||
cutoffIndex = 0;
|
||||
for (let i = 0; i < size; i++) {
|
||||
cweight += weights[indices[i]];
|
||||
|
||||
if (cweight / weightSum >= cumulativeThreshold) {
|
||||
cutoffIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let positive = Number.POSITIVE_INFINITY,
|
||||
negative = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < cutoffIndex; i++) {
|
||||
const v = values[indices[i]];
|
||||
if (v > 0) {
|
||||
if (v < positive) positive = v;
|
||||
} else if (v < 0) {
|
||||
if (v > negative) negative = v;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
negative: negative !== Number.NEGATIVE_INFINITY ? negative : void 0,
|
||||
positive: positive !== Number.POSITIVE_INFINITY ? positive : void 0,
|
||||
};
|
||||
}
|
||||
93
src/extensions/alpha-orbitals/spherical-functions.ts
Normal file
93
src/extensions/alpha-orbitals/spherical-functions.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* Inspired by https://github.com/dgasmith/gau2grid.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
// gaussian:
|
||||
// R_0, R^+_1, R^-_1, ..., R^+_l, R^-_l
|
||||
// cca:
|
||||
// R^-_(l), R^-_(l-1), ..., R_0, ..., R^+_(l-1), R^+_l
|
||||
// cca-reverse:
|
||||
// R^+_(l), R^+_(l-1), ..., R_0, ..., R^-_(l-1), R^-_l
|
||||
export type SphericalBasisOrder = 'gaussian' | 'cca' | 'cca-reverse';
|
||||
|
||||
export function normalizeBasicOrder(
|
||||
L: number,
|
||||
alpha: number[],
|
||||
order: SphericalBasisOrder
|
||||
) {
|
||||
if (order === 'gaussian' || L === 0) return alpha;
|
||||
|
||||
const ret: number[] = [alpha[L]];
|
||||
for (let l = 0; l < L; l++) {
|
||||
const a = alpha[L - l - 1],
|
||||
b = alpha[L + l + 1];
|
||||
if (order === 'cca') ret.push(b, a);
|
||||
else ret.push(a, b);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export type SphericalFunc = (
|
||||
alpha: number[],
|
||||
x: number,
|
||||
y: number,
|
||||
z: number
|
||||
) => number;
|
||||
|
||||
export const SphericalFunctions: SphericalFunc[] = [L0, L1, L2, L3, L4];
|
||||
|
||||
// L_i functions were auto-generated.
|
||||
|
||||
function L0(alpha: number[], x: number, y: number, z: number) {
|
||||
return alpha[0];
|
||||
}
|
||||
|
||||
function L1(alpha: number[], x: number, y: number, z: number) {
|
||||
return alpha[0] * z + alpha[1] * x + alpha[2] * y;
|
||||
}
|
||||
|
||||
function L2(alpha: number[], x: number, y: number, z: number) {
|
||||
const xx = x * x, yy = y * y, zz = z * z;
|
||||
return (
|
||||
alpha[0] * (-0.5 * xx - 0.5 * yy + zz) +
|
||||
alpha[1] * (1.7320508075688772 * x * z) +
|
||||
alpha[2] * (1.7320508075688772 * y * z) +
|
||||
alpha[3] * (0.8660254037844386 * xx - 0.8660254037844386 * yy) +
|
||||
alpha[4] * (1.7320508075688772 * x * y)
|
||||
);
|
||||
}
|
||||
|
||||
function L3(alpha: number[], x: number, y: number, z: number) {
|
||||
const xx = x * x, yy = y * y, zz = z * z;
|
||||
const xxx = xx * x, yyy = yy * y, zzz = zz * z;
|
||||
return (
|
||||
alpha[0] * (-1.5 * xx * z - 1.5 * yy * z + zzz) +
|
||||
alpha[1] * (-0.6123724356957945 * xxx - 0.6123724356957945 * x * yy + 2.449489742783178 * x * zz) +
|
||||
alpha[2] * (-0.6123724356957945 * xx * y - 0.6123724356957945 * yyy + 2.449489742783178 * y * zz) +
|
||||
alpha[3] * (1.9364916731037085 * xx * z - 1.9364916731037085 * yy * z) +
|
||||
alpha[4] * (3.872983346207417 * x * y * z) +
|
||||
alpha[5] * (0.7905694150420949 * xxx - 2.3717082451262845 * x * yy) +
|
||||
alpha[6] * (2.3717082451262845 * xx * y - 0.7905694150420949 * yyy)
|
||||
);
|
||||
}
|
||||
|
||||
function L4(alpha: number[], x: number, y: number, z: number) {
|
||||
const xx = x * x, yy = y * y, zz = z * z;
|
||||
const xxx = xx * x, yyy = yy * y, zzz = zz * z;
|
||||
const xxxx = xxx * x, yyyy = yyy * y, zzzz = zzz * z;
|
||||
return (
|
||||
alpha[0] * (0.375 * xxxx + 0.75 * xx * yy + 0.375 * yyyy - 3.0 * xx * zz - 3.0 * yy * zz + zzzz) +
|
||||
alpha[1] * (-2.3717082451262845 * xxx * z - 2.3717082451262845 * x * yy * z + 3.1622776601683795 * x * zzz) +
|
||||
alpha[2] * (-2.3717082451262845 * xx * y * z - 2.3717082451262845 * yyy * z + 3.1622776601683795 * y * zzz) +
|
||||
alpha[3] * (-0.5590169943749475 * xxxx + 0.5590169943749475 * yyyy + 3.3541019662496847 * xx * zz - 3.3541019662496847 * yy * zz) +
|
||||
alpha[4] * (-1.118033988749895 * xxx * y - 1.118033988749895 * x * yyy + 6.708203932499369 * x * y * zz) +
|
||||
alpha[5] * (2.091650066335189 * xxx * z + -6.274950199005566 * x * yy * z) +
|
||||
alpha[6] * (6.274950199005566 * xx * y * z + -2.091650066335189 * yyy * z) +
|
||||
alpha[7] * (0.739509972887452 * xxxx - 4.437059837324712 * xx * yy + 0.739509972887452 * yyyy) +
|
||||
alpha[8] * (2.958039891549808 * xxx * y + -2.958039891549808 * x * yyy)
|
||||
);
|
||||
}
|
||||
236
src/extensions/alpha-orbitals/transforms.ts
Normal file
236
src/extensions/alpha-orbitals/transforms.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginStateObject, PluginStateTransform } from '../../mol-plugin-state/objects';
|
||||
import { createSphericalCollocationGrid } from './orbitals';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Task } from '../../mol-task';
|
||||
import { CustomProperties } from '../../mol-model/custom-property';
|
||||
import { SphericalBasisOrder } from './spherical-functions';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { StateTransformer } from '../../mol-state';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { VolumeRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
|
||||
import { AlphaOrbital, Basis, CubeGrid } from './data-model';
|
||||
import { createSphericalCollocationDensityGrid } from './density';
|
||||
import { Tensor } from '../../mol-math/linear-algebra';
|
||||
|
||||
export class BasisAndOrbitals extends PluginStateObject.Create<{ basis: Basis, order: SphericalBasisOrder, orbitals: AlphaOrbital[] }>({ name: 'Basis', typeClass: 'Object' }) { }
|
||||
|
||||
export const StaticBasisAndOrbitals = PluginStateTransform.BuiltIn({
|
||||
name: 'static-basis-and-orbitals',
|
||||
display: 'Basis and Orbitals',
|
||||
from: PluginStateObject.Root,
|
||||
to: BasisAndOrbitals,
|
||||
params: {
|
||||
label: PD.Text('Orbital Data', { isHidden: true }),
|
||||
basis: PD.Value<Basis>(void 0 as any, { isHidden: true }),
|
||||
order: PD.Text<SphericalBasisOrder>('gaussian' as SphericalBasisOrder, { isHidden: true }),
|
||||
orbitals: PD.Value<AlphaOrbital[]>([], { isHidden: true })
|
||||
},
|
||||
})({
|
||||
apply({ params }) {
|
||||
return new BasisAndOrbitals({ basis: params.basis, order: params.order, orbitals: params.orbitals }, { label: params.label });
|
||||
}
|
||||
});
|
||||
|
||||
const CreateOrbitalVolumeParamBase = {
|
||||
cutoffThreshold: PD.Numeric(0.0015, { min: 0, max: 0.1, step: 0.0001 }),
|
||||
boxExpand: PD.Numeric(4.5, { min: 0, max: 7, step: 0.1 }),
|
||||
gridSpacing: PD.ObjectList({ atomCount: PD.Numeric(0), spacing: PD.Numeric(0.35, { min: 0.1, max: 2, step: 0.01 }) }, e => `Atoms ${e.atomCount}: ${e.spacing}`, {
|
||||
defaultValue: [
|
||||
{ atomCount: 55, spacing: 0.5 },
|
||||
{ atomCount: 40, spacing: 0.45 },
|
||||
{ atomCount: 25, spacing: 0.4 },
|
||||
{ atomCount: 0, spacing: 0.35 },
|
||||
]
|
||||
}),
|
||||
clampValues: PD.MappedStatic('off', {
|
||||
off: PD.EmptyGroup(),
|
||||
on: PD.Group({
|
||||
sigma: PD.Numeric(8, { min: 1, max: 20 }, { description: 'Clamp values to range [sigma * negIsoValue, sigma * posIsoValue].' })
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
function clampData(matrix: Tensor.Data, min: number, max: number) {
|
||||
for (let i = 0, _i = matrix.length; i < _i; i++) {
|
||||
const v = matrix[i];
|
||||
if (v < min) matrix[i] = min;
|
||||
else if (v > max) matrix[i] = max;
|
||||
}
|
||||
}
|
||||
|
||||
function clampGrid(data: CubeGrid, v: number) {
|
||||
const grid = data.grid;
|
||||
const min = (data.isovalues?.negative ?? data.grid.stats.min) * v;
|
||||
const max = (data.isovalues?.positive ?? data.grid.stats.max) * v;
|
||||
|
||||
// clamp values for better direct volume resolution
|
||||
// current implementation uses Byte array for values
|
||||
// if this is not enough, update mol* to use float
|
||||
// textures instead
|
||||
if (grid.stats.min < min || grid.stats.max > max) {
|
||||
clampData(data.grid.cells.data, min, max);
|
||||
if (grid.stats.min < min) {
|
||||
(grid.stats.min as number) = min;
|
||||
}
|
||||
if (grid.stats.max > max) {
|
||||
(grid.stats.max as number) = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const CreateOrbitalVolume = PluginStateTransform.BuiltIn({
|
||||
name: 'create-orbital-volume',
|
||||
display: 'Orbital Volume',
|
||||
from: BasisAndOrbitals,
|
||||
to: PluginStateObject.Volume.Data,
|
||||
params: (a) => {
|
||||
if (!a) {
|
||||
return { index: PD.Numeric(0), ...CreateOrbitalVolumeParamBase };
|
||||
}
|
||||
|
||||
return {
|
||||
index: PD.Select(0, a.data.orbitals.map((o, i) => [i, `[${i + 1}] ${o.energy.toFixed(4)}`])),
|
||||
...CreateOrbitalVolumeParamBase
|
||||
};
|
||||
}
|
||||
})({
|
||||
apply({ a, params }, plugin: PluginContext) {
|
||||
return Task.create('Orbital Volume', async ctx => {
|
||||
const data = await createSphericalCollocationGrid({
|
||||
basis: a.data.basis,
|
||||
cutoffThreshold: params.cutoffThreshold,
|
||||
sphericalOrder: a.data.order,
|
||||
boxExpand: params.boxExpand,
|
||||
gridSpacing: params.gridSpacing.map(e => [e.atomCount, e.spacing] as [number, number])
|
||||
}, a.data.orbitals[params.index], plugin.canvas3d?.webgl).runInContext(ctx);
|
||||
const volume: Volume = {
|
||||
grid: data.grid,
|
||||
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
|
||||
customProperties: new CustomProperties(),
|
||||
_propertyData: Object.create(null),
|
||||
};
|
||||
|
||||
if (params.clampValues?.name === 'on') {
|
||||
clampGrid(data, params.clampValues?.params?.sigma ?? 8);
|
||||
}
|
||||
|
||||
return new PluginStateObject.Volume.Data(volume, { label: 'Orbital Volume' });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const CreateOrbitalDensityVolume = PluginStateTransform.BuiltIn({
|
||||
name: 'create-orbital-density-volume',
|
||||
display: 'Orbital Density Volume',
|
||||
from: BasisAndOrbitals,
|
||||
to: PluginStateObject.Volume.Data,
|
||||
params: CreateOrbitalVolumeParamBase
|
||||
})({
|
||||
apply({ a, params }, plugin: PluginContext) {
|
||||
return Task.create('Orbital Volume', async ctx => {
|
||||
const data = await createSphericalCollocationDensityGrid({
|
||||
basis: a.data.basis,
|
||||
cutoffThreshold: params.cutoffThreshold,
|
||||
sphericalOrder: a.data.order,
|
||||
boxExpand: params.boxExpand,
|
||||
gridSpacing: params.gridSpacing.map(e => [e.atomCount, e.spacing] as [number, number])
|
||||
}, a.data.orbitals, plugin.canvas3d?.webgl).runInContext(ctx);
|
||||
const volume: Volume = {
|
||||
grid: data.grid,
|
||||
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
|
||||
customProperties: new CustomProperties(),
|
||||
_propertyData: Object.create(null),
|
||||
};
|
||||
|
||||
if (params.clampValues?.name === 'on') {
|
||||
clampGrid(data, params.clampValues?.params?.sigma ?? 8);
|
||||
}
|
||||
|
||||
return new PluginStateObject.Volume.Data(volume, { label: 'Orbital Volume' });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
name: 'create-orbital-representation-3d',
|
||||
display: 'Orbital Representation 3D',
|
||||
from: PluginStateObject.Volume.Data,
|
||||
to: PluginStateObject.Volume.Representation3D,
|
||||
params: {
|
||||
directVolume: PD.Boolean(false),
|
||||
relativeIsovalue: PD.Numeric(1, { min: 0.01, max: 5, step: 0.01 }),
|
||||
kind: PD.Select<'positive' | 'negative'>('positive', [['positive', 'Positive'], ['negative', 'Negative']]),
|
||||
color: PD.Color(ColorNames.blue),
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
|
||||
xrayShaded: PD.Boolean(false),
|
||||
pickable: PD.Boolean(true)
|
||||
}
|
||||
})({
|
||||
canAutoUpdate() {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params: srcParams }, plugin: PluginContext) {
|
||||
return Task.create('Orbitals Representation 3D', async ctx => {
|
||||
const params = volumeParams(plugin, a, srcParams);
|
||||
|
||||
const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
|
||||
const provider = plugin.representation.volume.registry.get(params.type.name);
|
||||
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
|
||||
const props = params.type.params || {};
|
||||
const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams);
|
||||
repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, params));
|
||||
await repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
repr.setState({ pickable: srcParams.pickable });
|
||||
return new PluginStateObject.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams: srcParams }, plugin: PluginContext) {
|
||||
return Task.create('Orbitals Representation 3D', async ctx => {
|
||||
const newParams = volumeParams(plugin, a, srcParams);
|
||||
|
||||
const props = { ...b.data.repr.props, ...newParams.type.params };
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.repr.setState({ pickable: srcParams.pickable });
|
||||
b.description = VolumeRepresentation3DHelpers.getDescription(props);
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function volumeParams(plugin: PluginContext, volume: PluginStateObject.Volume.Data, params: StateTransformer.Params<typeof CreateOrbitalRepresentation3D>) {
|
||||
if (volume.data.sourceData.kind !== 'alpha-orbitals') throw new Error('Invalid data source kind.');
|
||||
|
||||
const { isovalues } = volume.data.sourceData.data as CubeGrid;
|
||||
if (!isovalues) throw new Error('Isovalues are not computed.');
|
||||
|
||||
const value = isovalues[params.kind];
|
||||
|
||||
return createVolumeRepresentationParams(plugin, volume.data, params.directVolume ? {
|
||||
type: 'direct-volume',
|
||||
typeParams: {
|
||||
alpha: params.alpha,
|
||||
renderMode: {
|
||||
name: 'isosurface',
|
||||
params: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, singleLayer: false }
|
||||
},
|
||||
xrayShaded: params.xrayShaded
|
||||
},
|
||||
color: 'uniform',
|
||||
colorParams: { value: params.color }
|
||||
} : {
|
||||
type: 'isosurface',
|
||||
typeParams: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, alpha: params.alpha, xrayShaded: params.xrayShaded },
|
||||
color: 'uniform',
|
||||
colorParams: { value: params.color }
|
||||
});
|
||||
}
|
||||
370
src/extensions/anvil/algorithm.ts
Normal file
370
src/extensions/anvil/algorithm.ts
Normal file
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Structure, StructureElement, StructureProperties } from '../../mol-model/structure';
|
||||
import { Task, RuntimeContext } from '../../mol-task';
|
||||
import { CentroidHelper } from '../../mol-math/geometry/centroid-helper';
|
||||
import { AccessibleSurfaceAreaProvider } from '../../mol-model-props/computed/accessible-surface-area';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { getElementMoleculeType } from '../../mol-model/structure/util';
|
||||
import { MoleculeType } from '../../mol-model/structure/model/types';
|
||||
import { AccessibleSurfaceArea } from '../../mol-model-props/computed/accessible-surface-area/shrake-rupley';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { MembraneOrientation } from './prop';
|
||||
|
||||
interface ANVILContext {
|
||||
structure: Structure,
|
||||
|
||||
numberOfSpherePoints: number,
|
||||
stepSize: number,
|
||||
minThickness: number,
|
||||
maxThickness: number,
|
||||
asaCutoff: number,
|
||||
|
||||
offsets: ArrayLike<number>,
|
||||
exposed: ArrayLike<boolean>,
|
||||
centroid: Vec3,
|
||||
extent: number
|
||||
};
|
||||
|
||||
export const ANVILParams = {
|
||||
numberOfSpherePoints: PD.Numeric(120, { min: 35, max: 700, step: 1 }, { description: 'Number of spheres/directions to test for membrane placement. Original value is 350.' }),
|
||||
stepSize: PD.Numeric(1, { min: 0.25, max: 4, step: 0.25 }, { description: 'Thickness of membrane slices that will be tested' }),
|
||||
minThickness: PD.Numeric(20, { min: 10, max: 30, step: 1}, { description: 'Minimum membrane thickness used during refinement' }),
|
||||
maxThickness: PD.Numeric(40, { min: 30, max: 50, step: 1}, { description: 'Maximum membrane thickness used during refinement' }),
|
||||
asaCutoff: PD.Numeric(40, { min: 10, max: 100, step: 1 }, { description: 'Absolute ASA cutoff above which residues will be considered' })
|
||||
};
|
||||
export type ANVILParams = typeof ANVILParams
|
||||
export type ANVILProps = PD.Values<ANVILParams>
|
||||
|
||||
/**
|
||||
* Implements:
|
||||
* Membrane positioning for high- and low-resolution protein structures through a binary classification approach
|
||||
* Guillaume Postic, Yassine Ghouzam, Vincent Guiraud, and Jean-Christophe Gelly
|
||||
* Protein Engineering, Design & Selection, 2015, 1–5
|
||||
* doi: 10.1093/protein/gzv063
|
||||
*/
|
||||
export function computeANVIL(structure: Structure, props: ANVILProps) {
|
||||
return Task.create('Compute Membrane Orientation', async runtime => {
|
||||
return await calculate(runtime, structure, props);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const centroidHelper = new CentroidHelper();
|
||||
function initialize(structure: Structure, props: ANVILProps): ANVILContext {
|
||||
const l = StructureElement.Location.create(structure);
|
||||
const { label_atom_id, x, y, z } = StructureProperties.atom;
|
||||
const elementCount = structure.polymerResidueCount;
|
||||
centroidHelper.reset();
|
||||
|
||||
let offsets = new Int32Array(elementCount);
|
||||
let exposed = new Array<boolean>(elementCount);
|
||||
|
||||
const accessibleSurfaceArea = structure && AccessibleSurfaceAreaProvider.get(structure);
|
||||
const asa = accessibleSurfaceArea.value!;
|
||||
|
||||
const vec = Vec3();
|
||||
let m = 0;
|
||||
for (let i = 0, il = structure.units.length; i < il; ++i) {
|
||||
const unit = structure.units[i];
|
||||
const { elements } = unit;
|
||||
l.unit = unit;
|
||||
|
||||
for (let j = 0, jl = elements.length; j < jl; ++j) {
|
||||
const eI = elements[j];
|
||||
l.element = eI;
|
||||
|
||||
// consider only amino acids
|
||||
if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only CA is considered for downstream operations
|
||||
if (label_atom_id(l) !== 'CA') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// while iterating use first pass to compute centroid
|
||||
Vec3.set(vec, x(l), y(l), z(l));
|
||||
centroidHelper.includeStep(vec);
|
||||
|
||||
// keep track of offsets and exposed state to reuse
|
||||
offsets[m] = structure.serialMapping.getSerialIndex(l.unit, l.element);
|
||||
exposed[m] = AccessibleSurfaceArea.getValue(l, asa) > props.asaCutoff;
|
||||
|
||||
m++;
|
||||
}
|
||||
}
|
||||
|
||||
// omit potentially empty tail1
|
||||
offsets = offsets.slice(0, m);
|
||||
exposed = exposed.slice(0, m);
|
||||
|
||||
// calculate centroid and extent
|
||||
centroidHelper.finishedIncludeStep();
|
||||
const centroid = centroidHelper.center;
|
||||
for (let k = 0, kl = offsets.length; k < kl; k++) {
|
||||
setLocation(l, structure, offsets[k]);
|
||||
Vec3.set(vec, x(l), y(l), z(l));
|
||||
centroidHelper.radiusStep(vec);
|
||||
}
|
||||
const extent = 1.2 * Math.sqrt(centroidHelper.radiusSq);
|
||||
|
||||
return {
|
||||
...props,
|
||||
structure: structure,
|
||||
|
||||
offsets: offsets,
|
||||
exposed: exposed,
|
||||
centroid: centroid,
|
||||
extent: extent
|
||||
};
|
||||
}
|
||||
|
||||
export async function calculate(runtime: RuntimeContext, structure: Structure, params: ANVILProps): Promise<MembraneOrientation> {
|
||||
const { label_comp_id } = StructureProperties.atom;
|
||||
|
||||
const ctx = initialize(structure, params);
|
||||
const initialHphobHphil = HphobHphil.filtered(ctx, label_comp_id);
|
||||
|
||||
const initialMembrane = findMembrane(ctx, generateSpherePoints(ctx, ctx.numberOfSpherePoints), initialHphobHphil, label_comp_id);
|
||||
const alternativeMembrane = findMembrane(ctx, findProximateAxes(ctx, initialMembrane), initialHphobHphil, label_comp_id);
|
||||
|
||||
const membrane = initialMembrane.qmax! > alternativeMembrane.qmax! ? initialMembrane : alternativeMembrane;
|
||||
|
||||
return {
|
||||
planePoint1: membrane.planePoint1,
|
||||
planePoint2: membrane.planePoint2,
|
||||
normalVector: membrane.normalVector!,
|
||||
radius: ctx.extent,
|
||||
centroid: ctx.centroid
|
||||
};
|
||||
}
|
||||
|
||||
interface MembraneCandidate {
|
||||
planePoint1: Vec3,
|
||||
planePoint2: Vec3,
|
||||
stats: HphobHphil,
|
||||
normalVector?: Vec3,
|
||||
spherePoint?: Vec3,
|
||||
qmax?: number
|
||||
}
|
||||
|
||||
namespace MembraneCandidate {
|
||||
export function initial(c1: Vec3, c2: Vec3, stats: HphobHphil): MembraneCandidate {
|
||||
return {
|
||||
planePoint1: c1,
|
||||
planePoint2: c2,
|
||||
stats: stats
|
||||
};
|
||||
}
|
||||
|
||||
export function scored(spherePoint: Vec3, c1: Vec3, c2: Vec3, stats: HphobHphil, qmax: number, centroid: Vec3): MembraneCandidate {
|
||||
const diam_vect = Vec3();
|
||||
Vec3.sub(diam_vect, centroid, spherePoint);
|
||||
return {
|
||||
planePoint1: c1,
|
||||
planePoint2: c2,
|
||||
stats: stats,
|
||||
normalVector: diam_vect,
|
||||
spherePoint: spherePoint,
|
||||
qmax: qmax
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function findMembrane(ctx: ANVILContext, spherePoints: Vec3[], initialStats: HphobHphil, label_comp_id: StructureElement.Property<string>): MembraneCandidate {
|
||||
const { centroid, stepSize, minThickness, maxThickness } = ctx;
|
||||
// best performing membrane
|
||||
let membrane: MembraneCandidate;
|
||||
// score of the best performing membrane
|
||||
let qmax = 0;
|
||||
|
||||
// construct slices of thickness 1.0 along the axis connecting the centroid and the spherePoint
|
||||
const diam = Vec3();
|
||||
for (let i = 0, il = spherePoints.length; i < il; i++) {
|
||||
const spherePoint = spherePoints[i];
|
||||
Vec3.sub(diam, centroid, spherePoint);
|
||||
Vec3.scale(diam, diam, 2);
|
||||
const diamNorm = Vec3.magnitude(diam);
|
||||
const qvartemp = [];
|
||||
|
||||
for (let i = 0, il = diamNorm - stepSize; i < il; i += stepSize) {
|
||||
const c1 = Vec3();
|
||||
const c2 = Vec3();
|
||||
Vec3.scaleAndAdd(c1, spherePoint, diam, i / diamNorm);
|
||||
Vec3.scaleAndAdd(c2, spherePoint, diam, (i + stepSize) / diamNorm);
|
||||
|
||||
// evaluate how well this membrane slice embeddeds the peculiar residues
|
||||
const stats = HphobHphil.filtered(ctx, label_comp_id, (testPoint: Vec3) => isInMembranePlane(testPoint, diam, c1, c2));
|
||||
qvartemp.push(MembraneCandidate.initial(c1, c2, stats));
|
||||
}
|
||||
|
||||
let jmax = (minThickness / stepSize) - 1;
|
||||
|
||||
for (let width = 0, widthl = maxThickness; width < widthl;) {
|
||||
const imax = qvartemp.length - 1 - jmax;
|
||||
|
||||
for (let i = 0, il = imax; i < il; i++) {
|
||||
const c1 = qvartemp[i].planePoint1;
|
||||
const c2 = qvartemp[i + jmax].planePoint2;
|
||||
|
||||
let hphob = 0;
|
||||
let hphil = 0;
|
||||
let total = 0;
|
||||
for (let j = 0; j < jmax; j++) {
|
||||
const ij = qvartemp[i + j];
|
||||
if (j === 0 || j === jmax - 1) {
|
||||
hphob += 0.5 * ij.stats.hphob;
|
||||
hphil += 0.5 * ij.stats.hphil;
|
||||
} else {
|
||||
hphob += ij.stats.hphob;
|
||||
hphil += ij.stats.hphil;
|
||||
}
|
||||
total += ij.stats.total;
|
||||
}
|
||||
|
||||
const stats = HphobHphil.of(hphob, hphil, total);
|
||||
|
||||
if (hphob !== 0) {
|
||||
const qvaltest = qValue(stats, initialStats);
|
||||
if (qvaltest > qmax) {
|
||||
qmax = qvaltest;
|
||||
membrane = MembraneCandidate.scored(spherePoint, c1, c2, HphobHphil.of(hphob, hphil, total), qmax, centroid);
|
||||
}
|
||||
}
|
||||
}
|
||||
jmax++;
|
||||
width = (jmax + 1) * stepSize;
|
||||
}
|
||||
}
|
||||
|
||||
return membrane!;
|
||||
}
|
||||
|
||||
function qValue(currentStats: HphobHphil, initialStats: HphobHphil): number {
|
||||
if(initialStats.hphob < 1) {
|
||||
initialStats.hphob = 0.1;
|
||||
}
|
||||
|
||||
if(initialStats.hphil < 1) {
|
||||
initialStats.hphil += 1;
|
||||
}
|
||||
|
||||
const part_tot = currentStats.hphob + currentStats.hphil;
|
||||
return (currentStats.hphob * (initialStats.hphil - currentStats.hphil) - currentStats.hphil * (initialStats.hphob - currentStats.hphob)) /
|
||||
Math.sqrt(part_tot * initialStats.hphob * initialStats.hphil * (initialStats.hphob + initialStats.hphil - part_tot));
|
||||
}
|
||||
|
||||
export function isInMembranePlane(testPoint: Vec3, normalVector: Vec3, planePoint1: Vec3, planePoint2: Vec3): boolean {
|
||||
const d1 = -Vec3.dot(normalVector, planePoint1);
|
||||
const d2 = -Vec3.dot(normalVector, planePoint2);
|
||||
const d = -Vec3.dot(normalVector, testPoint);
|
||||
return d > Math.min(d1, d2) && d < Math.max(d1, d2);
|
||||
}
|
||||
|
||||
// generates a defined number of points on a sphere with radius = extent around the specified centroid
|
||||
function generateSpherePoints(ctx: ANVILContext, numberOfSpherePoints: number): Vec3[] {
|
||||
const { centroid, extent } = ctx;
|
||||
const points = [];
|
||||
let oldPhi = 0, h, theta, phi;
|
||||
for(let k = 1, kl = numberOfSpherePoints + 1; k < kl; k++) {
|
||||
h = -1 + 2 * (k - 1) / (numberOfSpherePoints - 1);
|
||||
theta = Math.acos(h);
|
||||
phi = (k === 1 || k === numberOfSpherePoints) ? 0 : (oldPhi + 3.6 / Math.sqrt(numberOfSpherePoints * (1 - h * h))) % (2 * Math.PI);
|
||||
|
||||
const point = Vec3.create(
|
||||
extent * Math.sin(phi) * Math.sin(theta) + centroid[0],
|
||||
extent * Math.cos(theta) + centroid[1],
|
||||
extent * Math.cos(phi) * Math.sin(theta) + centroid[2]
|
||||
);
|
||||
points[k - 1] = point;
|
||||
oldPhi = phi;
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
// generates sphere points close to that of the initial membrane
|
||||
function findProximateAxes(ctx: ANVILContext, membrane: MembraneCandidate): Vec3[] {
|
||||
const { numberOfSpherePoints, extent } = ctx;
|
||||
const points = generateSpherePoints(ctx, 30000);
|
||||
let j = 4;
|
||||
let sphere_pts2: Vec3[] = [];
|
||||
while (sphere_pts2.length < numberOfSpherePoints) {
|
||||
const d = 2 * extent / numberOfSpherePoints + j;
|
||||
const dsq = d * d;
|
||||
sphere_pts2 = [];
|
||||
for (let i = 0, il = points.length; i < il; i++) {
|
||||
if (Vec3.squaredDistance(points[i], membrane.spherePoint!) < dsq) {
|
||||
sphere_pts2.push(points[i]);
|
||||
}
|
||||
}
|
||||
j += 0.2;
|
||||
}
|
||||
return sphere_pts2;
|
||||
}
|
||||
|
||||
interface HphobHphil {
|
||||
hphob: number,
|
||||
hphil: number,
|
||||
total: number
|
||||
}
|
||||
|
||||
namespace HphobHphil {
|
||||
export function of(hphob: number, hphil: number, total?: number) {
|
||||
return {
|
||||
hphob: hphob,
|
||||
hphil: hphil,
|
||||
total: !!total ? total : hphob + hphil
|
||||
};
|
||||
}
|
||||
|
||||
const testPoint = Vec3();
|
||||
export function filtered(ctx: ANVILContext, label_comp_id: StructureElement.Property<string>, filter?: (test: Vec3) => boolean): HphobHphil {
|
||||
const { offsets, exposed, structure } = ctx;
|
||||
const l = StructureElement.Location.create(structure);
|
||||
const { x, y, z } = StructureProperties.atom;
|
||||
let hphob = 0;
|
||||
let hphil = 0;
|
||||
for (let k = 0, kl = offsets.length; k < kl; k++) {
|
||||
// ignore buried residues
|
||||
if (!exposed[k]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setLocation(l, structure, offsets[k]);
|
||||
Vec3.set(testPoint, x(l), y(l), z(l));
|
||||
|
||||
// testPoints have to be in putative membrane layer
|
||||
if (filter && !filter(testPoint)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isHydrophobic(label_comp_id(l))) {
|
||||
hphob++;
|
||||
} else {
|
||||
hphil++;
|
||||
}
|
||||
}
|
||||
return of(hphob, hphil);
|
||||
}
|
||||
}
|
||||
|
||||
// ANVIL-specific (not general) definition of membrane-favoring amino acids
|
||||
const HYDROPHOBIC_AMINO_ACIDS = new Set(['ALA', 'CYS', 'GLY', 'HIS', 'ILE', 'LEU', 'MET', 'PHE', 'SER', 'THR', 'VAL']);
|
||||
export function isHydrophobic(label_comp_id: string): boolean {
|
||||
return HYDROPHOBIC_AMINO_ACIDS.has(label_comp_id);
|
||||
}
|
||||
|
||||
function setLocation(l: StructureElement.Location, structure: Structure, serialIndex: number) {
|
||||
l.structure = structure;
|
||||
l.unit = structure.units[structure.serialMapping.unitIndices[serialIndex]];
|
||||
l.element = structure.serialMapping.elementIndices[serialIndex];
|
||||
return l;
|
||||
}
|
||||
174
src/extensions/anvil/behavior.ts
Normal file
174
src/extensions/anvil/behavior.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { MembraneOrientationProvider, MembraneOrientation } from './prop';
|
||||
import { StateObjectRef, StateTransformer, StateTransform } from '../../mol-state';
|
||||
import { Task } from '../../mol-task';
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior';
|
||||
import { MembraneOrientationRepresentationProvider, MembraneOrientationParams, MembraneOrientationRepresentation } from './representation';
|
||||
import { HydrophobicityColorThemeProvider } from '../../mol-theme/color/hydrophobicity';
|
||||
import { PluginStateObject, PluginStateTransform } from '../../mol-plugin-state/objects';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { DefaultQueryRuntimeTable } from '../../mol-script/runtime/query/compiler';
|
||||
import { StructureSelectionQuery, StructureSelectionCategory } from '../../mol-plugin-state/helpers/structure-selection-query';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { GenericRepresentationRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
|
||||
|
||||
const Tag = MembraneOrientation.Tag;
|
||||
|
||||
export const ANVILMembraneOrientation = PluginBehavior.create<{ autoAttach: boolean }>({
|
||||
name: 'anvil-membrane-orientation-prop',
|
||||
category: 'custom-props',
|
||||
display: {
|
||||
name: 'Membrane Orientation',
|
||||
description: 'Data calculated with ANVIL algorithm.'
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
|
||||
private provider = MembraneOrientationProvider
|
||||
|
||||
register(): void {
|
||||
DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
|
||||
|
||||
this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
|
||||
|
||||
this.ctx.representation.structure.registry.add(MembraneOrientationRepresentationProvider);
|
||||
this.ctx.query.structure.registry.add(isTransmembrane);
|
||||
|
||||
this.ctx.genericRepresentationControls.set(Tag.Representation, selection => {
|
||||
const refs: GenericRepresentationRef[] = [];
|
||||
selection.structures.forEach(structure => {
|
||||
const memRepr = structure.genericRepresentations?.filter(r => r.cell.transform.transformer.id === MembraneOrientation3D.id)[0];
|
||||
if (memRepr) refs.push(memRepr);
|
||||
});
|
||||
return [refs, 'Membrane Orientation'];
|
||||
});
|
||||
this.ctx.builders.structure.representation.registerPreset(MembraneOrientationPreset);
|
||||
}
|
||||
|
||||
update(p: { autoAttach: boolean }) {
|
||||
let updated = this.params.autoAttach !== p.autoAttach;
|
||||
this.params.autoAttach = p.autoAttach;
|
||||
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
|
||||
return updated;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
|
||||
|
||||
this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
|
||||
|
||||
this.ctx.representation.structure.registry.remove(MembraneOrientationRepresentationProvider);
|
||||
this.ctx.query.structure.registry.remove(isTransmembrane);
|
||||
|
||||
this.ctx.genericRepresentationControls.delete(Tag.Representation);
|
||||
this.ctx.builders.structure.representation.unregisterPreset(MembraneOrientationPreset);
|
||||
}
|
||||
},
|
||||
params: () => ({
|
||||
autoAttach: PD.Boolean(false)
|
||||
})
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
export const isTransmembrane = StructureSelectionQuery('Residues Embedded in Membrane', MS.struct.modifier.union([
|
||||
MS.struct.modifier.wholeResidues([
|
||||
MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
|
||||
'atom-test': MembraneOrientation.symbols.isTransmembrane.symbol(),
|
||||
})
|
||||
])
|
||||
])
|
||||
]), {
|
||||
description: 'Select residues that are embedded between the membrane layers.',
|
||||
category: StructureSelectionCategory.Residue,
|
||||
ensureCustomProperties: (ctx, structure) => {
|
||||
return MembraneOrientationProvider.attach(ctx, structure);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
export { MembraneOrientation3D };
|
||||
|
||||
type MembraneOrientation3D = typeof MembraneOrientation3D
|
||||
const MembraneOrientation3D = PluginStateTransform.BuiltIn({
|
||||
name: 'membrane-orientation-3d',
|
||||
display: {
|
||||
name: 'Membrane Orientation',
|
||||
description: 'Membrane Orientation planes and rims. Data calculated with ANVIL algorithm.'
|
||||
},
|
||||
from: PluginStateObject.Molecule.Structure,
|
||||
to: PluginStateObject.Shape.Representation3D,
|
||||
params: (a) => {
|
||||
return {
|
||||
...MembraneOrientationParams,
|
||||
};
|
||||
}
|
||||
})({
|
||||
canAutoUpdate({ oldParams, newParams }) {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }, plugin: PluginContext) {
|
||||
return Task.create('Membrane Orientation', async ctx => {
|
||||
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
|
||||
const repr = MembraneOrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => MembraneOrientationParams);
|
||||
await repr.createOrUpdate(params, a.data).runInContext(ctx);
|
||||
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: 'Membrane Orientation' });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams }, plugin: PluginContext) {
|
||||
return Task.create('Membrane Orientation', async ctx => {
|
||||
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
isApplicable(a) {
|
||||
return MembraneOrientationProvider.isApplicable(a.data);
|
||||
}
|
||||
});
|
||||
|
||||
export const MembraneOrientationPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-membrane-orientation',
|
||||
display: {
|
||||
name: 'Membrane Orientation', group: 'Annotation',
|
||||
description: 'Shows orientation of membrane layers. Data calculated with ANVIL algorithm.' // TODO add ' or obtained via RCSB PDB'
|
||||
},
|
||||
isApplicable(a) {
|
||||
return MembraneOrientationProvider.isApplicable(a.data);
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const structure = structureCell?.obj?.data;
|
||||
if (!structureCell || !structure) return {};
|
||||
|
||||
if (!MembraneOrientationProvider.get(structure).value) {
|
||||
await plugin.runTask(Task.create('Membrane Orientation', async runtime => {
|
||||
await MembraneOrientationProvider.attach({ runtime, assetManager: plugin.managers.asset }, structure);
|
||||
}));
|
||||
}
|
||||
|
||||
const membraneOrientation = await tryCreateMembraneOrientation(plugin, structureCell);
|
||||
const colorTheme = HydrophobicityColorThemeProvider.name as any;
|
||||
const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, theme: { globalName: colorTheme, focus: { name: colorTheme } } }, plugin);
|
||||
|
||||
return { components: preset.components, representations: { ...preset.representations, membraneOrientation } };
|
||||
}
|
||||
});
|
||||
|
||||
export function tryCreateMembraneOrientation(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, params?: StateTransformer.Params<MembraneOrientation3D>, initialState?: Partial<StateTransform.State>) {
|
||||
const state = plugin.state.data;
|
||||
const membraneOrientation = state.build().to(structure)
|
||||
.applyOrUpdateTagged('membrane-orientation-3d', MembraneOrientation3D, params, { state: initialState });
|
||||
return membraneOrientation.commit({ revertOnError: true });
|
||||
}
|
||||
82
src/extensions/anvil/prop.ts
Normal file
82
src/extensions/anvil/prop.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Structure, StructureProperties, Unit } from '../../mol-model/structure';
|
||||
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
import { ANVILParams, ANVILProps, computeANVIL, isInMembranePlane } from './algorithm';
|
||||
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { AccessibleSurfaceAreaProvider } from '../../mol-model-props/computed/accessible-surface-area';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { QuerySymbolRuntime } from '../../mol-script/runtime/query/base';
|
||||
import { CustomPropSymbol } from '../../mol-script/language/symbol';
|
||||
import { Type } from '../../mol-script/language/type';
|
||||
|
||||
export const MembraneOrientationParams = {
|
||||
...ANVILParams
|
||||
};
|
||||
export type MembraneOrientationParams = typeof MembraneOrientationParams
|
||||
export type MembraneOrientationProps = PD.Values<MembraneOrientationParams>
|
||||
|
||||
export { MembraneOrientation };
|
||||
|
||||
interface MembraneOrientation {
|
||||
// point in membrane boundary
|
||||
readonly planePoint1: Vec3,
|
||||
// point in opposite side of membrane boundary
|
||||
readonly planePoint2: Vec3,
|
||||
// normal vector of membrane layer
|
||||
readonly normalVector: Vec3,
|
||||
// the radius of the membrane layer
|
||||
readonly radius: number,
|
||||
readonly centroid: Vec3
|
||||
}
|
||||
|
||||
namespace MembraneOrientation {
|
||||
export enum Tag {
|
||||
Representation = 'membrane-orientation-3d'
|
||||
}
|
||||
|
||||
const pos = Vec3();
|
||||
export const symbols = {
|
||||
isTransmembrane: QuerySymbolRuntime.Dynamic(CustomPropSymbol('computed', 'membrane-orientation.is-transmembrane', Type.Bool),
|
||||
ctx => {
|
||||
const { unit, structure } = ctx.element;
|
||||
const { x, y, z } = StructureProperties.atom;
|
||||
if (!Unit.isAtomic(unit)) return 0;
|
||||
const membraneOrientation = MembraneOrientationProvider.get(structure).value;
|
||||
if (!membraneOrientation) return 0;
|
||||
Vec3.set(pos, x(ctx.element), y(ctx.element), z(ctx.element));
|
||||
const { normalVector, planePoint1, planePoint2 } = membraneOrientation!;
|
||||
return isInMembranePlane(pos, normalVector, planePoint1, planePoint2);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export const MembraneOrientationProvider: CustomStructureProperty.Provider<MembraneOrientationParams, MembraneOrientation> = CustomStructureProperty.createProvider({
|
||||
label: 'Membrane Orientation',
|
||||
descriptor: CustomPropertyDescriptor({
|
||||
name: 'anvil_computed_membrane_orientation',
|
||||
symbols: MembraneOrientation.symbols,
|
||||
// TODO `cifExport`
|
||||
}),
|
||||
type: 'root',
|
||||
defaultParams: MembraneOrientationParams,
|
||||
getParams: (data: Structure) => MembraneOrientationParams,
|
||||
isApplicable: (data: Structure) => true,
|
||||
obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<MembraneOrientationProps>) => {
|
||||
const p = { ...PD.getDefaultValues(MembraneOrientationParams), ...props };
|
||||
return { value: await computeAnvil(ctx, data, p) };
|
||||
}
|
||||
});
|
||||
|
||||
async function computeAnvil(ctx: CustomProperty.Context, data: Structure, props: Partial<ANVILProps>): Promise<MembraneOrientation> {
|
||||
await AccessibleSurfaceAreaProvider.attach(ctx, data);
|
||||
const p = { ...PD.getDefaultValues(ANVILParams), ...props };
|
||||
return await computeANVIL(data, p).runInContext(ctx.runtime);
|
||||
}
|
||||
156
src/extensions/anvil/representation.ts
Normal file
156
src/extensions/anvil/representation.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../mol-repr/representation';
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
|
||||
import { StructureRepresentationProvider, StructureRepresentation, StructureRepresentationStateBuilder } from '../../mol-repr/structure/representation';
|
||||
import { MembraneOrientation } from './prop';
|
||||
import { ThemeRegistryContext } from '../../mol-theme/theme';
|
||||
import { ShapeRepresentation } from '../../mol-repr/shape/representation';
|
||||
import { Shape } from '../../mol-model/shape';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { Lines } from '../../mol-geo/geometry/lines/lines';
|
||||
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
||||
import { LinesBuilder } from '../../mol-geo/geometry/lines/lines-builder';
|
||||
import { Circle } from '../../mol-geo/primitive/circle';
|
||||
import { transformPrimitive } from '../../mol-geo/primitive/primitive';
|
||||
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { MembraneOrientationProvider } from './prop';
|
||||
import { MarkerActions } from '../../mol-util/marker-action';
|
||||
import { lociLabel } from '../../mol-theme/label';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
|
||||
const SharedParams = {
|
||||
color: PD.Color(ColorNames.lightgrey),
|
||||
radiusFactor: PD.Numeric(0.8333, { min: 0.1, max: 3.0, step: 0.01 }, { description: 'Scale the radius of the membrane layer' })
|
||||
};
|
||||
|
||||
const BilayerSpheresParams = {
|
||||
...Spheres.Params,
|
||||
...SharedParams,
|
||||
sphereSize: PD.Numeric(1, { min: 0.1, max: 10, step: 0.1 }, { description: 'Size of spheres that represent membrane planes' }),
|
||||
density: PD.Numeric(1, { min: 0.25, max: 10, step: 0.25 }, { description: 'Distance between spheres'})
|
||||
};
|
||||
export type BilayerSpheresParams = typeof BilayerSpheresParams
|
||||
export type BilayerSpheresProps = PD.Values<BilayerSpheresParams>
|
||||
|
||||
const BilayerPlanesParams = {
|
||||
...Mesh.Params,
|
||||
...SharedParams,
|
||||
sectorOpacity: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
|
||||
};
|
||||
export type BilayerPlanesParams = typeof BilayerPlanesParams
|
||||
export type BilayerPlanesProps = PD.Values<BilayerPlanesParams>
|
||||
|
||||
const BilayerRimsParams = {
|
||||
...Lines.Params,
|
||||
...SharedParams,
|
||||
lineSizeAttenuation: PD.Boolean(true),
|
||||
linesSize: PD.Numeric(0.3, { min: 0.01, max: 50, step: 0.01 }),
|
||||
dashedLines: PD.Boolean(true),
|
||||
};
|
||||
export type BilayerRimsParams = typeof BilayerRimsParams
|
||||
export type BilayerRimsProps = PD.Values<BilayerRimsParams>
|
||||
|
||||
const MembraneOrientationVisuals = {
|
||||
'bilayer-planes': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerPlanesParams>) => ShapeRepresentation(getBilayerPlanes, Mesh.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }), modifyProps: p => ({ ...p, alpha: p.sectorOpacity, ignoreLight: true, doubleSided: false }) }),
|
||||
'bilayer-rims': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerRimsParams>) => ShapeRepresentation(getBilayerRims, Lines.Utils, { modifyState: s => ({ ...s, markerActions: MarkerActions.Highlighting }) })
|
||||
};
|
||||
|
||||
export const MembraneOrientationParams = {
|
||||
...BilayerSpheresParams,
|
||||
...BilayerPlanesParams,
|
||||
...BilayerRimsParams,
|
||||
visuals: PD.MultiSelect(['bilayer-planes', 'bilayer-rims'], PD.objectToOptions(MembraneOrientationVisuals)),
|
||||
};
|
||||
export type MembraneOrientationParams = typeof MembraneOrientationParams
|
||||
export type MembraneOrientationProps = PD.Values<MembraneOrientationParams>
|
||||
|
||||
export function getMembraneOrientationParams(ctx: ThemeRegistryContext, structure: Structure) {
|
||||
return PD.clone(MembraneOrientationParams);
|
||||
}
|
||||
|
||||
export type MembraneOrientationRepresentation = StructureRepresentation<MembraneOrientationParams>
|
||||
export function MembraneOrientationRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MembraneOrientationParams>): MembraneOrientationRepresentation {
|
||||
return Representation.createMulti('Membrane Orientation', ctx, getParams, StructureRepresentationStateBuilder, MembraneOrientationVisuals as unknown as Representation.Def<Structure, MembraneOrientationParams>);
|
||||
}
|
||||
|
||||
export const MembraneOrientationRepresentationProvider = StructureRepresentationProvider({
|
||||
name: 'membrane-orientation',
|
||||
label: 'Membrane Orientation',
|
||||
description: 'Displays a grid of points representing membrane layers.',
|
||||
factory: MembraneOrientationRepresentation,
|
||||
getParams: getMembraneOrientationParams,
|
||||
defaultValues: PD.getDefaultValues(MembraneOrientationParams),
|
||||
defaultColorTheme: { name: 'shape-group' },
|
||||
defaultSizeTheme: { name: 'shape-group' },
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0,
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, structure: Structure) => MembraneOrientationProvider.attach(ctx, structure, void 0, true),
|
||||
detach: (data) => MembraneOrientationProvider.ref(data, false)
|
||||
}
|
||||
});
|
||||
|
||||
function membraneLabel(data: Structure) {
|
||||
return `${lociLabel(Structure.Loci(data))} | Membrane Orientation`;
|
||||
}
|
||||
|
||||
function getBilayerRims(ctx: RuntimeContext, data: Structure, props: BilayerRimsProps, shape?: Shape<Lines>): Shape<Lines> {
|
||||
const { planePoint1: p1, planePoint2: p2, centroid, normalVector: normal, radius } = MembraneOrientationProvider.get(data).value!;
|
||||
const scaledRadius = props.radiusFactor * radius;
|
||||
const builder = LinesBuilder.create(128, 64, shape?.geometry);
|
||||
getLayerCircle(builder, p1, centroid, normal, scaledRadius, props);
|
||||
getLayerCircle(builder, p2, centroid, normal, scaledRadius, props);
|
||||
return Shape.create('Bilayer rims', data, builder.getLines(), () => props.color, () => props.linesSize, () => membraneLabel(data));
|
||||
}
|
||||
|
||||
function getLayerCircle(builder: LinesBuilder, p: Vec3, centroid: Vec3, normal: Vec3, radius: number, props: BilayerRimsProps, shape?: Shape<Lines>) {
|
||||
const circle = getCircle(p, centroid, normal, radius);
|
||||
const { indices, vertices } = circle;
|
||||
for (let j = 0, jl = indices.length; j < jl; j += 3) {
|
||||
if (props.dashedLines && j % 2 === 1) continue; // draw every other segment to get dashes
|
||||
const start = indices[j] * 3;
|
||||
const end = indices[j + 1] * 3;
|
||||
const startX = vertices[start];
|
||||
const startY = vertices[start + 1];
|
||||
const startZ = vertices[start + 2];
|
||||
const endX = vertices[end];
|
||||
const endY = vertices[end + 1];
|
||||
const endZ = vertices[end + 2];
|
||||
builder.add(startX, startY, startZ, endX, endY, endZ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const tmpMat = Mat4();
|
||||
function getCircle(p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
|
||||
Mat4.targetTo(tmpMat, p, centroid, normal);
|
||||
Mat4.setTranslation(tmpMat, p);
|
||||
Mat4.mul(tmpMat, tmpMat, Mat4.rotX90);
|
||||
|
||||
const circle = Circle({ radius, segments: 64 });
|
||||
return transformPrimitive(circle, tmpMat);
|
||||
}
|
||||
|
||||
function getBilayerPlanes(ctx: RuntimeContext, data: Structure, props: BilayerPlanesProps, shape?: Shape<Mesh>): Shape<Mesh> {
|
||||
const { planePoint1: p1, planePoint2: p2, centroid, normalVector: normal, radius } = MembraneOrientationProvider.get(data).value!;
|
||||
const state = MeshBuilder.createState(128, 64, shape && shape.geometry);
|
||||
const scaledRadius = props.radiusFactor * radius;
|
||||
getLayerPlane(state, p1, centroid, normal, scaledRadius);
|
||||
getLayerPlane(state, p2, centroid, normal, scaledRadius);
|
||||
return Shape.create('Bilayer planes', data, MeshBuilder.getMesh(state), () => props.color, () => 1, () => membraneLabel(data));
|
||||
}
|
||||
|
||||
function getLayerPlane(state: MeshBuilder.State, p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
|
||||
const circle = getCircle(p, centroid, normal, radius);
|
||||
state.currentGroup = 0;
|
||||
MeshBuilder.addPrimitive(state, Mat4.id, circle);
|
||||
MeshBuilder.addPrimitiveFlipped(state, Mat4.id, circle);
|
||||
}
|
||||
95
src/extensions/cellpack/color/generate.ts
Normal file
95
src/extensions/cellpack/color/generate.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { getPalette } from '../../../mol-util/color/palette';
|
||||
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
|
||||
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
|
||||
import { StructureElement, Bond, Model } from '../../../mol-model/structure';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CellPackInfoProvider } from '../property';
|
||||
import { distinctColors } from '../../../mol-util/color/distinct';
|
||||
import { Hcl } from '../../../mol-util/color/spaces/hcl';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Gives every model in a CellPack packing a unique generated color similar to other models in the packing.';
|
||||
|
||||
export const CellPackGenerateColorThemeParams = {};
|
||||
export type CellPackGenerateColorThemeParams = typeof CellPackGenerateColorThemeParams
|
||||
export function getCellPackGenerateColorThemeParams(ctx: ThemeDataContext) {
|
||||
return CellPackGenerateColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackGenerateColorThemeParams>): ColorTheme<CellPackGenerateColorThemeParams> {
|
||||
let color: LocationColor;
|
||||
let legend: ScaleLegend | TableLegend | undefined;
|
||||
|
||||
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
|
||||
|
||||
if (ctx.structure && info) {
|
||||
const colors = distinctColors(info.packingsCount);
|
||||
let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
|
||||
|
||||
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
|
||||
|
||||
const { models } = ctx.structure.root;
|
||||
|
||||
let size = 0;
|
||||
for (const m of models) size = Math.max(size, Model.TrajectoryInfo.get(m).size);
|
||||
|
||||
const palette = getPalette(size, { palette: {
|
||||
name: 'generate',
|
||||
params: {
|
||||
hue, chroma: [30, 80], luminance: [15, 85],
|
||||
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75
|
||||
}
|
||||
}}, { minLabel: 'Min', maxLabel: 'Max' });
|
||||
legend = palette.legend;
|
||||
const modelColor = new Map<number, Color>();
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
const idx = Model.TrajectoryInfo.get(models[i]).index;
|
||||
modelColor.set(Model.TrajectoryInfo.get(models[i]).index, palette.color(idx));
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
if (StructureElement.Location.is(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.unit.model).index)!;
|
||||
} else if (Bond.isLocation(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.aUnit.model).index)!;
|
||||
}
|
||||
return DefaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: CellPackGenerateColorTheme,
|
||||
granularity: 'instance',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend
|
||||
};
|
||||
}
|
||||
|
||||
export const CellPackGenerateColorThemeProvider: ColorTheme.Provider<CellPackGenerateColorThemeParams, 'cellpack-generate'> = {
|
||||
name: 'cellpack-generate',
|
||||
label: 'CellPack Generate',
|
||||
category: ColorTheme.Category.Chain,
|
||||
factory: CellPackGenerateColorTheme,
|
||||
getParams: getCellPackGenerateColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(CellPackGenerateColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => {
|
||||
return (
|
||||
!!ctx.structure && ctx.structure.elementCount > 0 &&
|
||||
!!CellPackInfoProvider.get(ctx.structure).value
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
75
src/extensions/cellpack/color/provided.ts
Normal file
75
src/extensions/cellpack/color/provided.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { ColorTheme, LocationColor } from '../../../mol-theme/color';
|
||||
import { ScaleLegend, TableLegend } from '../../../mol-util/legend';
|
||||
import { StructureElement, Model, Bond } from '../../../mol-model/structure';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CellPackInfoProvider } from '../property';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Gives every model in a CellPack the color provied in the packing data.';
|
||||
|
||||
export const CellPackProvidedColorThemeParams = {};
|
||||
export type CellPackProvidedColorThemeParams = typeof CellPackProvidedColorThemeParams
|
||||
export function getCellPackProvidedColorThemeParams(ctx: ThemeDataContext) {
|
||||
return CellPackProvidedColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function CellPackProvidedColorTheme(ctx: ThemeDataContext, props: PD.Values<CellPackProvidedColorThemeParams>): ColorTheme<CellPackProvidedColorThemeParams> {
|
||||
let color: LocationColor;
|
||||
let legend: ScaleLegend | TableLegend | undefined;
|
||||
|
||||
const info = ctx.structure && CellPackInfoProvider.get(ctx.structure).value;
|
||||
|
||||
if (ctx.structure && info?.colors) {
|
||||
const { models } = ctx.structure.root;
|
||||
const modelColor = new Map<number, Color>();
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
const idx = Model.TrajectoryInfo.get(models[i]).index;
|
||||
modelColor.set(Model.TrajectoryInfo.get(models[i]).index, info.colors[idx]);
|
||||
}
|
||||
|
||||
color = (location: Location): Color => {
|
||||
if (StructureElement.Location.is(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.unit.model).index)!;
|
||||
} else if (Bond.isLocation(location)) {
|
||||
return modelColor.get(Model.TrajectoryInfo.get(location.aUnit.model).index)!;
|
||||
}
|
||||
return DefaultColor;
|
||||
};
|
||||
} else {
|
||||
color = () => DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: CellPackProvidedColorTheme,
|
||||
granularity: 'instance',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend
|
||||
};
|
||||
}
|
||||
|
||||
export const CellPackProvidedColorThemeProvider: ColorTheme.Provider<CellPackProvidedColorThemeParams, 'cellpack-provided'> = {
|
||||
name: 'cellpack-provided',
|
||||
label: 'CellPack Provided',
|
||||
category: ColorTheme.Category.Chain,
|
||||
factory: CellPackProvidedColorTheme,
|
||||
getParams: getCellPackProvidedColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(CellPackProvidedColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => {
|
||||
return (
|
||||
!!ctx.structure && ctx.structure.elementCount > 0 &&
|
||||
Model.TrajectoryInfo.get(ctx.structure.models[0]).size > 1 &&
|
||||
!!CellPackInfoProvider.get(ctx.structure).value?.colors
|
||||
);
|
||||
}
|
||||
};
|
||||
229
src/extensions/cellpack/curve.ts
Normal file
229
src/extensions/cellpack/curve.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* 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, segmentLength: number, resample: boolean) {
|
||||
let new_points: Vec3[] = [];
|
||||
if (resample) new_points = ResampleControlPoints(points, segmentLength);
|
||||
else {
|
||||
for (let idx = 0; idx < points.length / 3; ++idx){
|
||||
new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
|
||||
}
|
||||
}
|
||||
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]);
|
||||
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;
|
||||
}
|
||||
84
src/extensions/cellpack/data.ts
Normal file
84
src/extensions/cellpack/data.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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
|
||||
paths: [string, string][] // [name: string, path: string][]
|
||||
version: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface Compartment {
|
||||
surface?: Packing
|
||||
interior?: Packing
|
||||
}
|
||||
|
||||
export interface Packing {
|
||||
ingredients: { [key: string]: Ingredient }
|
||||
}
|
||||
|
||||
export interface Positions {
|
||||
coords?: Vec3[];
|
||||
}
|
||||
|
||||
export interface Radii {
|
||||
radii?: number[];
|
||||
}
|
||||
|
||||
export interface Ingredient {
|
||||
source: IngredientSource;
|
||||
results: [Vec3, Quat][];
|
||||
name: string;
|
||||
/** Vec3[]];CoarseGraind Beads coordinates LOD */
|
||||
positions?: [Positions];
|
||||
/** number[]];CoarseGraind Beads radii LOD */
|
||||
radii?: [Radii];
|
||||
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
|
||||
nbCurve?: number;
|
||||
uLength?: number;
|
||||
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
|
||||
[curveX: string]: unknown;
|
||||
/** the orientation in the membrane */
|
||||
principalAxis?: Vec3;
|
||||
/** offset along membrane */
|
||||
offset?: Vec3;
|
||||
ingtype?: string;
|
||||
color?: Vec3;
|
||||
confidence?: number;
|
||||
}
|
||||
|
||||
export interface IngredientSource {
|
||||
pdb: string;
|
||||
bu?: string; /** biological unit e.g AU,BU1,etc.. */
|
||||
selection?: string; /** NGL selection or :A or :B etc.. */
|
||||
model?: string; /** model number e.g 0,1,2... */
|
||||
transform: {
|
||||
center: boolean;
|
||||
translate?: Vec3;
|
||||
};
|
||||
biomt?: boolean;
|
||||
}
|
||||
32
src/extensions/cellpack/index.ts
Normal file
32
src/extensions/cellpack/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior';
|
||||
import { LoadCellPackModel } from './model';
|
||||
import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
import { CellPackProvidedColorThemeProvider } from './color/provided';
|
||||
|
||||
export const CellPack = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
|
||||
name: 'cellpack',
|
||||
category: 'custom-props',
|
||||
display: {
|
||||
name: 'CellPack',
|
||||
description: 'CellPack Model Loading and Viewing.'
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
|
||||
register(): void {
|
||||
this.ctx.state.data.actions.add(LoadCellPackModel);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackGenerateColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(CellPackProvidedColorThemeProvider);
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.state.data.actions.remove(LoadCellPackModel);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackGenerateColorThemeProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(CellPackProvidedColorThemeProvider);
|
||||
}
|
||||
}
|
||||
});
|
||||
584
src/extensions/cellpack/model.ts
Normal file
584
src/extensions/cellpack/model.ts
Normal file
@@ -0,0 +1,584 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { StateAction, StateBuilder, StateTransformer, State } 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, IngredientSource, CellPacking } from './data';
|
||||
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
|
||||
import { trajectoryFromPDB } from '../../mol-model-formats/structure/pdb';
|
||||
import { Mat4, Vec3, Quat } from '../../mol-math/linear-algebra';
|
||||
import { SymmetryOperator } from '../../mol-math/geometry';
|
||||
import { Task, RuntimeContext } from '../../mol-task';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies } from './state';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { getMatFromResamplePoints } from './curve';
|
||||
import { compile } from '../../mol-script/runtime/query/compiler';
|
||||
import { CifCategory, CifField } from '../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../mol-data/db';
|
||||
import { createModels } from '../../mol-model-formats/structure/basic/parser';
|
||||
import { CellpackPackingPreset, CellpackMembranePreset } from './preset';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`;
|
||||
}
|
||||
|
||||
class TrajectoryCache {
|
||||
private map = new Map<string, Trajectory>();
|
||||
set(id: string, trajectory: Trajectory) { this.map.set(id, trajectory); }
|
||||
get(id: string) { return this.map.get(id); }
|
||||
}
|
||||
|
||||
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
|
||||
const assetManager = plugin.managers.asset;
|
||||
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
|
||||
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
|
||||
let trajectory = trajCache.get(id);
|
||||
let assets: Asset.Wrapper[] = [];
|
||||
if (!trajectory) {
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const cif = (await parseCif(plugin, text.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
const binary = await plugin.runTask(assetManager.resolve(file, 'binary'));
|
||||
assets.push(binary);
|
||||
const cif = (await parseCif(plugin, binary.data)).blocks[0];
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(cif));
|
||||
} else if (file.name.endsWith('.pdb')) {
|
||||
const text = await plugin.runTask(assetManager.resolve(file, 'string'));
|
||||
assets.push(text);
|
||||
const pdb = await parsePDBfile(plugin, text.data, id);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(pdb));
|
||||
} else {
|
||||
throw new Error(`unsupported file type '${file.name}'`);
|
||||
}
|
||||
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
|
||||
if (surface){
|
||||
try {
|
||||
const data = await getFromOPM(plugin, id, assetManager);
|
||||
assets.push(data.asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} catch (e) {
|
||||
// fallback to getFromPdb
|
||||
// console.error(e);
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
|
||||
assets.push(asset);
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));
|
||||
}
|
||||
} else {
|
||||
const data = await getFromCellPackDB(plugin, id, baseUrl, assetManager);
|
||||
assets.push(data.asset);
|
||||
if ('pdb' in data) {
|
||||
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
|
||||
} else {
|
||||
trajectory = await plugin.runTask(trajectoryFromMmCIF(data.mmcif));
|
||||
}
|
||||
}
|
||||
trajCache.set(id, trajectory!);
|
||||
}
|
||||
const model = await plugin.resolveTask(trajectory?.getFrameAtIndex(modelIndex)!);
|
||||
return { model, assets };
|
||||
}
|
||||
|
||||
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
|
||||
let structure = Structure.ofModel(model);
|
||||
const { assembly } = props;
|
||||
|
||||
if (assembly) {
|
||||
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
|
||||
}
|
||||
let query;
|
||||
if (source.selection){
|
||||
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
|
||||
query = MS.struct.modifier.union([
|
||||
MS.struct.generator.atomGroups({
|
||||
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
|
||||
'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('auth_asym_id')])
|
||||
})
|
||||
]);
|
||||
} else {
|
||||
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 getTransformLegacy(trans: Vec3, rot: Quat) {
|
||||
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2]);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
Mat4.transpose(m, m);
|
||||
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0));
|
||||
Mat4.setTranslation(m, trans);
|
||||
return m;
|
||||
}
|
||||
|
||||
function getTransform(trans: Vec3, rot: Quat) {
|
||||
const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3]);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
const p: Vec3 = Vec3.create(trans[0], trans[1], trans[2]);
|
||||
Mat4.setTranslation(m, p);
|
||||
return m;
|
||||
}
|
||||
|
||||
function getResultTransforms(results: Ingredient['results'], legacy: boolean) {
|
||||
if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]));
|
||||
else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]));
|
||||
}
|
||||
|
||||
function getCurveTransforms(ingredient: Ingredient) {
|
||||
const n = ingredient.nbCurve || 0;
|
||||
const instances: Mat4[] = [];
|
||||
let segmentLength = 3.4;
|
||||
if (ingredient.uLength){
|
||||
segmentLength = ingredient.uLength;
|
||||
} else if (ingredient.radii){
|
||||
segmentLength = ingredient.radii[0].radii
|
||||
? ingredient.radii[0].radii[0] * 2.0
|
||||
: 3.4;
|
||||
}
|
||||
let resampling: boolean = false;
|
||||
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;
|
||||
}
|
||||
// test for resampling
|
||||
let distance: number = Vec3.distance(_points[0], _points[1]);
|
||||
if (distance >= segmentLength + 2.0) {
|
||||
console.info(distance);
|
||||
resampling = true;
|
||||
}
|
||||
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, segmentLength, resampling);
|
||||
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], { assembly: { id, operId: i, 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(plugin: PluginContext, name: string, ingredient: Ingredient, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model);
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = MmcifFormat.fromFrame(cif);
|
||||
const models = await createModels(format.data.db, format, ctx);
|
||||
return models.representative;
|
||||
});
|
||||
|
||||
const curveModel = await plugin.runTask(curveModelTask);
|
||||
return getStructure(plugin, curveModel, ingredient.source);
|
||||
}
|
||||
|
||||
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
|
||||
const { name, source, results, nbCurve } = ingredient;
|
||||
if (source.pdb === 'None') return;
|
||||
|
||||
const file = ingredientFiles[source.pdb];
|
||||
if (!file) {
|
||||
// TODO can these be added to the library?
|
||||
if (name === 'HIV1_CAhex_0_1_0') return; // 1VU4CtoH_hex.pdb
|
||||
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return; // 1AK4fitTo1VU4hex.pdb
|
||||
if (name === 'iLDL') return; // EMD-5239
|
||||
if (name === 'peptides') return; // peptide.pdb
|
||||
if (name === 'lypoglycane') return;
|
||||
}
|
||||
|
||||
// model id in case structure is NMR
|
||||
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
|
||||
if (!model) return;
|
||||
|
||||
let structure: Structure;
|
||||
if (nbCurve) {
|
||||
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
|
||||
} else {
|
||||
let bu: string|undefined = source.bu ? source.bu : undefined;
|
||||
if (bu){
|
||||
if (bu === 'AU') {
|
||||
bu = undefined;
|
||||
} else {
|
||||
bu = bu.slice(2);
|
||||
}
|
||||
}
|
||||
structure = await getStructure(plugin, model, source, { assembly: bu });
|
||||
// transform with offset and pcp
|
||||
let legacy: boolean = true;
|
||||
if (ingredient.offset || ingredient.principalAxis){
|
||||
legacy = false;
|
||||
const structureMean = getStructureMean(structure);
|
||||
Vec3.negate(structureMean, structureMean);
|
||||
const m1: Mat4 = Mat4.identity();
|
||||
Mat4.setTranslation(m1, structureMean);
|
||||
structure = Structure.transform(structure, m1);
|
||||
if (ingredient.offset){
|
||||
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())){
|
||||
const m: Mat4 = Mat4.identity();
|
||||
Mat4.setTranslation(m, ingredient.offset);
|
||||
structure = Structure.transform(structure, m);
|
||||
}
|
||||
}
|
||||
if (ingredient.principalAxis){
|
||||
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
|
||||
const q: Quat = Quat.identity();
|
||||
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
|
||||
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
|
||||
structure = Structure.transform(structure, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
structure = getAssembly(getResultTransforms(results, legacy), structure);
|
||||
}
|
||||
|
||||
return { structure, assets };
|
||||
}
|
||||
|
||||
export function createStructureFromCellPack(plugin: PluginContext, packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
|
||||
return Task.create('Create Packing Structure', async ctx => {
|
||||
const { ingredients, name } = packing;
|
||||
const assets: Asset.Wrapper[] = [];
|
||||
const trajCache = new TrajectoryCache();
|
||||
const structures: Structure[] = [];
|
||||
const colors: Color[] = [];
|
||||
let skipColors: boolean = false;
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) await ctx.update(iName);
|
||||
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache);
|
||||
if (ingredientStructure) {
|
||||
structures.push(ingredientStructure.structure);
|
||||
assets.push(...ingredientStructure.assets);
|
||||
const c = ingredients[iName].color;
|
||||
if (c){
|
||||
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
|
||||
} else {
|
||||
skipColors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - units`);
|
||||
const units: Unit[] = [];
|
||||
let offsetInvariantId = 0;
|
||||
let offsetChainGroupId = 0;
|
||||
for (const s of structures) {
|
||||
if (ctx.shouldUpdate) await ctx.update(`${s.label}`);
|
||||
let maxInvariantId = 0;
|
||||
let maxChainGroupId = 0;
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId;
|
||||
const chainGroupId = u.chainGroupId + offsetChainGroupId;
|
||||
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
|
||||
units.push(Unit.create(units.length, invariantId, chainGroupId, u.traits, u.kind, u.model, u.conformation.operator, u.elements, u.props));
|
||||
}
|
||||
offsetInvariantId += maxInvariantId + 1;
|
||||
offsetChainGroupId += maxChainGroupId + 1;
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
|
||||
const structure = new Structure(units);
|
||||
for( let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
return { structure, assets, colors: skipColors ? undefined : colors };
|
||||
});
|
||||
}
|
||||
|
||||
async function handleHivRna(plugin: PluginContext, packings: CellPacking[], baseUrl: string) {
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${baseUrl}/extras/rna_allpoints.json`);
|
||||
const json = await plugin.runTask(plugin.managers.asset.resolve(url, 'json', false));
|
||||
const points = json.data.points as 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMembrane(plugin: PluginContext, name: string, state: State, params: LoadCellPackModelParams) {
|
||||
let file: Asset.File | undefined = undefined;
|
||||
if (params.ingredients !== null) {
|
||||
const fileName = `${name}.bcif`;
|
||||
for (const f of params.ingredients) {
|
||||
if (fileName === f.name) {
|
||||
file = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!file){
|
||||
// check for cif directly
|
||||
const cifileName = `${name}.cif`;
|
||||
for (const f of params.ingredients) {
|
||||
if (cifileName === f.name) {
|
||||
file = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let b = state.build().toRoot();
|
||||
if (file) {
|
||||
if (file.name.endsWith('.cif')) {
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: false, label: file.name }, { state: { isGhost: true } });
|
||||
} else if (file.name.endsWith('.bcif')) {
|
||||
b = b.apply(StateTransforms.Data.ReadFile, { file, isBinary: true, label: file.name }, { state: { isGhost: true } });
|
||||
}
|
||||
} else {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}.bcif`);
|
||||
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
|
||||
}
|
||||
|
||||
const membrane = await b.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(StructureFromAssemblies, undefined, { state: { isGhost: true } })
|
||||
.commit({ revertOnError: true });
|
||||
|
||||
const membraneParams = {
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
|
||||
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
|
||||
}
|
||||
|
||||
async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, state: State, params: LoadCellPackModelParams) {
|
||||
const ingredientFiles = params.ingredients || [];
|
||||
|
||||
let cellPackJson: StateBuilder.To<PSO.Format.Json, StateTransformer<PSO.Data.String, PSO.Format.Json>>;
|
||||
if (params.source.name === 'id') {
|
||||
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
|
||||
cellPackJson = state.build().toRoot()
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.source.params }, { state: { isGhost: true } });
|
||||
} else {
|
||||
const file = params.source.params;
|
||||
if (!file?.file) {
|
||||
plugin.log.error('No file selected');
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonFile: Asset.File;
|
||||
if (file.name.toLowerCase().endsWith('.zip')) {
|
||||
const data = await readFromFile(file.file, 'zip').runInContext(runtime);
|
||||
jsonFile = Asset.File(new File([data['model.json']], 'model.json'));
|
||||
objectForEach(data, (v, k) => {
|
||||
if (k === 'model.json') return;
|
||||
ingredientFiles.push(Asset.File(new File([v], k)));
|
||||
});
|
||||
} else {
|
||||
jsonFile = file;
|
||||
}
|
||||
|
||||
cellPackJson = state.build().toRoot()
|
||||
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
|
||||
}
|
||||
|
||||
const cellPackBuilder = cellPackJson
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack);
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
|
||||
const { packings } = cellPackObject.obj!.data;
|
||||
|
||||
await handleHivRna(plugin, packings, params.baseUrl);
|
||||
|
||||
for (let i = 0, il = packings.length; i < il; ++i) {
|
||||
const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles };
|
||||
|
||||
const packing = await state.build()
|
||||
.to(cellPackBuilder.ref)
|
||||
.apply(StructureFromCellpack, p)
|
||||
.commit({ revertOnError: true });
|
||||
|
||||
const packingParams = {
|
||||
traceOnly: params.preset.traceOnly,
|
||||
representation: params.preset.representation,
|
||||
};
|
||||
await CellpackPackingPreset.apply(packing, packingParams, plugin);
|
||||
if ( packings[i].location === 'surface' && params.membrane){
|
||||
await loadMembrane(plugin, packings[i].name, state, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LoadCellPackModelParams = {
|
||||
source: PD.MappedStatic('id', {
|
||||
'id': PD.Select('InfluenzaModel2.json', [
|
||||
['blood_hiv_immature_inside.json', 'Blood HIV immature'],
|
||||
['HIV_immature_model.json', 'HIV immature'],
|
||||
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'Blood HIV'],
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV'],
|
||||
['influenza_model1.json', 'Influenza envelope'],
|
||||
['InfluenzaModel2.json', 'Influenza Complete'],
|
||||
['ExosomeModel.json', 'Exosome Model'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
|
||||
['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
|
||||
] as const, { description: 'Download the model definition with `id` from the server at `baseUrl.`' }),
|
||||
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.' }),
|
||||
}, { options: [['id', 'Id'], ['file', 'File']] }),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
membrane: PD.Boolean(true),
|
||||
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredients' }),
|
||||
preset: PD.Group({
|
||||
traceOnly: PD.Boolean(false),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation']))
|
||||
}, { isExpanded: true })
|
||||
};
|
||||
type LoadCellPackModelParams = PD.Values<typeof LoadCellPackModelParams>
|
||||
|
||||
export const LoadCellPackModel = StateAction.build({
|
||||
display: { name: 'Load CellPack', description: 'Open or download a model' },
|
||||
params: LoadCellPackModelParams,
|
||||
from: PSO.Root
|
||||
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
|
||||
await loadPackings(ctx, taskCtx, state, params);
|
||||
}));
|
||||
95
src/extensions/cellpack/preset.ts
Normal file
95
src/extensions/cellpack/preset.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { CellPackGenerateColorThemeProvider } from './color/generate';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { CellPackProvidedColorThemeProvider } from './color/provided';
|
||||
|
||||
export const CellpackPackingPresetParams = {
|
||||
traceOnly: PD.Boolean(true),
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
|
||||
};
|
||||
export type CellpackPackingPresetParams = PD.ValuesFor<typeof CellpackPackingPresetParams>
|
||||
|
||||
export const CellpackPackingPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure-representation-cellpack-packing',
|
||||
display: { name: 'CellPack Packing' },
|
||||
params: () => CellpackPackingPresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
if (!structureCell) return {};
|
||||
|
||||
const reprProps = {
|
||||
ignoreHydrogens: true,
|
||||
traceOnly: params.traceOnly
|
||||
};
|
||||
const components = {
|
||||
polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
|
||||
};
|
||||
|
||||
if (params.representation === 'gaussian-surface') {
|
||||
Object.assign(reprProps, {
|
||||
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
|
||||
});
|
||||
} else if (params.representation === 'spacefill' && params.traceOnly) {
|
||||
Object.assign(reprProps, { sizeFactor: 2 });
|
||||
}
|
||||
|
||||
const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
|
||||
const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
|
||||
const representations = {
|
||||
polymer: builder.buildRepresentation<any>(update, components.polymer, { type: params.representation, typeParams: { ...typeParams, ...reprProps }, color }, { tag: 'polymer' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
export const CellpackMembranePresetParams = {
|
||||
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['gaussian-surface', 'spacefill', 'point', 'orientation'])),
|
||||
};
|
||||
export type CellpackMembranePresetParams = PD.ValuesFor<typeof CellpackMembranePresetParams>
|
||||
|
||||
export const CellpackMembranePreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure-representation-cellpack-membrane',
|
||||
display: { name: 'CellPack Membrane' },
|
||||
params: () => CellpackMembranePresetParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
if (!structureCell) return {};
|
||||
|
||||
const reprProps = {
|
||||
ignoreHydrogens: true,
|
||||
};
|
||||
const components = {
|
||||
membrane: await presetStaticComponent(plugin, structureCell, 'all', { label: 'Membrane' })
|
||||
};
|
||||
|
||||
if (params.representation === 'gaussian-surface') {
|
||||
Object.assign(reprProps, {
|
||||
quality: 'custom', resolution: 10, radiusOffset: 2, doubleSided: false
|
||||
});
|
||||
}
|
||||
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
|
||||
const representations = {
|
||||
membrane: builder.buildRepresentation(update, components.membrane, { type: 'gaussian-surface', typeParams: { ...typeParams, ...reprProps }, color: 'uniform', colorParams: { value: ColorNames.lightgrey } }, { tag: 'all' })
|
||||
};
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
|
||||
return { components, representations };
|
||||
}
|
||||
});
|
||||
37
src/extensions/cellpack/property.ts
Normal file
37
src/extensions/cellpack/property.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
|
||||
|
||||
export type CellPackInfoValue = {
|
||||
packingsCount: number
|
||||
packingIndex: number
|
||||
colors?: Color[]
|
||||
}
|
||||
|
||||
const CellPackInfoParams = {
|
||||
info: PD.Value<CellPackInfoValue>({ packingsCount: 1, packingIndex: 0, colors: undefined }, { isHidden: true })
|
||||
};
|
||||
type CellPackInfoParams = PD.Values<typeof CellPackInfoParams>
|
||||
|
||||
export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellPackInfoParams, CellPackInfoValue> = CustomStructureProperty.createProvider({
|
||||
label: 'CellPack Info',
|
||||
descriptor: CustomPropertyDescriptor({ name: 'cellpack-info' }),
|
||||
type: 'root',
|
||||
defaultParams: CellPackInfoParams,
|
||||
getParams: (data: Structure) => CellPackInfoParams,
|
||||
isApplicable: (data: Structure) => true,
|
||||
obtain: async (ctx: CustomProperty.Context, data: Structure, props: CellPackInfoParams) => {
|
||||
return {
|
||||
value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
|
||||
};
|
||||
}
|
||||
});
|
||||
150
src/extensions/cellpack/state.ts
Normal file
150
src/extensions/cellpack/state.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 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';
|
||||
import { IngredientFiles } from './util';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { CellPackInfoProvider } from './property';
|
||||
import { Structure, StructureSymmetry, Unit, Model } from '../../mol-model/structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
|
||||
|
||||
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 => {
|
||||
const options = a ? a.data.packings.map((d, i) => [i, d.name] as const) : [];
|
||||
return {
|
||||
packing: PD.Select(0, options),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
ingredientFiles: PD.FileList({ accept: '.cif,.bcif,.pdb' })
|
||||
};
|
||||
}
|
||||
})({
|
||||
apply({ a, params, cache }, plugin: PluginContext) {
|
||||
return Task.create('Structure from CellPack', async ctx => {
|
||||
const packing = a.data.packings[params.packing];
|
||||
const ingredientFiles: IngredientFiles = {};
|
||||
if (params.ingredientFiles !== null) {
|
||||
for (const file of params.ingredientFiles) {
|
||||
ingredientFiles[file.name] = file;
|
||||
}
|
||||
}
|
||||
const { structure, assets, colors } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
|
||||
await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
|
||||
info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
|
||||
});
|
||||
|
||||
(cache as any).assets = assets;
|
||||
return new PSO.Molecule.Structure(structure, { label: packing.name });
|
||||
});
|
||||
},
|
||||
dispose({ b, cache }) {
|
||||
const assets = (cache as any).assets as Asset.Wrapper[];
|
||||
if(assets) {
|
||||
for (const a of assets) a.dispose();
|
||||
}
|
||||
|
||||
if (b) {
|
||||
b.data.customPropertyDescriptors.dispose();
|
||||
for (const m of b.data.models) {
|
||||
m.customProperties.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export { StructureFromAssemblies };
|
||||
type StructureFromAssemblies = typeof StructureFromAssemblies
|
||||
const StructureFromAssemblies = PluginStateTransform.BuiltIn({
|
||||
name: 'Structure from all assemblies',
|
||||
display: { name: 'Structure from all assemblies' },
|
||||
from: PSO.Molecule.Model,
|
||||
to: PSO.Molecule.Structure,
|
||||
params: {
|
||||
}
|
||||
})({
|
||||
canAutoUpdate({ newParams }) {
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
return Task.create('Build Structure', async ctx => {
|
||||
// TODO: optimze
|
||||
// TODO: think of ways how to fast-track changes to this for animations
|
||||
const model = a.data;
|
||||
let initial_structure = Structure.ofModel(model);
|
||||
const structures: Structure[] = [];
|
||||
let structure: Structure = initial_structure;
|
||||
// the list of asambly *?
|
||||
const symmetry = ModelSymmetry.Provider.get(model);
|
||||
if (symmetry && symmetry.assemblies.length !== 0){
|
||||
for (const a of symmetry.assemblies) {
|
||||
const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
|
||||
structures.push(s);
|
||||
}
|
||||
const builder = Structure.Builder();
|
||||
let offsetInvariantId = 0;
|
||||
for (const s of structures) {
|
||||
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 + 1;
|
||||
}
|
||||
structure = builder.getStructure();
|
||||
for( let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
}
|
||||
return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
|
||||
});
|
||||
},
|
||||
dispose({ b }) {
|
||||
b?.data.customPropertyDescriptors.dispose();
|
||||
}
|
||||
});
|
||||
77
src/extensions/cellpack/util.ts
Normal file
77
src/extensions/cellpack/util.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 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';
|
||||
import { AssetManager, Asset } from '../../mol-util/assets';
|
||||
import { Structure } from '../../mol-model/structure';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
|
||||
export async function parseCif(plugin: PluginContext, data: string | Uint8Array) {
|
||||
const comp = CIF.parse(data);
|
||||
const parsed = await plugin.runTask(comp);
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
export async function parsePDBfile(plugin: PluginContext, data: string, id: string) {
|
||||
const comp = parsePDB(data, id);
|
||||
const parsed = await plugin.runTask(comp);
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function downloadCif(plugin: PluginContext, url: string, isBinary: boolean, assetManager: AssetManager) {
|
||||
const type = isBinary ? 'binary' : 'string';
|
||||
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, url), type));
|
||||
return { cif: await parseCif(plugin, asset.data), asset };
|
||||
}
|
||||
|
||||
async function downloadPDB(plugin: PluginContext, url: string, id: string, assetManager: AssetManager) {
|
||||
const asset = await assetManager.resolve(Asset.getUrlAsset(assetManager, url), 'string').run();
|
||||
return { pdb: await parsePDBfile(plugin, asset.data, id), asset };
|
||||
}
|
||||
|
||||
export async function getFromPdb(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
|
||||
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId.toUpperCase()}.bcif`, true, assetManager);
|
||||
return { mmcif: cif.blocks[0], asset };
|
||||
}
|
||||
|
||||
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager){
|
||||
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, `https://opm-assets.storage.googleapis.com/pdb/${pdbId.toLowerCase()}.pdb`), 'string'));
|
||||
return { pdb: await parsePDBfile(plugin, asset.data, pdbId), asset };
|
||||
}
|
||||
|
||||
export async function getFromCellPackDB(plugin: PluginContext, id: string, baseUrl: string, assetManager: AssetManager) {
|
||||
if (id.toLowerCase().endsWith('.cif') || id.toLowerCase().endsWith('.bcif')) {
|
||||
const isBinary = id.toLowerCase().endsWith('.bcif');
|
||||
const { cif, asset } = await downloadCif(plugin, `${baseUrl}/other/${id}`, isBinary, assetManager);
|
||||
return { mmcif: cif.blocks[0], asset };
|
||||
} else {
|
||||
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id;
|
||||
return await downloadPDB(plugin, `${baseUrl}/other/${name}.pdb`, name, assetManager);
|
||||
}
|
||||
}
|
||||
|
||||
export type IngredientFiles = { [name: string]: Asset.File }
|
||||
|
||||
export function getStructureMean(structure: Structure) {
|
||||
let xSum = 0, ySum = 0, zSum = 0;
|
||||
for (let i = 0, il = structure.units.length; i < il; ++i) {
|
||||
const unit = structure.units[i];
|
||||
const { elements } = unit;
|
||||
const { x, y, z } = unit.conformation;
|
||||
for (let j = 0, jl = elements.length; j < jl; ++j) {
|
||||
const eI = elements[j];
|
||||
xSum += x(eI);
|
||||
ySum += y(eI);
|
||||
zSum += z(eI);
|
||||
}
|
||||
}
|
||||
const { elementCount } = structure;
|
||||
return Vec3.create(xSum / elementCount, ySum / elementCount, zSum / elementCount);
|
||||
}
|
||||
10
src/extensions/dnatco/README.md
Normal file
10
src/extensions/dnatco/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## DNATCO Extensions
|
||||
|
||||
### Confal Pyramids
|
||||
|
||||
The Confal Pyramids extensions displays tetrahedron-like pyramids. These pyramids are a simple visual representation of nucleotide conformer classes that can be assigned to individual steps in nucleic acid structures.
|
||||
|
||||
For more information, see:
|
||||
* [Černý et al., Nucleic Acids Research, 44, W284 (2016)](http://dx.doi.org/10.1093/nar/gkw381)
|
||||
* [Schneider et al., Acta Cryst D, 74, 52-64 (2018)](http://dx.doi.org/10.1107/S2059798318000050)
|
||||
* [Schneider et al., Genes, 8(10), 278, (2017)](http://dx.doi.org/10.3390/genes8100278)
|
||||
103
src/extensions/dnatco/confal-pyramids/behavior.ts
Normal file
103
src/extensions/dnatco/confal-pyramids/behavior.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramidsColorThemeProvider } from './color';
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsRepresentationProvider } from './representation';
|
||||
import { Loci } from '../../../mol-model/loci';
|
||||
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
|
||||
import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../../mol-state';
|
||||
import { Task } from '../../../mol-task';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
|
||||
export const DnatcoConfalPyramidsPreset = StructureRepresentationPresetProvider({
|
||||
id: 'preset-structure-representation-confal-pyramids',
|
||||
display: {
|
||||
name: 'Confal Pyramids', group: 'Annotation',
|
||||
description: 'Schematic depiction of conformer class and confal value.',
|
||||
},
|
||||
isApplicable(a) {
|
||||
return a.data.models.length >= 1 && a.data.models.some(m => ConfalPyramids.isApplicable(m));
|
||||
},
|
||||
params: () => StructureRepresentationPresetProvider.CommonParams,
|
||||
async apply(ref, params, plugin) {
|
||||
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
|
||||
const model = structureCell?.obj?.data.model;
|
||||
if (!structureCell || !model) return {};
|
||||
|
||||
await plugin.runTask(Task.create('Confal Pyramids', async runtime => {
|
||||
await ConfalPyramidsProvider.attach({ runtime, assetManager: plugin.managers.asset }, model);
|
||||
}));
|
||||
|
||||
const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params }, plugin);
|
||||
|
||||
const pyramids = await plugin.builders.structure.tryCreateComponentStatic(structureCell, 'nucleic', { label: 'Confal Pyramids' });
|
||||
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
|
||||
|
||||
let pyramidsRepr;
|
||||
if (representations)
|
||||
pyramidsRepr = builder.buildRepresentation(update, pyramids, { type: ConfalPyramidsRepresentationProvider, typeParams, color: ConfalPyramidsColorThemeProvider }, { tag: 'confal-pyramdis' } );
|
||||
|
||||
await update.commit({ revertOnError: true });
|
||||
return { components: { ...components, pyramids }, representations: { ...representations, pyramidsRepr } };
|
||||
}
|
||||
});
|
||||
|
||||
export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean, showToolTip: boolean }>({
|
||||
name: 'dnatco-confal-pyramids-prop',
|
||||
category: 'custom-props',
|
||||
display: {
|
||||
name: 'Confal Pyramids',
|
||||
description: 'Schematic depiction of conformer class and confal value.',
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
|
||||
|
||||
private provider = ConfalPyramidsProvider;
|
||||
|
||||
private labelConfalPyramids = {
|
||||
label: (loci: Loci): string | undefined => {
|
||||
if (!this.params.showToolTip) return void 0;
|
||||
|
||||
/* TODO: Implement this */
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
|
||||
this.ctx.managers.lociLabels.addProvider(this.labelConfalPyramids);
|
||||
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
|
||||
this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.registerPreset(DnatcoConfalPyramidsPreset);
|
||||
}
|
||||
|
||||
update(p: { autoAttach: boolean, showToolTip: boolean }) {
|
||||
const updated = this.params.autoAttach !== p.autoAttach;
|
||||
this.params.autoAttach = p.autoAttach;
|
||||
this.params.showToolTip = p.showToolTip;
|
||||
this.ctx.customModelProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
|
||||
return updated;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
|
||||
this.ctx.managers.lociLabels.removeProvider(this.labelConfalPyramids);
|
||||
|
||||
this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
|
||||
this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
|
||||
|
||||
this.ctx.builders.structure.representation.unregisterPreset(DnatcoConfalPyramidsPreset);
|
||||
}
|
||||
},
|
||||
params: () => ({
|
||||
autoAttach: PD.Boolean(true),
|
||||
showToolTip: PD.Boolean(true)
|
||||
})
|
||||
});
|
||||
186
src/extensions/dnatco/confal-pyramids/color.ts
Normal file
186
src/extensions/dnatco/confal-pyramids/color.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Michal Malý <michal.maly@ibt.cas.cz>
|
||||
* @author Jiří Černý <jiri.cerny@ibt.cas.cz>
|
||||
*/
|
||||
|
||||
import { ConfalPyramids, ConfalPyramidsProvider } from './property';
|
||||
import { ConfalPyramidsTypes as CPT } from './types';
|
||||
import { Location } from '../../../mol-model/location';
|
||||
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
|
||||
import { ColorTheme } from '../../../mol-theme/color';
|
||||
import { ThemeDataContext } from '../../../mol-theme/theme';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { TableLegend } from '../../../mol-util/legend';
|
||||
import { iterableToArray } from '../../../mol-data/util';
|
||||
|
||||
const DefaultColor = Color(0xCCCCCC);
|
||||
const Description = 'Assigns colors to confal pyramids';
|
||||
const ErrorColor = Color(0xFFA10A);
|
||||
|
||||
type ConformerClasses = 'A' | 'B' | 'BII' | 'miB' | 'Z' | 'IC' | 'OPN' | 'SYN' | 'N';
|
||||
|
||||
const ColorMapping: ReadonlyMap<ConformerClasses, Color> = new Map([
|
||||
['A', Color(0xFFC1C1)],
|
||||
['B', Color(0xC8CFFF)],
|
||||
['BII', Color(0x0059DA)],
|
||||
['miB', Color(0x3BE8FB)],
|
||||
['Z', Color(0x01F60E)],
|
||||
['IC', Color(0xFA5CFB)],
|
||||
['OPN', Color(0xE90000)],
|
||||
['SYN', Color(0xFFFF01)],
|
||||
['N', Color(0xF2F2F2)],
|
||||
]);
|
||||
|
||||
const NtCToClasses: ReadonlyMap<string, [ConformerClasses, ConformerClasses]> = new Map([
|
||||
['NANT', ['N', 'N']],
|
||||
['AA00', ['A', 'A']],
|
||||
['AA02', ['A', 'A']],
|
||||
['AA03', ['A', 'A']],
|
||||
['AA04', ['A', 'A']],
|
||||
['AA08', ['A', 'A']],
|
||||
['AA09', ['A', 'A']],
|
||||
['AA01', ['A', 'A']],
|
||||
['AA05', ['A', 'A']],
|
||||
['AA06', ['A', 'A']],
|
||||
['AA10', ['A', 'A']],
|
||||
['AA11', ['A', 'A']],
|
||||
['AA07', ['A', 'A']],
|
||||
['AA12', ['A', 'A']],
|
||||
['AA13', ['A', 'A']],
|
||||
['AB01', ['A', 'B']],
|
||||
['AB02', ['A', 'B']],
|
||||
['AB03', ['A', 'B']],
|
||||
['AB04', ['A', 'B']],
|
||||
['AB05', ['A', 'B']],
|
||||
['BA01', ['B', 'A']],
|
||||
['BA05', ['B', 'A']],
|
||||
['BA09', ['B', 'A']],
|
||||
['BA08', ['BII', 'A']],
|
||||
['BA10', ['B', 'A']],
|
||||
['BA13', ['BII', 'A']],
|
||||
['BA16', ['BII', 'A']],
|
||||
['BA17', ['BII', 'A']],
|
||||
['BB00', ['B', 'B']],
|
||||
['BB01', ['B', 'B']],
|
||||
['BB17', ['B', 'B']],
|
||||
['BB02', ['B', 'B']],
|
||||
['BB03', ['B', 'B']],
|
||||
['BB11', ['B', 'B']],
|
||||
['BB16', ['B', 'B']],
|
||||
['BB04', ['B', 'BII']],
|
||||
['BB05', ['B', 'BII']],
|
||||
['BB07', ['BII', 'BII']],
|
||||
['BB08', ['BII', 'BII']],
|
||||
['BB10', ['miB', 'miB']],
|
||||
['BB12', ['miB', 'miB']],
|
||||
['BB13', ['miB', 'miB']],
|
||||
['BB14', ['miB', 'miB']],
|
||||
['BB15', ['miB', 'miB']],
|
||||
['BB20', ['miB', 'miB']],
|
||||
['IC01', ['IC', 'IC']],
|
||||
['IC02', ['IC', 'IC']],
|
||||
['IC03', ['IC', 'IC']],
|
||||
['IC04', ['IC', 'IC']],
|
||||
['IC05', ['IC', 'IC']],
|
||||
['IC06', ['IC', 'IC']],
|
||||
['IC07', ['IC', 'IC']],
|
||||
['OP01', ['OPN', 'OPN']],
|
||||
['OP02', ['OPN', 'OPN']],
|
||||
['OP03', ['OPN', 'OPN']],
|
||||
['OP04', ['OPN', 'OPN']],
|
||||
['OP05', ['OPN', 'OPN']],
|
||||
['OP06', ['OPN', 'OPN']],
|
||||
['OP07', ['OPN', 'OPN']],
|
||||
['OP08', ['OPN', 'OPN']],
|
||||
['OP09', ['OPN', 'OPN']],
|
||||
['OP10', ['OPN', 'OPN']],
|
||||
['OP11', ['OPN', 'OPN']],
|
||||
['OP12', ['OPN', 'OPN']],
|
||||
['OP13', ['OPN', 'OPN']],
|
||||
['OP14', ['OPN', 'OPN']],
|
||||
['OP15', ['OPN', 'OPN']],
|
||||
['OP16', ['OPN', 'OPN']],
|
||||
['OP17', ['OPN', 'OPN']],
|
||||
['OP18', ['OPN', 'OPN']],
|
||||
['OP19', ['OPN', 'OPN']],
|
||||
['OP20', ['OPN', 'OPN']],
|
||||
['OP21', ['OPN', 'OPN']],
|
||||
['OP22', ['OPN', 'OPN']],
|
||||
['OP23', ['OPN', 'OPN']],
|
||||
['OP24', ['OPN', 'OPN']],
|
||||
['OP25', ['OPN', 'OPN']],
|
||||
['OP26', ['OPN', 'OPN']],
|
||||
['OP27', ['OPN', 'OPN']],
|
||||
['OP28', ['OPN', 'OPN']],
|
||||
['OP29', ['OPN', 'OPN']],
|
||||
['OP30', ['OPN', 'OPN']],
|
||||
['OP31', ['OPN', 'OPN']],
|
||||
['OPS1', ['OPN', 'OPN']],
|
||||
['OP1S', ['OPN', 'SYN']],
|
||||
['AAS1', ['SYN', 'A']],
|
||||
['AB1S', ['A', 'SYN']],
|
||||
['AB2S', ['A', 'SYN']],
|
||||
['BB1S', ['B', 'SYN']],
|
||||
['BB2S', ['B', 'SYN']],
|
||||
['BBS1', ['SYN', 'B']],
|
||||
['ZZ01', ['Z', 'Z']],
|
||||
['ZZ02', ['Z', 'Z']],
|
||||
['ZZ1S', ['Z', 'SYN']],
|
||||
['ZZ2S', ['Z', 'SYN']],
|
||||
['ZZS1', ['SYN', 'Z']],
|
||||
['ZZS2', ['SYN', 'Z']],
|
||||
]);
|
||||
|
||||
function getConformerColor(ntc: string, useLower: boolean): Color {
|
||||
const item = NtCToClasses.get(ntc);
|
||||
if (!item) return ErrorColor;
|
||||
return ColorMapping.get(useLower ? item[1] : item[0]) ?? ErrorColor;
|
||||
}
|
||||
|
||||
export const ConfalPyramidsColorThemeParams = {};
|
||||
export type ConfalPyramidsColorThemeParams = typeof ConfalPyramidsColorThemeParams
|
||||
export function getConfalPyramidsColorThemeParams(ctx: ThemeDataContext) {
|
||||
return ConfalPyramidsColorThemeParams; // TODO return copy
|
||||
}
|
||||
|
||||
export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values<ConfalPyramidsColorThemeParams>): ColorTheme<ConfalPyramidsColorThemeParams> {
|
||||
function color(location: Location, isSecondary: boolean): Color {
|
||||
if (CPT.isLocation(location)) {
|
||||
const { pyramid, isLower } = location.data;
|
||||
return getConformerColor(pyramid.NtC, isLower);
|
||||
}
|
||||
|
||||
return DefaultColor;
|
||||
}
|
||||
|
||||
return {
|
||||
factory: ConfalPyramidsColorTheme,
|
||||
granularity: 'group',
|
||||
color,
|
||||
props,
|
||||
description: Description,
|
||||
legend: TableLegend(iterableToArray(ColorMapping.entries()).map(([conformer, color]) => {
|
||||
return [conformer, color] as [string, Color];
|
||||
}).concat([
|
||||
[ 'Error', ErrorColor ],
|
||||
[ 'Unknown', DefaultColor ]
|
||||
]))
|
||||
};
|
||||
}
|
||||
|
||||
export const ConfalPyramidsColorThemeProvider: ColorTheme.Provider<ConfalPyramidsColorThemeParams, 'confal-pyramids'> = {
|
||||
name: 'confal-pyramids',
|
||||
label: 'Confal Pyramids',
|
||||
category: ColorTheme.Category.Residue,
|
||||
factory: ConfalPyramidsColorTheme,
|
||||
getParams: getConfalPyramidsColorThemeParams,
|
||||
defaultValues: PD.getDefaultValues(ConfalPyramidsColorThemeParams),
|
||||
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => ConfalPyramids.isApplicable(m)),
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ConfalPyramidsProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
|
||||
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ConfalPyramidsProvider.descriptor, false)
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user