Compare commits

...

98 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
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
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
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
81 changed files with 3285 additions and 4850 deletions

View File

@@ -6,6 +6,60 @@ Note that since we don't clearly distinguish between a public and private interf
## [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``
@@ -19,7 +73,7 @@ Note that since we don't clearly distinguish between a public and private interf
- 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 instad of "chain-id"
- Defaults to "entity-id" style instead of "chain-id"
- Add "illustrative" representation preset
## [v3.0.0-dev.9] - 2022-01-09

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

@@ -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
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

6161
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.0.0-dev.10",
"version": "3.2.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -13,7 +13,7 @@
"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",
@@ -92,50 +92,50 @@
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.3.1",
"@graphql-codegen/cli": "^2.5.0",
"@graphql-codegen/time": "^3.1.1",
"@graphql-codegen/typescript": "^2.4.2",
"@graphql-codegen/typescript": "^2.4.3",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
"@graphql-codegen/typescript-graphql-request": "^4.3.3",
"@graphql-codegen/typescript-operations": "^2.2.2",
"@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.9.1",
"@typescript-eslint/parser": "^5.9.1",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"benchmark": "^2.1.4",
"concurrently": "^7.0.0",
"cpx2": "^4.1.2",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.5.1",
"eslint": "^8.7.0",
"css-loader": "^6.6.0",
"eslint": "^8.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
"graphql": "^16.2.0",
"graphql": "^16.3.0",
"http-server": "^14.1.0",
"jest": "^27.4.7",
"mini-css-extract-plugin": "~2.4.7",
"jest": "^27.5.0",
"mini-css-extract-plugin": "^2.5.3",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"sass": "^1.48.0",
"sass": "^1.49.7",
"sass-loader": "^12.4.0",
"simple-git": "^2.48.0",
"simple-git": "^3.1.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^27.1.3",
"typescript": "^4.5.4",
"webpack": "^5.66.0",
"webpack-cli": "^4.9.1"
"typescript": "^4.5.5",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.19",
"@types/node": "^16.11.22",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.38",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
@@ -146,9 +146,9 @@
"h264-mp4-encoder": "^1.0.12",
"immer": "^9.0.12",
"immutable": "^4.0.0",
"node-fetch": "^2.6.2",
"node-fetch": "^2.6.7",
"rxjs": "^7.5.2",
"swagger-ui-dist": "^4.1.3",
"swagger-ui-dist": "^4.5.0",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
@@ -156,8 +156,5 @@
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"optionalDependencies": {
"gl": "^5.0.0"
}
}

View File

@@ -13,9 +13,11 @@ 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';
@@ -57,6 +59,7 @@ const Extensions = {
'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),
@@ -99,7 +102,14 @@ export class Viewer {
}
static async create(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const o = { ...DefaultViewerOptions, ...options };
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 = {
@@ -357,11 +367,13 @@ export class Viewer {
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(parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume)
.to(volume)
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
type: 'isosurface',
typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
typeParams: { alpha: iso.alpha ?? 1, isoValue: Volume.adjustedIsoValue(volumeData, iso.value, iso.type) },
color: 'uniform',
colorParams: { value: iso.color }
}));

View File

@@ -57,7 +57,7 @@
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';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
@@ -71,7 +71,7 @@
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: !disableWboit,
enableWboit: disableWboit ? true : void 0, // use default value if disable-wboit is not set
preferWebgl1: preferWebgl1,
}).then(viewer => {
var snapshotId = getParam('snapshot-id', '[^&]+').trim();

View File

@@ -9,4 +9,4 @@ import './embedded.html';
import './favicon.ico';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
export * from './app';
export * from './app';

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

@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
// Generated on 2022-01-15T19:52:34-08:00
// Generated on 2022-02-06T15:40:15-08:00
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
@@ -13145,4 +13145,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
}>;
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null> | null } | null> } | null>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null, readonly start: ReadonlyArray<number | null>, readonly end: ReadonlyArray<number | null> } | null> | null } | null> | null } | null };

View File

@@ -71,7 +71,7 @@ export class Canvas3dInteractionHelper {
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
if (e === InputEvent.Drag) {
if (xyChanged && !Representation.Loci.isEmpty(this.prevLoci)) {
if (xyChanged && !this.outsideViewport(this.startX, this.startY)) {
this.events.drag.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, pageStart: Vec2.create(this.startX, this.startY), pageEnd: Vec2.create(this.endX, this.endY) });
this.startX = this.endX;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -310,12 +310,12 @@ export class DrawPass {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest) {
this.marking.depthTarget.bind();
renderer.clear(false);
renderer.clear(false, true);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
renderer.clear(false);
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(props.marking);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -53,7 +53,7 @@ interface Renderer {
readonly stats: RendererStats
readonly props: Readonly<RendererProps>
clear: (toBackgroundColor: boolean) => void
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
clearDepth: () => void
update: (camera: ICamera) => void
@@ -523,13 +523,13 @@ namespace Renderer {
};
return {
clear: (toBackgroundColor: boolean) => {
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
state.enable(gl.SCISSOR_TEST);
state.enable(gl.DEPTH_TEST);
state.colorMask(true, true, true, true);
state.depthMask(true);
if (transparentBackground) {
if (transparentBackground && !ignoreTransparentBackground) {
state.clearColor(0, 0, 0, 0);
} else if (toBackgroundColor) {
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
@@ -8,61 +8,65 @@
*/
export const apply_light_color = `
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
normal = bumpNormal;
}
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
normal = bumpNormal;
}
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#ifdef dXrayShaded
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -127,13 +127,9 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -166,9 +166,7 @@ vec3 v3m4(vec3 p, mat4 m) {
float preFogAlphaBlended = 0.0;
vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
#if !defined(dIgnoreLight)
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
mat4 cartnToUnit = uCartnToUnit * inverse4(vTransform);
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
mat4 modelTransform = uModel * vTransform * uTransform;
@@ -296,24 +294,20 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
material.rgb = mix(material.rgb, overpaint.rgb, overpaint.a);
#endif
#ifdef dIgnoreLight
if (material.a >= 0.01) {
#ifdef dPackedGroup
// compute gradient by central differences
gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a;
gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a;
gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a;
#else
gradient = cell.xyz * 2.0 - 1.0;
#endif
vec3 normal = -normalize(normalMatrix * normalize(gradient));
#include apply_light_color
} else {
gl_FragColor.rgb = material.rgb;
#else
if (material.a >= 0.01) {
#ifdef dPackedGroup
// compute gradient by central differences
gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a;
gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a;
gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a;
#else
gradient = cell.xyz * 2.0 - 1.0;
#endif
vec3 normal = -normalize(normalMatrix * normalize(gradient));
#include apply_light_color
} else {
gl_FragColor.rgb = material.rgb;
}
#endif
}
gl_FragColor.a = material.a * uAlpha * uTransferScale;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -43,17 +43,13 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
vec3 normal = -normalize(vNormal);
if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
#endif
#include apply_light_color
vec3 normal = -normalize(vNormal);
if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
#endif
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -92,12 +92,8 @@ void main(void){
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 normal = -cameraNormal;
#include apply_light_color
#endif
vec3 normal = -cameraNormal;
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.354, IHM 1.17, MA 1.3.3.
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.355, IHM 1.17, MA 1.3.4.
*
* @author molstar/ciftools package
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.354, IHM 1.17, MA 1.3.3.
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.355, IHM 1.17, MA 1.3.4.
*
* @author molstar/ciftools package
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.354, IHM 1.17, MA 1.3.3.
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.355, IHM 1.17, MA 1.3.4.
*
* @author molstar/ciftools package
*/
@@ -215,6 +215,28 @@ export const mmCIF_Schema = {
* formal charge assignment normally found in chemical diagrams.
*/
pdbx_formal_charge: int,
/**
* This data item is an ordinal which identifies distinct chemical components in the atom_site category, both
* polymeric and non-polymeric.
*/
pdbx_label_index: int,
/**
* The name of additional external databases with residue level mapping.
*/
pdbx_sifts_xref_db_name: str,
/**
* The accession code related to the additional external database entry.
*/
pdbx_sifts_xref_db_acc: str,
/**
* The sequence position of the external database entry that corresponds
* to the residue mapping defined by the SIFTS process.
*/
pdbx_sifts_xref_db_num: str,
/**
* Describes the residue type of the given UniProt match
*/
pdbx_sifts_xref_db_res: str,
/**
* The model id corresponding to the atom site.
* This data item is a pointer to _ihm_model_list.model_id
@@ -682,7 +704,7 @@ export const mmCIF_Schema = {
/**
* An abbreviation that identifies the database.
*/
database_id: Aliased<'AFDB' | 'CAS' | 'CSD' | 'EMDB' | 'ICSD' | 'MA' | 'MDF' | 'MODBASE' | 'NDB' | 'NBS' | 'PDB' | 'PDF' | 'RCSB' | 'SMR' | 'EBI' | 'PDBE' | 'BMRB' | 'WWPDB' | 'PDB_ACC'>(str),
database_id: Aliased<'AlphaFoldDB' | 'CAS' | 'CSD' | 'EMDB' | 'ICSD' | 'ModelArchive' | 'MDF' | 'MODBASE' | 'NDB' | 'NBS' | 'PDB' | 'PDF' | 'RCSB' | 'SWISS-MODEL_REPOSITORY' | 'EBI' | 'PDBE' | 'BMRB' | 'WWPDB' | 'PDB_ACC'>(str),
/**
* The code assigned by the database identified in
* _database_2.database_id.

View File

@@ -191,8 +191,14 @@ function getFieldData(field: Field<any, any>, arrayCtor: ArrayCtor<string | numb
array[offset] = '';
allPresent = false;
} else {
mask[offset] = Column.ValueKind.Present;
array[offset] = getter(key, d, offset);
const value = getter(key, d, offset);
if (typeof value === 'string' && !value) {
mask[offset] = Column.ValueKind.NotPresent;
allPresent = false;
} else {
mask[offset] = Column.ValueKind.Present;
}
array[offset] = value;
}
offset++;
}

View File

@@ -1,5 +1,5 @@
/**
* 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>
@@ -11,6 +11,7 @@ import { OrderedSet } from '../../../mol-data/int';
import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
import { Box3D } from './box3d';
import { Axes3D } from './axes3d';
import { PrincipalAxes } from '../../linear-algebra/matrix/principal-axes';
interface Sphere3D {
center: Vec3,
@@ -202,11 +203,28 @@ namespace Sphere3D {
return out;
}
if (hasExtrema(sphere)) {
const positions = new Float32Array(sphere.extrema.length * 3);
for (let i = 0; i < sphere.extrema.length; i++) {
Vec3.toArray(sphere.extrema[i], positions, i * 3);
}
const axes = PrincipalAxes.calculateMomentsAxes(positions);
Axes3D.scale(axes, Axes3D.normalize(axes, axes), delta);
setExtrema(out, sphere.extrema.map(e => {
Vec3.sub(tmpDir, e, sphere.center);
const dist = Vec3.distance(sphere.center, e);
Vec3.normalize(tmpDir, tmpDir);
return Vec3.scaleAndAdd(Vec3(), sphere.center, tmpDir, dist + delta);
const out = Vec3.clone(e);
const sA = Vec3.dot(tmpDir, axes.dirA) < 0 ? -1 : 1;
Vec3.scaleAndAdd(out, out, axes.dirA, sA);
const sB = Vec3.dot(tmpDir, axes.dirB) < 0 ? -1 : 1;
Vec3.scaleAndAdd(out, out, axes.dirB, sB);
const sC = Vec3.dot(tmpDir, axes.dirC) < 0 ? -1 : 1;
Vec3.scaleAndAdd(out, out, axes.dirC, sC);
return out;
}));
}
return out;

View File

@@ -52,24 +52,30 @@ namespace SymmetryOperator {
export const RotationTranslationEpsilon = 0.005;
export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number }
export function create(name: string, matrix: Mat4, info?: CreateInfo): SymmetryOperator {
export function create(name: string, matrix: Mat4, info?: CreateInfo | SymmetryOperator): SymmetryOperator {
let { assembly, ncsId, hkl, spgrOp } = info || { };
const _hkl = hkl ? Vec3.clone(hkl) : Vec3();
spgrOp = defaults(spgrOp, -1);
ncsId = ncsId || -1;
const suffix = getSuffix(info);
if (Mat4.isIdentity(matrix)) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix };
const isIdentity = Mat4.isIdentity(matrix);
const suffix = getSuffix(info, isIdentity);
if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix };
if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) {
console.warn(`Symmetry operator (${name}) should be a composition of rotation and translation.`);
}
return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, ncsId, suffix };
}
function getSuffix(info?: CreateInfo) {
function isSymmetryOperator(x: any): x is SymmetryOperator {
return !!x && !!x.matrix && !!x.inverse && typeof x.name === 'string';
}
function getSuffix(info?: CreateInfo | SymmetryOperator, isIdentity?: boolean) {
if (!info) return '';
if (info.assembly) {
return `_${info.assembly.operId}`;
if (isSymmetryOperator(info)) return info.suffix;
return isIdentity ? '' : `_${info.assembly.operId}`;
}
if (typeof info.spgrOp !== 'undefined' && typeof info.hkl !== 'undefined' && info.spgrOp !== -1) {

View File

@@ -7,21 +7,21 @@
import { Column, Table } from '../../../mol-data/db';
import { Interval, Segmentation } from '../../../mol-data/int';
import { UUID } from '../../../mol-util/uuid';
import { ElementIndex, ChainIndex } from '../../../mol-model/structure';
import { toDatabase } from '../../../mol-io/reader/cif/schema';
import { SymmetryOperator } from '../../../mol-math/geometry';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { ChainIndex, ElementIndex } from '../../../mol-model/structure';
import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';
import { Model } from '../../../mol-model/structure/model/model';
import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../../../mol-model/structure/model/properties/atomic';
import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index';
import { ElementSymbol } from '../../../mol-model/structure/model/types';
import { Entities } from '../../../mol-model/structure/model/properties/common';
import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived';
import { AtomSite } from './schema';
import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index';
import { ElementSymbol } from '../../../mol-model/structure/model/types';
import { UUID } from '../../../mol-util/uuid';
import { ModelFormat } from '../../format';
import { SymmetryOperator } from '../../../mol-math/geometry';
import { MmcifFormat } from '../mmcif';
import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';
import { toDatabase } from '../../../mol-io/reader/cif/schema';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { AtomSite } from './schema';
function findHierarchyOffsets(atom_site: AtomSite) {
if (atom_site._rowCount === 0) return { residues: [], chains: [] };

View File

@@ -30,7 +30,7 @@ export interface CoarseData {
export const EmptyCoarse = { hierarchy: CoarseHierarchy.Empty, conformation: void 0 as any };
export function getCoarse(data: CoarseData, properties: Model['properties']): { hierarchy: CoarseHierarchy, conformation: CoarseConformation } {
export function getCoarse(data: CoarseData, chemicalComponentMap: Model['properties']['chemicalComponentMap']): { hierarchy: CoarseHierarchy, conformation: CoarseConformation } {
const { ihm_sphere_obj_site, ihm_gaussian_obj_site } = data;
if (ihm_sphere_obj_site._rowCount === 0 && ihm_gaussian_obj_site._rowCount === 0) return EmptyCoarse;
@@ -38,12 +38,12 @@ export function getCoarse(data: CoarseData, properties: Model['properties']): {
const sphereData = getData(ihm_sphere_obj_site);
const sphereConformation = getSphereConformation(ihm_sphere_obj_site);
const sphereKeys = getCoarseKeys(sphereData, data.entities);
const sphereRanges = getCoarseRanges(sphereData, properties.chemicalComponentMap);
const sphereRanges = getCoarseRanges(sphereData, chemicalComponentMap);
const gaussianData = getData(ihm_gaussian_obj_site);
const gaussianConformation = getGaussianConformation(ihm_gaussian_obj_site);
const gaussianKeys = getCoarseKeys(gaussianData, data.entities);
const gaussianRanges = getCoarseRanges(gaussianData, properties.chemicalComponentMap);
const gaussianRanges = getCoarseRanges(gaussianData, chemicalComponentMap);
return {
hierarchy: {

View File

@@ -11,7 +11,7 @@ import { getEntityType, getEntitySubtype } from '../../../mol-model/structure/mo
import { ElementIndex, EntityIndex, Model } from '../../../mol-model/structure/model';
import { BasicData, BasicSchema, Entity } from './schema';
export function getEntities(data: BasicData, properties: Model['properties']): Entities {
export function getEntityData(data: BasicData): Entities {
let entityData: Entity;
if (!data.entity.id.isDefined) {
@@ -121,28 +121,32 @@ export function getEntities(data: BasicData, properties: Model['properties']): E
const subtypeColumn = Column.ofArray({ array: subtypes, schema: EntitySubtype });
//
const prdIds: string[] = new Array(entityData._rowCount);
prdIds.fill('');
if (data.pdbx_molecule && data.pdbx_molecule.prd_id.isDefined) {
const { asym_id, prd_id, _rowCount } = data.pdbx_molecule;
for (let i = 0; i < _rowCount; ++i) {
const asymId = asym_id.value(i);
const entityId = properties.structAsymMap.get(asymId)?.entity_id;
if (entityId !== undefined) {
prdIds[getEntityIndex(entityId)] = prd_id.value(i);
}
}
}
const prdIdColumn = Column.ofArray({ array: prdIds, schema: Column.Schema.str });
return {
data: entityData,
subtype: subtypeColumn,
prd_id: prdIdColumn,
getEntityIndex
};
}
export function getEntitiesWithPRD(data: BasicData, entities: Entities, structAsymMap: Model['properties']['structAsymMap']): Entities {
if (!data.pdbx_molecule || !data.pdbx_molecule.prd_id.isDefined) {
return entities;
}
const prdIds: string[] = new Array(entities.data._rowCount);
prdIds.fill('');
const { asym_id, prd_id, _rowCount } = data.pdbx_molecule;
for (let i = 0; i < _rowCount; ++i) {
const asymId = asym_id.value(i);
const entityId = structAsymMap.get(asymId)?.entity_id;
if (entityId !== undefined) {
prdIds[entities.getEntityIndex(entityId)] = prd_id.value(i);
}
}
const prdIdColumn = Column.ofArray({ array: prdIds, schema: Column.Schema.str });
return {
...entities,
prd_id: prdIdColumn
};
}

View File

@@ -18,13 +18,13 @@ import { sortAtomSite } from './sort';
import { ModelFormat } from '../../format';
import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges';
import { AtomSite, BasicData } from './schema';
import { getProperties } from './properties';
import { getEntities } from './entities';
import { getChemicalComponentMap, getMissingResidues, getSaccharideComponentMap, getStructAsymMap } from './properties';
import { getEntitiesWithPRD, getEntityData } from './entities';
import { getModelGroupName } from './util';
import { ArrayTrajectory } from '../../../mol-model/structure/trajectory';
export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
const properties = getProperties(data);
const properties = getCommonProperties(data, format);
const models = data.ihm_model_list._rowCount > 0
? await readIntegrative(ctx, data, properties, format)
: await readStandard(ctx, data, properties, format);
@@ -36,9 +36,18 @@ export async function createModels(data: BasicData, format: ModelFormat, ctx: Ru
return new ArrayTrajectory(models);
}
/** Standard atomic model */
function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, properties: Model['properties'], format: ModelFormat, previous?: Model): Model {
type CommonProperties = Omit<Model['properties'], 'structAsymMap'>
function getCommonProperties(data: BasicData, format: ModelFormat): CommonProperties {
return {
missingResidues: getMissingResidues(data),
chemicalComponentMap: getChemicalComponentMap(data),
saccharideComponentMap: getSaccharideComponentMap(data)
};
}
/** Standard atomic model */
function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, properties: CommonProperties, format: ModelFormat, previous?: Model): Model {
const atomic = getAtomicHierarchyAndConformation(atom_site, sourceIndex, entities, properties.chemicalComponentMap, format, previous);
const modelNum = atom_site.pdbx_PDB_model_num.value(0);
if (previous && atomic.sameAsPrevious) {
@@ -54,6 +63,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
const coarse = EmptyCoarse;
const sequence = getSequence(data, entities, atomic.hierarchy, coarse.hierarchy);
const atomicRanges = getAtomicRanges(atomic.hierarchy, entities, atomic.conformation, sequence);
const structAsymMap = getStructAsymMap(atomic.hierarchy);
const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
? data.entry.id.value(0) : format.name;
@@ -70,7 +80,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
sourceData: format,
modelNum,
parent: undefined,
entities,
entities: getEntitiesWithPRD(data, entities, structAsymMap),
sequence,
atomicHierarchy: atomic.hierarchy,
atomicConformation: atomic.conformation,
@@ -78,7 +88,10 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
atomicChainOperatorMappinng: atomic.chainOperatorMapping,
coarseHierarchy: coarse.hierarchy,
coarseConformation: coarse.conformation,
properties,
properties: {
...properties,
structAsymMap
},
customProperties: new CustomProperties(),
_staticPropertyData: Object.create(null),
_dynamicPropertyData: Object.create(null)
@@ -86,9 +99,9 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
}
/** Integrative model with atomic/coarse parts */
function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Model['properties'], format: ModelFormat): Model {
function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: CommonProperties, format: ModelFormat): Model {
const atomic = getAtomicHierarchyAndConformation(ihm.atom_site, ihm.atom_site_sourceIndex, ihm.entities, properties.chemicalComponentMap, format);
const coarse = getCoarse(ihm, properties);
const coarse = getCoarse(ihm, properties.chemicalComponentMap);
const sequence = getSequence(data, ihm.entities, atomic.hierarchy, coarse.hierarchy);
const atomicRanges = getAtomicRanges(atomic.hierarchy, ihm.entities, atomic.conformation, sequence);
@@ -101,6 +114,8 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
if (ihm.model_name) label.push(ihm.model_name);
if (ihm.model_group_name) label.push(ihm.model_group_name);
const structAsymMap = getStructAsymMap(atomic.hierarchy, data);
return {
id: UUID.create22(),
entryId: entry,
@@ -109,7 +124,7 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
sourceData: format,
modelNum: ihm.model_id,
parent: undefined,
entities: ihm.entities,
entities: getEntitiesWithPRD(data, ihm.entities, structAsymMap),
sequence,
atomicHierarchy: atomic.hierarchy,
atomicConformation: atomic.conformation,
@@ -117,7 +132,10 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
atomicChainOperatorMappinng: atomic.chainOperatorMapping,
coarseHierarchy: coarse.hierarchy,
coarseConformation: coarse.conformation,
properties,
properties: {
...properties,
structAsymMap
},
customProperties: new CustomProperties(),
_staticPropertyData: Object.create(null),
_dynamicPropertyData: Object.create(null)
@@ -132,12 +150,12 @@ function findModelEnd(num: Column<number>, startIndex: number) {
return endIndex;
}
async function readStandard(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
async function readStandard(ctx: RuntimeContext, data: BasicData, properties: CommonProperties, format: ModelFormat) {
const models: Model[] = [];
if (data.atom_site) {
const atomCount = data.atom_site.id.rowCount;
const entities = getEntities(data, properties);
const entities = getEntityData(data);
let modelStart = 0;
while (modelStart < atomCount) {
@@ -170,8 +188,8 @@ function splitTable<T extends Table<any>>(table: T, col: Column<number>) {
async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
const entities = getEntities(data, properties);
async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: CommonProperties, format: ModelFormat) {
const entities = getEntityData(data);
// when `atom_site.ihm_model_id` is undefined fall back to `atom_site.pdbx_PDB_model_num`
const atom_sites_modelColumn = data.atom_site.ihm_model_id.isDefined
? data.atom_site.ihm_model_id : data.atom_site.pdbx_PDB_model_num;
@@ -206,7 +224,7 @@ async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties:
model_id: id,
model_name: model_name.value(i),
model_group_name: getModelGroupName(id, data),
entities: entities,
entities,
atom_site,
atom_site_sourceIndex,
ihm_sphere_obj_site: sphere_sites.has(id) ? sphere_sites.get(id)!.table : Table.window(data.ihm_sphere_obj_site, data.ihm_sphere_obj_site._schema, 0, 0),

View File

@@ -5,15 +5,16 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Table } from '../../../mol-data/db';
import { Model } from '../../../mol-model/structure/model/model';
import { AtomicHierarchy } from '../../../mol-model/structure/model/properties/atomic';
import { ChemicalComponent, MissingResidue, StructAsym } from '../../../mol-model/structure/model/properties/common';
import { getMoleculeType, MoleculeType, getDefaultChemicalComponent } from '../../../mol-model/structure/model/types';
import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
import { getDefaultChemicalComponent, getMoleculeType, MoleculeType } from '../../../mol-model/structure/model/types';
import { SaccharideCompIdMap, SaccharideComponent, SaccharideComponentMap, SaccharidesSnfgMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
import { memoize1 } from '../../../mol-util/memoize';
import { BasicData } from './schema';
import { Table } from '../../../mol-data/db';
function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
export function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
const map = new Map<string, MissingResidue>();
const getKey = (model_num: number, asym_id: string, seq_id: number) => {
return `${model_num}|${asym_id}|${seq_id}`;
@@ -36,7 +37,7 @@ function getMissingResidues(data: BasicData): Model['properties']['missingResidu
};
}
function getChemicalComponentMap(data: BasicData): Model['properties']['chemicalComponentMap'] {
export function getChemicalComponentMap(data: BasicData): Model['properties']['chemicalComponentMap'] {
const map = new Map<string, ChemicalComponent>();
if (data.chem_comp._rowCount > 0) {
@@ -53,7 +54,7 @@ function getChemicalComponentMap(data: BasicData): Model['properties']['chemical
return map;
}
function getSaccharideComponentMap(data: BasicData): SaccharideComponentMap {
export function getSaccharideComponentMap(data: BasicData): SaccharideComponentMap {
const map = new Map<string, SaccharideComponent>();
if (data.pdbx_chem_comp_identifier._rowCount > 0) {
@@ -108,22 +109,18 @@ const getUniqueComponentNames = memoize1((data: BasicData) => {
});
function getStructAsymMap(data: BasicData): Model['properties']['structAsymMap'] {
export function getStructAsymMap(atomic: AtomicHierarchy, data?: BasicData): Model['properties']['structAsymMap'] {
const map = new Map<string, StructAsym>();
const { label_asym_id, auth_asym_id, label_entity_id } = data.atom_site;
for (let i = 0, il = label_asym_id.rowCount; i < il; ++i) {
const { auth_asym_id, label_asym_id, label_entity_id } = atomic.chains;
for (let i = 0, _i = atomic.chains._rowCount; i < _i; i ++) {
const id = label_asym_id.value(i);
if (!map.has(id)) {
map.set(id, {
id,
auth_id: auth_asym_id.value(i),
entity_id: label_entity_id.value(i)
});
}
map.set(id, { id, auth_id: auth_asym_id.value(i), entity_id: label_entity_id.value(i) });
}
if (data.struct_asym._rowCount > 0) {
// to get asym mapping for coarse/ihm data
if (data?.struct_asym._rowCount) {
const { id, entity_id } = data.struct_asym;
for (let i = 0, il = id.rowCount; i < il; ++i) {
const _id = id.value(i);
@@ -136,14 +133,6 @@ function getStructAsymMap(data: BasicData): Model['properties']['structAsymMap']
}
}
}
return map;
}
export function getProperties(data: BasicData): Model['properties'] {
return {
missingResidues: getMissingResidues(data),
chemicalComponentMap: getChemicalComponentMap(data),
saccharideComponentMap: getSaccharideComponentMap(data),
structAsymMap: getStructAsymMap(data)
};
return map;
}

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Model } from '../../../mol-model/structure';
@@ -44,7 +45,7 @@ interface FormatPropertyProvider<T> {
}
namespace FormatPropertyProvider {
export function create<T>(descriptor: CustomPropertyDescriptor): FormatPropertyProvider<T> {
export function create<T>(descriptor: CustomPropertyDescriptor, options?: { asDynamic?: boolean }): FormatPropertyProvider<T> {
const { name } = descriptor;
const formatRegistry = new FormatRegistry<T>();
@@ -55,21 +56,31 @@ namespace FormatPropertyProvider {
return formatRegistry.isApplicable(model);
},
get(model: Model): T | undefined {
if (model._staticPropertyData[name]) return model._staticPropertyData[name];
const store = options?.asDynamic ? model._dynamicPropertyData : model._staticPropertyData;
if (store[name]) return store[name];
if (model.customProperties.has(descriptor)) return;
const obtain = formatRegistry.get(model.sourceData.kind);
if (!obtain) return;
model._staticPropertyData[name] = obtain(model);
store[name] = obtain(model);
model.customProperties.add(descriptor);
return model._staticPropertyData[name];
return store[name];
},
set(model: Model, value: T) {
model._staticPropertyData[name] = value;
if (options?.asDynamic) {
model._dynamicPropertyData[name] = value;
} else {
model._staticPropertyData[name] = value;
}
},
delete(model: Model) {
delete model._staticPropertyData[name];
if (options?.asDynamic) {
delete model._dynamicPropertyData[name];
} else {
delete model._staticPropertyData[name];
}
}
};
}

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2019-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
@@ -42,7 +43,7 @@ export namespace IndexPairBonds {
name: 'index_pair_bonds',
};
export const Provider = FormatPropertyProvider.create<IndexPairBonds>(Descriptor);
export const Provider = FormatPropertyProvider.create<IndexPairBonds>(Descriptor, { asDynamic: true });
export type Data = {
pairs: {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -98,29 +98,20 @@ class InteractionsInterContacts extends InterUnitGraph<number, Features.FeatureI
constructor(map: Map<number, InterUnitGraph.UnitPairEdges<number, Features.FeatureIndex, InteractionsInterContacts.Props>[]>, unitsFeatures: IntMap<Features>) {
super(map);
let count = 0;
const elementKeyIndex = new Map<string, number[]>();
const add = (index: StructureElement.UnitIndex, unitId: number) => {
const vertexKey = this.getElementKey(index, unitId);
const e = elementKeyIndex.get(vertexKey);
if (e === undefined) elementKeyIndex.set(vertexKey, [count]);
else e.push(count);
};
this.map.forEach(pairEdgesArray => {
pairEdgesArray.forEach(({ unitA, connectedIndices }) => {
connectedIndices.forEach(indexA => {
const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA);
for (let j = offsetsA[indexA], jl = offsetsA[indexA + 1]; j < jl; ++j) {
add(membersA[j], unitA);
}
count += 1;
});
});
});
this.elementKeyIndex = elementKeyIndex;
this.elementKeyIndex = new Map<string, number[]>();
for (let i = 0, il = this.edges.length; i < il; ++i) {
const { unitA, indexA } = this.edges[i];
const { offsets, members } = unitsFeatures.get(unitA);
for (let j = offsets[indexA], jl = offsets[indexA + 1]; j < jl; ++j) {
const vertexKey = this.getElementKey(members[j], unitA);
const e = this.elementKeyIndex.get(vertexKey);
if (e === undefined) {
this.elementKeyIndex.set(vertexKey, [i]);
} else {
e.push(i);
}
}
}
}
}
namespace InteractionsInterContacts {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -74,10 +74,11 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
if (!childUnitA) return true;
const unitA = structure.unitMap.get(b.unitA);
const fA = unitsFeatures.get(b.unitA);
// TODO: check all members
const eA = unitA.elements[fA.members[fA.offsets[b.indexA]]];
if (!SortedArray.has(childUnitA.elements, eA)) return true;
const { offsets, members } = unitsFeatures.get(b.unitA);
for (let i = offsets[b.indexA], il = offsets[b.indexA + 1]; i < il; ++i) {
const eA = unitA.elements[members[i]];
if (!SortedArray.has(childUnitA.elements, eA)) return true;
}
}
return false;
@@ -144,6 +145,9 @@ function getInteractionLoci(pickingId: PickingId, structure: Structure, id: numb
return EmptyLoci;
}
const __unitMap = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
const __contactIndicesSet = new Set<number>();
function eachInteraction(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean, isMarking: boolean) {
let changed = false;
if (Interactions.isLoci(loci)) {
@@ -162,21 +166,48 @@ function eachInteraction(loci: Loci, structure: Structure, apply: (interval: Int
if (!Structure.areEquivalent(loci.structure, structure)) return false;
if (isMarking && loci.elements.length === 1) return false; // only a single unit
const contacts = InteractionsProvider.get(structure).value?.contacts;
if (!contacts) return false;
const interactions = InteractionsProvider.get(structure).value;
if (!interactions) return false;
const { contacts, unitsFeatures } = interactions;
for (const e of loci.elements) __unitMap.set(e.unit.id, e.indices);
// TODO when isMarking, all elements of contact features need to be in the loci
for (const e of loci.elements) {
const { unit } = e;
if (!Unit.isAtomic(unit)) continue;
if (isMarking && OrderedSet.size(e.indices) === 1) continue;
OrderedSet.forEach(e.indices, v => {
for (const idx of contacts.getContactIndicesForElement(v, unit)) {
if (apply(Interval.ofSingleton(idx))) changed = true;
__contactIndicesSet.add(idx);
}
});
}
__contactIndicesSet.forEach(i => {
if (isMarking) {
const { indexA, unitA, indexB, unitB } = contacts.edges[i];
const indicesA = __unitMap.get(unitA);
const indicesB = __unitMap.get(unitB);
if (!indicesA || !indicesB) return;
const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA);
for (let j = offsetsA[indexA], jl = offsetsA[indexA + 1]; j < jl; ++j) {
if (!OrderedSet.has(indicesA, membersA[j])) return;
}
const { offsets: offsetsB, members: membersB } = unitsFeatures.get(unitB);
for (let j = offsetsB[indexB], jl = offsetsB[indexB + 1]; j < jl; ++j) {
if (!OrderedSet.has(indicesB, membersB[j])) return;
}
}
if (apply(Interval.ofSingleton(i))) changed = true;
});
__unitMap.clear();
__contactIndicesSet.clear();
}
return changed;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -56,11 +56,19 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
const sizeB = theme.size.size(location);
return Math.min(sizeA, sizeB) * sizeFactor;
},
ignore: (edgeIndex: number) => (
flag[edgeIndex] === InteractionFlag.Filtered ||
// TODO: check all members
(!!childUnit && !SortedArray.has(childUnit.elements, unit.elements[members[offsets[a[edgeIndex]]]]))
)
ignore: (edgeIndex: number) => {
if (flag[edgeIndex] === InteractionFlag.Filtered) return true;
if (childUnit) {
const f = a[edgeIndex];
for (let i = offsets[f], jl = offsets[f + 1]; i < jl; ++i) {
const e = unit.elements[members[offsets[i]]];
if (!SortedArray.has(childUnit.elements, e)) return true;
}
}
return false;
}
};
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
@@ -123,6 +131,8 @@ function getInteractionLoci(pickingId: PickingId, structureGroup: StructureGroup
return EmptyLoci;
}
const __contactIndicesSet = new Set<number>();
function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean, isMarking: boolean) {
let changed = false;
if (Interactions.isLoci(loci)) {
@@ -156,21 +166,37 @@ function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (int
const { offset } = contacts;
const { offsets: fOffsets, indices: fIndices } = features.elementsIndex;
const { members, offsets } = features;
// TODO: when isMarking, all elements of contact features need to be in the loci
for (const e of loci.elements) {
const unitIdx = group.unitIndexMap.get(e.unit.id);
if (unitIdx !== undefined) continue;
if (isMarking && OrderedSet.size(e.indices) === 1) continue;
if (unitIdx === undefined) continue;
OrderedSet.forEach(e.indices, v => {
for (let i = fOffsets[v], il = fOffsets[v + 1]; i < il; ++i) {
const fI = fIndices[i];
for (let j = offset[fI], jl = offset[fI + 1]; j < jl; ++j) {
if (apply(Interval.ofSingleton(unitIdx * groupCount + j))) changed = true;
__contactIndicesSet.add(j);
}
}
});
__contactIndicesSet.forEach(i => {
if (isMarking) {
const fA = contacts.a[i];
for (let j = offsets[fA], jl = offsets[fA + 1]; j < jl; ++j) {
if (!OrderedSet.has(e.indices, members[j])) return;
}
const fB = contacts.b[i];
for (let j = offsets[fB], jl = offsets[fB + 1]; j < jl; ++j) {
if (!OrderedSet.has(e.indices, members[j])) return;
}
}
if (apply(Interval.ofSingleton(unitIdx * groupCount + i))) changed = true;
});
__contactIndicesSet.clear();
}
}
return changed;

View File

@@ -11,36 +11,49 @@ import { Model } from '../../mol-model/structure';
import { StructureElement } from '../../mol-model/structure/structure';
import { CustomModelProperty } from '../common/custom-model-property';
export { BestDatabaseSequenceMapping };
export { SIFTSMapping as SIFTSMapping };
interface BestDatabaseSequenceMapping {
interface SIFTSMappingMapping {
readonly dbName: string[],
readonly accession: string[],
readonly num: number[],
readonly num: string[],
readonly residue: string[]
}
namespace BestDatabaseSequenceMapping {
export const Provider: CustomModelProperty.Provider<{}, BestDatabaseSequenceMapping> = CustomModelProperty.createProvider({
label: 'Best Database Sequence Mapping',
namespace SIFTSMapping {
export const Provider: CustomModelProperty.Provider<{}, SIFTSMappingMapping> = CustomModelProperty.createProvider({
label: 'SIFTS Mapping',
descriptor: CustomPropertyDescriptor({
name: 'molstar_best_database_sequence_mapping'
name: 'sifts_sequence_mapping'
}),
type: 'static',
defaultParams: {},
getParams: () => ({}),
isApplicable: (data: Model) => MmcifFormat.is(data.sourceData) && data.sourceData.data.frame.categories?.atom_site?.fieldNames.indexOf('db_name') >= 0,
isApplicable: (data: Model) => isAvailable(data),
obtain: async (ctx, data) => {
return { value: fromCif(data) };
}
});
export function isAvailable(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return false;
const {
pdbx_sifts_xref_db_name: db_name,
pdbx_sifts_xref_db_acc: db_acc,
pdbx_sifts_xref_db_num: db_num,
pdbx_sifts_xref_db_res: db_res
} = model.sourceData.data.db.atom_site;
return db_name.isDefined && db_acc.isDefined && db_num.isDefined && db_res.isDefined;
}
export function getKey(loc: StructureElement.Location) {
const model = loc.unit.model;
const data = Provider.get(model).value;
if (!data) return '';
const eI = loc.unit.elements[loc.element];
const rI = model.atomicHierarchy.residueAtomSegments.offsets[eI];
const rI = model.atomicHierarchy.residueAtomSegments.index[eI];
return data.accession[rI];
}
@@ -49,45 +62,46 @@ namespace BestDatabaseSequenceMapping {
const data = Provider.get(model).value;
if (!data) return;
const eI = loc.unit.elements[loc.element];
const rI = model.atomicHierarchy.residueAtomSegments.offsets[eI];
const rI = model.atomicHierarchy.residueAtomSegments.index[eI];
const dbName = data.dbName[rI];
if (!dbName) return;
return `${dbName} ${data.accession[rI]} ${data.num[rI]} ${data.residue[rI]}`;
}
function fromCif(model: Model): BestDatabaseSequenceMapping | undefined {
function fromCif(model: Model): SIFTSMappingMapping | undefined {
if (!MmcifFormat.is(model.sourceData)) return;
const { atom_site } = model.sourceData.data.frame.categories;
const db_name = atom_site.getField('db_name');
const db_acc = atom_site.getField('db_acc');
const db_num = atom_site.getField('db_num');
const db_res = atom_site.getField('db_res');
const {
pdbx_sifts_xref_db_name: db_name,
pdbx_sifts_xref_db_acc: db_acc,
pdbx_sifts_xref_db_num: db_num,
pdbx_sifts_xref_db_res: db_res
} = model.sourceData.data.db.atom_site;
if (!db_name || !db_acc || !db_num || !db_res) return;
if (!db_name.isDefined || !db_acc.isDefined || !db_num.isDefined || !db_res.isDefined) return;
const { atomSourceIndex } = model.atomicHierarchy;
const { count, offsets: residueOffsets } = model.atomicHierarchy.residueAtomSegments;
const dbName = new Array<string>(count);
const accession = new Array<string>(count);
const num = new Array<number>(count);
const num = new Array<string>(count);
const residue = new Array<string>(count);
for (let i = 0; i < count; i++) {
const row = atomSourceIndex.value(residueOffsets[i]);
if (db_name.valueKind(row) !== Column.ValueKind.Present) {
dbName[row] = '';
accession[row] = '';
num[row] = 0;
residue[row] = '';
dbName[i] = '';
accession[i] = '';
num[i] = '';
residue[i] = '';
continue;
}
dbName[row] = db_name.str(row);
accession[row] = db_acc.str(row);
num[row] = db_num.int(row);
residue[row] = db_res.str(row);
dbName[i] = db_name.value(row);
accession[i] = db_acc.value(row);
num[i] = db_num.value(row);
residue[i] = db_res.value(row);
}
return { dbName, accession, num, residue };

View File

@@ -12,27 +12,27 @@ import { Color } from '../../../mol-util/color';
import { getPalette, getPaletteParams } from '../../../mol-util/color/palette';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { CustomProperty } from '../../common/custom-property';
import { BestDatabaseSequenceMapping } from '../best-database-mapping';
import { SIFTSMapping } from '../sifts-mapping';
const DefaultColor = Color(0xFAFAFA);
const Description = 'Assigns a color based on best dababase sequence mapping.';
const Description = 'Assigns a color based on SIFTS mapping.';
// same colors for same accessions
const globalAccessionMap = new Map<string, number>();
export const BestDatabaseSequenceMappingColorThemeParams = {
export const SIFTSMappingColorThemeParams = {
...getPaletteParams({ type: 'colors', colorList: 'set-1' }),
};
export type BestDatabaseSequenceMappingColorThemeParams = typeof BestDatabaseSequenceMappingColorThemeParams
export function getBestDatabaseSequenceMappingColorThemeParams(ctx: ThemeDataContext) {
return BestDatabaseSequenceMappingColorThemeParams; // TODO return copy
export type SIFTSMappingColorThemeParams = typeof SIFTSMappingColorThemeParams
export function getSIFTSMappingColorThemeParams(ctx: ThemeDataContext) {
return SIFTSMappingColorThemeParams; // TODO return copy
}
export function BestDatabaseSequenceMappingColorTheme(ctx: ThemeDataContext, props: PD.Values<BestDatabaseSequenceMappingColorThemeParams>): ColorTheme<BestDatabaseSequenceMappingColorThemeParams> {
export function SIFTSMappingColorTheme(ctx: ThemeDataContext, props: PD.Values<SIFTSMappingColorThemeParams>): ColorTheme<SIFTSMappingColorThemeParams> {
let color: LocationColor;
if (ctx.structure) {
for (const m of ctx.structure.models) {
const mapping = BestDatabaseSequenceMapping.Provider.get(m).value;
const mapping = SIFTSMapping.Provider.get(m).value;
if (!mapping) continue;
for (const acc of mapping.accession) {
if (!acc || globalAccessionMap.has(acc)) continue;
@@ -45,7 +45,7 @@ export function BestDatabaseSequenceMappingColorTheme(ctx: ThemeDataContext, pro
const colorMap = new Map<string, Color>();
const getColor = (location: StructureElement.Location) => {
const key = BestDatabaseSequenceMapping.getKey(location);
const key = SIFTSMapping.getKey(location);
if (!key) return DefaultColor;
if (colorMap.has(key)) return colorMap.get(key)!;
@@ -70,7 +70,7 @@ export function BestDatabaseSequenceMappingColorTheme(ctx: ThemeDataContext, pro
}
return {
factory: BestDatabaseSequenceMappingColorTheme,
factory: SIFTSMappingColorTheme,
granularity: 'group',
preferSmoothing: true,
color,
@@ -79,26 +79,26 @@ export function BestDatabaseSequenceMappingColorTheme(ctx: ThemeDataContext, pro
};
}
export const BestDatabaseSequenceMappingColorThemeProvider: ColorTheme.Provider<BestDatabaseSequenceMappingColorThemeParams, 'best-sequence-database-mapping'> = {
name: 'best-sequence-database-mapping',
label: 'Best Database Sequence Mapping',
export const SIFTSMappingColorThemeProvider: ColorTheme.Provider<SIFTSMappingColorThemeParams, 'sifts-mapping'> = {
name: 'sifts-mapping',
label: 'SIFTS Mapping',
category: ColorTheme.Category.Residue,
factory: BestDatabaseSequenceMappingColorTheme,
getParams: getBestDatabaseSequenceMappingColorThemeParams,
defaultValues: PD.getDefaultValues(BestDatabaseSequenceMappingColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure?.models.some(m => BestDatabaseSequenceMapping.Provider.isApplicable(m)),
factory: SIFTSMappingColorTheme,
getParams: getSIFTSMappingColorThemeParams,
defaultValues: PD.getDefaultValues(SIFTSMappingColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure?.models.some(m => SIFTSMapping.Provider.isApplicable(m)),
ensureCustomProperties: {
attach: async (ctx: CustomProperty.Context, data: ThemeDataContext) => {
if (!data.structure) return;
for (const m of data.structure.models) {
await BestDatabaseSequenceMapping.Provider.attach(ctx, m, void 0, true);
await SIFTSMapping.Provider.attach(ctx, m, void 0, true);
}
},
detach: (data) => {
if (!data.structure) return;
for (const m of data.structure.models) {
BestDatabaseSequenceMapping.Provider.ref(m, false);
SIFTSMapping.Provider.ref(m, false);
}
}
}

View File

@@ -5,7 +5,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Column } from '../../../../mol-data/db';
import { mmCIF_Database } from '../../../../mol-io/reader/cif/schema/mmcif';
import { CifWriter } from '../../../../mol-io/writer/cif';
import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
import { SIFTSMapping } from '../../../../mol-model-props/sequence/sifts-mapping';
import { StructureElement, Structure, StructureProperties as P } from '../../structure';
import { CifExportContext } from '../mmcif';
import CifField = CifWriter.Field
@@ -26,7 +30,64 @@ function atom_site_auth_asym_id(e: StructureElement.Location) {
return l + suffix;
}
const atom_site_fields = () => CifWriter.fields<StructureElement.Location, Structure>()
const atom_site_pdbx_label_index = {
shouldInclude(s: AtomSiteData) {
return !!s.atom_site?.pdbx_label_index.isDefined;
},
value(e: StructureElement.Location, d: AtomSiteData) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_label_index.value(srcIndex);
},
};
const SIFTS = {
shouldInclude(s: AtomSiteData) {
return SIFTSMapping.isAvailable(s.structure.models[0]);
},
pdbx_sifts_xref_db_name: {
value(e: StructureElement.Location, d: AtomSiteData) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_name.value(srcIndex);
},
valueKind(e: StructureElement.Location, d: any) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_name.valueKind(srcIndex);
},
},
pdbx_sifts_xref_db_acc: {
value(e: StructureElement.Location, d: AtomSiteData) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_acc.value(srcIndex);
},
valueKind(e: StructureElement.Location, d: any) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_acc.valueKind(srcIndex);
},
},
pdbx_sifts_xref_db_num: {
value(e: StructureElement.Location, d: AtomSiteData) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_num.value(srcIndex);
},
valueKind(e: StructureElement.Location, d: any) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_num.valueKind(srcIndex);
},
},
pdbx_sifts_xref_db_res: {
value(e: StructureElement.Location, d: AtomSiteData) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_res.value(srcIndex);
},
valueKind(e: StructureElement.Location, d: any) {
const srcIndex = d.sourceIndex.value(e.element);
return d.atom_site!.pdbx_sifts_xref_db_res.valueKind(srcIndex);
},
}
};
const atom_site_fields = () => CifWriter.fields<StructureElement.Location, AtomSiteData>()
.str('group_PDB', P.residue.group_PDB)
.index('id')
.str('type_symbol', P.atom.type_symbol as any)
@@ -62,18 +123,37 @@ const atom_site_fields = () => CifWriter.fields<StructureElement.Location, Struc
.str('auth_asym_id', atom_site_auth_asym_id)
.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE })
.int('pdbx_label_index', atom_site_pdbx_label_index.value, { shouldInclude: atom_site_pdbx_label_index.shouldInclude })
// SIFTS
.str('pdbx_sifts_xref_db_name', SIFTS.pdbx_sifts_xref_db_name.value, { shouldInclude: SIFTS.shouldInclude, valueKind: SIFTS.pdbx_sifts_xref_db_name.valueKind })
.str('pdbx_sifts_xref_db_acc', SIFTS.pdbx_sifts_xref_db_acc.value, { shouldInclude: SIFTS.shouldInclude, valueKind: SIFTS.pdbx_sifts_xref_db_acc.valueKind })
.str('pdbx_sifts_xref_db_num', SIFTS.pdbx_sifts_xref_db_num.value, { shouldInclude: SIFTS.shouldInclude, valueKind: SIFTS.pdbx_sifts_xref_db_num.valueKind })
.str('pdbx_sifts_xref_db_res', SIFTS.pdbx_sifts_xref_db_res.value, { shouldInclude: SIFTS.shouldInclude, valueKind: SIFTS.pdbx_sifts_xref_db_res.valueKind })
// .str('operator_name', P.unit.operator_name, {
// shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity)
// })
.getFields();
interface AtomSiteData {
structure: Structure,
sourceIndex: Column<number>,
atom_site?: mmCIF_Database['atom_site']
}
export const _atom_site: CifCategory<CifExportContext> = {
name: 'atom_site',
instance({ structures }: CifExportContext) {
return {
fields: atom_site_fields(),
source: structures.map(s => ({
data: s,
data: {
structure: s,
sourceIndex: s.model.atomicHierarchy.atomSourceIndex,
atom_site: MmcifFormat.is(s.model.sourceData) ? s.model.sourceData.data.db.atom_site : void 0
} as AtomSiteData,
rowCount: s.elementCount,
keys: () => s.elementLocations()
}))

View File

@@ -78,12 +78,12 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex
const ssElements: SSElement<any>[] = [];
const structure = ctx.structures[0];
for (const unit of structure.units) {
// currently can only support this for "identity" operators.
if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue;
for (const { units } of structure.unitSymmetryGroups) {
const u = units[0];
if (!Unit.isAtomic(u)) continue;
const segs = unit.model.atomicHierarchy.residueAtomSegments;
const residues = Segmentation.transientSegments(segs, unit.elements);
const segs = u.model.atomicHierarchy.residueAtomSegments;
const residues = Segmentation.transientSegments(segs, u.elements);
let current: Segmentation.Segment, move = true;
while (residues.hasNext) {
@@ -104,8 +104,8 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex
if (startIdx !== key[current.index]) {
move = false;
ssElements[ssElements.length] = {
start: StructureElement.Location.create(structure, unit, segs.offsets[start]),
end: StructureElement.Location.create(structure, unit, segs.offsets[prev]),
start: StructureElement.Location.create(structure, u, segs.offsets[start]),
end: StructureElement.Location.create(structure, u, segs.offsets[prev]),
length: prev - start + 1,
element
};

View File

@@ -52,6 +52,10 @@ function isWithoutSymmetry(structure: Structure) {
return structure.units.every(u => u.conformation.operator.isIdentity);
}
function isWithoutOperator(structure: Structure) {
return isWithoutSymmetry(structure) && structure.units.every(u => !u.conformation.operator.assembly && !u.conformation.operator.suffix);
}
const Categories = [
// Basics
copy_mmCif_category('entry'),
@@ -63,9 +67,9 @@ const Categories = [
copy_mmCif_category('symmetry', isWithoutSymmetry),
// Assemblies
copy_mmCif_category('pdbx_struct_assembly', isWithoutSymmetry),
copy_mmCif_category('pdbx_struct_assembly_gen', isWithoutSymmetry),
copy_mmCif_category('pdbx_struct_oper_list', isWithoutSymmetry),
copy_mmCif_category('pdbx_struct_assembly', isWithoutOperator),
copy_mmCif_category('pdbx_struct_assembly_gen', isWithoutOperator),
copy_mmCif_category('pdbx_struct_oper_list', isWithoutOperator),
// Secondary structure
_struct_conf,
@@ -250,10 +254,10 @@ function encode_mmCIF_categories_copyAll(encoder: CifWriter.Encoder, ctx: CifExp
}
function to_mmCIF(name: string, structure: Structure, asBinary = false) {
function to_mmCIF(name: string, structure: Structure, asBinary = false, params?: encode_mmCIF_categories_Params) {
const enc = CifWriter.createEncoder({ binary: asBinary });
enc.startDataBlock(name);
encode_mmCIF_categories(enc, structure);
encode_mmCIF_categories(enc, structure, params);
return enc.getData();
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 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>
@@ -63,6 +63,28 @@ export interface CoarseHierarchy {
gaussians: CoarseElements
}
const EmptyCoarseElements: CoarseElements = {
chainKey: [],
entityKey: [],
findSequenceKey: () => -1 as ElementIndex,
findChainKey: () => -1 as ChainIndex,
getEntityFromChain: () => -1 as EntityIndex,
count: 0,
entity_id: Column.Undefined(0, Column.Schema.str),
asym_id: Column.Undefined(0, Column.Schema.str),
seq_id_begin: Column.Undefined(0, Column.Schema.int),
seq_id_end: Column.Undefined(0, Column.Schema.int),
chainElementSegments: Segmentation.create([]),
polymerRanges: SortedRanges.ofSortedRanges([]),
gapRanges: SortedRanges.ofSortedRanges([]),
};
export namespace CoarseHierarchy {
export const Empty: CoarseHierarchy = { isDefined: false } as any;
export const Empty: CoarseHierarchy = {
isDefined: false,
spheres: EmptyCoarseElements,
gaussians: EmptyCoarseElements
};
}

View File

@@ -22,7 +22,7 @@ export const EntitySubtype = Column.Schema.Aliased<EntitySubtype>(Column.Schema.
export interface Entities {
data: mmCIF_Database['entity'],
subtype: Column<EntitySubtype>,
prd_id: Column<string>,
prd_id?: Column<string>,
getEntityIndex(id: string): EntityIndex
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-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>
@@ -497,7 +497,9 @@ export namespace Loci {
if (!elementIndices) continue;
const indices = getUnitIndices(unit.elements, elementIndices);
elements[elements.length] = { unit, indices };
if (OrderedSet.size(indices)) {
elements[elements.length] = { unit, indices };
}
}
return Loci(loci.structure, elements);

View File

@@ -126,6 +126,7 @@ const coarse = {
z: atom.z,
asym_id: p(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)),
entity_id: p(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entity_id.value(l.element)),
seq_id_begin: p(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)),
seq_id_end: p(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)),
@@ -162,7 +163,7 @@ const entity = {
pdbx_ec: p(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))),
subtype: p(l => l.unit.model.entities.subtype.value(eK(l))),
prd_id: p(l => l.unit.model.entities.prd_id.value(eK(l))),
prd_id: p(l => l.unit.model.entities.prd_id?.value(eK(l)) ?? ''),
};
const _emptyList: any[] = [];

View File

@@ -1,27 +1,34 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import { Segmentation } from '../../../../mol-data/int';
import { MinimizeRmsd } from '../../../../mol-math/linear-algebra/3d/minimize-rmsd';
import { BestDatabaseSequenceMapping } from '../../../../mol-model-props/sequence/best-database-mapping';
import { SIFTSMapping } from '../../../../mol-model-props/sequence/sifts-mapping';
import { ElementIndex } from '../../model/indexing';
import { Structure } from '../structure';
import { Unit } from '../unit';
export interface AlignmentResult {
export interface AlignmentResultEntry {
transform: MinimizeRmsd.Result,
pivot: number,
other: number
}
export function alignAndSuperposeWithBestDatabaseMapping(structures: Structure[]): AlignmentResult[] {
export interface AlignmentResult {
entries: AlignmentResultEntry[],
zeroOverlapPairs: [number, number][],
failedPairs: [number, number][]
}
export function alignAndSuperposeWithSIFTSMapping(structures: Structure[], options?: { traceOnly?: boolean }): AlignmentResult {
const indexMap = new Map<string, IndexEntry>();
for (let i = 0; i < structures.length; i++) {
buildIndex(structures[i], indexMap, i);
buildIndex(structures[i], indexMap, i, options?.traceOnly ?? true);
}
const index = Array.from(indexMap.values());
@@ -29,14 +36,26 @@ export function alignAndSuperposeWithBestDatabaseMapping(structures: Structure[]
// TODO: support non-first structure pivots
const pairs = findPairs(structures.length, index);
const ret: AlignmentResult[] = [];
const zeroOverlapPairs: AlignmentResult['zeroOverlapPairs'] = [];
const failedPairs: AlignmentResult['failedPairs'] = [];
const entries: AlignmentResultEntry[] = [];
for (const p of pairs) {
const [a, b] = getPositionTables(index, p.i, p.j, p.count);
const transform = MinimizeRmsd.compute({ a, b });
ret.push({ transform, pivot: p.i, other: p.j });
if (p.count === 0) {
zeroOverlapPairs.push([p.i, p.j]);
} else {
const [a, b] = getPositionTables(index, p.i, p.j, p.count);
const transform = MinimizeRmsd.compute({ a, b });
if (Number.isNaN(transform.rmsd)) {
failedPairs.push([p.i, p.j]);
} else {
entries.push({ transform, pivot: p.i, other: p.j });
}
}
}
return ret;
return { entries, zeroOverlapPairs, failedPairs };
}
function getPositionTables(index: IndexEntry[], pivot: number, other: number, N: number) {
@@ -51,6 +70,7 @@ function getPositionTables(index: IndexEntry[], pivot: number, other: number, N:
const l = Math.min(a[2] - a[1], b[2] - b[1]);
// TODO: check if residue types match?
for (let i = 0; i < l; i++) {
let eI = (a[1] + i) as ElementIndex;
xs.x[o] = a[0].conformation.x(eI);
@@ -117,40 +137,49 @@ interface IndexEntry {
pivots: { [i: number]: [unit: Unit.Atomic, start: ElementIndex, end: ElementIndex] | undefined }
}
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number) {
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number, traceOnly: boolean) {
for (const unit of structure.units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
const { elements, model } = unit;
const { offsets: residueOffset } = model.atomicHierarchy.residueAtomSegments;
const map = BestDatabaseSequenceMapping.Provider.get(model).value;
const map = SIFTSMapping.Provider.get(model).value;
if (!map) return;
const { dbName, accession, num } = map;
const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
const traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex;
while (chainsIt.hasNext) {
const chainSegment = chainsIt.move();
residuesIt.setSegment(chainSegment);
while (residuesIt.hasNext) {
const residueSegment = residuesIt.move();
const eI = elements[residueSegment.start];
const rI = residueOffset[eI];
const rI = residueSegment.index;
if (!dbName[rI]) continue;
let start, end;
if (traceOnly) {
start = traceElementIndex[rI];
if (start === -1) continue;
end = start + 1 as ElementIndex;
} else {
start = elements[residueSegment.start];
end = elements[residueSegment.end - 1] + 1 as ElementIndex;
}
const key = `${dbName[rI]}-${accession[rI]}-${num[rI]}`;
if (!index.has(key)) {
index.set(key, { key, pivots: { [sI]: [unit, eI, elements[residueSegment.end]] } });
index.set(key, { key, pivots: { [sI]: [unit, start, end] } });
} else {
const entry = index.get(key)!;
if (!entry.pivots[sI]) {
entry.pivots[sI] = [unit, eI, elements[residueSegment.end]];
entry.pivots[sI] = [unit, start, end];
}
}
}

View File

@@ -15,6 +15,7 @@ import { ModelFormat } from '../../mol-model-formats/format';
import { CustomProperties } from '../custom-property';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { toPrecision } from '../../mol-util/number';
import { DscifFormat } from '../../mol-model-formats/volume/density-server';
export interface Volume {
readonly label?: string
@@ -84,6 +85,23 @@ export namespace Volume {
}
}
// Converts iso value to relative if using downsample VolumeServer data
export function adjustedIsoValue(volume: Volume, value: number, kind: 'absolute' | 'relative') {
if (kind === 'relative') return IsoValue.relative(value);
const absolute = IsoValue.absolute(value);
if (DscifFormat.is(volume.sourceData)) {
const stats = {
min: volume.sourceData.data.volume_data_3d_info.min_source.value(0),
max: volume.sourceData.data.volume_data_3d_info.max_source.value(0),
mean: volume.sourceData.data.volume_data_3d_info.mean_source.value(0),
sigma: volume.sourceData.data.volume_data_3d_info.sigma_source.value(0),
};
return Volume.IsoValue.toRelative(absolute, stats);
}
return absolute;
}
const defaultStats: Grid['stats'] = { min: -1, max: 1, mean: 0, sigma: 0.1 };
export function createIsoValueParam(defaultValue: Volume.IsoValue, stats?: Grid['stats']) {
const sts = stats || defaultStats;

View File

@@ -7,8 +7,10 @@
import { PluginContext } from '../../mol-plugin/context';
import { StateAction } from '../../mol-state';
import { Task } from '../../mol-task';
import { Asset } from '../../mol-util/assets';
import { getFileInfo } from '../../mol-util/file-info';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { unzip } from '../../mol-util/zip/zip';
import { PluginStateObject } from '../objects';
export const OpenFiles = StateAction.build({
@@ -33,24 +35,37 @@ export const OpenFiles = StateAction.build({
plugin.log.error('No file(s) selected');
return;
}
const processFile = async (file: Asset.File) => {
const info = getFileInfo(file.file!);
const isBinary = plugin.dataFormats.binaryExtensions.has(info.ext);
const { data } = await plugin.builders.data.readFile({ file, isBinary });
const provider = params.format.name === 'auto'
? plugin.dataFormats.auto(info, data.cell?.obj!)
: plugin.dataFormats.get(params.format.params);
if (!provider) {
plugin.log.warn(`OpenFiles: could not find data provider for '${info.name}.${info.ext}'`);
return;
}
// need to await so that the enclosing Task finishes after the update is done.
const parsed = await provider.parse(plugin, data);
if (params.visuals) {
await provider.visuals?.(plugin, parsed);
}
};
for (const file of params.files) {
try {
const info = getFileInfo(file.file!);
const isBinary = plugin.dataFormats.binaryExtensions.has(info.ext);
const { data } = await plugin.builders.data.readFile({ file, isBinary });
const provider = params.format.name === 'auto'
? plugin.dataFormats.auto(info, data.cell?.obj!)
: plugin.dataFormats.get(params.format.params);
if (!provider) {
plugin.log.warn(`OpenFiles: could not find data provider for '${info.name}.${info.ext}'`);
continue;
}
// need to await so that the enclosing Task finishes after the update is done.
const parsed = await provider.parse(plugin, data);
if (params.visuals) {
await provider.visuals?.(plugin, parsed);
if (file.file && file.name.toLowerCase().endsWith('.zip')) {
const zippedFiles = await unzip(taskCtx, await file.file.arrayBuffer());
for (const [fn, filedata] of Object.entries(zippedFiles)) {
const asset = Asset.File(new File([filedata as Uint8Array], fn));
await processFile(asset);
}
} else {
await processFile(file);
}
} catch (e) {
console.error(e);

View File

@@ -1,5 +1,5 @@
/**
* 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>
@@ -75,8 +75,8 @@ const DownloadDensity = StateAction.build({
case 'pdb-xray':
downloadParams = src.params.provider.server === 'pdbe' ? {
url: Asset.Url(src.params.type === '2fofc'
? `http://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}.ccp4`
: `http://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}_diff.ccp4`),
? `https://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}.ccp4`
: `https://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}_diff.ccp4`),
isBinary: true,
label: `PDBe X-ray map: ${src.params.provider.id}`
} : {

View File

@@ -24,8 +24,6 @@ import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bo
import { StructConn } from '../../../mol-model-formats/structure/property/bonds/struct_conn';
import { StructureRepresentationRegistry } from '../../../mol-repr/structure/registry';
import { assertUnreachable } from '../../../mol-util/type-helpers';
import { Color } from '../../../mol-util/color';
import { PostprocessingParams } from '../../../mol-canvas3d/passes/postprocessing';
export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { }
export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; }
@@ -39,6 +37,7 @@ export namespace StructureRepresentationPresetProvider {
export const CommonParams = {
ignoreHydrogens: PD.Optional(PD.Boolean(false)),
ignoreLight: PD.Optional(PD.Boolean(false)),
quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)),
theme: PD.Optional(PD.Group({
globalName: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
@@ -70,9 +69,11 @@ export namespace StructureRepresentationPresetProvider {
const typeParams = {
quality: plugin.managers.structure.component.state.options.visualQuality,
ignoreHydrogens: !plugin.managers.structure.component.state.options.showHydrogens,
ignoreLight: plugin.managers.structure.component.state.options.ignoreLight,
};
if (params.quality && params.quality !== 'auto') typeParams.quality = params.quality;
if (params.ignoreHydrogens !== void 0) typeParams.ignoreHydrogens = !!params.ignoreHydrogens;
if (params.ignoreLight !== void 0) typeParams.ignoreLight = !!params.ignoreLight;
const color: ColorTheme.BuiltIn | undefined = params.theme?.globalName ? params.theme?.globalName : void 0;
const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = params.theme?.carbonColor !== undefined
? { carbonColor: getCarbonColorParams(params.theme?.carbonColor) }
@@ -100,15 +101,6 @@ type CommonParams = StructureRepresentationPresetProvider.CommonParams
const reprBuilder = StructureRepresentationPresetProvider.reprBuilder;
const updateFocusRepr = StructureRepresentationPresetProvider.updateFocusRepr;
function resetPostprocessingProps(plugin: PluginContext) {
if (plugin.canvas3d) {
const p = PD.getDefaultValues(PostprocessingParams);
plugin.canvas3d.setProps({
postprocessing: { outline: p.outline, occlusion: p.occlusion }
});
}
}
const auto = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-auto',
display: {
@@ -148,7 +140,6 @@ const empty = StructureRepresentationPresetProvider({
id: 'preset-structure-representation-empty',
display: { name: 'Empty', description: 'Removes all existing representations.' },
async apply(ref, params, plugin) {
resetPostprocessingProps(plugin);
return { };
}
});
@@ -203,8 +194,6 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: false });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
resetPostprocessingProps(plugin);
return { components, representations };
}
});
@@ -244,8 +233,6 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: true });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
resetPostprocessingProps(plugin);
return { components, representations };
}
});
@@ -298,8 +285,6 @@ const coarseSurface = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: true });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
resetPostprocessingProps(plugin);
return { components, representations };
}
});
@@ -333,8 +318,6 @@ const polymerCartoon = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: true });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
resetPostprocessingProps(plugin);
return { components, representations };
}
});
@@ -401,8 +384,6 @@ const atomicDetail = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: true });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name ?? color, params.theme?.focus?.params ?? colorParams);
resetPostprocessingProps(plugin);
return { components, representations };
}
});
@@ -436,21 +417,6 @@ const illustrative = StructureRepresentationPresetProvider({
await update.commit({ revertOnError: true });
await updateFocusRepr(plugin, structure, params.theme?.focus?.name ?? color, params.theme?.focus?.params);
if (plugin.canvas3d) {
plugin.canvas3d.setProps({
postprocessing: {
outline: {
name: 'on',
params: { scale: 1, color: Color(0x000000), threshold: 0.25 }
},
occlusion: {
name: 'on',
params: { bias: 0.9, blurKernelSize: 15, radius: 5, samples: 32 }
},
}
});
}
return { components, representations };
}
});
@@ -471,6 +437,6 @@ export const PresetStructureRepresentations = {
'polymer-and-ligand': polymerAndLigand,
'protein-and-nucleic': proteinAndNucleic,
'coarse-surface': coarseSurface,
'illustrative': illustrative,
illustrative,
};
export type PresetStructureRepresentations = typeof PresetStructureRepresentations;

View File

@@ -29,7 +29,7 @@ export class StructureRepresentationBuilder {
readonly defaultProvider = PresetStructureRepresentations.auto;
private resolveProvider(ref: StructureRepresentationPresetProviderRef) {
resolveProvider(ref: StructureRepresentationPresetProviderRef) {
return typeof ref === 'string'
? PresetStructureRepresentations[ref as keyof PresetStructureRepresentations] ?? arrayFind(this._providers, p => p.id === ref)
: ref;

View File

@@ -18,7 +18,6 @@ import { objectForEach } from '../../mol-util/object';
import { RecommendedIsoValue } from '../../mol-model-formats/volume/property';
import { getContourLevelEmdb } from '../../mol-plugin/behavior/dynamic/volume-streaming/util';
import { Task } from '../../mol-task';
import { DscifFormat } from '../../mol-model-formats/volume/density-server';
export const VolumeFormatCategory = 'Volume';
type Params = { entryId?: string };
@@ -42,19 +41,9 @@ async function tryObtainRecommendedIsoValue(plugin: PluginContext, volume?: Volu
function tryGetRecomendedIsoValue(volume: Volume) {
const recommendedIsoValue = RecommendedIsoValue.Provider.get(volume);
if (!recommendedIsoValue) return;
if (recommendedIsoValue.kind === 'relative') return recommendedIsoValue;
let stats = volume.grid.stats;
if (DscifFormat.is(volume.sourceData)) {
stats = {
min: volume.sourceData.data.volume_data_3d_info.min_source.value(0),
max: volume.sourceData.data.volume_data_3d_info.max_source.value(0),
mean: volume.sourceData.data.volume_data_3d_info.mean_source.value(0),
sigma: volume.sourceData.data.volume_data_3d_info.sigma_source.value(0),
};
}
return Volume.IsoValue.toRelative(recommendedIsoValue, stats);
return Volume.adjustedIsoValue(volume, recommendedIsoValue.absoluteValue, 'absolute');
}
async function defaultVisuals(plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data> }) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-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>
@@ -71,6 +71,7 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
await update.commit();
await this.plugin.state.updateBehavior(StructureFocusRepresentation, p => {
p.ignoreHydrogens = !options.showHydrogens;
p.ignoreLight = options.ignoreLight;
p.material = options.materialStyle;
p.clip = options.clipObjects;
});
@@ -79,16 +80,17 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
}
private updateReprParams(update: StateBuilder.Root, component: StructureComponentRef) {
const { showHydrogens, visualQuality: quality, materialStyle: material, clipObjects: clip } = this.state.options;
const { showHydrogens, visualQuality: quality, ignoreLight, materialStyle: material, clipObjects: clip } = this.state.options;
const ignoreHydrogens = !showHydrogens;
for (const r of component.representations) {
if (r.cell.transform.transformer !== StructureRepresentation3D) continue;
const params = r.cell.transform.params as StateTransformer.Params<StructureRepresentation3D>;
if (!!params.type.params.ignoreHydrogens !== ignoreHydrogens || params.type.params.quality !== quality || !shallowEqual(params.type.params.material, material) || !PD.areEqual(Clip.Params, params.type.params.clip, clip)) {
if (!!params.type.params.ignoreHydrogens !== ignoreHydrogens || params.type.params.quality !== quality || params.type.params.ignoreLight !== ignoreLight || !shallowEqual(params.type.params.material, material) || !PD.areEqual(Clip.Params, params.type.params.clip, clip)) {
update.to(r.cell).update(old => {
old.type.params.ignoreHydrogens = ignoreHydrogens;
old.type.params.quality = quality;
old.type.params.ignoreLight = ignoreLight;
old.type.params.material = material;
old.type.params.clip = clip;
});
@@ -309,9 +311,9 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
addRepresentation(components: ReadonlyArray<StructureComponentRef>, type: string) {
if (components.length === 0) return;
const { showHydrogens, visualQuality: quality, materialStyle: material, clipObjects: clip } = this.state.options;
const { showHydrogens, visualQuality: quality, ignoreLight, materialStyle: material, clipObjects: clip } = this.state.options;
const ignoreHydrogens = !showHydrogens;
const typeParams = { ignoreHydrogens, quality, material, clip };
const typeParams = { ignoreHydrogens, quality, ignoreLight, material, clip };
return this.plugin.dataTransaction(async () => {
for (const component of components) {
@@ -346,9 +348,9 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
const xs = structures || this.currentStructures;
if (xs.length === 0) return;
const { showHydrogens, visualQuality: quality, materialStyle: material, clipObjects: clip } = this.state.options;
const { showHydrogens, visualQuality: quality, ignoreLight, materialStyle: material, clipObjects: clip } = this.state.options;
const ignoreHydrogens = !showHydrogens;
const typeParams = { ignoreHydrogens, quality, material, clip };
const typeParams = { ignoreHydrogens, quality, ignoreLight, material, clip };
const componentKey = UUID.create22();
for (const s of xs) {
@@ -458,6 +460,7 @@ namespace StructureComponentManager {
export const OptionsParams = {
showHydrogens: PD.Boolean(true, { description: 'Toggle display of hydrogen atoms in representations' }),
visualQuality: PD.Select('auto', VisualQualityOptions, { description: 'Control the visual/rendering quality of representations' }),
ignoreLight: PD.Boolean(false, { description: 'Ignore light for stylized rendering of representtions' }),
materialStyle: Material.getParam(),
clipObjects: PD.Group(Clip.Params),
interactions: PD.Group(InteractionsProvider.defaultParams, { label: 'Non-covalent Interactions' }),

View File

@@ -1,5 +1,5 @@
/**
* 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>
@@ -24,6 +24,7 @@ import { StructureSourceControls } from './structure/source';
import { VolumeStreamingControls, VolumeSourceControls } from './structure/volume';
import { PluginConfig } from '../mol-plugin/config';
import { StructureSuperpositionControls } from './structure/superposition';
import { StructureQuickStylesControls } from './structure/quick-styles';
export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> {
state = { show: false, label: '' };
@@ -292,6 +293,7 @@ export class DefaultStructureTools extends PluginUIComponent {
<StructureSourceControls />
<StructureMeasurementsControls />
<StructureSuperpositionControls />
<StructureQuickStylesControls />
<StructureComponentControls />
{this.plugin.config.get(PluginConfig.VolumeStreaming.Enabled) && <VolumeStreamingControls />}
<VolumeSourceControls />

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-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>
@@ -38,10 +38,10 @@ export function MoleculeSvg() { return _Molecule; }
// The following icons are adapted from https://materialdesignicons.com/ and
// licensed with https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z" /></svg>;
const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z' /></svg>;
export function CubeOutlineSvg() { return _CubeOutline; }
const _CubeScan = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M17,22V20H20V17H22V20.5C22,20.89 21.84,21.24 21.54,21.54C21.24,21.84 20.89,22 20.5,22H17M7,22H3.5C3.11,22 2.76,21.84 2.46,21.54C2.16,21.24 2,20.89 2,20.5V17H4V20H7V22M17,2H20.5C20.89,2 21.24,2.16 21.54,2.46C21.84,2.76 22,3.11 22,3.5V7H20V4H17V2M7,2V4H4V7H2V3.5C2,3.11 2.16,2.76 2.46,2.46C2.76,2.16 3.11,2 3.5,2H7M13,17.25L17,14.95V10.36L13,12.66V17.25M12,10.92L16,8.63L12,6.28L8,8.63L12,10.92M7,14.95L11,17.25V12.66L7,10.36V14.95M18.23,7.59C18.73,7.91 19,8.34 19,8.91V15.23C19,15.8 18.73,16.23 18.23,16.55L12.75,19.73C12.25,20.05 11.75,20.05 11.25,19.73L5.77,16.55C5.27,16.23 5,15.8 5,15.23V8.91C5,8.34 5.27,7.91 5.77,7.59L11.25,4.41C11.5,4.28 11.75,4.22 12,4.22C12.25,4.22 12.5,4.28 12.75,4.41L18.23,7.59Z" /></svg>;
const _CubeScan = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M17,22V20H20V17H22V20.5C22,20.89 21.84,21.24 21.54,21.54C21.24,21.84 20.89,22 20.5,22H17M7,22H3.5C3.11,22 2.76,21.84 2.46,21.54C2.16,21.24 2,20.89 2,20.5V17H4V20H7V22M17,2H20.5C20.89,2 21.24,2.16 21.54,2.46C21.84,2.76 22,3.11 22,3.5V7H20V4H17V2M7,2V4H4V7H2V3.5C2,3.11 2.16,2.76 2.46,2.46C2.76,2.16 3.11,2 3.5,2H7M13,17.25L17,14.95V10.36L13,12.66V17.25M12,10.92L16,8.63L12,6.28L8,8.63L12,10.92M7,14.95L11,17.25V12.66L7,10.36V14.95M18.23,7.59C18.73,7.91 19,8.34 19,8.91V15.23C19,15.8 18.73,16.23 18.23,16.55L12.75,19.73C12.25,20.05 11.75,20.05 11.25,19.73L5.77,16.55C5.27,16.23 5,15.8 5,15.23V8.91C5,8.34 5.27,7.91 5.77,7.59L11.25,4.41C11.5,4.28 11.75,4.22 12,4.22C12.25,4.22 12.5,4.28 12.75,4.41L18.23,7.59Z' /></svg>;
export function CubeScanSvg() { return _CubeScan; }
const _CubeSend = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M16,4L9,8.04V15.96L16,20L23,15.96V8.04M16,6.31L19.8,8.5L16,10.69L12.21,8.5M0,7V9H7V7M11,10.11L15,12.42V17.11L11,14.81M21,10.11V14.81L17,17.11V12.42M2,11V13H7V11M4,15V17H7V15" /></svg>;
@@ -53,9 +53,12 @@ export function CursorDefaultOutlineSvg() { return _CursorDefaultOutline; }
const _FileOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path fill='currentColor' d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z' /></svg>;
export function FileOutlineSvg() { return _FileOutline; }
const _PencilRuler = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M3 17.25V21H6.75L17.81 9.93L14.06 6.18L3 17.25M22.61 18.36L18.36 22.61L13.16 17.41L14.93 15.64L15.93 16.64L18.4 14.16L19.82 15.58L18.36 17L19.42 18L20.84 16.6L22.61 18.36M6.61 10.83L1.39 5.64L5.64 1.39L7.4 3.16L4.93 5.64L6 6.7L8.46 4.22L9.88 5.64L8.46 7.05L9.46 8.05L6.61 10.83M20.71 7C21.1 6.61 21.1 6 20.71 5.59L18.37 3.29C18 2.9 17.35 2.9 16.96 3.29L15.12 5.12L18.87 8.87L20.71 7Z" /></svg>;
const _PencilRuler = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M3 17.25V21H6.75L17.81 9.93L14.06 6.18L3 17.25M22.61 18.36L18.36 22.61L13.16 17.41L14.93 15.64L15.93 16.64L18.4 14.16L19.82 15.58L18.36 17L19.42 18L20.84 16.6L22.61 18.36M6.61 10.83L1.39 5.64L5.64 1.39L7.4 3.16L4.93 5.64L6 6.7L8.46 4.22L9.88 5.64L8.46 7.05L9.46 8.05L6.61 10.83M20.71 7C21.1 6.61 21.1 6 20.71 5.59L18.37 3.29C18 2.9 17.35 2.9 16.96 3.29L15.12 5.12L18.87 8.87L20.71 7Z' /></svg>;
export function PencilRulerSvg() { return _PencilRuler; }
const _MagicWand = <svg width='24px' height='24px' viewBox='0 0 24 24'><path fill='currentColor' d='M7.5,5.6L5,7L6.4,4.5L5,2L7.5,3.4L10,2L8.6,4.5L10,7L7.5,5.6M19.5,15.4L22,14L20.6,16.5L22,19L19.5,17.6L17,19L18.4,16.5L17,14L19.5,15.4M22,2L20.6,4.5L22,7L19.5,5.6L17,7L18.4,4.5L17,2L19.5,3.4L22,2M13.34,12.78L15.78,10.34L13.66,8.22L11.22,10.66L13.34,12.78M14.37,7.29L16.71,9.63C17.1,10 17.1,10.65 16.71,11.04L5.04,22.71C4.65,23.1 4,23.1 3.63,22.71L1.29,20.37C0.9,20 0.9,19.35 1.29,18.96L12.96,7.29C13.35,6.9 14,6.9 14.37,7.29Z' /></svg>;
export function MagicWandSvg() { return _MagicWand; }
// The following icons are adapted from https://material-ui.com/components/material-icons/ and
// licensed with https://github.com/mui-org/material-ui/blob/master/LICENSE

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PresetStructureRepresentations } from '../../mol-plugin-state/builder/structure/representation-preset';
import { Color } from '../../mol-util/color';
import { CollapsableControls, PurePluginUIComponent } from '../base';
import { Button } from '../controls/common';
import { MagicWandSvg } from '../controls/icons';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { PostprocessingParams } from '../../mol-canvas3d/passes/postprocessing';
import { PluginConfig } from '../../mol-plugin/config';
import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
export class StructureQuickStylesControls extends CollapsableControls {
defaultState() {
return {
isCollapsed: false,
header: 'Quick Styles',
brand: { accent: 'gray' as const, svg: MagicWandSvg }
};
}
renderControls() {
return <>
<QuickStyles />
</>;
}
}
export class QuickStyles extends PurePluginUIComponent {
async default() {
const { structures } = this.plugin.managers.structure.hierarchy.selection;
const preset = this.plugin.config.get(PluginConfig.Structure.DefaultRepresentationPreset) || PresetStructureRepresentations.auto.id;
const provider = this.plugin.builders.structure.representation.resolveProvider(preset);
await this.plugin.managers.structure.component.applyPreset(structures, provider);
this.plugin.managers.structure.component.setOptions(PD.getDefaultValues(StructureComponentManager.OptionsParams));
if (this.plugin.canvas3d) {
const p = PD.getDefaultValues(PostprocessingParams);
this.plugin.canvas3d.setProps({
postprocessing: { outline: p.outline, occlusion: p.occlusion }
});
}
}
async illustrative() {
const { structures } = this.plugin.managers.structure.hierarchy.selection;
await this.plugin.managers.structure.component.applyPreset(structures, PresetStructureRepresentations.illustrative);
if (this.plugin.canvas3d) {
this.plugin.canvas3d.setProps({
postprocessing: {
outline: {
name: 'on',
params: { scale: 1, color: Color(0x000000), threshold: 0.25 }
},
occlusion: {
name: 'on',
params: { bias: 0.9, blurKernelSize: 15, radius: 5, samples: 32 }
},
}
});
}
}
async stylized() {
this.plugin.managers.structure.component.setOptions({ ...this.plugin.managers.structure.component.state.options, ignoreLight: true });
if (this.plugin.canvas3d) {
const pp = this.plugin.canvas3d.props.postprocessing;
this.plugin.canvas3d.setProps({
postprocessing: {
outline: {
name: 'on',
params: pp.outline.name === 'on'
? pp.outline.params
: { scale: 1, color: Color(0x000000), threshold: 0.33 }
},
occlusion: {
name: 'on',
params: pp.occlusion.name === 'on'
? pp.occlusion.params
: { bias: 0.9, blurKernelSize: 15, radius: 5, samples: 32 }
},
}
});
}
}
render() {
return <div className='msp-flex-row'>
<Button noOverflow title='Applies default representation preset. Set outline and occlusion effects to defaults.' onClick={() => this.default()} style={{ width: 'auto' }}>
Default
</Button>
<Button noOverflow title='Applies no representation preset. Enables outline and occlusion effects. Enables ignore-light representation parameter.' onClick={() => this.stylized()} style={{ width: 'auto' }}>
Stylized
</Button>
<Button noOverflow title='Applies illustrative representation preset. Enables outline and occlusion effects. Enables ignore-light parameter.' onClick={() => this.illustrative()} style={{ width: 'auto' }}>
Illustrative
</Button>
</div>;
}
}

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
*/
import { CollapsableControls, PurePluginUIComponent } from '../base';
@@ -20,9 +21,9 @@ import { ParameterControls } from '../controls/parameters';
import { stripTags } from '../../mol-util/string';
import { StructureSelectionHistoryEntry } from '../../mol-plugin-state/manager/structure/selection';
import { ToggleSelectionModeButton } from './selection';
import { alignAndSuperposeWithBestDatabaseMapping } from '../../mol-model/structure/structure/util/superposition-db-mapping';
import { alignAndSuperposeWithSIFTSMapping } from '../../mol-model/structure/structure/util/superposition-sifts-mapping';
import { PluginCommands } from '../../mol-plugin/commands';
import { BestDatabaseSequenceMapping } from '../../mol-model-props/sequence/best-database-mapping';
import { SIFTSMapping } from '../../mol-model-props/sequence/sifts-mapping';
export class StructureSuperpositionControls extends CollapsableControls {
defaultState() {
@@ -48,7 +49,8 @@ export class StructureSuperpositionControls extends CollapsableControls {
}
export const StructureSuperpositionParams = {
alignSequences: PD.Boolean(true, { isEssential: true, description: 'Perform a sequence alignment and use the aligned residue pairs to guide the 3D superposition.' }),
alignSequences: PD.Boolean(true, { isEssential: true, description: 'For Chain-based 3D superposition, perform a sequence alignment and use the aligned residue pairs to guide the 3D superposition.' }),
traceOnly: PD.Boolean(true, { description: 'For Chain- and Uniprot-based 3D superposition, base superposition only on CA (and equivalent) atoms.' })
};
const DefaultStructureSuperpositionOptions = PD.getDefaultValues(StructureSuperpositionParams);
export type StructureSuperpositionOptions = PD.ValuesFor<typeof StructureSuperpositionParams>
@@ -94,7 +96,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
});
this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, sel => {
this.setState({ canUseDb: sel.structures.every(s => !!s.cell.obj?.data && s.cell.obj.data.models.some(m => BestDatabaseSequenceMapping.Provider.isApplicable(m))) });
this.setState({ canUseDb: sel.structures.every(s => !!s.cell.obj?.data && s.cell.obj.data.models.some(m => SIFTSMapping.Provider.isApplicable(m))) });
});
}
@@ -123,10 +125,10 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
}
superposeChains = async () => {
const { query } = StructureSelectionQueries.trace;
const { query } = this.state.options.traceOnly ? StructureSelectionQueries.trace : StructureSelectionQueries.polymer;
const entries = this.chainEntries;
const traceLocis = entries.map((e, i) => {
const locis = entries.map((e, i) => {
const s = StructureElement.Loci.toStructure(e.loci);
const loci = StructureSelection.toLociWithSourceUnits(query(new QueryContext(s)));
return StructureElement.Loci.remap(loci, i === 0
@@ -136,11 +138,11 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
});
const transforms = this.state.options.alignSequences
? alignAndSuperpose(traceLocis)
: superpose(traceLocis);
? alignAndSuperpose(locis)
: superpose(locis);
const eA = entries[0];
for (let i = 1, il = traceLocis.length; i < il; ++i) {
for (let i = 1, il = locis.length; i < il; ++i) {
const eB = entries[i];
const { bTransform, rmsd } = transforms[i - 1];
await this.transform(eB.cell, bTransform);
@@ -148,6 +150,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
const labelB = stripTags(eB.label);
this.plugin.log.info(`Superposed [${labelA}] and [${labelB}] with RMSD ${rmsd.toFixed(2)}.`);
}
await this.cameraReset();
};
superposeAtoms = async () => {
@@ -171,26 +174,47 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
const count = entries[i].atoms.length;
this.plugin.log.info(`Superposed ${count} ${count === 1 ? 'atom' : 'atoms'} of [${labelA}] and [${labelB}] with RMSD ${rmsd.toFixed(2)}.`);
}
await this.cameraReset();
};
superposeDb = async () => {
const input = this.plugin.managers.structure.hierarchy.behaviors.selection.value.structures;
const traceOnly = this.state.options.traceOnly;
const transforms = alignAndSuperposeWithBestDatabaseMapping(input.map(s => s.cell.obj?.data!));
const structures = input.map(s => s.cell.obj?.data!);
const { entries, failedPairs, zeroOverlapPairs } = alignAndSuperposeWithSIFTSMapping(structures, { traceOnly });
let rmsd = 0;
for (const xform of transforms) {
for (const xform of entries) {
await this.transform(input[xform.other].cell, xform.transform.bTransform);
rmsd += xform.transform.rmsd;
}
rmsd /= transforms.length - 1;
rmsd /= Math.max(entries.length - 1, 1);
this.plugin.log.info(`Superposed ${input.length} structures with avg. RMSD ${rmsd.toFixed(2)}.`);
const formatPairs = (pairs: [number, number][]) => {
return `[${pairs.map(([i, j]) => `(${structures[i].models[0].entryId}, ${structures[j].models[0].entryId})`).join(', ')}]`;
};
if (zeroOverlapPairs.length) {
this.plugin.log.warn(`Superposition: No UNIPROT mapping overlap between structures ${formatPairs(zeroOverlapPairs)}.`);
}
if (failedPairs.length) {
this.plugin.log.error(`Superposition: Failed to superpose structures ${formatPairs(failedPairs)}.`);
}
if (entries.length) {
this.plugin.log.info(`Superposed ${entries.length + 1} structures with avg. RMSD ${rmsd.toFixed(2)} Å.`);
await this.cameraReset();
}
};
async cameraReset() {
await new Promise(res => requestAnimationFrame(res));
PluginCommands.Camera.Reset(this.plugin);
};
}
toggleByChains = () => this.setState({ action: this.state.action === 'byChains' ? void 0 : 'byChains' });
toggleByAtoms = () => this.setState({ action: this.state.action === 'byAtoms' ? void 0 : 'byAtoms' });
@@ -323,8 +347,8 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
superposeByDbMapping() {
return <>
<Button icon={SuperposeChainsSvg} title='Superpose structures using database mapping.' className='msp-btn msp-btn-block' onClick={this.superposeDb} style={{ marginTop: '1px' }} disabled={this.state.isBusy}>
DB
<Button icon={SuperposeChainsSvg} title='Superpose structures using intersection of residues from SIFTS UNIPROT mapping.' className='msp-btn msp-btn-block' onClick={this.superposeDb} style={{ marginTop: '1px' }} disabled={this.state.isBusy}>
Uniprot
</Button>
</>;
}

View File

@@ -77,9 +77,9 @@ export class VolumeStreamingControls extends CollapsableControls<{}, VolumeStrea
const rootCell = root && pivot.cell.parent.cells.get(root);
const simpleApply = rootCell && rootCell.status === 'error'
? { header: 'Error enabling', icon: ErrorSvg, title: rootCell.errorText }
? { header: !!rootCell.errorText && rootCell.errorText?.includes('404') ? 'No Density Data Available' : 'Error Enabling', icon: ErrorSvg, title: rootCell.errorText }
: rootCell && rootCell.obj?.data.entries.length === 0
? { header: 'Error enabling', icon: ErrorSvg, title: 'No entry for streaming found' }
? { header: 'Error Enabling', icon: ErrorSvg, title: 'No Entry for Streaming Found' }
: { header: 'Enable', icon: CheckSvg, title: 'Enable' };
return <ApplyActionControl state={pivot.cell.parent} action={InitVolumeStreaming} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={simpleApply} />;

View File

@@ -11,6 +11,6 @@ export { AccessibleSurfaceArea } from './custom-props/computed/accessible-surfac
export { Interactions } from './custom-props/computed/interactions';
export { SecondaryStructure } from './custom-props/computed/secondary-structure';
export { ValenceModel } from './custom-props/computed/valence-model';
export { BestDatabaseSequenceMapping } from './custom-props/sequence/best-database-mapping';
export { SIFTSMapping as BestDatabaseSequenceMapping } from './custom-props/sequence/sifts-mapping';
export { CrossLinkRestraint } from './custom-props/integrative/cross-link-restraint';

View File

@@ -5,17 +5,17 @@
*/
import { OrderedSet } from '../../../../../mol-data/int';
import { BestDatabaseSequenceMapping as BestDatabaseSequenceMappingProp } from '../../../../../mol-model-props/sequence/best-database-mapping';
import { BestDatabaseSequenceMappingColorThemeProvider } from '../../../../../mol-model-props/sequence/themes/best-database-mapping';
import { SIFTSMapping as BestDatabaseSequenceMappingProp } from '../../../../../mol-model-props/sequence/sifts-mapping';
import { SIFTSMappingColorThemeProvider } from '../../../../../mol-model-props/sequence/themes/sifts-mapping';
import { Loci } from '../../../../../mol-model/loci';
import { StructureElement } from '../../../../../mol-model/structure';
import { ParamDefinition as PD } from '../../../../../mol-util/param-definition';
import { PluginBehavior } from '../../../behavior';
export const BestDatabaseSequenceMapping = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'best-sequence-database-mapping-prop',
export const SIFTSMapping = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
name: 'sifts-mapping-prop',
category: 'custom-props',
display: { name: 'Best Database Sequence Mapping' },
display: { name: 'SIFTS Mapping' },
ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
private provider = BestDatabaseSequenceMappingProp.Provider;
@@ -39,13 +39,13 @@ export const BestDatabaseSequenceMapping = PluginBehavior.create<{ autoAttach: b
register(): void {
this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
this.ctx.representation.structure.themes.colorThemeRegistry.add(BestDatabaseSequenceMappingColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.add(SIFTSMappingColorThemeProvider);
this.ctx.managers.lociLabels.addProvider(this.labelProvider);
}
unregister() {
this.ctx.customModelProperties.unregister(this.provider.descriptor.name);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(BestDatabaseSequenceMappingColorThemeProvider);
this.ctx.representation.structure.themes.colorThemeRegistry.remove(SIFTSMappingColorThemeProvider);
this.ctx.managers.lociLabels.removeProvider(this.labelProvider);
}
},

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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>
@@ -212,11 +212,14 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
private f = {
label: (loci: Loci) => {
const label: string[] = [];
if (StructureElement.Loci.is(loci) && loci.elements.length === 1) {
const { unit: u } = loci.elements[0];
const l = StructureElement.Location.create(loci.structure, u, u.elements[0]);
const name = StructureProperties.entity.pdbx_description(l).join(', ');
label.push(name);
if (StructureElement.Loci.is(loci)) {
const entityNames = new Set<string>();
for (const { unit: u } of loci.elements) {
const l = StructureElement.Location.create(loci.structure, u, u.elements[0]);
const name = StructureProperties.entity.pdbx_description(l).join(', ');
entityNames.add(name);
}
if (entityNames.size === 1) entityNames.forEach(name => label.push(name));
}
label.push(lociLabel(loci));
return label.filter(l => !!l).join('</br>');

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-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>
@@ -27,11 +27,19 @@ const StructureFocusRepresentationParams = (plugin: PluginContext) => {
expandRadius: PD.Numeric(5, { min: 1, max: 10, step: 1 }),
targetParams: PD.Group(reprParams, {
label: 'Target',
customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'physical', typeParams: { sizeFactor: 0.26, alpha: 0.51, adjustCylinderLength: true } })
customDefault: createStructureRepresentationParams(plugin, void 0, {
type: 'ball-and-stick',
size: 'physical',
typeParams: { sizeFactor: 0.22, sizeAspectRatio: 0.73, adjustCylinderLength: true, xrayShaded: true, aromaticBonds: false, multipleBonds: 'off', excludeTypes: ['hydrogen-bond', 'metal-coordination'] },
})
}),
surroundingsParams: PD.Group(reprParams, {
label: 'Surroundings',
customDefault: createStructureRepresentationParams(plugin, void 0, { type: 'ball-and-stick', size: 'physical', typeParams: { sizeFactor: 0.16 } })
customDefault: createStructureRepresentationParams(plugin, void 0, {
type: 'ball-and-stick',
size: 'physical',
typeParams: { sizeFactor: 0.16, excludeTypes: ['hydrogen-bond', 'metal-coordination'] }
})
}),
nciParams: PD.Group(reprParams, {
label: 'Non-covalent Int.',
@@ -44,6 +52,7 @@ const StructureFocusRepresentationParams = (plugin: PluginContext) => {
components: PD.MultiSelect(FocusComponents, PD.arrayToOptions(FocusComponents)),
excludeTargetFromSurroundings: PD.Boolean(false, { label: 'Exclude Target', description: 'Exclude the focus "target" from the surroudings component.' }),
ignoreHydrogens: PD.Boolean(false),
ignoreLight: PD.Boolean(false),
material: Material.getParam(),
clip: PD.Group(Clip.Params),
};
@@ -68,10 +77,10 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
private getReprParams(reprParams: PD.Values<PD.Params>) {
return {
...this.params.targetParams,
...reprParams,
type: {
name: reprParams.type.name,
params: { ...reprParams.type.params, ignoreHydrogens: this.params.ignoreHydrogens, material: this.params.material, clip: this.params.clip }
params: { ...reprParams.type.params, ignoreHydrogens: this.params.ignoreHydrogens, ignoreLight: this.params.ignoreLight, material: this.params.material, clip: this.params.clip }
}
};
}
@@ -114,7 +123,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr] && cell.obj && InteractionsRepresentationProvider.isApplicable(cell.obj?.data)) {
refs[StructureFocusRepresentationTags.SurrNciRepr] = builder
.to(refs[StructureFocusRepresentationTags.SurrSel]!)
.apply(StateTransforms.Representation.StructureRepresentation3D, this.params.nciParams, { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref;
.apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.nciParams), { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref;
}
return { state, builder, refs };
@@ -216,7 +225,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
hasComponent = components.indexOf('interactions') >= 0;
for (const repr of state.select(all.withTag(StructureFocusRepresentationTags.SurrNciRepr))) {
if (!hasComponent) builder.delete(repr.transform.ref);
else builder.to(repr).update(this.params.nciParams);
else builder.to(repr).update(this.getReprParams(this.params.nciParams));
}
await PluginCommands.State.Update(this.plugin, { state, tree: builder, options: { doNotLogTiming: true, doNotUpdateCurrent: true } });

View File

@@ -84,7 +84,7 @@ export function UpdateRepresentationVisibility(ctx: PluginContext) {
}
function updateVisibility(cell: StateObjectCell, r: Representation<any>) {
if (r.state.visible === cell.state.isHidden) {
if (r.state.visible === !!cell.state.isHidden) {
r.setState({ visible: !cell.state.isHidden });
return true;
} else {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -10,9 +10,9 @@ import { ColorNames } from '../../../mol-util/color/names';
import { Text } from '../../../mol-geo/geometry/text/text';
export const MeasurementRepresentationCommonTextParams = {
customText: PD.Text('', { label: 'Text', description: 'Override the label with custom value.' }),
customText: PD.Text('', { label: 'Text', description: 'Override the label with custom value.', isEssential: true }),
textColor: PD.Color(ColorNames.black, { isEssential: true }),
textSize: PD.Numeric(0.5, { min: 0.1, max: 5, step: 0.1 }, { isEssential: true }),
textSize: PD.Numeric(0.5, { min: 0.1, max: 10, step: 0.1 }, { isEssential: true }),
};
export const LociLabelTextParams = {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -35,7 +35,7 @@ export const CartoonParams = {
...NucleotideRingParams,
...PolymerDirectionParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
visuals: PD.MultiSelect(['polymer-trace', 'polymer-gap', 'nucleotide-block'], PD.objectToOptions(CartoonVisuals)),
visuals: PD.MultiSelect(['polymer-trace', 'polymer-gap', 'nucleotide-ring'], PD.objectToOptions(CartoonVisuals)),
bumpFrequency: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -49,7 +49,7 @@ export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure
if (!hasGaps && u.gapElements.length) hasGaps = true;
});
params.visuals.defaultValue = ['polymer-trace'];
if (hasNucleotides) params.visuals.defaultValue.push('nucleotide-block');
if (hasNucleotides) params.visuals.defaultValue.push('nucleotide-ring');
if (hasGaps) params.visuals.defaultValue.push('polymer-gap');
return params;
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -101,6 +101,8 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
return EmptyLoci;
}
const __linkIndicesSet = new Set<number>();
function eachCarbohydrateLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
let changed = false;
if (!StructureElement.Loci.is(loci)) return false;
@@ -110,11 +112,14 @@ function eachCarbohydrateLink(loci: Loci, structure: Structure, apply: (interval
for (const { unit, indices } of loci.elements) {
if (!Unit.isAtomic(unit)) continue;
__linkIndicesSet.clear();
OrderedSet.forEach(indices, v => {
// TODO avoid duplicate calls to apply
const linkIndices = getLinkIndices(unit, unit.elements[v]);
for (let i = 0, il = linkIndices.length; i < il; ++i) {
if (apply(Interval.ofSingleton(linkIndices[i]))) changed = true;
if (!__linkIndicesSet.has(linkIndices[i])) {
__linkIndicesSet.add(linkIndices[i]);
if (apply(Interval.ofSingleton(linkIndices[i]))) changed = true;
}
}
});
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -29,8 +29,8 @@ import { getAltResidueLociFromId } from './util/common';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const t = Mat4.identity();
const sVec = Vec3.zero();
const pd = Vec3.zero();
const sVec = Vec3();
const pd = Vec3();
const SideFactor = 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
@@ -212,6 +212,8 @@ function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: num
return EmptyLoci;
}
const __elementIndicesSet = new Set<number>();
/** For each carbohydrate (usually a monosaccharide) when all its residue's elements are in a loci. */
function eachCarbohydrate(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
const { getElementIndices } = structure.carbohydrates;
@@ -222,11 +224,14 @@ function eachCarbohydrate(loci: Loci, structure: Structure, apply: (interval: In
for (const { unit, indices } of loci.elements) {
if (!Unit.isAtomic(unit)) continue;
__elementIndicesSet.clear();
OrderedSet.forEach(indices, v => {
// TODO avoid duplicate calls to apply
const elementIndices = getElementIndices(unit, unit.elements[v]);
for (let i = 0, il = elementIndices.length; i < il; ++i) {
if (apply(Interval.ofSingleton(elementIndices[i] * 2))) changed = true;
if (!__elementIndicesSet.has(elementIndices[i])) {
__elementIndicesSet.add(elementIndices[i]);
if (apply(Interval.ofSingleton(elementIndices[i] * 2))) changed = true;
}
}
});
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -123,6 +123,8 @@ function getTerminalLinkLoci(pickingId: PickingId, structure: Structure, id: num
return EmptyLoci;
}
const __linkIndicesSet = new Set<number>();
function eachTerminalLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
let changed = false;
if (!StructureElement.Loci.is(loci)) return false;
@@ -132,11 +134,14 @@ function eachTerminalLink(loci: Loci, structure: Structure, apply: (interval: In
for (const { unit, indices } of loci.elements) {
if (!Unit.isAtomic(unit)) continue;
__linkIndicesSet.clear();
OrderedSet.forEach(indices, v => {
// TODO avoid duplicate calls to apply
const linkIndices = getTerminalLinkIndices(unit, unit.elements[v]);
for (let i = 0, il = linkIndices.length; i < il; ++i) {
if (apply(Interval.ofSingleton(linkIndices[i]))) changed = true;
if (!__linkIndicesSet.has(linkIndices[i])) {
__linkIndicesSet.add(linkIndices[i]);
if (apply(Interval.ofSingleton(linkIndices[i]))) changed = true;
}
}
});
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -57,7 +57,7 @@ async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, struct
Mesh.transform(surface, transform);
if (ctx.webgl && !ctx.webgl.isWebGL2) Mesh.uniformTriangleGroup(surface);
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.probeRadius + getUnitExtraRadius(unit));
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, getUnitExtraRadius(unit));
surface.setBoundingSphere(sphere);
(surface.meta as MolecularSurfaceMeta).resolution = resolution;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -224,6 +224,8 @@ export function getInterBondLoci(pickingId: PickingId, structure: Structure, id:
return EmptyLoci;
}
const __unitMap = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
export function eachInterBond(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean, isMarking: boolean) {
let changed = false;
if (Bond.isLoci(loci)) {
@@ -238,14 +240,13 @@ export function eachInterBond(loci: Loci, structure: Structure, apply: (interval
if (!Structure.areEquivalent(loci.structure, structure)) return false;
if (isMarking && loci.elements.length === 1) return false; // only a single unit
const map = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
for (const e of loci.elements) map.set(e.unit.id, e.indices);
for (const e of loci.elements) __unitMap.set(e.unit.id, e.indices);
for (const e of loci.elements) {
const { unit } = e;
if (!Unit.isAtomic(unit)) continue;
structure.interUnitBonds.getConnectedUnits(unit.id).forEach(b => {
const otherLociIndices = map.get(b.unitB);
const otherLociIndices = __unitMap.get(b.unitB);
if (!isMarking || otherLociIndices) {
OrderedSet.forEach(e.indices, v => {
if (!b.connectedIndices.includes(v)) return;
@@ -259,6 +260,8 @@ export function eachInterBond(loci: Loci, structure: Structure, apply: (interval
}
});
}
__unitMap.clear();
}
return changed;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -88,6 +88,7 @@ namespace Visual {
const currentStatus = markerStatus.ref.value as MarkerInfo['status'];
if (!isEveryLoci(loci)) {
// assume that all interval are non-overlapping
let intervalSize = 0;
lociApply(loci, interval => {
intervalSize += Interval.size(interval);

View File

@@ -76,14 +76,23 @@ namespace Transform {
const s = (b as any)[k], t = (a as any)[k];
if (!!s === !!t) continue;
changed = true;
(a as any)[k] = s;
if (s !== void 0) {
(a as any)[k] = s;
} else {
delete (a as any)[k];
}
}
for (const k of Object.keys(a)) {
const s = (b as any)[k], t = (a as any)[k];
if (!!s === !!t) continue;
changed = true;
(a as any)[k] = s;
if (s !== void 0) {
(a as any)[k] = s;
} else {
delete (a as any)[k];
}
}
return changed;
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -13,8 +13,12 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ThemeDataContext } from '../theme';
import { TableLegend } from '../../mol-util/legend';
import { getAdjustedColorMap } from '../../mol-util/color/color';
import { getColorMapParams } from '../../mol-util/color/params';
import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id';
import { OperatorNameColorThemeParams, OperatorNameColorTheme } from './operator-name';
import { EntityIdColorTheme, EntityIdColorThemeParams } from './entity-id';
import { assertUnreachable } from '../../mol-util/type-helpers';
import { EntitySourceColorTheme, EntitySourceColorThemeParams } from './entity-source';
// from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
export const ElementSymbolColors = ColorMap({
@@ -25,16 +29,20 @@ export type ElementSymbolColors = typeof ElementSymbolColors
const DefaultElementSymbolColor = Color(0xFFFFFF);
const Description = 'Assigns a color to every atom according to its chemical element.';
// TODO generalise `carbonColor` param to all themes?
export const ElementSymbolColorThemeParams = {
carbonColor: PD.MappedStatic('chain-id', {
'chain-id': PD.Group({ ...ChainIdColorThemeParams }),
'operator-name': PD.Group({ ...OperatorNameColorThemeParams }),
'element-symbol': PD.Group({})
'chain-id': PD.Group(ChainIdColorThemeParams),
'entity-id': PD.Group(EntityIdColorThemeParams),
'entity-source': PD.Group(EntitySourceColorThemeParams),
'operator-name': PD.Group(OperatorNameColorThemeParams),
'element-symbol': PD.EmptyGroup()
}, { description: 'Use chain-id coloring for carbon atoms.' }),
saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 })
lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 }),
colors: PD.MappedStatic('default', {
'default': PD.EmptyGroup(),
'custom': PD.Group(getColorMapParams(ElementSymbolColors))
})
};
export type ElementSymbolColorThemeParams = typeof ElementSymbolColorThemeParams
export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) {
@@ -47,13 +55,16 @@ export function elementSymbolColor(colorMap: ElementSymbolColors, element: Eleme
}
export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementSymbolColorThemeParams>): ColorTheme<ElementSymbolColorThemeParams> {
const colorMap = getAdjustedColorMap(ElementSymbolColors, props.saturation, props.lightness);
const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? ElementSymbolColors : props.colors.params, props.saturation, props.lightness);
const carbonColor = props.carbonColor.name === 'chain-id'
? ChainIdColorTheme(ctx, props.carbonColor.params).color
: props.carbonColor.name === 'operator-name'
? OperatorNameColorTheme(ctx, props.carbonColor.params).color
: undefined;
const pcc = props.carbonColor;
const carbonColor =
pcc.name === 'chain-id' ? ChainIdColorTheme(ctx, pcc.params).color :
pcc.name === 'entity-id' ? EntityIdColorTheme(ctx, pcc.params).color :
pcc.name === 'entity-source' ? EntitySourceColorTheme(ctx, pcc.params).color :
pcc.name === 'operator-name' ? OperatorNameColorTheme(ctx, pcc.params).color :
pcc.name === 'element-symbol' ? undefined :
assertUnreachable(pcc);
function elementColor(element: ElementSymbol, location: Location) {
return (carbonColor && element === 'C')
@@ -86,8 +97,8 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<
color,
props,
description: Description,
legend: TableLegend(Object.keys(ElementSymbolColors).map(name => {
return [name, (ElementSymbolColors as any)[name] as Color] as [string, Color];
legend: TableLegend(Object.keys(colorMap).map(name => {
return [name, (colorMap as any)[name] as Color] as [string, Color];
}))
};
}

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StructureProperties, StructureElement, Bond, Structure } from '../../mol-model/structure';
import { StructureProperties, StructureElement, Bond, Structure, Unit } from '../../mol-model/structure';
import { Color } from '../../mol-util/color';
import { Location } from '../../mol-model/location';
import { ColorTheme, LocationColor } from '../color';
@@ -38,10 +38,33 @@ function getEntityIdSerialMap(structure: Structure) {
const k = key(label_entity_id.value(j), i);
if (!map.has(k)) map.set(k, map.size);
}
const { coarseHierarchy } = structure.models[i];
if (coarseHierarchy.isDefined) {
const { entity_id: spheres_entity_id } = coarseHierarchy.spheres;
for (let j = 0, jl = spheres_entity_id.rowCount; j < jl; ++j) {
const k = key(spheres_entity_id.value(j), i);
if (!map.has(k)) map.set(k, map.size);
}
const { entity_id: gaussians_entity_id } = coarseHierarchy.gaussians;
for (let j = 0, jl = gaussians_entity_id.rowCount; j < jl; ++j) {
const k = key(gaussians_entity_id.value(j), i);
if (!map.has(k)) map.set(k, map.size);
}
}
}
return map;
}
function getEntityId(location: StructureElement.Location): string {
switch (location.unit.kind) {
case Unit.Kind.Atomic:
return StructureProperties.chain.label_entity_id(location);
case Unit.Kind.Spheres:
case Unit.Kind.Gaussians:
return StructureProperties.coarse.entity_id(location);
}
}
export function EntityIdColorTheme(ctx: ThemeDataContext, props: PD.Values<EntityIdColorThemeParams>): ColorTheme<EntityIdColorThemeParams> {
let color: LocationColor;
let legend: ScaleLegend | TableLegend | undefined;
@@ -59,16 +82,16 @@ export function EntityIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Entit
color = (location: Location): Color => {
let serial: number | undefined = undefined;
if (StructureElement.Location.is(location)) {
const atomId = StructureProperties.chain.label_entity_id(location);
const entityId = getEntityId(location);
const modelIndex = location.structure.models.indexOf(location.unit.model);
const k = key(atomId, modelIndex);
const k = key(entityId, modelIndex);
serial = entityIdSerialMap.get(k);
} else if (Bond.isLocation(location)) {
l.unit = location.aUnit;
l.element = location.aUnit.elements[location.aIndex];
const atomId = StructureProperties.chain.label_entity_id(l);
const entityId = getEntityId(l);
const modelIndex = l.structure.models.indexOf(l.unit.model);
const k = key(atomId, modelIndex);
const k = key(entityId, modelIndex);
serial = entityIdSerialMap.get(k);
}
return serial === undefined ? DefaultColor : palette.color(serial);

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Color } from '../../mol-util/color';
import { Color, ColorMap } from '../../mol-util/color';
import { StructureElement, Unit, Bond, ElementIndex } from '../../mol-model/structure';
import { Location } from '../../mol-model/location';
import { ColorTheme } from '../color';
@@ -13,6 +13,19 @@ import { getElementMoleculeType } from '../../mol-model/structure/util';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ThemeDataContext } from '../theme';
import { TableLegend } from '../../mol-util/legend';
import { getAdjustedColorMap } from '../../mol-util/color/color';
import { getColorMapParams } from '../../mol-util/color/params';
export const MoleculeTypeColors = ColorMap({
water: 0x386cb0,
ion: 0xf0027f,
protein: 0xbeaed4,
RNA: 0xfdc086,
DNA: 0xbf5b17,
PNA: 0x42A49A,
saccharide: 0x7fc97f,
});
export type MoleculeTypeColors = typeof MoleculeTypeColors
const DefaultMoleculeTypeColor = Color(0xffff99);
const Description = 'Assigns a color based on the molecule type of a residue.';
@@ -20,15 +33,9 @@ const Description = 'Assigns a color based on the molecule type of a residue.';
export const MoleculeTypeColorThemeParams = {
saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
colors: PD.Group({
water: PD.Color(Color(0x386cb0)),
ion: PD.Color(Color(0xf0027f)),
protein: PD.Color(Color(0xbeaed4)),
rna: PD.Color(Color(0xfdc086)),
dna: PD.Color(Color(0xbf5b17)),
pna: PD.Color(Color(0x42A49A)),
saccharide: PD.Color(Color(0x7fc97f)),
lipid: PD.Color(Color(0xcccccc)),
colors: PD.MappedStatic('default', {
'default': PD.EmptyGroup(),
'custom': PD.Group(getColorMapParams(MoleculeTypeColors))
})
};
export type MoleculeTypeColorThemeParams = typeof MoleculeTypeColorThemeParams
@@ -36,32 +43,28 @@ export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) {
return MoleculeTypeColorThemeParams; // TODO return copy
}
type MoleculeTypeColorThemeProps = PD.Values<MoleculeTypeColorThemeParams>;
export function moleculeTypeColor(props: MoleculeTypeColorThemeProps, unit: Unit, element: ElementIndex): Color {
let c = DefaultMoleculeTypeColor;
export function moleculeTypeColor(colorMap: MoleculeTypeColors, unit: Unit, element: ElementIndex): Color {
const moleculeType = getElementMoleculeType(unit, element);
switch (moleculeType) {
case MoleculeType.Water: c = props.colors.water; break;
case MoleculeType.Ion: c = props.colors.ion; break;
case MoleculeType.Protein: c = props.colors.protein; break;
case MoleculeType.RNA: c = props.colors.rna; break;
case MoleculeType.DNA: c = props.colors.dna; break;
case MoleculeType.PNA: c = props.colors.pna; break;
case MoleculeType.Saccharide: c = props.colors.saccharide; break;
case MoleculeType.Lipid: c = props.colors.lipid; break;
case MoleculeType.Water: return colorMap.water;
case MoleculeType.Ion: return colorMap.ion;
case MoleculeType.Protein: return colorMap.protein;
case MoleculeType.RNA: return colorMap.RNA;
case MoleculeType.DNA: return colorMap.DNA;
case MoleculeType.PNA: return colorMap.PNA;
case MoleculeType.Saccharide: return colorMap.saccharide;
}
c = Color.saturate(c, props.saturation);
c = Color.darken(c, -props.lightness);
return c;
return DefaultMoleculeTypeColor;
}
export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<MoleculeTypeColorThemeParams>): ColorTheme<MoleculeTypeColorThemeParams> {
const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? MoleculeTypeColors : props.colors.params, props.saturation, props.lightness);
function color(location: Location): Color {
if (StructureElement.Location.is(location)) {
return moleculeTypeColor(props, location.unit, location.element);
return moleculeTypeColor(colorMap, location.unit, location.element);
} else if (Bond.isLocation(location)) {
return moleculeTypeColor(props, location.aUnit, location.aUnit.elements[location.aIndex]);
return moleculeTypeColor(colorMap, location.aUnit, location.aUnit.elements[location.aIndex]);
}
return DefaultMoleculeTypeColor;
}
@@ -72,8 +75,8 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<M
color,
props,
description: Description,
legend: TableLegend(Object.keys(props.colors).map(name => {
return [name, (props.colors as any)[name] as Color] as [string, Color];
legend: TableLegend(Object.keys(colorMap).map(name => {
return [name, (colorMap as any)[name] as Color] as [string, Color];
}).concat([['Other/unknown', DefaultMoleculeTypeColor]]))
};
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -12,6 +12,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ThemeDataContext } from '../theme';
import { TableLegend } from '../../mol-util/legend';
import { getAdjustedColorMap } from '../../mol-util/color/color';
import { getColorMapParams } from '../../mol-util/color/params';
// protein colors from Jmol http://jmol.sourceforge.net/jscolors/
export const ResidueNameColors = ColorMap({
@@ -66,7 +67,11 @@ const Description = 'Assigns a color to every residue according to its name.';
export const ResidueNameColorThemeParams = {
saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
lightness: PD.Numeric(1, { min: -6, max: 6, step: 0.1 })
lightness: PD.Numeric(1, { min: -6, max: 6, step: 0.1 }),
colors: PD.MappedStatic('default', {
'default': PD.EmptyGroup(),
'custom': PD.Group(getColorMapParams(ResidueNameColors))
})
};
export type ResidueNameColorThemeParams = typeof ResidueNameColorThemeParams
export function getResidueNameColorThemeParams(ctx: ThemeDataContext) {
@@ -93,7 +98,7 @@ export function residueNameColor(colorMap: ResidueNameColors, residueName: strin
}
export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<ResidueNameColorThemeParams>): ColorTheme<ResidueNameColorThemeParams> {
const colorMap = getAdjustedColorMap(ResidueNameColors, props.saturation, props.lightness);
const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? ResidueNameColors : props.colors.params, props.saturation, props.lightness);
function color(location: Location): Color {
if (StructureElement.Location.is(location)) {
@@ -123,8 +128,8 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<Re
color,
props,
description: Description,
legend: TableLegend(Object.keys(ResidueNameColors).map(name => {
return [name, (ResidueNameColors as any)[name] as Color] as [string, Color];
legend: TableLegend(Object.keys(colorMap).map(name => {
return [name, (colorMap as any)[name] as Color] as [string, Color];
}).concat([['Unknown', DefaultResidueNameColor]]))
};
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -15,6 +15,7 @@ import { ThemeDataContext } from '../theme';
import { TableLegend } from '../../mol-util/legend';
import { SecondaryStructureProvider, SecondaryStructureValue } from '../../mol-model-props/computed/secondary-structure';
import { getAdjustedColorMap } from '../../mol-util/color/color';
import { getColorMapParams } from '../../mol-util/color/params';
import { CustomProperty } from '../../mol-model-props/common/custom-property';
import { hash2 } from '../../mol-data/util';
@@ -41,7 +42,11 @@ const Description = 'Assigns a color based on the type of secondary structure an
export const SecondaryStructureColorThemeParams = {
saturation: PD.Numeric(-1, { min: -6, max: 6, step: 0.1 }),
lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 })
lightness: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
colors: PD.MappedStatic('default', {
'default': PD.EmptyGroup(),
'custom': PD.Group(getColorMapParams(SecondaryStructureColors))
})
};
export type SecondaryStructureColorThemeParams = typeof SecondaryStructureColorThemeParams
export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
@@ -88,7 +93,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
const computedSecondaryStructure = ctx.structure && SecondaryStructureProvider.get(ctx.structure);
const contextHash = computedSecondaryStructure ? hash2(computedSecondaryStructure.id, computedSecondaryStructure.version) : -1;
const colorMap = getAdjustedColorMap(SecondaryStructureColors, props.saturation, props.lightness);
const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? SecondaryStructureColors : props.colors.params, props.saturation, props.lightness);
function color(location: Location): Color {
if (StructureElement.Location.is(location)) {
@@ -107,8 +112,8 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
props,
contextHash,
description: Description,
legend: TableLegend(Object.keys(SecondaryStructureColors).map(name => {
return [name, (SecondaryStructureColors as any)[name] as Color] as [string, Color];
legend: TableLegend(Object.keys(colorMap).map(name => {
return [name, (colorMap as any)[name] as Color] as [string, Color];
}).concat([['Other', DefaultSecondaryStructureColor]]))
};
}

View File

@@ -1,5 +1,5 @@
/**
* 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2022 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 { objectForEach } from '../object';
import { ColorMap } from './color';
export function getColorMapParams<T extends { [k: string]: number }>(map: ColorMap<T>) {
const colors: Record<string, PD.Color> = {};
objectForEach(map, (_, k) => {
colors[k] = PD.Color(map[k]);
});
return colors as { [k in keyof T]: PD.Color };
}

View File

@@ -197,13 +197,13 @@ export function configureLocal() {
description: VOLUME_SERVER_HEADER
});
parser.add_argument('--jobs', { help: `Path to a JSON file with job specification.`, required: false });
parser.add_argument('--jobsTemplate', { help: 'Print example template for jobs.json and exit.', required: false, nargs: 0 });
parser.add_argument('--jobsTemplate', { help: 'Print example template for jobs.json and exit.', required: false, action: 'store_true' });
addJsonConfigArgs(parser);
addLimitsArgs(parser);
const config = parser.parse_args() as LimitsConfig & ServerJsonConfig;
if (config.cfgTemplate !== null) {
if (config.cfgTemplate) {
console.log(JSON.stringify(DefaultLimitsConfig, null, 2));
process.exit(0);
}
@@ -216,7 +216,7 @@ export function configureLocal() {
setLimitsConfig(cfg);
}
if (config.printCfg !== null) {
if (config.printCfg) {
console.log(JSON.stringify(LimitsConfig, null, 2));
process.exit(0);
}

View File

@@ -14,7 +14,7 @@ import * as LocalApi from './server/local-api';
const config = configureLocal();
if (config.jobsTemplate !== null) {
if (config.jobsTemplate) {
const exampleJobs: LocalApi.JobEntry[] = [{
source: {
filename: `g:/test/mdb/xray-1tqn.mdb`,