Compare commits

...

919 Commits

Author SHA1 Message Date
dsehnal
9bec644997 3.2.0 2022-02-17 19:24:55 +01:00
dsehnal
650d38dff8 Store IndexPairBonds as a dynamic property 2022-02-17 19:21:49 +01:00
David Sehnal
097277e397 Merge pull request #373 from JonStargaryen/master
Add TraceOnly option for Structure Superposition
2022-02-14 19:53:51 +01:00
Sebastian Bittrich
82a4d5eedf cleanup 2022-02-14 10:46:03 -08:00
Sebastian Bittrich
2a00248812 determine trace during buildIndex 2022-02-14 10:41:10 -08:00
Sebastian Bittrich
9841c773cb reset camera after all alignments 2022-02-10 13:55:09 -08:00
Sebastian Bittrich
b21a78ad14 support shuffled atoms when aligning SIFTS trace 2022-02-10 13:49:37 -08:00
Sebastian Bittrich
7329fa597d options obj for alignAndSuperposeWithSIFTSMapping 2022-02-10 11:36:50 -08:00
Sebastian Bittrich
f1d8f0ecb4 auth 2022-02-10 11:32:21 -08:00
Sebastian Bittrich
1d127f2364 changelog 2022-02-10 11:30:55 -08:00
Sebastian Bittrich
b244405cc3 polymer-based query 2022-02-10 10:39:22 -08:00
Sebastian Bittrich
38adfe0ca6 cleanup 2022-02-10 10:29:34 -08:00
Sebastian Bittrich
6f0d798847 optional filtering for trace during alignment 2022-02-10 10:23:31 -08:00
Sebastian Bittrich
6e58bfd2b0 traceOnly param for superpos 2022-02-08 16:03:05 -08:00
David Sehnal
bc2e8d8ac4 Merge pull request #369 from molstar/pdbx_sifts-export
Better support for atom_site.pdbx_sifts_xref
2022-02-08 10:19:02 +01:00
dsehnal
7be654d47f tweak labels 2022-02-08 10:18:01 +01:00
dsehnal
bfe46e3604 pdbx_sifts_xref PR feedback 2022-02-08 10:12:21 +01:00
dsehnal
008b597fc5 changelog 2022-02-07 17:51:13 +01:00
dsehnal
c4b4f2e3b1 rename BestDatabaseSequenceMapping -> SIFTSMapping 2022-02-07 17:49:10 +01:00
dsehnal
76ee97301b atom_site.pdbx_label_index support 2022-02-07 17:44:03 +01:00
dsehnal
289dc09eae add support for atom_site.pdbx_sifts_xref export 2022-02-07 17:33:11 +01:00
Alexander Rose
f23f84f0f3 3.1.0 2022-02-06 15:56:41 -08:00
Alexander Rose
62259f3295 changelog 2022-02-06 15:51:40 -08:00
Alexander Rose
854a430a12 tweak quick-styles order 2022-02-06 15:46:40 -08:00
Alexander Rose
d27cdb5637 schema updates 2022-02-06 15:41:09 -08:00
Alexander Rose
7d12d9ee90 package udpdates 2022-02-06 15:38:09 -08:00
Alexander Rose
d70a4ff347 Merge pull request #366 from molstar/repr-defaults-tweaks
cleaner default representation style
2022-02-06 15:24:27 -08:00
Alexander Rose
49541558d1 Merge branch 'master' into repr-defaults-tweaks 2022-02-06 15:24:16 -08:00
Alexander Rose
7b00a1227c changelog 2022-02-06 15:22:50 -08:00
Alexander Rose
7800603c81 Merge pull request #367 from molstar/quick-styles
Quick styles
2022-02-06 15:16:43 -08:00
Alexander Rose
fca00c8116 variable naming 2022-02-06 15:15:55 -08:00
Alexander Rose
00fa549e44 Merge pull request #368 from JonStargaryen/master
iterate over `structure.unitSymmetryGroups`, fixes #364
2022-02-06 15:13:57 -08:00
Alexander Rose
36181b6b87 tweak quick style names 2022-02-06 15:13:15 -08:00
JonStargaryen
88a95162e9 iterate over structure.unitSymmetryGroups, fixes #364 2022-02-06 10:47:40 -08:00
Alexander Rose
9c1d59a2c8 add Quick Styles panel 2022-02-05 12:58:32 -08:00
Alexander Rose
fdd894956a fix representation preset side effects 2022-02-05 12:56:55 -08:00
Alexander Rose
eb6dc0859d cleaner default representation style 2022-02-05 12:23:28 -08:00
Alexander Rose
b99026bba2 add ignoreLight to component params 2022-02-05 12:10:56 -08:00
Alexander Rose
6fa50eb8d5 fix xrayShader & ignoreLight not working together 2022-02-05 11:57:39 -08:00
David Sehnal
e5046f15a9 Merge pull request #365 from JonStargaryen/master
Volume Streaming Error Message
2022-02-04 17:11:31 +01:00
JonStargaryen
09f1c066a0 logic 2022-02-03 17:11:50 -08:00
Sebastian Bittrich
ed2f0b34c9 volume streaming err msg 2022-02-03 16:11:10 -08:00
Alexander Rose
c7f75861de 3.0.2 2022-01-30 12:24:48 -08:00
Alexander Rose
ccd04dbc9d changelog 2022-01-30 12:19:49 -08:00
Alexander Rose
e71f8d2c10 Merge pull request #360 from molstar/fix/visual-visibility
Fix visual visibility edge case
2022-01-30 12:17:12 -08:00
dsehnal
d9b4c60239 Fix visual visibility edge case 2022-01-30 15:02:46 +01:00
Alexander Rose
103c1fca21 measurement options tweaks
- allow larger text size
- make customText essential
2022-01-29 19:30:03 -08:00
Alexander Rose
49559bf5fb citation tweak 2022-01-29 16:05:17 -08:00
Alexander Rose
26dceabf83 add citation file 2022-01-29 15:56:39 -08:00
Alexander Rose
abe506182e fix multi-instance entity label display
- fix empty elements created in extendToAllInstances
2022-01-29 11:21:48 -08:00
Alexander Rose
582a0e2a38 fix Sphere.expand for highly directional extrema 2022-01-29 11:13:19 -08:00
dsehnal
2784ccf379 3.0.1 2022-01-27 12:15:53 +01:00
dsehnal
0ad1d578fe changelog 2022-01-27 12:13:22 +01:00
David Sehnal
31fd1c9c68 Merge pull request #353 from molstar/drag-tweak
emit drag event whenever started within viewport
2022-01-27 12:12:07 +01:00
David Sehnal
9c961297a2 Merge branch 'master' into drag-tweak 2022-01-27 12:11:59 +01:00
David Sehnal
b920053349 Merge pull request #352 from molstar/var-fixes
Various fixes
2022-01-27 12:09:39 +01:00
David Sehnal
0a5c764e4a Merge pull request #351 from molstar/volume-server-data-fix
Volume server data fix
2022-01-27 11:37:31 +01:00
Alexander Rose
b9a71c83ff emit drag event whenever started within viewport 2022-01-26 21:32:14 -08:00
Alexander Rose
3255f207d0 Merge branch 'master' of https://github.com/molstar/molstar into var-fixes 2022-01-26 20:46:11 -08:00
Alexander Rose
e3b4ca8862 add entity-id and entity-source as carbonColor 2022-01-26 20:42:38 -08:00
Alexander Rose
6810793015 fix marking of InteractionsInterUnitVisual 2022-01-26 20:40:12 -08:00
Alexander Rose
1feb3c2095 fix entity-id coloring broken for non-ihm models 2022-01-26 20:38:46 -08:00
Alexander Rose
f2da6033d0 fix pdbe xray maps url 2022-01-26 20:36:52 -08:00
Alexander Rose
28bc212132 fix marking pass for transparentBackground 2022-01-26 20:35:38 -08:00
dsehnal
1a7c62eec6 remove gl from dependencies & install it on demand instead
- installing gl on M1 Mac was taking several minutes to compile on each update
2022-01-26 23:12:12 +01:00
dsehnal
de67dbacba iso-value adjustment for VolumeServer data in default Viewer 2022-01-26 17:47:23 +01:00
dsehnal
57223a0f9a Fix VolumeServer/query CLI 2022-01-26 16:49:18 +01:00
Alexander Rose
2ad0754b90 3.0.0 2022-01-23 18:12:48 -08:00
Alexander Rose
3ecb3af57b changelog 2022-01-23 18:08:01 -08:00
Alexander Rose
ec4f15f549 improve/fix InteractionsIntraUnitVisual marking 2022-01-23 17:55:51 -08:00
Alexander Rose
2458ea7b92 Merge pull request #349 from molstar/custom-theme-colors
add custom theme colors
2022-01-23 15:13:24 -08:00
Alexander Rose
c5e6bedf11 Merge branch 'master' into custom-theme-colors 2022-01-23 13:54:57 -08:00
dsehnal
8528e5a666 Support/bugfixes for atom_site.pdbx_sifts_xref categories 2022-01-23 20:46:49 +01:00
dsehnal
6ed232b3d9 fix using default values for webgl1/wboit features in the default viewer app 2022-01-23 19:25:09 +01:00
dsehnal
f8aae8cbd1 skip Coarse models in export extension 2022-01-23 16:13:02 +01:00
Alexander Rose
00c2517045 package updates 2022-01-22 14:06:26 -08:00
Alexander Rose
99b043a929 avoid circular dependency 2022-01-22 11:10:41 -08:00
Alexander Rose
5900e27e39 add custom theme colors
- element-symbol
- molecule-type
- residue-name
- secondary-structure
2022-01-22 10:51:41 -08:00
Alexander Rose
1b79d34907 fix marking of carbohydrate visuals 2022-01-22 08:53:47 -08:00
Alexander Rose
fc52e29c92 re-add using _struct_asym cat in getStructAsymMap
- needed for ihm model support
2022-01-22 08:50:29 -08:00
Alexander Rose
df23b3c0fe fix coarse model support in entity-id color theme
- add StructureProperties.coarse.entity_id
2022-01-22 08:48:07 -08:00
David Sehnal
5e25716c98 Merge pull request #334 from molstar/export-extension
Model export extension & related improvements
2022-01-18 13:23:11 +01:00
dsehnal
f70a10bc56 add info about CIF export failure 2022-01-18 13:20:53 +01:00
dsehnal
0ccb045f4e Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-18 12:30:45 +01:00
Alexander Rose
fa18d0d852 3.0.0-dev.10 2022-01-17 14:08:28 -08:00
Alexander Rose
687c4342fb changelog 2022-01-17 14:03:55 -08:00
Alexander Rose
9459af46b8 Merge pull request #337 from molstar/anim-rock
Add rock animation
2022-01-17 13:38:08 -08:00
Alexander Rose
fc5832747a Merge pull request #345 from molstar/bump-immutable
bump immutable to 4.0
2022-01-17 13:37:19 -08:00
Alexander Rose
01205d244b tweak trackball speed calc 2022-01-17 13:16:19 -08:00
dsehnal
31a555255a bump immutable to 4.0 2022-01-17 17:56:39 +01:00
dsehnal
fbb60c9493 Treat empty string as non-present value in BinaryCIF 2022-01-17 16:21:14 +01:00
dsehnal
9f953ef51c add Model Export overlay 2022-01-17 15:47:56 +01:00
dsehnal
4871f1547c Generate structAsymMap from normalized atomic hierarchy
- Fixes issue with remapped chains
- Requires to parse PRD separately
2022-01-17 14:52:53 +01:00
dsehnal
d6413529f4 PR feedback 2022-01-17 13:58:20 +01:00
dsehnal
724cf5a0da Add integrations section to readme 2022-01-17 12:58:35 +01:00
dsehnal
b6847907ca Add ModelExport to viewer/app.ts 2022-01-17 11:37:49 +01:00
dsehnal
fb54a1aed7 Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-17 11:37:16 +01:00
Alexander Rose
9815318daf add entity-source option to illustrative coloring 2022-01-16 23:08:43 -08:00
Alexander Rose
bc13b98111 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-16 15:48:33 -08:00
Alexander Rose
238b70c121 simplify rock state/trackball animation
- use sin() instead of smoothstep()
2022-01-16 15:47:25 -08:00
Alexander Rose
dcd23bc0cb improve illustrative style support
- add illustrative representation preset
- add style option to illustrative color theme
2022-01-16 13:13:00 -08:00
Alexander Rose
4694ea85fa support custom colors in molecule-type theme 2022-01-16 13:05:43 -08:00
Alexander Rose
bdb17743d7 update schemas 2022-01-15 19:54:22 -08:00
Alexander Rose
8b76ff2461 update packages 2022-01-15 19:53:04 -08:00
Alexander Rose
ea5421002b add camera rock state animation 2022-01-15 18:59:12 -08:00
Alexander Rose
76ac55917d type, tweaks 2022-01-15 18:45:56 -08:00
Alexander Rose
5cfb2376c4 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-15 15:56:55 -08:00
Alexander Rose
6b9d3fd80e cleaner MembraneOrientationVisuals defaults 2022-01-15 13:52:14 -08:00
dsehnal
a09752b62e changelog and contibutors in package.json 2022-01-14 12:53:51 +01:00
David Sehnal
3134e1d9f9 Merge pull request #341 from molstar/fix-camera-spin-stutter
Pass animation info to state animations
2022-01-14 12:51:51 +01:00
David Sehnal
e94ecf2a0b Merge pull request #314 from ptourlas/feature/formal-charge-labels
Feature/formal charge labels
2022-01-14 12:51:09 +01:00
ptourlas
1bd4d841a1 ADD: Test multiple charge lines in molfiles 2022-01-12 17:01:36 +02:00
ptourlas
8e349f47a5 (author tags) 2022-01-12 16:50:06 +02:00
ptourlas
119c0a4231 ADD: Test formal charge parsing in sdf reader 2022-01-12 16:25:23 +02:00
ptourlas
f009f533e0 TWEAK: Default charges in V3000 sdf files 2022-01-12 15:05:07 +02:00
ptourlas
3ab0c1e509 ADD: Test formal charge parsing in mol reader 2022-01-12 12:42:29 +02:00
dsehnal
e3d264e239 Pass animation info to state animations
+ Fix camera stutter for "camera spin"
2022-01-11 19:38:32 +01:00
dsehnal
9bd60f8e8e Fix getOperatorsForIndex 2022-01-11 17:34:43 +01:00
ptourlas
bcec1d9637 (forgot to cleanup) 2022-01-11 16:41:23 +02:00
ptourlas
1b431b1d20 RFR: Formal charge assignment at model creation 2022-01-11 16:04:46 +02:00
ptourlas
23c2dcdfd4 FIX: Use tokenizer to ensure the loop terminates 2022-01-11 15:01:15 +02:00
ptourlas
dd415bf802 FTR: Support multiple formal charge lines
* Start with two empty arrays for indexes and charges.
* Each `M CHG` line is passed to `handleFormalCharges()` and the
parsed elements are pushed to the arrays.
* The `Column`s are made at the end of this process.
2022-01-11 14:40:57 +02:00
ptourlas
7bc0e9db7c RFR: formalChargeMapper tweaks
* The key is of type number, no need to stringify it.
* Simplified `switch()`.
2022-01-11 00:18:25 +02:00
ptourlas
ca10bb01db Merge branch 'molstar:master' into feature/formal-charge-labels 2022-01-10 23:46:37 +02:00
Alexander Rose
c0f14b7c33 3.0.0-dev.9 2022-01-09 18:24:30 -08:00
Alexander Rose
b096f328fc changelog 2022-01-09 18:19:52 -08:00
Alexander Rose
88dbd43884 re-allow interaction during trackball animation
- was disallowed as a stop-gap measure
- ok after improving temporal multi sampling
2022-01-09 14:39:09 -08:00
Alexander Rose
ade5e4d4b8 re-allow interaction during trackball animation
- was disallowed as a stop-gap measure
- ok after improving temporal multi sampling
2022-01-09 14:30:43 -08:00
ptourlas
cb76b53a1b FTR: Add formal charges during model creation
This implementation takes into account both the property and atom block
cases and makes sure the latter is ignored if the first one is present.
2022-01-10 00:30:25 +02:00
Alexander Rose
b9423f70d4 Merge branch 'master' of https://github.com/molstar/molstar into anim-rock 2022-01-09 14:25:36 -08:00
Alexander Rose
d61e18e6f3 fix mol2 element symbol assignment 2022-01-09 14:04:31 -08:00
Alexander Rose
ca4a725a79 Merge pull request #336 from molstar/bond-dist-id
IndexPairBonds improvements
2022-01-09 13:37:38 -08:00
Alexander Rose
17a18d5fea tweak bond assignment from IndexPairBonds
- fix & clarify logic
2022-01-09 13:25:14 -08:00
Alexander Rose
73be238ac4 rename IndexPairBonds mapping field from id to key 2022-01-09 12:51:26 -08:00
Alexander Rose
796a034fec add rock animation to trackball controls 2022-01-08 17:02:33 -08:00
Alexander Rose
952b320975 Merge branch 'master' of https://github.com/molstar/molstar into bond-dist-id 2022-01-08 13:23:15 -08:00
Alexander Rose
be0f06ff0f fix mol2 crysin support 2022-01-08 13:22:26 -08:00
Alexander Rose
6294ef2db2 fix stats for single element in multi-chain unit
- observe in, e.g., label for water molecule in 3pqr
2022-01-08 12:42:37 -08:00
Alexander Rose
78b5d505bd improve IndexPairBonds
- add id field
- better distance-based assignment
2022-01-08 12:35:40 -08:00
Alexander Rose
22afdffa15 add mol2 symmetry support
- only for spacegroup setting 1
2022-01-08 12:14:14 -08:00
Alexander Rose
d1056eddeb Merge pull request #335 from molstar/standalone-viewer
move Viewer class to separate file
2022-01-08 11:55:53 -08:00
dsehnal
8655f4d85a move viewer app to separate file 2022-01-08 10:31:53 +01:00
ptourlas
f9deb54352 FIX: Found the right tokenizer operations
(at last)
2022-01-08 01:16:59 +02:00
ptourlas
eae3c1b33a FIX: Formal charges are all on the same line
Therefore we have to get the count of charges and iterate based on that.
A handle properties function is added so that new handler can be used based
the property type. Note that this function only returns the charges at
the moment for simplicity. A more general version should return multiple
properties.
2022-01-07 16:14:47 +02:00
ptourlas
5c5f8aa741 FTR:(WIP) Add formal charges during model creation 2022-01-06 16:12:44 +02:00
ptourlas
ba68ac2e32 FTR: Parse formal charges in the atom block 2022-01-06 15:58:49 +02:00
ptourlas
239fef281e Merge branch 'master' into feature/formal-charge-labels 2022-01-06 13:32:05 +02:00
dsehnal
c0880b647f changelog 2022-01-03 13:51:09 +01:00
dsehnal
039dc6a76b changelog 2022-01-03 13:49:59 +01:00
dsehnal
042a7625ad isWithoutOperator tweak 2022-01-03 13:46:13 +01:00
dsehnal
41827c478d open zip files with multiple entries 2022-01-03 13:42:37 +01:00
dsehnal
9a73180c3c support transformed export & structAsymMap parsing fix 2022-01-03 13:15:51 +01:00
dsehnal
333ee85fdb omit suffix for identity assembly operators 2022-01-03 12:52:05 +01:00
dsehnal
fa8ca45b6a do not include assembly categories in export if an operator has been applied 2022-01-03 12:34:37 +01:00
dsehnal
c2bae1aeb7 model export extension 2022-01-03 12:26:36 +01:00
Alexander Rose
ada7a45fe6 add PDBj pdb-provider option 2022-01-01 16:56:23 -08:00
Alexander Rose
2d09df55a9 3.0.0-dev.8 2021-12-31 11:42:09 -08:00
Alexander Rose
ec2554537e changelog 2021-12-31 11:37:40 -08:00
Alexander Rose
f266dfadc6 emulate round function for webgl1 compatibility 2021-12-31 11:37:02 -08:00
Alexander Rose
99048eed61 Merge pull request #323 from molstar/dv-refactor
Direct-volume refactor
2021-12-31 10:43:26 -08:00
Alexander Rose
fe63718b0c Merge branch 'master' into dv-refactor 2021-12-31 10:34:50 -08:00
Alexander Rose
2c1200433c Merge pull request #333 from molstar/surface-size-theme
Use size theme in molecular/gaussian surface & label representations
2021-12-31 10:33:46 -08:00
Alexander Rose
4a3252c929 use size theme in label repr 2021-12-30 18:17:40 -08:00
Alexander Rose
5e052174ee use size theme in molecular/gaussian surface repr 2021-12-30 18:12:53 -08:00
Alexander Rose
cda0966105 filter repr size themes to applicable types 2021-12-30 18:01:02 -08:00
Alexander Rose
c0a9716846 Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-30 17:30:44 -08:00
Alexander Rose
18c7395f9d Merge pull request #326 from molstar/marker-fixes
Marker improvements
2021-12-30 17:29:07 -08:00
Alexander Rose
d3da79f3dd better use of StateSelection API 2021-12-30 17:18:21 -08:00
Alexander Rose
5a215daca4 remove superfluous arg from canvas3d.requestDraw 2021-12-30 17:14:07 -08:00
Alexander Rose
8527a3b3ef change canvas3d draw to use options argument 2021-12-30 17:08:28 -08:00
Alexander Rose
492dc1ba32 Merge branch 'master' of https://github.com/molstar/molstar into marker-fixes 2021-12-30 17:04:17 -08:00
Alexander Rose
305a8ca802 Merge pull request #332 from molstar/fix-331
fix gpu isosurface group id
2021-12-30 17:02:54 -08:00
Alexander Rose
6d2a35494f rename float-packing to number-packing
- more telling name
2021-12-30 17:01:23 -08:00
Alexander Rose
e76a08c73a remove unused en/decodeFloatLog 2021-12-30 16:59:39 -08:00
Alexander Rose
a0fef0c20f rename en/decodeFloatRGB
- since they only work for positive integers
- encodeFloatRGB -> packIntToRGB
- decodeFloatRGB -> unpackRGBToInt
2021-12-30 16:57:50 -08:00
Alexander Rose
605432ddd1 fix glsl decodeFloatRGB 2021-12-30 16:39:06 -08:00
Alexander Rose
0c895071d8 support axis order in direct-volume shader 2021-12-30 15:36:12 -08:00
Alexander Rose
79cd833ae6 cleanup 2021-12-30 14:37:34 -08:00
Alexander Rose
0fee928e37 fix gpu isosurface group id
- was wrong for axis order other than 012
- fixes #331
2021-12-30 13:49:08 -08:00
Alexander Rose
7f698336d7 fix webgl error in volume-streaming behavior
- don't use gpu mc for small volumes
- return empty texture-mesh for volumes of size 1 or 0
2021-12-29 18:51:55 -08:00
Alexander Rose
cef04f192a no picking during camera spinning 2021-12-29 16:54:26 -08:00
Alexander Rose
4087c4c226 changelog 2021-12-29 16:27:43 -08:00
Alexander Rose
87bdcd2372 add prefer-webgl1 & disable-wboit Viewer GET params 2021-12-29 16:03:45 -08:00
Alexander Rose
1dbcc0d7c8 Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-29 15:42:41 -08:00
Alexander Rose
4c93f01c64 improve temporal multi-smaple handling
- add forceOn arg
- instead of letting temporal converge do full sampling
- saves one geometry pass
2021-12-28 20:39:03 -08:00
Alexander Rose
0a18412da0 refactor draw/multiSample .render args 2021-12-28 20:24:16 -08:00
Alexander Rose
b67d16bdc4 enable marking pass by default 2021-12-28 19:47:54 -08:00
Alexander Rose
976542d355 ensure mark changes are rendered (even w/ noDraw)
- schedule using forceNextRender to avoid rendering twice
2021-12-28 19:30:45 -08:00
Alexander Rose
a2e5fda646 improve temporal multi-sample rendering
- don't render all when spinning
- only render all when explicitely requested (new allowMulti arg)
2021-12-28 19:28:36 -08:00
Alexander Rose
41b1b65d5f improve SelectLoci behavior
- only handle direct children of updated objects (no update of same repr multiple times)
2021-12-28 19:25:40 -08:00
Alexander Rose
2ec2d1997f improve StructureSelectionManager.onUpdate
- only process for root structure (processing childs is superfluous)
2021-12-28 19:20:54 -08:00
Alexander Rose
0bc65f3b72 improve visual marking performance
- treat structure-loci as every-loci
2021-12-28 19:17:46 -08:00
Alexander Rose
9ed96b3599 fix getMarkersAverage edge case with small arrays 2021-12-28 19:16:08 -08:00
David Sehnal
b1cf9566f6 Merge pull request #324 from molstar/safari15-wboit
PluginFeatureDetection and disable WBOIT in Safari
2021-12-28 20:46:46 +01:00
ptourlas
13ea97bd98 Merge branch 'master' into feature/formal-charge-labels 2021-12-27 18:18:27 +02:00
ptourlas
009a17a9ca (forgot the author tag) 2021-12-27 17:56:25 +02:00
ptourlas
ca38d9adb1 (WIP) Formal charges handler implementation 2021-12-27 17:44:59 +02:00
ptourlas
4e5a86e3db (WIP) Add the formal charges handler to sdf parser 2021-12-27 17:37:52 +02:00
dsehnal
983ae4f8c2 PluginFeatureDetection and disable WBOIT in Safari 2021-12-24 11:21:15 +01:00
Alexander Rose
7ce3531cc7 direct-volume define cleanup 2021-12-23 15:35:14 -08:00
Alexander Rose
11f1a7fd1c add general dGeometryType
- remove dRenderMode & dGeoTexture
2021-12-23 15:16:14 -08:00
Alexander Rose
47d7dd4d22 move direct-volume coloring into theme
- add 'direct' color type
- remove color from transfer-function (now only alpha)
- add direct-volume color theme support
- add volume-value color theme
2021-12-23 14:47:17 -08:00
Alexander Rose
0d4f6bb5d9 3.0.0-dev.7 2021-12-20 22:42:23 -08:00
Alexander Rose
b3a4e1976d changelog 2021-12-20 22:36:56 -08:00
Alexander Rose
8b3c0fd94e Merge branch 'master' of https://github.com/molstar/molstar into dv-refactor 2021-12-20 17:51:38 -08:00
Alexander Rose
ab4a24d8ab Merge pull request #310 from molstar/clipping
Per Object Clip Objects
2021-12-20 17:38:41 -08:00
Alexander Rose
7c93e9f834 changelog & fix clipping exclude group filtering 2021-12-20 17:22:15 -08:00
Alexander Rose
90ea8cfebd Merge branch 'master' of https://github.com/molstar/molstar into clipping 2021-12-20 16:49:58 -08:00
Alexander Rose
7fc1866dac Merge pull request #320 from molstar/shader-compilation
Shader compilation
2021-12-20 16:46:56 -08:00
Alexander Rose
bb520ff424 fix spec 2021-12-20 16:38:24 -08:00
Alexander Rose
f9d2e20cb9 webgl1 compat 2021-12-20 16:16:22 -08:00
Alexander Rose
6f13b67bf1 combine markingDepth/markingMask shader variants 2021-12-20 16:14:37 -08:00
Alexander Rose
f37026a980 Merge branch 'master' of https://github.com/molstar/molstar into shader-compilation 2021-12-20 14:15:06 -08:00
Alexander Rose
ddc4d8e867 3.0.0-dev.6 2021-12-19 13:15:50 -08:00
Alexander Rose
7cded03598 changelog 2021-12-19 13:11:41 -08:00
Alexander Rose
744b04edc6 add demos to deploy script
- include analytics in deployed demos
2021-12-19 13:06:41 -08:00
Alexander Rose
f2119b1d0b tweak alpha-orbitals example
- remove direct-volume option
- enable tryUseGpu for isosurface repr
2021-12-19 13:05:34 -08:00
Alexander Rose
b4783909d7 fix lighting example
- adjust to new light & material handling
2021-12-19 13:04:05 -08:00
Alexander Rose
fe5f841ab8 improve docking-viewer example
- add example pdbqt & mol2
2021-12-19 13:03:10 -08:00
Alexander Rose
2c3f0dbc97 fix missing uniform in direct-volume shader 2021-12-19 13:02:23 -08:00
Alexander Rose
224fd1733f improve aromatic bonds
- Don't detect aromatic bonds for rings < 5 atoms based on planarity
- Prefer atoms in aromatic rings as bond reference positions
2021-12-19 13:02:02 -08:00
Alexander Rose
1f262ee422 Merge pull request #321 from molstar/update-create-plugin
mol-plugin-ui/createPluginUI
2021-12-19 11:51:44 -08:00
Alexander Rose
df3bcdd05a fix embedded 2021-12-19 11:41:53 -08:00
dsehnal
d5d08542ed tweak 2021-12-19 17:36:32 +01:00
dsehnal
564a360c8f Added createPluginUI, removed createPlugin and createPluginAsync 2021-12-19 17:32:58 +01:00
Alexander Rose
74f123265b correctly set shader define flags
- overpaint, transparency, substance, clipping
2021-12-18 19:53:35 -08:00
Alexander Rose
ccfae65b01 fix tests 2021-12-18 19:29:02 -08:00
Alexander Rose
bcfaef77c9 combined pick shader variant 2021-12-18 18:11:31 -08:00
Alexander Rose
0b6243c0d1 remove log statement 2021-12-18 16:53:48 -08:00
Alexander Rose
472866d8ec support ignoring defines for shader variants 2021-12-18 16:48:56 -08:00
Alexander Rose
471163f3d8 better defaults for postprocessing pass 2021-12-18 16:44:08 -08:00
Alexander Rose
ab6106896d only include vPaletteV for color shaders 2021-12-18 16:43:48 -08:00
Alexander Rose
7ca624d04b remove dMarkerType shader define 2021-12-18 16:42:43 -08:00
Alexander Rose
bd3d18f43f use uniform for double-sided shader param 2021-12-18 16:39:15 -08:00
Alexander Rose
21eb21b6dd support variants for graphics render-itms 2021-12-18 16:32:20 -08:00
Alexander Rose
fe0d4dc11e warn (not throw) about erroneous symmetry matrix
- #303
2021-12-18 15:24:52 -08:00
Alexander Rose
812eb0efa9 Merge pull request #309 from molstar/ma-support
ModelArchive / AlphaFold support
2021-12-18 14:52:55 -08:00
Alexander Rose
10d0bf293a fix wrong method name loadAfdb 2021-12-18 14:41:38 -08:00
Alexander Rose
ce2544b9f3 add model-archive viewer helpers 2021-12-18 14:40:45 -08:00
Alexander Rose
1964de1e44 Merge branch 'master' of https://github.com/molstar/molstar into ma-support 2021-12-18 14:26:00 -08:00
Alexander Rose
20c9d2cc41 enable aromaticBonds by default 2021-12-18 14:22:21 -08:00
Alexander Rose
3b1513adc0 fix eslint warnings 2021-12-18 14:12:58 -08:00
Alexander Rose
eaa60fc5cd fix linting errors and vscode settings 2021-12-18 12:47:41 -08:00
Alexander Rose
ce1e3960a2 avoid standard polymers misqualified as ligands
- overrule erroneous chem comp type in some files
2021-12-18 12:13:36 -08:00
Alexander Rose
176f80ea9b ensure trace element for polymer residues 2021-12-18 12:09:17 -08:00
Alexander Rose
28c9dc8286 refactor Model.isFromPdbArchive
- split into isFromPdbArchive and hasPdbId
- improve when secondary structure is calculated
2021-12-18 10:45:59 -08:00
Alexander Rose
2135f76441 fix temporal multi-sample flicker, enable by default 2021-12-18 10:29:42 -08:00
Alexander Rose
5637a23153 fix unnecessary cartoon geometry recreation 2021-12-18 10:14:55 -08:00
dsehnal
2818389741 update label 2021-12-17 23:33:50 +01:00
dsehnal
805f772696 3.0.0-dev.5 2021-12-16 12:21:45 +01:00
dsehnal
308bbc1ea0 lint 2021-12-16 12:19:32 +01:00
dsehnal
4a248b5591 shouldResetCamera reset fix 2021-12-16 12:17:46 +01:00
David Sehnal
704a9a111d Merge pull request #316 from JonStargaryen/master
update State Snapshots canApply when snapshots are added
2021-12-16 11:48:15 +01:00
JonStargaryen
a6befc5509 update State Snapshots canApply when snapshots are added 2021-12-16 11:31:53 +01:00
ptourlas
a736fe7989 Add formal charge option to atom site 2021-12-15 00:35:34 +02:00
ptourlas
1970b7f249 Spot the formal charge prefix 2021-12-15 00:34:06 +02:00
dsehnal
9e7aa4226d CI task 2021-12-14 21:55:23 +01:00
David Sehnal
7a25699c23 Merge pull request #312 from molstar/dependabot/npm_and_yarn/swagger-ui-dist-4.1.3
Bump swagger-ui-dist from 4.1.1 to 4.1.3
2021-12-14 14:29:01 +01:00
dependabot[bot]
901ae7f6d6 Bump swagger-ui-dist from 4.1.1 to 4.1.3
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 4.1.1 to 4.1.3.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v4.1.1...v4.1.3)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-14 13:26:49 +00:00
dsehnal
c57a4cdf6e 3.0.0-dev.4 2021-12-14 14:25:51 +01:00
dsehnal
18573e17f0 package.lock 2021-12-14 14:23:37 +01:00
dsehnal
5bc7ffa8a7 react and react-dom as peerDependencies 2021-12-14 14:17:25 +01:00
David Sehnal
430cc83259 Merge pull request #311 from molstar/upload-overlay
Drag and Drop Overlay
2021-12-14 14:11:15 +01:00
dsehnal
3cd3afb775 drag and drop overlay 2021-12-14 14:10:05 +01:00
dsehnal
93215b6beb prefer webgl 1 in Safari 15.1-15.3 2021-12-14 13:19:27 +01:00
Alexander Rose
694261c19c add ModelArchive to DownloadStructure action 2021-12-13 19:57:19 -08:00
Alexander Rose
24ad38e260 add clip support to components & focus repr 2021-12-12 18:09:26 -08:00
Alexander Rose
53bac83dff remove renderable.noClip 2021-12-12 17:59:09 -08:00
Alexander Rose
ab1578f667 move clip props to base geometry 2021-12-12 17:56:14 -08:00
Alexander Rose
4e70301b62 cleanup after merge 2021-12-12 17:27:47 -08:00
Alexander Rose
76ed2e9e11 Merge branch 'master' of https://github.com/molstar/molstar into clipping 2021-12-12 17:11:59 -08:00
Alexander Rose
58ce1f6498 Viewer.loadAfdb -> Viewer.loadAlphaFoldDb 2021-12-12 14:56:04 -08:00
Alexander Rose
47c4353eb9 use web api to get alphafold model for uniprot ac 2021-12-12 14:55:09 -08:00
Alexander Rose
d82cd3a8fe fix: do plugin ui render after context init 2021-12-12 14:45:57 -08:00
Alexander Rose
8f06b603e6 add bond location support to qmean & pLDDT color themes 2021-12-11 21:35:56 -08:00
Alexander Rose
0865c1b7fb changelog 2021-12-11 18:04:40 -08:00
Alexander Rose
c8c32b89c1 improve loading of ModelArchive schema structures
- includes AlphaFold mmcif structures
- add Viewer.loadAfdb
- add afdb GET param to Viewer html
- add Viewer default presets showing quality assessment (qmean & pLDDT)
- add alphafolddb option to download structure action
2021-12-11 17:56:26 -08:00
Alexander Rose
658c789906 tweak: use PD.arrayToOptions 2021-12-11 17:47:29 -08:00
Alexander Rose
c9ad7fce5b add PluginConfig.Structure.DefaultRepresentationPreset 2021-12-11 17:46:21 -08:00
Alexander Rose
720a8a440f tweak param comparision
- avoid unneccesary update
2021-12-11 17:42:41 -08:00
Alexander Rose
84fe2d2502 add ModelArchive extension
- QualityAssessment prop (from ma_qa_metric_local mmcif category)
- pLDDT & qmean coloring
- pLDDT & qmean repr presets
- pLDDT & qmean molql symbol
- pLDDT & qmean loci labels (including avg for mutli residue selections)
- pLDDT selection query
2021-12-11 17:40:04 -08:00
Alexander Rose
6f159c592f cleanup mmcif schema generation
- remove BRANCH and CARB extension (nwo included in mmCIF)
2021-12-11 16:57:09 -08:00
Alexander Rose
beff1ecb3e add model-archive cif schema 2021-12-11 16:40:29 -08:00
Alexander Rose
e4f630dbef improve drag and drop support
- any file type
- multiple files
- if session, only first is loaded
2021-12-11 16:37:20 -08:00
Alexander Rose
ccaf18af04 add analytics to viewer on deployment
- only for molstar.org (done in ./scripts/deploy.js)
2021-12-11 12:25:05 -08:00
Alexander Rose
2b72098f95 fix: false positives in Model.isFromPdbArchive
- wrong for, e.g., AlphaFold DB entries
2021-12-11 11:42:15 -08:00
Alexander Rose
b32546bea7 add outline color option to renderer 2021-12-11 11:30:00 -08:00
Alexander Rose
b9b0413e9f improve label repr defaults 2021-12-11 11:25:35 -08:00
David Sehnal
1d29b4627f Merge pull request #305 from MadCatX/cleanup-old-nodejs
Make cleanup script work with older vesions of Node.js
2021-12-10 12:43:08 +01:00
Michal Malý
bd44c76709 Make cleanup script work with older versions of Node.js 2021-12-10 09:36:58 +01:00
Alexander Rose
06b4761f2b add `carbonLightness` to illustrative color theme 2021-12-06 20:02:25 -08:00
Alexander Rose
daa3d1dbaa handle zero light count 2021-12-06 20:01:46 -08:00
Alexander Rose
5490d5ceb5 fixes for material bumpiness 2021-12-06 20:01:18 -08:00
dsehnal
cf3c1cfcce update Group Presets UI 2021-12-06 10:44:20 +01:00
Alexander Rose
be2607ae84 add bumpAmplitude render parameter 2021-12-05 21:29:07 -08:00
Alexander Rose
aa1f081664 remove direct-volume isosurface render-mode 2021-12-05 14:24:18 -08:00
Alexander Rose
e2966241e8 Merge pull request #299 from molstar/bump
procedural bump mapping
2021-12-05 12:42:27 -08:00
Alexander Rose
447792b1ef changelog 2021-12-05 12:30:28 -08:00
Alexander Rose
1cbcb5c530 remove duplicated line 2021-12-05 12:10:26 -08:00
Alexander Rose
f8d32d1d8d fix temp texture usage for color smoothing 2021-12-05 12:07:32 -08:00
Alexander Rose
8c556c2849 Merge branch 'master' of https://github.com/molstar/molstar into bump 2021-12-05 12:02:21 -08:00
Alexander Rose
4d0f0ceebf 3.0.0-dev.3 2021-12-04 22:23:51 -08:00
Alexander Rose
14abcddfcf changelog 2021-12-04 22:17:24 -08:00
Alexander Rose
704cc96a9f Merge pull request #300 from sukolsak/fix-export
Fix USDZ and OBJ export
2021-12-04 22:13:41 -08:00
Sukolsak Sakshuwong
b51f610173 fix USDZ and OBJ export 2021-12-04 20:08:15 -08:00
Alexander Rose
98050875c7 add bumpiness to material 2021-12-04 16:54:23 -08:00
dsehnal
470280ea1a changelog and update eslint 2021-12-01 13:29:02 +01:00
dsehnal
dafb5a8299 3.0.0-dev.2 2021-12-01 13:26:08 +01:00
dsehnal
4a1af03744 package.json fix 2021-12-01 13:23:50 +01:00
dsehnal
bb176f1efb 3.0.0-dev.1 2021-12-01 13:17:15 +01:00
dsehnal
a53bcde973 npmignore tweaks 2021-12-01 13:14:28 +01:00
David Sehnal
1a8dc2c637 Merge pull request #295 from molstar/material-object
Refactor Material Representation
2021-11-29 18:04:52 +01:00
dsehnal
f96211ff91 comment 2021-11-29 18:04:23 +01:00
dsehnal
77f9c02785 udpate presets 2021-11-29 18:01:41 +01:00
dsehnal
7910b65fdc Merge branch 'master' of https://github.com/molstar/molstar 2021-11-29 14:49:53 +01:00
dsehnal
eb4fc4588d remove console.log 2021-11-29 14:49:51 +01:00
David Sehnal
5430674071 Merge pull request #294 from MadCatX/fixtypo
Fix typo in README
2021-11-29 14:10:20 +01:00
dsehnal
17e67e3b79 Refactor Material representation 2021-11-29 14:09:56 +01:00
dsehnal
e87a0d72e4 Fix docking viewer material usage 2021-11-29 12:48:26 +01:00
dsehnal
67d3c65907 ParamDefinition.Group.presets support 2021-11-29 12:35:57 +01:00
dsehnal
564a5486c9 StructureComponentManager.Options state saving support 2021-11-29 11:57:37 +01:00
Michal Malý
9ce11c4c32 Fix typo in README 2021-11-29 11:41:57 +01:00
dsehnal
5e97b551a5 changelog 2021-11-29 11:10:30 +01:00
David Sehnal
77536e75af Merge pull request #292 from MadCatX/provide-rebuild-cmd
Add packaging command to force a full rebuild
2021-11-29 11:05:44 +01:00
Alexander Rose
6f12f714d2 3.0.0-dev.0 2021-11-28 14:02:46 -08:00
Alexander Rose
1f67077400 changelog 2021-11-28 13:56:07 -08:00
Alexander Rose
d1c4cf69cb Merge pull request #291 from molstar/lighting
Lighting
2021-11-28 13:53:42 -08:00
Alexander Rose
803c5eaa15 changelog 2021-11-28 13:39:53 -08:00
Alexander Rose
970fd5d9c3 Merge branch 'master' of https://github.com/molstar/molstar into lighting 2021-11-28 13:32:56 -08:00
Alexander Rose
7ccd4a1e0d 2.4.1 2021-11-28 13:31:12 -08:00
Michal Malý
3a6ab55266 Add a cleanup packaging script 2021-11-28 22:29:50 +01:00
Alexander Rose
eb41882c56 Merge branch 'master' of https://github.com/molstar/molstar 2021-11-28 13:25:42 -08:00
Alexander Rose
734851a810 changelog 2021-11-28 12:46:12 -08:00
Alexander Rose
6318717a15 tweak material param 2021-11-28 12:42:33 -08:00
dsehnal
d8498feaef updade npmignore 2021-11-28 14:36:54 +01:00
Michal Malý
aaec452bc2 Add packaging command to force a full rebuild 2021-11-28 13:28:06 +01:00
Alexander Rose
bce959195a fix renderer spec baseline 2021-11-27 17:31:42 -08:00
Alexander Rose
4287e09a9a fix material param number formating 2021-11-27 17:25:51 -08:00
Alexander Rose
fdc006f833 Merge branch 'master' of https://github.com/molstar/molstar into lighting 2021-11-27 17:08:56 -08:00
Alexander Rose
c704b7505c add Substance theming (per-group material) 2021-11-27 17:06:55 -08:00
Alexander Rose
ceaf238322 material improvements
- material helpers
- material in structure component manager
2021-11-27 17:02:48 -08:00
Alexander Rose
b7224ce5c7 lighting tweaks 2021-11-26 18:04:33 -08:00
Alexander Rose
95654175fe improve 'rounded' tube geometry
- correct normals
- circle offset
2021-11-26 15:34:16 -08:00
Alexander Rose
de96244706 allow atoms in aromatic rings to do hydrogen bonds 2021-11-26 14:37:50 -08:00
Alexander Rose
3104ee5742 Merge branch 'master' of https://github.com/molstar/molstar into lighting 2021-11-25 15:00:19 -08:00
Alexander Rose
73ac445a44 2.4.0 2021-11-25 14:46:10 -08:00
Alexander Rose
1a1d1d9d30 changelog 2021-11-25 14:41:19 -08:00
Alexander Rose
062aff76da update schemas 2021-11-25 14:40:35 -08:00
Alexander Rose
7d0d24b66d update packages 2021-11-25 14:40:26 -08:00
Alexander Rose
6655672d11 Merge pull request #290 from molstar/smoothing2
Smooth Overpaint and Transparency
2021-11-25 14:13:10 -08:00
Alexander Rose
6e573ae410 reduce args counts in geo exporters 2021-11-25 14:04:58 -08:00
Alexander Rose
1c48c02473 basic overpaint for direct-volume isosurface 2021-11-24 19:49:42 -08:00
Alexander Rose
78be3320ce geo export support smoothed overpaint/transparency 2021-11-24 19:49:05 -08:00
Alexander Rose
c8018800cc grid-based smoothing of Overpaint and Transparency 2021-11-24 19:47:07 -08:00
Alexander Rose
bb795aca98 refactor grid-based color smoothing
- support rgba and alpha values
- CPU and GPU versions
- for Mesh and TextureMesh
2021-11-24 19:43:15 -08:00
Alexander Rose
2cb1279f4c gl compute utils improvements
- CopyRenderable
- readTexture and readAlphaTexture helpers
2021-11-24 19:10:15 -08:00
Alexander Rose
b876c6f618 avoid unnecessary representation state updates 2021-11-24 18:51:16 -08:00
Alexander Rose
3a7dfc055e add Representation.geometryVersion
- increments whenever the geometry of any visual changes
2021-11-24 18:49:10 -08:00
Alexander Rose
928e521ac7 improve handling of .meta in Mesh & TextureMesh 2021-11-24 18:36:04 -08:00
Alexander Rose
e5e9598e4b changelog 2021-11-24 18:31:08 -08:00
Alexander Rose
e6e1809592 Fix secondary-structure property handling
- StructureElement.Property was incorrectly resolving type & key
- StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
2021-11-24 18:30:53 -08:00
Alexander Rose
812f97ddb7 skip picking/depth pass for volume rendering
- not supported in shader anyway
- was printing 'no output' warning in Chrome console
2021-11-24 18:20:42 -08:00
Alexander Rose
c6b814b31b re-enable VAO with better workaround 2021-11-24 18:17:48 -08:00
Alexander Rose
98566fa389 improve error handling
- console.error if not re-thrown
- better messages for users
2021-11-24 18:15:47 -08:00
David Sehnal
4318c89bdb Merge pull request #288 from jpattle/allow-v3-sdf
Added the ability to handle v3000 sd files
2021-11-23 15:49:42 +01:00
Jason Pattle
b41a97ce6a Added a separate function to handle v2 counts and refactored the existing code that distinguishes v2 and v3 atom and bond counts 2021-11-23 10:30:09 +00:00
Jason Pattle
862c384dc0 Added the ability to handle v3000 sd files. Added a set of utility functions for parsing atoms and bonds from v3000 sd files. Updated the existing sdf parser to determine the version and run the v3000 sd file parser functions instead of the default v2000 ones. Added tests to verify parsing functionality for example v3000 ctab 2021-11-22 16:00:35 +00:00
Alexander Rose
26cc7e94c2 2.3.9 2021-11-20 16:58:28 -08:00
Alexander Rose
c6fe6ddcba switch off VAO support for now 2021-11-20 16:54:38 -08:00
Alexander Rose
154984e74d 2.3.8 2021-11-20 16:30:22 -08:00
Alexander Rose
72fcaf8321 changelog 2021-11-20 16:24:54 -08:00
Alexander Rose
0c14ca5888 workaround for VAO issue in Chrome 96 2021-11-20 16:14:30 -08:00
Alexander Rose
a85ede5058 fix unused vertex attribute handling 2021-11-20 16:13:18 -08:00
Alexander Rose
db49a16184 fix double canvas context creation 2021-11-20 12:52:47 -08:00
dsehnal
0704db2343 replace webpack-version-file-plugin 2021-11-20 13:45:28 +01:00
dsehnal
425dca4665 fix sass division 2021-11-20 12:55:46 +01:00
dsehnal
8d65ccabd2 update packages
- use sass instead of node sass
2021-11-20 12:49:26 +01:00
Alexander Rose
cbd417ca13 2.3.7 2021-11-15 19:31:07 -08:00
Alexander Rose
1e4d1e45f9 Merge branch 'master' of https://github.com/molstar/molstar into lighting 2021-11-13 13:36:03 -08:00
Alexander Rose
1578211157 add helix profile option to cartoon repr 2021-11-13 13:16:11 -08:00
Alexander Rose
15932dc5df handle parent in Structure.remapModel 2021-11-13 09:23:57 -08:00
dsehnal
7db2205956 changelog 2021-11-09 21:18:55 +01:00
dsehnal
d87f0d236a ViewerOptions.collapseRightPanel 2021-11-09 21:17:01 +01:00
dsehnal
16daca6008 undo test code 2021-11-09 21:13:28 +01:00
dsehnal
a0d919c8db Viewer.loadTrajectory 2021-11-09 21:12:24 +01:00
David Sehnal
ffee2bf1c4 Merge pull request #285 from MadCatX/tweak-measurement-order-labels
Tweak measurement order labels
2021-11-09 10:28:13 +01:00
Michal Malý
de77f6ac59 Fix a visual glitch where the label border was initially rendered with
half of the intended size.
2021-11-09 09:59:25 +01:00
Michal Malý
c8c2ebcd65 Reduce Z-offset of measurement order labels to avoid too early clipping 2021-11-09 09:59:25 +01:00
Michal Malý
42796b984f Reduce border width of measurement order labels 2021-11-09 09:58:58 +01:00
David Sehnal
746557bf52 Merge pull request #284 from russellp17/master
Move jest types to devDependencies
2021-11-08 22:48:15 +01:00
Russell Parker
a5020a9e96 Move jest types to devDependencies 2021-11-08 15:58:14 -05:00
dsehnal
2ebb0a35fd 2.3.6 2021-11-08 18:57:06 +01:00
dsehnal
64aaa92d45 changelog 2021-11-08 18:54:52 +01:00
dsehnal
4baf391efe prefer webgl1 in safari 15.1
- WebGL2 is broken there for Mol* shaders
- It works again in Safari 15.4 tech preview
2021-11-08 18:50:16 +01:00
David Sehnal
5afdcff6a5 Merge pull request #273 from molstar/shader-tests
add basic unit tests for graphics shaders
2021-11-08 18:40:51 +01:00
dsehnal
339c397860 package lock 2021-11-08 18:30:52 +01:00
dsehnal
a58cbd31ef Merge branch 'master' of https://github.com/molstar/molstar into shader-tests 2021-11-08 18:30:30 +01:00
David Sehnal
d232b01cf9 Merge pull request #283 from MadCatX/improve-measurements-ux
Improve measurements user experience
2021-11-08 17:12:37 +01:00
Michal Malý
ec95270854 Use the entire element to trigger highlighting of loci from additionsHistory 2021-11-08 15:46:07 +01:00
Michal Malý
78cc0d960f Show the order of locis to be used for measurements in 3D view 2021-11-08 15:46:02 +01:00
Alexander Rose
b5ccdfdd53 multiple lights, per object materials
- multiple directional lights
- per-object materials (roughness & metalness)
- fixed reflectivity to 0.5
- update PhysicalMaterial from three.js to r134
2021-11-06 21:14:14 -07:00
Alexander Rose
d0c0d8e703 require standardDerivatives glsl extension 2021-11-06 20:57:22 -07:00
Alexander Rose
ebf64404be add loop unrolling glsl support 2021-11-06 15:02:14 -07:00
Alexander Rose
7f39cf0f37 add missing updateFocusRepr to atomicDetail preset
- fixes #280
2021-11-02 22:10:35 -07:00
Alexander Rose
e5dcc8e54f wip, lighting improvements
- set light direction
- set light color
- set ambient color
2021-10-31 14:59:19 -07:00
Alexander Rose
4592510a95 linting fix 2021-10-30 21:50:15 -07:00
Alexander Rose
46d5442dc5 Merge pull request #252 from corredD/forkdev
binary model loading support and latest mycoplasma model.
2021-10-30 17:11:27 -07:00
Alexander Rose
271cff4aba changelog 2021-10-30 17:09:35 -07:00
Alexander Rose
94fd5a97d6 Merge branch 'master' of https://github.com/molstar/molstar into pr/corredD/252 2021-10-30 16:53:35 -07:00
Alexander Rose
28678e2f80 Merge pull request #270 from molstar/measurements
Additional measurement controls
2021-10-30 16:52:32 -07:00
Alexander Rose
406307a432 add radiusScale param to orientation measurement 2021-10-30 16:48:47 -07:00
Alexander Rose
56345b5096 Merge branch 'master' of https://github.com/molstar/molstar into measurements 2021-10-30 16:31:12 -07:00
Alexander Rose
3fcc42ee0e fix, proper EmptyRepresentationProvider 2021-10-30 16:26:37 -07:00
Alexander Rose
b903677f8a gh action, add npm run build 2021-10-30 16:12:42 -07:00
Alexander Rose
ef4b632a07 update package-lock 2021-10-30 16:05:24 -07:00
Alexander Rose
e9d485ca85 gh action, use npm ci 2021-10-30 16:01:09 -07:00
Alexander Rose
a149fa5929 gh action 2021-10-30 15:53:58 -07:00
Alexander Rose
bb3dde585b gh action 2021-10-30 15:46:23 -07:00
Alexander Rose
cd6bbeaa86 gh action 2021-10-30 15:44:06 -07:00
Alexander Rose
e3d24dae4b gh action 2021-10-30 15:43:05 -07:00
Alexander Rose
687a814a62 gh action
- update eslint
- add jest
2021-10-30 15:02:25 -07:00
Alexander Rose
8f2e99dc51 improve shader tests
- made gl package optional
- skip test if gl package not available
2021-10-30 14:52:11 -07:00
Alexander Rose
568be030c3 Merge branch 'master' of https://github.com/molstar/molstar into shader-tests 2021-10-30 14:45:19 -07:00
Alexander Rose
97c3ab8b5a changelog 2021-10-30 14:44:27 -07:00
Alexander Rose
5db646d139 fix marker highlight color overriding select color 2021-10-30 14:43:57 -07:00
Alexander Rose
340f8f1af3 add additional aromatic bond visual params 2021-10-30 14:42:17 -07:00
dsehnal
4484a4452c 2.3.5 2021-10-19 18:22:50 +02:00
dsehnal
65b654a0a2 fix index pair bonds order assignment 2021-10-19 18:20:54 +02:00
dsehnal
a8e0c13b0e fix sequence viewer for PDB files with COMPND record and multichain entities 2021-10-17 13:21:57 +02:00
dsehnal
41d67eb642 2.3.4 2021-10-12 19:05:48 +02:00
dsehnal
c76c8335d1 changelog 2021-10-12 19:03:37 +02:00
dsehnal
42528b7be5 fix argparse config 2021-10-12 19:01:23 +02:00
Alexander Rose
b371f8c11c Merge branch 'master' of https://github.com/molstar/molstar into shader-tests 2021-10-03 15:24:02 -07:00
Alexander Rose
3d651b40f0 use node 14 in lint action 2021-10-03 15:12:58 -07:00
Alexander Rose
895a13fc0d Merge branch 'master' into shader-tests 2021-10-03 14:36:23 -07:00
Alexander Rose
c94acff82e update packages 2021-10-03 11:12:24 -07:00
Alexander Rose
2f9ac711d1 add basic unit tests for graphics shaders
- compile using `gl` package
2021-10-03 10:52:58 -07:00
Alexander Rose
93b9953f6d add missing createEmpty* geo utils
- direct-volume
- image
- texture-mesh
2021-10-03 10:43:56 -07:00
Alexander Rose
dcaf6f8927 add multipleBonds param to bond visuals 2021-10-02 15:44:17 -07:00
Alexander Rose
d96eb404e1 add elements crosses visual 2021-10-02 15:09:13 -07:00
Alexander Rose
07322819f0 Merge pull request #271 from molstar/pick-atom
Picking improvements
2021-10-02 13:41:25 -07:00
Alexander Rose
9be686686d add pickPadding config option 2021-10-02 09:34:56 -07:00
Alexander Rose
3df539c9e1 Merge branch 'master' of https://github.com/molstar/molstar into pick-atom 2021-10-02 09:30:07 -07:00
dsehnal
903f06bab6 2.3.3 2021-10-01 17:56:26 +02:00
dsehnal
13f2810f90 fix direct volume shader 2021-10-01 17:54:33 +02:00
dsehnal
ee8cae16d2 2.3.2 2021-10-01 17:12:42 +02:00
dsehnal
feaf6f7fd4 (temporarily) prefer webgl1 on iOS 2021-10-01 17:10:49 +02:00
dsehnal
04775a2e44 2.3.1 2021-09-28 16:13:51 +02:00
dsehnal
bec9fec755 chem_comp_bond and struct_conn to mmcif exporter
+ fix argparse config for Model/Volume servers
2021-09-28 16:12:01 +02:00
Alexander Rose
e840059a38 pick improvements
- ensure lines & points are at least 1 pixel big
- look around center pixel in a spiral for hits
2021-09-26 17:20:16 -07:00
Alexander Rose
1bd0339dec add points visual to line repr 2021-09-26 17:13:49 -07:00
Alexander Rose
d0eaf2f71e Merge branch 'master' of https://github.com/molstar/molstar into pick-atom 2021-09-26 13:41:23 -07:00
Alexander Rose
c7edf40afe add markerPriority param to renderer 2021-09-26 13:31:41 -07:00
Alexander Rose
b44962eb2f lint: add semi-spacing rule 2021-09-26 13:14:32 -07:00
Alexander Rose
254c9efbf5 improve/fix implicit atom picking 2021-09-26 13:06:56 -07:00
Alexander Rose
73e9aed98c add preferAtoms param to Select/Highlight behaviors 2021-09-26 12:58:29 -07:00
Alexander Rose
6e60d9713a fix bond atoms not added in selection manager 2021-09-26 12:53:53 -07:00
Alexander Rose
ef0593b1e2 add pixel-scale & pick-scale GET params to Viewer 2021-09-26 12:43:57 -07:00
Alexander Rose
7831fa8b33 fix: pickScale not considered in line/point shader 2021-09-26 12:39:44 -07:00
dsehnal
c64851492c applyMarkerAction take 2 2021-09-21 18:38:05 +02:00
dsehnal
4a2e93e265 fix applyMarkerAction edge case 2021-09-21 10:15:29 +02:00
Alexander Rose
d4bb1a6e93 wip: prefer to pick atoms close to ends over bonds 2021-09-19 16:24:45 -07:00
Alexander Rose
55de0aba69 fix point repr & shader 2021-09-19 16:00:40 -07:00
Alexander Rose
ecfa7b5a99 fix currentTheme not set in Representation.createMulti 2021-09-19 15:45:08 -07:00
dsehnal
bee3dc4595 fix double bonds from sctruct_conn records 2021-09-19 17:40:38 +02:00
Alexander Rose
787ca47825 fix line shader not accounting for aspect ratio
- also remove uViewportHeight in favor of uViewport
2021-09-18 23:13:00 -07:00
Alexander Rose
b06c134b61 add additional measurement controls
- orientation (box, axes, ellipsoid)
- plane (best fit)
2021-09-18 22:39:40 -07:00
Alexander Rose
3436d03468 add helpers to work with many locis
- StructureElement.Loci.getPrincipalAxesMany
- structureElementLociLabelMany
2021-09-18 22:13:22 -07:00
Alexander Rose
58df6f3b85 formating 2021-09-18 22:12:08 -07:00
Alexander Rose
6fab6ce1f2 add map-provider GET param to Viewer app 2021-09-18 16:52:35 -07:00
Alexander Rose
9fd95f1a11 handle missing occupancy column
- treat as occ 1
2021-09-18 16:47:52 -07:00
Alexander Rose
69fe0901e2 add CharmmSaccharideNames 2021-09-18 16:37:39 -07:00
Alexander Rose
ffaf008dce limit max display counts in sequence panel
- MaxSelectOptionsCount
- MaxSequenceWrappersCount
- workaround for cellpack models
2021-09-18 16:30:14 -07:00
ludovic autin
eb196a41b5 change variables names to avoid confusion with other types. Added the xrayshading for the compartment geometry 2021-09-14 11:25:15 -07:00
ludovic autin
35baaaf594 support for compartment PLY file. 2021-09-13 11:46:27 -07:00
Alexander Rose
0fc305aaea fix linting issues 2021-09-12 23:32:55 -07:00
Alexander Rose
bf67546a61 Merge branch 'master' of https://github.com/molstar/molstar into pr/corredD/252 2021-09-12 23:30:26 -07:00
Alexander Rose
8c417ef35c lint: add space-before-blocks rule 2021-09-12 23:23:03 -07:00
Alexander Rose
01691f3050 lint: add keyword-spacing rule 2021-09-12 23:20:59 -07:00
Alexander Rose
128abf3090 lint: add no-multi-spaces rule 2021-09-12 23:13:44 -07:00
Alexander Rose
0c0e995256 lint: add no-multi-spaces rule 2021-09-12 23:11:29 -07:00
Alexander Rose
4090498f92 lint: add func-call-spacing rule 2021-09-12 23:03:35 -07:00
Alexander Rose
24677d6931 lint: add space-before-function-paren rule 2021-09-12 22:57:48 -07:00
Alexander Rose
80d54afdd0 Merge branch 'master' into forkdev 2021-09-12 20:16:00 -07:00
Alexander Rose
908fff8041 lint: add prefer-const rule 2021-09-12 20:02:53 -07:00
Alexander Rose
aa1eb90f66 lint: add computed-property-spacing rule 2021-09-12 19:41:13 -07:00
Alexander Rose
1d21787e7e lint: add space-in-parens rule 2021-09-12 19:37:26 -07:00
Alexander Rose
42409a2bc7 lint: add array-bracket-spacing rule 2021-09-12 19:32:54 -07:00
Alexander Rose
b31302ba3a lint: add object-curly-spacing rule 2021-09-12 18:45:32 -07:00
Alexander Rose
3840b0041b lint: add key-spacing rule 2021-09-12 18:38:45 -07:00
Alexander Rose
cc68f8311d lint: add no-throw-literal rule 2021-09-12 16:22:43 -07:00
Alexander Rose
3400c8e94a update coreCif dictionary 2021-09-12 16:19:23 -07:00
Alexander Rose
61c47c517b update mmcif schema types and derived data 2021-09-12 15:06:54 -07:00
Alexander Rose
76674917ca update rscb graphql schema types 2021-09-12 15:04:49 -07:00
Alexander Rose
b835eb8de7 update packages 2021-09-12 13:39:00 -07:00
Alexander Rose
2a74dfcf46 factor out membrane sphere handling 2021-09-12 10:26:10 -07:00
Alexander Rose
b0f447adde fix cellpack results file asset handling 2021-09-12 10:17:37 -07:00
Alexander Rose
3f0d476e94 Merge pull request #266 from JonStargaryen/master
ANVIL: add TMDET amino acid classification and reference
2021-09-10 22:41:01 -07:00
Sebastian Bittrich
9423d56d36 lint 2021-09-09 16:16:49 -07:00
Sebastian Bittrich
0f03fe99d6 ANVIL: fix for 6y1z 2021-09-09 16:02:39 -07:00
Sebastian Bittrich
556d5bb003 ANVIL: add TMDET ref and option 2021-09-09 09:06:51 -07:00
dsehnal
8e3ea6943f updated packages & tsc 2021-09-07 14:10:10 +02:00
Alexander Rose
599cfc1b1c 2.3.0 2021-09-06 15:33:03 -07:00
Alexander Rose
84eccb5019 changelog 2021-09-06 15:29:44 -07:00
Alexander Rose
2dd07bb0e3 improved getMarkersAverage performance
- use lut
- add performance tests
2021-09-04 15:12:35 -07:00
Alexander Rose
f404d23280 take include/exclude flags into account when displaying aromatic bonds
- this allows to show aromatic rings only when explicitely given
2021-09-04 14:56:40 -07:00
Alexander Rose
8b7333b470 avoid unnecessary draw calls/ui updates when marking
- fix wrong type narrowing of Loci.isEmpty
2021-09-04 14:48:09 -07:00
ludovic autin
b8628ccff1 Merge branch 'master' of https://github.com/molstar/molstar into forkdev 2021-09-01 09:55:13 -07:00
Alexander Rose
7d26567d40 Merge pull request #258 from molstar/marking
Add optional marking pass
2021-08-31 21:18:23 -07:00
Alexander Rose
8f6dbf2192 asorted performance tweaks 2021-08-31 21:14:02 -07:00
Alexander Rose
db350ddfd3 loci/marking performance improvements
- use Interval for ranges instead of SortedArray
- pre-check if loci overlaps with unit visual
2021-08-30 22:43:01 -07:00
Alexander Rose
c0144d826c SortedArray: add .isRange, improve .areEqual 2021-08-30 22:33:09 -07:00
Alexander Rose
de3e819b80 fix uMarker not being updated 2021-08-30 22:29:35 -07:00
Alexander Rose
bbf96567b1 handle clipping/fog for marking edges 2021-08-29 22:14:11 -07:00
Alexander Rose
c9a3254bd6 fix partial marker average calculation 2021-08-29 21:25:41 -07:00
Alexander Rose
bad6d030f1 marker-data improvements
- add uniform marker type
- check if a loci is superset of a visual
- special case to improve reversing the previous mark
2021-08-29 10:44:09 -07:00
Alexander Rose
e1ad67a059 StructureElement.Loci improvements
- early return in .isEmpty
- ensure Interval is used when possible in .extendToAllInstances
2021-08-29 10:35:05 -07:00
ludovic autin
4c30057edf I am trying to get the binary asset to get cached. But still can't save a working session. 2021-08-27 16:14:43 -07:00
ludovic autin
53a4826274 Merge branch 'master' of https://github.com/molstar/molstar into forkdev 2021-08-27 14:55:22 -07:00
dsehnal
8d3ac92989 2.2.3 2021-08-25 17:40:28 +02:00
dsehnal
d6eb334d12 changelog 2021-08-25 17:38:28 +02:00
Alexander Rose
c62f19623c add optional marking pass
- outlines visible and hidden parts of highlighted/selected groups
- add highlightStrength/selectStrength renderer params
2021-08-22 12:08:15 -07:00
Alexander Rose
77139afe7f tweak Interval.size 2021-08-21 14:00:51 -07:00
Alexander Rose
54476ad85e improve print texture debug helpers 2021-08-21 13:59:38 -07:00
Alexander Rose
6dd876232d avoid superfluous calls to Loci.isWholeStructure 2021-08-21 13:51:40 -07:00
Alexander Rose
ae2314d76c fix camera/bounding helper not showing up 2021-08-21 13:48:56 -07:00
David Sehnal
6667509745 Merge pull request #257 from JonStargaryen/master
ANVIL: improve prediction for 3pqr
2021-08-21 20:22:38 +02:00
JonStargaryen
5c871a5aae ANVIL: increase number of sphere points to 175 2021-08-20 12:43:24 -07:00
ludovic autin
393fc99ed2 moved the model loading in model.ts and get the info from the recipe dictionary ( result file and lipids file ) if available. Fix the dates and some const / let variable defintion 2021-08-16 12:35:51 -07:00
ludovic autin
7c5dff1c8b Merge remote-tracking branch 'upstream/master' into forkdev 2021-08-16 11:55:09 -07:00
Alexander Rose
2482ef92af Improved StructureElement.Loci.size performance
- inlined code
- important for marking large cellpack models
2021-08-15 14:42:49 -07:00
Alexander Rose
db59303a84 Merge pull request #244 from molstar/meshproc
Mesh processing: border smoothing
2021-08-14 19:36:23 -07:00
Alexander Rose
fe700953ff Merge branch 'master' into meshproc 2021-08-14 19:32:06 -07:00
Alexander Rose
047946e41c remove superfluous type casts 2021-08-14 19:30:16 -07:00
ludovic autin
ce3f13431d endianess test with IsNativeEndianLittle 2021-08-13 10:58:25 -07:00
ludovic autin
df54766ab2 lower-case icosahedron 2021-08-12 10:48:27 -07:00
ludovic autin
94233fbcd9 eslint error fix 2021-08-11 11:07:34 -07:00
ludovic autin
d044496eaa fix eslint errors 2021-08-11 11:04:08 -07:00
ludovic autin
ca825d720e binary model loading support, latest mycoplasma model. 2021-08-11 10:44:44 -07:00
dsehnal
f833efae37 2.2.2 2021-08-11 14:54:03 +02:00
dsehnal
be4b787e66 Fix mol-script query compiler const expression recognition 2021-08-11 14:49:52 +02:00
David Sehnal
950b1c179a Merge pull request #248 from MadCatX/fix-isosurface
Do not cache LevelTexturesFramebuffers as they may become invalid
2021-08-10 12:40:52 +02:00
Michal Malý
2bd1a01afb Do not attach framebuffer unnecessarily 2021-08-10 09:15:05 +02:00
dsehnal
2fe43eda2b Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-09 20:33:57 +02:00
dsehnal
45fc0c61af Mesh.smoothEdges options 2021-08-09 20:32:40 +02:00
dsehnal
7e7993f5ba improve fillEdges in mesh edge smoothing
- sort boundary vertices
- limit the length added edges
2021-08-09 20:22:13 +02:00
Michal Malý
9d34dbff0f Do not cache LevelTexturesFramebuffers as they may become invalid 2021-08-09 09:58:55 +02:00
Alexander Rose
ba1b03f01b fix TransformData issues, see #133
- handle structure vs structure.root in ExplodeStructureRepresentation3D and SpinStructureRepresentation3D
2021-08-08 13:11:50 -07:00
Alexander Rose
791f7ca3c8 Merge pull request #245 from sukolsak/optimize-setCylinderMat 2021-08-08 10:45:23 -07:00
Sukolsak Sakshuwong
c14d50e4ff optimize setCylinderMat() 2021-08-08 08:51:16 -07:00
Alexander Rose
3e9de449c8 cleanup 2021-08-07 19:45:47 -07:00
Alexander Rose
0132c7ef5e Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-07 19:42:41 -07:00
Alexander Rose
aa2222c086 remove clipSphere option & param cleanup
- clip objects are better
- includeParent option not useful for gaussian-surface visuals
2021-08-07 19:20:51 -07:00
dsehnal
fc2765d376 2.2.1 2021-08-02 18:11:24 +02:00
dsehnal
9d85194082 2.2.1 changelog 2021-08-02 18:09:50 +02:00
David Sehnal
abfcc60898 Merge pull request #243 from molstar/input-observer-improvements
Input observer improvements
2021-08-02 18:08:48 +02:00
dsehnal
c688a83fa2 fix typo 2021-08-02 18:07:34 +02:00
dsehnal
77376056b9 changelog 2021-08-02 18:03:07 +02:00
dsehnal
8efd943c2b PinchInput.fractionDelta 2021-08-02 17:24:49 +02:00
dsehnal
b230655439 fix type 2021-08-02 16:55:06 +02:00
dsehnal
8ba792c4b0 add maxWheelDelta 2021-08-02 16:50:09 +02:00
dsehnal
fde8ca69e4 support for Safari gestures (pinch zoom on MacBook trackpad) 2021-08-02 16:30:48 +02:00
dsehnal
9ee1439299 normalize wheel speed in input observer 2021-08-02 14:45:25 +02:00
David Sehnal
195668760e Merge pull request #242 from sukolsak/export-overpaint
Add overpaint support to geometry exporters
2021-08-02 13:04:09 +02:00
Sukolsak Sakshuwong
bd64f1db9a update changelog and fix type 2021-08-02 01:19:31 -07:00
Sukolsak Sakshuwong
38a5a857aa refactor color calculation 2021-08-01 23:09:30 -07:00
Sukolsak Sakshuwong
5e8cdfe3a7 add overpaint support to geometry exporters 2021-08-01 22:30:05 -07:00
Alexander Rose
504406eb22 move clip variant and objects to repr state 2021-08-01 21:16:44 -07:00
Alexander Rose
f892917e1c Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-01 14:04:50 -07:00
Alexander Rose
738b7f4ca5 Merge pull request #238 from molstar/dynbonds
Bond improvements (mostly IndexPairBonds)
2021-08-01 13:53:42 -07:00
Alexander Rose
bce53d03a5 bond tweaks
- add DefaultBondMaxRadius constant
- add IndexPairBonds.Props object
2021-08-01 13:49:44 -07:00
Alexander Rose
74f721ab9f improve Structure.asParent
- handle parent coordinate system not identity
2021-08-01 13:48:26 -07:00
Alexander Rose
f011025f16 changelog 2021-07-31 23:22:11 -07:00
Alexander Rose
9e44cd83fa add clipSphere param to molecular-surface mesh 2021-07-31 23:21:57 -07:00
Alexander Rose
e0e45b64ac optimize invertCantorPairing 2021-07-31 23:15:33 -07:00
Alexander Rose
f10b152252 Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-07-31 14:42:18 -07:00
Alexander Rose
23cf5c2fdd changelog and docs 2021-07-31 14:22:45 -07:00
Alexander Rose
e211abd5ae Merge branch 'master' of https://github.com/molstar/molstar into dynbonds 2021-07-31 14:11:45 -07:00
Alexander Rose
7199be4d62 tweak getColorSmoothingProps
- make independent of webgl context
2021-07-31 14:10:50 -07:00
Alexander Rose
e1a40ded1d add surronding atoms structure selection query 2021-07-31 13:53:49 -07:00
Alexander Rose
8999c3097d handle dynamicBonds in root structure helper 2021-07-31 13:50:07 -07:00
Alexander Rose
44308fa1fd add maxDistance prop to IndexPairBonds 2021-07-31 13:48:10 -07:00
Alexander Rose
f5dd2f4579 support coordinateSystem in structure.asParent 2021-07-31 13:45:39 -07:00
dsehnal
104999b7dc 2.2.0 2021-07-31 15:15:09 +02:00
dsehnal
e5341623d3 changelog v2.2.0 2021-07-31 15:12:40 +02:00
dsehnal
0e9238e5ec Canvas3D tweaks:
- update "forceDraw" logic
- Ensure the scene is re-rendered when viewport size changes
- Support noDraw mode in PluginAnimationLoop
2021-07-31 15:06:58 +02:00
dsehnal
43c292e2df Support new EMDB API for EM volume contour levels 2021-07-31 14:12:33 +02:00
dsehnal
fbfd1b20d8 Prefer _label_seq_id fields in secondary structure assignment 2021-07-31 13:54:11 +02:00
dsehnal
5330df87e1 Merge branch 'master' of https://github.com/molstar/molstar 2021-07-27 12:29:17 +02:00
dsehnal
ad6b3c6fe0 add DS_store to .gitignore 2021-07-27 12:28:56 +02:00
Alexander Rose
b983df7eb5 mesh edge smoothing 2021-07-25 20:26:17 -07:00
Alexander Rose
add76a87d9 remove unnecessary check
- see 7686b61728
2021-07-25 20:22:00 -07:00
Alexander Rose
f9f8350d28 dynamic pair bonds on coordinate changes
- add dynamicBonds structure parameter
- add maxRadius, ignoreWater bond compute parameters
- ensure inter unit bond visuals are recreated
2021-07-24 17:16:59 -07:00
Alexander Rose
b71c2f365c add operator Loci granularity 2021-07-24 16:55:07 -07:00
Alexander Rose
a5443189d3 missing param 2021-07-24 16:46:30 -07:00
Alexander Rose
7686b61728 fix includeParent for multi instance bond visuals 2021-07-24 16:31:26 -07:00
Alexander Rose
844c13cd35 2.2.0-dev.1 2021-07-20 21:08:51 -07:00
Alexander Rose
d1c8b92fdf Merge pull request #224 from sukolsak/usdz-export
USDZ export
2021-07-18 09:28:27 -07:00
Sukolsak Sakshuwong
93d33bca80 view USDZ in AR on iOS 2021-07-17 21:41:26 -07:00
Sukolsak Sakshuwong
6550e53414 Merge branch 'master' into usdz-export 2021-07-17 18:50:43 -07:00
Alexander Rose
96dddb0998 updated changelog 2021-07-17 10:54:34 -07:00
Alexander Rose
baa64d8109 handle mononucleotides when guessing component type 2021-07-17 10:42:56 -07:00
David Sehnal
2df145aa8f Merge pull request #231 from molstar/sdf-parser-improvements
Sdf parser improvements
2021-07-16 18:27:16 +02:00
dsehnal
06b9c5f2de change SDF data header parsing
- do not trim <> around field
- store whole line staring with '> ' as data header (without the staring '> ')
2021-07-16 18:25:22 +02:00
dsehnal
e03b689f27 add SdfFormat 2021-07-16 18:04:25 +02:00
Sukolsak Sakshuwong
e4cdcff3ee Merge branch 'master' into usdz-export 2021-07-12 00:11:24 -07:00
Alexander Rose
f73150d074 Merge pull request #225 from molstar/tubular-helices
Add tubularHelices parameter to Cartoon representation
2021-07-11 11:45:38 -07:00
Alexander Rose
451dc12689 cleanup 2021-07-11 11:31:02 -07:00
Alexander Rose
a3fb7762d8 add tubular helices to Cartoon representation 2021-07-10 15:41:54 -07:00
Alexander Rose
3dfafc3202 handle cell angles close to zero in dcd reader 2021-07-10 15:35:53 -07:00
Alexander Rose
4fcea991d3 set default outline scale back to 1 2021-07-10 15:28:49 -07:00
Alexander Rose
0607ed46d1 handle more common ff residue/atom names 2021-07-10 15:28:23 -07:00
Sukolsak Sakshuwong
30d6244e82 add support for USDZ 2021-07-10 05:19:42 -07:00
Sukolsak Sakshuwong
fab8c74365 move quantizeColors to MeshExporter 2021-07-09 13:10:09 -07:00
Sukolsak Sakshuwong
1952922e4e make RenderObjectExporter.getData async 2021-07-09 13:10:09 -07:00
Alexander Rose
1eb351369e 2.1.0 2021-07-05 16:11:46 -07:00
Alexander Rose
701d782485 changelog 2021-07-05 16:07:30 -07:00
Alexander Rose
dc8457c4dc smoother trace normals 2021-07-05 15:59:28 -07:00
Alexander Rose
f104cd4d11 add missing import 2021-07-05 13:47:39 -07:00
Alexander Rose
9b56a6ae65 Merge pull request #221 from molstar/aromatic
Aromatic bond display option
2021-07-05 13:42:11 -07:00
Alexander Rose
2485ad5a2f Merge branch 'master' into aromatic 2021-07-05 13:38:05 -07:00
Alexander Rose
a56716ab6a Merge pull request #222 from molstar/backbone
Backbone representation
2021-07-05 13:37:24 -07:00
Alexander Rose
e0aaaa989e Merge branch 'master' into backbone 2021-07-05 13:34:40 -07:00
Alexander Rose
9ec0f9e736 outline fixes and improvements
- better handle outlines in orthographic mode
- remove unused code
- increase default outline scale to 2
2021-07-05 13:31:00 -07:00
Alexander Rose
47968eeeec fix traceOnly handling in makeElementIgnoreTest 2021-07-04 14:54:52 -07:00
Alexander Rose
9c157b70e1 warning for arrayAreIntersecting/arrayIntersectionSize 2021-07-04 14:50:58 -07:00
Alexander Rose
6d7e4ca227 remove unused import 2021-07-04 14:48:06 -07:00
Alexander Rose
fccd08d2ec add backbone repr
- atomistic and coarse units
2021-07-04 14:39:56 -07:00
Alexander Rose
19bae202d0 handle Vec3.angle edge case 2021-07-04 14:35:46 -07:00
Alexander Rose
4ba0ae24e4 support aromatic bond display as dashes
- add arrayAreIntersecting/arrayIntersectionSize helpers
- prefer reference atoms within rings (also for double/triple bonds)
2021-07-03 23:12:06 -07:00
Alexander Rose
fcf3718d75 better parsing of mol2 bond types 2021-07-03 22:38:29 -07:00
Alexander Rose
df1dd94f1c fix BondType.Names 2021-07-03 22:35:35 -07:00
Alexander Rose
65ba401850 fix repr update for Structure.asParent objects 2021-07-03 22:26:20 -07:00
Alexander Rose
a98f5e1047 fix fxaa antialiasing
- was broken when used with other postprocessing effects
- expose texture.filter
2021-07-03 22:19:51 -07:00
David Sehnal
e5cf97d1ea Merge pull request #217 from sukolsak/fix-cylinder
Fix cylinder orientation
2021-06-28 14:45:38 +02:00
Sukolsak Sakshuwong
1844fc14b2 fix cylinder orientation 2021-06-27 13:14:30 -07:00
dsehnal
d185c0ef34 2.0.7 2021-06-23 12:44:20 +02:00
dsehnal
40a4211e75 fix CIF text encoder edge cases & added test 2021-06-23 12:41:23 +02:00
dsehnal
daa2bbd042 Merge branch 'master' of https://github.com/molstar/molstar 2021-06-21 16:39:25 +02:00
Alexander Rose
ed5b4b27a8 guard against atom_site not being available 2021-06-20 22:55:25 -07:00
Alexander Rose
408ccb4353 fix bond cylinder imposter update issue 2021-06-20 13:54:15 -07:00
Alexander Rose
99e3cd6654 fix image export issues
- handle pre-multiplied alpha
- don't clear draw target unless written to
2021-06-20 13:51:58 -07:00
Alexander Rose
0819ace1dc use CustomProperty.Provider.ref 2021-06-20 13:49:42 -07:00
dsehnal
987c9210bd In-place reordering support for Frame.x/y/z 2021-06-19 12:26:42 +02:00
dsehnal
84fb42a161 Support volumeIndex in Viewer.loadVolumeFromUrl 2021-06-19 11:24:32 +02:00
dsehnal
53d3480701 fix isConnectedTo query 2021-06-15 17:53:40 +02:00
David Sehnal
eb629ef337 Merge pull request #212 from sukolsak/center-export
Geometry export: center exported models
2021-06-14 15:55:16 +02:00
Sukolsak Sakshuwong
c26111e8fb center exported models 2021-06-13 23:32:00 -07:00
dsehnal
4853ff7a1a fix volume streaming channel visibility 2021-06-11 15:19:04 +02:00
dsehnal
1bdebda136 2.0.6 2021-06-01 18:49:51 +02:00
dsehnal
fe5b847797 2.0.6 changelog 2021-06-01 18:47:51 +02:00
dsehnal
19ec5b226c changelog 2021-06-01 12:43:11 +02:00
dsehnal
4bb32d31dc support atom id list in selection helper 2021-06-01 12:41:04 +02:00
Alexander Rose
976a469cc7 Merge pull request #199 from molstar/original-mesh-data
Better handling of processed meshes
2021-05-31 10:46:35 -07:00
Alexander Rose
86087aa3ca Merge pull request #203 from sukolsak/export-original-mesh-data
Geometry export: use original mesh data
2021-05-31 10:34:22 -07:00
Sukolsak Sakshuwong
c0e955d472 export original mesh data 2021-05-30 06:25:43 -07:00
Alexander Rose
eca052e52e fix entity-source color showing black
- fixes #172
- now always using light-grey/white for regions with unkown source
2021-05-29 15:20:27 -07:00
Alexander Rose
a1e05387e4 add Mesh.getOriginalData accessor 2021-05-29 15:01:02 -07:00
Alexander Rose
301940c8bd fix canvas not cleared
- #201
- happens e.g. with antialiasing disabled plus transparent background on
2021-05-29 14:26:11 -07:00
Alexander Rose
d96303627c keep some original data after mesh processing
- processing in uniformTriangleGroup
- to be used in, e.g., geometry export
2021-05-28 23:43:37 -07:00
Alexander Rose
051b48776e consider BB atoms as trace 2021-05-28 23:37:25 -07:00
Alexander Rose
26054681d8 color smoothing param tweaks 2021-05-28 23:36:59 -07:00
Alexander Rose
70fa85d7d4 fix assembly-symmetry off option
- would not remove cage/axes visuals
2021-05-28 23:04:08 -07:00
Alexander Rose
5a23cd483e Merge pull request #198 from sukolsak/export-auto-quality
Geometry export: auto adjust quality of sphere/cylinder meshes
2021-05-28 22:57:19 -07:00
Alexander Rose
d759b07f1b Merge pull request #193 from JonStargaryen/anvil-fixes
ANVIL fixes
2021-05-28 22:52:49 -07:00
Sukolsak Sakshuwong
4694da0057 auto adjust quality of sphere/cylinder meshes 2021-05-28 11:05:06 -07:00
JonStargaryen
f930e3dbe0 avoiid cast 2021-05-28 08:43:05 -07:00
Alexander Rose
fcf45d20be Merge pull request #197 from sukolsak/gltf-material
Geometry export: add material to GLB
2021-05-27 22:53:45 -07:00
JonStargaryen
ad4ba7bcf9 drop redundant ops 2021-05-27 16:15:06 -07:00
JonStargaryen
26644ede49 no need to reassign neighbors 2021-05-27 15:41:48 -07:00
JonStargaryen
810973ff54 cleanup 2021-05-27 14:07:40 -07:00
JonStargaryen
6ad09c60c0 cleanup 2021-05-27 14:01:40 -07:00
JonStargaryen
dc146f5f04 Eisenhaber 1995 improvements 2021-05-27 12:38:35 -07:00
JonStargaryen
e1b771bba4 use existing impl 2021-05-27 09:48:31 -07:00
Sukolsak Sakshuwong
e2ab3a6fd6 add material to glb 2021-05-27 07:33:28 -07:00
JonStargaryen
d1296de676 bs on ordered array 2021-05-26 14:11:31 -07:00
JonStargaryen
fcac1a62c6 sample only 1 hemisphere 2021-05-26 12:54:03 -07:00
JonStargaryen
5eafddf97a fine-grained updates for large structures 2021-05-26 12:02:04 -07:00
JonStargaryen
e2dcbc3d65 const 2021-05-26 11:26:28 -07:00
JonStargaryen
54a388da9c store hphobhphil stats 2021-05-26 09:58:58 -07:00
JonStargaryen
3849c341b8 no optional chaining 2021-05-26 09:30:06 -07:00
JonStargaryen
31f4803c0a no array copies 2021-05-26 09:21:42 -07:00
JonStargaryen
d6e36d4ca7 cleanup 2021-05-25 14:55:49 -07:00
JonStargaryen
0d526fdc98 drop location in some more places 2021-05-25 14:35:19 -07:00
JonStargaryen
04b36170d8 cleanup 2021-05-25 13:15:36 -07:00
JonStargaryen
db787c9ea4 avoid location in filter loop 2021-05-25 12:38:43 -07:00
JonStargaryen
e1e6f9ca48 wip membership 2021-05-25 11:55:22 -07:00
JonStargaryen
40b5605e10 local Vec3 methods 2021-05-25 08:57:08 -07:00
JonStargaryen
609654b689 dont attach ASA values 2021-05-25 08:39:50 -07:00
dsehnal
45ef00f1d1 Move FileHandle.fromDescriptor to /servers to avoid importing 'fs' in browser builds 2021-05-25 17:18:47 +02:00
dsehnal
88380ff917 fix production and debug flags 2021-05-25 16:22:58 +02:00
dsehnal
bc7bfe9788 fix webpack watch build 2021-05-25 16:09:40 +02:00
dsehnal
469ca6cb41 fix webpack build 2021-05-25 15:51:40 +02:00
David Sehnal
c0be790ff1 Merge pull request #195 from MadCatX/update_deps
Update node-sass and webpack dependencies
2021-05-25 15:24:21 +02:00
David Sehnal
8c1d16353e Merge pull request #196 from molstar/sequence-mapping
Basic sequence mapping support
2021-05-25 15:20:49 +02:00
dsehnal
d76d475015 BestDatabaseSequenceMapping superposition 2021-05-25 15:15:33 +02:00
dsehnal
69024152cb Best Database Sequence Mapping property
- assigned based on atom_site.db_name/_acc/_num/_res CIF fields
- added basic color theme
2021-05-25 13:20:35 +02:00
Michal Malý
4a19aedec8 Update node-sass and webpack dependencies 2021-05-24 21:50:25 +02:00
JonStargaryen
df89351301 remove normal vector 2021-05-24 12:24:23 -07:00
JonStargaryen
9a0c87695f cleanup 2021-05-24 11:45:38 -07:00
JonStargaryen
a393231522 inline x/y/z 2021-05-24 10:00:54 -07:00
Alexander Rose
33de60d365 fix typeof check 2021-05-22 22:49:13 -07:00
JonStargaryen
3cf67f7605 fix coloring for trace-only ASA calc 2021-05-22 19:48:02 -07:00
Alexander Rose
ffdcf798e0 Merge pull request #194 from sukolsak/fix-smaa
Fix HTMLImageElement check for SMAA
2021-05-22 14:19:59 -07:00
Sukolsak Sakshuwong
397e1235e7 fix HTMLImageElement check for SMAA 2021-05-22 02:58:59 -07:00
JonStargaryen
4e77699076 more fine-grained status messages 2021-05-21 21:35:57 -07:00
JonStargaryen
b47d046505 mapping still needed for #132 2021-05-21 15:31:49 -07:00
JonStargaryen
74aa24bfa0 traceOnly: check for BB as well 2021-05-21 09:27:02 -07:00
JonStargaryen
30d5b0ddb1 rename to traceOnly 2021-05-21 09:24:09 -07:00
Alexander Rose
1e35ea15eb Merge pull request #192 from sukolsak/gltf-instancing
Geometry export: use instancing in GLB
2021-05-20 23:47:52 -07:00
Sukolsak Sakshuwong
bc998ab328 don't split triangles in Mesh.uniformTriangleGroup
Calling Mesh.uniformTriangleGroup for WebGL1 is required for picking to work properly. With color smoothing implemented, we don't have to split triangles in Mesh.uniformTriangleGroup anymore. This should help reduce the exported file size in WebGL1. This change is suggested by @arose
2021-05-20 17:03:48 -07:00
JonStargaryen
e5e245f4ee scale radius used for evaluation 2021-05-20 14:26:42 -07:00
JonStargaryen
c6073b894a ASA coloring wrong after removing part of a chain #132 2021-05-20 11:38:04 -07:00
Sukolsak Sakshuwong
9b11794f22 store colors in glb using 8 bits per channel 2021-05-20 09:57:59 -07:00
JonStargaryen
f2b9dceaab ANVIL runtime updates 2021-05-20 09:16:19 -07:00
Sukolsak Sakshuwong
9ccaaf6c80 cleanup 2021-05-20 07:20:56 -07:00
Sukolsak Sakshuwong
ecb97e525e omit indices from texture-mesh in glb 2021-05-20 04:44:13 -07:00
dsehnal
c36c6a6d97 support nested Lookup3D queries
- fixes non-covalent interactions bug
2021-05-20 09:34:59 +02:00
JonStargaryen
60b92471f1 cleanup 2021-05-19 19:34:06 -07:00
JonStargaryen
79e283cfbd wip ANVIL debugging 2021-05-19 15:22:16 -07:00
Sukolsak Sakshuwong
3778dacb08 min and max only required for vertex arrays 2021-05-18 22:27:58 -07:00
Sukolsak Sakshuwong
e407f7279b add generator info to glb 2021-05-18 22:25:37 -07:00
Sukolsak Sakshuwong
ea54209414 gltf instancing 2021-05-18 19:48:59 -07:00
JonStargaryen
d10a36509b ignore non-canonical aa 2021-05-18 15:03:48 -07:00
JonStargaryen
4af560e63a visuals cleanup 2021-05-18 12:23:55 -07:00
JonStargaryen
ecb8900258 wip adjust thickness 2021-05-18 09:49:50 -07:00
Alexander Rose
7bfc1b0ebc sreenshot tweaks
- don't turn of screenspace antialiasing
- use higher numbr of AO samples than default
2021-05-16 12:04:00 -07:00
Alexander Rose
5edae9d6f7 Merge pull request #173 from molstar/smcol
Color smoothing
2021-05-16 11:39:44 -07:00
Alexander Rose
fe702a8c63 fix high resolution molecular surface artefacts
- adjust probePositions prop with quality
2021-05-16 11:33:03 -07:00
Alexander Rose
c8868464a5 color smoothing param tweaks
- reduce sample stride to avoid artefacts
- min color grid resolution for better smoothing at high mesh resolution
2021-05-16 11:31:57 -07:00
Alexander Rose
720e65d2e6 Merge pull request #189 from sukolsak/volume-color
Geometry export: support volume & volumeInstance color types
2021-05-16 10:39:34 -07:00
Sukolsak Sakshuwong
b5123ff36a fix drawCount 2021-05-16 05:33:58 -07:00
Sukolsak Sakshuwong
d237034e8e quantize colors when exporting OBJ 2021-05-16 00:28:31 -07:00
Sukolsak Sakshuwong
aab95d27e0 convert sRGB to linear when exporting GLB 2021-05-15 23:56:50 -07:00
Sukolsak Sakshuwong
c68306125e support volume & volumeInstance color type in geo exporters 2021-05-15 23:55:21 -07:00
Alexander Rose
3173396737 use constant from gl context if available 2021-05-15 11:19:30 -07:00
Alexander Rose
212a3eeb6c tweaked auto color smoothing props 2021-05-15 11:18:39 -07:00
Alexander Rose
17b25354f5 added isPromiseLike helper
- use instead of instanceof Promise
2021-05-15 11:16:32 -07:00
JonStargaryen
9f176bd2bc add alpha-oonly option to asa calc 2021-05-12 12:20:28 -07:00
Alexander Rose
4a78283ce1 changelog 2021-05-09 21:10:43 -07:00
Alexander Rose
81e29533dc Merge branch 'master' of https://github.com/molstar/molstar into smcol 2021-05-09 21:09:19 -07:00
Alexander Rose
c1a2c602a1 remove unfinished dColorGridType support 2021-05-09 21:08:19 -07:00
Alexander Rose
c436653ce9 made transparency helpers a bit more generic 2021-05-09 14:35:00 -07:00
Alexander Rose
a2b4ed7c1c queries: add nos-bridge, improve disulfide-bridge 2021-05-09 13:56:40 -07:00
Alexander Rose
83968aa408 cylinder mesh builder improvements 2021-05-09 13:09:00 -07:00
dsehnal
71539cc75a citation in readme 2021-05-09 11:53:16 +02:00
dsehnal
881d4d2a99 Fix IndexPairBonds for structures with re-ordered atoms 2021-05-09 11:46:20 +02:00
David Sehnal
ae2f2e7d0e Merge pull request #181 from molstar/spin-struct-anim
Spin structure animation
2021-05-09 10:47:14 +02:00
Alexander Rose
e31f0f7660 basic spin-structure animation 2021-05-08 23:54:27 -07:00
Alexander Rose
3586207968 better SO autobonding 2021-05-08 19:55:36 -07:00
Alexander Rose
b575793b83 Merge pull request #180 from sukolsak/fix-sheet-caps
Fix normals in sheet caps
2021-05-08 18:06:30 -07:00
Alexander Rose
81bf653790 Merge pull request #179 from sukolsak/fix-arrow
Fix normals in sheet arrows
2021-05-08 18:03:30 -07:00
Sukolsak Sakshuwong
6186c60cd9 fix normals in sheet caps 2021-05-08 00:03:42 -07:00
Sukolsak Sakshuwong
6ab480589a fix lint 2021-05-07 23:55:53 -07:00
Sukolsak Sakshuwong
571f8187c3 fix normals in sheet arrows 2021-05-07 23:29:35 -07:00
Alexander Rose
d510ff00dc Merge pull request #177 from sukolsak/per-group-transparency
Add per-group transparency support to geometry exporters
2021-05-06 23:10:08 -07:00
Sukolsak Sakshuwong
7a0f286fb4 add per-group transparency support to geo-export 2021-05-05 17:54:27 -07:00
Alexander Rose
fccf8d6b87 Merge pull request #174 from sukolsak/glb-export
GLB and STL export
2021-05-04 22:22:41 -07:00
Sukolsak Sakshuwong
e0c08e89d0 use more descriptive labels for geometry exporters 2021-05-04 20:40:28 -07:00
Sukolsak Sakshuwong
ef9885411c small cleanup 2021-05-04 07:45:16 -07:00
Sukolsak Sakshuwong
7542ead360 address @arose's comments 2021-05-03 23:57:32 -07:00
Sukolsak Sakshuwong
043ab08066 fix normal vectors being zero 2021-05-03 18:11:36 -07:00
Sukolsak Sakshuwong
ec0933d197 update changelog 2021-05-03 12:20:20 -07:00
Sukolsak Sakshuwong
aef34a687d glb and stl export 2021-05-03 12:19:28 -07:00
Alexander Rose
9b7192f261 color smoothing for element-index theme 2021-05-03 01:02:34 -07:00
Alexander Rose
18212d9ee7 cpu trilinear interpolation for color smoothing 2021-05-03 00:48:14 -07:00
Alexander Rose
8d4e0730e8 improved color smoothing params handling 2021-05-02 23:34:19 -07:00
Alexander Rose
ba8e9e189f auto-smooth colors if preferred 2021-05-01 18:59:06 -07:00
Alexander Rose
b7935de7af limit color smoothing to group-based themes 2021-05-01 18:57:42 -07:00
Alexander Rose
10ca32f9d7 volumetric color smoothing
- for Mesh and TextureMesh geometries
2021-05-01 18:29:30 -07:00
Alexander Rose
bb86d83c96 better granularity in element-symbol color theme 2021-05-01 17:48:35 -07:00
Alexander Rose
907b08cc99 wip, color grid shader support
- only tested for mesh
2021-05-01 17:47:53 -07:00
Alexander Rose
a07d593909 tweak printImageData debug helper 2021-05-01 17:43:43 -07:00
Alexander Rose
a0a3ff1969 Merge branch 'master' of https://github.com/molstar/molstar into smcol 2021-05-01 17:42:52 -07:00
dsehnal
7fac8a8f77 2.0.5 2021-04-26 16:02:31 +02:00
dsehnal
7266c67e32 2.0.5 changelog 2021-04-26 16:00:31 +02:00
dsehnal
50c8d09742 default camera radius set to 0 2021-04-26 15:13:46 +02:00
dsehnal
7377947975 Changelog 2021-04-25 15:15:26 +02:00
David Sehnal
a3c4daf30a Merge pull request #169 from sukolsak/texture-mesh-export
Add TextureMesh export support
2021-04-25 15:13:26 +02:00
Sukolsak Sakshuwong
9d7e6f1d99 add TextureMesh export support 2021-04-25 05:04:03 -07:00
dsehnal
9e105020e3 lazy volume loading 2021-04-25 12:10:09 +02:00
Alexander Rose
698f7e16bd Merge branch 'master' of https://github.com/molstar/molstar into smcol 2021-04-24 09:42:34 -07:00
Alexander Rose
93df548cfe add torus primitive and fix render tests 2021-04-23 22:55:20 -07:00
Alexander Rose
a0b1593c82 add MeshBuilder.addMesh 2021-04-23 22:12:19 -07:00
Alexander Rose
fc81e08d73 Support full pausing (no draw) rendering 2021-04-23 22:10:01 -07:00
Alexander Rose
5369fa5adf canvas viewport support fixes and improvements
- restrict ssao to viewport bounds
- only downscale ssao buffer (not upscale)
- avoid zero camera radius/radiusMax to allow camera movements in empty scenes and to avoid ssao artifacts
2021-04-23 22:07:34 -07:00
dsehnal
316a77c716 guard against non-invertible matrices in Camera.update
+ relative viewports and dynamic updating of them sometimes caused non-invertible matrix
2021-04-23 19:05:29 +02:00
dsehnal
42dfa69ad7 Residue list selection helper 2021-04-21 21:24:28 +02:00
dsehnal
cae4eb8b0e await screenshot clipboard write & fallback to <img> on fail 2021-04-21 20:08:37 +02:00
David Sehnal
5514b24fdf Merge pull request #167 from molstar/multi-canvas3d
Multi-canvas support for PluginContext
2021-04-21 20:05:05 +02:00
dsehnal
d570bc352e "relative" canvas3d viewport and picking dimensions fix 2021-04-20 20:26:13 +02:00
dsehnal
8a76a3fa64 2.0.4 2021-04-20 11:23:23 +02:00
dsehnal
71bf4e21f5 changelog 2021-04-20 11:21:29 +02:00
dsehnal
e0d36c30d3 Fix measurement labels & interactions 2021-04-20 11:19:15 +02:00
Alexander Rose
d653a96b25 Merge branch 'master' of https://github.com/molstar/molstar into smcol 2021-04-18 13:12:12 -07:00
David Sehnal
b53debcfef Merge pull request #163 from sukolsak/fix-zip-progress
Fix incorrect deflate progress
2021-04-14 15:02:15 +02:00
Sukolsak Sakshuwong
d0705ac226 fix deflate progress 2021-04-14 05:07:05 -07:00
Alexander Rose
e01eacb3fe changelog 2021-04-13 23:12:39 -07:00
Alexander Rose
d4102b476b Fix, read SDF multi-line values 2021-04-13 23:07:55 -07:00
dsehnal
83ce17174a changelog 2021-04-13 21:17:28 +02:00
dsehnal
18023d7f26 Merge branch 'master' of https://github.com/molstar/molstar 2021-04-13 21:16:28 +02:00
dsehnal
a8541d5967 Structure.eachAtomicHierarchyElement 2021-04-13 21:15:44 +02:00
David Sehnal
8b21818f2e Merge pull request #159 from sukolsak/obj-export
OBJ Export
2021-04-13 17:06:48 +02:00
Sukolsak Sakshuwong
0b290247dc show partial progress when exporting large meshes 2021-04-11 13:15:50 -07:00
Sukolsak Sakshuwong
fb5010e962 reorder arguments to addMeshWithColors() 2021-04-10 18:12:32 -07:00
Sukolsak Sakshuwong
178789d327 make RenderObjectExporter.add() async 2021-04-10 18:10:42 -07:00
Sukolsak Sakshuwong
4fae526073 wip 2021-04-09 15:59:14 -07:00
dsehnal
05f1d8085a 2.0.3 2021-04-09 17:47:12 +02:00
dsehnal
38bbabd742 fix test 2021-04-09 17:45:51 +02:00
dsehnal
3ab958a93c changelog 2021-04-09 17:43:23 +02:00
dsehnal
f59d589a30 CubeGridFormat 2021-04-09 17:41:13 +02:00
David Sehnal
11f7e54704 Merge pull request #158 from molstar/gradient-themes
Add ColorTheme.palette support
2021-04-09 10:01:59 +02:00
dsehnal
16ebd8266e changelog 2021-04-09 10:01:21 +02:00
dsehnal
7a796a4d3d Merge branch 'master' of https://github.com/molstar/molstar into gradient-themes 2021-04-09 09:59:46 +02:00
Alexander Rose
1cbb915962 started a changelog 2021-04-08 23:14:55 -07:00
Alexander Rose
80486d58c3 async deflate (and zip) 2021-04-08 23:13:30 -07:00
dsehnal
81bc116c4d TextureImage.filter 2021-04-08 09:49:35 +02:00
dsehnal
4249064dd1 Add ColorTheme.palette support
- add example to basic-wrapper that uses it
2021-04-07 15:55:54 +02:00
Alexander Rose
e0a594121b wip, color smoothing experiments 2021-04-06 23:36:29 -07:00
dsehnal
028c02f50d 2.0.2 2021-03-29 12:48:26 +02:00
dsehnal
76e97d7b59 fix VolumeFromDensityServerCif label 2021-03-29 12:46:10 +02:00
dsehnal
ad1181a75b interpolate ModelFromTrajectory transform 2021-03-29 12:03:13 +02:00
Alexander Rose
5d683462fb add common non-standard amino-acids 2021-03-28 15:38:15 -07:00
Alexander Rose
42422bb0ea add canvas3d.getRenderObjects 2021-03-28 15:36:40 -07:00
dsehnal
861e5c3e97 SDF data item test 2021-03-24 16:02:53 +01:00
dsehnal
614cffda96 2.0.1 2021-03-23 15:44:24 +01:00
dsehnal
2e0379d202 npm ignore 2021-03-23 15:42:52 +01:00
dsehnal
b5cfdcd2a3 2.0.0 2021-03-23 15:31:18 +01:00
dsehnal
c00de6fde0 Merge branch 'master' of https://github.com/molstar/molstar 2021-03-23 11:16:26 +01:00
Alexander Rose
da3a8e56f3 handle negative isovalues in gpu mc 2021-03-22 22:06:49 -07:00
dsehnal
103d6fe775 alpha orbitals tryUseGpu param 2021-03-22 20:18:48 +01:00
dsehnal
5df55e6bf7 SDF delimiter bugfix + multi-molecule SDF support in mol-plugin 2021-03-22 17:21:47 +01:00
dsehnal
3b285086d4 rename files called "macro" due to Jest not being able to process them 2021-03-22 16:53:48 +01:00
dsehnal
91793bc3cc 2.0.0-dev.13 2021-03-22 13:29:42 +01:00
dsehnal
fa3828e820 add model-server-query transform support 2021-03-22 12:51:03 +01:00
Alexander Rose
31ba8212da 2.0.0-dev.12 2021-03-21 16:47:53 -07:00
Alexander Rose
fe27d8e134 Merge pull request #150 from molstar/stubs2
basic support for bond stubs
2021-03-21 16:40:03 -07:00
Alexander Rose
83dcdfdc4b Merge commit '2faa821c50a6dfce700eb8072a61d01d937c18e5' into stubs2 2021-03-21 16:36:33 -07:00
Alexander Rose
f9aaabc1f7 fix interactions bounding sphere 2021-03-21 16:29:56 -07:00
Alexander Rose
034370b44c add includeParent support to interactions 2021-03-21 16:25:03 -07:00
Alexander Rose
b87666df3e don't pad empty bounding spheres 2021-03-21 16:24:25 -07:00
Alexander Rose
c98c3228fe fix structure.asParent 2021-03-21 16:23:57 -07:00
Alexander Rose
9419980dfc make structure state private (like before) 2021-03-21 12:39:21 -07:00
Alexander Rose
42d60420e5 added Structure.asParent
- refactored structure state handling
- removed Structure.WithChild
2021-03-21 12:10:24 -07:00
dsehnal
5b1df333a7 tsconfig jsx param 2021-03-21 16:18:01 +01:00
Alexander Rose
0bb376706d fix ellipsoid repr and support includeParent
- switch off adjustCylinderLength
- handle structure with child
2021-03-20 23:58:48 -07:00
Alexander Rose
eca7da2c72 add adjustCylinderLength param
- so it can be switched off
2021-03-20 23:54:50 -07:00
Alexander Rose
b0bdb3ddb6 tweak param help 2021-03-20 23:52:23 -07:00
Alexander Rose
3180d7c305 basic support for bond stubs
- line and ball & stick repr
- stubs support in link visual helper
- getData and mustRecreate methods for structure repr provider
- Structure.WithChild helper (needs Proxy support)
2021-03-20 18:05:58 -07:00
dsehnal
2faa821c50 2.0.0-dev.11 2021-03-19 17:29:29 +01:00
David Sehnal
7f355ae501 Merge pull request #141 from molstar/surrounding-ligands
Surrounding Ligands query
2021-03-19 17:16:25 +01:00
dsehnal
7f79ff9ff2 StructureSourceControls: show hierarchy preset is >1 trajectory is selected 2021-03-18 15:29:48 +01:00
dsehnal
02de871c59 StructureBoundingBox3D transform 2021-03-18 15:18:15 +01:00
dsehnal
00cb783d4c BoxShape3D transform 2021-03-18 14:15:04 +01:00
David Sehnal
c925919ee5 Merge pull request #148 from TomasKulhanek/master
FIX issue #147 CSS transform:scale cause molstar canvas to have incorrect size
2021-03-17 10:50:44 +01:00
dsehnal
324820890a Fix createModelProperty.isApplicable 2021-03-17 10:35:29 +01:00
Tomas Kulhanek
2687b29d4d FIX molstar/molstar#147 offsetWidth/offsetHeight is correct size of element when css transform:scale is used 2021-03-17 07:46:41 +00:00
dsehnal
7084aaee1a adjust text 2021-03-16 23:02:14 +01:00
dsehnal
520a2f7850 model-server: empty result console output 2021-03-16 22:47:34 +01:00
Alexander Rose
9264987817 camera helper tweaks
- add highlighting
- improved axes alignment
2021-03-15 23:16:19 -07:00
dsehnal
b736ed3ea4 readme tweaks 2021-03-15 21:35:17 +01:00
dsehnal
166d660fa7 2.0.0-dev.10 2021-03-15 20:20:21 +01:00
dsehnal
b8249cde4d interactive camera axis helper 2021-03-15 20:16:07 +01:00
dsehnal
f12f5eca90 Merge branch 'master' into surrounding-ligands 2021-03-15 16:45:53 +01:00
dsehnal
cd3798b46f disable SwaggerUI response syntax highlight 2021-03-15 16:44:36 +01:00
dsehnal
0240e54737 TrackballControlsParams.autoAdjustControls 2021-03-15 14:13:11 +01:00
dsehnal
6a735d902e fix XYZ parser bug 2021-03-15 13:31:44 +01:00
dsehnal
57a942ecb5 requestCameraReset SnapshotProvide
- allow to customize the snapshop based on the current scane/boundingbox/camera state
2021-03-15 12:47:45 +01:00
dsehnal
f67605a398 applyMarkerAction fix 2 2021-03-14 18:46:04 +01:00
dsehnal
aaafa1d5ad model-server: surroundingLigands query 2021-03-14 15:09:37 +01:00
dsehnal
a1d9a77653 surroundingLigands query 2021-03-14 15:00:13 +01:00
dsehnal
f2f1181af3 Merge branch 'master' into surrounding-ligands 2021-03-14 13:16:22 +01:00
dsehnal
864befc48a applyMarkerAction fix 2021-03-14 13:09:53 +01:00
dsehnal
73f6793bd8 surroundingLigands query wip 2021-03-14 12:22:01 +01:00
dsehnal
87ee9d88f2 ResidueSet helper (wip) 2021-03-14 11:21:12 +01:00
dsehnal
b1e245e913 add UndirectedGraph 2021-03-14 10:19:28 +01:00
Alexander Rose
78c0471f39 remove unused Structure.unitsSortedByVolume 2021-03-13 22:35:24 -08:00
Alexander Rose
c57b9b9214 improve preset for many polymer gaps
- show all atom instead
- for medium sized structures
- fixes #57
2021-03-13 22:27:51 -08:00
Alexander Rose
34f33c5bbb fix apply marker type error 2021-03-13 22:24:48 -08:00
Alexander Rose
57da2a7ebb optimized applyMarkerAction
- extract switch statement out of loop
- use int32 view to handle 4 byte together
- don't check for change (essentially done at a higher level anyway)
2021-03-13 12:22:53 -08:00
Alexander Rose
d45d5c0e55 add assertUnreachable helper
- to type check if, e.g. if/switch statements are exhaustive
- TODO use...
2021-03-13 12:20:00 -08:00
dsehnal
42ed425e65 fix secondary_structure_type 2021-03-13 19:58:50 +01:00
dsehnal
f752ee5094 plugin-state server: remove /clear and can't remotely remove sticky entries 2021-03-13 17:56:18 +01:00
dsehnal
044c796942 Fix getSymmetryOperatorRef indexing 2021-03-13 17:53:42 +01:00
dsehnal
0aabbcfaab add back CreateVolumeStreamingBehavior custom controls 2021-03-13 16:40:48 +01:00
Alexander Rose
24274cc53b 2.0.0-dev.9 2021-03-09 22:50:34 -08:00
Alexander Rose
870cef2fd4 add collapse-left-panel to viewer query params 2021-03-09 22:46:53 -08:00
Alexander Rose
bf7b1f5bfd move StateActions to PluginSpec 2021-03-09 22:46:13 -08:00
Alexander Rose
9c9a0312db gpu mc attribution 2021-03-09 22:42:52 -08:00
dsehnal
724fa2a7cd package lock 2021-03-08 18:32:27 +01:00
Alexander Rose
19b36e5942 2.0.0-dev.8 2021-03-07 14:13:51 -08:00
Alexander Rose
b0dd9ab026 Merge pull request #135 from molstar/split-plugin-context
Move part of PluginContext to mol-plugin-ui
2021-03-07 13:41:17 -08:00
Alexander Rose
b77f1d4dee move initDataActions to PluginContext 2021-03-07 13:39:30 -08:00
dsehnal
3770fd7706 move actions back to PluginSpec 2021-03-07 13:36:50 +01:00
dsehnal
e3175c3ed1 Merge branch 'master' into split-plugin-context 2021-03-07 13:24:17 +01:00
dsehnal
7c5dd5b15b fix build caused by some typing edge case 2021-03-07 11:41:56 +01:00
Alexander Rose
0872e11669 2.0.0-dev.7 2021-03-07 01:29:34 -08:00
Alexander Rose
a66da4defc fix missing vars 2021-03-07 01:27:16 -08:00
Alexander Rose
d4ba13a2f2 2.0.0-dev.6 2021-03-07 01:22:30 -08:00
Alexander Rose
3b25e037aa remove obsolete viewer query params 2021-03-07 01:18:37 -08:00
Alexander Rose
189fad3d84 better handle focus on structure update
- fixes #123
2021-03-07 00:30:35 -08:00
Alexander Rose
c3c22ee3bc fix typos in xyz format 2021-03-06 23:39:12 -08:00
Alexander Rose
8a3222005c fix calculated label_seq_id 2021-03-06 23:38:21 -08:00
Alexander Rose
a17da36410 coarse grained tweaks
- coarse grained if less than three times as many atoms as polymer residues
- don't try dssp if coarse grained
2021-03-06 23:37:48 -08:00
Alexander Rose
80323d8122 2.0.0-dev.5 2021-03-06 13:59:17 -08:00
Alexander Rose
cbd6aa0b6b use 32bit depth texture in webgl2 2021-03-06 13:27:04 -08:00
Alexander Rose
3831bd9941 improve handling of coarse grained models 2021-03-06 11:17:43 -08:00
dsehnal
3d3e2c3a86 packages 2021-03-05 00:48:49 +01:00
dsehnal
acf13fa46f split plugin context (wip) 2021-03-05 00:33:00 +01:00
dsehnal
bc5d796653 make pairingThreshold slightly larger 2021-03-04 19:14:35 +01:00
dsehnal
82dd0496c2 covalentlyBondedComponent query 2021-03-04 18:53:05 +01:00
dsehnal
056742ac74 model index animation loop direction 2021-03-04 18:37:47 +01:00
dsehnal
29d4cfbcca add xyz support 2021-03-04 18:10:39 +01:00
Alexander Rose
376449f7c8 add missing PRO to standard components 2021-03-03 17:51:17 -08:00
Alexander Rose
bc37fad007 2.0.0-dev.4 2021-02-27 18:29:41 -08:00
Alexander Rose
2e561a8de7 added interesting pdb entry 2021-02-27 18:25:34 -08:00
Alexander Rose
e6c8c69d0c fix texture-mesh rendering artifacts
- added double buffering for texture-mesh textures
- added buffered uniforms
2021-02-27 18:25:06 -08:00
Alexander Rose
d121a11e28 tweaked structure-element export
- got rid of index.ts for better compatibility
2021-02-22 21:24:19 -08:00
dsehnal
5484a2a72c add logo to orbitals example 2021-02-22 18:56:03 +01:00
791 changed files with 50871 additions and 35084 deletions

View File

@@ -38,7 +38,24 @@
"selector": "ExportDefaultDeclaration",
"message": "Default exports are not allowed"
}
]
],
"no-throw-literal": "error",
"key-spacing": "error",
"object-curly-spacing": ["error", "always"],
"array-bracket-spacing": "error",
"space-in-parens": "error",
"computed-property-spacing": "error",
"prefer-const": ["error", {
"destructuring": "all",
"ignoreReadBeforeAssign": false
}],
"space-before-function-paren": "off",
"func-call-spacing": "off",
"no-multi-spaces": "error",
"block-spacing": "error",
"keyword-spacing": "off",
"space-before-blocks": "error",
"semi-spacing": "error"
},
"overrides": [
{
@@ -89,7 +106,14 @@
"error",
"1tbs", { "allowSingleLine": true }
],
"@typescript-eslint/comma-spacing": "error"
"@typescript-eslint/comma-spacing": "error",
"@typescript-eslint/space-before-function-paren": ["error", {
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}],
"@typescript-eslint/func-call-spacing": ["error"],
"@typescript-eslint/keyword-spacing": ["error"]
}
}
]

View File

@@ -1,18 +0,0 @@
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

20
.github/workflows/node.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 17
- run: npm ci
- run: sudo apt-get install xvfb
- name: Lint
run: npm run lint
- name: Test
run: xvfb-run --auto-servernum npm run jest
- name: Build
run: npm run build

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@ tsconfig.commonjs.tsbuildinfo
*.sublime-workspace
.idea
.DS_Store

View File

@@ -1 +1,5 @@
tsconfig.commonjs.buildinfo
tests
perf-tests
_spec
*.tsbuildinfo
*.js.map

View File

@@ -7,6 +7,8 @@
"*.gql.ts": "graphql"
},
"eslint.options": {
"ignorePattern": ["webpack.config.js", "scripts/*"],
"overrideConfig": {
"ignorePatterns": ["webpack.config.js", "scripts/*"],
},
}
}

386
CHANGELOG.md Normal file
View File

@@ -0,0 +1,386 @@
# Change Log
All notable changes to this project will be documented in this file, following the suggestions of [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/) for its most widely used - and defacto - public interfaces.
Note that since we don't clearly distinguish between a public and private interfaces there will be changes in non-major versions that are potentially breaking. If we make breaking changes to less used interfaces we will highlight it in here.
## [Unreleased]
## [v3.2.0] - 2022-02-17
- Rename "best database mapping" to "SIFTS Mapping"
- Add schema and export support for ``atom_site.pdbx_sifts_xref_*`` fields
- Add schema export support for ``atom_site.pdbx_label_index`` field
- Add `traceOnly` parameter to chain/UniProt-based structure alignment
- Store ``IndexPairBonds`` as a dynamic property.
## [v3.1.0] - 2022-02-06
- Fix ``xrayShaded`` & ``ignoreLight`` params not working at the same time
- Add ``ignoreLight`` to component params
- Tweaks for cleaner default representation style
- Cartoon: use ``nucleotide-ring`` instead of ``nucleotide-block``
- Focus: use ``xrayShaded`` instead of opacity; adjust target size; don't show non-covalent interactions twice
- Fix representation preset side effects (changing post-processing parameters, see #363)
- Add Quick Styles panel (default, illustrative, stylized)
- Fix exported structure missing secondary-structure categories (#364)
- Fix volume streaming error message: distinguish between missing data and server error (#364)
## [v3.0.2] - 2022-01-30
- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
- Fix entity label not displayed when multiple instances of the same entity are highlighted
- Fix empty elements created in ``StructureElement.Loci.extendToAllInstances``
- Measurement options tweaks (allow larger ``textSize``; make ``customText`` essential)
- Fix visual visibility sync edge case when changing state snapshots
## [v3.0.1] - 2022-01-27
- Fix marking pass not working with ``transparentBackground``
- Fix pdbe xray maps url not https
- Fix entity-id color theme broken for non-IHM models
- Improve/fix marking of ``InteractionsInterUnitVisual`` (mark when all contact-feature members are given)
- Add missing "entity-id" and "enity-source" options for carbon coloring to "element-symbol" color theme
- Fix VolumeServer/query CLI
- Support automatic iso-value adjustment for VolumeServer data in ``Viewer.loadVolumeFromUrl``
- Emit drag event whenever started within viewport (not only for non-empty loci)
## [v3.0.0] - 2022-01-23
- Assembly handling tweaks:
- Do not include suffix for "identity assembly operators"
- Do not include assembly-related categories to export if the structure was composed from an assembly
- Special case for ``structAsymMap`` if Mol* asym id operator mapping is present
- Support for opening ZIP files with multiple entries
- Add Model Export extension
- Bugfix: Automatically treat empty string as "non-present" value in BinaryCIF writer.
- Fix coarse model support in entity-id color theme
- Fix marking of carbohydrate visuals (whole chain could get marked instead of single residue)
- Add custom colors to "element-symbol", "molecule-type", "residue-name", and "secondary-structure" themes
- Support/bugfixes for ``atom_site.pdbx_sifts_xref`` categories
- Improve/fix marking of ``InteractionsIntraUnitVisual`` (mark when all contact-feature members are given)
## [v3.0.0-dev.10] - 2022-01-17
- Fix ``getOperatorsForIndex``
- Pass animation info (current frame & count) to state animations
- Fix camera stutter for "camera spin" animation
- Add formal charge parsing support for MOL/SDF files (thanks @ptourlas)
- [Breaking] Cleaner looking ``MembraneOrientationVisuals`` defaults
- [Breaking] Add rock animation to trackball controls
- Add ``animate`` to ``TrackballControlsParams``, remove ``spin`` and ``spinSpeed``
- Add ``animate`` to ``SimpleSettingsParams``, remove ``spin``
- Add "camera rock" state animation
- Add support for custom colors to "molecule-type" theme
- [Breaking] Add style parameter to "illustrative" color theme
- Defaults to "entity-id" style instead of "chain-id"
- Add "illustrative" representation preset
## [v3.0.0-dev.9] - 2022-01-09
- Add PDBj as a ``pdb-provider`` option
- Move Viewer APP to a separate file to allow use without importing light theme & index.html
- Add symmetry support for mol2 files (only spacegroup setting 1)
- Fix mol2 files element symbol assignment
- Improve bond assignment from ``IndexPairBonds``
- Add ``key`` field for mapping to source data
- Fix assignment of bonds with unphysical length
- Fix label/stats of single atom selection in multi-chain units
## [v3.0.0-dev.8] - 2021-12-31
- Add ``PluginFeatureDetection`` and disable WBOIT in Safari 15.
- Add ``disable-wboit`` Viewer GET param
- Add ``prefer-webgl1`` Viewer GET param
- [Breaking] Refactor direct-volume rendering
- Remove isosurface render-mode (use GPU MC instead)
- Move coloring into theme (like for other geometries/renderables)
- Add ``direct`` color type
- Remove color from transfer-function (now only alpha)
- Add direct-volume color theme support
- Add volume-value color theme
- [Breaking] Use size theme in molecular/gaussian surface & label representations
- This is breaking because it was hardcoded to ``physical`` internally but the repr size theme default was ``uniform`` (now ``physical``)
## [v3.0.0-dev.7] - 2021-12-20
- Reduce number of created programs/shaders
- Support specifying variants when creating graphics render-items
- Change double-side shader param from define to uniform
- Remove dMarkerType shader define (use uMarker as needed)
- Support to ignore defines depending on the shader variant
- Combine pickObject/pickInstance/pickGroup shader variants into one
- Combine markingDepth/markingMask shader variants into one
- Correctly set shader define flags for overpaint, transparency, substance, clipping
- [Breaking] Add per-object clip rendering properties (variant/objects)
- ``SimpleSettingsParams.clipping.variant/objects`` and ``RendererParams.clip`` were removed
## [v3.0.0-dev.6] - 2021-12-19
- Enable temporal multi-sampling by default
- Fix flickering during marking with camera at rest
- Enable ``aromaticBonds`` in structure representations by default
- Add ``PluginConfig.Structure.DefaultRepresentationPreset``
- Add ModelArchive support
- schema extensions (e.g., AlphaFold uses it for the pLDDT score)
- ModelArchive option in DownloadStructure action
- ``model-archive`` GET parameter for Viewer app
- ``Viewer.loadModelArchive`` method
- Improve support for loading AlphaFold structures
- Automatic coloring by pLDDT
- AlphaFold DB option in DownloadStructure action
- ``afdb`` GET parameter for Viewer app
- ``Viewer.loadAlphaFoldDb`` method
- Add QualityAssessment extension (using data from ma_qa_metric_local mmcif category)
- pLDDT & qmean score: coloring, repr presets, molql symbol, loci labels (including avg for mutli-residue selections)
- pLDDT: selection query
- Warn about erroneous symmetry operator matrix (instead of throwing an error)
- Added ``createPluginUI`` to ``mol-plugin-ui``
- Support ``onBeforeUIRender`` to make sure initial UI works with custom presets and similar features.
- [Breaking] Removed ``createPlugin`` and ``createPluginAsync`` from ``mol-plugin-ui``
- Please use ``createPluginUI`` instead
- Improve aromatic bonds handling
- Don't detect aromatic bonds for rings < 5 atoms based on planarity
- Prefer atoms in aromatic rings as bond reference positions
## [v3.0.0-dev.5] - 2021-12-16
- Fix initial camera reset not triggering for some entries.
## [v3.0.0-dev.4] - 2021-12-14
- Add ``bumpiness`` (per-object and per-group), ``bumpFrequency`` & ``bumpAmplitude`` (per-object) render parameters (#299)
- Change ``label`` representation defaults: Use text border instead of rectangle background
- Add outline color option to renderer
- Fix false positives in Model.isFromPdbArchive
- Add drag and drop support for loading any file, including multiple at once
- If there are session files (.molx or .molj) among the dropped files, only the first session will be loaded
- Add drag and drop overlay
- Safari 15.1 - 15.3 WebGL 2 support workaround
- [Breaking] Move ``react`` and ``react-dom`` to ``peerDependencies``. This might break some builds.
## [v3.0.0-dev.3] - 2021-12-4
- Fix OBJ and USDZ export
## [v3.0.0-dev.2] - 2021-12-1
- Do not include tests and source maps in NPM package
## [v3.0.0-dev.0] - 2021-11-28
- Add multiple lights support (with color, intensity, and direction parameters)
- [Breaking] Add per-object material rendering properties
- ``SimpleSettingsParams.lighting.renderStyle`` and ``RendererParams.style`` were removed
- Add substance theme with per-group material rendering properties
- ``StructureComponentManager.Options`` state saving support
- ``ParamDefinition.Group.presets`` support
## [v2.4.1] - 2021-11-28
- Fix: allow atoms in aromatic rings to do hydrogen bonds
## [v2.4.0] - 2021-11-25
- Fix secondary-structure property handling
- StructureElement.Property was incorrectly resolving type & key
- StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
- Re-enable VAO with better workaround (bind null elements buffer before deleting)
- Add ``Representation.geometryVersion`` (increments whenever the geometry of any of its visuals changes)
- Add support for grid-based smoothing of Overpaint and Transparency visual state for surfaces
## [v2.3.9] - 2021-11-20
- Workaround: switch off VAO support for now
## [v2.3.8] - 2021-11-20
- Fix double canvas context creation (in plugin context)
- Fix unused vertex attribute handling (track which are used, disable the rest)
- Workaround for VAO issue in Chrome 96 (can cause WebGL to crash on geometry updates)
## [v2.3.7] - 2021-11-15
- Added ``ViewerOptions.collapseRightPanel``
- Added ``Viewer.loadTrajectory`` to support loading "composed" trajectories (e.g. from gro + xtc)
- Fix: handle parent in Structure.remapModel
- Add ``rounded`` and ``square`` helix profile options to Cartoon representation (in addition to the default ``elliptical``)
## [v2.3.6] - 2021-11-8
- Add additional measurement controls: orientation (box, axes, ellipsoid) & plane (best fit)
- Improve aromatic bond visuals (add ``aromaticScale``, ``aromaticSpacing``, ``aromaticDashCount`` params)
- [Breaking] Change ``adjustCylinderLength`` default to ``false`` (set to true for focus representation)
- Fix marker highlight color overriding select color
- CellPack extension update
- add binary model support
- add compartment (including membrane) geometry support
- add latest mycoplasma model example
- Prefer WebGL1 in Safari 15.1.
## [v2.3.5] - 2021-10-19
- Fix sequence viewer for PDB files with COMPND record and multichain entities.
- Fix index pair bonds order assignment
## [v2.3.4] - 2021-10-12
- Fix pickScale not taken into account in line/point shader
- Add pixel-scale, pick-scale & pick-padding GET params to Viewer app
- Fix selecting bonds not adding their atoms in selection manager
- Add ``preferAtoms`` option to SelectLoci/HighlightLoci behaviors
- Make the implicit atoms of bond visuals pickable
- Add ``preferAtomPixelPadding`` to Canvas3dInteractionHelper
- Add points & crosses visuals to Line representation
- Add ``pickPadding`` config option (look around in case target pixel is empty)
- Add ``multipleBonds`` param to bond visuals with options: off, symmetric, offset
- Fix ``argparse`` config in servers.
## [v2.3.3] - 2021-10-01
- Fix direct volume shader
## [v2.3.2] - 2021-10-01
- Prefer WebGL1 on iOS devices until WebGL2 support has stabilized.
## [v2.3.1] - 2021-09-28
- Add Charmm saccharide names
- Treat missing occupancy column as occupancy of 1
- Fix line shader not accounting for aspect ratio
- [Breaking] Fix point repr & shader
- Was unusable with ``wboit``
- Replaced ``pointFilledCircle`` & ``pointEdgeBleach`` params by ``pointStyle`` (square, circle, fuzzy)
- Set ``pointSizeAttenuation`` to false by default
- Set ``sizeTheme`` to ``uniform`` by default
- Add ``markerPriority`` option to Renderer (useful in combination with edges of marking pass)
- Add support support for ``chem_comp_bond`` and ``struct_conn`` categories (fixes ModelServer behavior where these categories should have been present)
- Model and VolumeServer: fix argparse config
## [v2.3.0] - 2021-09-06
- Take include/exclude flags into account when displaying aromatic bonds
- Improve marking performance
- Avoid unnecessary draw calls/ui updates when marking
- Check if loci is superset of visual
- Check if loci overlaps with unit visual
- Ensure ``Interval`` is used for ranges instead of ``SortedArray``
- Add uniform marker type
- Special case for reversing previous mark
- Add optional marking pass
- Outlines visible and hidden parts of highlighted/selected groups
- Add highlightStrength/selectStrength renderer params
## [v2.2.3] - 2021-08-25
- Add ``invertCantorPairing`` helper function
- Add ``Mesh`` processing helper ``.smoothEdges``
- Smooth border of molecular-surface with ``includeParent`` enabled
- Hide ``includeParent`` option from gaussian-surface visuals (not particularly useful)
- Improved ``StructureElement.Loci.size`` performance (for marking large cellpack models)
- Fix new ``TransformData`` issues (camera/bounding helper not showing up)
- Improve marking performance (avoid superfluous calls to ``StructureElement.Loci.isWholeStructure``)
## [v2.2.2] - 2021-08-11
- Fix ``TransformData`` issues [#133](https://github.com/molstar/molstar/issues/133)
- Fix ``mol-script`` query compiler const expression recognition.
## [v2.2.1] - 2021-08-02
- Add surrounding atoms (5 Angstrom) structure selection query
- [Breaking] Add maxDistance prop to ``IndexPairBonds``
- Fix coordinateSystem not handled in ``Structure.asParent``
- Add ``dynamicBonds`` to ``Structure`` props (force re-calc on model change)
- Expose as optional param in root structure transform helper
- Add overpaint support to geometry exporters
- ``InputObserver`` improvements
- normalize wheel speed across browsers/platforms
- support Safari gestures (used by ``TrackballControls``)
- ``PinchInput.fractionDelta`` and use it in ``TrackballControls``
## [v2.2.0] - 2021-07-31
- Add ``tubularHelices`` parameter to Cartoon representation
- Add ``SdfFormat`` and update SDF parser to be able to parse data headers according to spec (hopefully :)) #230
- Fix mononucleotides detected as polymer components (#229)
- Set default outline scale back to 1
- Improved DCD reader cell angle handling (interpret near 0 angles as 90 deg)
- Handle more residue/atom names commonly used in force-fields
- Add USDZ support to ``geo-export`` extension.
- Fix ``includeParent`` support for multi-instance bond visuals.
- Add ``operator`` Loci granularity, selecting everything with the same operator name.
- Prefer ``_label_seq_id`` fields in secondary structure assignment.
- Support new EMDB API (https://www.ebi.ac.uk/emdb/api/entry/map/[EMBD-ID]) for EM volume contour levels.
- ``Canvas3D`` tweaks:
- Update ``forceDraw`` logic.
- Ensure the scene is re-rendered when viewport size changes.
- Support ``noDraw`` mode in ``PluginAnimationLoop``.
## [v2.1.0] - 2021-07-05
- Add parameter for to display aromatic bonds as dashes next to solid cylinder/line.
- Add backbone representation
- Fix outline in orthographic mode and set default scale to 2.
## [v2.0.7] - 2021-06-23
- Add ability to specify ``volumeIndex`` in ``Viewer.loadVolumeFromUrl`` to better support Volume Server inputs.
- Support in-place reordering for trajectory ``Frame.x/y/z`` arrays for better memory efficiency.
- Fixed text CIF encoder edge cases (most notably single whitespace not being escaped).
## [v2.0.6] - 2021-06-01
- Add glTF (GLB) and STL support to ``geo-export`` extension.
- Protein crosslink improvements
- Change O-S bond distance to allow for NOS bridges (doi:10.1038/s41586-021-03513-3)
- Added NOS-bridges query & improved disulfide-bridges query
- Fix #178: ``IndexPairBonds`` for non-single residue structures (bug due to atom reordering).
- Add volumetric color smoothing for MolecularSurface and GaussianSurface representations (#173)
- Fix nested 3d grid lookup that caused results being overwritten in non-covalent interactions computation.
- Basic implementation of ``BestDatabaseSequenceMapping`` (parse from CIF, color theme, superposition).
- Add atom id ranges support to Selection UI.
## [v2.0.5] - 2021-04-26
- Ability to pass ``Canvas3DContext`` to ``PluginContext.fromCanvas``.
- Relative frame support for ``Canvas3D`` viewport.
- Fix bug in screenshot copy UI.
- Add ability to select residues from a list of identifiers to the Selection UI.
- Fix SSAO bugs when used with ``Canvas3D`` viewport.
- Support for full pausing (no draw) rendering: ``Canvas3D.pause(true)``.
- Add ``MeshBuilder.addMesh``.
- Add ``Torus`` primitive.
- Lazy volume loading support.
- [Breaking] ``Viewer.loadVolumeFromUrl`` signature change.
- ``loadVolumeFromUrl(url, format, isBinary, isovalues, entryId)`` => ``loadVolumeFromUrl({ url, format, isBinary }, isovalues, { entryId, isLazy })``
- Add ``TextureMesh`` support to ``geo-export`` extension.
## [v2.0.4] - 2021-04-20
- [WIP] Mesh export extension
- ``Structure.eachAtomicHierarchyElement`` (#161)
- Fixed reading multi-line values in SDF format
- Fixed Measurements UI labels (#166)
## [v2.0.3] - 2021-04-09
- Add support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
- [Breaking] The ``zip`` function is now asynchronous and expects a ``RuntimeContext``. Also added ``Zip()`` returning a ``Task``.
- [Breaking] Add ``CubeGridFormat`` in ``alpha-orbitals`` extension.
## [v2.0.2] - 2021-03-29
- Add ``Canvas3D.getRenderObjects``.
- [WIP] Animate state interpolating, including model trajectories
- Recognise MSE, SEP, TPO, PTR and PCA as non-standard amino-acids.
- Fix VolumeFromDensityServerCif transform label
## [v2.0.1] - 2021-03-23
- Exclude tsconfig.commonjs.tsbuildinfo from npm bundle
## [v2.0.0] - 2021-03-23
Too many changes to list as this is the start of the changelog... Notably, default exports are now forbidden.

72
CITATION.cff Normal file
View File

@@ -0,0 +1,72 @@
cff-version: 1.2.0
title: >-
Mol* library
message: >-
Please cite this software using the metadata from
'preferred-citation'.
authors:
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Áron Samuel
family-names: Kovács
- given-names: Ludovic
family-names: Autin
orcid: 'https://orcid.org/0000-0002-2197-191X'
- given-names: Michal
family-names: Malý
- given-names: Jiří
family-names: Černý
- given-names: Panagiotis
family-names: Tourlas
type: software
doi: 10.5281/zenodo.3947306
preferred-citation:
authors:
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Mandar
family-names: Deshpande
orcid: 'https://orcid.org/0000-0002-9043-7665'
- given-names: Radka
family-names: Svobodová
orcid: 'https://orcid.org/0000-0002-3840-8760'
- given-names: Karel
family-names: Berka
orcid: 'https://orcid.org/0000-0001-9472-2589'
- given-names: Václav
family-names: Bazgier
orcid: 'https://orcid.org/0000-0003-3393-3010'
- given-names: Sameer
family-names: Velankar
orcid: 'https://orcid.org/0000-0002-8439-5964'
- given-names: Stephen K
family-names: Burley
orcid: 'https://orcid.org/0000-0002-2487-9713'
- given-names: Jaroslav
family-names: Koča
orcid: 'https://orcid.org/0000-0002-2780-4901'
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
title: >-
Mol* Viewer: modern web app for 3D visualization
and analysis of large biomolecular structures
type: article
doi: 10.1093/nar/gkab314
journal: "Nucleic Acids Research"
issue: W1
volume: 49
year: 2021
month: 7
pages: "W431W437"

View File

@@ -5,15 +5,22 @@
# Mol*
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.
The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that serves as a basis for the next-generation data delivery and analysis tools for (not only) macromolecular structure data. Mol* development was jointly initiated by PDBe and RCSB PDB to combine and build on the strengths of [LiteMol](https://litemol.org) (developed by PDBe) and [NGL](https://nglviewer.org) (developed by RCSB PDB) viewers.
This particular project is the implementation of this technology (still under development).
When using Mol*, please cite:
*If you are looking for the "MOLeculAR structure annoTator", that package is now available on NPM as [MolArt](https://www.npmjs.com/package/molart).*
David Sehnal, Sebastian Bittrich, Mandar Deshpande, Radka Svobodová, Karel Berka, Václav Bazgier, Sameer Velankar, Stephen K Burley, Jaroslav Koča, Alexander S Rose: [Mol* Viewer: modern web app for 3D visualization and analysis of large biomolecular structures](https://doi.org/10.1093/nar/gkab314), *Nucleic Acids Research*, 2021; https://doi.org/10.1093/nar/gkab314.
## Project Overview
### Protein Data Bank Integrations
The core of Mol* currently consists of these modules (see under `src/`):
- The [pdbe-molstar](https://github.com/molstar/pdbe-molstar) library is the Mol* implementation used by EMBL-EBI data resources such as [PDBe](https://pdbe.org/), [PDBe-KB](https://pdbe-kb.org/) and [AlphaFold DB](https://alphafold.ebi.ac.uk/). This implementation can be used as a JS plugin and a Web component and supports property/attribute-based easy customisation. It provides helper methods to facilitate programmatic interactions between the web application and the 3D viewer. It also provides a superposition view for overlaying all the observed ligand molecules on representative protein conformations.
- [rcsb-molstar](https://github.com/molstar/rcsb-molstar) is the Mol* plugin used by [RCSB PDB](https://www.rcsb.org). The project provides additional presets for the visualization of structure alignments and structure motifs such as ligand binding sites. Furthermore, [rcsb-molstar](https://github.com/molstar/rcsb-molstar) allows to interactively add or hide of (parts of) chains, as seen in the [3D Protein Feature View](https://www.rcsb.org/3d-sequence/4hhb).
## Project Structure Overview
The core of Mol* 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.)
@@ -29,7 +36,6 @@ The core of Mol* currently consists of these modules (see under `src/`):
- `mol-gl` A wrapper around WebGL.
- `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 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 into 3rd party solutions.
@@ -41,7 +47,7 @@ Moreover, the project contains the implementation of `servers`, including
- `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 `cli` apps (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
The project also contains performance tests (`perf-tests`), `examples`, and `cli` apps (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
## Previous Work
This project builds on experience from previous solutions:
@@ -69,6 +75,17 @@ If working on just the viewer, ``npm run watch-viewer`` will provide shorter com
Debug/production mode in browsers can be turned on/off during runtime by calling ``setMolStarDebugMode(true/false, true/false)`` from the dev console.
### Cleaning and forcing a full rebuild
npm run clean
Wipes the `build` and `lib` directories and `.tsbuildinfo` files.
npm run rebuild
Runs the cleanup script prior to building the project, forcing a full rebuild of the project.
Use these commands to resolve occassional build failures which may arise after some dependency updates. Once done, `npm run build` should work again. Note that full rebuilds take more time to complete.
### Build for production:
NODE_ENV=production npm run build
@@ -123,9 +140,9 @@ and navigate to `build/viewer`
**Convert any CIF to BinaryCIF**
node lib/servers/model/preprocess -i file.cif -ob file.bcif
node lib/commonjs/servers/model/preprocess -i file.cif -ob file.bcif
To see all available commands, use ``node lib/servers/model/preprocess -h``.
To see all available commands, use ``node lib/commonjs/servers/model/preprocess -h``.
Or
@@ -169,12 +186,9 @@ To get syntax highlighting for shader and graphql files add the following to Vis
## Contributing
Just open an issue or make a pull request. All contributions are welcome.
## Roadmap
Continually develop this prototype project. As individual modules become stable, make them into standalone libraries.
## Funding
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/)
* [EntosAI](https://www.entos.ai) (``alpha-orbitals`` extension)
* [EntosAI](https://www.entos.ai)

View File

@@ -2,11 +2,11 @@ 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
database_code.icsd
database_code.mdf
database_code.nbs
database_code.csd
database_code.cod
chemical.name_systematic
chemical.name_common
@@ -24,8 +24,8 @@ 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.name_h-m_full
space_group.it_number
space_group_symop.operation_xyz
cell.length_a
@@ -35,14 +35,14 @@ cell.angle_alpha
cell.angle_beta
cell.angle_gamma
cell.volume
cell.formula_units_Z
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.u_iso_or_equiv
atom_site.adp_type
atom_site.occupancy
atom_site.calc_flag
@@ -52,20 +52,13 @@ 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
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
geom_bond.atom_site_label_1
geom_bond.atom_site_label_2
1 audit.block_doi
2 database_code.depnum_ccdc_archive
3 database_code.depnum_ccdc_fiz
4 database_code.ICSD database_code.icsd
5 database_code.MDF database_code.mdf
6 database_code.NBS database_code.nbs
7 database_code.CSD database_code.csd
8 database_code.COD database_code.cod
9 chemical.name_systematic
10 chemical.name_common
11 chemical.melting_point
12 chemical_formula.moiety
24 cell.length_a
25 cell.length_b
26 cell.length_c
27 cell.angle_alpha
28 cell.angle_beta
29 cell.angle_gamma
30 cell.volume
31 cell.formula_units_Z cell.formula_units_z
35 atom_site.fract_y
36 atom_site.fract_z
37 atom_site.U_iso_or_equiv atom_site.u_iso_or_equiv
38 atom_site.adp_type
39 atom_site.occupancy
40 atom_site.calc_flag
41 atom_site.refinement_flags
42 atom_site.disorder_assembly
43 atom_site.disorder_group
44 atom_site.site_symmetry_multiplicity
45 atom_site_aniso.label
46 atom_site_aniso.U atom_site_aniso.u
47 atom_site_aniso.U_11 atom_site_aniso.u_11
48 atom_site_aniso.U_22 atom_site_aniso.u_22
52 atom_site_aniso.U_12 atom_site_aniso.u_12
53 atom_site_aniso.U_su geom_bond.atom_site_label_1
54 atom_site_aniso.U_11_su geom_bond.atom_site_label_2
55 atom_site_aniso.U_22_su geom_bond.distance
56 atom_site_aniso.U_33_su geom_bond.site_symmetry_1
57 atom_site_aniso.U_23_su geom_bond.site_symmetry_2
58 atom_site_aniso.U_13_su geom_bond.publ_flag
59 atom_site_aniso.U_12_su geom_bond.valence
60
61
geom_bond.distance
geom_bond.site_symmetry_1
geom_bond.site_symmetry_2
geom_bond.publ_flag
geom_bond.valence
62
63
64

View File

@@ -24,6 +24,11 @@ atom_site.auth_asym_id
atom_site.auth_seq_id
atom_site.pdbx_PDB_model_num
atom_site.ihm_model_id
atom_site.pdbx_label_index
atom_site.pdbx_sifts_xref_db_name
atom_site.pdbx_sifts_xref_db_acc
atom_site.pdbx_sifts_xref_db_num
atom_site.pdbx_sifts_xref_db_res
atom_site_anisotrop.id
atom_site_anisotrop.U
@@ -246,6 +251,14 @@ citation_author.ordinal
exptl.entry_id
exptl.method
software.classification
software.date
software.description
software.name
software.pdbx_ordinal
software.type
software.version
struct.entry_id
struct.title
struct.pdbx_descriptor
@@ -802,4 +815,58 @@ 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
ihm_multi_state_modeling.details
ma_data.content_type
ma_data.content_type_other_details
ma_data.id
ma_data.name
ma_model_list.data_id
ma_model_list.model_group_id
ma_model_list.model_group_name
ma_model_list.model_id
ma_model_list.model_name
ma_model_list.model_type
ma_model_list.ordinal_id
ma_qa_metric.id
ma_qa_metric.mode
ma_qa_metric.name
ma_qa_metric.software_group_id
ma_qa_metric.type
ma_qa_metric_global.metric_id
ma_qa_metric_global.metric_value
ma_qa_metric_global.model_id
ma_qa_metric_global.ordinal_id
ma_qa_metric_local.label_asym_id
ma_qa_metric_local.label_comp_id
ma_qa_metric_local.label_seq_id
ma_qa_metric_local.metric_id
ma_qa_metric_local.metric_value
ma_qa_metric_local.model_id
ma_qa_metric_local.ordinal_id
ma_software_group.group_id
ma_software_group.ordinal_id
ma_software_group.software_id
ma_target_entity.data_id
ma_target_entity.entity_id
ma_target_entity.origin
ma_target_entity_instance.asym_id
ma_target_entity_instance.details
ma_target_entity_instance.entity_id
ma_target_ref_db_details.db_accession
ma_target_ref_db_details.db_code
ma_target_ref_db_details.db_name
ma_target_ref_db_details.ncbi_taxonomy_id
ma_target_ref_db_details.organism_scientific
ma_target_ref_db_details.seq_db_align_begin
ma_target_ref_db_details.seq_db_align_end
ma_target_ref_db_details.seq_db_isoform
ma_target_ref_db_details.target_entity_id
1 atom_sites.entry_id
24 atom_site.pdbx_PDB_model_num
25 atom_site.ihm_model_id
26 atom_site_anisotrop.id atom_site.pdbx_label_index
27 atom_site.pdbx_sifts_xref_db_name
28 atom_site.pdbx_sifts_xref_db_acc
29 atom_site.pdbx_sifts_xref_db_num
30 atom_site.pdbx_sifts_xref_db_res
31 atom_site_anisotrop.id
32 atom_site_anisotrop.U
33 atom_site_anisotrop.U_esd
34 atom_site_anisotrop.pdbx_PDB_ins_code
251 struct_conn.conn_type_id struct_conf.end_auth_comp_id
252 struct_conn.pdbx_PDB_id struct_conf.end_auth_asym_id
253 struct_conn.ptnr1_label_asym_id struct_conf.end_auth_seq_id
254 struct_conf.pdbx_PDB_helix_class
255 struct_conf.details
256 struct_conf.pdbx_PDB_helix_length
257 struct_conn.id
258 struct_conn.conn_type_id
259 struct_conn.pdbx_PDB_id
260 struct_conn.ptnr1_label_asym_id
261 struct_conn.ptnr1_label_comp_id
262 struct_conn.ptnr1_label_comp_id struct_conn.ptnr1_label_seq_id
263 struct_conn.ptnr1_label_seq_id struct_conn.ptnr1_label_atom_id
264 struct_conn.ptnr1_label_atom_id struct_conn.pdbx_ptnr1_label_alt_id
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872

View File

@@ -26,3 +26,15 @@
* Protein (1BRR, 5Z6Y)
* DNA (5D3G)
* Multiple models with different sets of ligands or missing ligands (1J6T, 1VRC, 2ICY, 1O2F)
* Long linear sugar chain (4HG6)
* Anisotropic B-factors/Ellipsoids (1EJG)
* NOS bridges (LYS-CSO in 7B0L, 6ZWJ, 6ZWH)
* Non-polymer components in polymer entities
* PN2 in 1F80
* ACE (many, e.g. 5AGU, 1E1X)
* ACY in 7ABY
* NH2 (many, e.g. 6Y13)
Assembly symmetries
* 5M30 (Assembly 1, C3 local and pseudo)
* 1RB8 (Assembly 1, I global)

86
examples/ace2-hit.mol2 Normal file
View File

@@ -0,0 +1,86 @@
@<TRIPOS>MOLECULE
ace2_r_r2.top2000.poses.plain/3_Z1137565832_1_T2.pdb
37 41 0 0 0
SMALL
GASTEIGER
@<TRIPOS>ATOM
1 C 64.7720 85.9180 38.1090 C.3 1 LIG1 0.0799
2 C 64.6440 84.6900 37.1570 C.3 1 LIG1 0.0306
3 C 65.2660 83.4260 37.8080 C.3 1 LIG1 0.0927
4 N 66.6560 83.6710 38.1790 N.pl3 1 LIG1 -0.2919
5 C 67.7080 82.9280 37.6260 C.ar 1 LIG1 0.1520
6 C 69.0970 83.1860 37.8250 C.ar 1 LIG1 0.0393
7 C 70.0830 82.4100 37.1810 C.ar 1 LIG1 0.0436
8 C 69.6450 81.3740 36.3510 C.ar 1 LIG1 0.1867
9 N 70.3370 80.5030 35.6050 N.ar 1 LIG1 -0.1270
10 N 69.4700 79.7480 35.0040 N.ar 1 LIG1 -0.1228
11 C 68.2200 80.1120 35.3570 C.ar 1 LIG1 0.2583
12 N 68.3350 81.1470 36.2040 N.ar 1 LIG1 -0.1866
13 N 67.4230 81.8700 36.8030 N.ar 1 LIG1 -0.1473
14 C 66.8190 84.7600 39.1350 C.3 1 LIG1 0.0927
15 C 66.2560 86.0870 38.5570 C.3 1 LIG1 0.0306
16 N 64.4520 87.4670 36.2150 N.am 1 LIG1 -0.2979
17 H 64.9740 86.7650 35.7110 H 1 LIG1 0.1498
18 C 64.2290 87.1720 37.5220 C.2 1 LIG1 0.2224
19 O 63.5690 87.9550 38.2480 O.2 1 LIG1 -0.2751
20 C 64.0160 88.6470 35.5420 C.3 1 LIG1 0.1559
21 C 65.9650 88.2170 32.5700 C.ar 1 LIG1 0.1629
22 N 65.8040 88.0390 33.8860 N.ar 1 LIG1 -0.3232
23 H 66.4190 87.5270 34.5040 H 1 LIG1 0.1686
24 C 64.6770 88.6690 34.2330 C.ar 1 LIG1 0.1601
25 N 64.1890 89.2810 33.1430 N.ar 1 LIG1 -0.1318
26 N 64.9640 89.0070 32.1450 N.ar 1 LIG1 -0.1293
27 F 69.9260 86.0280 29.3520 F 1 LIG1 -0.2042
28 C 68.9880 86.5420 30.0990 C.ar 1 LIG1 0.1401
29 C 67.6590 86.0900 29.9830 C.ar 1 LIG1 0.0297
30 C 66.6500 86.6450 30.7950 C.ar 1 LIG1 0.0053
31 C 66.9590 87.6470 31.7450 C.ar 1 LIG1 0.0359
32 C 68.2970 88.0990 31.8400 C.ar 1 LIG1 0.0053
33 C 69.3030 87.5560 31.0230 C.ar 1 LIG1 0.0297
34 C 66.9850 79.4910 34.8440 C.3 1 LIG1 0.4541
35 F 67.3150 78.4110 34.0640 F 1 LIG1 -0.1631
36 F 66.2460 80.3690 34.0910 F 1 LIG1 -0.1631
37 F 66.1920 79.0650 35.8800 F 1 LIG1 -0.1631
@<TRIPOS>BOND
1 1 2 1
2 1 18 1
3 1 15 1
4 2 3 1
5 3 4 1
6 4 5 1
7 4 14 1
8 5 13 ar
9 5 6 ar
10 6 7 ar
11 7 8 ar
12 8 9 ar
13 8 12 ar
14 9 10 ar
15 10 11 ar
16 11 34 1
17 11 12 ar
18 12 13 ar
19 14 15 1
20 16 20 1
21 16 17 1
22 16 18 am
23 18 19 2
24 20 24 1
25 21 31 1
26 21 26 ar
27 21 22 ar
28 22 24 ar
29 22 23 1
30 24 25 ar
31 25 26 ar
32 27 28 1
33 28 29 ar
34 28 33 ar
35 29 30 ar
36 30 31 ar
37 31 32 ar
38 32 33 ar
39 34 35 1
40 34 36 1
41 34 37 1

7628
examples/ace2.pdbqt Normal file

File diff suppressed because it is too large Load Diff

25202
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "2.0.0-dev.3",
"version": "3.2.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -13,8 +13,11 @@
"scripts": {
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"test": "npm run lint && jest",
"test": "npm install --no-save \"gl@^5.0.0\" && npm run lint && jest",
"jest": "jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"clean": "node ./scripts/clean.js",
"rebuild": "npm run clean && npm run build",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
@@ -26,16 +29,16 @@
"watch-tsc": "tsc --watch --incremental",
"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",
"watch-webpack-viewer-debug": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.debug.js",
"watch-webpack": "webpack -w --mode development --stats minimal",
"watch-webpack-viewer": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.js",
"watch-webpack-viewer-debug": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.debug.js",
"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",
"version": "npm run build",
"version": "npm run rebuild && cpx .npmignore lib/",
"postversion": "git push && git push --tags"
},
"files": [
@@ -83,69 +86,75 @@
"Á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>"
"Jiří Černý <jiri.cerny@ibt.cas.cz>",
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
],
"license": "MIT",
"devDependencies": {
"@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",
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.5.0",
"@graphql-codegen/time": "^3.1.1",
"@graphql-codegen/typescript": "^2.4.3",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
"@graphql-codegen/typescript-graphql-request": "^4.3.4",
"@graphql-codegen/typescript-operations": "^2.2.4",
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.4.0",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"benchmark": "^2.1.4",
"concurrently": "^5.3.0",
"cpx2": "^3.0.0",
"css-loader": "^5.0.1",
"eslint": "^7.15.0",
"concurrently": "^7.0.0",
"cpx2": "^4.1.2",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.6.0",
"eslint": "^8.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"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",
"fs-extra": "^10.0.0",
"graphql": "^16.3.0",
"http-server": "^14.1.0",
"jest": "^27.5.0",
"mini-css-extract-plugin": "^2.5.3",
"path-browserify": "^1.0.1",
"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.1.2",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-version-file-plugin": "^0.4.0"
"sass": "^1.49.7",
"sass-loader": "^12.4.0",
"simple-git": "^3.1.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^27.1.3",
"typescript": "^4.5.5",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"@types/argparse": "^1.0.38",
"@types/benchmark": "^2.1.0",
"@types/compression": "1.7.0",
"@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",
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.22",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.19.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"express": "^4.17.2",
"h264-mp4-encoder": "^1.0.12",
"immer": "^8.0.0",
"immutable": "^3.8.2",
"node-fetch": "^2.6.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rxjs": "^6.6.3",
"swagger-ui-dist": "^3.37.2",
"tslib": "^2.0.3",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.0"
"immer": "^9.0.12",
"immutable": "^4.0.0",
"node-fetch": "^2.6.7",
"rxjs": "^7.5.2",
"swagger-ui-dist": "^4.5.0",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}

41
scripts/clean.js Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Michal Malý <malym@ibt.cas.cz>
*/
const fs = require('fs');
const path = require('path');
function removeDir(dirPath) {
for (const ent of fs.readdirSync(dirPath)) {
const entryPath = path.join(dirPath, ent);
remove(entryPath);
}
fs.rmdirSync(dirPath);
}
function remove(entryPath) {
const st = fs.statSync(entryPath);
if (st.isDirectory())
removeDir(entryPath);
else
fs.unlinkSync(entryPath);
}
const toClean = [
path.resolve(__dirname, '../build'),
path.resolve(__dirname, '../lib'),
path.resolve(__dirname, '../tsconfig.tsbuildinfo'),
];
toClean.forEach(ph => {
if (fs.existsSync(ph)) {
try {
remove(ph);
} catch (err) {
console.warn(`Cleanup failed: ${err}`);
}
}
});

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -14,6 +14,9 @@ const buildDir = path.resolve(__dirname, '../build/');
const deployDir = path.resolve(buildDir, 'deploy/');
const localPath = path.resolve(deployDir, 'molstar.github.io/');
const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g;
const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics -->`;
function log(command, stdout, stderr) {
if (command) {
console.log('\n###', command);
@@ -22,11 +25,36 @@ function log(command, stdout, stderr) {
}
}
function addAnalytics(path) {
const data = fs.readFileSync(path, 'utf8');
const result = data.replace(analyticsTag, analyticsCode);
fs.writeFileSync(path, result, 'utf8');
}
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 });
addAnalytics(path.resolve(viewerDeployPath, 'index.html'));
}
function copyDemos() {
console.log('\n###', 'copy demos files');
const lightingBuildPath = path.resolve(buildDir, '../build/examples/lighting/');
const lightingDeployPath = path.resolve(localPath, 'demos/lighting/');
fse.copySync(lightingBuildPath, lightingDeployPath, { overwrite: true });
addAnalytics(path.resolve(lightingDeployPath, 'index.html'));
const orbitalsBuildPath = path.resolve(buildDir, '../build/examples/alpha-orbitals/');
const orbitalsDeployPath = path.resolve(localPath, 'demos/alpha-orbitals/');
fse.copySync(orbitalsBuildPath, orbitalsDeployPath, { overwrite: true });
addAnalytics(path.resolve(orbitalsDeployPath, 'index.html'));
}
function copyFiles() {
copyViewer();
copyDemos();
}
if (!fs.existsSync(localPath)) {
@@ -42,9 +70,9 @@ if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
.outputHandler(log)
.clone(remoteUrl, localPath)
.fetch(['--all'])
.exec(copyViewer)
.exec(copyFiles)
.add(['-A'])
.commit('updated viewer')
.commit('updated viewer & demos')
.push();
} else {
console.log('\n###', 'update repository');
@@ -52,8 +80,8 @@ if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
.outputHandler(log)
.fetch(['--all'])
.reset(['--hard', 'origin/master'])
.exec(copyViewer)
.exec(copyFiles)
.add(['-A'])
.commit('updated viewer')
.commit('updated viewer & demos')
.push();
}

View File

@@ -19,19 +19,19 @@
<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();
var pdbqt = getParam('pdbqt', '[^&]+').trim() || '../../examples/ace2.pdbqt';
var mol2 = getParam('mol2', '[^&]+').trim() || '../../examples/ace2-hit.mol2';
viewer.loadStructuresFromUrlsAndMerge([
{ url: pdbqt, format: 'pdbqt' },
{ url: mol2, format: 'mol2' }
]);
DockingViewer.create('app', [0x33DD22, 0x1133EE], true).then(viewer => {
viewer.loadStructuresFromUrlsAndMerge([
{ url: pdbqt, format: 'pdbqt' },
{ url: mol2, format: 'mol2' }
]);
});
</script>
</body>
</html>

View File

@@ -5,31 +5,32 @@
* @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 { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginBehaviors } from '../../mol-plugin/behavior';
import { ColorNames } from '../../mol-util/color/names';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginSpec } from '../../mol-plugin/spec';
import { StateObject } from '../../mol-state';
import { Task } from '../../mol-task';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import '../../mol-util/polyfill';
import { ObjectKeys } from '../../mol-util/type-helpers';
import './index.html';
import { ShowButtons, StructurePreset, ViewportComponent } from './viewport';
require('mol-plugin-ui/skin/light.scss');
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
export { Viewer as DockingViewer };
const DefaultViewerOptions = {
extensions: ObjectKeys({}),
@@ -53,9 +54,10 @@ const DefaultViewerOptions = {
};
class Viewer {
plugin: PluginContext
constructor(public plugin: PluginUIContext) {
}
constructor(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
static async create(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
const o = { ...DefaultViewerOptions, ...{
layoutIsExpanded: false,
layoutShowControls: false,
@@ -70,10 +72,10 @@ class Viewer {
viewportShowSelectionMode: false,
viewportShowAnimation: false,
} };
const defaultSpec = DefaultPluginSpec();
const defaultSpec = DefaultPluginUISpec();
const spec: PluginSpec = {
actions: [...defaultSpec.actions],
const spec: PluginUISpec = {
actions: defaultSpec.actions,
behaviors: [
PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci, { mark: false }),
PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider),
@@ -83,7 +85,7 @@ class Viewer {
PluginSpec.Behavior(PluginBehaviors.CustomProps.Interactions),
PluginSpec.Behavior(PluginBehaviors.CustomProps.SecondaryStructure),
],
animations: [...defaultSpec.animations || []],
animations: defaultSpec.animations,
customParamEditors: defaultSpec.customParamEditors,
layout: {
initial: {
@@ -91,15 +93,15 @@ class Viewer {
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,
controls: {
...defaultSpec.components?.controls,
top: o.layoutShowSequence ? undefined : 'none',
bottom: o.layoutShowLog ? undefined : 'none',
left: o.layoutShowLeftPanel ? undefined : 'none',
},
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
viewport: {
view: ViewportComponent
@@ -124,29 +126,31 @@ class Viewer {
? document.getElementById(elementOrId)
: elementOrId;
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
this.plugin = createPlugin(element, spec);
const plugin = await createPluginUI(element, spec);
(this.plugin.customState as any) = {
(plugin.customState as any) = {
colorPalette: {
name: 'colors',
params: { list: { colors } }
}
};
this.plugin.behaviors.canvas3d.initialized.subscribe(v => {
plugin.behaviors.canvas3d.initialized.subscribe(v => {
if (v) {
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
renderer: {
...this.plugin.canvas3d!.props.renderer,
...plugin.canvas3d!.props.renderer,
backgroundColor: ColorNames.white,
},
camera: {
...this.plugin.canvas3d!.props.camera,
...plugin.canvas3d!.props.camera,
helper: { axes: { name: 'off', params: {} } }
}
} });
}
});
return new Viewer(plugin);
}
async loadStructuresFromUrlsAndMerge(sources: { url: string, format: BuiltInTrajectoryFormat, isBinary?: boolean }[]) {
@@ -210,4 +214,3 @@ const MergeStructures = PluginStateTransform.BuiltIn({
});
(window as any).DockingViewer = Viewer;
export { Viewer as DockingViewer };

View File

@@ -4,30 +4,29 @@
* @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 { presetStaticComponent, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
import { Color } from '../../mol-util/color';
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import { LociLabels } from '../../mol-plugin-ui/controls';
import { Button } from '../../mol-plugin-ui/controls/common';
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
import { Toasts } from '../../mol-plugin-ui/toast';
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateObjectRef } from '../../mol-state';
import { Color } from '../../mol-util/color';
import { Material } from '../../mol-util/material';
function shinyStyle(plugin: PluginContext) {
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
renderer: {
...plugin.canvas3d!.props.renderer,
style: { name: 'plastic', params: {} },
},
postprocessing: {
...plugin.canvas3d!.props.postprocessing,
@@ -41,7 +40,6 @@ function occlusionStyle(plugin: PluginContext) {
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
renderer: {
...plugin.canvas3d!.props.renderer,
style: { name: 'flat', params: {} }
},
postprocessing: {
...plugin.canvas3d!.props.postprocessing,
@@ -53,7 +51,8 @@ function occlusionStyle(plugin: PluginContext) {
} },
outline: { name: 'on', params: {
scale: 1.0,
threshold: 0.33
threshold: 0.33,
color: Color(0x0000),
} }
}
} });
@@ -78,7 +77,7 @@ const PresetParams = {
...StructureRepresentationPresetProvider.CommonParams,
};
const CustomMaterial = Material({ roughness: 0.2, metalness: 0 });
export const StructurePreset = StructureRepresentationPresetProvider({
id: 'preset-structure',
@@ -95,8 +94,8 @@ export const StructurePreset = StructureRepresentationPresetProvider({
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' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, material: CustomMaterial }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
};
await update.commit({ revertOnError: true });
@@ -122,8 +121,8 @@ export const IllustrativePreset = StructureRepresentationPresetProvider({
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' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'spacefill', typeParams: { ...typeParams, ignoreLight: true }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'spacefill', typeParams: { ...typeParams, ignoreLight: true }, color: 'illustrative', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
};
await update.commit({ revertOnError: true });
@@ -150,8 +149,8 @@ const SurfacePreset = StructureRepresentationPresetProvider({
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' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, 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, material: CustomMaterial, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
};
await update.commit({ revertOnError: true });
@@ -178,8 +177,8 @@ const PocketPreset = StructureRepresentationPresetProvider({
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' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, 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, material: CustomMaterial, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
};
await update.commit({ revertOnError: true });
@@ -207,10 +206,10 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
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' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, 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, material: CustomMaterial, 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, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
};
await update.commit({ revertOnError: true });
@@ -231,7 +230,7 @@ export class ViewportComponent extends PluginUIComponent {
set = async (preset: StructureRepresentationPresetProvider) => {
await this._set(this.plugin.managers.structure.hierarchy.selection.structures, preset);
}
};
structurePreset = () => this.set(StructurePreset);
illustrativePreset = () => this.set(IllustrativePreset);
@@ -239,7 +238,7 @@ export class ViewportComponent extends PluginUIComponent {
pocketPreset = () => this.set(PocketPreset);
interactionsPreset = () => this.set(InteractionsPreset);
get showButtons () {
get showButtons() {
return this.plugin.config.get(ShowButtons);
}

491
src/apps/viewer/app.ts Normal file
View File

@@ -0,0 +1,491 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
import { CellPack } from '../../extensions/cellpack';
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
import { GeometryExport } from '../../extensions/geo-export';
import { MAQualityAssessment } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessmentPLDDTPreset, QualityAssessmentQmeanPreset } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessment } from '../../extensions/model-archive/quality-assessment/prop';
import { ModelExport } from '../../extensions/model-export';
import { Mp4Export } from '../../extensions/mp4-export';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { Volume } from '../../mol-model/volume';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginConfig } from '../../mol-plugin/config';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { PluginSpec } from '../../mol-plugin/spec';
import { PluginState } from '../../mol-plugin/state';
import { StateObjectRef, StateObjectSelector } from '../../mol-state';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import '../../mol-util/polyfill';
import { ObjectKeys } from '../../mol-util/type-helpers';
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setDebugMode, setProductionMode } from '../../mol-util/debug';
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),
'model-export': PluginSpec.Behavior(ModelExport),
'mp4-export': PluginSpec.Behavior(Mp4Export),
'geo-export': PluginSpec.Behavior(GeometryExport),
'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
};
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,
collapseLeftPanel: false,
collapseRightPanel: false,
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
pixelScale: PluginConfig.General.PixelScale.defaultValue,
pickScale: PluginConfig.General.PickScale.defaultValue,
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
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 {
constructor(public plugin: PluginUIContext) {
}
static async create(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const definedOptions = {} as any;
// filter for defined properies only so the default values
// are property applied
for (const p of Object.keys(options) as (keyof ViewerOptions)[]) {
if (options[p] !== void 0) definedOptions[p] = options[p];
}
const o: ViewerOptions = { ...DefaultViewerOptions, ...definedOptions };
const defaultSpec = DefaultPluginUISpec();
const spec: PluginUISpec = {
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,
regionState: {
bottom: 'full',
left: o.collapseLeftPanel ? 'collapsed' : 'full',
right: o.collapseRightPanel ? 'hidden' : 'full',
top: 'full',
}
},
},
components: {
...defaultSpec.components,
controls: {
...defaultSpec.components?.controls,
top: o.layoutShowSequence ? undefined : 'none',
bottom: o.layoutShowLog ? undefined : 'none',
left: o.layoutShowLeftPanel ? undefined : 'none',
},
remoteState: o.layoutShowRemoteState ? 'default' : 'none',
},
config: [
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
[PluginConfig.General.PixelScale, o.pixelScale],
[PluginConfig.General.PickScale, o.pickScale],
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.General.PreferWebGl1, o.preferWebgl1],
[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],
[PluginConfig.Structure.DefaultRepresentationPreset, ViewerAutoPreset.id],
]
};
const element = typeof elementOrId === 'string'
? document.getElementById(elementOrId)
: elementOrId;
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
const plugin = await createPluginUI(element, spec, {
onBeforeUIRender: plugin => {
// the preset needs to be added before the UI renders otherwise
// "Download Structure" wont be able to pick it up
plugin.builders.structure.representation.registerPreset(ViewerAutoPreset);
}
});
return new Viewer(plugin);
}
setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
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: 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,
}
}
}));
}
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,
}
}
}));
}
loadAlphaFoldDb(afdb: 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: 'alphafolddb' as const,
params: {
id: afdb,
options: {
...params.source.params.options,
representation: 'preset-structure-representation-ma-quality-assessment-plddt'
},
}
}
}));
}
loadModelArchive(id: 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: 'modelarchive' as const,
params: {
id,
options: params.source.params.options,
}
}
}));
}
/**
* @example Load X-ray density from volume server
viewer.loadVolumeFromUrl({
url: 'https://www.ebi.ac.uk/pdbe/densities/x-ray/1tqn/cell?detail=3',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1.5,
color: 0x3362B2
}, {
type: 'relative',
value: 3,
color: 0x33BB33,
volumeIndex: 1
}, {
type: 'relative',
value: -3,
color: 0xBB3333,
volumeIndex: 1
}], {
entryId: ['2FO-FC', 'FO-FC'],
isLazy: true
});
* *********************
* @example Load EM density from volume server
viewer.loadVolumeFromUrl({
url: 'https://maps.rcsb.org/em/emd-30210/cell?detail=6',
format: 'dscif',
isBinary: true
}, [{
type: 'relative',
value: 1,
color: 0x3377aa
}], {
entryId: 'EMD-30210',
isLazy: true
});
*/
async loadVolumeFromUrl({ url, format, isBinary }: { url: string, format: BuildInVolumeFormat, isBinary: boolean }, isovalues: VolumeIsovalueInfo[], options?: { entryId?: string | string[], isLazy?: boolean }) {
const plugin = this.plugin;
if (!plugin.dataFormats.get(format)) {
throw new Error(`Unknown density format: ${format}`);
}
if (options?.isLazy) {
const update = this.plugin.build();
update.toRoot().apply(StateTransforms.Data.LazyVolume, {
url,
format,
entryId: options?.entryId,
isBinary,
isovalues: isovalues.map(v => ({ alpha: 1, volumeIndex: 0, ...v }))
});
return update.commit();
}
return plugin.dataTransaction(async () => {
const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId: options?.entryId });
const firstVolume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
if (!firstVolume?.isOk) throw new Error('Failed to parse any volume.');
const repr = plugin.build();
for (const iso of isovalues) {
const volume: StateObjectSelector<PluginStateObject.Volume.Data> = parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume;
const volumeData = volume.cell!.obj!.data;
repr
.to(volume)
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
type: 'isosurface',
typeParams: { alpha: iso.alpha ?? 1, isoValue: Volume.adjustedIsoValue(volumeData, iso.value, iso.type) },
color: 'uniform',
colorParams: { value: iso.color }
}));
}
await repr.commit();
});
}
/**
* @example
* viewer.loadTrajectory({
* model: { kind: 'model-url', url: 'villin.gro', format: 'gro' },
* coordinates: { kind: 'coordinates-url', url: 'villin.xtc', format: 'xtc', isBinary: true },
* preset: 'all-models' // or 'default'
* });
*/
async loadTrajectory(params: LoadTrajectoryParams) {
const plugin = this.plugin;
let model: StateObjectSelector, coords: StateObjectSelector;
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
const data = params.model.kind === 'model-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const trajectory = await plugin.builders.structure.parseTrajectory(data, params.model.format ?? 'mmcif');
model = await plugin.builders.structure.createModel(trajectory);
} else {
const data = params.model.kind === 'topology-data'
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
const provider = plugin.dataFormats.get(params.model.format);
model = await provider!.parse(plugin, data);
}
{
const data = params.coordinates.kind === 'coordinates-data'
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
const provider = plugin.dataFormats.get(params.coordinates.format);
coords = await provider!.parse(plugin, data);
}
const trajectory = await plugin.build().toRoot()
.apply(TrajectoryFromModelAndCoordinates, {
modelRef: model.ref,
coordinatesRef: coords.ref
}, { dependsOn: [model.ref, coords.ref] })
.commit();
const preset = await plugin.builders.structure.hierarchy.applyPreset(trajectory, params.preset ?? 'default');
return { model, coords, preset };
}
handleResize() {
this.plugin.layout.events.updated.next(void 0);
}
}
export interface LoadStructureOptions {
representationParams?: StructureRepresentationPresetProvider.CommonParams
}
export interface VolumeIsovalueInfo {
type: 'absolute' | 'relative',
value: number,
color: Color,
alpha?: number,
volumeIndex?: number
}
export interface LoadTrajectoryParams {
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
modelLabel?: string,
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
coordinatesLabel?: string,
preset?: keyof PresetTrajectoryHierarchy
}
export const ViewerAutoPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-viewer-auto',
display: {
name: 'Automatic (w/ Annotation)', group: 'Annotation',
description: 'Show standard automatic representation but colored by quality assessment (if available in the model).'
},
isApplicable(a) {
return (
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT')) ||
!!a.data.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))
);
},
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 (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT'))) {
return await QualityAssessmentPLDDTPreset.apply(ref, params, plugin);
} else if (!!structure.models.some(m => QualityAssessment.isApplicable(m, 'qmean'))) {
return await QualityAssessmentQmeanPreset.apply(ref, params, plugin);
} else {
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
}
}
});

View File

@@ -20,8 +20,8 @@
<div id="app"></div>
<script type="text/javascript" src="./molstar.js"></script>
<script type="text/javascript">
var viewer = new molstar.Viewer('app', {
layoutIsExpanded: false,
molstar.Viewer.create('app', {
layoutIsExpanded: true,
layoutShowControls: false,
layoutShowRemoteState: false,
layoutShowSequence: true,
@@ -34,10 +34,11 @@
pdbProvider: 'rcsb',
emdbProvider: 'rcsb',
}).then(viewer => {
viewer.loadPdb('7bv2');
viewer.loadEmdb('EMD-30210', { detail: 6 });
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
});
viewer.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>

View File

@@ -48,42 +48,60 @@
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 collapseLeftPanel = getParam('collapse-left-panel', '[^&]+').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,
var mapProvider = getParam('map-provider', '[^&]+').trim().toLowerCase();
var pixelScale = getParam('pixel-scale', '[^&]+').trim();
var pickScale = getParam('pick-scale', '[^&]+').trim();
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
viewportShowExpand: false,
collapseLeftPanel: collapseLeftPanel,
pdbProvider: pdbProvider || 'pdbe',
emdbProvider: emdbProvider || 'pdbe',
volumeStreamingServer: (mapProvider || 'pdbe') === 'rcsb'
? 'https://maps.rcsb.org'
: 'https://www.ebi.ac.uk/pdbe/densities',
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: disableWboit ? true : void 0, // use default value if disable-wboit is not set
preferWebgl1: preferWebgl1,
}).then(viewer => {
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);
var afdb = getParam('afdb', '[^&]+').trim();
if (afdb) viewer.loadAlphaFoldDb(afdb);
var modelArchive = getParam('model-archive', '[^&]+').trim();
if (modelArchive) viewer.loadModelArchive(modelArchive);
});
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>
<!-- __MOLSTAR_ANALYTICS__ -->
</body>
</html>

View File

@@ -1,279 +1,12 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author 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 './embedded.html';
import './favicon.ico';
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginSpec } from '../../mol-plugin/spec';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { PluginConfig } from '../../mol-plugin/config';
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';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
export { setProductionMode, setDebugMode } from '../../mol-util/debug';
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',
}
},
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]
]
};
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);
}
setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
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: 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,
}
}
}));
}
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();
}
}
export interface LoadStructureOptions {
representationParams?: StructureRepresentationPresetProvider.CommonParams
}
export interface VolumeIsovalueInfo {
type: 'absolute' | 'relative',
value: number,
color: Color,
alpha?: number
}
export * from './app';

View File

@@ -19,7 +19,7 @@ import { ensureDataAvailable, readCCD } from './util';
function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
const ionNames: string[] = [];
for (const k in ccd) {
const {chem_comp} = ccd[k];
const { chem_comp } = ccd[k];
if (chem_comp.name.value(0).toUpperCase().includes(' ION')) {
ionNames.push(chem_comp.id.value(0));
}
@@ -54,20 +54,20 @@ async function run(out: string, forceDownload = false) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Extract and save IonNames from CCD.'
});
parser.addArgument('out', {
parser.add_argument('out', {
help: 'Generated file output path.'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
interface Args {
out: string,
forceDownload?: boolean,
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(args.out, args.forceDownload);

View File

@@ -171,7 +171,7 @@ async function createBonds(
pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
});
const bondDatabase = Database.ofTables(
const bondDatabase = Database.ofTables(
CCB_TABLE_NAME,
{ chem_comp_bond: mmCIF_chemCompBond_schema },
{ chem_comp_bond: bondTable }
@@ -265,21 +265,21 @@ const CCB_TABLE_NAME = 'CHEM_COMP_BONDS';
const CCA_TABLE_NAME = 'CHEM_COMP_ATOMS';
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
});
parser.addArgument('out', {
parser.add_argument('out', {
help: 'Generated file output path.'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
parser.addArgument([ '--binary', '-b' ], {
action: 'storeTrue',
parser.add_argument('--binary', '-b', {
action: 'store_true',
help: 'Output as BinaryCIF.'
});
parser.addArgument(['--ccaOut', '-a'], {
parser.add_argument('--ccaOut', '-a', {
help: 'Optional generated file output path for chem_comp_atom data.',
required: false
});
@@ -289,6 +289,6 @@ interface Args {
binary?: boolean,
ccaOut?: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(args.out, args.binary, args.forceDownload, args.ccaOut);

View File

@@ -37,20 +37,20 @@ function run(args: Args) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Convert any CIF file to a BCIF file'
});
parser.addArgument([ 'src' ], {
parser.add_argument('src', {
help: 'Source CIF path'
});
parser.addArgument([ 'out' ], {
parser.add_argument('out', {
help: 'Output BCIF path'
});
parser.addArgument([ '-c', '--config' ], {
parser.add_argument('-c', '--config', {
help: 'Optional encoding strategy/precision config path',
required: false
});
parser.addArgument([ '-f', '--filter' ], {
parser.add_argument('-f', '--filter', {
help: 'Optional filter whitelist/blacklist path',
required: false
});
@@ -61,7 +61,7 @@ interface Args {
config?: string
filter?: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
if (args) {
run(args);

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -35,20 +35,16 @@ async function runGenerateSchemaMmcif(name: string, fieldNamesPath: string, type
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;
await ensureMaDicAvailable();
const maDic = await parseCifText(fs.readFileSync(MA_DIC_PATH, 'utf8')).run();
if (maDic.isError) throw maDic;
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 maDicVersion = getDicVersion(maDic.result.blocks[0]);
const version = `Dictionary versions: mmCIF ${mmcifDicVersion}, IHM ${ihmDicVersion}, MA ${maDicVersion}.`;
const frames: CifFrame[] = [...mmcifDic.result.blocks[0].saveFrames, ...ihmDic.result.blocks[0].saveFrames, ...carbBranchDic.result.blocks[0].saveFrames, ...carbCompDic.result.blocks[0].saveFrames];
const frames: CifFrame[] = [...mmcifDic.result.blocks[0].saveFrames, ...ihmDic.result.blocks[0].saveFrames, ...maDic.result.blocks[0].saveFrames];
const schema = generateSchema(frames);
await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases);
@@ -124,23 +120,22 @@ async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
const csvFile = parsed.result;
const fieldNamesCol = csvFile.table.getColumn('0');
if (!fieldNamesCol) throw 'error getting fields columns';
if (!fieldNamesCol) throw new Error('error getting fields columns');
const fieldNames = fieldNamesCol.toStringArray();
const filter: Filter = {};
fieldNames.forEach((name, i) => {
const [ category, field ] = name.split('.');
const [category, field] = name.split('.');
// console.log(category, field)
if (!filter[ category ]) filter[ category ] = {};
filter[ category ][ field ] = true;
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 ensureMaDicAvailable() { await ensureDicAvailable(MA_DIC_PATH, MA_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);
@@ -165,10 +160,8 @@ 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 MA_DIC_PATH = `${DIC_DIR}/ma-extension.dic`;
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/MA-dictionary/master/mmcif_ma.dic';
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';
@@ -178,44 +171,44 @@ 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,
add_help: true,
description: 'Create schema from mmcif dictionary (v50 plus IHM and entity_branch extensions, downloaded from wwPDB)'
});
parser.addArgument([ '--preset', '-p' ], {
defaultValue: '',
parser.add_argument('--preset', '-p', {
default: '',
choices: ['', 'mmCIF', 'CCD', 'BIRD', 'CifCore'],
help: 'Preset name'
});
parser.addArgument([ '--name', '-n' ], {
defaultValue: '',
parser.add_argument('--name', '-n', {
default: '',
help: 'Schema name'
});
parser.addArgument([ '--out', '-o' ], {
parser.add_argument('--out', '-o', {
help: 'Generated schema output path, if not given printed to stdout'
});
parser.addArgument([ '--targetFormat', '-tf' ], {
defaultValue: 'typescript-molstar',
parser.add_argument('--targetFormat', '-tf', {
default: 'typescript-molstar',
choices: ['typescript-molstar', 'json-internal'],
help: 'Target format'
});
parser.addArgument([ '--dicPath', '-d' ], {
defaultValue: '',
parser.add_argument('--dicPath', '-d', {
default: '',
help: 'Path to dictionary'
});
parser.addArgument([ '--fieldNamesPath', '-fn' ], {
defaultValue: '',
parser.add_argument('--fieldNamesPath', '-fn', {
default: '',
help: 'Field names to include'
});
parser.addArgument([ '--forceDicDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDicDownload', '-f', {
action: 'store_true',
help: 'Force download of dictionaries'
});
parser.addArgument([ '--moldataImportPath', '-mip' ], {
defaultValue: 'molstar/lib/mol-data',
parser.add_argument('--moldataImportPath', '-mip', {
default: 'molstar/lib/mol-data',
help: 'mol-data import path (for typescript target only)'
});
parser.addArgument([ '--addAliases', '-aa' ], {
action: 'storeTrue',
parser.add_argument('--addAliases', '-aa', {
action: 'store_true',
help: 'Add field name/path aliases'
});
interface Args {
@@ -230,7 +223,7 @@ interface Args {
moldataImportPath: string
addAliases: boolean
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
const FORCE_DIC_DOWNLOAD = args.forceDicDownload;

View File

@@ -34,6 +34,8 @@ export function getFieldType(type: string, description: string, values?: string[
case 'seq-one-letter-code':
case 'author':
case 'orcid_id':
case 'pdbx_PDB_obsoleted_db_id':
case 'pdbx_related_db_id':
case 'sequence_dep':
case 'pdb_id':
case 'emd_id':
@@ -79,9 +81,10 @@ export function getFieldType(type: string, description: string, values?: string[
case 'List(Real,Real)':
case 'List(Real,Real,Real,Real)':
case 'Date':
case 'Datetime':
case 'DateTime':
case 'Tag':
case 'Implied':
case 'Word':
return wrapContainer('str', ',', description, container);
case 'Real':
return wrapContainer('float', ',', description, container);
@@ -187,7 +190,7 @@ function getContainer(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
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) ];
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}'`);
}
@@ -232,29 +235,26 @@ const FORCE_INT_FIELDS = [
'_struct_sheet_range.end_auth_seq_id',
];
/**
* Note that name and mapped name must share a prefix. This is not always the case in
* the cifCore dictionary, but for downstream code to work a container field with the
* same prefix as the member fields must be given here and in the field names filter
* list.
*/
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',
'atom_site_aniso.u_11': 'u', // is matrix_u in the the dic
'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',
};
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',
'atom_site_aniso.matrix_u': [
'atom_site_anisotrop_U',
'atom_site_aniso.U'
],
};
@@ -317,7 +317,7 @@ export function generateSchema(frames: CifFrame[], imports: Imports = new Map())
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;
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;
@@ -372,7 +372,7 @@ export function generateSchema(frames: CifFrame[], imports: Imports = new Map())
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 childName: string = 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}`);

View File

@@ -8,7 +8,7 @@ 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) {
function header(name: string, info: string, moldataImportPath: string) {
return `/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
@@ -22,7 +22,7 @@ import { Database, Column } from '${moldataImportPath}/db';
import Schema = Column.Schema;`;
}
function footer (name: string) {
function footer(name: string) {
return `
export type ${name}_Schema = typeof ${name}_Schema;
export interface ${name}_Database extends Database<${name}_Schema> {};`;
@@ -89,7 +89,7 @@ function doc(description: string, spacesCount: number) {
].join('\n');
}
export function generate (name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
export function generate(name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
const codeLines: string[] = [];
if (fields) {
@@ -128,7 +128,7 @@ export function generate (name: string, info: string, schema: Database, fields:
codeLines.push('');
codeLines.push(`export const ${name}_Aliases = {`);
Object.keys(schema.aliases).forEach(path => {
const [ table, columnName ] = path.split('.');
const [table, columnName] = path.split('.');
if (fields && !fields[table]) return;
if (fields && !fields[table][columnName]) return;

View File

@@ -10,8 +10,8 @@ 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]+)/);
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

View File

@@ -51,13 +51,13 @@ export function ListCol(subType: 'int' | 'str' | 'float' | 'coord', separator: s
export type Filter = { [ table: string ]: { [ column: string ]: true } }
export function mergeFilters (...filters: Filter[]) {
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 => {
Object.keys(filter[category]).forEach(field => {
const key = `${category}.${field}`;
const value = fields.get(key) || 0;
fields.set(key, value + 1);

View File

@@ -70,21 +70,21 @@ export const LipidNames = new Set(${lipidNames.replace(/"/g, "'").replace(/,/g,
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Create lipid params (from martini lipids itp)'
});
parser.addArgument([ '--out', '-o' ], {
parser.add_argument('--out', '-o', {
help: 'Generated lipid params output path, if not given printed to stdout'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of martini lipids itp'
});
interface Args {
out: string
forceDownload: boolean
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
const FORCE_DOWNLOAD = args.forceDownload;

View File

@@ -18,7 +18,6 @@ _.StateTransforms.Data.Download.id;
// Empty plugin context
const ctx = new PluginContext({
actions: [],
behaviors: []
});

View File

@@ -63,7 +63,7 @@ export function printSecStructure(model: Model) {
const count = residues._rowCount;
let rI = 0;
while (rI < count) {
let start = rI;
const start = rI;
while (rI < count && key[start] === key[rI]) rI++;
rI--;
@@ -230,21 +230,21 @@ async function runFile(filename: string, args: Args) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Print info about a structure, mainly to test and showcase the mol-model module'
});
parser.addArgument(['--download', '-d'], { help: 'Pdb entry id' });
parser.addArgument(['--file', '-f'], { help: 'filename' });
parser.add_argument('--download', '-d', { help: 'Pdb entry id' });
parser.add_argument('--file', '-f', { help: 'filename' });
parser.addArgument(['--models'], { help: 'print models info', action: 'storeTrue' });
parser.addArgument(['--seq'], { help: 'print sequence', action: 'storeTrue' });
parser.addArgument(['--units'], { help: 'print units', action: 'storeTrue' });
parser.addArgument(['--sym'], { help: 'print symmetry', action: 'storeTrue' });
parser.addArgument(['--rings'], { help: 'print rings', action: 'storeTrue' });
parser.addArgument(['--intraBonds'], { help: 'print intra unit bonds', action: 'storeTrue' });
parser.addArgument(['--interBonds'], { help: 'print inter unit bonds', action: 'storeTrue' });
parser.addArgument(['--mod'], { help: 'print modified residues', action: 'storeTrue' });
parser.addArgument(['--sec'], { help: 'print secoundary structure', action: 'storeTrue' });
parser.add_argument('--models', { help: 'print models info', action: 'store_true' });
parser.add_argument('--seq', { help: 'print sequence', action: 'store_true' });
parser.add_argument('--units', { help: 'print units', action: 'store_true' });
parser.add_argument('--sym', { help: 'print symmetry', action: 'store_true' });
parser.add_argument('--rings', { help: 'print rings', action: 'store_true' });
parser.add_argument('--intraBonds', { help: 'print intra unit bonds', action: 'store_true' });
parser.add_argument('--interBonds', { help: 'print inter unit bonds', action: 'store_true' });
parser.add_argument('--mod', { help: 'print modified residues', action: 'store_true' });
parser.add_argument('--sec', { help: 'print secoundary structure', action: 'store_true' });
interface Args {
download?: string,
file?: string,
@@ -260,7 +260,7 @@ interface Args {
mod?: boolean,
sec?: boolean,
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
if (args.download) runDL(args.download, args);
else if (args.file) runFile(args.file, args);

View File

@@ -38,7 +38,7 @@ function print(volume: Volume) {
}
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();
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.
@@ -75,13 +75,13 @@ async function run(url: string, meshFilename: string) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Info about VolumeData from mol-model module'
});
parser.addArgument([ '--emdb', '-e' ], {
parser.add_argument('--emdb', '-e', {
help: 'EMDB id, for example 8116',
});
parser.addArgument([ '--mesh' ], {
parser.add_argument('--mesh', {
help: 'Mesh filename',
required: true
});
@@ -89,6 +89,6 @@ interface Args {
emdb?: string,
mesh: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(`https://ds.litemol.org/em/emd-${args.emdb}/cell?detail=4`, args.mesh);

View File

@@ -4,7 +4,6 @@
* @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';

View File

@@ -22,7 +22,23 @@
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>
@@ -30,8 +46,17 @@
<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>
<!-- __MOLSTAR_ANALYTICS__ -->
</body>
</html>

View File

@@ -4,25 +4,25 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { BehaviorSubject } from 'rxjs';
import { debounceTime, skip } from 'rxjs/operators';
import { AlphaOrbital, Basis } from '../../extensions/alpha-orbitals/data-model';
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 { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
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 {
@@ -50,18 +50,26 @@ type Selectors = {
}
export class AlphaOrbitalsExample {
plugin: PluginContext;
plugin: PluginUIContext;
async init(target: string | HTMLElement) {
this.plugin = await createPluginAsync(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec(),
const defaultSpec = DefaultPluginUISpec();
this.plugin = await createPluginUI(typeof target === 'string' ? document.getElementById(target)! : target, {
...defaultSpec,
layout: {
initial: {
isExpanded: false,
showControls: false
},
},
components: {
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' },
},
canvas3d: {
camera: {
helper: { axes: { name: 'off', params: {} } }
}
},
config: [
[PluginConfig.Viewport.ShowExpand, false],
[PluginConfig.Viewport.ShowControls, false],
@@ -89,7 +97,7 @@ export class AlphaOrbitalsExample {
}
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);
readonly state = new BehaviorSubject<Params>({ show: { name: 'orbital', params: { index: 32 } }, isoValue: 1, gpuSurface: false });
readonly state = new BehaviorSubject<Params>({ show: { name: 'orbital', params: { index: 32 } }, isoValue: 1, gpuSurface: true });
private selectors?: Selectors = void 0;
private basis?: StateObjectSelector<BasisAndOrbitals> = void 0;
@@ -162,11 +170,11 @@ export class AlphaOrbitalsExample {
return {
alpha: 0.85,
color,
directVolume: this.state.value.gpuSurface,
kind,
relativeIsovalue: this.state.value.isoValue,
pickable: false,
xrayShaded: true
xrayShaded: true,
tryUseGpu: true
};
}

View File

@@ -5,7 +5,6 @@
*/
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import * as React from 'react';
export class CustomToastMessage extends PluginUIComponent {
render() {

View File

@@ -0,0 +1,51 @@
import { isPositionLocation } from '../../mol-geo/util/location-iterator';
import { Vec3 } from '../../mol-math/linear-algebra';
import { ColorTheme } from '../../mol-theme/color';
import { ThemeDataContext } from '../../mol-theme/theme';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/names';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
export function CustomColorTheme(
ctx: ThemeDataContext,
props: PD.Values<{}>
): ColorTheme<{}> {
const { radius, center } = ctx.structure?.boundary.sphere!;
const radiusSq = Math.max(radius * radius, 0.001);
const scale = ColorTheme.PaletteScale;
return {
factory: CustomColorTheme,
granularity: 'vertex',
color: location => {
if (!isPositionLocation(location)) return ColorNames.black;
const dist = Vec3.squaredDistance(location.position, center);
const t = Math.min(dist / radiusSq, 1);
return ((t * scale) | 0) as Color;
},
palette: {
filter: 'nearest',
colors: [
ColorNames.red,
ColorNames.pink,
ColorNames.violet,
ColorNames.orange,
ColorNames.yellow,
ColorNames.green,
ColorNames.blue
]
},
props: props,
description: '',
};
}
export const CustomColorThemeProvider: ColorTheme.Provider<{}, 'basic-wrapper-custom-color-theme'> = {
name: 'basic-wrapper-custom-color-theme',
label: 'Custom Color Theme',
category: ColorTheme.Category.Misc,
factory: CustomColorTheme,
getParams: () => ({}),
defaultValues: { },
isApplicable: (ctx: ThemeDataContext) => true,
};

View File

@@ -69,8 +69,9 @@
$('format').value = format;
$('format').onchange = function (e) { format = e.target.value; }
BasicMolStarWrapper.init('app' /** or document.getElementById('app') */);
BasicMolStarWrapper.setBackground(0xffffff);
BasicMolStarWrapper.init('app' /** or document.getElementById('app') */).then(() => {
BasicMolStarWrapper.setBackground(0xffffff);
});
addControl('Load Asym Unit', () => BasicMolStarWrapper.load({ url: url, format: format }));
addControl('Load Assembly', () => BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }));
@@ -97,6 +98,7 @@
addHeader('Misc');
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
addControl('Apply Custom Theme', () => BasicMolStarWrapper.coloring.applyCustomTheme());
addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
addHeader('Interactivity');

View File

@@ -4,39 +4,37 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
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 { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import { Script } from '../../mol-script/script';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { StripedResidues } from './coloring';
import { CustomToastMessage } from './controls';
import { CustomColorThemeProvider } from './custom-theme';
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;
plugin: PluginUIContext;
init(target: string | HTMLElement) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec(),
async init(target: string | HTMLElement) {
this.plugin = await createPluginUI(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginUISpec(),
layout: {
initial: {
isExpanded: false,
showControls: false
},
controls: {
// left: 'none'
}
},
components: {
@@ -45,6 +43,7 @@ class BasicWrapper {
});
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
this.plugin.representation.structure.themes.colorThemeRegistry.add(CustomColorThemeProvider);
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
@@ -61,7 +60,7 @@ class BasicWrapper {
params: { id: assemblyId }
} : {
name: 'model',
params: { }
params: {}
},
showUnitcell: false,
representationPreset: 'auto'
@@ -75,12 +74,20 @@ class BasicWrapper {
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: props => {
props.trackball.spin = !props.trackball.spin;
settings: {
trackball: {
...trackball,
animate: trackball.animate.name === 'spin'
? { name: 'off', params: {} }
: { name: 'spin', params: { speed: 1 } }
}
}
});
if (!this.plugin.canvas3d.props.trackball.spin) PluginCommands.Camera.Reset(this.plugin, {});
if (this.plugin.canvas3d.props.trackball.animate.name !== 'spin') {
PluginCommands.Camera.Reset(this.plugin, {});
}
}
private animateModelIndexTargetFps() {
@@ -93,10 +100,10 @@ class BasicWrapper {
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: {} } }); },
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 () => {
@@ -106,6 +113,13 @@ class BasicWrapper {
}
});
},
applyCustomTheme: 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: CustomColorThemeProvider.name as any });
}
});
},
applyDefault: async () => {
this.plugin.dataTransaction(async () => {
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
@@ -113,7 +127,7 @@ class BasicWrapper {
}
});
}
}
};
interactivity = {
highlightOn: () => {
@@ -131,7 +145,7 @@ class BasicWrapper {
clearHighlight: () => {
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
};
tests = {
staticSuperposition: async () => {
@@ -162,7 +176,7 @@ class BasicWrapper {
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
}
}
};
}
(window as any).BasicMolStarWrapper = new BasicWrapper();

View File

@@ -38,7 +38,7 @@ type MappingRow = Table.Row<S.mapping>;
function writeDomain(enc: CifWriter.Encoder, domain: DomainAnnotation | undefined) {
if (!domain) return;
enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () => CifWriter.Category.ofTable(domain.domains) });
enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () => CifWriter.Category.ofTable(domain.domains) });
enc.writeCategory({ name: `pdbx_${domain.name}_domain_mapping`, instance: () => CifWriter.Category.ofTable(domain.mappings) });
}

View File

@@ -15,7 +15,7 @@ async function getMappings(id: string) {
};
let PORT = process.env.port || 1338;
const PORT = process.env.port || 1338;
const app = express();

View File

@@ -45,8 +45,9 @@
<div id='controls'></div>
<div id="app"></div>
<script>
LightingDemo.init('app')
LightingDemo.load({ url: 'https://models.rcsb.org/4KTC.bcif', assemblyId: '1' }, 5, 1.3)
LightingDemo.init('app').then(() => {
LightingDemo.load({ url: 'https://models.rcsb.org/4KTC.bcif', assemblyId: '1' }, 5, 1.3);
});
addHeader('Example PDB IDs');
addControl('4KTC', () => LightingDemo.load({ url: 'https://models.rcsb.org/4KTC.bcif', assemblyId: '1' }, 5, 1.3));
@@ -83,5 +84,6 @@
$('controls').appendChild(h);
}
</script>
<!-- __MOLSTAR_ANALYTICS__ -->
</body>
</html>

View File

@@ -1,59 +1,62 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 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 { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { PluginContext } from '../../mol-plugin/context';
import './index.html';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import './index.html';
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 = Pick<Canvas3DProps, '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: {} }
illustrative: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } }
},
renderer: {
ambientIntensity: 1.0,
light: []
}
}
},
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: {} }
occlusion: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
outline: { name: 'off', params: {} }
},
renderer: {
ambientIntensity: 0.4,
light: [{ inclination: 180, azimuth: 0, color: Color.fromNormalizedRgb(1.0, 1.0, 1.0),
intensity: 0.6 }]
}
}
},
standard: <Preset> {
multiSample: {
mode: 'off' as Canvas3DProps['multiSample']['mode']
},
postprocessing: {
occlusion: { name: 'off', params: { } },
outline: { name: 'off', params: { } }
},
renderer: {
style: { name: 'matte', params: {} }
standard: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'off', params: {} },
outline: { name: 'off', params: {} }
},
renderer: {
ambientIntensity: 0.4,
light: [{ inclination: 180, azimuth: 0, color: Color.fromNormalizedRgb(1.0, 1.0, 1.0),
intensity: 0.6 }]
}
}
}
};
@@ -62,20 +65,22 @@ const Canvas3DPresets = {
type Canvas3DPreset = keyof typeof Canvas3DPresets
class LightingDemo {
plugin: PluginContext;
plugin: PluginUIContext;
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(),
async init(target: string | HTMLElement) {
this.plugin = await createPluginUI(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginUISpec(),
layout: {
initial: {
isExpanded: false,
showControls: false
},
},
components: {
controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
}
});
@@ -85,25 +90,23 @@ class LightingDemo {
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;
if (props.canvas3d.postprocessing.occlusion?.name === 'on') {
props.canvas3d.postprocessing.occlusion.params.radius = this.radius;
props.canvas3d.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
},
}});
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: {
...props,
renderer: {
...this.plugin.canvas3d!.props.renderer,
...props.canvas3d.renderer
},
postprocessing: {
...this.plugin.canvas3d!.props.postprocessing,
...props.canvas3d.postprocessing
},
}
});
}
async load({ url, format = 'mmcif', isBinary = true, assemblyId = '' }: LoadParams, radius: number, bias: number) {
@@ -112,7 +115,7 @@ class LightingDemo {
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 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' });

View File

@@ -103,10 +103,11 @@
PluginWrapper.init('app' /** or document.getElementById('app') */, {
customColorList: CustomColors
}).then(() => {
PluginWrapper.setBackground(0xffffff);
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.toggleSpin();
});
PluginWrapper.setBackground(0xffffff);
loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
PluginWrapper.toggleSpin();
PluginWrapper.events.modelInfo.subscribe(function (info) {
console.log('Model Info', info);

View File

@@ -6,15 +6,15 @@
import * as ReactDOM from 'react-dom';
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 { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { createPluginUI } from '../../mol-plugin-ui';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
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';
@@ -41,13 +41,13 @@ class MolStarProteopediaWrapper {
modelInfo: this._ev<ModelInfo>()
};
plugin: PluginContext;
plugin: PluginUIContext;
init(target: string | HTMLElement, options?: {
async init(target: string | HTMLElement, options?: {
customColorList?: number[]
}) {
this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginSpec(),
this.plugin = await createPluginUI(typeof target === 'string' ? document.getElementById(target)! : target, {
...DefaultPluginUISpec(),
animations: [
AnimateModelIndex
],
@@ -95,7 +95,7 @@ class MolStarProteopediaWrapper {
params: { id: assemblyId }
} : {
name: 'model' as const,
params: { }
params: {}
}
};
@@ -113,7 +113,7 @@ class MolStarProteopediaWrapper {
const structure = this.getObj<PluginStateObject.Molecule.Structure>(StateElements.Assembly);
if (!structure) return;
const style = _style || { };
const style = _style || {};
const update = this.state.build();
@@ -229,7 +229,7 @@ class MolStarProteopediaWrapper {
params: { id: asmId }
} : {
name: 'model' as const,
params: { }
params: {}
}
};
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
@@ -250,13 +250,22 @@ class MolStarProteopediaWrapper {
setBackground(color: number) {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: {
trackball: {
...trackball,
animate: trackball.animate.name === 'spin'
? { name: 'off', params: {} }
: { name: 'spin', params: { speed: 1 } }
}
}
});
}
viewport = {
@@ -269,8 +278,8 @@ class MolStarProteopediaWrapper {
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { })
}
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, {})
};
private animateModelIndexTargetFps() {
return Math.max(1, this.animate.modelIndex.targetFps | 0);
@@ -282,10 +291,10 @@ class MolStarProteopediaWrapper {
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: {} } }); },
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 = {
evolutionaryConservation: async (params?: { sequence?: boolean, het?: boolean, keepStyle?: boolean }) => {
@@ -306,7 +315,7 @@ class MolStarProteopediaWrapper {
await PluginCommands.State.Update(this.plugin, { state, tree });
}
}
};
private experimentalDataElement?: Element = void 0;
experimentalData = {
@@ -330,17 +339,17 @@ class MolStarProteopediaWrapper {
this.experimentalDataElement = void 0;
}
}
}
};
hetGroups = {
reset: () => {
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset(this.plugin, { });
PluginCommands.Camera.Reset(this.plugin, {});
},
focusFirst: async (compId: string, options?: { hideLabels: boolean, doNotLabelWaters: boolean }) => {
if (!this.state.transforms.has(StateElements.Assembly)) return;
await PluginCommands.Camera.Reset(this.plugin, { });
await PluginCommands.Camera.Reset(this.plugin, {});
const update = this.state.build();
@@ -397,7 +406,7 @@ class MolStarProteopediaWrapper {
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius);
PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: 250 });
}
}
};
snapshot = {
get: (params?: PluginState.SnapshotParams) => {
@@ -420,7 +429,7 @@ class MolStarProteopediaWrapper {
}
}
}
};
}
(window as any).MolStarProteopediaWrapper = MolStarProteopediaWrapper;

View File

@@ -4,14 +4,13 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PluginUIContext } from '../../../mol-plugin-ui/context';
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
import { PluginContext } from '../../../mol-plugin/context';
import { StateElements } from '../helpers';
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
export function volumeStreamingControls(plugin: PluginUIContext, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={plugin}>
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
</PluginContextContainer>, parent);

View File

@@ -9,6 +9,7 @@ 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';
import { ModelFormat } from '../../mol-model-formats/format';
// Note: generally contracted gaussians are currently not supported.
export interface SphericalElectronShell {
@@ -59,6 +60,17 @@ export interface CubeGrid {
isovalues?: { negative?: number; positive?: number };
}
export type CubeGridFormat = ModelFormat<CubeGrid>;
// eslint-disable-next-line
export function CubeGridFormat(grid: CubeGrid): CubeGridFormat {
return { name: 'custom grid', kind: 'cube-grid', data: grid };
}
export function isCubeGridData(f: ModelFormat): f is CubeGridFormat {
return f.kind === 'cube-grid';
}
export function initCubeGrid(params: CubeGridComputationParams): CubeGridInfo {
const geometry = params.basis.atoms.map(a => a.center);
const { gridSpacing: spacing, boxExpand: expand } = params;

View File

@@ -17,7 +17,7 @@ import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers
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 { AlphaOrbital, Basis, CubeGrid, CubeGridFormat, isCubeGridData } from './data-model';
import { createSphericalCollocationDensityGrid } from './density';
import { Tensor } from '../../mol-math/linear-algebra';
@@ -114,7 +114,7 @@ export const CreateOrbitalVolume = PluginStateTransform.BuiltIn({
}, a.data.orbitals[params.index], plugin.canvas3d?.webgl).runInContext(ctx);
const volume: Volume = {
grid: data.grid,
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
sourceData: CubeGridFormat(data),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
@@ -146,7 +146,7 @@ export const CreateOrbitalDensityVolume = PluginStateTransform.BuiltIn({
}, a.data.orbitals, plugin.canvas3d?.webgl).runInContext(ctx);
const volume: Volume = {
grid: data.grid,
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
sourceData: CubeGridFormat(data),
customProperties: new CustomProperties(),
_propertyData: Object.create(null),
};
@@ -166,13 +166,13 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
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)
pickable: PD.Boolean(true),
tryUseGpu: PD.Boolean(true)
}
})({
canAutoUpdate() {
@@ -190,7 +190,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
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) });
return new PluginStateObject.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
});
},
update({ a, b, newParams: srcParams }, plugin: PluginContext) {
@@ -200,6 +200,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
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.sourceData = a.data;
b.data.repr.setState({ pickable: srcParams.pickable });
b.description = VolumeRepresentation3DHelpers.getDescription(props);
return StateTransformer.UpdateResult.Updated;
@@ -208,28 +209,16 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
});
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.');
if (!isCubeGridData(volume.data.sourceData)) throw new Error('Invalid data source kind.');
const { isovalues } = volume.data.sourceData.data as CubeGrid;
const { isovalues } = volume.data.sourceData.data;
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 }
} : {
return createVolumeRepresentationParams(plugin, volume.data, {
type: 'isosurface',
typeParams: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, alpha: params.alpha, xrayShaded: params.xrayShaded },
typeParams: { isoValue: { kind: 'absolute', absoluteValue: (value ?? 1000) * params.relativeIsovalue }, alpha: params.alpha, xrayShaded: params.xrayShaded, tryUseGpu: params.tryUseGpu },
color: 'uniform',
colorParams: { value: params.color }
});

View File

@@ -5,10 +5,10 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Structure, StructureElement, StructureProperties } from '../../mol-model/structure';
import { Structure, StructureElement, StructureProperties, Unit } 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 { AccessibleSurfaceAreaParams } 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';
@@ -16,6 +16,10 @@ import { AccessibleSurfaceArea } from '../../mol-model-props/computed/accessible
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { MembraneOrientation } from './prop';
const LARGE_CA_THRESHOLD = 5000;
const DEFAULT_UPDATE_INTERVAL = 10;
const LARGE_CA_UPDATE_INTERVAL = 1;
interface ANVILContext {
structure: Structure,
@@ -24,29 +28,45 @@ interface ANVILContext {
minThickness: number,
maxThickness: number,
asaCutoff: number,
adjust: number,
offsets: ArrayLike<number>,
exposed: ArrayLike<boolean>,
exposed: ArrayLike<number>,
hydrophobic: ArrayLike<boolean>,
centroid: Vec3,
extent: number
extent: number,
large: boolean
};
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.' }),
numberOfSpherePoints: PD.Numeric(175, { 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' })
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: 'Relative ASA cutoff above which residues will be considered' }),
adjust: PD.Numeric(14, { min: 0, max: 30, step: 1 }, { description: 'Minimum length of membrane-spanning regions (original values: 14 for alpha-helices and 5 for beta sheets). Set to 0 to not optimize membrane thickness.' }),
tmdetDefinition: PD.Boolean(false, { description: `Use TMDET's classification of membrane-favoring amino acids. TMDET's classification shows better performance on porins and other beta-barrel structures.` })
};
export type ANVILParams = typeof ANVILParams
export type ANVILProps = PD.Values<ANVILParams>
/** ANVIL-specific (not general) definition of membrane-favoring amino acids */
const ANVIL_DEFINITION = new Set(['ALA', 'CYS', 'GLY', 'HIS', 'ILE', 'LEU', 'MET', 'PHE', 'SER', 'TRP', 'VAL']);
/** TMDET-specific (not general) definition of membrane-favoring amino acids */
const TMDET_DEFINITION = new Set(['LEU', 'ILE', 'VAL', 'PHE', 'MET', 'GLY', 'TRP', 'TYR']);
/**
* 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, 15
* doi: 10.1093/protein/gzv063
*
* ANVIL is derived from TMDET, the corresponding classification of hydrophobic amino acids is provided as optional parameter:
* Gabor E. Tusnady, Zsuzsanna Dosztanyi and Istvan Simon
* Transmembrane proteins in the Protein Data Bank: identification and classification
* Bioinformatics, 2004, 2964-2972
* doi: 10.1093/bioinformatics/bth340
*/
export function computeANVIL(structure: Structure, props: ANVILProps) {
return Task.create('Compute Membrane Orientation', async runtime => {
@@ -54,22 +74,38 @@ export function computeANVIL(structure: Structure, props: ANVILProps) {
});
}
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3add = Vec3.add;
const v3clone = Vec3.clone;
const v3create = Vec3.create;
const v3distance = Vec3.distance;
const v3dot = Vec3.dot;
const v3magnitude = Vec3.magnitude;
const v3normalize = Vec3.normalize;
const v3scale = Vec3.scale;
const v3scaleAndAdd = Vec3.scaleAndAdd;
const v3set = Vec3.set;
const v3squaredDistance = Vec3.squaredDistance;
const v3sub = Vec3.sub;
const v3zero = Vec3.zero;
const centroidHelper = new CentroidHelper();
function initialize(structure: Structure, props: ANVILProps): ANVILContext {
async function initialize(structure: Structure, props: ANVILProps, accessibleSurfaceArea: AccessibleSurfaceArea): Promise<ANVILContext> {
const l = StructureElement.Location.create(structure);
const { label_atom_id, x, y, z } = StructureProperties.atom;
const elementCount = structure.polymerResidueCount;
const { label_atom_id, label_comp_id, x, y, z } = StructureProperties.atom;
const asaCutoff = props.asaCutoff / 100;
centroidHelper.reset();
let offsets = new Int32Array(elementCount);
let exposed = new Array<boolean>(elementCount);
const offsets = new Array<number>();
const exposed = new Array<number>();
const hydrophobic = new Array<boolean>();
const definition = props.tmdetDefinition ? TMDET_DEFINITION : ANVIL_DEFINITION;
const accessibleSurfaceArea = structure && AccessibleSurfaceAreaProvider.get(structure);
const asa = accessibleSurfaceArea.value!;
function isPartOfEntity(l: StructureElement.Location): boolean {
return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.valueKind(l.unit.residueIndex[l.element]) === 0;
}
const vec = Vec3();
let m = 0;
const vec = v3zero();
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i];
const { elements } = unit;
@@ -79,70 +115,88 @@ function initialize(structure: Structure, props: ANVILProps): ANVILContext {
const eI = elements[j];
l.element = eI;
// consider only amino acids
if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein) {
// consider only amino acids in chains
if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein || !isPartOfEntity(l)) {
continue;
}
// only CA is considered for downstream operations
if (label_atom_id(l) !== 'CA') {
if (label_atom_id(l) !== 'CA' && label_atom_id(l) !== 'BB') {
continue;
}
// original ANVIL only considers canonical amino acids
if (!MaxAsa[label_comp_id(l)]) {
continue;
}
// while iterating use first pass to compute centroid
Vec3.set(vec, x(l), y(l), z(l));
v3set(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++;
offsets.push(structure.serialMapping.getSerialIndex(l.unit, l.element));
if (AccessibleSurfaceArea.getValue(l, accessibleSurfaceArea) / MaxAsa[label_comp_id(l)] > asaCutoff) {
exposed.push(structure.serialMapping.getSerialIndex(l.unit, l.element));
hydrophobic.push(isHydrophobic(definition, label_comp_id(l)));
}
}
}
// omit potentially empty tail1
offsets = offsets.slice(0, m);
exposed = exposed.slice(0, m);
// calculate centroid and extent
centroidHelper.finishedIncludeStep();
const centroid = centroidHelper.center;
const centroid = v3clone(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));
v3set(vec, x(l), y(l), z(l));
centroidHelper.radiusStep(vec);
}
const extent = 1.2 * Math.sqrt(centroidHelper.radiusSq);
return {
...props,
structure: structure,
structure,
offsets: offsets,
exposed: exposed,
centroid: centroid,
extent: extent
offsets,
exposed,
hydrophobic,
centroid,
extent,
large: offsets.length > LARGE_CA_THRESHOLD
};
}
export async function calculate(runtime: RuntimeContext, structure: Structure, params: ANVILProps): Promise<MembraneOrientation> {
const { label_comp_id } = StructureProperties.atom;
// can't get away with the default 92 points here
const asaProps = { ...PD.getDefaultValues(AccessibleSurfaceAreaParams), probeSize: 4.0, traceOnly: true, numberOfSpherePoints: 184 };
const accessibleSurfaceArea = await AccessibleSurfaceArea.compute(structure, asaProps).runInContext(runtime);
const ctx = initialize(structure, params);
const initialHphobHphil = HphobHphil.filtered(ctx, label_comp_id);
const ctx = await initialize(structure, params, accessibleSurfaceArea);
const initialHphobHphil = HphobHphil.initial(ctx);
const initialMembrane = findMembrane(ctx, generateSpherePoints(ctx, ctx.numberOfSpherePoints), initialHphobHphil, label_comp_id);
const alternativeMembrane = findMembrane(ctx, findProximateAxes(ctx, initialMembrane), initialHphobHphil, label_comp_id);
const initialMembrane = (await findMembrane(runtime, 'Placing initial membrane...', ctx, generateSpherePoints(ctx, ctx.numberOfSpherePoints), initialHphobHphil))!;
const refinedMembrane = (await findMembrane(runtime, 'Refining membrane placement...', ctx, findProximateAxes(ctx, initialMembrane), initialHphobHphil))!;
let membrane = initialMembrane.qmax! > refinedMembrane.qmax! ? initialMembrane : refinedMembrane;
const membrane = initialMembrane.qmax! > alternativeMembrane.qmax! ? initialMembrane : alternativeMembrane;
if (ctx.adjust && !ctx.large) {
membrane = await adjustThickness(runtime, 'Adjusting membrane thickness...', ctx, membrane, initialHphobHphil);
}
const normalVector = v3zero();
const center = v3zero();
v3sub(normalVector, membrane.planePoint1, membrane.planePoint2);
v3normalize(normalVector, normalVector);
v3add(center, membrane.planePoint1, membrane.planePoint2);
v3scale(center, center, 0.5);
const extent = adjustExtent(ctx, membrane, center);
return {
planePoint1: membrane.planePoint1,
planePoint2: membrane.planePoint2,
normalVector: membrane.normalVector!,
radius: ctx.extent,
centroid: ctx.centroid
normalVector,
centroid: center,
radius: extent
};
}
@@ -160,82 +214,79 @@ namespace MembraneCandidate {
return {
planePoint1: c1,
planePoint2: c2,
stats: 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);
export function scored(spherePoint: Vec3, planePoint1: Vec3, planePoint2: Vec3, stats: HphobHphil, qmax: number, centroid: Vec3): MembraneCandidate {
const normalVector = v3zero();
v3sub(normalVector, centroid, spherePoint);
return {
planePoint1: c1,
planePoint2: c2,
stats: stats,
normalVector: diam_vect,
spherePoint: spherePoint,
qmax: qmax
planePoint1,
planePoint2,
stats,
normalVector,
spherePoint,
qmax
};
}
}
function findMembrane(ctx: ANVILContext, spherePoints: Vec3[], initialStats: HphobHphil, label_comp_id: StructureElement.Property<string>): MembraneCandidate {
const { centroid, stepSize, minThickness, maxThickness } = ctx;
async function findMembrane(runtime: RuntimeContext, message: string | undefined, ctx: ANVILContext, spherePoints: Vec3[], initialStats: HphobHphil): Promise<MembraneCandidate | undefined> {
const { centroid, stepSize, minThickness, maxThickness, large } = ctx;
// best performing membrane
let membrane: MembraneCandidate;
let membrane: MembraneCandidate | undefined;
// 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 = [];
const diam = v3zero();
for (let n = 0, nl = spherePoints.length; n < nl; n++) {
if (runtime.shouldUpdate && message && (n + 1) % (large ? LARGE_CA_UPDATE_INTERVAL : DEFAULT_UPDATE_INTERVAL) === 0) {
await runtime.update({ message, current: (n + 1), max: nl });
}
const spherePoint = spherePoints[n];
v3sub(diam, centroid, spherePoint);
v3scale(diam, diam, 2);
const diamNorm = v3magnitude(diam);
const sliceStats = HphobHphil.sliced(ctx, stepSize, spherePoint, diam, diamNorm);
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);
const c1 = v3zero();
const c2 = v3zero();
v3scaleAndAdd(c1, spherePoint, diam, i / diamNorm);
v3scaleAndAdd(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));
const stats = sliceStats[Math.round(i / stepSize)];
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 jmax = Math.floor((minThickness / stepSize) - 1);
for (let width = 0, widthl = maxThickness; width <= widthl;) {
for (let i = 0, il = qvartemp.length - 1 - jmax; i < il; i++) {
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;
hphob += Math.floor(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 stats = { hphob, hphil };
const qvaltest = qValue(stats, initialStats);
if (qvaltest > qmax) {
if (qvaltest >= qmax) {
qmax = qvaltest;
membrane = MembraneCandidate.scored(spherePoint, c1, c2, HphobHphil.of(hphob, hphil, total), qmax, centroid);
membrane = MembraneCandidate.scored(spherePoint, qvartemp[i].planePoint1, qvartemp[i + jmax].planePoint2, stats, qmax, centroid);
}
}
}
@@ -244,15 +295,192 @@ function findMembrane(ctx: ANVILContext, spherePoints: Vec3[], initialStats: Hph
}
}
return membrane!;
return membrane;
}
/** Adjust membrane thickness by maximizing the number of membrane segments. */
async function adjustThickness(runtime: RuntimeContext, message: string | undefined, ctx: ANVILContext, membrane: MembraneCandidate, initialHphobHphil: HphobHphil): Promise<MembraneCandidate> {
const { minThickness, large } = ctx;
const step = 0.3;
let maxThickness = v3distance(membrane.planePoint1, membrane.planePoint2);
let maxNos = membraneSegments(ctx, membrane).length;
let optimalThickness = membrane;
let n = 0;
const nl = Math.ceil((maxThickness - minThickness) / step);
while (maxThickness > minThickness) {
n++;
if (runtime.shouldUpdate && message && n % (large ? LARGE_CA_UPDATE_INTERVAL : DEFAULT_UPDATE_INTERVAL) === 0) {
await runtime.update({ message, current: n, max: nl });
}
const p = {
...ctx,
maxThickness,
stepSize: step
};
const temp = await findMembrane(runtime, void 0, p, [membrane.spherePoint!], initialHphobHphil);
if (temp) {
const nos = membraneSegments(ctx, temp).length;
if (nos > maxNos) {
maxNos = nos;
optimalThickness = temp;
}
}
maxThickness -= step;
}
return optimalThickness;
}
/** Report auth_seq_ids for all transmembrane segments. Will reject segments that are shorter than the adjust parameter specifies. Missing residues are considered in-membrane. */
function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): ArrayLike<{ start: number, end: number }> {
const { offsets, structure, adjust } = ctx;
const { normalVector, planePoint1, planePoint2 } = membrane;
const { units } = structure;
const { elementIndices, unitIndices } = structure.serialMapping;
const testPoint = v3zero();
const { auth_seq_id } = StructureProperties.residue;
const d1 = -v3dot(normalVector!, planePoint1);
const d2 = -v3dot(normalVector!, planePoint2);
const dMin = Math.min(d1, d2);
const dMax = Math.max(d1, d2);
const inMembrane: { [k: string]: Set<number> } = Object.create(null);
const outMembrane: { [k: string]: Set<number> } = Object.create(null);
const segments: Array<{ start: number, end: number }> = [];
let authAsymId;
let lastAuthAsymId = null;
let authSeqId;
let lastAuthSeqId = units[0].model.atomicHierarchy.residues.auth_seq_id.value((units[0] as Unit.Atomic).chainIndex[0]) - 1;
let startOffset = 0;
let endOffset = 0;
// collect all residues in membrane layer
for (let k = 0, kl = offsets.length; k < kl; k++) {
const unit = units[unitIndices[offsets[k]]];
if (!Unit.isAtomic(unit)) notAtomic();
const elementIndex = elementIndices[offsets[k]];
authAsymId = unit.model.atomicHierarchy.chains.auth_asym_id.value(unit.chainIndex[elementIndex]);
if (authAsymId !== lastAuthAsymId) {
if (!inMembrane[authAsymId]) inMembrane[authAsymId] = new Set<number>();
if (!outMembrane[authAsymId]) outMembrane[authAsymId] = new Set<number>();
lastAuthAsymId = authAsymId;
}
authSeqId = unit.model.atomicHierarchy.residues.auth_seq_id.value(unit.residueIndex[elementIndex]);
v3set(testPoint, unit.conformation.x(elementIndex), unit.conformation.y(elementIndex), unit.conformation.z(elementIndex));
if (_isInMembranePlane(testPoint, normalVector!, dMin, dMax)) {
inMembrane[authAsymId].add(authSeqId);
} else {
outMembrane[authAsymId].add(authSeqId);
}
}
for (let k = 0, kl = offsets.length; k < kl; k++) {
const unit = units[unitIndices[offsets[k]]];
if (!Unit.isAtomic(unit)) notAtomic();
const elementIndex = elementIndices[offsets[k]];
authAsymId = unit.model.atomicHierarchy.chains.auth_asym_id.value(unit.chainIndex[elementIndex]);
authSeqId = unit.model.atomicHierarchy.residues.auth_seq_id.value(unit.residueIndex[elementIndex]);
if (inMembrane[authAsymId].has(authSeqId)) {
// chain change
if (authAsymId !== lastAuthAsymId) {
segments.push({ start: startOffset, end: endOffset });
lastAuthAsymId = authAsymId;
startOffset = k;
endOffset = k;
}
// sequence gaps
if (authSeqId !== lastAuthSeqId + 1) {
if (outMembrane[authAsymId].has(lastAuthSeqId + 1)) {
segments.push({ start: startOffset, end: endOffset });
startOffset = k;
}
lastAuthSeqId = authSeqId;
endOffset = k;
} else {
lastAuthSeqId++;
endOffset++;
}
}
}
segments.push({ start: startOffset, end: endOffset });
const l = StructureElement.Location.create(structure);
let startAuth;
let endAuth;
const refinedSegments: Array<{ start: number, end: number }> = [];
for (let k = 0, kl = segments.length; k < kl; k++) {
const { start, end } = segments[k];
if (start === 0 || end === offsets.length - 1) continue;
// evaluate residues 1 pos outside of membrane
setLocation(l, structure, offsets[start - 1]);
v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element));
const d3 = -v3dot(normalVector!, testPoint);
setLocation(l, structure, offsets[end + 1]);
v3set(testPoint, l.unit.conformation.x(l.element), l.unit.conformation.y(l.element), l.unit.conformation.z(l.element));
const d4 = -v3dot(normalVector!, testPoint);
if (Math.min(d3, d4) < dMin && Math.max(d3, d4) > dMax) {
// reject this refinement
setLocation(l, structure, offsets[start]);
startAuth = auth_seq_id(l);
setLocation(l, structure, offsets[end]);
endAuth = auth_seq_id(l);
if (Math.abs(startAuth - endAuth) + 1 < adjust) {
return [];
}
refinedSegments.push(segments[k]);
}
}
return refinedSegments;
}
function notAtomic(): never {
throw new Error('Property only available for atomic models.');
}
/** Filter for membrane residues and calculate the final extent of the membrane layer */
function adjustExtent(ctx: ANVILContext, membrane: MembraneCandidate, centroid: Vec3): number {
const { offsets, structure } = ctx;
const { normalVector, planePoint1, planePoint2 } = membrane;
const l = StructureElement.Location.create(structure);
const testPoint = v3zero();
const { x, y, z } = StructureProperties.atom;
const d1 = -v3dot(normalVector!, planePoint1);
const d2 = -v3dot(normalVector!, planePoint2);
const dMin = Math.min(d1, d2);
const dMax = Math.max(d1, d2);
let extent = 0;
for (let k = 0, kl = offsets.length; k < kl; k++) {
setLocation(l, structure, offsets[k]);
v3set(testPoint, x(l), y(l), z(l));
if (_isInMembranePlane(testPoint, normalVector!, dMin, dMax)) {
const dsq = v3squaredDistance(testPoint, centroid);
if (dsq > extent) extent = dsq;
}
}
return Math.sqrt(extent);
}
function qValue(currentStats: HphobHphil, initialStats: HphobHphil): number {
if(initialStats.hphob < 1) {
if (initialStats.hphob < 1) {
initialStats.hphob = 0.1;
}
if(initialStats.hphil < 1) {
if (initialStats.hphil < 1) {
initialStats.hphil += 1;
}
@@ -262,23 +490,27 @@ function qValue(currentStats: HphobHphil, initialStats: HphobHphil): number {
}
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);
const d1 = -v3dot(normalVector, planePoint1);
const d2 = -v3dot(normalVector, planePoint2);
return _isInMembranePlane(testPoint, normalVector, Math.min(d1, d2), Math.max(d1, d2));
}
// generates a defined number of points on a sphere with radius = extent around the specified centroid
function _isInMembranePlane(testPoint: Vec3, normalVector: Vec3, min: number, max: number): boolean {
const d = -v3dot(normalVector, testPoint);
return d > min && d < max;
}
/** 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);
for (let k = 1, kl = numberOfSpherePoints + 1; k < kl; k++) {
h = -1 + 2 * (k - 1) / (2 * numberOfSpherePoints - 1);
theta = Math.acos(h);
phi = (k === 1 || k === numberOfSpherePoints) ? 0 : (oldPhi + 3.6 / Math.sqrt(numberOfSpherePoints * (1 - h * h))) % (2 * Math.PI);
phi = (k === 1 || k === numberOfSpherePoints) ? 0 : (oldPhi + 3.6 / Math.sqrt(2 * numberOfSpherePoints * (1 - h * h))) % (2 * Math.PI);
const point = Vec3.create(
const point = v3create(
extent * Math.sin(phi) * Math.sin(theta) + centroid[0],
extent * Math.cos(theta) + centroid[1],
extent * Math.cos(phi) * Math.sin(theta) + centroid[2]
@@ -290,18 +522,18 @@ function generateSpherePoints(ctx: ANVILContext, numberOfSpherePoints: number):
return points;
}
// generates sphere points close to that of the initial membrane
/** 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[] = [];
const s = 2 * extent / numberOfSpherePoints;
while (sphere_pts2.length < numberOfSpherePoints) {
const d = 2 * extent / numberOfSpherePoints + j;
const dsq = d * d;
const dsq = (s + j) * (s + j);
sphere_pts2 = [];
for (let i = 0, il = points.length; i < il; i++) {
if (Vec3.squaredDistance(points[i], membrane.spherePoint!) < dsq) {
if (v3squaredDistance(points[i], membrane.spherePoint!) < dsq) {
sphere_pts2.push(points[i]);
}
}
@@ -312,56 +544,78 @@ function findProximateAxes(ctx: ANVILContext, membrane: MembraneCandidate): Vec3
interface HphobHphil {
hphob: number,
hphil: number,
total: number
hphil: 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;
export function initial(ctx: ANVILContext): HphobHphil {
const { exposed, hydrophobic } = ctx;
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))) {
for (let k = 0, kl = exposed.length; k < kl; k++) {
if (hydrophobic[k]) {
hphob++;
} else {
hphil++;
}
}
return of(hphob, hphil);
return { hphob, hphil };
}
const testPoint = v3zero();
export function sliced(ctx: ANVILContext, stepSize: number, spherePoint: Vec3, diam: Vec3, diamNorm: number): HphobHphil[] {
const { exposed, hydrophobic, structure } = ctx;
const { units, serialMapping } = structure;
const { unitIndices, elementIndices } = serialMapping;
const sliceStats: HphobHphil[] = [];
for (let i = 0, il = diamNorm - stepSize; i < il; i += stepSize) {
sliceStats[sliceStats.length] = { hphob: 0, hphil: 0 };
}
for (let i = 0, il = exposed.length; i < il; i++) {
const unit = units[unitIndices[exposed[i]]];
const elementIndex = elementIndices[exposed[i]];
v3set(testPoint, unit.conformation.x(elementIndex), unit.conformation.y(elementIndex), unit.conformation.z(elementIndex));
v3sub(testPoint, testPoint, spherePoint);
if (hydrophobic[i]) {
sliceStats[Math.floor(v3dot(testPoint, diam) / diamNorm / stepSize)].hphob++;
} else {
sliceStats[Math.floor(v3dot(testPoint, diam) / diamNorm / stepSize)].hphil++;
}
}
return sliceStats;
}
}
// 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);
/** Returns true if the definition considers this as membrane-favoring amino acid */
export function isHydrophobic(definition: Set<string>, label_comp_id: string): boolean {
return definition.has(label_comp_id);
}
/** Accessible surface area used for normalization. ANVIL uses 'Total-Side REL' values from NACCESS, from: Hubbard, S. J., & Thornton, J. M. (1993). naccess. Computer Program, Department of Biochemistry and Molecular Biology, University College London, 2(1). */
export const MaxAsa: { [k: string]: number } = {
'ALA': 69.41,
'ARG': 201.25,
'ASN': 106.24,
'ASP': 102.69,
'CYS': 96.75,
'GLU': 134.74,
'GLN': 140.99,
'GLY': 32.33,
'HIS': 147.08,
'ILE': 137.96,
'LEU': 141.12,
'LYS': 163.30,
'MET': 156.64,
'PHE': 164.11,
'PRO': 119.90,
'SER': 78.11,
'THR': 101.70,
'TRP': 211.26,
'TYR': 177.38,
'VAL': 114.28
};
function setLocation(l: StructureElement.Location, structure: Structure, serialIndex: number) {
l.structure = structure;
l.unit = structure.units[structure.serialMapping.unitIndices[serialIndex]];

View File

@@ -30,7 +30,7 @@ export const ANVILMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
description: 'Data calculated with ANVIL algorithm.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
private provider = MembraneOrientationProvider
private provider = MembraneOrientationProvider;
register(): void {
DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
@@ -52,7 +52,7 @@ export const ANVILMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
@@ -121,7 +121,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
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' });
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: 'Membrane Orientation' });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
@@ -129,6 +129,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
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);
b.data.sourceData = a.data;
return StateTransformer.UpdateResult.Updated;
});
},
@@ -149,7 +150,7 @@ export const MembraneOrientationPreset = StructureRepresentationPresetProvider({
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const structure = structureCell?.obj?.data;
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
if (!MembraneOrientationProvider.get(structure).value) {

View File

@@ -11,7 +11,6 @@ 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';
@@ -76,7 +75,6 @@ export const MembraneOrientationProvider: CustomStructureProperty.Provider<Membr
});
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);
}

View File

@@ -9,7 +9,6 @@ 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';
@@ -30,18 +29,9 @@ 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' })
radiusFactor: PD.Numeric(1.2, { 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,
@@ -53,9 +43,9 @@ 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),
lineSizeAttenuation: PD.Boolean(false),
linesSize: PD.Numeric(0.5, { min: 0.01, max: 50, step: 0.01 }),
dashedLines: PD.Boolean(false),
};
export type BilayerRimsParams = typeof BilayerRimsParams
export type BilayerRimsProps = PD.Values<BilayerRimsParams>
@@ -66,7 +56,6 @@ const MembraneOrientationVisuals = {
};
export const MembraneOrientationParams = {
...BilayerSpheresParams,
...BilayerPlanesParams,
...BilayerRimsParams,
visuals: PD.MultiSelect(['bilayer-planes', 'bilayer-rims'], PD.objectToOptions(MembraneOrientationVisuals)),
@@ -104,16 +93,16 @@ function membraneLabel(data: Structure) {
}
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 { planePoint1: p1, planePoint2: p2, centroid, 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);
getLayerCircle(builder, p1, centroid, scaledRadius, props);
getLayerCircle(builder, p2, centroid, 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);
function getLayerCircle(builder: LinesBuilder, p: Vec3, centroid: Vec3, radius: number, props: BilayerRimsProps, shape?: Shape<Lines>) {
const circle = getCircle(p, centroid, 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
@@ -130,8 +119,13 @@ function getLayerCircle(builder: LinesBuilder, p: Vec3, centroid: Vec3, normal:
}
const tmpMat = Mat4();
function getCircle(p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
Mat4.targetTo(tmpMat, p, centroid, normal);
const tmpV = Vec3();
function getCircle(p: Vec3, centroid: Vec3, radius: number) {
if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, p, centroid)) === 0) {
Mat4.targetTo(tmpMat, p, centroid, Vec3.unitY);
} else {
Mat4.targetTo(tmpMat, p, centroid, Vec3.unitX);
}
Mat4.setTranslation(tmpMat, p);
Mat4.mul(tmpMat, tmpMat, Mat4.rotX90);
@@ -140,16 +134,16 @@ function getCircle(p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
}
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 { planePoint1: p1, planePoint2: p2, centroid, 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);
getLayerPlane(state, p1, centroid, scaledRadius);
getLayerPlane(state, p2, centroid, 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);
function getLayerPlane(state: MeshBuilder.State, p: Vec3, centroid: Vec3, radius: number) {
const circle = getCircle(p, centroid, radius);
state.currentGroup = 0;
MeshBuilder.addPrimitive(state, Mat4.id, circle);
MeshBuilder.addPrimitiveFlipped(state, Mat4.id, circle);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -33,7 +33,7 @@ export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Valu
if (ctx.structure && info) {
const colors = distinctColors(info.packingsCount);
let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
@@ -48,7 +48,7 @@ export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Valu
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75
}
}}, { minLabel: 'Min', maxLabel: 'Max' });
} }, { minLabel: 'Min', maxLabel: 'Max' });
legend = palette.legend;
const modelColor = new Map<number, Color>();
for (let i = 0, il = models.length; i < il; ++i) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Ludovic Autin <autin@scripps.edu>
* @author Ludovic Autin <ludovic.autin@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -49,7 +49,7 @@ function ResampleControlPoints(points: NumberArray, segmentLength: number) {
// 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[] = [];
const resampledControlPoints: Vec3[] = [];
// resampledControlPoints.Add(controlPoints[0]);
// resampledControlPoints.Add(controlPoints[1]);
@@ -111,7 +111,7 @@ function GetSmoothNormals(points: Vec3[]) {
let p1 = points[1];
let p2 = points[2];
const p21 = Vec3.sub(tmpV1, p2, p1);
const p01 = Vec3.sub(tmpV2, p0, p1);
const p01 = Vec3.sub(tmpV2, p0, p1);
const p0121 = Vec3.cross(tmpV3, p01, p21);
Vec3.normalize(prevV, p0121);
smoothNormals.push(Vec3.clone(prevV));
@@ -179,7 +179,7 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
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 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));
@@ -195,7 +195,7 @@ export function getMatFromResamplePoints(points: NumberArray, segmentLength: num
let new_points: Vec3[] = [];
if (resample) new_points = ResampleControlPoints(points, segmentLength);
else {
for (let idx = 0; idx < points.length / 3; ++idx){
for (let idx = 0; idx < points.length / 3; ++idx) {
new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
}
}
@@ -211,7 +211,7 @@ export function getMatFromResamplePoints(points: NumberArray, segmentLength: num
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 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);

View File

@@ -13,16 +13,27 @@ export interface CellPack {
export interface CellPacking {
name: string,
location: 'surface' | 'interior' | 'cytoplasme',
location: 'surface' | 'interior' | 'cytoplasme'
ingredients: Packing['ingredients']
compartment?: CellCompartment
}
//
export interface CellCompartment {
filename?: string
geom_type?: 'raw' | 'file' | 'sphere' | 'mb' | 'None'
compartment_primitives?: CompartmentPrimitives
}
export interface Cell {
recipe: Recipe
options?: RecipeOptions
cytoplasme?: Packing
compartments?: { [key: string]: Compartment }
mapping_ids?: { [key: number]: [number, string] }
}
export interface RecipeOptions {
resultfile?: string
}
export interface Recipe {
@@ -35,8 +46,29 @@ export interface Recipe {
export interface Compartment {
surface?: Packing
interior?: Packing
geom?: unknown
geom_type?: 'raw' | 'file' | 'sphere' | 'mb' | 'None'
mb?: CompartmentPrimitives
}
// Primitives discribing a compartment
export const enum CompartmentPrimitiveType {
MetaBall = 0,
Sphere = 1,
Cube = 2,
Cylinder = 3,
Cone = 4,
Plane = 5,
None = 6
}
export interface CompartmentPrimitives{
positions?: number[];
radii?: number[];
types?: CompartmentPrimitiveType[];
}
export interface Packing {
ingredients: { [key: string]: Ingredient }
}
@@ -64,18 +96,20 @@ export interface Ingredient {
[curveX: string]: unknown;
/** the orientation in the membrane */
principalAxis?: Vec3;
principalVector?: Vec3;
/** offset along membrane */
offset?: Vec3;
ingtype?: string;
color?: Vec3;
confidence?: number;
Type?: string;
}
export interface IngredientSource {
pdb: string;
bu?: string; /** biological unit e.g AU,BU1,etc.. */
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... */
model?: string; /** model number e.g 0,1,2... */
transform: {
center: boolean;
translate?: Vec3;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/

View File

@@ -2,13 +2,14 @@
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
*/
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 { Ingredient, CellPacking, CompartmentPrimitives } from './data';
import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile, getStructureMean, getFromOPM } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit, Trajectory } from '../../mol-model/structure';
import { trajectoryFromMmCIF, MmcifFormat } from '../../mol-model-formats/structure/mmcif';
@@ -17,7 +18,7 @@ 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 { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl, StructureFromAssemblies, CreateCompartmentSphere } from './state';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../mol-script/runtime/query/compiler';
@@ -28,8 +29,9 @@ 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';
import { readFromFile } from '../../mol-util/data-source';
import { ColorNames } from '../../mol-util/color/names';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
@@ -41,12 +43,16 @@ class TrajectoryCache {
get(id: string) { return this.map.get(id); }
}
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient, baseUrl: string, trajCache: TrajectoryCache, file?: Asset.File) {
async function getModel(plugin: PluginContext, id: string, ingredient: Ingredient,
baseUrl: string, trajCache: TrajectoryCache, location: string,
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 surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
if (location === 'surface') surface = true;
let trajectory = trajCache.get(id);
let assets: Asset.Wrapper[] = [];
const assets: Asset.Wrapper[] = [];
if (!trajectory) {
if (file) {
if (file.name.endsWith('.cif')) {
@@ -68,10 +74,11 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
if (surface) {
try {
const data = await getFromOPM(plugin, id, assetManager);
assets.push(data.asset);
data.pdb.id! = id.toUpperCase();
trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
} catch (e) {
// fallback to getFromPdb
@@ -100,7 +107,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
return { model, assets };
}
async function getStructure(plugin: PluginContext, model: Model, source: IngredientSource, props: { assembly?: string } = {}) {
async function getStructure(plugin: PluginContext, model: Model, source: Ingredient, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model);
const { assembly } = props;
@@ -108,11 +115,12 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
}
let query;
if (source.selection){
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
if (source.source.selection) {
const sel = source.source.selection;
// selection can have the model ID as well. remove it
const asymIds: string[] = sel.replace(/ /g, '').replace(/:/g, '').split('or').slice(1);
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')])
})
]);
@@ -123,11 +131,11 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
})
]);
}
const compiled = compile<StructureSelection>(query);
const result = compiled(new QueryContext(structure));
structure = StructureSelection.unionStructure(result);
// change here if possible the label ?
// structure.label = source.name;
return structure;
}
@@ -141,9 +149,9 @@ function getTransformLegacy(trans: Vec3, rot: Quat) {
}
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3]);
const 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]);
const p: Vec3 = Vec3.create(-trans[0], trans[1], trans[2]);
Mat4.setTranslation(m, p);
return m;
}
@@ -157,9 +165,9 @@ function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0;
const instances: Mat4[] = [];
let segmentLength = 3.4;
if (ingredient.uLength){
if (ingredient.uLength) {
segmentLength = ingredient.uLength;
} else if (ingredient.radii){
} else if (ingredient.radii) {
segmentLength = ingredient.radii[0].radii
? ingredient.radii[0].radii[0] * 2.0
: 3.4;
@@ -168,7 +176,7 @@ function getCurveTransforms(ingredient: Ingredient) {
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`;
if (!(cname in ingredient)) {
// console.warn(`Expected '${cname}' in ingredient`)
console.warn(`Expected '${cname}' in ingredient`);
continue;
}
const _points = ingredient[cname] as Vec3[];
@@ -177,9 +185,9 @@ function getCurveTransforms(ingredient: Ingredient) {
continue;
}
// test for resampling
let distance: number = Vec3.distance(_points[0], _points[1]);
const distance: number = Vec3.distance(_points[0], _points[1]);
if (distance >= segmentLength + 2.0) {
console.info(distance);
// console.info(distance);
resampling = true;
}
const points = new Float32Array(_points.length * 3);
@@ -190,13 +198,13 @@ function getCurveTransforms(ingredient: Ingredient) {
return instances;
}
function getAssembly(transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder();
function getAssembly(name: string, transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder({ label: name });
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 ] } });
const op = SymmetryOperator.create(id, transforms[i], { assembly: { id, operId: i, operList: [id] } });
for (const unit of units) {
builder.addWithOperator(unit, op);
}
@@ -307,13 +315,13 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
});
const curveModel = await plugin.runTask(curveModelTask);
return getStructure(plugin, curveModel, ingredient.source);
// ingredient.source.selection = undefined;
return getStructure(plugin, curveModel, ingredient);
}
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache) {
async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles, trajCache: TrajectoryCache, location: 'surface' | 'interior' | 'cytoplasme') {
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?
@@ -325,72 +333,79 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
}
// model id in case structure is NMR
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, file);
const { model, assets } = await getModel(plugin, source.pdb || name, ingredient, baseUrl, trajCache, location, file);
if (!model) return;
let structure: Structure;
if (nbCurve) {
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
} else {
if ((!results || results.length === 0)) return;
let bu: string|undefined = source.bu ? source.bu : undefined;
if (bu){
if (bu) {
if (bu === 'AU') {
bu = undefined;
} else {
bu = bu.slice(2);
}
}
structure = await getStructure(plugin, model, source, { assembly: bu });
structure = await getStructure(plugin, model, ingredient, { assembly: bu });
// transform with offset and pcp
let legacy: boolean = true;
if (ingredient.offset || ingredient.principalAxis){
const pcp = ingredient.principalVector ? ingredient.principalVector : ingredient.principalAxis;
if (pcp) {
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())){
if (ingredient.offset) {
const o: Vec3 = Vec3.create(ingredient.offset[0], ingredient.offset[1], ingredient.offset[2]);
if (!Vec3.exactEquals(o, Vec3.zero())) { // -1, 1, 4e-16 ??
if (location !== 'surface') {
Vec3.negate(o, o);
}
const m: Mat4 = Mat4.identity();
Mat4.setTranslation(m, ingredient.offset);
Mat4.setTranslation(m, o);
structure = Structure.transform(structure, m);
}
}
if (ingredient.principalAxis){
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
if (pcp) {
const p: Vec3 = Vec3.create(pcp[0], pcp[1], pcp[2]);
if (!Vec3.exactEquals(p, Vec3.unitZ)) {
const q: Quat = Quat.identity();
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
Quat.rotationTo(q, p, Vec3.unitZ);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
structure = Structure.transform(structure, m);
}
}
}
structure = getAssembly(getResultTransforms(results, legacy), structure);
structure = getAssembly(name, 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 { ingredients, location, 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);
const ingredientStructure = await getIngredientStructure(plugin, ingredients[iName], baseUrl, ingredientFiles, trajCache, location);
if (ingredientStructure) {
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
const c = ingredients[iName].color;
if (c){
if (c) {
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
} else {
skipColors = true;
colors.push(Color.fromNormalizedRgb(1, 0, 0));
}
}
}
@@ -402,7 +417,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`);
let maxInvariantId = 0;
let maxChainGroupId = 0;
const maxChainGroupId = 0;
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId;
const chainGroupId = u.chainGroupId + offsetChainGroupId;
@@ -414,21 +429,20 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
const structure = new Structure(units);
for( let i = 0, il = structure.models.length; i < il; ++i) {
const structure = Structure.create(units, { label: name + '.' + location });
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 };
return { structure, assets, colors: 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') {
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0' || packings[i].name === 'HIV_capsid') {
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));
@@ -454,7 +468,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
break;
}
}
if (!file){
if (!file) {
// check for cif directly
const cifileName = `${name}.cif`;
for (const f of params.ingredients) {
@@ -465,7 +479,8 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
}
}
}
let legacy_membrane: boolean = false; // temporary variable until all membrane are converted to the new correct cif format
let geometry_membrane: boolean = false; // membrane can be a mesh geometry
let b = state.build().toRoot();
if (file) {
if (file.name.endsWith('.cif')) {
@@ -474,27 +489,82 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
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 } });
if (name.toLowerCase().endsWith('.bcif')) {
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}`);
b = b.apply(StateTransforms.Data.Download, { url, isBinary: true, label: name }, { state: { isGhost: true } });
} else if (name.toLowerCase().endsWith('.cif')) {
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/membranes/${name}`);
b = b.apply(StateTransforms.Data.Download, { url, isBinary: false, label: name }, { state: { isGhost: true } });
} else if (name.toLowerCase().endsWith('.ply')) {
const url = Asset.getUrlAsset(plugin.managers.asset, `${params.baseUrl}/geometries/${name}`);
b = b.apply(StateTransforms.Data.Download, { url, isBinary: false, label: name }, { state: { isGhost: true } });
geometry_membrane = 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 } });
legacy_membrane = 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,
const props = {
type: {
name: 'assembly' as const,
params: { id: '1' }
}
};
if (legacy_membrane) {
// old membrane
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);
} else if (geometry_membrane) {
await b.apply(StateTransforms.Data.ParsePly, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.ShapeFromPly)
.apply(StateTransforms.Representation.ShapeRepresentation3D, { xrayShaded: true,
doubleSided: true, coloring: { name: 'uniform', params: { color: ColorNames.orange } } })
.commit({ revertOnError: true });
} else {
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(StateTransforms.Model.StructureFromModel, props, { state: { isGhost: true } })
.commit({ revertOnError: true });
const membraneParams = {
representation: params.preset.representation,
};
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
}
}
await CellpackMembranePreset.apply(membrane, membraneParams, plugin);
async function handleMembraneSpheres(state: State, primitives: CompartmentPrimitives) {
const nSpheres = primitives.positions!.length / 3;
// console.log('ok mb ', nSpheres);
// TODO : take in account the type of the primitives.
for (let j = 0; j < nSpheres; j++) {
await state.build()
.toRoot()
.apply(CreateCompartmentSphere, {
center: Vec3.create(
primitives.positions![j * 3 + 0],
primitives.positions![j * 3 + 1],
primitives.positions![j * 3 + 2]
),
radius: primitives!.radii![j]
})
.commit();
}
}
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>>;
let resultsFile: Asset.File | null = params.results;
if (params.source.name === 'id') {
const url = Asset.getUrlAsset(plugin.managers.asset, getCellPackModelUrl(params.source.params, params.baseUrl));
cellPackJson = state.build().toRoot()
@@ -506,29 +576,36 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
return;
}
let jsonFile: Asset.File;
let modelFile: 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'));
if (data['model.json']) {
modelFile = Asset.File(new File([data['model.json']], 'model.json'));
} else {
throw new Error('model.json missing from zip file');
}
if (data['results.bin']) {
resultsFile = Asset.File(new File([data['results.bin']], 'results.bin'));
}
objectForEach(data, (v, k) => {
if (k === 'model.json') return;
if (k === 'results.bin') return;
ingredientFiles.push(Asset.File(new File([v], k)));
});
} else {
jsonFile = file;
modelFile = file;
}
cellPackJson = state.build().toRoot()
.apply(StateTransforms.Data.ReadFile, { file: jsonFile, isBinary: false, label: jsonFile.name }, { state: { isGhost: true } });
.apply(StateTransforms.Data.ReadFile, { file: modelFile, isBinary: false, label: modelFile.name }, { state: { isGhost: true } });
}
const cellPackBuilder = cellPackJson
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack);
.apply(ParseCellPack, { resultsFile, baseUrl: params.baseUrl });
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(runtime);
const { packings } = cellPackObject.obj!.data;
const { packings } = cellPackObject.obj!.data;
await handleHivRna(plugin, packings, params.baseUrl);
for (let i = 0, il = packings.length; i < il; ++i) {
@@ -544,8 +621,30 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
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);
if (packings[i].compartment) {
if (params.membrane === 'lipids') {
if (packings[i].compartment!.geom_type) {
if (packings[i].compartment!.geom_type === 'file') {
// TODO: load mesh files or vertex,faces data
await loadMembrane(plugin, packings[i].compartment!.filename!, state, params);
} else if (packings[i].compartment!.compartment_primitives) {
await handleMembraneSpheres(state, packings[i].compartment!.compartment_primitives!);
}
} else {
// try loading membrane from repo as a bcif file or from the given list of files.
if (params.membrane === 'lipids') {
await loadMembrane(plugin, packings[i].name, state, params);
}
}
} else if (params.membrane === 'geometry') {
if (packings[i].compartment!.compartment_primitives) {
await handleMembraneSpheres(state, packings[i].compartment!.compartment_primitives!);
} else if (packings[i].compartment!.geom_type === 'file') {
if (packings[i].compartment!.filename!.toLowerCase().endsWith('.ply')) {
await loadMembrane(plugin, packings[i].compartment!.filename!, state, params);
}
}
}
}
}
}
@@ -555,19 +654,19 @@ const LoadCellPackModelParams = {
'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'],
['Blood_HIV.json', 'Blood HIV'],
['HIV-1_0.1.6-8_mixed_radii_pdb.json', 'HIV'],
['influenza_model1.json', 'Influenza envelope'],
['InfluenzaModel2.json', 'Influenza Complete'],
['InfluenzaModel2.json', 'Influenza complete'],
['ExosomeModel.json', 'Exosome Model'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma simple'],
['MycoplasmaModel.json', 'Mycoplasma WholeCell model'],
['MycoplasmaGenitalium.json', 'Mycoplasma Genitalium curated 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.' }),
'file': PD.File({ accept: '.json,.cpr,.zip', description: 'Open model definition from .json/.cpr file or open .zip file containing model definition plus ingredients.', label: 'Recipe file' }),
}, { options: [['id', 'Id'], ['file', 'File']] }),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
membrane: PD.Boolean(true),
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredients' }),
results: PD.File({ accept: '.bin', description: 'open results file in binary format from cellpackgpu for the specified recipe', label: 'Results file' }),
membrane: PD.Select('lipids', PD.arrayToOptions(['lipids', 'geometry', 'none'])),
ingredients: PD.FileList({ accept: '.cif,.bcif,.pdb', label: 'Ingredient files' }),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'orientation']))
@@ -581,4 +680,4 @@ export const LoadCellPackModel = StateAction.build({
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
await loadPackings(ctx, taskCtx, state, params);
}));
}));

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
*/
import { StateObjectRef } from '../../mol-state';
@@ -9,8 +10,6 @@ import { StructureRepresentationPresetProvider, presetStaticComponent } from '..
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),
@@ -42,8 +41,8 @@ export const CellpackPackingPreset = StructureRepresentationPresetProvider({
Object.assign(reprProps, { sizeFactor: 2 });
}
const info = structureCell.obj?.data && CellPackInfoProvider.get(structureCell.obj?.data).value;
const color = info?.colors ? CellPackProvidedColorThemeProvider.name : CellPackGenerateColorThemeProvider.name;
// default is generated
const color = CellPackGenerateColorThemeProvider.name;
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, {});
const representations = {
@@ -92,4 +91,4 @@ export const CellpackMembranePreset = StructureRepresentationPresetProvider({
return { components, representations };
}
});
});

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -34,4 +34,4 @@ export const CellPackInfoProvider: CustomStructureProperty.Provider<typeof CellP
value: { ...CellPackInfoParams.info.defaultValue, ...props.info }
};
}
});
});

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
*/
import { ShapeRepresentation } from '../../mol-repr/shape/representation';
import { Shape } from '../../mol-model/shape';
import { ColorNames } from '../../mol-util/color/names';
import { RuntimeContext } from '../../mol-task';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
// import { Polyhedron, DefaultPolyhedronProps } from '../../mol-geo/primitive/polyhedron';
// import { Icosahedron } from '../../mol-geo/primitive/icosahedron';
import { Sphere } from '../../mol-geo/primitive/sphere';
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { RepresentationParamsGetter, Representation, RepresentationContext } from '../../mol-repr/representation';
interface MembraneSphereData {
radius: number
center: Vec3
}
const MembraneSphereParams = {
...Mesh.Params,
cellColor: PD.Color(ColorNames.orange),
cellScale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
radius: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }),
center: PD.Vec3(Vec3.create(0, 0, 0)),
quality: { ...Mesh.Params.quality, isEssential: false },
};
type MeshParams = typeof MembraneSphereParams
const MembraneSphereVisuals = {
'mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MeshParams>) => ShapeRepresentation(getMBShape, Mesh.Utils),
};
export const MBParams = {
...MembraneSphereParams
};
export type MBParams = typeof MBParams
export type UnitcellProps = PD.Values<MBParams>
function getMBMesh(data: MembraneSphereData, props: UnitcellProps, mesh?: Mesh) {
const state = MeshBuilder.createState(256, 128, mesh);
const radius = props.radius;
const asphere = Sphere(3);
const trans: Mat4 = Mat4.identity();
Mat4.fromScaling(trans, Vec3.create(radius, radius, radius));
state.currentGroup = 1;
MeshBuilder.addPrimitive(state, trans, asphere);
const m = MeshBuilder.getMesh(state);
return m;
}
function getMBShape(ctx: RuntimeContext, data: MembraneSphereData, props: UnitcellProps, shape?: Shape<Mesh>) {
const geo = getMBMesh(data, props, shape && shape.geometry);
const label = 'mb';
return Shape.create(label, data, geo, () => props.cellColor, () => 1, () => label);
}
export type MBRepresentation = Representation<MembraneSphereData, MBParams>
export function MBRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneSphereData, MBParams>): MBRepresentation {
return Representation.createMulti('MB', ctx, getParams, Representation.StateBuilder, MembraneSphereVisuals as unknown as Representation.Def<MembraneSphereData, MBParams>);
}

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
*/
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
@@ -15,9 +16,13 @@ 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';
import { Vec3, Quat } from '../../mol-math/linear-algebra';
import { StateTransformer } from '../../mol-state';
import { MBRepresentation, MBParams } from './representation';
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
import { getFloatValue } from './util';
export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/';
export const DefaultCellPackBaseUrl = 'https://raw.githubusercontent.com/mesoscope/cellPACK_data/master/cellPACK_database_1.1.0';
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
export { ParseCellPack };
@@ -26,26 +31,173 @@ const ParseCellPack = PluginStateTransform.BuiltIn({
name: 'parse-cellpack',
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
from: PSO.Format.Json,
to: CellPack
to: CellPack,
params: a => {
return {
resultsFile: PD.File({ accept: '.bin' }),
baseUrl: PD.Text(DefaultCellPackBaseUrl)
};
}
})({
apply({ a }) {
apply({ a, params, cache }, plugin: PluginContext) {
return Task.create('Parse CellPack', async ctx => {
const cell = a.data as Cell;
let counter_id = 0;
let fiber_counter_id = 0;
let comp_counter = 0;
const packings: CellPacking[] = [];
const { compartments, cytoplasme } = cell;
if (!cell.mapping_ids) cell.mapping_ids = {};
if (cytoplasme) {
packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
for (const iName in cytoplasme.ingredients) {
if (cytoplasme.ingredients[iName].ingtype === 'fiber') {
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
if (!cytoplasme.ingredients[iName].nbCurve) cytoplasme.ingredients[iName].nbCurve = 0;
fiber_counter_id++;
} else {
cell.mapping_ids[counter_id] = [comp_counter, iName];
if (!cytoplasme.ingredients[iName].results) { cytoplasme.ingredients[iName].results = []; }
counter_id++;
}
}
comp_counter++;
}
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 });
let filename = '';
if (compartments[name].geom_type === 'file') {
filename = (compartments[name].geom) ? compartments[name].geom as string : '';
}
const compartment = { filename: filename, geom_type: compartments[name].geom_type, compartment_primitives: compartments[name].mb };
if (surface) {
packings.push({ name, location: 'surface', ingredients: surface.ingredients, compartment: compartment });
for (const iName in surface.ingredients) {
if (surface.ingredients[iName].ingtype === 'fiber') {
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
if (!surface.ingredients[iName].nbCurve) surface.ingredients[iName].nbCurve = 0;
fiber_counter_id++;
} else {
cell.mapping_ids[counter_id] = [comp_counter, iName];
if (!surface.ingredients[iName].results) { surface.ingredients[iName].results = []; }
counter_id++;
}
}
comp_counter++;
}
if (interior) {
if (!surface) packings.push({ name, location: 'interior', ingredients: interior.ingredients, compartment: compartment });
else packings.push({ name, location: 'interior', ingredients: interior.ingredients });
for (const iName in interior.ingredients) {
if (interior.ingredients[iName].ingtype === 'fiber') {
cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
if (!interior.ingredients[iName].nbCurve) interior.ingredients[iName].nbCurve = 0;
fiber_counter_id++;
} else {
cell.mapping_ids[counter_id] = [comp_counter, iName];
if (!interior.ingredients[iName].results) { interior.ingredients[iName].results = []; }
counter_id++;
}
}
comp_counter++;
}
}
}
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
const { options } = cell;
let resultsAsset: Asset.Wrapper<'binary'> | undefined;
if (params.resultsFile) {
resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(params.resultsFile, 'binary', true));
} else if (options?.resultfile) {
const url = `${params.baseUrl}/results/${options.resultfile}`;
resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(Asset.getUrlAsset(plugin.managers.asset, url), 'binary', true));
}
if (resultsAsset) {
(cache as any).asset = resultsAsset;
const results = resultsAsset.data;
// flip the byte order if needed
const buffer = IsNativeEndianLittle ? results.buffer : flipByteOrder(results, 4);
const numbers = new DataView(buffer);
const ninst = getFloatValue(numbers, 0);
const npoints = getFloatValue(numbers, 4);
const ncurve = getFloatValue(numbers, 8);
let offset = 12;
if (ninst !== 0) {
const pos = new Float32Array(buffer, offset, ninst * 4);
offset += ninst * 4 * 4;
const quat = new Float32Array(buffer, offset, ninst * 4);
offset += ninst * 4 * 4;
for (let i = 0; i < ninst; i++) {
const x: number = pos[i * 4 + 0];
const y: number = pos[i * 4 + 1];
const z: number = pos[i * 4 + 2];
const ingr_id = pos[i * 4 + 3] as number;
const pid = cell.mapping_ids![ingr_id];
if (!packings[pid[0]].ingredients[pid[1]].results) {
packings[pid[0]].ingredients[pid[1]].results = [];
}
packings[pid[0]].ingredients[pid[1]].results.push([Vec3.create(x, y, z),
Quat.create(quat[i * 4 + 0], quat[i * 4 + 1], quat[i * 4 + 2], quat[i * 4 + 3])]);
}
}
if (npoints !== 0) {
const ctr_pos = new Float32Array(buffer, offset, npoints * 4);
offset += npoints * 4 * 4;
offset += npoints * 4 * 4;
const ctr_info = new Float32Array(buffer, offset, npoints * 4);
offset += npoints * 4 * 4;
const curve_ids = new Float32Array(buffer, offset, ncurve * 4);
offset += ncurve * 4 * 4;
let counter = 0;
let ctr_points: Vec3[] = [];
let prev_ctype = 0;
let prev_cid = 0;
for (let i = 0; i < npoints; i++) {
const x: number = -ctr_pos[i * 4 + 0];
const y: number = ctr_pos[i * 4 + 1];
const z: number = ctr_pos[i * 4 + 2];
const cid: number = ctr_info[i * 4 + 0]; // curve id
const ctype: number = curve_ids[cid * 4 + 0]; // curve type
// cid 148 165 -1 0
// console.log("cid ",cid,ctype,prev_cid,prev_ctype);//165,148
if (prev_ctype !== ctype) {
const pid = cell.mapping_ids![-prev_ctype - 1];
const cname = `curve${counter}`;
packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
ctr_points = [];
counter = 0;
} else if (prev_cid !== cid) {
ctr_points = [];
const pid = cell.mapping_ids![-prev_ctype - 1];
const cname = `curve${counter}`;
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
counter += 1;
}
ctr_points.push(Vec3.create(x, y, z));
prev_ctype = ctype;
prev_cid = cid;
}
// do the last one
const pid = cell.mapping_ids![-prev_ctype - 1];
const cname = `curve${counter}`;
packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
}
}
return new CellPack({ cell, packings });
});
}
},
dispose({ cache }) {
((cache as any)?.asset as Asset.Wrapper | undefined)?.dispose();
},
});
export { StructureFromCellpack };
@@ -77,14 +229,13 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
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 });
return new PSO.Molecule.Structure(structure, { label: packing.name + '.' + packing.location });
});
},
dispose({ b, cache }) {
const assets = (cache as any).assets as Asset.Wrapper[];
if(assets) {
if (assets) {
for (const a of assets) a.dispose();
}
@@ -115,17 +266,17 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
// 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 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){
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();
const builder = Structure.Builder({ label: 'Membrane' });
let offsetInvariantId = 0;
for (const s of structures) {
let maxInvariantId = 0;
@@ -137,7 +288,7 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
offsetInvariantId += maxInvariantId + 1;
}
structure = builder.getStructure();
for( let i = 0, il = structure.models.length; i < il; ++i) {
for (let i = 0, il = structure.models.length; i < il; ++i) {
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
}
}
@@ -148,3 +299,28 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
b?.data.customPropertyDescriptors.dispose();
}
});
const CreateTransformer = StateTransformer.builderFactory('cellPACK');
export const CreateCompartmentSphere = CreateTransformer({
name: 'create-compartment-sphere',
display: 'CompartmentSphere',
from: PSO.Root, // or whatever data source
to: PSO.Shape.Representation3D,
params: {
center: PD.Vec3(Vec3()),
radius: PD.Numeric(1),
label: PD.Text(`Compartment Sphere`)
}
})({
canAutoUpdate({ oldParams, newParams }) {
return true;
},
apply({ a, params }, plugin: PluginContext) {
return Task.create('Compartment Sphere', async ctx => {
const data = params;
const repr = MBRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => (MBParams));
await repr.createOrUpdate({ ...params, quality: 'custom', xrayShaded: true, doubleSided: true }, data).runInContext(ctx);
return new PSO.Shape.Representation3D({ repr, sourceData: a }, { label: data.label });
});
}
});

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
*/
import { CIF } from '../../mol-io/reader/cif';
@@ -37,11 +38,11 @@ async function downloadPDB(plugin: PluginContext, url: string, id: string, 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);
const { cif, asset } = await downloadCif(plugin, `https://models.rcsb.org/${pdbId}.bcif`, true, assetManager);
return { mmcif: cif.blocks[0], asset };
}
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager){
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 };
}
@@ -74,4 +75,35 @@ export function getStructureMean(structure: Structure) {
}
const { elementCount } = structure;
return Vec3.create(xSum / elementCount, ySum / elementCount, zSum / elementCount);
}
export function getFloatValue(value: DataView, offset: number) {
// if the last byte is a negative value (MSB is 1), the final
// float should be too
const negative = value.getInt8(offset + 2) >>> 31;
// this is how the bytes are arranged in the byte array/DataView
// buffer
const [b0, b1, b2, exponent] = [
// get first three bytes as unsigned since we only care
// about the last 8 bits of 32-bit js number returned by
// getUint8().
// Should be the same as: getInt8(offset) & -1 >>> 24
value.getUint8(offset),
value.getUint8(offset + 1),
value.getUint8(offset + 2),
// get the last byte, which is the exponent, as a signed int
// since it's already correct
value.getInt8(offset + 3)
];
let mantissa = b0 | (b1 << 8) | (b2 << 16);
if (negative) {
// need to set the most significant 8 bits to 1's since a js
// number is 32 bits but our mantissa is only 24.
mantissa |= 255 << 24;
}
return mantissa * Math.pow(10, exponent);
}

View File

@@ -41,10 +41,10 @@ export const DnatcoConfalPyramidsPreset = StructureRepresentationPresetProvider(
let pyramidsRepr;
if (representations)
pyramidsRepr = builder.buildRepresentation(update, pyramids, { type: ConfalPyramidsRepresentationProvider, typeParams, color: ConfalPyramidsColorThemeProvider }, { tag: 'confal-pyramdis' } );
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 } };
return { components: { ...components, pyramids }, representations: { ...representations, pyramidsRepr } };
}
});
@@ -66,7 +66,7 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
/* TODO: Implement this */
return void 0;
}
}
};
register(): void {
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);

View File

@@ -27,7 +27,7 @@ const ColorMapping: ReadonlyMap<ConformerClasses, Color> = new Map([
['B', Color(0xC8CFFF)],
['BII', Color(0x0059DA)],
['miB', Color(0x3BE8FB)],
['Z', Color(0x01F60E)],
['Z', Color(0x01F60E)],
['IC', Color(0xFA5CFB)],
['OPN', Color(0xE90000)],
['SYN', Color(0xFFFF01)],
@@ -165,8 +165,8 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
legend: TableLegend(iterableToArray(ColorMapping.entries()).map(([conformer, color]) => {
return [conformer, color] as [string, Color];
}).concat([
[ 'Error', ErrorColor ],
[ 'Unknown', DefaultColor ]
['Error', ErrorColor],
['Unknown', DefaultColor]
]))
};
}
@@ -181,6 +181,6 @@ export const ConfalPyramidsColorThemeProvider: ColorTheme.Provider<ConfalPyramid
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)
detach: (data) => data.structure && ConfalPyramidsProvider.ref(data.structure.models[0], false)
}
};

View File

@@ -20,10 +20,10 @@ import { Structure, StructureProperties, Unit } from '../../../mol-model/structu
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
import { StructureGroup, UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
import { VisualUpdateState } from '../../../mol-repr/util';
import { VisualContext } from '../../../mol-repr/visual';
import { getAltResidueLociFromId } from '../../../mol-repr/structure/visual/util/common';
import { getAltResidueLociFromId, StructureGroup } from '../../../mol-repr/structure/visual/util/common';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
import { NullLocation } from '../../../mol-model/location';

View File

@@ -95,7 +95,7 @@ export namespace ConfalPyramidsUtil {
const first = residueInfoFromLocation(locFirst);
const second = residueInfoFromLocation(locSecond);
const model_id = this.hasMultipleModels ? `-m${modelNum}` : '';
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : '');
const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : '';
const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : '';
@@ -114,7 +114,7 @@ export namespace ConfalPyramidsUtil {
this.modelNum = unit.model.modelNum;
}
protected readonly data: CPT.PyramidsData
protected readonly data: CPT.PyramidsData;
protected readonly hasMultipleModels: boolean;
protected readonly entryId: string;
protected readonly modelNum: number;

View File

@@ -61,7 +61,7 @@ function getColumns(block: G3dDataBlock) {
objectForEach(data, (hs, h) => {
objectForEach(hs, (chs, ch) => {
const entity_id = `${ch}-${h}`;
const l = chs.start.length;
const l = chs.start.length;
if (l === 0) return;
let x = chs.x[0];

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { Box3D } from '../../mol-math/geometry';
import { PluginComponent } from '../../mol-plugin-state/component';
import { PluginContext } from '../../mol-plugin/context';
import { Task } from '../../mol-task';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateSelection } from '../../mol-state';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { SetUtils } from '../../mol-util/set';
import { GlbExporter } from './glb-exporter';
import { ObjExporter } from './obj-exporter';
import { StlExporter } from './stl-exporter';
import { UsdzExporter } from './usdz-exporter';
export const GeometryParams = {
format: PD.Select('glb', [
['glb', 'glTF 2.0 Binary (.glb)'],
['stl', 'Stl (.stl)'],
['obj', 'Wavefront (.obj)'],
['usdz', 'Universal Scene Description (.usdz)']
])
};
export class GeometryControls extends PluginComponent {
readonly behaviors = {
params: this.ev.behavior<PD.Values<typeof GeometryParams>>(PD.getDefaultValues(GeometryParams))
};
private getFilename() {
const models = this.plugin.state.data.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Model)).map(s => s.obj!.data);
const uniqueIds = new Set<string>();
models.forEach(m => uniqueIds.add(m.entryId.toUpperCase()));
const idString = SetUtils.toArray(uniqueIds).join('-');
return `${idString || 'molstar-model'}`;
}
exportGeometry() {
const task = Task.create('Export Geometry', async ctx => {
try {
const renderObjects = this.plugin.canvas3d?.getRenderObjects()!;
const filename = this.getFilename();
const boundingSphere = this.plugin.canvas3d?.boundingSphereVisible!;
const boundingBox = Box3D.fromSphere3D(Box3D(), boundingSphere);
let renderObjectExporter: GlbExporter | ObjExporter | StlExporter | UsdzExporter;
switch (this.behaviors.params.value.format) {
case 'glb':
renderObjectExporter = new GlbExporter(boundingBox);
break;
case 'obj':
renderObjectExporter = new ObjExporter(filename, boundingBox);
break;
case 'stl':
renderObjectExporter = new StlExporter(boundingBox);
break;
case 'usdz':
renderObjectExporter = new UsdzExporter(boundingBox, boundingSphere.radius);
break;
default: throw new Error('Unsupported format.');
}
for (let i = 0, il = renderObjects.length; i < il; ++i) {
await ctx.update({ message: `Exporting object ${i}/${il}` });
await renderObjectExporter.add(renderObjects[i], this.plugin.canvas3d?.webgl!, ctx);
}
const blob = await renderObjectExporter.getBlob(ctx);
return {
blob,
filename: filename + '.' + renderObjectExporter.fileExtension
};
} catch (e) {
this.plugin.log.error('Error during geometry export');
throw e;
}
});
return this.plugin.runTask(task, { useOverlay: true });
}
constructor(private plugin: PluginContext) {
super();
}
}

View File

@@ -0,0 +1,341 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { asciiWrite } from '../../mol-io/common/ascii';
import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
import { Box3D } from '../../mol-math/geometry';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { PLUGIN_VERSION } from '../../mol-plugin/version';
import { RuntimeContext } from '../../mol-task';
import { Color } from '../../mol-util/color/color';
import { fillSerial } from '../../mol-util/array';
import { NumberArray } from '../../mol-util/type-helpers';
import { MeshExporter, AddMeshInput, MeshGeoData } from './mesh-exporter';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
const v3normalize = Vec3.normalize;
const v3toArray = Vec3.toArray;
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
const UNSIGNED_BYTE = 5121;
const UNSIGNED_INT = 5125;
const FLOAT = 5126;
const ARRAY_BUFFER = 34962;
const ELEMENT_ARRAY_BUFFER = 34963;
const GLTF_MAGIC_BYTE = 0x46546C67;
const JSON_CHUNK_TYPE = 0x4E4F534A;
const BIN_CHUNK_TYPE = 0x004E4942;
const JSON_PAD_CHAR = 0x20;
const BIN_PAD_CHAR = 0x00;
export type GlbData = {
glb: Uint8Array
}
export class GlbExporter extends MeshExporter<GlbData> {
readonly fileExtension = 'glb';
private nodes: Record<string, any>[] = [];
private meshes: Record<string, any>[] = [];
private materials: Record<string, any>[] = [];
private materialMap = new Map<string, number>();
private accessors: Record<string, any>[] = [];
private bufferViews: Record<string, any>[] = [];
private binaryBuffer: ArrayBuffer[] = [];
private byteOffset = 0;
private centerTransform: Mat4;
private static vec3MinMax(a: NumberArray) {
const min: number[] = [Infinity, Infinity, Infinity];
const max: number[] = [-Infinity, -Infinity, -Infinity];
for (let i = 0, il = a.length; i < il; i += 3) {
for (let j = 0; j < 3; ++j) {
min[j] = Math.min(a[i + j], min[j]);
max[j] = Math.max(a[i + j], max[j]);
}
}
return [min, max];
}
private addBuffer(buffer: ArrayBuffer, componentType: number, type: string, count: number, target: number, min?: any, max?: any, normalized?: boolean) {
this.binaryBuffer.push(buffer);
const bufferViewOffset = this.bufferViews.length;
this.bufferViews.push({
buffer: 0,
byteOffset: this.byteOffset,
byteLength: buffer.byteLength,
target
});
this.byteOffset += buffer.byteLength;
const accessorOffset = this.accessors.length;
this.accessors.push({
bufferView: bufferViewOffset,
byteOffset: 0,
componentType,
count,
type,
min,
max,
normalized
});
return accessorOffset;
}
private addGeometryBuffers(vertices: Float32Array, normals: Float32Array, indices: Uint32Array | undefined, vertexCount: number, drawCount: number, isGeoTexture: boolean) {
const tmpV = Vec3();
const stride = isGeoTexture ? 4 : 3;
const vertexArray = new Float32Array(vertexCount * 3);
const normalArray = new Float32Array(vertexCount * 3);
let indexArray: Uint32Array | undefined;
// position
for (let i = 0; i < vertexCount; ++i) {
v3fromArray(tmpV, vertices, i * stride);
v3toArray(tmpV, vertexArray, i * 3);
}
// normal
for (let i = 0; i < vertexCount; ++i) {
v3fromArray(tmpV, normals, i * stride);
v3normalize(tmpV, tmpV);
v3toArray(tmpV, normalArray, i * 3);
}
// face
if (!isGeoTexture) {
indexArray = indices!.slice(0, drawCount);
}
const [vertexMin, vertexMax] = GlbExporter.vec3MinMax(vertexArray);
let vertexBuffer = vertexArray.buffer;
let normalBuffer = normalArray.buffer;
let indexBuffer = isGeoTexture ? undefined : indexArray!.buffer;
if (!IsNativeEndianLittle) {
vertexBuffer = flipByteOrder(new Uint8Array(vertexBuffer), 4);
normalBuffer = flipByteOrder(new Uint8Array(normalBuffer), 4);
if (!isGeoTexture) indexBuffer = flipByteOrder(new Uint8Array(indexBuffer!), 4);
}
return {
vertexAccessorIndex: this.addBuffer(vertexBuffer, FLOAT, 'VEC3', vertexCount, ARRAY_BUFFER, vertexMin, vertexMax),
normalAccessorIndex: this.addBuffer(normalBuffer, FLOAT, 'VEC3', vertexCount, ARRAY_BUFFER),
indexAccessorIndex: isGeoTexture ? undefined : this.addBuffer(indexBuffer!, UNSIGNED_INT, 'SCALAR', drawCount, ELEMENT_ARRAY_BUFFER)
};
}
private addColorBuffer(geoData: MeshGeoData, interpolatedColors: Uint8Array | undefined, interpolatedOverpaint: Uint8Array | undefined, interpolatedTransparency: Uint8Array | undefined) {
const { values, vertexCount } = geoData;
const uAlpha = values.uAlpha.ref.value;
const colorArray = new Uint8Array(vertexCount * 4);
for (let i = 0; i < vertexCount; ++i) {
let color = GlbExporter.getColor(i, geoData, interpolatedColors, interpolatedOverpaint);
const transparency = GlbExporter.getTransparency(i, geoData, interpolatedTransparency);
const alpha = uAlpha * (1 - transparency);
color = Color.sRGBToLinear(color);
Color.toArray(color, colorArray, i * 4);
colorArray[i * 4 + 3] = Math.round(alpha * 255);
}
let colorBuffer = colorArray.buffer;
if (!IsNativeEndianLittle) {
colorBuffer = flipByteOrder(new Uint8Array(colorBuffer), 4);
}
return this.addBuffer(colorBuffer, UNSIGNED_BYTE, 'VEC4', vertexCount, ARRAY_BUFFER, undefined, undefined, true);
}
private addMaterial(metalness: number, roughness: number) {
const hash = `${metalness}|${roughness}`;
if (!this.materialMap.has(hash)) {
this.materialMap.set(hash, this.materials.length);
this.materials.push({
pbrMetallicRoughness: {
baseColorFactor: [1, 1, 1, 1],
metallicFactor: metalness,
roughnessFactor: roughness
}
});
}
return this.materialMap.get(hash)!;
}
protected async addMeshWithColors(input: AddMeshInput) {
const { mesh, values, isGeoTexture, webgl, ctx } = input;
const t = Mat4();
const colorType = values.dColorType.ref.value;
const overpaintType = values.dOverpaintType.ref.value;
const transparencyType = values.dTransparencyType.ref.value;
const dTransparency = values.dTransparency.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
const metalness = values.uMetalness.ref.value;
const roughness = values.uRoughness.ref.value;
const material = this.addMaterial(metalness, roughness);
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedColors = GlbExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
}
let interpolatedOverpaint: Uint8Array | undefined;
if (overpaintType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedOverpaint = GlbExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
}
let interpolatedTransparency: Uint8Array | undefined;
if (transparencyType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedTransparency = GlbExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
}
// instancing
const sameGeometryBuffers = mesh !== undefined;
const sameColorBuffer = sameGeometryBuffers && colorType !== 'instance' && !colorType.endsWith('Instance') && !dTransparency;
let vertexAccessorIndex: number;
let normalAccessorIndex: number;
let indexAccessorIndex: number | undefined;
let colorAccessorIndex: number;
let meshIndex: number;
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
if (ctx.shouldUpdate) await ctx.update({ current: instanceIndex + 1 });
// create a glTF mesh if needed
if (instanceIndex === 0 || !sameGeometryBuffers || !sameColorBuffer) {
const { vertices, normals, indices, groups, vertexCount, drawCount } = GlbExporter.getInstance(input, instanceIndex);
// create geometry buffers if needed
if (instanceIndex === 0 || !sameGeometryBuffers) {
const accessorIndices = this.addGeometryBuffers(vertices, normals, indices, vertexCount, drawCount, isGeoTexture);
vertexAccessorIndex = accessorIndices.vertexAccessorIndex;
normalAccessorIndex = accessorIndices.normalAccessorIndex;
indexAccessorIndex = accessorIndices.indexAccessorIndex;
}
// create a color buffer if needed
if (instanceIndex === 0 || !sameColorBuffer) {
colorAccessorIndex = this.addColorBuffer({ values, groups, vertexCount, instanceIndex, isGeoTexture }, interpolatedColors, interpolatedOverpaint, interpolatedTransparency);
}
// glTF mesh
meshIndex = this.meshes.length;
this.meshes.push({
primitives: [{
attributes: {
POSITION: vertexAccessorIndex!,
NORMAL: normalAccessorIndex!,
COLOR_0: colorAccessorIndex!
},
indices: indexAccessorIndex,
material
}]
});
}
// node
Mat4.fromArray(t, aTransform, instanceIndex * 16);
Mat4.mul(t, this.centerTransform, t);
const node: Record<string, any> = {
mesh: meshIndex!,
matrix: t.slice()
};
this.nodes.push(node);
}
}
async getData() {
const binaryBufferLength = this.byteOffset;
const gltf = {
asset: {
version: '2.0',
generator: `Mol* ${PLUGIN_VERSION}`
},
scenes: [{
nodes: fillSerial(new Array(this.nodes.length) as number[])
}],
nodes: this.nodes,
meshes: this.meshes,
buffers: [{
byteLength: binaryBufferLength,
}],
bufferViews: this.bufferViews,
accessors: this.accessors,
materials: this.materials
};
const createChunk = (chunkType: number, data: ArrayBuffer[], byteLength: number, padChar: number): [ArrayBuffer[], number] => {
let padding = null;
if (byteLength % 4 !== 0) {
const pad = 4 - (byteLength % 4);
byteLength += pad;
padding = new Uint8Array(pad);
padding.fill(padChar);
}
const preamble = new ArrayBuffer(8);
const preambleDataView = new DataView(preamble);
preambleDataView.setUint32(0, byteLength, true);
preambleDataView.setUint32(4, chunkType, true);
const chunk = [preamble, ...data];
if (padding) {
chunk.push(padding.buffer);
}
return [chunk, 8 + byteLength];
};
const jsonString = JSON.stringify(gltf);
const jsonBuffer = new Uint8Array(jsonString.length);
asciiWrite(jsonBuffer, jsonString);
const [jsonChunk, jsonChunkLength] = createChunk(JSON_CHUNK_TYPE, [jsonBuffer.buffer], jsonBuffer.length, JSON_PAD_CHAR);
const [binaryChunk, binaryChunkLength] = createChunk(BIN_CHUNK_TYPE, this.binaryBuffer, binaryBufferLength, BIN_PAD_CHAR);
const glbBufferLength = 12 + jsonChunkLength + binaryChunkLength;
const header = new ArrayBuffer(12);
const headerDataView = new DataView(header);
headerDataView.setUint32(0, GLTF_MAGIC_BYTE, true); // magic number "glTF"
headerDataView.setUint32(4, 2, true); // version
headerDataView.setUint32(8, glbBufferLength, true); // length
const glbBuffer = [header, ...jsonChunk, ...binaryChunk];
const glb = new Uint8Array(glbBufferLength);
let offset = 0;
for (const buffer of glbBuffer) {
glb.set(new Uint8Array(buffer), offset);
offset += buffer.byteLength;
}
return { glb };
}
async getBlob(ctx: RuntimeContext) {
return new Blob([(await this.getData()).glb], { type: 'model/gltf-binary' });
}
constructor(boundingBox: Box3D) {
super();
const tmpV = Vec3();
Vec3.add(tmpV, boundingBox.min, boundingBox.max);
Vec3.scale(tmpV, tmpV, -0.5);
this.centerTransform = Mat4.fromTranslation(Mat4(), tmpV);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { GeometryExporterUI } from './ui';
export const GeometryExport = PluginBehavior.create<{ }>({
name: 'extension-geo-export',
category: 'misc',
display: {
name: 'Geometry Export'
},
ctor: class extends PluginBehavior.Handler<{ }> {
register(): void {
this.ctx.customStructureControls.set('geo-export', GeometryExporterUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customStructureControls.delete('geo-export');
}
},
params: () => ({ })
});

View File

@@ -0,0 +1,485 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { sort, arraySwap } from '../../mol-data/util';
import { GraphicsRenderObject } from '../../mol-gl/render-object';
import { MeshValues } from '../../mol-gl/renderable/mesh';
import { LinesValues } from '../../mol-gl/renderable/lines';
import { PointsValues } from '../../mol-gl/renderable/points';
import { SpheresValues } from '../../mol-gl/renderable/spheres';
import { CylindersValues } from '../../mol-gl/renderable/cylinders';
import { TextureMeshValues } from '../../mol-gl/renderable/texture-mesh';
import { BaseValues, SizeValues } from '../../mol-gl/renderable/schema';
import { TextureImage } from '../../mol-gl/renderable/util';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { getTrilinearlyInterpolated } from '../../mol-geo/geometry/mesh/color-smoothing';
import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
import { sizeDataFactor } from '../../mol-geo/geometry/size-data';
import { Vec3 } from '../../mol-math/linear-algebra';
import { RuntimeContext } from '../../mol-task';
import { Color } from '../../mol-util/color/color';
import { unpackRGBToInt } from '../../mol-util/number-packing';
import { RenderObjectExporter, RenderObjectExportData } from './render-object-exporter';
import { readAlphaTexture, readTexture } from '../../mol-gl/compute/util';
const GeoExportName = 'geo-export';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
export interface AddMeshInput {
mesh: {
vertices: Float32Array
normals: Float32Array
indices: Uint32Array | undefined
groups: Float32Array | Uint8Array
vertexCount: number
drawCount: number
} | undefined
meshes: Mesh[] | undefined
values: BaseValues
isGeoTexture: boolean
webgl: WebGLContext | undefined
ctx: RuntimeContext
}
export type MeshGeoData = {
values: BaseValues,
groups: Float32Array | Uint8Array,
vertexCount: number,
instanceIndex: number,
isGeoTexture: boolean
}
export abstract class MeshExporter<D extends RenderObjectExportData> implements RenderObjectExporter<D> {
abstract readonly fileExtension: string;
private static getSizeFromTexture(tSize: TextureImage<Uint8Array>, i: number): number {
const r = tSize.array[i * 3];
const g = tSize.array[i * 3 + 1];
const b = tSize.array[i * 3 + 2];
return unpackRGBToInt(r, g, b) / sizeDataFactor;
}
private static getSize(values: BaseValues & SizeValues, instanceIndex: number, group: number): number {
const tSize = values.tSize.ref.value;
let size = 0;
switch (values.dSizeType.ref.value) {
case 'uniform':
size = values.uSize.ref.value;
break;
case 'instance':
size = MeshExporter.getSizeFromTexture(tSize, instanceIndex);
break;
case 'group':
size = MeshExporter.getSizeFromTexture(tSize, group);
break;
case 'groupInstance':
const groupCount = values.uGroupCount.ref.value;
size = MeshExporter.getSizeFromTexture(tSize, instanceIndex * groupCount + group);
break;
}
return size * values.uSizeFactor.ref.value;
}
protected static getGroup(groups: Float32Array | Uint8Array, i: number): number {
const i4 = i * 4;
const r = groups[i4];
const g = groups[i4 + 1];
const b = groups[i4 + 2];
if (groups instanceof Float32Array) {
return unpackRGBToInt(r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5);
}
return unpackRGBToInt(r, g, b);
}
protected static getInterpolatedColors(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volume' | 'volumeInstance' }) {
const { values, vertexCount, vertices, colorType, stride } = input;
const colorGridTransform = values.uColorGridTransform.ref.value;
const colorGridDim = values.uColorGridDim.ref.value;
const colorTexDim = values.uColorTexDim.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
const colorGrid = readTexture(webgl, values.tColorGrid.ref.value).array;
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4, outputStride: 3 });
return interpolated.array;
}
protected static getInterpolatedOverpaint(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
const { values, vertexCount, vertices, colorType, stride } = input;
const overpaintGridTransform = values.uOverpaintGridTransform.ref.value;
const overpaintGridDim = values.uOverpaintGridDim.ref.value;
const overpaintTexDim = values.uOverpaintTexDim.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
const overpaintGrid = readTexture(webgl, values.tOverpaintGrid.ref.value).array;
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: overpaintGrid, gridDim: overpaintGridDim, gridTexDim: overpaintTexDim, gridTransform: overpaintGridTransform, vertexStride: stride, colorStride: 4, outputStride: 4 });
return interpolated.array;
}
protected static getInterpolatedTransparency(webgl: WebGLContext, input: { vertices: Float32Array, vertexCount: number, values: BaseValues, stride: 3 | 4, colorType: 'volumeInstance' }) {
const { values, vertexCount, vertices, colorType, stride } = input;
const transparencyGridTransform = values.uTransparencyGridTransform.ref.value;
const transparencyGridDim = values.uTransparencyGridDim.ref.value;
const transparencyTexDim = values.uTransparencyTexDim.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
const transparencyGrid = readAlphaTexture(webgl, values.tTransparencyGrid.ref.value).array;
const interpolated = getTrilinearlyInterpolated({ vertexCount, instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType, grid: transparencyGrid, gridDim: transparencyGridDim, gridTexDim: transparencyTexDim, gridTransform: transparencyGridTransform, vertexStride: stride, colorStride: 4, outputStride: 1, itemOffset: 3 });
return interpolated.array;
}
protected static quantizeColors(colorArray: Uint8Array, vertexCount: number) {
if (vertexCount <= 1024) return;
const rgb = Vec3();
const min = Vec3();
const max = Vec3();
const sum = Vec3();
const colorMap = new Map<Color, Color>();
const colorComparers = [
(colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[0] - Color.toVec3(rgb, colors[j])[0]),
(colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[1] - Color.toVec3(rgb, colors[j])[1]),
(colors: Color[], i: number, j: number) => (Color.toVec3(rgb, colors[i])[2] - Color.toVec3(rgb, colors[j])[2]),
];
const medianCut = (colors: Color[], l: number, r: number, depth: number) => {
if (l > r) return;
if (l === r || depth >= 10) {
// Find the average color.
Vec3.set(sum, 0, 0, 0);
for (let i = l; i <= r; ++i) {
Color.toVec3(rgb, colors[i]);
Vec3.add(sum, sum, rgb);
}
Vec3.round(rgb, Vec3.scale(rgb, sum, 1 / (r - l + 1)));
const averageColor = Color.fromArray(rgb, 0);
for (let i = l; i <= r; ++i) colorMap.set(colors[i], averageColor);
return;
}
// Find the color channel with the greatest range.
Vec3.set(min, 255, 255, 255);
Vec3.set(max, 0, 0, 0);
for (let i = l; i <= r; ++i) {
Color.toVec3(rgb, colors[i]);
for (let j = 0; j < 3; ++j) {
Vec3.min(min, min, rgb);
Vec3.max(max, max, rgb);
}
}
let k = 0;
if (max[1] - min[1] > max[k] - min[k]) k = 1;
if (max[2] - min[2] > max[k] - min[k]) k = 2;
sort(colors, l, r + 1, colorComparers[k], arraySwap);
const m = (l + r) >> 1;
medianCut(colors, l, m, depth + 1);
medianCut(colors, m + 1, r, depth + 1);
};
// Create an array of unique colors and use the median cut algorithm.
const colorSet = new Set<Color>();
for (let i = 0; i < vertexCount; ++i) {
colorSet.add(Color.fromArray(colorArray, i * 3));
}
const colors = Array.from(colorSet);
medianCut(colors, 0, colors.length - 1, 0);
// Map actual colors to quantized colors.
for (let i = 0; i < vertexCount; ++i) {
const color = colorMap.get(Color.fromArray(colorArray, i * 3));
Color.toArray(color!, colorArray, i * 3);
}
}
protected static getInstance(input: AddMeshInput, instanceIndex: number) {
const { mesh, meshes } = input;
if (mesh !== undefined) {
return mesh;
} else {
const mesh = meshes![instanceIndex];
return {
vertices: mesh.vertexBuffer.ref.value,
normals: mesh.normalBuffer.ref.value,
indices: mesh.indexBuffer.ref.value,
groups: mesh.groupBuffer.ref.value,
vertexCount: mesh.vertexCount,
drawCount: mesh.triangleCount * 3
};
}
}
protected static getColor(vertexIndex: number, geoData: MeshGeoData, interpolatedColors?: Uint8Array, interpolatedOverpaint?: Uint8Array): Color {
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
const groupCount = values.uGroupCount.ref.value;
const colorType = values.dColorType.ref.value;
const uColor = values.uColor.ref.value;
const tColor = values.tColor.ref.value.array;
const overpaintType = values.dOverpaintType.ref.value;
const dOverpaint = values.dOverpaint.ref.value;
const tOverpaint = values.tOverpaint.ref.value.array;
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(uColor, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
color = Color.fromArray(tColor, group * 3);
break;
}
case 'groupInstance': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
}
case 'vertex':
color = Color.fromArray(tColor, vertexIndex * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * vertexCount + vertexIndex) * 3);
break;
case 'volume':
color = Color.fromArray(interpolatedColors!, vertexIndex * 3);
break;
case 'volumeInstance':
color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + vertexIndex) * 3);
break;
default: throw new Error('Unsupported color type.');
}
if (dOverpaint) {
let overpaintColor: Color;
let overpaintAlpha: number;
switch (overpaintType) {
case 'groupInstance': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
const idx = (instanceIndex * groupCount + group) * 4;
overpaintColor = Color.fromArray(tOverpaint, idx);
overpaintAlpha = tOverpaint[idx + 3] / 255;
break;
}
case 'vertexInstance': {
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
overpaintColor = Color.fromArray(tOverpaint, idx);
overpaintAlpha = tOverpaint[idx + 3] / 255;
break;
}
case 'volumeInstance': {
const idx = (instanceIndex * vertexCount + vertexIndex) * 4;
overpaintColor = Color.fromArray(interpolatedOverpaint!, idx);
overpaintAlpha = interpolatedOverpaint![idx + 3] / 255;
break;
}
default: throw new Error('Unsupported overpaint type.');
}
// interpolate twice to avoid darkening due to empty overpaint
overpaintColor = Color.interpolate(color, overpaintColor, overpaintAlpha);
color = Color.interpolate(color, overpaintColor, overpaintAlpha);
}
return color;
}
protected static getTransparency(vertexIndex: number, geoData: MeshGeoData, interpolatedTransparency?: Uint8Array): number {
const { values, instanceIndex, isGeoTexture, groups, vertexCount } = geoData;
const groupCount = values.uGroupCount.ref.value;
const dTransparency = values.dTransparency.ref.value;
const tTransparency = values.tTransparency.ref.value.array;
const transparencyType = values.dTransparencyType.ref.value;
let transparency: number = 0;
if (dTransparency) {
switch (transparencyType) {
case 'groupInstance': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
const idx = (instanceIndex * groupCount + group);
transparency = tTransparency[idx] / 255;
break;
}
case 'vertexInstance': {
const idx = (instanceIndex * vertexCount + vertexIndex);
transparency = tTransparency[idx] / 255;
break;
}
case 'volumeInstance': {
const idx = (instanceIndex * vertexCount + vertexIndex);
transparency = interpolatedTransparency![idx] / 255;
break;
}
default: throw new Error('Unsupported transparency type.');
}
}
return transparency;
}
protected abstract addMeshWithColors(input: AddMeshInput): void;
private async addMesh(values: MeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
const aPosition = values.aPosition.ref.value;
const aNormal = values.aNormal.ref.value;
const aGroup = values.aGroup.ref.value;
const originalData = Mesh.getOriginalData(values);
let indices: Uint32Array;
let vertexCount: number;
let drawCount: number;
if (originalData) {
indices = originalData.indexBuffer;
vertexCount = originalData.vertexCount;
drawCount = originalData.triangleCount * 3;
} else {
indices = values.elements.ref.value;
vertexCount = values.uVertexCount.ref.value;
drawCount = values.drawCount.ref.value;
}
await this.addMeshWithColors({ mesh: { vertices: aPosition, normals: aNormal, indices, groups: aGroup, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: false, webgl, ctx });
}
private async addLines(values: LinesValues, webgl: WebGLContext, ctx: RuntimeContext) {
// TODO
}
private async addPoints(values: PointsValues, webgl: WebGLContext, ctx: RuntimeContext) {
// TODO
}
private async addSpheres(values: SpheresValues, webgl: WebGLContext, ctx: RuntimeContext) {
const center = Vec3();
const aPosition = values.aPosition.ref.value;
const aGroup = values.aGroup.ref.value;
const instanceCount = values.instanceCount.ref.value;
const vertexCount = values.uVertexCount.ref.value;
const meshes: Mesh[] = [];
const sphereCount = vertexCount / 4 * instanceCount;
let detail: number;
if (sphereCount < 2000) detail = 3;
else if (sphereCount < 20000) detail = 2;
else detail = 1;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
const state = MeshBuilder.createState(512, 256);
for (let i = 0; i < vertexCount; i += 4) {
v3fromArray(center, aPosition, i * 3);
const group = aGroup[i];
const radius = MeshExporter.getSize(values, instanceIndex, group);
state.currentGroup = group;
addSphere(state, center, radius, detail);
}
meshes.push(MeshBuilder.getMesh(state));
}
await this.addMeshWithColors({ mesh: undefined, meshes, values, isGeoTexture: false, webgl, ctx });
}
private async addCylinders(values: CylindersValues, webgl: WebGLContext, ctx: RuntimeContext) {
const start = Vec3();
const end = Vec3();
const aStart = values.aStart.ref.value;
const aEnd = values.aEnd.ref.value;
const aScale = values.aScale.ref.value;
const aCap = values.aCap.ref.value;
const aGroup = values.aGroup.ref.value;
const instanceCount = values.instanceCount.ref.value;
const vertexCount = values.uVertexCount.ref.value;
const meshes: Mesh[] = [];
const cylinderCount = vertexCount / 6 * instanceCount;
let radialSegments: number;
if (cylinderCount < 2000) radialSegments = 36;
else if (cylinderCount < 20000) radialSegments = 24;
else radialSegments = 12;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
const state = MeshBuilder.createState(512, 256);
for (let i = 0; i < vertexCount; i += 6) {
v3fromArray(start, aStart, i * 3);
v3fromArray(end, aEnd, i * 3);
const group = aGroup[i];
const radius = MeshExporter.getSize(values, instanceIndex, group) * aScale[i];
const cap = aCap[i];
const topCap = cap === 1 || cap === 3;
const bottomCap = cap >= 2;
const cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap, bottomCap, radialSegments };
state.currentGroup = aGroup[i];
addCylinder(state, start, end, 1, cylinderProps);
}
meshes.push(MeshBuilder.getMesh(state));
}
await this.addMeshWithColors({ mesh: undefined, meshes, values, isGeoTexture: false, webgl, ctx });
}
private async addTextureMesh(values: TextureMeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
if (!webgl.namedFramebuffers[GeoExportName]) {
webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer();
}
const framebuffer = webgl.namedFramebuffers[GeoExportName];
const [width, height] = values.uGeoTexDim.ref.value;
const vertices = new Float32Array(width * height * 4);
const normals = new Float32Array(width * height * 4);
const groups = webgl.isWebGL2 ? new Uint8Array(width * height * 4) : new Float32Array(width * height * 4);
framebuffer.bind();
values.tPosition.ref.value.attachFramebuffer(framebuffer, 0);
webgl.readPixels(0, 0, width, height, vertices);
values.tNormal.ref.value.attachFramebuffer(framebuffer, 0);
webgl.readPixels(0, 0, width, height, normals);
values.tGroup.ref.value.attachFramebuffer(framebuffer, 0);
webgl.readPixels(0, 0, width, height, groups);
const vertexCount = values.uVertexCount.ref.value;
const drawCount = values.drawCount.ref.value;
await this.addMeshWithColors({ mesh: { vertices, normals, indices: undefined, groups, vertexCount, drawCount }, meshes: undefined, values, isGeoTexture: true, webgl, ctx });
}
add(renderObject: GraphicsRenderObject, webgl: WebGLContext, ctx: RuntimeContext) {
if (!renderObject.state.visible) return;
switch (renderObject.type) {
case 'mesh':
return this.addMesh(renderObject.values as MeshValues, webgl, ctx);
case 'lines':
return this.addLines(renderObject.values as LinesValues, webgl, ctx);
case 'points':
return this.addPoints(renderObject.values as PointsValues, webgl, ctx);
case 'spheres':
return this.addSpheres(renderObject.values as SpheresValues, webgl, ctx);
case 'cylinders':
return this.addCylinders(renderObject.values as CylindersValues, webgl, ctx);
case 'texture-mesh':
return this.addTextureMesh(renderObject.values as TextureMeshValues, webgl, ctx);
}
}
abstract getData(ctx: RuntimeContext): Promise<D>;
abstract getBlob(ctx: RuntimeContext): Promise<Blob>;
}

View File

@@ -0,0 +1,207 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { asciiWrite } from '../../mol-io/common/ascii';
import { Box3D } from '../../mol-math/geometry';
import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
import { RuntimeContext } from '../../mol-task';
import { StringBuilder } from '../../mol-util';
import { Color } from '../../mol-util/color/color';
import { zip } from '../../mol-util/zip/zip';
import { MeshExporter, AddMeshInput } from './mesh-exporter';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
const v3transformMat4 = Vec3.transformMat4;
const v3transformMat3 = Vec3.transformMat3;
const mat3directionTransform = Mat3.directionTransform;
// http://paulbourke.net/dataformats/obj/
// http://paulbourke.net/dataformats/mtl/
export type ObjData = {
obj: string
mtl: string
}
export class ObjExporter extends MeshExporter<ObjData> {
readonly fileExtension = 'zip';
private obj = StringBuilder.create();
private mtl = StringBuilder.create();
private vertexOffset = 0;
private currentColor: Color | undefined;
private currentAlpha: number | undefined;
private materialSet = new Set<string>();
private centerTransform: Mat4;
private updateMaterial(color: Color, alpha: number) {
if (this.currentColor === color && this.currentAlpha === alpha) return;
this.currentColor = color;
this.currentAlpha = alpha;
const material = Color.toHexString(color) + alpha;
StringBuilder.writeSafe(this.obj, `usemtl ${material}`);
StringBuilder.newline(this.obj);
if (!this.materialSet.has(material)) {
this.materialSet.add(material);
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
const mtl = this.mtl;
StringBuilder.writeSafe(mtl, `newmtl ${material}\n`);
StringBuilder.writeSafe(mtl, 'illum 2\n'); // illumination model
StringBuilder.writeSafe(mtl, 'Ns 163\n'); // specular exponent
StringBuilder.writeSafe(mtl, 'Ni 0.001\n'); // optical density a.k.a. index of refraction
StringBuilder.writeSafe(mtl, 'Ka 0 0 0\n'); // ambient reflectivity
StringBuilder.writeSafe(mtl, 'Kd '); // diffuse reflectivity
StringBuilder.writeFloat(mtl, r, 1000);
StringBuilder.whitespace1(mtl);
StringBuilder.writeFloat(mtl, g, 1000);
StringBuilder.whitespace1(mtl);
StringBuilder.writeFloat(mtl, b, 1000);
StringBuilder.newline(mtl);
StringBuilder.writeSafe(mtl, 'Ks 0.25 0.25 0.25\n'); // specular reflectivity
StringBuilder.writeSafe(mtl, 'd '); // dissolve
StringBuilder.writeFloat(mtl, alpha, 1000);
StringBuilder.newline(mtl);
}
}
protected async addMeshWithColors(input: AddMeshInput) {
const { mesh, values, isGeoTexture, webgl, ctx } = input;
const obj = this.obj;
const t = Mat4();
const n = Mat3();
const tmpV = Vec3();
const stride = isGeoTexture ? 4 : 3;
const colorType = values.dColorType.ref.value;
const overpaintType = values.dOverpaintType.ref.value;
const transparencyType = values.dTransparencyType.ref.value;
const uAlpha = values.uAlpha.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
interpolatedColors = ObjExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
}
let interpolatedOverpaint: Uint8Array | undefined;
if (overpaintType === 'volumeInstance') {
interpolatedOverpaint = ObjExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
}
let interpolatedTransparency: Uint8Array | undefined;
if (transparencyType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedTransparency = ObjExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
}
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
if (ctx.shouldUpdate) await ctx.update({ current: instanceIndex + 1 });
const { vertices, normals, indices, groups, vertexCount, drawCount } = ObjExporter.getInstance(input, instanceIndex);
Mat4.fromArray(t, aTransform, instanceIndex * 16);
Mat4.mul(t, this.centerTransform, t);
mat3directionTransform(n, t);
// position
for (let i = 0; i < vertexCount; ++i) {
v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * stride), t);
StringBuilder.writeSafe(obj, 'v ');
StringBuilder.writeFloat(obj, tmpV[0], 1000);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[1], 1000);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[2], 1000);
StringBuilder.newline(obj);
}
// normal
for (let i = 0; i < vertexCount; ++i) {
v3transformMat3(tmpV, v3fromArray(tmpV, normals, i * stride), n);
StringBuilder.writeSafe(obj, 'vn ');
StringBuilder.writeFloat(obj, tmpV[0], 100);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[1], 100);
StringBuilder.whitespace1(obj);
StringBuilder.writeFloat(obj, tmpV[2], 100);
StringBuilder.newline(obj);
}
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
// color
const quantizedColors = new Uint8Array(drawCount * 3);
for (let i = 0; i < drawCount; i += 3) {
const v = isGeoTexture ? i : indices![i];
const color = ObjExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
Color.toArray(color, quantizedColors, i);
}
ObjExporter.quantizeColors(quantizedColors, vertexCount);
// face
for (let i = 0; i < drawCount; i += 3) {
const color = Color.fromArray(quantizedColors, i);
const transparency = ObjExporter.getTransparency(i, geoData, interpolatedTransparency);
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
this.updateMaterial(color, alpha);
const v1 = this.vertexOffset + (isGeoTexture ? i : indices![i]) + 1;
const v2 = this.vertexOffset + (isGeoTexture ? i + 1 : indices![i + 1]) + 1;
const v3 = this.vertexOffset + (isGeoTexture ? i + 2 : indices![i + 2]) + 1;
StringBuilder.writeSafe(obj, 'f ');
StringBuilder.writeInteger(obj, v1);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeIntegerAndSpace(obj, v1);
StringBuilder.writeInteger(obj, v2);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeIntegerAndSpace(obj, v2);
StringBuilder.writeInteger(obj, v3);
StringBuilder.writeSafe(obj, '//');
StringBuilder.writeInteger(obj, v3);
StringBuilder.newline(obj);
}
this.vertexOffset += vertexCount;
}
}
async getData() {
return {
obj: StringBuilder.getString(this.obj),
mtl: StringBuilder.getString(this.mtl)
};
}
async getBlob(ctx: RuntimeContext) {
const { obj, mtl } = await this.getData();
const objData = new Uint8Array(obj.length);
asciiWrite(objData, obj);
const mtlData = new Uint8Array(mtl.length);
asciiWrite(mtlData, mtl);
const zipDataObj = {
[this.filename + '.obj']: objData,
[this.filename + '.mtl']: mtlData
};
return new Blob([await zip(ctx, zipDataObj)], { type: 'application/zip' });
}
constructor(private filename: string, boundingBox: Box3D) {
super();
StringBuilder.writeSafe(this.obj, `mtllib ${filename}.mtl\n`);
const tmpV = Vec3();
Vec3.add(tmpV, boundingBox.min, boundingBox.max);
Vec3.scale(tmpV, tmpV, -0.5);
this.centerTransform = Mat4.fromTranslation(Mat4(), tmpV);
}
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { GraphicsRenderObject } from '../../mol-gl/render-object';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { RuntimeContext } from '../../mol-task';
export type RenderObjectExportData = {
[k: string]: string | Uint8Array | ArrayBuffer | undefined
}
export interface RenderObjectExporter<D extends RenderObjectExportData> {
readonly fileExtension: string
add(renderObject: GraphicsRenderObject, webgl: WebGLContext, ctx: RuntimeContext): Promise<void> | undefined
getData(ctx: RuntimeContext): Promise<D>
getBlob(ctx: RuntimeContext): Promise<Blob>
}

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { asciiWrite } from '../../mol-io/common/ascii';
import { Box3D } from '../../mol-math/geometry';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { PLUGIN_VERSION } from '../../mol-plugin/version';
import { RuntimeContext } from '../../mol-task';
import { MeshExporter, AddMeshInput } from './mesh-exporter';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
const v3transformMat4 = Vec3.transformMat4;
const v3triangleNormal = Vec3.triangleNormal;
const v3toArray = Vec3.toArray;
// https://www.fabbers.com/tech/STL_Format
export type StlData = {
stl: Uint8Array
}
export class StlExporter extends MeshExporter<StlData> {
readonly fileExtension = 'stl';
private triangleBuffers: ArrayBuffer[] = [];
private triangleCount = 0;
private centerTransform: Mat4;
protected async addMeshWithColors(input: AddMeshInput) {
const { values, isGeoTexture, ctx } = input;
const t = Mat4();
const tmpV = Vec3();
const v1 = Vec3();
const v2 = Vec3();
const v3 = Vec3();
const stride = isGeoTexture ? 4 : 3;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
if (ctx.shouldUpdate) await ctx.update({ current: instanceIndex + 1 });
const { vertices, indices, vertexCount, drawCount } = StlExporter.getInstance(input, instanceIndex);
Mat4.fromArray(t, aTransform, instanceIndex * 16);
Mat4.mul(t, this.centerTransform, t);
// position
const vertexArray = new Float32Array(vertexCount * 3);
for (let i = 0; i < vertexCount; ++i) {
v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * stride), t);
v3toArray(tmpV, vertexArray, i * 3);
}
// face
const triangleBuffer = new ArrayBuffer(50 * drawCount);
const dataView = new DataView(triangleBuffer);
for (let i = 0; i < drawCount; i += 3) {
v3fromArray(v1, vertexArray, (isGeoTexture ? i : indices![i]) * 3);
v3fromArray(v2, vertexArray, (isGeoTexture ? i + 1 : indices![i + 1]) * 3);
v3fromArray(v3, vertexArray, (isGeoTexture ? i + 2 : indices![i + 2]) * 3);
v3triangleNormal(tmpV, v1, v2, v3);
const byteOffset = 50 * i;
dataView.setFloat32(byteOffset, tmpV[0], true);
dataView.setFloat32(byteOffset + 4, tmpV[1], true);
dataView.setFloat32(byteOffset + 8, tmpV[2], true);
dataView.setFloat32(byteOffset + 12, v1[0], true);
dataView.setFloat32(byteOffset + 16, v1[1], true);
dataView.setFloat32(byteOffset + 20, v1[2], true);
dataView.setFloat32(byteOffset + 24, v2[0], true);
dataView.setFloat32(byteOffset + 28, v2[1], true);
dataView.setFloat32(byteOffset + 32, v2[2], true);
dataView.setFloat32(byteOffset + 36, v3[0], true);
dataView.setFloat32(byteOffset + 40, v3[1], true);
dataView.setFloat32(byteOffset + 44, v3[2], true);
}
this.triangleBuffers.push(triangleBuffer);
this.triangleCount += drawCount;
}
}
async getData() {
const stl = new Uint8Array(84 + 50 * this.triangleCount);
asciiWrite(stl, `Exported from Mol* ${PLUGIN_VERSION}`);
const dataView = new DataView(stl.buffer);
dataView.setUint32(80, this.triangleCount, true);
let byteOffset = 84;
for (const buffer of this.triangleBuffers) {
stl.set(new Uint8Array(buffer), byteOffset);
byteOffset += buffer.byteLength;
}
return { stl };
}
async getBlob(ctx: RuntimeContext) {
return new Blob([(await this.getData()).stl], { type: 'model/stl' });
}
constructor(boundingBox: Box3D) {
super();
const tmpV = Vec3();
Vec3.add(tmpV, boundingBox.min, boundingBox.max);
Vec3.scale(tmpV, tmpV, -0.5);
this.centerTransform = Mat4.fromTranslation(Mat4(), tmpV);
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
*/
import { merge } from 'rxjs';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { GetAppSvg, CubeScanSvg, CubeSendSvg } from '../../mol-plugin-ui/controls/icons';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { download } from '../../mol-util/download';
import { GeometryParams, GeometryControls } from './controls';
interface State {
busy?: boolean
}
export class GeometryExporterUI extends CollapsableControls<{}, State> {
private _controls: GeometryControls | undefined;
private isARSupported: boolean | undefined;
get controls() {
return this._controls || (this._controls = new GeometryControls(this.plugin));
}
protected defaultState(): State & CollapsableState {
return {
header: 'Export Geometry',
isCollapsed: true,
brand: { accent: 'cyan', svg: CubeSendSvg }
};
}
protected renderControls(): JSX.Element {
if (this.isARSupported === undefined) {
this.isARSupported = !!document.createElement('a').relList?.supports?.('ar');
}
const ctrl = this.controls;
return <>
<ParameterControls
params={GeometryParams}
values={ctrl.behaviors.params.value}
onChangeValues={xs => ctrl.behaviors.params.next(xs)}
isDisabled={this.state.busy}
/>
<Button icon={GetAppSvg}
onClick={this.save} style={{ marginTop: 1 }}
disabled={this.state.busy || !this.plugin.canvas3d?.reprCount.value}>
Save
</Button>
{this.isARSupported && ctrl.behaviors.params.value.format === 'usdz' &&
<Button icon={CubeScanSvg}
onClick={this.viewInAR} style={{ marginTop: 1 }}
disabled={this.state.busy || !this.plugin.canvas3d?.reprCount.value}>
View in AR
</Button>
}
</>;
}
componentDidMount() {
const merged = merge(
this.controls.behaviors.params,
this.plugin.canvas3d!.reprCount
);
this.subscribe(merged, () => {
if (!this.state.isCollapsed) this.forceUpdate();
});
}
componentWillUnmount() {
this._controls?.dispose();
this._controls = void 0;
}
save = async () => {
try {
this.setState({ busy: true });
const data = await this.controls.exportGeometry();
download(data.blob, data.filename);
} catch (e) {
console.error(e);
} finally {
this.setState({ busy: false });
}
};
viewInAR = async () => {
try {
this.setState({ busy: true });
const data = await this.controls.exportGeometry();
const a = document.createElement('a');
a.rel = 'ar';
a.href = URL.createObjectURL(data.blob);
// For in-place viewing of USDZ on iOS, the link must contain a single child that is either an img or picture.
// https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/
a.appendChild(document.createElement('img'));
setTimeout(() => URL.revokeObjectURL(a.href), 4E4); // 40s
setTimeout(() => a.dispatchEvent(new MouseEvent('click')));
} catch (e) {
console.error(e);
} finally {
this.setState({ busy: false });
}
};
}

View File

@@ -0,0 +1,247 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { asciiWrite } from '../../mol-io/common/ascii';
import { Box3D } from '../../mol-math/geometry';
import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
import { PLUGIN_VERSION } from '../../mol-plugin/version';
import { RuntimeContext } from '../../mol-task';
import { StringBuilder } from '../../mol-util';
import { Color } from '../../mol-util/color/color';
import { zip } from '../../mol-util/zip/zip';
import { MeshExporter, AddMeshInput } from './mesh-exporter';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3fromArray = Vec3.fromArray;
const v3transformMat4 = Vec3.transformMat4;
const v3transformMat3 = Vec3.transformMat3;
const mat3directionTransform = Mat3.directionTransform;
// https://graphics.pixar.com/usd/docs/index.html
export type UsdzData = {
usdz: ArrayBuffer
}
export class UsdzExporter extends MeshExporter<UsdzData> {
readonly fileExtension = 'usdz';
private meshes: string[] = [];
private materials: string[] = [];
private materialMap = new Map<string, number>();
private centerTransform: Mat4;
private addMaterial(color: Color, alpha: number, metalness: number, roughness: number): number {
const hash = `${color}|${alpha}|${metalness}|${roughness}`;
if (this.materialMap.has(hash)) return this.materialMap.get(hash)!;
const materialKey = this.materialMap.size;
this.materialMap.set(hash, materialKey);
const [r, g, b] = Color.toRgbNormalized(color).map(v => Math.round(v * 1000) / 1000);
this.materials.push(`
def Material "material${materialKey}"
{
token outputs:surface.connect = </material${materialKey}/shader.outputs:surface>
def Shader "shader"
{
uniform token info:id = "UsdPreviewSurface"
color3f inputs:diffuseColor = (${r},${g},${b})
float inputs:opacity = ${alpha}
float inputs:metallic = ${metalness}
float inputs:roughness = ${roughness}
token outputs:surface
}
}
`);
return materialKey;
}
protected async addMeshWithColors(input: AddMeshInput) {
const { mesh, values, isGeoTexture, webgl, ctx } = input;
const t = Mat4();
const n = Mat3();
const tmpV = Vec3();
const stride = isGeoTexture ? 4 : 3;
const colorType = values.dColorType.ref.value;
const overpaintType = values.dOverpaintType.ref.value;
const transparencyType = values.dTransparencyType.ref.value;
const uAlpha = values.uAlpha.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
const metalness = values.uMetalness.ref.value;
const roughness = values.uRoughness.ref.value;
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
interpolatedColors = UsdzExporter.getInterpolatedColors(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType });
}
let interpolatedOverpaint: Uint8Array | undefined;
if (overpaintType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedOverpaint = UsdzExporter.getInterpolatedOverpaint(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: overpaintType });
}
let interpolatedTransparency: Uint8Array | undefined;
if (transparencyType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedTransparency = UsdzExporter.getInterpolatedTransparency(webgl!, { vertices: mesh!.vertices, vertexCount: mesh!.vertexCount, values, stride, colorType: transparencyType });
}
await ctx.update({ isIndeterminate: false, current: 0, max: instanceCount });
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
if (ctx.shouldUpdate) await ctx.update({ current: instanceIndex + 1 });
const { vertices, normals, indices, groups, vertexCount, drawCount } = UsdzExporter.getInstance(input, instanceIndex);
Mat4.fromArray(t, aTransform, instanceIndex * 16);
Mat4.mul(t, this.centerTransform, t);
mat3directionTransform(n, t);
const vertexBuilder = StringBuilder.create();
const normalBuilder = StringBuilder.create();
const indexBuilder = StringBuilder.create();
// position
for (let i = 0; i < vertexCount; ++i) {
v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * stride), t);
StringBuilder.writeSafe(vertexBuilder, (i === 0) ? '(' : ',(');
StringBuilder.writeFloat(vertexBuilder, tmpV[0], 10000);
StringBuilder.writeSafe(vertexBuilder, ',');
StringBuilder.writeFloat(vertexBuilder, tmpV[1], 10000);
StringBuilder.writeSafe(vertexBuilder, ',');
StringBuilder.writeFloat(vertexBuilder, tmpV[2], 10000);
StringBuilder.writeSafe(vertexBuilder, ')');
}
// normal
for (let i = 0; i < vertexCount; ++i) {
v3transformMat3(tmpV, v3fromArray(tmpV, normals, i * stride), n);
StringBuilder.writeSafe(normalBuilder, (i === 0) ? '(' : ',(');
StringBuilder.writeFloat(normalBuilder, tmpV[0], 100);
StringBuilder.writeSafe(normalBuilder, ',');
StringBuilder.writeFloat(normalBuilder, tmpV[1], 100);
StringBuilder.writeSafe(normalBuilder, ',');
StringBuilder.writeFloat(normalBuilder, tmpV[2], 100);
StringBuilder.writeSafe(normalBuilder, ')');
}
const geoData = { values, groups, vertexCount, instanceIndex, isGeoTexture };
// face
for (let i = 0; i < drawCount; ++i) {
const v = isGeoTexture ? i : indices![i];
if (i > 0) StringBuilder.writeSafe(indexBuilder, ',');
StringBuilder.writeInteger(indexBuilder, v);
}
// color
const quantizedColors = new Uint8Array(drawCount * 3);
for (let i = 0; i < drawCount; i += 3) {
const v = isGeoTexture ? i : indices![i];
const color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
Color.toArray(color, quantizedColors, i);
}
UsdzExporter.quantizeColors(quantizedColors, vertexCount);
// material
const faceIndicesByMaterial = new Map<number, number[]>();
for (let i = 0; i < drawCount; i += 3) {
const color = Color.fromArray(quantizedColors, i);
const transparency = UsdzExporter.getTransparency(i, geoData, interpolatedTransparency);
const alpha = Math.round(uAlpha * (1 - transparency) * 10) / 10; // quantized
const materialKey = this.addMaterial(color, alpha, metalness, roughness);
let faceIndices = faceIndicesByMaterial.get(materialKey);
if (faceIndices === undefined) {
faceIndices = [];
faceIndicesByMaterial.set(materialKey, faceIndices);
}
faceIndices.push(i / 3);
}
// If this mesh uses only one material, bind it to the material directly.
// Otherwise, use GeomSubsets to bind it to multiple materials.
let materialBinding: string;
if (faceIndicesByMaterial.size === 1) {
const materialKey = faceIndicesByMaterial.keys().next().value;
materialBinding = `rel material:binding = </material${materialKey}>`;
} else {
const geomSubsets: string[] = [];
faceIndicesByMaterial.forEach((faceIndices: number[], materialKey: number) => {
geomSubsets.push(`
def GeomSubset "g${materialKey}"
{
uniform token elementType = "face"
uniform token familyName = "materialBind"
int[] indices = [${faceIndices.join(',')}]
rel material:binding = </material${materialKey}>
}
`);
});
materialBinding = geomSubsets.join('');
}
this.meshes.push(`
def Mesh "mesh${this.meshes.length}"
{
int[] faceVertexCounts = [${new Array(drawCount / 3).fill(3).join(',')}]
int[] faceVertexIndices = [${StringBuilder.getString(indexBuilder)}]
point3f[] points = [${StringBuilder.getString(vertexBuilder)}]
normal3f[] primvars:normals = [${StringBuilder.getString(normalBuilder)}] (
interpolation = "vertex"
)
uniform token subdivisionScheme = "none"
${materialBinding}
}
`);
}
}
async getData(ctx: RuntimeContext) {
const header = `#usda 1.0
(
customLayerData = {
string creator = "Mol* ${PLUGIN_VERSION}"
}
metersPerUnit = 1
)
`;
const usda = [header, ...this.materials, ...this.meshes].join('');
const usdaData = new Uint8Array(usda.length);
asciiWrite(usdaData, usda);
const zipDataObj = {
['model.usda']: usdaData
};
return {
usdz: await zip(ctx, zipDataObj, true)
};
}
async getBlob(ctx: RuntimeContext) {
const { usdz } = await this.getData(ctx);
return new Blob([usdz], { type: 'model/vnd.usdz+zip' });
}
constructor(boundingBox: Box3D, radius: number) {
super();
const t = Mat4();
// scale the model so that it fits within 1 meter
Mat4.fromUniformScaling(t, Math.min(1 / (radius * 2), 1));
// translate the model so that it sits on the ground plane (y = 0)
Mat4.translate(t, t, Vec3.create(
-(boundingBox.min[0] + boundingBox.max[0]) / 2,
-boundingBox.min[1],
-(boundingBox.min[2] + boundingBox.max[2]) / 2
));
this.centerTransform = t;
}
}

View File

@@ -0,0 +1,212 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
import { Loci } from '../../../mol-model/loci';
import { DefaultQueryRuntimeTable } from '../../../mol-script/runtime/query/compiler';
import { PLDDTConfidenceColorThemeProvider } from './color/plddt';
import { QualityAssessment, QualityAssessmentProvider } from './prop';
import { StructureSelectionCategory, StructureSelectionQuery } from '../../../mol-plugin-state/helpers/structure-selection-query';
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
import { OrderedSet } from '../../../mol-data/int';
import { cantorPairing } from '../../../mol-data/util';
import { QmeanScoreColorThemeProvider } from './color/qmean';
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../../mol-plugin-state/builder/structure/representation-preset';
import { StateObjectRef } from '../../../mol-state';
export const MAQualityAssessment = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'ma-quality-assessment-prop',
category: 'custom-props',
display: {
name: 'Quality Assessment',
description: 'Data included in Model Archive files.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = QualityAssessmentProvider;
private labelProvider = {
label: (loci: Loci): string | undefined => {
if (!this.params.showTooltip) return;
return [
plddtLabel(loci),
qmeanLabel(loci),
].filter(l => !!l).join('</br>');
}
};
register(): void {
DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.managers.lociLabels.addProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(PLDDTConfidenceColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(QmeanScoreColorThemeProvider);
this.ctx.query.structure.registry.add(confidentPLDDT);
this.ctx.builders.structure.representation.registerPreset(QualityAssessmentPLDDTPreset);
this.ctx.builders.structure.representation.registerPreset(QualityAssessmentQmeanPreset);
}
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.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.managers.lociLabels.removeProvider(this.labelProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(PLDDTConfidenceColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(QmeanScoreColorThemeProvider);
this.ctx.query.structure.registry.remove(confidentPLDDT);
this.ctx.builders.structure.representation.unregisterPreset(QualityAssessmentPLDDTPreset);
this.ctx.builders.structure.representation.unregisterPreset(QualityAssessmentQmeanPreset);
}
},
params: () => ({
autoAttach: PD.Boolean(false),
showTooltip: PD.Boolean(true),
})
});
//
function plddtCategory(score: number) {
if (score > 50 && score <= 70) return 'Low';
if (score > 70 && score <= 90) return 'Confident';
if (score > 90) return 'Very high';
return 'Very low';
}
function plddtLabel(loci: Loci): string | undefined {
return metricLabel(loci, 'pLDDT', (scoreAvg: number, countInfo: string) => `pLDDT Score ${countInfo}: ${scoreAvg.toFixed(2)} <small>(${plddtCategory(scoreAvg)})</small>`);
}
function qmeanLabel(loci: Loci): string | undefined {
return metricLabel(loci, 'qmean', (scoreAvg: number, countInfo: string) => `QMEAN Score ${countInfo}: ${scoreAvg.toFixed(2)}`);
}
function metricLabel(loci: Loci, name: 'qmean' | 'pLDDT', label: (scoreAvg: number, countInfo: string) => string): string | undefined {
if (loci.kind === 'element-loci') {
if (loci.elements.length === 0) return;
const seen = new Set<number>();
const scoreSeen = new Set<number>();
let scoreSum = 0;
for (const { indices, unit } of loci.elements) {
const metric = QualityAssessmentProvider.get(unit.model).value?.[name];
if (!metric) continue;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const { elements } = unit;
OrderedSet.forEach(indices, idx => {
const eI = elements[idx];
const rI = residueIndex[eI];
const residueKey = cantorPairing(rI, unit.id);
if (!seen.has(residueKey)) {
const score = metric.get(residueIndex[eI]) ?? -1;
if (score !== -1) {
scoreSum += score;
scoreSeen.add(residueKey);
}
seen.add(residueKey);
}
});
}
if (seen.size === 0) return;
const summary: string[] = [];
if (scoreSeen.size) {
const countInfo = `<small>(${scoreSeen.size} ${scoreSeen.size > 1 ? 'Residues avg.' : 'Residue'})</small>`;
const scoreAvg = scoreSum / scoreSeen.size;
summary.push(label(scoreAvg, countInfo));
}
if (summary.length) {
return summary.join('</br>');
}
}
}
//
const confidentPLDDT = StructureSelectionQuery('Confident pLDDT (> 70)', 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']),
'residue-test': MS.core.rel.gr([QualityAssessment.symbols.pLDDT.symbol(), 70]),
})
])
])
]), {
description: 'Select residues with a pLDDT > 70 (confident).',
category: StructureSelectionCategory.Validation,
ensureCustomProperties: async (ctx, structure) => {
for (const m of structure.models) {
await QualityAssessmentProvider.attach(ctx, m, void 0, true);
}
}
});
//
export const QualityAssessmentPLDDTPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-ma-quality-assessment-plddt',
display: {
name: 'Quality Assessment (pLDDT)', group: 'Annotation',
description: 'Color structure based on pLDDT Confidence.'
},
isApplicable(a) {
return !!a.data.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT'));
},
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 {};
const colorTheme = PLDDTConfidenceColorThemeProvider.name as any;
return await PresetStructureRepresentations.auto.apply(ref, { ...params, theme: { globalName: colorTheme, focus: { name: colorTheme } } }, plugin);
}
});
export const QualityAssessmentQmeanPreset = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-ma-quality-assessment-qmean',
display: {
name: 'Quality Assessment (QMEAN)', group: 'Annotation',
description: 'Color structure based on QMEAN Score.'
},
isApplicable(a) {
return !!a.data.models.some(m => QualityAssessment.isApplicable(m, 'qmean'));
},
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 {};
const colorTheme = QmeanScoreColorThemeProvider.name as any;
return await PresetStructureRepresentations.auto.apply(ref, { ...params, theme: { globalName: colorTheme, focus: { name: colorTheme } } }, plugin);
}
});

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Mandar Deshpande <mandar@ebi.ac.uk>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { QualityAssessment, QualityAssessmentProvider } from '../prop';
import { Location } from '../../../../mol-model/location';
import { Bond, StructureElement, Unit } from '../../../../mol-model/structure';
import { ColorTheme, LocationColor } 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 { CustomProperty } from '../../../../mol-model-props/common/custom-property';
import { TableLegend } from '../../../../mol-util/legend';
const DefaultColor = Color(0xaaaaaa);
const ConfidenceColors = {
'No Score': DefaultColor,
'Very Low': Color(0xff7d45),
'Low': Color(0xffdb13),
'Confident': Color(0x65cbf3),
'Very High': Color(0x0053d6)
};
const ConfidenceColorLegend = TableLegend(Object.entries(ConfidenceColors));
export function getPLDDTConfidenceColorThemeParams(ctx: ThemeDataContext) {
return {};
}
export type PLDDTConfidenceColorThemeParams = ReturnType<typeof getPLDDTConfidenceColorThemeParams>
export function PLDDTConfidenceColorTheme(ctx: ThemeDataContext, props: PD.Values<PLDDTConfidenceColorThemeParams>): ColorTheme<PLDDTConfidenceColorThemeParams> {
let color: LocationColor = () => DefaultColor;
if (ctx.structure) {
const l = StructureElement.Location.create(ctx.structure.root);
const getColor = (location: StructureElement.Location): Color => {
const { unit, element } = location;
if (!Unit.isAtomic(unit)) return DefaultColor;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
const score = qualityAssessment?.pLDDT?.get(unit.model.atomicHierarchy.residueAtomSegments.index[element]) ?? -1;
if (score < 0) {
return DefaultColor;
} else if (score <= 50) {
return Color(0xff7d45);
} else if (score <= 70) {
return Color(0xffdb13);
} else if (score <= 90) {
return Color(0x65cbf3);
} else {
return Color(0x0053d6);
}
};
color = (location: Location) => {
if (StructureElement.Location.is(location)) {
return getColor(location);
} else if (Bond.isLocation(location)) {
l.unit = location.aUnit;
l.element = location.aUnit.elements[location.aIndex];
return getColor(l);
}
return DefaultColor;
};
}
return {
factory: PLDDTConfidenceColorTheme,
granularity: 'group',
preferSmoothing: true,
color,
props,
description: 'Assigns residue colors according to the pLDDT Confidence score.',
legend: ConfidenceColorLegend
};
}
export const PLDDTConfidenceColorThemeProvider: ColorTheme.Provider<PLDDTConfidenceColorThemeParams, 'plddt-confidence'> = {
name: 'plddt-confidence',
label: 'pLDDT Confidence',
category: ColorTheme.Category.Validation,
factory: PLDDTConfidenceColorTheme,
getParams: getPLDDTConfidenceColorThemeParams,
defaultValues: PD.getDefaultValues(getPLDDTConfidenceColorThemeParams({})),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure?.models.some(m => QualityAssessment.isApplicable(m, 'pLDDT')),
ensureCustomProperties: {
attach: async (ctx: CustomProperty.Context, data: ThemeDataContext) => {
if (data.structure) {
for (const m of data.structure.models) {
await QualityAssessmentProvider.attach(ctx, m, void 0, true);
}
}
},
detach: async (data: ThemeDataContext) => {
if (data.structure) {
for (const m of data.structure.models) {
QualityAssessmentProvider.ref(m, false);
}
}
}
}
};

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { QualityAssessment, QualityAssessmentProvider } from '../prop';
import { Location } from '../../../../mol-model/location';
import { Bond, StructureElement, Unit } from '../../../../mol-model/structure';
import { ColorTheme, LocationColor } from '../../../../mol-theme/color';
import { ThemeDataContext } from '../../../../mol-theme/theme';
import { Color, ColorScale } from '../../../../mol-util/color';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { CustomProperty } from '../../../../mol-model-props/common/custom-property';
const DefaultColor = Color(0xaaaaaa);
export function getQmeanScoreColorThemeParams(ctx: ThemeDataContext) {
return {};
}
export type QmeanScoreColorThemeParams = ReturnType<typeof getQmeanScoreColorThemeParams>
export function QmeanScoreColorTheme(ctx: ThemeDataContext, props: PD.Values<QmeanScoreColorThemeParams>): ColorTheme<QmeanScoreColorThemeParams> {
let color: LocationColor = () => DefaultColor;
const scale = ColorScale.create({
domain: [0, 1],
listOrName: [
[Color(0xFF5000), 0.5], [Color(0x025AFD), 1.0]
]
});
if (ctx.structure) {
const l = StructureElement.Location.create(ctx.structure.root);
const getColor = (location: StructureElement.Location): Color => {
const { unit, element } = location;
if (!Unit.isAtomic(unit)) return DefaultColor;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
const score = qualityAssessment?.qmean?.get(unit.model.atomicHierarchy.residueAtomSegments.index[element]) ?? -1;
if (score < 0) {
return DefaultColor;
} else {
return scale.color(score);
}
};
color = (location: Location) => {
if (StructureElement.Location.is(location)) {
return getColor(location);
} else if (Bond.isLocation(location)) {
l.unit = location.aUnit;
l.element = location.aUnit.elements[location.aIndex];
return getColor(l);
}
return DefaultColor;
};
}
return {
factory: QmeanScoreColorTheme,
granularity: 'group',
preferSmoothing: true,
color,
props,
description: 'Assigns residue colors according to the QMEAN score.',
legend: scale.legend
};
}
export const QmeanScoreColorThemeProvider: ColorTheme.Provider<QmeanScoreColorThemeParams, 'qmean-score'> = {
name: 'qmean-score',
label: 'QMEAN Score',
category: ColorTheme.Category.Validation,
factory: QmeanScoreColorTheme,
getParams: getQmeanScoreColorThemeParams,
defaultValues: PD.getDefaultValues(getQmeanScoreColorThemeParams({})),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure?.models.some(m => QualityAssessment.isApplicable(m, 'qmean')),
ensureCustomProperties: {
attach: async (ctx: CustomProperty.Context, data: ThemeDataContext) => {
if (data.structure) {
for (const m of data.structure.models) {
await QualityAssessmentProvider.attach(ctx, m, void 0, true);
}
}
},
detach: async (data: ThemeDataContext) => {
if (data.structure) {
for (const m of data.structure.models) {
QualityAssessmentProvider.ref(m, false);
}
}
}
}
};

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Unit } from '../../../mol-model/structure';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
import { Model, ResidueIndex } from '../../../mol-model/structure/model';
import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
import { Type } from '../../../mol-script/language/type';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
export { QualityAssessment };
interface QualityAssessment {
localMetrics: Map<string, Map<ResidueIndex, number>>
pLDDT?: Map<ResidueIndex, number>
qmean?: Map<ResidueIndex, number>
}
namespace QualityAssessment {
const Empty = {
value: {
localMetrics: new Map()
}
};
export function isApplicable(model?: Model, localMetricName?: 'pLDDT' | 'qmean'): boolean {
if (!model || !MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
const hasLocalMetric = (
db.ma_qa_metric.id.isDefined &&
db.ma_qa_metric_local.ordinal_id.isDefined
);
if (localMetricName && hasLocalMetric) {
for (let i = 0, il = db.ma_qa_metric._rowCount; i < il; i++) {
if (db.ma_qa_metric.mode.value(i) !== 'local') continue;
if (localMetricName === db.ma_qa_metric.name.value(i)) return true;
}
return false;
} else {
return hasLocalMetric;
}
}
export async function obtain(ctx: CustomProperty.Context, model: Model, props: QualityAssessmentProps): Promise<CustomProperty.Data<QualityAssessment>> {
if (!model || !MmcifFormat.is(model.sourceData)) return Empty;
const { ma_qa_metric, ma_qa_metric_local } = model.sourceData.data.db;
const { model_id, label_asym_id, label_seq_id, metric_id, metric_value } = ma_qa_metric_local;
const { index } = model.atomicHierarchy;
// for simplicity we assume names in ma_qa_metric for mode 'local' are unique
const localMetrics = new Map<string, Map<ResidueIndex, number>>();
const localNames = new Map<number, string>();
for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) {
if (ma_qa_metric.mode.value(i) !== 'local') continue;
const name = ma_qa_metric.name.value(i);
if (localMetrics.has(name)) {
console.warn(`local ma_qa_metric with name '${name}' already added`);
continue;
}
localMetrics.set(name, new Map());
localNames.set(ma_qa_metric.id.value(i), name);
}
for (let i = 0, il = ma_qa_metric_local._rowCount; i < il; i++) {
if (model_id.value(i) !== model.modelNum) continue;
const labelAsymId = label_asym_id.value(i);
const entityIndex = index.findEntity(labelAsymId);
const rI = index.findResidue(model.entities.data.id.value(entityIndex), labelAsymId, label_seq_id.value(i));
const name = localNames.get(metric_id.value(i))!;
localMetrics.get(name)!.set(rI, metric_value.value(i));
}
return {
value: {
localMetrics,
pLDDT: localMetrics.get('pLDDT'),
qmean: localMetrics.get('qmean'),
}
};
}
export const symbols = {
pLDDT: QuerySymbolRuntime.Dynamic(CustomPropSymbol('ma', 'quality-assessment.pLDDT', Type.Num),
ctx => {
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit)) return -1;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
return qualityAssessment?.pLDDT?.get(unit.model.atomicHierarchy.residueAtomSegments.index[element]) ?? -1;
}
),
qmean: QuerySymbolRuntime.Dynamic(CustomPropSymbol('ma', 'quality-assessment.qmean', Type.Num),
ctx => {
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit)) return -1;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
return qualityAssessment?.qmean?.get(unit.model.atomicHierarchy.residueAtomSegments.index[element]) ?? -1;
}
),
};
}
export const QualityAssessmentParams = { };
export type QualityAssessmentParams = typeof QualityAssessmentParams
export type QualityAssessmentProps = PD.Values<QualityAssessmentParams>
export const QualityAssessmentProvider: CustomModelProperty.Provider<QualityAssessmentParams, QualityAssessment> = CustomModelProperty.createProvider({
label: 'QualityAssessment',
descriptor: CustomPropertyDescriptor({
name: 'ma_quality_assessment',
symbols: QualityAssessment.symbols
}),
type: 'static',
defaultParams: QualityAssessmentParams,
getParams: (data: Model) => QualityAssessmentParams,
isApplicable: (data: Model) => QualityAssessment.isApplicable(data),
obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<QualityAssessmentProps>) => {
const p = { ...PD.getDefaultValues(QualityAssessmentParams), ...props };
return await QualityAssessment.obtain(ctx, data, p);
}
});

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { utf8ByteCount, utf8Write } from '../../mol-io/common/utf8';
import { to_mmCIF, Unit } from '../../mol-model/structure';
import { PluginContext } from '../../mol-plugin/context';
import { Task } from '../../mol-task';
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { zip } from '../../mol-util/zip/zip';
export async function exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
try {
await plugin.runTask(_exportHierarchy(plugin, options), { useOverlay: true });
} catch (e) {
console.error(e);
plugin.log.error(`Model export failed. See console for details.`);
}
}
function _exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
return Task.create('Export', async ctx => {
await ctx.update({ message: 'Exporting...', isIndeterminate: true, canAbort: false });
const format = options?.format ?? 'cif';
const { structures } = plugin.managers.structure.hierarchy.current;
const files: [name: string, data: string | Uint8Array][] = [];
const entryMap = new Map<string, number>();
for (const _s of structures) {
const s = _s.transform?.cell.obj?.data ?? _s.cell.obj?.data;
if (!s) continue;
if (s.models.length > 1) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Multimodel exports not supported.`);
continue;
}
if (s.units.some(u => !Unit.isAtomic(u))) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Non-atomic model exports not supported.`);
continue;
}
const name = entryMap.has(s.model.entryId)
? `${s.model.entryId}_${entryMap.get(s.model.entryId)! + 1}.${format}`
: `${s.model.entryId}.${format}`;
entryMap.set(s.model.entryId, (entryMap.get(s.model.entryId) ?? 0) + 1);
await ctx.update({ message: `Exporting ${s.model.entryId}...`, isIndeterminate: true, canAbort: false });
if (s.elementCount > 100000) {
// Give UI chance to update, only needed for larger structures.
await new Promise(res => setTimeout(res, 50));
}
try {
files.push([name, to_mmCIF(s.model.entryId, s, format === 'bcif', { copyAllCategories: true })]);
} catch (e) {
if (format === 'cif' && s.elementCount > 2000000) {
plugin.log.warn(`[Export] The structure might be too big to be exported as Text CIF, consider using the BinaryCIF format instead.`);
}
throw e;
}
}
if (files.length === 1) {
download(new Blob([files[0][1]]), files[0][0]);
} else if (files.length > 1) {
const zipData: Record<string, Uint8Array> = {};
for (const [fn, data] of files) {
if (data instanceof Uint8Array) {
zipData[fn] = data;
} else {
const bytes = new Uint8Array(utf8ByteCount(data));
utf8Write(bytes, 0, data);
zipData[fn] = bytes;
}
}
await ctx.update({ message: `Compressing Data...`, isIndeterminate: true, canAbort: false });
const buffer = await zip(ctx, zipData);
download(new Blob([new Uint8Array(buffer, 0, buffer.byteLength)]), `structures_${getFormattedTime()}.zip`);
}
plugin.log.info(`[Export] Done.`);
});
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { ModelExportUI } from './ui';
export const ModelExport = PluginBehavior.create<{}>({
name: 'extension-model-export',
category: 'misc',
display: {
name: 'Model Export'
},
ctor: class extends PluginBehavior.Handler<{}> {
register(): void {
this.ctx.customStructureControls.set('model-export', ModelExportUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customStructureControls.delete('model-export');
}
},
params: () => ({})
});

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { useState } from 'react';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { GetAppSvg } from '../../mol-plugin-ui/controls/icons';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
import { PluginContext } from '../../mol-plugin/context';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { exportHierarchy } from './export';
export class ModelExportUI extends CollapsableControls<{}, {}> {
protected defaultState(): CollapsableState {
return {
header: 'Export Models',
isCollapsed: true,
brand: { accent: 'cyan', svg: GetAppSvg }
};
}
protected renderControls(): JSX.Element | null {
return <ExportControls plugin={this.plugin} />;
}
}
const Params = {
format: PD.Select<'cif' | 'bcif'>('cif', [['cif', 'mmCIF'], ['bcif', 'Binary mmCIF']])
};
const DefaultParams = PD.getDefaultValues(Params);
function ExportControls({ plugin }: { plugin: PluginContext }) {
const [params, setParams] = useState(DefaultParams);
const [exporting, setExporting] = useState(false);
useBehavior(plugin.managers.structure.hierarchy.behaviors.selection); // triggers UI update
const isBusy = useBehavior(plugin.behaviors.state.isBusy);
const hierarchy = plugin.managers.structure.hierarchy.current;
let label: string = 'Nothing to Export';
if (hierarchy.structures.length === 1) {
label = 'Export';
} if (hierarchy.structures.length > 1) {
label = 'Export (as ZIP)';
}
const onExport = async () => {
setExporting(true);
try {
await exportHierarchy(plugin, { format: params.format });
} finally {
setExporting(false);
}
};
return <>
<ParameterControls params={Params} values={params} onChangeValues={setParams} isDisabled={isBusy || exporting} />
<Button
onClick={onExport}
style={{ marginTop: 1 }}
disabled={isBusy || hierarchy.structures.length === 0 || exporting}
commit={hierarchy.structures.length ? 'on' : 'off'}
>
{label}
</Button>
</>;
}

View File

@@ -31,7 +31,7 @@ export class Mp4Controls extends PluginComponent {
canApply: this.ev.behavior<PluginStateAnimation.CanApply>({ canApply: false }),
info: this.ev.behavior<Mp4AnimationInfo>({ width: 0, height: 0 }),
params: this.ev.behavior<PD.Values<typeof Mp4AnimationParams>>(PD.getDefaultValues(Mp4AnimationParams))
}
};
setCurrent(name?: string) {
const anim = this.animations.find(a => a.name === name);
@@ -73,7 +73,7 @@ export class Mp4Controls extends PluginComponent {
const filename = anim.anim.display.name.toLowerCase().replace(/\s/g, '-').replace(/[^a-z0-9_\-]/g, '');
return { movie, filename: `${this.plugin.helpers.viewportScreenshot?.getFilename('')}_${filename}.mp4` };
} catch (e) {
this.plugin.log.error('' + e);
this.plugin.log.error('Error during animation export');
throw e;
}
});
@@ -125,17 +125,20 @@ export class Mp4Controls extends PluginComponent {
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
this.subscribe(this.plugin.behaviors.state.isBusy, b => {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
});
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));
this.subscribe(this.plugin.managers.snapshot.events.changed, b => this.updateCanApply(b));
this.sync();
this.syncInfo();
}
private updateCanApply(b?: any) {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
}
constructor(private plugin: PluginContext) {
super();

View File

@@ -73,7 +73,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
await plugin.managers.animation.play(params.animation.definition, params.animation.params);
stoppedAnimation = false;
for (let i = 0; i <= N; i++) {
await loop.tick(i * dt, { isSynchronous: true, manualDraw: true });
await loop.tick(i * dt, { isSynchronous: true, animation: { currentFrame: i, frameCount: N }, manualDraw: true });
const image = params.pass.getImageData(width, height, normalizedViewport);
encoder.addFrameRgba(image.data);

View File

@@ -4,7 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import React from 'react';
import { merge } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
@@ -109,15 +108,16 @@ export class Mp4EncoderUI extends CollapsableControls<{}, State> {
save = () => {
download(new Blob([this.state.data!.movie]), this.state.data!.filename);
}
};
generate = async () => {
try {
this.setState({ busy: true });
const data = await this.controls.render();
this.setState({ busy: false, data });
} catch {
} catch (e) {
console.error(e);
this.setState({ busy: false });
}
}
};
}

View File

@@ -62,7 +62,7 @@ export namespace PDBePreferredAssembly {
if (model.customProperties.has(Descriptor)) return true;
let asmName: string | undefined = fromCifData(model);
if (asmName === void 0 && params.PDBe_apiSourceJson) {
if (asmName === void 0 && params.PDBe_apiSourceJson) {
const data = await params.PDBe_apiSourceJson(model);
if (!data) return false;
asmName = asmNameFromJson(model, data);

View File

@@ -21,7 +21,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = StructureQualityReportProvider
private provider = StructureQualityReportProvider;
private labelPDBeValidation = {
label: (loci: Loci): string | undefined => {
@@ -42,7 +42,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
default: return void 0;
}
}
}
};
register(): void {
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
@@ -52,7 +52,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
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);

View File

@@ -79,6 +79,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
return {
factory: StructureQualityReportColorTheme,
granularity: 'group',
preferSmoothing: true,
color: color,
props: props,
description: 'Assigns residue colors according to the number of quality issues or a specific quality issue. Data from wwPDB Validation Report, obtained via PDBe.',
@@ -86,7 +87,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
};
}
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
name: 'pdbe-structure-quality-report',
label: 'Structure Quality Report',
category: ColorTheme.Category.Validation,
@@ -114,6 +115,6 @@ export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Param
isApplicable: (ctx: ThemeDataContext) => StructureQualityReport.isApplicable(ctx.structure?.models[0]),
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? StructureQualityReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(StructureQualityReportProvider.descriptor, false)
detach: (data) => data.structure && StructureQualityReportProvider.ref(data.structure.models[0], false)
}
};

View File

@@ -38,7 +38,7 @@ namespace StructureQualityReport {
}
export function isApplicable(model?: Model): boolean {
return !!model && Model.isFromPdbArchive(model);
return !!model && Model.hasPdbId(model);
}
export const Schema = {
@@ -73,7 +73,7 @@ namespace StructureQualityReport {
}
export function fromCif(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): StructureQualityReport | undefined {
let info = PropertyWrapper.tryGetInfoFromCif('pdbe_structure_quality_report', model);
const info = PropertyWrapper.tryGetInfoFromCif('pdbe_structure_quality_report', model);
if (!info) return;
const data = getCifData(model);
const issueMap = createIssueMapFromCif(model, data.residues, data.groups);

View File

@@ -27,7 +27,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
description: 'Assembly Symmetry data calculated with BioJava, obtained via RCSB PDB.'
},
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
private provider = AssemblySymmetryProvider
private provider = AssemblySymmetryProvider;
register(): void {
this.ctx.state.data.actions.add(InitAssemblySymmetry3D);
@@ -47,7 +47,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
@@ -85,7 +85,7 @@ export const InitAssemblySymmetry3D = StateAction.build({
const assemblySymmetryData = AssemblySymmetryDataProvider.get(a.data).value;
const symmetryIndex = assemblySymmetryData ? AssemblySymmetry.firstNonC1(assemblySymmetryData) : -1;
await AssemblySymmetryProvider.attach(propCtx, a.data, { symmetryIndex });
} catch(e) {
} catch (e) {
plugin.log.error(`Assembly Symmetry: ${e}`);
return;
}
@@ -124,7 +124,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AssemblySymmetryParams);
await repr.createOrUpdate(params, a.data).runInContext(ctx);
const { type, kind, symbol } = assemblySymmetry;
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: kind, description: `${type} (${symbol})` });
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: kind, description: `${type} (${symbol})` });
});
},
update({ a, b, newParams }, plugin: PluginContext) {
@@ -138,6 +138,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
}
const props = { ...b.data.repr.props, ...newParams };
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
b.data.sourceData = a.data;
const { type, kind, symbol } = assemblySymmetry;
b.label = kind;
b.description = `${type} (${symbol})`;

View File

@@ -109,6 +109,6 @@ export const AssemblySymmetryClusterColorThemeProvider: ColorTheme.Provider<Asse
isApplicable: (ctx: ThemeDataContext) => AssemblySymmetry.isApplicable(ctx.structure),
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? AssemblySymmetryProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.customPropertyDescriptors.reference(AssemblySymmetryProvider.descriptor, false)
detach: (data) => data.structure && AssemblySymmetryProvider.ref(data.structure, false)
}
};

View File

@@ -53,7 +53,7 @@ export namespace AssemblySymmetry {
export function isApplicable(structure?: Structure): boolean {
return (
!!structure && structure.models.length === 1 &&
Model.isFromPdbArchive(structure.models[0]) &&
Model.hasPdbId(structure.models[0]) &&
isBiologicalAssembly(structure)
);
}
@@ -124,16 +124,20 @@ export function getSymmetrySelectParam(structure?: Structure) {
if (structure) {
const assemblySymmetryData = AssemblySymmetryDataProvider.get(structure).value;
if (assemblySymmetryData) {
const options: [number, string][] = [];
const options: [number, string][] = [
[-1, 'Off']
];
for (let i = 0, il = assemblySymmetryData.length; i < il; ++i) {
const { symbol, kind } = assemblySymmetryData[i];
if (symbol !== 'C1') {
options.push([ i, `${i + 1}: ${symbol} ${kind}` ]);
options.push([i, `${i + 1}: ${symbol} ${kind}`]);
}
}
if (options.length) {
if (options.length > 1) {
param.options = options;
param.defaultValue = options[0][0];
param.defaultValue = options[1][0];
} else {
options.length = 0;
}
}
}

View File

@@ -310,7 +310,7 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
}
}
const unitCircleDirections = (function() {
const unitCircleDirections = (function () {
const dirs: Vec3[] = [];
const circle = polygon(12, false, 1);
for (let i = 0, il = circle.length; i < il; i += 3) {

Some files were not shown because too many files have changed in this diff Show More